0034-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. From 5941d6eccd8522090c18a68c506ab51885b955d5 Mon Sep 17 00:00:00 2001
  2. From: popcornmix <popcornmix@gmail.com>
  3. Date: Wed, 3 Jul 2013 00:31:47 +0100
  4. Subject: [PATCH] cma: Add vc_cma driver to enable use of CMA
  5. MIME-Version: 1.0
  6. Content-Type: text/plain; charset=UTF-8
  7. Content-Transfer-Encoding: 8bit
  8. Signed-off-by: popcornmix <popcornmix@gmail.com>
  9. vc_cma: Make the vc_cma area the default contiguous DMA area
  10. vc_cma: Provide empty functions when module is not built
  11. Providing empty functions saves the users from guarding the
  12. function call with an #if clause.
  13. Move __init markings from prototypes to functions.
  14. Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
  15. ---
  16. drivers/char/Kconfig | 2 +
  17. drivers/char/Makefile | 1 +
  18. drivers/char/broadcom/Kconfig | 15 +
  19. drivers/char/broadcom/Makefile | 1 +
  20. drivers/char/broadcom/vc_cma/Makefile | 14 +
  21. drivers/char/broadcom/vc_cma/vc_cma.c | 1193 +++++++++++++++++++++++++++++++++
  22. include/linux/broadcom/vc_cma.h | 36 +
  23. 7 files changed, 1262 insertions(+)
  24. create mode 100644 drivers/char/broadcom/Kconfig
  25. create mode 100644 drivers/char/broadcom/Makefile
  26. create mode 100644 drivers/char/broadcom/vc_cma/Makefile
  27. create mode 100644 drivers/char/broadcom/vc_cma/vc_cma.c
  28. create mode 100644 include/linux/broadcom/vc_cma.h
  29. --- a/drivers/char/Kconfig
  30. +++ b/drivers/char/Kconfig
  31. @@ -4,6 +4,8 @@
  32. menu "Character devices"
  33. +source "drivers/char/broadcom/Kconfig"
  34. +
  35. source "drivers/tty/Kconfig"
  36. config DEVMEM
  37. --- a/drivers/char/Makefile
  38. +++ b/drivers/char/Makefile
  39. @@ -60,3 +60,4 @@ js-rtc-y = rtc.o
  40. obj-$(CONFIG_TILE_SROM) += tile-srom.o
  41. obj-$(CONFIG_XILLYBUS) += xillybus/
  42. +obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/
  43. --- /dev/null
  44. +++ b/drivers/char/broadcom/Kconfig
  45. @@ -0,0 +1,15 @@
  46. +#
  47. +# Broadcom char driver config
  48. +#
  49. +
  50. +menuconfig BRCM_CHAR_DRIVERS
  51. + bool "Broadcom Char Drivers"
  52. + help
  53. + Broadcom's char drivers
  54. +
  55. +config BCM_VC_CMA
  56. + bool "Videocore CMA"
  57. + depends on CMA && BRCM_CHAR_DRIVERS && BCM2708_VCHIQ
  58. + default n
  59. + help
  60. + Helper for videocore CMA access.
  61. --- /dev/null
  62. +++ b/drivers/char/broadcom/Makefile
  63. @@ -0,0 +1 @@
  64. +obj-$(CONFIG_BCM_VC_CMA) += vc_cma/
  65. --- /dev/null
  66. +++ b/drivers/char/broadcom/vc_cma/Makefile
  67. @@ -0,0 +1,14 @@
  68. +ccflags-y += -Wall -Wstrict-prototypes -Wno-trigraphs
  69. +ccflags-y += -Werror
  70. +ccflags-y += -Iinclude/linux/broadcom
  71. +ccflags-y += -Idrivers/misc/vc04_services
  72. +ccflags-y += -Idrivers/misc/vc04_services/interface/vchi
  73. +ccflags-y += -Idrivers/misc/vc04_services/interface/vchiq_arm
  74. +
  75. +ccflags-y += -D__KERNEL__
  76. +ccflags-y += -D__linux__
  77. +ccflags-y += -Werror
  78. +
  79. +obj-$(CONFIG_BCM_VC_CMA) += vc-cma.o
  80. +
  81. +vc-cma-objs := vc_cma.o
  82. --- /dev/null
  83. +++ b/drivers/char/broadcom/vc_cma/vc_cma.c
  84. @@ -0,0 +1,1193 @@
  85. +/**
  86. + * Copyright (c) 2010-2012 Broadcom. All rights reserved.
  87. + *
  88. + * Redistribution and use in source and binary forms, with or without
  89. + * modification, are permitted provided that the following conditions
  90. + * are met:
  91. + * 1. Redistributions of source code must retain the above copyright
  92. + * notice, this list of conditions, and the following disclaimer,
  93. + * without modification.
  94. + * 2. Redistributions in binary form must reproduce the above copyright
  95. + * notice, this list of conditions and the following disclaimer in the
  96. + * documentation and/or other materials provided with the distribution.
  97. + * 3. The names of the above-listed copyright holders may not be used
  98. + * to endorse or promote products derived from this software without
  99. + * specific prior written permission.
  100. + *
  101. + * ALTERNATIVELY, this software may be distributed under the terms of the
  102. + * GNU General Public License ("GPL") version 2, as published by the Free
  103. + * Software Foundation.
  104. + *
  105. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  106. + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  107. + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  108. + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  109. + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  110. + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  111. + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  112. + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  113. + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  114. + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  115. + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  116. + */
  117. +
  118. +#include <linux/kernel.h>
  119. +#include <linux/module.h>
  120. +#include <linux/kthread.h>
  121. +#include <linux/fs.h>
  122. +#include <linux/device.h>
  123. +#include <linux/cdev.h>
  124. +#include <linux/mm.h>
  125. +#include <linux/proc_fs.h>
  126. +#include <linux/seq_file.h>
  127. +#include <linux/dma-mapping.h>
  128. +#include <linux/dma-contiguous.h>
  129. +#include <linux/platform_device.h>
  130. +#include <linux/uaccess.h>
  131. +#include <asm/cacheflush.h>
  132. +
  133. +#include "vc_cma.h"
  134. +
  135. +#include "vchiq_util.h"
  136. +#include "vchiq_connected.h"
  137. +//#include "debug_sym.h"
  138. +//#include "vc_mem.h"
  139. +
  140. +#define DRIVER_NAME "vc-cma"
  141. +
  142. +#define LOG_DBG(fmt, ...) \
  143. + if (vc_cma_debug) \
  144. + printk(KERN_INFO fmt "\n", ##__VA_ARGS__)
  145. +#define LOG_INFO(fmt, ...) \
  146. + printk(KERN_INFO fmt "\n", ##__VA_ARGS__)
  147. +#define LOG_ERR(fmt, ...) \
  148. + printk(KERN_ERR fmt "\n", ##__VA_ARGS__)
  149. +
  150. +#define VC_CMA_FOURCC VCHIQ_MAKE_FOURCC('C', 'M', 'A', ' ')
  151. +#define VC_CMA_VERSION 2
  152. +
  153. +#define VC_CMA_CHUNK_ORDER 6 /* 256K */
  154. +#define VC_CMA_CHUNK_SIZE (4096 << VC_CMA_CHUNK_ORDER)
  155. +#define VC_CMA_MAX_PARAMS_PER_MSG \
  156. + ((VCHIQ_MAX_MSG_SIZE - sizeof(unsigned short))/sizeof(unsigned short))
  157. +#define VC_CMA_RESERVE_COUNT_MAX 16
  158. +
  159. +#define PAGES_PER_CHUNK (VC_CMA_CHUNK_SIZE / PAGE_SIZE)
  160. +
  161. +#define VCADDR_TO_PHYSADDR(vcaddr) (mm_vc_mem_phys_addr + vcaddr)
  162. +
  163. +#define loud_error(...) \
  164. + LOG_ERR("===== " __VA_ARGS__)
  165. +
  166. +enum {
  167. + VC_CMA_MSG_QUIT,
  168. + VC_CMA_MSG_OPEN,
  169. + VC_CMA_MSG_TICK,
  170. + VC_CMA_MSG_ALLOC, /* chunk count */
  171. + VC_CMA_MSG_FREE, /* chunk, chunk, ... */
  172. + VC_CMA_MSG_ALLOCATED, /* chunk, chunk, ... */
  173. + VC_CMA_MSG_REQUEST_ALLOC, /* chunk count */
  174. + VC_CMA_MSG_REQUEST_FREE, /* chunk count */
  175. + VC_CMA_MSG_RESERVE, /* bytes lo, bytes hi */
  176. + VC_CMA_MSG_UPDATE_RESERVE,
  177. + VC_CMA_MSG_MAX
  178. +};
  179. +
  180. +struct cma_msg {
  181. + unsigned short type;
  182. + unsigned short params[VC_CMA_MAX_PARAMS_PER_MSG];
  183. +};
  184. +
  185. +struct vc_cma_reserve_user {
  186. + unsigned int pid;
  187. + unsigned int reserve;
  188. +};
  189. +
  190. +/* Device (/dev) related variables */
  191. +static dev_t vc_cma_devnum;
  192. +static struct class *vc_cma_class;
  193. +static struct cdev vc_cma_cdev;
  194. +static int vc_cma_inited;
  195. +static int vc_cma_debug;
  196. +
  197. +/* Proc entry */
  198. +static struct proc_dir_entry *vc_cma_proc_entry;
  199. +
  200. +phys_addr_t vc_cma_base;
  201. +struct page *vc_cma_base_page;
  202. +unsigned int vc_cma_size;
  203. +EXPORT_SYMBOL(vc_cma_size);
  204. +unsigned int vc_cma_initial;
  205. +unsigned int vc_cma_chunks;
  206. +unsigned int vc_cma_chunks_used;
  207. +unsigned int vc_cma_chunks_reserved;
  208. +
  209. +
  210. +void *vc_cma_dma_alloc;
  211. +unsigned int vc_cma_dma_size;
  212. +
  213. +static int in_loud_error;
  214. +
  215. +unsigned int vc_cma_reserve_total;
  216. +unsigned int vc_cma_reserve_count;
  217. +struct vc_cma_reserve_user vc_cma_reserve_users[VC_CMA_RESERVE_COUNT_MAX];
  218. +static DEFINE_SEMAPHORE(vc_cma_reserve_mutex);
  219. +static DEFINE_SEMAPHORE(vc_cma_worker_queue_push_mutex);
  220. +
  221. +static u64 vc_cma_dma_mask = DMA_BIT_MASK(32);
  222. +static struct platform_device vc_cma_device = {
  223. + .name = "vc-cma",
  224. + .id = 0,
  225. + .dev = {
  226. + .dma_mask = &vc_cma_dma_mask,
  227. + .coherent_dma_mask = DMA_BIT_MASK(32),
  228. + },
  229. +};
  230. +
  231. +static VCHIQ_INSTANCE_T cma_instance;
  232. +static VCHIQ_SERVICE_HANDLE_T cma_service;
  233. +static VCHIU_QUEUE_T cma_msg_queue;
  234. +static struct task_struct *cma_worker;
  235. +
  236. +static int vc_cma_set_reserve(unsigned int reserve, unsigned int pid);
  237. +static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply);
  238. +static VCHIQ_STATUS_T cma_service_callback(VCHIQ_REASON_T reason,
  239. + VCHIQ_HEADER_T * header,
  240. + VCHIQ_SERVICE_HANDLE_T service,
  241. + void *bulk_userdata);
  242. +static void send_vc_msg(unsigned short type,
  243. + unsigned short param1, unsigned short param2);
  244. +static bool send_worker_msg(VCHIQ_HEADER_T * msg);
  245. +
  246. +static int early_vc_cma_mem(char *p)
  247. +{
  248. + unsigned int new_size;
  249. + printk(KERN_NOTICE "early_vc_cma_mem(%s)", p);
  250. + vc_cma_size = memparse(p, &p);
  251. + vc_cma_initial = vc_cma_size;
  252. + if (*p == '/')
  253. + vc_cma_size = memparse(p + 1, &p);
  254. + if (*p == '@')
  255. + vc_cma_base = memparse(p + 1, &p);
  256. +
  257. + new_size = (vc_cma_size - ((-vc_cma_base) & (VC_CMA_CHUNK_SIZE - 1)))
  258. + & ~(VC_CMA_CHUNK_SIZE - 1);
  259. + if (new_size > vc_cma_size)
  260. + vc_cma_size = 0;
  261. + vc_cma_initial = (vc_cma_initial + VC_CMA_CHUNK_SIZE - 1)
  262. + & ~(VC_CMA_CHUNK_SIZE - 1);
  263. + if (vc_cma_initial > vc_cma_size)
  264. + vc_cma_initial = vc_cma_size;
  265. + vc_cma_base = (vc_cma_base + VC_CMA_CHUNK_SIZE - 1)
  266. + & ~(VC_CMA_CHUNK_SIZE - 1);
  267. +
  268. + printk(KERN_NOTICE " -> initial %x, size %x, base %x", vc_cma_initial,
  269. + vc_cma_size, (unsigned int)vc_cma_base);
  270. +
  271. + return 0;
  272. +}
  273. +
  274. +early_param("vc-cma-mem", early_vc_cma_mem);
  275. +
  276. +void __init vc_cma_early_init(void)
  277. +{
  278. + LOG_DBG("vc_cma_early_init - vc_cma_chunks = %d", vc_cma_chunks);
  279. + if (vc_cma_size) {
  280. + int rc = platform_device_register(&vc_cma_device);
  281. + LOG_DBG("platform_device_register -> %d", rc);
  282. + }
  283. +}
  284. +
  285. +void __init vc_cma_reserve(void)
  286. +{
  287. + /* if vc_cma_size is set, then declare vc CMA area of the same
  288. + * size from the end of memory
  289. + */
  290. + if (vc_cma_size) {
  291. + if (dma_declare_contiguous(&vc_cma_device.dev, vc_cma_size,
  292. + vc_cma_base, 0) == 0) {
  293. + if (!dev_get_cma_area(NULL)) {
  294. + /* There is no default CMA area - make this
  295. + the default */
  296. + struct cma *vc_cma_area = dev_get_cma_area(
  297. + &vc_cma_device.dev);
  298. + dma_contiguous_set_default(vc_cma_area);
  299. + LOG_INFO("vc_cma_reserve - using vc_cma as "
  300. + "the default contiguous DMA area");
  301. + }
  302. + } else {
  303. + LOG_ERR("vc_cma: dma_declare_contiguous(%x,%x) failed",
  304. + vc_cma_size, (unsigned int)vc_cma_base);
  305. + vc_cma_size = 0;
  306. + }
  307. + }
  308. + vc_cma_chunks = vc_cma_size / VC_CMA_CHUNK_SIZE;
  309. +}
  310. +
  311. +/****************************************************************************
  312. +*
  313. +* vc_cma_open
  314. +*
  315. +***************************************************************************/
  316. +
  317. +static int vc_cma_open(struct inode *inode, struct file *file)
  318. +{
  319. + (void)inode;
  320. + (void)file;
  321. +
  322. + return 0;
  323. +}
  324. +
  325. +/****************************************************************************
  326. +*
  327. +* vc_cma_release
  328. +*
  329. +***************************************************************************/
  330. +
  331. +static int vc_cma_release(struct inode *inode, struct file *file)
  332. +{
  333. + (void)inode;
  334. + (void)file;
  335. +
  336. + vc_cma_set_reserve(0, current->tgid);
  337. +
  338. + return 0;
  339. +}
  340. +
  341. +/****************************************************************************
  342. +*
  343. +* vc_cma_ioctl
  344. +*
  345. +***************************************************************************/
  346. +
  347. +static long vc_cma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  348. +{
  349. + int rc = 0;
  350. +
  351. + (void)cmd;
  352. + (void)arg;
  353. +
  354. + switch (cmd) {
  355. + case VC_CMA_IOC_RESERVE:
  356. + rc = vc_cma_set_reserve((unsigned int)arg, current->tgid);
  357. + if (rc >= 0)
  358. + rc = 0;
  359. + break;
  360. + default:
  361. + LOG_ERR("vc-cma: Unknown ioctl %x", cmd);
  362. + return -ENOTTY;
  363. + }
  364. +
  365. + return rc;
  366. +}
  367. +
  368. +/****************************************************************************
  369. +*
  370. +* File Operations for the driver.
  371. +*
  372. +***************************************************************************/
  373. +
  374. +static const struct file_operations vc_cma_fops = {
  375. + .owner = THIS_MODULE,
  376. + .open = vc_cma_open,
  377. + .release = vc_cma_release,
  378. + .unlocked_ioctl = vc_cma_ioctl,
  379. +};
  380. +
  381. +/****************************************************************************
  382. +*
  383. +* vc_cma_proc_open
  384. +*
  385. +***************************************************************************/
  386. +
  387. +static int vc_cma_show_info(struct seq_file *m, void *v)
  388. +{
  389. + int i;
  390. +
  391. + seq_printf(m, "Videocore CMA:\n");
  392. + seq_printf(m, " Base : %08x\n", (unsigned int)vc_cma_base);
  393. + seq_printf(m, " Length : %08x\n", vc_cma_size);
  394. + seq_printf(m, " Initial : %08x\n", vc_cma_initial);
  395. + seq_printf(m, " Chunk size : %08x\n", VC_CMA_CHUNK_SIZE);
  396. + seq_printf(m, " Chunks : %4d (%d bytes)\n",
  397. + (int)vc_cma_chunks,
  398. + (int)(vc_cma_chunks * VC_CMA_CHUNK_SIZE));
  399. + seq_printf(m, " Used : %4d (%d bytes)\n",
  400. + (int)vc_cma_chunks_used,
  401. + (int)(vc_cma_chunks_used * VC_CMA_CHUNK_SIZE));
  402. + seq_printf(m, " Reserved : %4d (%d bytes)\n",
  403. + (unsigned int)vc_cma_chunks_reserved,
  404. + (int)(vc_cma_chunks_reserved * VC_CMA_CHUNK_SIZE));
  405. +
  406. + for (i = 0; i < vc_cma_reserve_count; i++) {
  407. + struct vc_cma_reserve_user *user = &vc_cma_reserve_users[i];
  408. + seq_printf(m, " PID %5d: %d bytes\n", user->pid,
  409. + user->reserve);
  410. + }
  411. + seq_printf(m, " dma_alloc : %p (%d pages)\n",
  412. + vc_cma_dma_alloc ? page_address(vc_cma_dma_alloc) : 0,
  413. + vc_cma_dma_size);
  414. +
  415. + seq_printf(m, "\n");
  416. +
  417. + return 0;
  418. +}
  419. +
  420. +static int vc_cma_proc_open(struct inode *inode, struct file *file)
  421. +{
  422. + return single_open(file, vc_cma_show_info, NULL);
  423. +}
  424. +
  425. +/****************************************************************************
  426. +*
  427. +* vc_cma_proc_write
  428. +*
  429. +***************************************************************************/
  430. +
  431. +static int vc_cma_proc_write(struct file *file,
  432. + const char __user *buffer,
  433. + size_t size, loff_t *ppos)
  434. +{
  435. + int rc = -EFAULT;
  436. + char input_str[20];
  437. +
  438. + memset(input_str, 0, sizeof(input_str));
  439. +
  440. + if (size > sizeof(input_str)) {
  441. + LOG_ERR("%s: input string length too long", __func__);
  442. + goto out;
  443. + }
  444. +
  445. + if (copy_from_user(input_str, buffer, size - 1)) {
  446. + LOG_ERR("%s: failed to get input string", __func__);
  447. + goto out;
  448. + }
  449. +#define ALLOC_STR "alloc"
  450. +#define FREE_STR "free"
  451. +#define DEBUG_STR "debug"
  452. +#define RESERVE_STR "reserve"
  453. +#define DMA_ALLOC_STR "dma_alloc"
  454. +#define DMA_FREE_STR "dma_free"
  455. + if (strncmp(input_str, ALLOC_STR, strlen(ALLOC_STR)) == 0) {
  456. + int alloc_size;
  457. + char *p = input_str + strlen(ALLOC_STR);
  458. +
  459. + while (*p == ' ')
  460. + p++;
  461. + alloc_size = memparse(p, NULL);
  462. + LOG_INFO("/proc/vc-cma: alloc %d", alloc_size);
  463. + if (alloc_size)
  464. + send_vc_msg(VC_CMA_MSG_REQUEST_FREE,
  465. + alloc_size / VC_CMA_CHUNK_SIZE, 0);
  466. + else
  467. + LOG_ERR("invalid size '%s'", p);
  468. + rc = size;
  469. + } else if (strncmp(input_str, FREE_STR, strlen(FREE_STR)) == 0) {
  470. + int alloc_size;
  471. + char *p = input_str + strlen(FREE_STR);
  472. +
  473. + while (*p == ' ')
  474. + p++;
  475. + alloc_size = memparse(p, NULL);
  476. + LOG_INFO("/proc/vc-cma: free %d", alloc_size);
  477. + if (alloc_size)
  478. + send_vc_msg(VC_CMA_MSG_REQUEST_ALLOC,
  479. + alloc_size / VC_CMA_CHUNK_SIZE, 0);
  480. + else
  481. + LOG_ERR("invalid size '%s'", p);
  482. + rc = size;
  483. + } else if (strncmp(input_str, DEBUG_STR, strlen(DEBUG_STR)) == 0) {
  484. + char *p = input_str + strlen(DEBUG_STR);
  485. + while (*p == ' ')
  486. + p++;
  487. + if ((strcmp(p, "on") == 0) || (strcmp(p, "1") == 0))
  488. + vc_cma_debug = 1;
  489. + else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0))
  490. + vc_cma_debug = 0;
  491. + LOG_INFO("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off");
  492. + rc = size;
  493. + } else if (strncmp(input_str, RESERVE_STR, strlen(RESERVE_STR)) == 0) {
  494. + int alloc_size;
  495. + int reserved;
  496. + char *p = input_str + strlen(RESERVE_STR);
  497. + while (*p == ' ')
  498. + p++;
  499. + alloc_size = memparse(p, NULL);
  500. +
  501. + reserved = vc_cma_set_reserve(alloc_size, current->tgid);
  502. + rc = (reserved >= 0) ? size : reserved;
  503. + } else if (strncmp(input_str, DMA_ALLOC_STR, strlen(DMA_ALLOC_STR)) == 0) {
  504. + int alloc_size;
  505. + char *p = input_str + strlen(DMA_ALLOC_STR);
  506. + while (*p == ' ')
  507. + p++;
  508. + alloc_size = memparse(p, NULL);
  509. +
  510. + if (vc_cma_dma_alloc) {
  511. + dma_release_from_contiguous(NULL, vc_cma_dma_alloc,
  512. + vc_cma_dma_size);
  513. + vc_cma_dma_alloc = NULL;
  514. + vc_cma_dma_size = 0;
  515. + }
  516. + vc_cma_dma_alloc = dma_alloc_from_contiguous(NULL, alloc_size, 0);
  517. + vc_cma_dma_size = (vc_cma_dma_alloc ? alloc_size : 0);
  518. + if (vc_cma_dma_alloc)
  519. + LOG_INFO("dma_alloc(%d pages) -> %p", alloc_size, page_address(vc_cma_dma_alloc));
  520. + else
  521. + LOG_ERR("dma_alloc(%d pages) failed", alloc_size);
  522. + rc = size;
  523. + } else if (strncmp(input_str, DMA_FREE_STR, strlen(DMA_FREE_STR)) == 0) {
  524. + if (vc_cma_dma_alloc) {
  525. + dma_release_from_contiguous(NULL, vc_cma_dma_alloc,
  526. + vc_cma_dma_size);
  527. + vc_cma_dma_alloc = NULL;
  528. + vc_cma_dma_size = 0;
  529. + }
  530. + rc = size;
  531. + }
  532. +
  533. +out:
  534. + return rc;
  535. +}
  536. +
  537. +/****************************************************************************
  538. +*
  539. +* File Operations for /proc interface.
  540. +*
  541. +***************************************************************************/
  542. +
  543. +static const struct file_operations vc_cma_proc_fops = {
  544. + .open = vc_cma_proc_open,
  545. + .read = seq_read,
  546. + .write = vc_cma_proc_write,
  547. + .llseek = seq_lseek,
  548. + .release = single_release
  549. +};
  550. +
  551. +static int vc_cma_set_reserve(unsigned int reserve, unsigned int pid)
  552. +{
  553. + struct vc_cma_reserve_user *user = NULL;
  554. + int delta = 0;
  555. + int i;
  556. +
  557. + if (down_interruptible(&vc_cma_reserve_mutex))
  558. + return -ERESTARTSYS;
  559. +
  560. + for (i = 0; i < vc_cma_reserve_count; i++) {
  561. + if (pid == vc_cma_reserve_users[i].pid) {
  562. + user = &vc_cma_reserve_users[i];
  563. + delta = reserve - user->reserve;
  564. + if (reserve)
  565. + user->reserve = reserve;
  566. + else {
  567. + /* Remove this entry by copying downwards */
  568. + while ((i + 1) < vc_cma_reserve_count) {
  569. + user[0].pid = user[1].pid;
  570. + user[0].reserve = user[1].reserve;
  571. + user++;
  572. + i++;
  573. + }
  574. + vc_cma_reserve_count--;
  575. + user = NULL;
  576. + }
  577. + break;
  578. + }
  579. + }
  580. +
  581. + if (reserve && !user) {
  582. + if (vc_cma_reserve_count == VC_CMA_RESERVE_COUNT_MAX) {
  583. + LOG_ERR("vc-cma: Too many reservations - "
  584. + "increase CMA_RESERVE_COUNT_MAX");
  585. + up(&vc_cma_reserve_mutex);
  586. + return -EBUSY;
  587. + }
  588. + user = &vc_cma_reserve_users[vc_cma_reserve_count];
  589. + user->pid = pid;
  590. + user->reserve = reserve;
  591. + delta = reserve;
  592. + vc_cma_reserve_count++;
  593. + }
  594. +
  595. + vc_cma_reserve_total += delta;
  596. +
  597. + send_vc_msg(VC_CMA_MSG_RESERVE,
  598. + vc_cma_reserve_total & 0xffff, vc_cma_reserve_total >> 16);
  599. +
  600. + send_worker_msg((VCHIQ_HEADER_T *) VC_CMA_MSG_UPDATE_RESERVE);
  601. +
  602. + LOG_DBG("/proc/vc-cma: reserve %d (PID %d) - total %u",
  603. + reserve, pid, vc_cma_reserve_total);
  604. +
  605. + up(&vc_cma_reserve_mutex);
  606. +
  607. + return vc_cma_reserve_total;
  608. +}
  609. +
  610. +static VCHIQ_STATUS_T cma_service_callback(VCHIQ_REASON_T reason,
  611. + VCHIQ_HEADER_T * header,
  612. + VCHIQ_SERVICE_HANDLE_T service,
  613. + void *bulk_userdata)
  614. +{
  615. + switch (reason) {
  616. + case VCHIQ_MESSAGE_AVAILABLE:
  617. + if (!send_worker_msg(header))
  618. + return VCHIQ_RETRY;
  619. + break;
  620. + case VCHIQ_SERVICE_CLOSED:
  621. + LOG_DBG("CMA service closed");
  622. + break;
  623. + default:
  624. + LOG_ERR("Unexpected CMA callback reason %d", reason);
  625. + break;
  626. + }
  627. + return VCHIQ_SUCCESS;
  628. +}
  629. +
  630. +static void send_vc_msg(unsigned short type,
  631. + unsigned short param1, unsigned short param2)
  632. +{
  633. + unsigned short msg[] = { type, param1, param2 };
  634. + VCHIQ_ELEMENT_T elem = { &msg, sizeof(msg) };
  635. + VCHIQ_STATUS_T ret;
  636. + vchiq_use_service(cma_service);
  637. + ret = vchiq_queue_message(cma_service, &elem, 1);
  638. + vchiq_release_service(cma_service);
  639. + if (ret != VCHIQ_SUCCESS)
  640. + LOG_ERR("vchiq_queue_message returned %x", ret);
  641. +}
  642. +
  643. +static bool send_worker_msg(VCHIQ_HEADER_T * msg)
  644. +{
  645. + if (down_interruptible(&vc_cma_worker_queue_push_mutex))
  646. + return false;
  647. + vchiu_queue_push(&cma_msg_queue, msg);
  648. + up(&vc_cma_worker_queue_push_mutex);
  649. + return true;
  650. +}
  651. +
  652. +static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply)
  653. +{
  654. + int i;
  655. + for (i = 0; i < num_chunks; i++) {
  656. + struct page *chunk;
  657. + unsigned int chunk_num;
  658. + uint8_t *chunk_addr;
  659. + size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT;
  660. +
  661. + chunk = dma_alloc_from_contiguous(&vc_cma_device.dev,
  662. + PAGES_PER_CHUNK,
  663. + VC_CMA_CHUNK_ORDER);
  664. + if (!chunk)
  665. + break;
  666. +
  667. + chunk_addr = page_address(chunk);
  668. + dmac_flush_range(chunk_addr, chunk_addr + chunk_size);
  669. + outer_inv_range(__pa(chunk_addr), __pa(chunk_addr) +
  670. + chunk_size);
  671. +
  672. + chunk_num =
  673. + (page_to_phys(chunk) - vc_cma_base) / VC_CMA_CHUNK_SIZE;
  674. + BUG_ON(((page_to_phys(chunk) - vc_cma_base) %
  675. + VC_CMA_CHUNK_SIZE) != 0);
  676. + if (chunk_num >= vc_cma_chunks) {
  677. + phys_addr_t _pa = vc_cma_base + vc_cma_size - 1;
  678. + LOG_ERR("%s: ===============================",
  679. + __func__);
  680. + LOG_ERR("%s: chunk phys %x, vc_cma %pa-%pa - "
  681. + "bad SPARSEMEM configuration?",
  682. + __func__, (unsigned int)page_to_phys(chunk),
  683. + &vc_cma_base, &_pa);
  684. + LOG_ERR("%s: dev->cma_area = %p", __func__,
  685. + (void*)0/*vc_cma_device.dev.cma_area*/);
  686. + LOG_ERR("%s: ===============================",
  687. + __func__);
  688. + break;
  689. + }
  690. + reply->params[i] = chunk_num;
  691. + vc_cma_chunks_used++;
  692. + }
  693. +
  694. + if (i < num_chunks) {
  695. + LOG_ERR("%s: dma_alloc_from_contiguous failed "
  696. + "for %x bytes (alloc %d of %d, %d free)",
  697. + __func__, VC_CMA_CHUNK_SIZE, i,
  698. + num_chunks, vc_cma_chunks - vc_cma_chunks_used);
  699. + num_chunks = i;
  700. + }
  701. +
  702. + LOG_DBG("CMA allocated %d chunks -> %d used",
  703. + num_chunks, vc_cma_chunks_used);
  704. + reply->type = VC_CMA_MSG_ALLOCATED;
  705. +
  706. + {
  707. + VCHIQ_ELEMENT_T elem = {
  708. + reply,
  709. + offsetof(struct cma_msg, params[0]) +
  710. + num_chunks * sizeof(reply->params[0])
  711. + };
  712. + VCHIQ_STATUS_T ret;
  713. + vchiq_use_service(cma_service);
  714. + ret = vchiq_queue_message(cma_service, &elem, 1);
  715. + vchiq_release_service(cma_service);
  716. + if (ret != VCHIQ_SUCCESS)
  717. + LOG_ERR("vchiq_queue_message return " "%x", ret);
  718. + }
  719. +
  720. + return num_chunks;
  721. +}
  722. +
  723. +static int cma_worker_proc(void *param)
  724. +{
  725. + static struct cma_msg reply;
  726. + (void)param;
  727. +
  728. + while (1) {
  729. + VCHIQ_HEADER_T *msg;
  730. + static struct cma_msg msg_copy;
  731. + struct cma_msg *cma_msg = &msg_copy;
  732. + int type, msg_size;
  733. +
  734. + msg = vchiu_queue_pop(&cma_msg_queue);
  735. + if ((unsigned int)msg >= VC_CMA_MSG_MAX) {
  736. + msg_size = msg->size;
  737. + memcpy(&msg_copy, msg->data, msg_size);
  738. + type = cma_msg->type;
  739. + vchiq_release_message(cma_service, msg);
  740. + } else {
  741. + msg_size = 0;
  742. + type = (int)msg;
  743. + if (type == VC_CMA_MSG_QUIT)
  744. + break;
  745. + else if (type == VC_CMA_MSG_UPDATE_RESERVE) {
  746. + msg = NULL;
  747. + cma_msg = NULL;
  748. + } else {
  749. + BUG();
  750. + continue;
  751. + }
  752. + }
  753. +
  754. + switch (type) {
  755. + case VC_CMA_MSG_ALLOC:{
  756. + int num_chunks, free_chunks;
  757. + num_chunks = cma_msg->params[0];
  758. + free_chunks =
  759. + vc_cma_chunks - vc_cma_chunks_used;
  760. + LOG_DBG("CMA_MSG_ALLOC(%d chunks)", num_chunks);
  761. + if (num_chunks > VC_CMA_MAX_PARAMS_PER_MSG) {
  762. + LOG_ERR
  763. + ("CMA_MSG_ALLOC - chunk count (%d) "
  764. + "exceeds VC_CMA_MAX_PARAMS_PER_MSG (%d)",
  765. + num_chunks,
  766. + VC_CMA_MAX_PARAMS_PER_MSG);
  767. + num_chunks = VC_CMA_MAX_PARAMS_PER_MSG;
  768. + }
  769. +
  770. + if (num_chunks > free_chunks) {
  771. + LOG_ERR
  772. + ("CMA_MSG_ALLOC - chunk count (%d) "
  773. + "exceeds free chunks (%d)",
  774. + num_chunks, free_chunks);
  775. + num_chunks = free_chunks;
  776. + }
  777. +
  778. + vc_cma_alloc_chunks(num_chunks, &reply);
  779. + }
  780. + break;
  781. +
  782. + case VC_CMA_MSG_FREE:{
  783. + int chunk_count =
  784. + (msg_size -
  785. + offsetof(struct cma_msg,
  786. + params)) /
  787. + sizeof(cma_msg->params[0]);
  788. + int i;
  789. + BUG_ON(chunk_count <= 0);
  790. +
  791. + LOG_DBG("CMA_MSG_FREE(%d chunks - %x, ...)",
  792. + chunk_count, cma_msg->params[0]);
  793. + for (i = 0; i < chunk_count; i++) {
  794. + int chunk_num = cma_msg->params[i];
  795. + struct page *page = vc_cma_base_page +
  796. + chunk_num * PAGES_PER_CHUNK;
  797. + if (chunk_num >= vc_cma_chunks) {
  798. + LOG_ERR
  799. + ("CMA_MSG_FREE - chunk %d of %d"
  800. + " (value %x) exceeds maximum "
  801. + "(%x)", i, chunk_count,
  802. + chunk_num,
  803. + vc_cma_chunks - 1);
  804. + break;
  805. + }
  806. +
  807. + if (!dma_release_from_contiguous
  808. + (&vc_cma_device.dev, page,
  809. + PAGES_PER_CHUNK)) {
  810. + phys_addr_t _pa = page_to_phys(page);
  811. + LOG_ERR
  812. + ("CMA_MSG_FREE - failed to "
  813. + "release chunk %d (phys %pa, "
  814. + "page %x)", chunk_num,
  815. + &_pa,
  816. + (unsigned int)page);
  817. + }
  818. + vc_cma_chunks_used--;
  819. + }
  820. + LOG_DBG("CMA released %d chunks -> %d used",
  821. + i, vc_cma_chunks_used);
  822. + }
  823. + break;
  824. +
  825. + case VC_CMA_MSG_UPDATE_RESERVE:{
  826. + int chunks_needed =
  827. + ((vc_cma_reserve_total + VC_CMA_CHUNK_SIZE -
  828. + 1)
  829. + / VC_CMA_CHUNK_SIZE) -
  830. + vc_cma_chunks_reserved;
  831. +
  832. + LOG_DBG
  833. + ("CMA_MSG_UPDATE_RESERVE(%d chunks needed)",
  834. + chunks_needed);
  835. +
  836. + /* Cap the reservations to what is available */
  837. + if (chunks_needed > 0) {
  838. + if (chunks_needed >
  839. + (vc_cma_chunks -
  840. + vc_cma_chunks_used))
  841. + chunks_needed =
  842. + (vc_cma_chunks -
  843. + vc_cma_chunks_used);
  844. +
  845. + chunks_needed =
  846. + vc_cma_alloc_chunks(chunks_needed,
  847. + &reply);
  848. + }
  849. +
  850. + LOG_DBG
  851. + ("CMA_MSG_UPDATE_RESERVE(%d chunks allocated)",
  852. + chunks_needed);
  853. + vc_cma_chunks_reserved += chunks_needed;
  854. + }
  855. + break;
  856. +
  857. + default:
  858. + LOG_ERR("unexpected msg type %d", type);
  859. + break;
  860. + }
  861. + }
  862. +
  863. + LOG_DBG("quitting...");
  864. + return 0;
  865. +}
  866. +
  867. +/****************************************************************************
  868. +*
  869. +* vc_cma_connected_init
  870. +*
  871. +* This function is called once the videocore has been connected.
  872. +*
  873. +***************************************************************************/
  874. +
  875. +static void vc_cma_connected_init(void)
  876. +{
  877. + VCHIQ_SERVICE_PARAMS_T service_params;
  878. +
  879. + LOG_DBG("vc_cma_connected_init");
  880. +
  881. + if (!vchiu_queue_init(&cma_msg_queue, 16)) {
  882. + LOG_ERR("could not create CMA msg queue");
  883. + goto fail_queue;
  884. + }
  885. +
  886. + if (vchiq_initialise(&cma_instance) != VCHIQ_SUCCESS)
  887. + goto fail_vchiq_init;
  888. +
  889. + vchiq_connect(cma_instance);
  890. +
  891. + service_params.fourcc = VC_CMA_FOURCC;
  892. + service_params.callback = cma_service_callback;
  893. + service_params.userdata = NULL;
  894. + service_params.version = VC_CMA_VERSION;
  895. + service_params.version_min = VC_CMA_VERSION;
  896. +
  897. + if (vchiq_open_service(cma_instance, &service_params,
  898. + &cma_service) != VCHIQ_SUCCESS) {
  899. + LOG_ERR("failed to open service - already in use?");
  900. + goto fail_vchiq_open;
  901. + }
  902. +
  903. + vchiq_release_service(cma_service);
  904. +
  905. + cma_worker = kthread_create(cma_worker_proc, NULL, "cma_worker");
  906. + if (!cma_worker) {
  907. + LOG_ERR("could not create CMA worker thread");
  908. + goto fail_worker;
  909. + }
  910. + set_user_nice(cma_worker, -20);
  911. + wake_up_process(cma_worker);
  912. +
  913. + return;
  914. +
  915. +fail_worker:
  916. + vchiq_close_service(cma_service);
  917. +fail_vchiq_open:
  918. + vchiq_shutdown(cma_instance);
  919. +fail_vchiq_init:
  920. + vchiu_queue_delete(&cma_msg_queue);
  921. +fail_queue:
  922. + return;
  923. +}
  924. +
  925. +void
  926. +loud_error_header(void)
  927. +{
  928. + if (in_loud_error)
  929. + return;
  930. +
  931. + LOG_ERR("============================================================"
  932. + "================");
  933. + LOG_ERR("============================================================"
  934. + "================");
  935. + LOG_ERR("=====");
  936. +
  937. + in_loud_error = 1;
  938. +}
  939. +
  940. +void
  941. +loud_error_footer(void)
  942. +{
  943. + if (!in_loud_error)
  944. + return;
  945. +
  946. + LOG_ERR("=====");
  947. + LOG_ERR("============================================================"
  948. + "================");
  949. + LOG_ERR("============================================================"
  950. + "================");
  951. +
  952. + in_loud_error = 0;
  953. +}
  954. +
  955. +#if 1
  956. +static int check_cma_config(void) { return 1; }
  957. +#else
  958. +static int
  959. +read_vc_debug_var(VC_MEM_ACCESS_HANDLE_T handle,
  960. + const char *symbol,
  961. + void *buf, size_t bufsize)
  962. +{
  963. + VC_MEM_ADDR_T vcMemAddr;
  964. + size_t vcMemSize;
  965. + uint8_t *mapAddr;
  966. + off_t vcMapAddr;
  967. +
  968. + if (!LookupVideoCoreSymbol(handle, symbol,
  969. + &vcMemAddr,
  970. + &vcMemSize)) {
  971. + loud_error_header();
  972. + loud_error(
  973. + "failed to find VC symbol \"%s\".",
  974. + symbol);
  975. + loud_error_footer();
  976. + return 0;
  977. + }
  978. +
  979. + if (vcMemSize != bufsize) {
  980. + loud_error_header();
  981. + loud_error(
  982. + "VC symbol \"%s\" is the wrong size.",
  983. + symbol);
  984. + loud_error_footer();
  985. + return 0;
  986. + }
  987. +
  988. + vcMapAddr = (off_t)vcMemAddr & VC_MEM_TO_ARM_ADDR_MASK;
  989. + vcMapAddr += mm_vc_mem_phys_addr;
  990. + mapAddr = ioremap_nocache(vcMapAddr, vcMemSize);
  991. + if (mapAddr == 0) {
  992. + loud_error_header();
  993. + loud_error(
  994. + "failed to ioremap \"%s\" @ 0x%x "
  995. + "(phys: 0x%x, size: %u).",
  996. + symbol,
  997. + (unsigned int)vcMapAddr,
  998. + (unsigned int)vcMemAddr,
  999. + (unsigned int)vcMemSize);
  1000. + loud_error_footer();
  1001. + return 0;
  1002. + }
  1003. +
  1004. + memcpy(buf, mapAddr, bufsize);
  1005. + iounmap(mapAddr);
  1006. +
  1007. + return 1;
  1008. +}
  1009. +
  1010. +
  1011. +static int
  1012. +check_cma_config(void)
  1013. +{
  1014. + VC_MEM_ACCESS_HANDLE_T mem_hndl;
  1015. + VC_MEM_ADDR_T mempool_start;
  1016. + VC_MEM_ADDR_T mempool_end;
  1017. + VC_MEM_ADDR_T mempool_offline_start;
  1018. + VC_MEM_ADDR_T mempool_offline_end;
  1019. + VC_MEM_ADDR_T cam_alloc_base;
  1020. + VC_MEM_ADDR_T cam_alloc_size;
  1021. + VC_MEM_ADDR_T cam_alloc_end;
  1022. + int success = 0;
  1023. +
  1024. + if (OpenVideoCoreMemory(&mem_hndl) != 0)
  1025. + goto out;
  1026. +
  1027. + /* Read the relevant VideoCore variables */
  1028. + if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_START",
  1029. + &mempool_start,
  1030. + sizeof(mempool_start)))
  1031. + goto close;
  1032. +
  1033. + if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_END",
  1034. + &mempool_end,
  1035. + sizeof(mempool_end)))
  1036. + goto close;
  1037. +
  1038. + if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_OFFLINE_START",
  1039. + &mempool_offline_start,
  1040. + sizeof(mempool_offline_start)))
  1041. + goto close;
  1042. +
  1043. + if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_OFFLINE_END",
  1044. + &mempool_offline_end,
  1045. + sizeof(mempool_offline_end)))
  1046. + goto close;
  1047. +
  1048. + if (!read_vc_debug_var(mem_hndl, "cam_alloc_base",
  1049. + &cam_alloc_base,
  1050. + sizeof(cam_alloc_base)))
  1051. + goto close;
  1052. +
  1053. + if (!read_vc_debug_var(mem_hndl, "cam_alloc_size",
  1054. + &cam_alloc_size,
  1055. + sizeof(cam_alloc_size)))
  1056. + goto close;
  1057. +
  1058. + cam_alloc_end = cam_alloc_base + cam_alloc_size;
  1059. +
  1060. + success = 1;
  1061. +
  1062. + /* Now the sanity checks */
  1063. + if (!mempool_offline_start)
  1064. + mempool_offline_start = mempool_start;
  1065. + if (!mempool_offline_end)
  1066. + mempool_offline_end = mempool_end;
  1067. +
  1068. + if (VCADDR_TO_PHYSADDR(mempool_offline_start) != vc_cma_base) {
  1069. + loud_error_header();
  1070. + loud_error(
  1071. + "__MEMPOOL_OFFLINE_START(%x -> %lx) doesn't match "
  1072. + "vc_cma_base(%x)",
  1073. + mempool_offline_start,
  1074. + VCADDR_TO_PHYSADDR(mempool_offline_start),
  1075. + vc_cma_base);
  1076. + success = 0;
  1077. + }
  1078. +
  1079. + if (VCADDR_TO_PHYSADDR(mempool_offline_end) !=
  1080. + (vc_cma_base + vc_cma_size)) {
  1081. + loud_error_header();
  1082. + loud_error(
  1083. + "__MEMPOOL_OFFLINE_END(%x -> %lx) doesn't match "
  1084. + "vc_cma_base(%x) + vc_cma_size(%x) = %x",
  1085. + mempool_offline_start,
  1086. + VCADDR_TO_PHYSADDR(mempool_offline_end),
  1087. + vc_cma_base, vc_cma_size, vc_cma_base + vc_cma_size);
  1088. + success = 0;
  1089. + }
  1090. +
  1091. + if (mempool_end < mempool_start) {
  1092. + loud_error_header();
  1093. + loud_error(
  1094. + "__MEMPOOL_END(%x) must not be before "
  1095. + "__MEMPOOL_START(%x)",
  1096. + mempool_end,
  1097. + mempool_start);
  1098. + success = 0;
  1099. + }
  1100. +
  1101. + if (mempool_offline_end < mempool_offline_start) {
  1102. + loud_error_header();
  1103. + loud_error(
  1104. + "__MEMPOOL_OFFLINE_END(%x) must not be before "
  1105. + "__MEMPOOL_OFFLINE_START(%x)",
  1106. + mempool_offline_end,
  1107. + mempool_offline_start);
  1108. + success = 0;
  1109. + }
  1110. +
  1111. + if (mempool_offline_start < mempool_start) {
  1112. + loud_error_header();
  1113. + loud_error(
  1114. + "__MEMPOOL_OFFLINE_START(%x) must not be before "
  1115. + "__MEMPOOL_START(%x)",
  1116. + mempool_offline_start,
  1117. + mempool_start);
  1118. + success = 0;
  1119. + }
  1120. +
  1121. + if (mempool_offline_end > mempool_end) {
  1122. + loud_error_header();
  1123. + loud_error(
  1124. + "__MEMPOOL_OFFLINE_END(%x) must not be after "
  1125. + "__MEMPOOL_END(%x)",
  1126. + mempool_offline_end,
  1127. + mempool_end);
  1128. + success = 0;
  1129. + }
  1130. +
  1131. + if ((cam_alloc_base < mempool_end) &&
  1132. + (cam_alloc_end > mempool_start)) {
  1133. + loud_error_header();
  1134. + loud_error(
  1135. + "cam_alloc pool(%x-%x) overlaps "
  1136. + "mempool(%x-%x)",
  1137. + cam_alloc_base, cam_alloc_end,
  1138. + mempool_start, mempool_end);
  1139. + success = 0;
  1140. + }
  1141. +
  1142. + loud_error_footer();
  1143. +
  1144. +close:
  1145. + CloseVideoCoreMemory(mem_hndl);
  1146. +
  1147. +out:
  1148. + return success;
  1149. +}
  1150. +#endif
  1151. +
  1152. +static int vc_cma_init(void)
  1153. +{
  1154. + int rc = -EFAULT;
  1155. + struct device *dev;
  1156. +
  1157. + if (!check_cma_config())
  1158. + goto out_release;
  1159. +
  1160. + LOG_INFO("vc-cma: Videocore CMA driver");
  1161. + LOG_INFO("vc-cma: vc_cma_base = %pa", &vc_cma_base);
  1162. + LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)",
  1163. + vc_cma_size, vc_cma_size / (1024 * 1024));
  1164. + LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)",
  1165. + vc_cma_initial, vc_cma_initial / (1024 * 1024));
  1166. +
  1167. + vc_cma_base_page = phys_to_page(vc_cma_base);
  1168. +
  1169. + if (vc_cma_chunks) {
  1170. + int chunks_needed = vc_cma_initial / VC_CMA_CHUNK_SIZE;
  1171. +
  1172. + for (vc_cma_chunks_used = 0;
  1173. + vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) {
  1174. + struct page *chunk;
  1175. + chunk = dma_alloc_from_contiguous(&vc_cma_device.dev,
  1176. + PAGES_PER_CHUNK,
  1177. + VC_CMA_CHUNK_ORDER);
  1178. + if (!chunk)
  1179. + break;
  1180. + BUG_ON(((page_to_phys(chunk) - vc_cma_base) %
  1181. + VC_CMA_CHUNK_SIZE) != 0);
  1182. + }
  1183. + if (vc_cma_chunks_used != chunks_needed) {
  1184. + LOG_ERR("%s: dma_alloc_from_contiguous failed (%d "
  1185. + "bytes, allocation %d of %d)",
  1186. + __func__, VC_CMA_CHUNK_SIZE,
  1187. + vc_cma_chunks_used, chunks_needed);
  1188. + goto out_release;
  1189. + }
  1190. +
  1191. + vchiq_add_connected_callback(vc_cma_connected_init);
  1192. + }
  1193. +
  1194. + rc = alloc_chrdev_region(&vc_cma_devnum, 0, 1, DRIVER_NAME);
  1195. + if (rc < 0) {
  1196. + LOG_ERR("%s: alloc_chrdev_region failed (rc=%d)", __func__, rc);
  1197. + goto out_release;
  1198. + }
  1199. +
  1200. + cdev_init(&vc_cma_cdev, &vc_cma_fops);
  1201. + rc = cdev_add(&vc_cma_cdev, vc_cma_devnum, 1);
  1202. + if (rc != 0) {
  1203. + LOG_ERR("%s: cdev_add failed (rc=%d)", __func__, rc);
  1204. + goto out_unregister;
  1205. + }
  1206. +
  1207. + vc_cma_class = class_create(THIS_MODULE, DRIVER_NAME);
  1208. + if (IS_ERR(vc_cma_class)) {
  1209. + rc = PTR_ERR(vc_cma_class);
  1210. + LOG_ERR("%s: class_create failed (rc=%d)", __func__, rc);
  1211. + goto out_cdev_del;
  1212. + }
  1213. +
  1214. + dev = device_create(vc_cma_class, NULL, vc_cma_devnum, NULL,
  1215. + DRIVER_NAME);
  1216. + if (IS_ERR(dev)) {
  1217. + rc = PTR_ERR(dev);
  1218. + LOG_ERR("%s: device_create failed (rc=%d)", __func__, rc);
  1219. + goto out_class_destroy;
  1220. + }
  1221. +
  1222. + vc_cma_proc_entry = proc_create(DRIVER_NAME, 0444, NULL, &vc_cma_proc_fops);
  1223. + if (vc_cma_proc_entry == NULL) {
  1224. + rc = -EFAULT;
  1225. + LOG_ERR("%s: proc_create failed", __func__);
  1226. + goto out_device_destroy;
  1227. + }
  1228. +
  1229. + vc_cma_inited = 1;
  1230. + return 0;
  1231. +
  1232. +out_device_destroy:
  1233. + device_destroy(vc_cma_class, vc_cma_devnum);
  1234. +
  1235. +out_class_destroy:
  1236. + class_destroy(vc_cma_class);
  1237. + vc_cma_class = NULL;
  1238. +
  1239. +out_cdev_del:
  1240. + cdev_del(&vc_cma_cdev);
  1241. +
  1242. +out_unregister:
  1243. + unregister_chrdev_region(vc_cma_devnum, 1);
  1244. +
  1245. +out_release:
  1246. + /* It is tempting to try to clean up by calling
  1247. + dma_release_from_contiguous for all allocated chunks, but it isn't
  1248. + a very safe thing to do. If vc_cma_initial is non-zero it is because
  1249. + VideoCore is already using that memory, so giving it back to Linux
  1250. + is likely to be fatal.
  1251. + */
  1252. + return -1;
  1253. +}
  1254. +
  1255. +/****************************************************************************
  1256. +*
  1257. +* vc_cma_exit
  1258. +*
  1259. +***************************************************************************/
  1260. +
  1261. +static void __exit vc_cma_exit(void)
  1262. +{
  1263. + LOG_DBG("%s: called", __func__);
  1264. +
  1265. + if (vc_cma_inited) {
  1266. + remove_proc_entry(DRIVER_NAME, NULL);
  1267. + device_destroy(vc_cma_class, vc_cma_devnum);
  1268. + class_destroy(vc_cma_class);
  1269. + cdev_del(&vc_cma_cdev);
  1270. + unregister_chrdev_region(vc_cma_devnum, 1);
  1271. + }
  1272. +}
  1273. +
  1274. +module_init(vc_cma_init);
  1275. +module_exit(vc_cma_exit);
  1276. +MODULE_LICENSE("GPL");
  1277. +MODULE_AUTHOR("Broadcom Corporation");
  1278. --- /dev/null
  1279. +++ b/include/linux/broadcom/vc_cma.h
  1280. @@ -0,0 +1,36 @@
  1281. +/*****************************************************************************
  1282. +* Copyright 2012 Broadcom Corporation. All rights reserved.
  1283. +*
  1284. +* Unless you and Broadcom execute a separate written software license
  1285. +* agreement governing use of this software, this software is licensed to you
  1286. +* under the terms of the GNU General Public License version 2, available at
  1287. +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
  1288. +*
  1289. +* Notwithstanding the above, under no circumstances may you combine this
  1290. +* software in any way with any other Broadcom software provided under a
  1291. +* license other than the GPL, without Broadcom's express prior written
  1292. +* consent.
  1293. +*****************************************************************************/
  1294. +
  1295. +#if !defined( VC_CMA_H )
  1296. +#define VC_CMA_H
  1297. +
  1298. +#include <linux/ioctl.h>
  1299. +
  1300. +#define VC_CMA_IOC_MAGIC 0xc5
  1301. +
  1302. +#define VC_CMA_IOC_RESERVE _IO(VC_CMA_IOC_MAGIC, 0)
  1303. +
  1304. +#ifdef __KERNEL__
  1305. +
  1306. +#ifdef CONFIG_BCM_VC_CMA
  1307. +void vc_cma_early_init(void);
  1308. +void vc_cma_reserve(void);
  1309. +#else
  1310. +static inline void vc_cma_early_init(void) { }
  1311. +static inline void vc_cma_reserve(void) { }
  1312. +#endif
  1313. +
  1314. +#endif
  1315. +
  1316. +#endif /* VC_CMA_H */