blob: eb9c38872d623e714851cd613a12460fb1887a4d [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/qdsp5/adsp_driver.c
2 *
3 * Copyright (C) 2008 Google, Inc.
Duy Truong790f06d2013-02-13 16:38:12 -08004 * Copyright (c) 2009, 2012 The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 * Author: Iliyan Malchev <ibm@android.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/cdev.h>
19#include <linux/fs.h>
20#include <linux/list.h>
21#include <linux/platform_device.h>
22#include <linux/sched.h>
23#include <linux/uaccess.h>
24#include <linux/slab.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070025#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include "adsp.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027#include <linux/msm_adsp.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include <mach/debug_mm.h>
29
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053030struct adsp_ion_info {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031 int fd;
32 void *vaddr;
33};
34
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053035struct adsp_ion_region {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036 struct hlist_node list;
37 void *vaddr;
38 unsigned long paddr;
39 unsigned long kvaddr;
40 unsigned long len;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053041 unsigned long ion_flag;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042 struct file *file;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053043 struct ion_handle *handle;
44 struct ion_client *client;
45 int fd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046};
47
48struct adsp_device {
49 struct msm_adsp_module *module;
50
51 spinlock_t event_queue_lock;
52 wait_queue_head_t event_wait;
53 struct list_head event_queue;
54 int abort;
55
56 const char *name;
57 struct device *device;
58 struct cdev cdev;
59};
60
61static struct adsp_device *inode_to_device(struct inode *inode);
62
63#define __CONTAINS(r, v, l) ({ \
64 typeof(r) __r = r; \
65 typeof(v) __v = v; \
66 typeof(v) __e = __v + l; \
67 int res = __v >= __r->vaddr && \
68 __e <= __r->vaddr + __r->len; \
69 res; \
70})
71
72#define CONTAINS(r1, r2) ({ \
73 typeof(r2) __r2 = r2; \
74 __CONTAINS(r1, __r2->vaddr, __r2->len); \
75})
76
77#define IN_RANGE(r, v) ({ \
78 typeof(r) __r = r; \
79 typeof(v) __vv = v; \
80 int res = ((__vv >= __r->vaddr) && \
81 (__vv < (__r->vaddr + __r->len))); \
82 res; \
83})
84
85#define OVERLAPS(r1, r2) ({ \
86 typeof(r1) __r1 = r1; \
87 typeof(r2) __r2 = r2; \
88 typeof(__r2->vaddr) __v = __r2->vaddr; \
89 typeof(__v) __e = __v + __r2->len - 1; \
90 int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
91 res; \
92})
93
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053094static int adsp_ion_check(struct msm_adsp_module *module,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 void *vaddr, unsigned long len)
96{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053097 struct adsp_ion_region *region_elt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 struct hlist_node *node;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053099 struct adsp_ion_region t = { .vaddr = vaddr, .len = len };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530101 hlist_for_each_entry(region_elt, node, &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102 if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
103 OVERLAPS(region_elt, &t)) {
104 MM_ERR("module %s:"
105 " region (vaddr %p len %ld)"
106 " clashes with registered region"
107 " (vaddr %p paddr %p len %ld)\n",
108 module->name,
109 vaddr, len,
110 region_elt->vaddr,
111 (void *)region_elt->paddr,
112 region_elt->len);
113 return -EINVAL;
114 }
115 }
116
117 return 0;
118}
119
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530120static int get_ion_region_info(int fd, struct adsp_ion_region *region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530122 unsigned long ionflag;
123 void *temp_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 int rc = -EINVAL;
125
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530126 region->client = msm_ion_client_create(UINT_MAX, "Video_Client");
127 if (IS_ERR_OR_NULL(region->client)) {
128 pr_err("Unable to create ION client\n");
129 goto client_error;
130 }
131 region->handle = ion_import_dma_buf(region->client, fd);
132 if (IS_ERR_OR_NULL(region->handle)) {
133 pr_err("%s: could not get handle of the given fd\n", __func__);
134 goto import_error;
135 }
136 rc = ion_handle_get_flags(region->client, region->handle, &ionflag);
137 if (rc) {
138 pr_err("%s: could not get flags for the handle\n", __func__);
139 goto flag_error;
140 }
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700141 temp_ptr = ion_map_kernel(region->client, region->handle);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530142 if (IS_ERR_OR_NULL(temp_ptr)) {
143 pr_err("%s: could not get virtual address\n", __func__);
144 goto map_error;
145 }
146 region->kvaddr = (unsigned long) temp_ptr;
147 region->ion_flag = (unsigned long) ionflag;
148
149 rc = ion_phys(region->client, region->handle, &region->paddr,
150 (size_t *)(&region->len));
151 if (rc) {
152 pr_err("%s: could not get physical address\n", __func__);
153 goto ion_error;
154 }
155 return rc;
156ion_error:
157 ion_unmap_kernel(region->client, region->handle);
158map_error:
159 ion_free(region->client, region->handle);
160flag_error:
161import_error:
162 ion_client_destroy(region->client);
163client_error:
164 return -EINVAL;
165}
166
167static void free_ion_region(struct ion_client *client,
168 struct ion_handle *handle)
169{
170 ion_unmap_kernel(client, handle);
171 ion_free(client, handle);
172 ion_client_destroy(client);
173}
174
175static int adsp_ion_add(struct msm_adsp_module *module,
176 struct adsp_ion_info *info)
177{
178 struct adsp_ion_region *region;
179 int rc = -EINVAL;
180 mutex_lock(&module->ion_regions_lock);
181 region = kmalloc(sizeof(struct adsp_ion_region), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182 if (!region) {
183 rc = -ENOMEM;
184 goto end;
185 }
186 INIT_HLIST_NODE(&region->list);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530187 if (get_ion_region_info(info->fd, region)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188 kfree(region);
189 goto end;
190 }
191
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530192 rc = adsp_ion_check(module, info->vaddr, region->len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 if (rc < 0) {
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530194 free_ion_region(region->client, region->handle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 kfree(region);
196 goto end;
197 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 region->vaddr = info->vaddr;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530199 region->fd = info->fd;
200 region->file = NULL;
201 MM_INFO("adsp_ion_add: module %s: fd %d, vaddr Ox%x, len %d\n",
202 module->name, region->fd, (unsigned int)region->vaddr,
203 (int)region->len);
204 hlist_add_head(&region->list, &module->ion_regions);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205end:
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530206 mutex_unlock(&module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 return rc;
208}
209
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530210static int adsp_ion_lookup_vaddr(struct msm_adsp_module *module, void **addr,
211 unsigned long len, struct adsp_ion_region **region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212{
213 struct hlist_node *node;
214 void *vaddr = *addr;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530215 struct adsp_ion_region *region_elt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216
217 int match_count = 0;
218
219 *region = NULL;
220
221 /* returns physical address or zero */
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530222 hlist_for_each_entry(region_elt, node, &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 if (vaddr >= region_elt->vaddr &&
224 vaddr < region_elt->vaddr + region_elt->len &&
225 vaddr + len <= region_elt->vaddr + region_elt->len) {
226 /* offset since we could pass vaddr inside a registerd
227 * pmem buffer
228 */
229
230 match_count++;
231 if (!*region)
232 *region = region_elt;
233 }
234 }
235
236 if (match_count > 1) {
237 MM_ERR("module %s: "
238 "multiple hits for vaddr %p, len %ld\n",
239 module->name, vaddr, len);
240 hlist_for_each_entry(region_elt, node,
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530241 &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 if (vaddr >= region_elt->vaddr &&
243 vaddr < region_elt->vaddr + region_elt->len &&
244 vaddr + len <= region_elt->vaddr + region_elt->len)
245 MM_ERR("%p, %ld --> %p\n",
246 region_elt->vaddr,
247 region_elt->len,
248 (void *)region_elt->paddr);
249 }
250 }
251
252 return *region ? 0 : -1;
253}
254
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530255int adsp_ion_do_cache_op(struct msm_adsp_module *module,
256 void *addr, void *paddr, unsigned long len,
257 unsigned long offset, int cmd)
258{
259 struct adsp_ion_region *region;
260 void *vaddr = addr;
261 int ret;
262 ret = adsp_ion_lookup_vaddr(module, &vaddr, len, &region);
263 if (ret) {
264 MM_ERR("not patching %s (paddr & kvaddr)," \
265 " lookup (%p, %ld) failed\n",
266 module->name, vaddr, len);
267 return ret;
268 }
Mitchel Humpherys76b9b4d2012-09-17 14:33:22 -0700269 if ((region->ion_flag == ION_FLAG_CACHED) && region->handle) {
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530270 len = ((((len) + 31) & (~31)) + 32);
271 ret = msm_ion_do_cache_op(region->client, region->handle,
272 (void *)paddr, len, cmd);
273 }
274 return ret;
275}
276int adsp_ion_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 unsigned long *kvaddr, unsigned long len,
278 struct file **filp, unsigned long *offset)
279{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530280 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 void *vaddr = *addr;
282 unsigned long *paddr = (unsigned long *)addr;
283 int ret;
284
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530285 ret = adsp_ion_lookup_vaddr(module, addr, len, &region);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286 if (ret) {
287 MM_ERR("not patching %s (paddr & kvaddr),"
288 " lookup (%p, %ld) failed\n",
289 module->name, vaddr, len);
290 return ret;
291 }
292 *paddr = region->paddr + (vaddr - region->vaddr);
293 *kvaddr = region->kvaddr + (vaddr - region->vaddr);
294 if (filp)
295 *filp = region->file;
296 if (offset)
297 *offset = vaddr - region->vaddr;
298 return 0;
299}
300
301int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
302 unsigned long len)
303{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530304 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 void *vaddr = *addr;
306 unsigned long *paddr = (unsigned long *)addr;
307 int ret;
308
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530309 ret = adsp_ion_lookup_vaddr(module, addr, len, &region);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 if (ret) {
311 MM_ERR("not patching %s, lookup (%p, %ld) failed\n",
312 module->name, vaddr, len);
313 return ret;
314 }
315
316 *paddr = region->paddr + (vaddr - region->vaddr);
317 return 0;
318}
319
320static int adsp_verify_cmd(struct msm_adsp_module *module,
321 unsigned int queue_id, void *cmd_data,
322 size_t cmd_size)
323{
324 /* call the per module verifier */
325 if (module->verify_cmd)
326 return module->verify_cmd(module, queue_id, cmd_data,
327 cmd_size);
328 else
329 MM_INFO("no packet verifying function "
330 "for task %s\n", module->name);
331 return 0;
332}
333
334static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
335{
336 struct adsp_command_t cmd;
337 unsigned char buf[256];
338 void *cmd_data;
339 long rc;
340
341 if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
342 return -EFAULT;
343
344 if (cmd.len > 256) {
345 cmd_data = kmalloc(cmd.len, GFP_USER);
346 if (!cmd_data)
347 return -ENOMEM;
348 } else {
349 cmd_data = buf;
350 }
351
352 if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
353 rc = -EFAULT;
354 goto end;
355 }
356
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530357 mutex_lock(&adev->module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
359 MM_ERR("module %s: verify failed.\n", adev->module->name);
360 rc = -EINVAL;
361 goto end;
362 }
363 /* complete the writes to the buffer */
364 wmb();
365 rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
366end:
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530367 mutex_unlock(&adev->module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368
369 if (cmd.len > 256)
370 kfree(cmd_data);
371
372 return rc;
373}
374
375static int adsp_events_pending(struct adsp_device *adev)
376{
377 unsigned long flags;
378 int yes;
379 spin_lock_irqsave(&adev->event_queue_lock, flags);
380 yes = !list_empty(&adev->event_queue);
381 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
382 return yes || adev->abort;
383}
384
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530385static int adsp_ion_lookup_paddr(struct msm_adsp_module *module, void **addr,
386 struct adsp_ion_region **region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387{
388 struct hlist_node *node;
389 unsigned long paddr = (unsigned long)(*addr);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530390 struct adsp_ion_region *region_elt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530392 hlist_for_each_entry(region_elt, node, &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393 if (paddr >= region_elt->paddr &&
394 paddr < region_elt->paddr + region_elt->len) {
395 *region = region_elt;
396 return 0;
397 }
398 }
399 return -1;
400}
401
402int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
403{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530404 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405 unsigned long paddr = (unsigned long)(*addr);
406 unsigned long *vaddr = (unsigned long *)addr;
407 int ret;
408
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530409 ret = adsp_ion_lookup_paddr(module, addr, &region);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410 if (ret) {
411 MM_ERR("not patching %s, paddr %p lookup failed\n",
412 module->name, vaddr);
413 return ret;
414 }
415
416 *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
417 return 0;
418}
419
420static int adsp_patch_event(struct msm_adsp_module *module,
421 struct adsp_event *event)
422{
423 /* call the per-module msg verifier */
424 if (module->patch_event)
425 return module->patch_event(module, event);
426 return 0;
427}
428
429static long adsp_get_event(struct adsp_device *adev, void __user *arg)
430{
431 unsigned long flags;
432 struct adsp_event *data = NULL;
433 struct adsp_event_t evt;
434 int timeout;
435 long rc = 0;
436
437 if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
438 return -EFAULT;
439
440 timeout = (int)evt.timeout_ms;
441
442 if (timeout > 0) {
443 rc = wait_event_interruptible_timeout(
444 adev->event_wait, adsp_events_pending(adev),
445 msecs_to_jiffies(timeout));
446 if (rc == 0)
447 return -ETIMEDOUT;
448 } else {
449 rc = wait_event_interruptible(
450 adev->event_wait, adsp_events_pending(adev));
451 }
452 if (rc < 0)
453 return rc;
454
455 if (adev->abort)
456 return -ENODEV;
457
458 spin_lock_irqsave(&adev->event_queue_lock, flags);
459 if (!list_empty(&adev->event_queue)) {
460 data = list_first_entry(&adev->event_queue,
461 struct adsp_event, list);
462 list_del(&data->list);
463 }
464 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
465
466 if (!data)
467 return -EAGAIN;
468
469 /* DSP messages are type 0; they may contain physical addresses */
470 if (data->type == 0)
471 adsp_patch_event(adev->module, data);
472
473 /* map adsp_event --> adsp_event_t */
474 if (evt.len < data->size) {
475 rc = -ETOOSMALL;
476 goto end;
477 }
478 /* order the reads to the buffer */
479 rmb();
480 if (data->msg_id != EVENT_MSG_ID) {
481 if (copy_to_user((void *)(evt.data), data->data.msg16,
482 data->size)) {
483 rc = -EFAULT;
484 goto end;
485 }
486 } else {
487 if (copy_to_user((void *)(evt.data), data->data.msg32,
488 data->size)) {
489 rc = -EFAULT;
490 goto end;
491 }
492 }
493
494 evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
495 evt.msg_id = data->msg_id;
496 evt.flags = data->is16;
497 evt.len = data->size;
498 if (copy_to_user(arg, &evt, sizeof(evt)))
499 rc = -EFAULT;
500end:
501 kfree(data);
502 return rc;
503}
504
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530505static int adsp_ion_del(struct msm_adsp_module *module)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506{
507 struct hlist_node *node, *tmp;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530508 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530510 mutex_lock(&module->ion_regions_lock);
511 hlist_for_each_safe(node, tmp, &module->ion_regions) {
512 region = hlist_entry(node, struct adsp_ion_region, list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 hlist_del(node);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530514 MM_INFO("adsp_ion_del: module %s: fd %d, vaddr Ox%x, len %d\n",
515 module->name, region->fd, (unsigned int)region->vaddr,
516 (int)region->len);
517 free_ion_region(region->client, region->handle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 kfree(region);
519 }
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530520 mutex_unlock(&module->ion_regions_lock);
521 BUG_ON(!hlist_empty(&module->ion_regions));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522
523 return 0;
524}
525
526static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
527{
528 struct adsp_device *adev = filp->private_data;
529
530 switch (cmd) {
531 case ADSP_IOCTL_ENABLE:
532 return msm_adsp_enable(adev->module);
533
534 case ADSP_IOCTL_DISABLE:
535 return msm_adsp_disable(adev->module);
536
537 case ADSP_IOCTL_DISABLE_EVENT_RSP:
538 return msm_adsp_disable_event_rsp(adev->module);
539
540 case ADSP_IOCTL_DISABLE_ACK:
541 MM_ERR("ADSP_IOCTL_DISABLE_ACK is not implemented\n");
542 break;
543
544 case ADSP_IOCTL_WRITE_COMMAND:
545 return adsp_write_cmd(adev, (void __user *) arg);
546
547 case ADSP_IOCTL_GET_EVENT:
548 return adsp_get_event(adev, (void __user *) arg);
549
550 case ADSP_IOCTL_SET_CLKRATE: {
551 unsigned long clk_rate;
552 if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
553 return -EFAULT;
554 return adsp_set_clkrate(adev->module, clk_rate);
555 }
556
557 case ADSP_IOCTL_REGISTER_PMEM: {
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530558 struct adsp_ion_info info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559 if (copy_from_user(&info, (void *) arg, sizeof(info)))
560 return -EFAULT;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530561 return adsp_ion_add(adev->module, &info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700562 }
563
564 case ADSP_IOCTL_ABORT_EVENT_READ:
565 adev->abort = 1;
566 wake_up(&adev->event_wait);
567 break;
568
569 case ADSP_IOCTL_UNREGISTER_PMEM:
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530570 return adsp_ion_del(adev->module);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571
572 default:
573 break;
574 }
575 return -EINVAL;
576}
577
578static int adsp_release(struct inode *inode, struct file *filp)
579{
580 struct adsp_device *adev = filp->private_data;
581 struct msm_adsp_module *module = adev->module;
582 int rc = 0;
583
584 MM_INFO("release '%s'\n", adev->name);
585
586 /* clear module before putting it to avoid race with open() */
587 adev->module = NULL;
588
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530589 rc = adsp_ion_del(module);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590
591 msm_adsp_put(module);
592 return rc;
593}
594
595static void adsp_event(void *driver_data, unsigned id, size_t len,
596 void (*getevent)(void *ptr, size_t len))
597{
598 struct adsp_device *adev = driver_data;
599 struct adsp_event *event;
600 unsigned long flags;
601
602 if (len > ADSP_EVENT_MAX_SIZE) {
603 MM_ERR("event too large (%d bytes)\n", len);
604 return;
605 }
606
607 event = kmalloc(sizeof(*event), GFP_ATOMIC);
608 if (!event) {
609 MM_ERR("cannot allocate buffer\n");
610 return;
611 }
612
613 if (id != EVENT_MSG_ID) {
614 event->type = 0;
615 event->is16 = 0;
616 event->msg_id = id;
617 event->size = len;
618
619 getevent(event->data.msg16, len);
620 } else {
621 event->type = 1;
622 event->is16 = 1;
623 event->msg_id = id;
624 event->size = len;
625 getevent(event->data.msg32, len);
626 }
627
628 spin_lock_irqsave(&adev->event_queue_lock, flags);
629 list_add_tail(&event->list, &adev->event_queue);
630 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
631 wake_up(&adev->event_wait);
632}
633
634static struct msm_adsp_ops adsp_ops = {
635 .event = adsp_event,
636};
637
638static int adsp_open(struct inode *inode, struct file *filp)
639{
640 struct adsp_device *adev;
641 int rc;
642
643 rc = nonseekable_open(inode, filp);
644 if (rc < 0)
645 return rc;
646
647 adev = inode_to_device(inode);
648 if (!adev)
649 return -ENODEV;
650
651 MM_INFO("open '%s'\n", adev->name);
652
653 rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
654 if (rc)
655 return rc;
656
657 MM_INFO("opened module '%s' adev %p\n", adev->name, adev);
658 filp->private_data = adev;
659 adev->abort = 0;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530660 INIT_HLIST_HEAD(&adev->module->ion_regions);
661 mutex_init(&adev->module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700662
663 return 0;
664}
665
666static unsigned adsp_device_count;
667static struct adsp_device *adsp_devices;
668
669static struct adsp_device *inode_to_device(struct inode *inode)
670{
671 unsigned n = MINOR(inode->i_rdev);
672 if (n < adsp_device_count) {
673 if (adsp_devices[n].device)
674 return adsp_devices + n;
675 }
676 return NULL;
677}
678
679static dev_t adsp_devno;
680static struct class *adsp_class;
681
682static struct file_operations adsp_fops = {
683 .owner = THIS_MODULE,
684 .open = adsp_open,
685 .unlocked_ioctl = adsp_ioctl,
686 .release = adsp_release,
687};
688
689static void adsp_create(struct adsp_device *adev, const char *name,
690 struct device *parent, dev_t devt)
691{
692 struct device *dev;
693 int rc;
694
695 dev = device_create(adsp_class, parent, devt, "%s", name);
696 if (IS_ERR(dev))
697 return;
698
699 init_waitqueue_head(&adev->event_wait);
700 INIT_LIST_HEAD(&adev->event_queue);
701 spin_lock_init(&adev->event_queue_lock);
702
703 cdev_init(&adev->cdev, &adsp_fops);
704 adev->cdev.owner = THIS_MODULE;
705
706 rc = cdev_add(&adev->cdev, devt, 1);
707 if (rc < 0) {
708 device_destroy(adsp_class, devt);
709 } else {
710 adev->device = dev;
711 adev->name = name;
712 }
713}
714
715void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
716{
717 int rc;
718
719 adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
720 if (!adsp_devices)
721 return;
722
723 adsp_class = class_create(THIS_MODULE, "adsp");
724 if (IS_ERR(adsp_class))
725 goto fail_create_class;
726
727 rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
728 if (rc < 0)
729 goto fail_alloc_region;
730
731 adsp_device_count = n;
732 for (n = 0; n < adsp_device_count; n++) {
733 adsp_create(adsp_devices + n,
734 modules[n].name, &modules[n].pdev.dev,
735 MKDEV(MAJOR(adsp_devno), n));
736 }
737
738 return;
739
740fail_alloc_region:
741 class_unregister(adsp_class);
742fail_create_class:
743 kfree(adsp_devices);
744}