]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/dream/qdsp5/adsp_driver.c
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel...
[net-next-2.6.git] / drivers / staging / dream / qdsp5 / adsp_driver.c
CommitLineData
caff4cae
IM
1/* arch/arm/mach-msm/qdsp5/adsp_driver.c
2 *
3 * Copyright (C) 2008 Google, Inc.
4 * Author: Iliyan Malchev <ibm@android.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/cdev.h>
18#include <linux/fs.h>
19#include <linux/list.h>
20#include <linux/platform_device.h>
21#include <linux/sched.h>
5a0e3ad6 22#include <linux/slab.h>
caff4cae
IM
23#include <linux/uaccess.h>
24
25#include "adsp.h"
26
27#include <linux/msm_adsp.h>
28#include <linux/android_pmem.h>
29
30struct adsp_pmem_region {
31 struct hlist_node list;
32 void *vaddr;
33 unsigned long paddr;
34 unsigned long kvaddr;
35 unsigned long len;
36 struct file *file;
37};
38
39struct adsp_device {
40 struct msm_adsp_module *module;
41
42 spinlock_t event_queue_lock;
43 wait_queue_head_t event_wait;
44 struct list_head event_queue;
45 int abort;
46
47 const char *name;
48 struct device *device;
49 struct cdev cdev;
50};
51
52static struct adsp_device *inode_to_device(struct inode *inode);
53
54#define __CONTAINS(r, v, l) ({ \
55 typeof(r) __r = r; \
56 typeof(v) __v = v; \
57 typeof(v) __e = __v + l; \
58 int res = __v >= __r->vaddr && \
59 __e <= __r->vaddr + __r->len; \
60 res; \
61})
62
63#define CONTAINS(r1, r2) ({ \
64 typeof(r2) __r2 = r2; \
65 __CONTAINS(r1, __r2->vaddr, __r2->len); \
66})
67
68#define IN_RANGE(r, v) ({ \
69 typeof(r) __r = r; \
70 typeof(v) __vv = v; \
71 int res = ((__vv >= __r->vaddr) && \
72 (__vv < (__r->vaddr + __r->len))); \
73 res; \
74})
75
76#define OVERLAPS(r1, r2) ({ \
77 typeof(r1) __r1 = r1; \
78 typeof(r2) __r2 = r2; \
79 typeof(__r2->vaddr) __v = __r2->vaddr; \
80 typeof(__v) __e = __v + __r2->len - 1; \
81 int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
82 res; \
83})
84
85static int adsp_pmem_check(struct msm_adsp_module *module,
86 void *vaddr, unsigned long len)
87{
88 struct adsp_pmem_region *region_elt;
89 struct hlist_node *node;
90 struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
91
92 hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
93 if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
94 OVERLAPS(region_elt, &t)) {
95 printk(KERN_ERR "adsp: module %s:"
96 " region (vaddr %p len %ld)"
97 " clashes with registered region"
98 " (vaddr %p paddr %p len %ld)\n",
99 module->name,
100 vaddr, len,
101 region_elt->vaddr,
102 (void *)region_elt->paddr,
103 region_elt->len);
104 return -EINVAL;
105 }
106 }
107
108 return 0;
109}
110
111static int adsp_pmem_add(struct msm_adsp_module *module,
112 struct adsp_pmem_info *info)
113{
114 unsigned long paddr, kvaddr, len;
115 struct file *file;
116 struct adsp_pmem_region *region;
117 int rc = -EINVAL;
118
119 mutex_lock(&module->pmem_regions_lock);
120 region = kmalloc(sizeof(*region), GFP_KERNEL);
121 if (!region) {
122 rc = -ENOMEM;
123 goto end;
124 }
125 INIT_HLIST_NODE(&region->list);
126 if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
127 kfree(region);
128 goto end;
129 }
130
131 rc = adsp_pmem_check(module, info->vaddr, len);
132 if (rc < 0) {
133 put_pmem_file(file);
134 kfree(region);
135 goto end;
136 }
137
138 region->vaddr = info->vaddr;
139 region->paddr = paddr;
140 region->kvaddr = kvaddr;
141 region->len = len;
142 region->file = file;
143
144 hlist_add_head(&region->list, &module->pmem_regions);
145end:
146 mutex_unlock(&module->pmem_regions_lock);
147 return rc;
148}
149
150static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
151 unsigned long len, struct adsp_pmem_region **region)
152{
153 struct hlist_node *node;
154 void *vaddr = *addr;
155 struct adsp_pmem_region *region_elt;
156
157 int match_count = 0;
158
159 *region = NULL;
160
161 /* returns physical address or zero */
162 hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
163 if (vaddr >= region_elt->vaddr &&
164 vaddr < region_elt->vaddr + region_elt->len &&
165 vaddr + len <= region_elt->vaddr + region_elt->len) {
166 /* offset since we could pass vaddr inside a registerd
167 * pmem buffer
168 */
169
170 match_count++;
171 if (!*region)
172 *region = region_elt;
173 }
174 }
175
176 if (match_count > 1) {
177 printk(KERN_ERR "adsp: module %s: "
178 "multiple hits for vaddr %p, len %ld\n",
179 module->name, vaddr, len);
180 hlist_for_each_entry(region_elt, node,
181 &module->pmem_regions, list) {
182 if (vaddr >= region_elt->vaddr &&
183 vaddr < region_elt->vaddr + region_elt->len &&
184 vaddr + len <= region_elt->vaddr + region_elt->len)
185 printk(KERN_ERR "\t%p, %ld --> %p\n",
186 region_elt->vaddr,
187 region_elt->len,
188 (void *)region_elt->paddr);
189 }
190 }
191
192 return *region ? 0 : -1;
193}
194
195int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
196 unsigned long *kvaddr, unsigned long len)
197{
198 struct adsp_pmem_region *region;
199 void *vaddr = *addr;
200 unsigned long *paddr = (unsigned long *)addr;
201 int ret;
202
203 ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
204 if (ret) {
205 printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr),"
206 " lookup (%p, %ld) failed\n",
207 module->name, vaddr, len);
208 return ret;
209 }
210 *paddr = region->paddr + (vaddr - region->vaddr);
211 *kvaddr = region->kvaddr + (vaddr - region->vaddr);
212 return 0;
213}
214
215int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
216 unsigned long len)
217{
218 struct adsp_pmem_region *region;
219 void *vaddr = *addr;
220 unsigned long *paddr = (unsigned long *)addr;
221 int ret;
222
223 ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
224 if (ret) {
225 printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n",
226 module->name, vaddr, len);
227 return ret;
228 }
229
230 *paddr = region->paddr + (vaddr - region->vaddr);
231 return 0;
232}
233
234static int adsp_verify_cmd(struct msm_adsp_module *module,
235 unsigned int queue_id, void *cmd_data,
236 size_t cmd_size)
237{
238 /* call the per module verifier */
239 if (module->verify_cmd)
240 return module->verify_cmd(module, queue_id, cmd_data,
241 cmd_size);
242 else
243 printk(KERN_INFO "adsp: no packet verifying function "
244 "for task %s\n", module->name);
245 return 0;
246}
247
248static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
249{
250 struct adsp_command_t cmd;
251 unsigned char buf[256];
252 void *cmd_data;
253 long rc;
254
255 if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
256 return -EFAULT;
257
258 if (cmd.len > 256) {
259 cmd_data = kmalloc(cmd.len, GFP_USER);
260 if (!cmd_data)
261 return -ENOMEM;
262 } else {
263 cmd_data = buf;
264 }
265
266 if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
267 rc = -EFAULT;
268 goto end;
269 }
270
271 mutex_lock(&adev->module->pmem_regions_lock);
272 if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
273 printk(KERN_ERR "module %s: verify failed.\n",
274 adev->module->name);
275 rc = -EINVAL;
276 goto end;
277 }
278 rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
279end:
280 mutex_unlock(&adev->module->pmem_regions_lock);
281
282 if (cmd.len > 256)
283 kfree(cmd_data);
284
285 return rc;
286}
287
288static int adsp_events_pending(struct adsp_device *adev)
289{
290 unsigned long flags;
291 int yes;
292 spin_lock_irqsave(&adev->event_queue_lock, flags);
293 yes = !list_empty(&adev->event_queue);
294 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
295 return yes || adev->abort;
296}
297
298static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
299 struct adsp_pmem_region **region)
300{
301 struct hlist_node *node;
302 unsigned long paddr = (unsigned long)(*addr);
303 struct adsp_pmem_region *region_elt;
304
305 hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
306 if (paddr >= region_elt->paddr &&
307 paddr < region_elt->paddr + region_elt->len) {
308 *region = region_elt;
309 return 0;
310 }
311 }
312 return -1;
313}
314
315int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
316{
317 struct adsp_pmem_region *region;
318 unsigned long paddr = (unsigned long)(*addr);
319 unsigned long *vaddr = (unsigned long *)addr;
320 int ret;
321
322 ret = adsp_pmem_lookup_paddr(module, addr, &region);
323 if (ret) {
324 printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n",
325 module->name, vaddr);
326 return ret;
327 }
328
329 *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
330 return 0;
331}
332
333static int adsp_patch_event(struct msm_adsp_module *module,
334 struct adsp_event *event)
335{
336 /* call the per-module msg verifier */
337 if (module->patch_event)
338 return module->patch_event(module, event);
339 return 0;
340}
341
342static long adsp_get_event(struct adsp_device *adev, void __user *arg)
343{
344 unsigned long flags;
345 struct adsp_event *data = NULL;
346 struct adsp_event_t evt;
347 int timeout;
348 long rc = 0;
349
350 if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
351 return -EFAULT;
352
353 timeout = (int)evt.timeout_ms;
354
355 if (timeout > 0) {
356 rc = wait_event_interruptible_timeout(
357 adev->event_wait, adsp_events_pending(adev),
358 msecs_to_jiffies(timeout));
359 if (rc == 0)
360 return -ETIMEDOUT;
361 } else {
362 rc = wait_event_interruptible(
363 adev->event_wait, adsp_events_pending(adev));
364 }
365 if (rc < 0)
366 return rc;
367
368 if (adev->abort)
369 return -ENODEV;
370
371 spin_lock_irqsave(&adev->event_queue_lock, flags);
372 if (!list_empty(&adev->event_queue)) {
373 data = list_first_entry(&adev->event_queue,
374 struct adsp_event, list);
375 list_del(&data->list);
376 }
377 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
378
379 if (!data)
380 return -EAGAIN;
381
382 /* DSP messages are type 0; they may contain physical addresses */
383 if (data->type == 0)
384 adsp_patch_event(adev->module, data);
385
386 /* map adsp_event --> adsp_event_t */
387 if (evt.len < data->size) {
388 rc = -ETOOSMALL;
389 goto end;
390 }
391 if (data->msg_id != EVENT_MSG_ID) {
392 if (copy_to_user((void *)(evt.data), data->data.msg16,
393 data->size)) {
394 rc = -EFAULT;
395 goto end;
396 }
397 } else {
398 if (copy_to_user((void *)(evt.data), data->data.msg32,
399 data->size)) {
400 rc = -EFAULT;
401 goto end;
402 }
403 }
404
405 evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
406 evt.msg_id = data->msg_id;
407 evt.flags = data->is16;
408 evt.len = data->size;
409 if (copy_to_user(arg, &evt, sizeof(evt)))
410 rc = -EFAULT;
411end:
412 kfree(data);
413 return rc;
414}
415
416static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
417{
418 struct adsp_device *adev = filp->private_data;
419
420 switch (cmd) {
421 case ADSP_IOCTL_ENABLE:
422 return msm_adsp_enable(adev->module);
423
424 case ADSP_IOCTL_DISABLE:
425 return msm_adsp_disable(adev->module);
426
427 case ADSP_IOCTL_DISABLE_EVENT_RSP:
428 return 0;
429
430 case ADSP_IOCTL_DISABLE_ACK:
431 pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n");
432 break;
433
434 case ADSP_IOCTL_WRITE_COMMAND:
435 return adsp_write_cmd(adev, (void __user *) arg);
436
437 case ADSP_IOCTL_GET_EVENT:
438 return adsp_get_event(adev, (void __user *) arg);
439
440 case ADSP_IOCTL_SET_CLKRATE: {
441#if CONFIG_MSM_AMSS_VERSION==6350
442 unsigned long clk_rate;
443 if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
444 return -EFAULT;
445 return adsp_set_clkrate(adev->module, clk_rate);
446#endif
447 }
448
449 case ADSP_IOCTL_REGISTER_PMEM: {
450 struct adsp_pmem_info info;
451 if (copy_from_user(&info, (void *) arg, sizeof(info)))
452 return -EFAULT;
453 return adsp_pmem_add(adev->module, &info);
454 }
455
456 case ADSP_IOCTL_ABORT_EVENT_READ:
457 adev->abort = 1;
458 wake_up(&adev->event_wait);
459 break;
460
461 default:
462 break;
463 }
464 return -EINVAL;
465}
466
467static int adsp_release(struct inode *inode, struct file *filp)
468{
469 struct adsp_device *adev = filp->private_data;
470 struct msm_adsp_module *module = adev->module;
471 struct hlist_node *node, *tmp;
472 struct adsp_pmem_region *region;
473
474 pr_info("adsp_release() '%s'\n", adev->name);
475
476 /* clear module before putting it to avoid race with open() */
477 adev->module = NULL;
478
479 mutex_lock(&module->pmem_regions_lock);
480 hlist_for_each_safe(node, tmp, &module->pmem_regions) {
481 region = hlist_entry(node, struct adsp_pmem_region, list);
482 hlist_del(node);
483 put_pmem_file(region->file);
484 kfree(region);
485 }
486 mutex_unlock(&module->pmem_regions_lock);
487 BUG_ON(!hlist_empty(&module->pmem_regions));
488
489 msm_adsp_put(module);
490 return 0;
491}
492
493static void adsp_event(void *driver_data, unsigned id, size_t len,
494 void (*getevent)(void *ptr, size_t len))
495{
496 struct adsp_device *adev = driver_data;
497 struct adsp_event *event;
498 unsigned long flags;
499
500 if (len > ADSP_EVENT_MAX_SIZE) {
501 pr_err("adsp_event: event too large (%d bytes)\n", len);
502 return;
503 }
504
505 event = kmalloc(sizeof(*event), GFP_ATOMIC);
506 if (!event) {
507 pr_err("adsp_event: cannot allocate buffer\n");
508 return;
509 }
510
511 if (id != EVENT_MSG_ID) {
512 event->type = 0;
513 event->is16 = 0;
514 event->msg_id = id;
515 event->size = len;
516
517 getevent(event->data.msg16, len);
518 } else {
519 event->type = 1;
520 event->is16 = 1;
521 event->msg_id = id;
522 event->size = len;
523 getevent(event->data.msg32, len);
524 }
525
526 spin_lock_irqsave(&adev->event_queue_lock, flags);
527 list_add_tail(&event->list, &adev->event_queue);
528 spin_unlock_irqrestore(&adev->event_queue_lock, flags);
529 wake_up(&adev->event_wait);
530}
531
532static struct msm_adsp_ops adsp_ops = {
533 .event = adsp_event,
534};
535
536static int adsp_open(struct inode *inode, struct file *filp)
537{
538 struct adsp_device *adev;
539 int rc;
540
541 rc = nonseekable_open(inode, filp);
542 if (rc < 0)
543 return rc;
544
545 adev = inode_to_device(inode);
546 if (!adev)
547 return -ENODEV;
548
549 pr_info("adsp_open() name = '%s'\n", adev->name);
550
551 rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
552 if (rc)
553 return rc;
554
555 pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev);
556 filp->private_data = adev;
557 adev->abort = 0;
558 INIT_HLIST_HEAD(&adev->module->pmem_regions);
559 mutex_init(&adev->module->pmem_regions_lock);
560
561 return 0;
562}
563
564static unsigned adsp_device_count;
565static struct adsp_device *adsp_devices;
566
567static struct adsp_device *inode_to_device(struct inode *inode)
568{
569 unsigned n = MINOR(inode->i_rdev);
570 if (n < adsp_device_count) {
571 if (adsp_devices[n].device)
572 return adsp_devices + n;
573 }
574 return NULL;
575}
576
577static dev_t adsp_devno;
578static struct class *adsp_class;
579
580static struct file_operations adsp_fops = {
581 .owner = THIS_MODULE,
582 .open = adsp_open,
583 .unlocked_ioctl = adsp_ioctl,
584 .release = adsp_release,
6038f373 585 .llseek = no_llseek,
caff4cae
IM
586};
587
588static void adsp_create(struct adsp_device *adev, const char *name,
589 struct device *parent, dev_t devt)
590{
591 struct device *dev;
592 int rc;
593
594 dev = device_create(adsp_class, parent, devt, "%s", name);
595 if (IS_ERR(dev))
596 return;
597
598 init_waitqueue_head(&adev->event_wait);
599 INIT_LIST_HEAD(&adev->event_queue);
600 spin_lock_init(&adev->event_queue_lock);
601
602 cdev_init(&adev->cdev, &adsp_fops);
603 adev->cdev.owner = THIS_MODULE;
604
605 rc = cdev_add(&adev->cdev, devt, 1);
606 if (rc < 0) {
607 device_destroy(adsp_class, devt);
608 } else {
609 adev->device = dev;
610 adev->name = name;
611 }
612}
613
614void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
615{
616 int rc;
617
618 adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
619 if (!adsp_devices)
620 return;
621
622 adsp_class = class_create(THIS_MODULE, "adsp");
623 if (IS_ERR(adsp_class))
624 goto fail_create_class;
625
626 rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
627 if (rc < 0)
628 goto fail_alloc_region;
629
630 adsp_device_count = n;
631 for (n = 0; n < adsp_device_count; n++) {
632 adsp_create(adsp_devices + n,
633 modules[n].name, &modules[n].pdev.dev,
634 MKDEV(MAJOR(adsp_devno), n));
635 }
636
637 return;
638
639fail_alloc_region:
640 class_unregister(adsp_class);
641fail_create_class:
642 kfree(adsp_devices);
643}