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