blob: d83a1409291f1b728de3af8075c1ed24fbec0413 [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>
28#include <linux/android_pmem.h>
29#include <mach/debug_mm.h>
30
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053031struct adsp_ion_info {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032 int fd;
33 void *vaddr;
34};
35
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053036struct adsp_ion_region {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037 struct hlist_node list;
38 void *vaddr;
39 unsigned long paddr;
40 unsigned long kvaddr;
41 unsigned long len;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053042 unsigned long ion_flag;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043 struct file *file;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053044 struct ion_handle *handle;
45 struct ion_client *client;
46 int fd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047};
48
49struct adsp_device {
50 struct msm_adsp_module *module;
51
52 spinlock_t event_queue_lock;
53 wait_queue_head_t event_wait;
54 struct list_head event_queue;
55 int abort;
56
57 const char *name;
58 struct device *device;
59 struct cdev cdev;
60};
61
62static struct adsp_device *inode_to_device(struct inode *inode);
63
64#define __CONTAINS(r, v, l) ({ \
65 typeof(r) __r = r; \
66 typeof(v) __v = v; \
67 typeof(v) __e = __v + l; \
68 int res = __v >= __r->vaddr && \
69 __e <= __r->vaddr + __r->len; \
70 res; \
71})
72
73#define CONTAINS(r1, r2) ({ \
74 typeof(r2) __r2 = r2; \
75 __CONTAINS(r1, __r2->vaddr, __r2->len); \
76})
77
78#define IN_RANGE(r, v) ({ \
79 typeof(r) __r = r; \
80 typeof(v) __vv = v; \
81 int res = ((__vv >= __r->vaddr) && \
82 (__vv < (__r->vaddr + __r->len))); \
83 res; \
84})
85
86#define OVERLAPS(r1, r2) ({ \
87 typeof(r1) __r1 = r1; \
88 typeof(r2) __r2 = r2; \
89 typeof(__r2->vaddr) __v = __r2->vaddr; \
90 typeof(__v) __e = __v + __r2->len - 1; \
91 int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
92 res; \
93})
94
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053095static int adsp_ion_check(struct msm_adsp_module *module,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096 void *vaddr, unsigned long len)
97{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +053098 struct adsp_ion_region *region_elt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099 struct hlist_node *node;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530100 struct adsp_ion_region t = { .vaddr = vaddr, .len = len };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530102 hlist_for_each_entry(region_elt, node, &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103 if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
104 OVERLAPS(region_elt, &t)) {
105 MM_ERR("module %s:"
106 " region (vaddr %p len %ld)"
107 " clashes with registered region"
108 " (vaddr %p paddr %p len %ld)\n",
109 module->name,
110 vaddr, len,
111 region_elt->vaddr,
112 (void *)region_elt->paddr,
113 region_elt->len);
114 return -EINVAL;
115 }
116 }
117
118 return 0;
119}
120
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530121static int get_ion_region_info(int fd, struct adsp_ion_region *region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530123 unsigned long ionflag;
124 void *temp_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 int rc = -EINVAL;
126
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530127 region->client = msm_ion_client_create(UINT_MAX, "Video_Client");
128 if (IS_ERR_OR_NULL(region->client)) {
129 pr_err("Unable to create ION client\n");
130 goto client_error;
131 }
132 region->handle = ion_import_dma_buf(region->client, fd);
133 if (IS_ERR_OR_NULL(region->handle)) {
134 pr_err("%s: could not get handle of the given fd\n", __func__);
135 goto import_error;
136 }
137 rc = ion_handle_get_flags(region->client, region->handle, &ionflag);
138 if (rc) {
139 pr_err("%s: could not get flags for the handle\n", __func__);
140 goto flag_error;
141 }
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700142 temp_ptr = ion_map_kernel(region->client, region->handle);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530143 if (IS_ERR_OR_NULL(temp_ptr)) {
144 pr_err("%s: could not get virtual address\n", __func__);
145 goto map_error;
146 }
147 region->kvaddr = (unsigned long) temp_ptr;
148 region->ion_flag = (unsigned long) ionflag;
149
150 rc = ion_phys(region->client, region->handle, &region->paddr,
151 (size_t *)(&region->len));
152 if (rc) {
153 pr_err("%s: could not get physical address\n", __func__);
154 goto ion_error;
155 }
156 return rc;
157ion_error:
158 ion_unmap_kernel(region->client, region->handle);
159map_error:
160 ion_free(region->client, region->handle);
161flag_error:
162import_error:
163 ion_client_destroy(region->client);
164client_error:
165 return -EINVAL;
166}
167
168static void free_ion_region(struct ion_client *client,
169 struct ion_handle *handle)
170{
171 ion_unmap_kernel(client, handle);
172 ion_free(client, handle);
173 ion_client_destroy(client);
174}
175
176static int adsp_ion_add(struct msm_adsp_module *module,
177 struct adsp_ion_info *info)
178{
179 struct adsp_ion_region *region;
180 int rc = -EINVAL;
181 mutex_lock(&module->ion_regions_lock);
182 region = kmalloc(sizeof(struct adsp_ion_region), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 if (!region) {
184 rc = -ENOMEM;
185 goto end;
186 }
187 INIT_HLIST_NODE(&region->list);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530188 if (get_ion_region_info(info->fd, region)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 kfree(region);
190 goto end;
191 }
192
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530193 rc = adsp_ion_check(module, info->vaddr, region->len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 if (rc < 0) {
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530195 free_ion_region(region->client, region->handle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 kfree(region);
197 goto end;
198 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 region->vaddr = info->vaddr;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530200 region->fd = info->fd;
201 region->file = NULL;
202 MM_INFO("adsp_ion_add: module %s: fd %d, vaddr Ox%x, len %d\n",
203 module->name, region->fd, (unsigned int)region->vaddr,
204 (int)region->len);
205 hlist_add_head(&region->list, &module->ion_regions);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206end:
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530207 mutex_unlock(&module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 return rc;
209}
210
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530211static int adsp_ion_lookup_vaddr(struct msm_adsp_module *module, void **addr,
212 unsigned long len, struct adsp_ion_region **region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213{
214 struct hlist_node *node;
215 void *vaddr = *addr;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530216 struct adsp_ion_region *region_elt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217
218 int match_count = 0;
219
220 *region = NULL;
221
222 /* returns physical address or zero */
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530223 hlist_for_each_entry(region_elt, node, &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224 if (vaddr >= region_elt->vaddr &&
225 vaddr < region_elt->vaddr + region_elt->len &&
226 vaddr + len <= region_elt->vaddr + region_elt->len) {
227 /* offset since we could pass vaddr inside a registerd
228 * pmem buffer
229 */
230
231 match_count++;
232 if (!*region)
233 *region = region_elt;
234 }
235 }
236
237 if (match_count > 1) {
238 MM_ERR("module %s: "
239 "multiple hits for vaddr %p, len %ld\n",
240 module->name, vaddr, len);
241 hlist_for_each_entry(region_elt, node,
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530242 &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 if (vaddr >= region_elt->vaddr &&
244 vaddr < region_elt->vaddr + region_elt->len &&
245 vaddr + len <= region_elt->vaddr + region_elt->len)
246 MM_ERR("%p, %ld --> %p\n",
247 region_elt->vaddr,
248 region_elt->len,
249 (void *)region_elt->paddr);
250 }
251 }
252
253 return *region ? 0 : -1;
254}
255
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530256int adsp_ion_do_cache_op(struct msm_adsp_module *module,
257 void *addr, void *paddr, unsigned long len,
258 unsigned long offset, int cmd)
259{
260 struct adsp_ion_region *region;
261 void *vaddr = addr;
262 int ret;
263 ret = adsp_ion_lookup_vaddr(module, &vaddr, len, &region);
264 if (ret) {
265 MM_ERR("not patching %s (paddr & kvaddr)," \
266 " lookup (%p, %ld) failed\n",
267 module->name, vaddr, len);
268 return ret;
269 }
Mitchel Humpherys76b9b4d2012-09-17 14:33:22 -0700270 if ((region->ion_flag == ION_FLAG_CACHED) && region->handle) {
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530271 len = ((((len) + 31) & (~31)) + 32);
272 ret = msm_ion_do_cache_op(region->client, region->handle,
273 (void *)paddr, len, cmd);
274 }
275 return ret;
276}
277int adsp_ion_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 unsigned long *kvaddr, unsigned long len,
279 struct file **filp, unsigned long *offset)
280{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530281 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 void *vaddr = *addr;
283 unsigned long *paddr = (unsigned long *)addr;
284 int ret;
285
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530286 ret = adsp_ion_lookup_vaddr(module, addr, len, &region);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 if (ret) {
288 MM_ERR("not patching %s (paddr & kvaddr),"
289 " lookup (%p, %ld) failed\n",
290 module->name, vaddr, len);
291 return ret;
292 }
293 *paddr = region->paddr + (vaddr - region->vaddr);
294 *kvaddr = region->kvaddr + (vaddr - region->vaddr);
295 if (filp)
296 *filp = region->file;
297 if (offset)
298 *offset = vaddr - region->vaddr;
299 return 0;
300}
301
302int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
303 unsigned long len)
304{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530305 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306 void *vaddr = *addr;
307 unsigned long *paddr = (unsigned long *)addr;
308 int ret;
309
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530310 ret = adsp_ion_lookup_vaddr(module, addr, len, &region);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 if (ret) {
312 MM_ERR("not patching %s, lookup (%p, %ld) failed\n",
313 module->name, vaddr, len);
314 return ret;
315 }
316
317 *paddr = region->paddr + (vaddr - region->vaddr);
318 return 0;
319}
320
321static int adsp_verify_cmd(struct msm_adsp_module *module,
322 unsigned int queue_id, void *cmd_data,
323 size_t cmd_size)
324{
325 /* call the per module verifier */
326 if (module->verify_cmd)
327 return module->verify_cmd(module, queue_id, cmd_data,
328 cmd_size);
329 else
330 MM_INFO("no packet verifying function "
331 "for task %s\n", module->name);
332 return 0;
333}
334
335static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
336{
337 struct adsp_command_t cmd;
338 unsigned char buf[256];
339 void *cmd_data;
340 long rc;
341
342 if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
343 return -EFAULT;
344
345 if (cmd.len > 256) {
346 cmd_data = kmalloc(cmd.len, GFP_USER);
347 if (!cmd_data)
348 return -ENOMEM;
349 } else {
350 cmd_data = buf;
351 }
352
353 if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
354 rc = -EFAULT;
355 goto end;
356 }
357
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530358 mutex_lock(&adev->module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359 if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
360 MM_ERR("module %s: verify failed.\n", adev->module->name);
361 rc = -EINVAL;
362 goto end;
363 }
364 /* complete the writes to the buffer */
365 wmb();
366 rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
367end:
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530368 mutex_unlock(&adev->module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369
370 if (cmd.len > 256)
371 kfree(cmd_data);
372
373 return rc;
374}
375
376static int adsp_events_pending(struct adsp_device *adev)
377{
378 unsigned long flags;
379 int yes;
380 spin_lock_irqsave(&adev->event_queue_lock, flags);
381 yes = !list_empty(&adev->event_queue);
382 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
383 return yes || adev->abort;
384}
385
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530386static int adsp_ion_lookup_paddr(struct msm_adsp_module *module, void **addr,
387 struct adsp_ion_region **region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388{
389 struct hlist_node *node;
390 unsigned long paddr = (unsigned long)(*addr);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530391 struct adsp_ion_region *region_elt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530393 hlist_for_each_entry(region_elt, node, &module->ion_regions, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 if (paddr >= region_elt->paddr &&
395 paddr < region_elt->paddr + region_elt->len) {
396 *region = region_elt;
397 return 0;
398 }
399 }
400 return -1;
401}
402
403int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
404{
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530405 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 unsigned long paddr = (unsigned long)(*addr);
407 unsigned long *vaddr = (unsigned long *)addr;
408 int ret;
409
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530410 ret = adsp_ion_lookup_paddr(module, addr, &region);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411 if (ret) {
412 MM_ERR("not patching %s, paddr %p lookup failed\n",
413 module->name, vaddr);
414 return ret;
415 }
416
417 *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
418 return 0;
419}
420
421static int adsp_patch_event(struct msm_adsp_module *module,
422 struct adsp_event *event)
423{
424 /* call the per-module msg verifier */
425 if (module->patch_event)
426 return module->patch_event(module, event);
427 return 0;
428}
429
430static long adsp_get_event(struct adsp_device *adev, void __user *arg)
431{
432 unsigned long flags;
433 struct adsp_event *data = NULL;
434 struct adsp_event_t evt;
435 int timeout;
436 long rc = 0;
437
438 if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
439 return -EFAULT;
440
441 timeout = (int)evt.timeout_ms;
442
443 if (timeout > 0) {
444 rc = wait_event_interruptible_timeout(
445 adev->event_wait, adsp_events_pending(adev),
446 msecs_to_jiffies(timeout));
447 if (rc == 0)
448 return -ETIMEDOUT;
449 } else {
450 rc = wait_event_interruptible(
451 adev->event_wait, adsp_events_pending(adev));
452 }
453 if (rc < 0)
454 return rc;
455
456 if (adev->abort)
457 return -ENODEV;
458
459 spin_lock_irqsave(&adev->event_queue_lock, flags);
460 if (!list_empty(&adev->event_queue)) {
461 data = list_first_entry(&adev->event_queue,
462 struct adsp_event, list);
463 list_del(&data->list);
464 }
465 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
466
467 if (!data)
468 return -EAGAIN;
469
470 /* DSP messages are type 0; they may contain physical addresses */
471 if (data->type == 0)
472 adsp_patch_event(adev->module, data);
473
474 /* map adsp_event --> adsp_event_t */
475 if (evt.len < data->size) {
476 rc = -ETOOSMALL;
477 goto end;
478 }
479 /* order the reads to the buffer */
480 rmb();
481 if (data->msg_id != EVENT_MSG_ID) {
482 if (copy_to_user((void *)(evt.data), data->data.msg16,
483 data->size)) {
484 rc = -EFAULT;
485 goto end;
486 }
487 } else {
488 if (copy_to_user((void *)(evt.data), data->data.msg32,
489 data->size)) {
490 rc = -EFAULT;
491 goto end;
492 }
493 }
494
495 evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
496 evt.msg_id = data->msg_id;
497 evt.flags = data->is16;
498 evt.len = data->size;
499 if (copy_to_user(arg, &evt, sizeof(evt)))
500 rc = -EFAULT;
501end:
502 kfree(data);
503 return rc;
504}
505
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530506static int adsp_ion_del(struct msm_adsp_module *module)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700507{
508 struct hlist_node *node, *tmp;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530509 struct adsp_ion_region *region;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530511 mutex_lock(&module->ion_regions_lock);
512 hlist_for_each_safe(node, tmp, &module->ion_regions) {
513 region = hlist_entry(node, struct adsp_ion_region, list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700514 hlist_del(node);
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530515 MM_INFO("adsp_ion_del: module %s: fd %d, vaddr Ox%x, len %d\n",
516 module->name, region->fd, (unsigned int)region->vaddr,
517 (int)region->len);
518 free_ion_region(region->client, region->handle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 kfree(region);
520 }
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530521 mutex_unlock(&module->ion_regions_lock);
522 BUG_ON(!hlist_empty(&module->ion_regions));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523
524 return 0;
525}
526
527static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
528{
529 struct adsp_device *adev = filp->private_data;
530
531 switch (cmd) {
532 case ADSP_IOCTL_ENABLE:
533 return msm_adsp_enable(adev->module);
534
535 case ADSP_IOCTL_DISABLE:
536 return msm_adsp_disable(adev->module);
537
538 case ADSP_IOCTL_DISABLE_EVENT_RSP:
539 return msm_adsp_disable_event_rsp(adev->module);
540
541 case ADSP_IOCTL_DISABLE_ACK:
542 MM_ERR("ADSP_IOCTL_DISABLE_ACK is not implemented\n");
543 break;
544
545 case ADSP_IOCTL_WRITE_COMMAND:
546 return adsp_write_cmd(adev, (void __user *) arg);
547
548 case ADSP_IOCTL_GET_EVENT:
549 return adsp_get_event(adev, (void __user *) arg);
550
551 case ADSP_IOCTL_SET_CLKRATE: {
552 unsigned long clk_rate;
553 if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
554 return -EFAULT;
555 return adsp_set_clkrate(adev->module, clk_rate);
556 }
557
558 case ADSP_IOCTL_REGISTER_PMEM: {
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530559 struct adsp_ion_info info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 if (copy_from_user(&info, (void *) arg, sizeof(info)))
561 return -EFAULT;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530562 return adsp_ion_add(adev->module, &info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563 }
564
565 case ADSP_IOCTL_ABORT_EVENT_READ:
566 adev->abort = 1;
567 wake_up(&adev->event_wait);
568 break;
569
570 case ADSP_IOCTL_UNREGISTER_PMEM:
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530571 return adsp_ion_del(adev->module);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572
573 default:
574 break;
575 }
576 return -EINVAL;
577}
578
579static int adsp_release(struct inode *inode, struct file *filp)
580{
581 struct adsp_device *adev = filp->private_data;
582 struct msm_adsp_module *module = adev->module;
583 int rc = 0;
584
585 MM_INFO("release '%s'\n", adev->name);
586
587 /* clear module before putting it to avoid race with open() */
588 adev->module = NULL;
589
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530590 rc = adsp_ion_del(module);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591
592 msm_adsp_put(module);
593 return rc;
594}
595
596static void adsp_event(void *driver_data, unsigned id, size_t len,
597 void (*getevent)(void *ptr, size_t len))
598{
599 struct adsp_device *adev = driver_data;
600 struct adsp_event *event;
601 unsigned long flags;
602
603 if (len > ADSP_EVENT_MAX_SIZE) {
604 MM_ERR("event too large (%d bytes)\n", len);
605 return;
606 }
607
608 event = kmalloc(sizeof(*event), GFP_ATOMIC);
609 if (!event) {
610 MM_ERR("cannot allocate buffer\n");
611 return;
612 }
613
614 if (id != EVENT_MSG_ID) {
615 event->type = 0;
616 event->is16 = 0;
617 event->msg_id = id;
618 event->size = len;
619
620 getevent(event->data.msg16, len);
621 } else {
622 event->type = 1;
623 event->is16 = 1;
624 event->msg_id = id;
625 event->size = len;
626 getevent(event->data.msg32, len);
627 }
628
629 spin_lock_irqsave(&adev->event_queue_lock, flags);
630 list_add_tail(&event->list, &adev->event_queue);
631 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
632 wake_up(&adev->event_wait);
633}
634
635static struct msm_adsp_ops adsp_ops = {
636 .event = adsp_event,
637};
638
639static int adsp_open(struct inode *inode, struct file *filp)
640{
641 struct adsp_device *adev;
642 int rc;
643
644 rc = nonseekable_open(inode, filp);
645 if (rc < 0)
646 return rc;
647
648 adev = inode_to_device(inode);
649 if (!adev)
650 return -ENODEV;
651
652 MM_INFO("open '%s'\n", adev->name);
653
654 rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
655 if (rc)
656 return rc;
657
658 MM_INFO("opened module '%s' adev %p\n", adev->name, adev);
659 filp->private_data = adev;
660 adev->abort = 0;
Saikumar Kondaparthi316620f2012-06-26 15:43:22 +0530661 INIT_HLIST_HEAD(&adev->module->ion_regions);
662 mutex_init(&adev->module->ion_regions_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663
664 return 0;
665}
666
667static unsigned adsp_device_count;
668static struct adsp_device *adsp_devices;
669
670static struct adsp_device *inode_to_device(struct inode *inode)
671{
672 unsigned n = MINOR(inode->i_rdev);
673 if (n < adsp_device_count) {
674 if (adsp_devices[n].device)
675 return adsp_devices + n;
676 }
677 return NULL;
678}
679
680static dev_t adsp_devno;
681static struct class *adsp_class;
682
683static struct file_operations adsp_fops = {
684 .owner = THIS_MODULE,
685 .open = adsp_open,
686 .unlocked_ioctl = adsp_ioctl,
687 .release = adsp_release,
688};
689
690static void adsp_create(struct adsp_device *adev, const char *name,
691 struct device *parent, dev_t devt)
692{
693 struct device *dev;
694 int rc;
695
696 dev = device_create(adsp_class, parent, devt, "%s", name);
697 if (IS_ERR(dev))
698 return;
699
700 init_waitqueue_head(&adev->event_wait);
701 INIT_LIST_HEAD(&adev->event_queue);
702 spin_lock_init(&adev->event_queue_lock);
703
704 cdev_init(&adev->cdev, &adsp_fops);
705 adev->cdev.owner = THIS_MODULE;
706
707 rc = cdev_add(&adev->cdev, devt, 1);
708 if (rc < 0) {
709 device_destroy(adsp_class, devt);
710 } else {
711 adev->device = dev;
712 adev->name = name;
713 }
714}
715
716void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
717{
718 int rc;
719
720 adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
721 if (!adsp_devices)
722 return;
723
724 adsp_class = class_create(THIS_MODULE, "adsp");
725 if (IS_ERR(adsp_class))
726 goto fail_create_class;
727
728 rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
729 if (rc < 0)
730 goto fail_alloc_region;
731
732 adsp_device_count = n;
733 for (n = 0; n < adsp_device_count; n++) {
734 adsp_create(adsp_devices + n,
735 modules[n].name, &modules[n].pdev.dev,
736 MKDEV(MAJOR(adsp_devno), n));
737 }
738
739 return;
740
741fail_alloc_region:
742 class_unregister(adsp_class);
743fail_create_class:
744 kfree(adsp_devices);
745}