1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/platform_data/dsp-omap.h>
20
21#include <linux/types.h>
22#include <linux/platform_device.h>
23#include <linux/pm.h>
24#include <linux/module.h>
25#include <linux/device.h>
26#include <linux/init.h>
27#include <linux/moduleparam.h>
28#include <linux/cdev.h>
29
30
31#include <dspbridge/dbdefs.h>
32
33
34#include <dspbridge/clk.h>
35
36
37#include <dspbridge/dspapi.h>
38#include <dspbridge/dspdrv.h>
39
40
41#include <dspbridge/pwr.h>
42
43#include <dspbridge/resourcecleanup.h>
44#include <dspbridge/proc.h>
45#include <dspbridge/dev.h>
46
47#ifdef CONFIG_TIDSPBRIDGE_DVFS
48#include <mach-omap2/omap3-opp.h>
49#endif
50
51
52#define DSPBRIDGE_VERSION "0.3"
53s32 dsp_debug;
54
55struct platform_device *omap_dspbridge_dev;
56struct device *bridge;
57
58
59s32 dsp_test_sleepstate;
60
61static struct cdev bridge_cdev;
62
63static struct class *bridge_class;
64
65static u32 driver_context;
66static s32 driver_major;
67static char *base_img;
68static s32 shm_size = 0x500000;
69static int tc_wordswapon;
70#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
71#define REC_TIMEOUT 5000
72static atomic_t bridge_cref;
73static struct workqueue_struct *bridge_rec_queue;
74static struct work_struct bridge_recovery_work;
75static DECLARE_COMPLETION(bridge_comp);
76static DECLARE_COMPLETION(bridge_open_comp);
77static bool recover;
78#endif
79
80#ifdef CONFIG_PM
81struct omap34_xx_bridge_suspend_data {
82 int suspended;
83 wait_queue_head_t suspend_wq;
84};
85
86static struct omap34_xx_bridge_suspend_data bridge_suspend_data;
87
88static int omap34_xxbridge_suspend_lockout(struct omap34_xx_bridge_suspend_data
89 *s, struct file *f)
90{
91 if ((s)->suspended) {
92 if ((f)->f_flags & O_NONBLOCK)
93 return -EPERM;
94 wait_event_interruptible((s)->suspend_wq, (s)->suspended == 0);
95 }
96 return 0;
97}
98#endif
99
100module_param(dsp_debug, int, 0);
101MODULE_PARM_DESC(dsp_debug, "Wait after loading DSP image. default = false");
102
103module_param(dsp_test_sleepstate, int, 0);
104MODULE_PARM_DESC(dsp_test_sleepstate, "DSP Sleep state = 0");
105
106module_param(base_img, charp, 0);
107MODULE_PARM_DESC(base_img, "DSP base image, default = NULL");
108
109module_param(shm_size, int, 0);
110MODULE_PARM_DESC(shm_size, "shm size, default = 4 MB, minimum = 64 KB");
111
112module_param(tc_wordswapon, int, 0);
113MODULE_PARM_DESC(tc_wordswapon, "TC Word Swap Option. default = 0");
114
115MODULE_AUTHOR("Texas Instruments");
116MODULE_LICENSE("GPL");
117MODULE_VERSION(DSPBRIDGE_VERSION);
118
119
120
121
122
123static int bridge_open(struct inode *ip, struct file *filp)
124{
125 int status = 0;
126 struct process_context *pr_ctxt = NULL;
127
128
129
130
131
132
133#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
134 if (recover) {
135 if (filp->f_flags & O_NONBLOCK ||
136 wait_for_completion_interruptible(&bridge_open_comp))
137 return -EBUSY;
138 }
139#endif
140 pr_ctxt = kzalloc(sizeof(struct process_context), GFP_KERNEL);
141 if (!pr_ctxt)
142 return -ENOMEM;
143
144 pr_ctxt->res_state = PROC_RES_ALLOCATED;
145 spin_lock_init(&pr_ctxt->dmm_map_lock);
146 INIT_LIST_HEAD(&pr_ctxt->dmm_map_list);
147 spin_lock_init(&pr_ctxt->dmm_rsv_lock);
148 INIT_LIST_HEAD(&pr_ctxt->dmm_rsv_list);
149
150 pr_ctxt->node_id = kzalloc(sizeof(struct idr), GFP_KERNEL);
151 if (!pr_ctxt->node_id) {
152 status = -ENOMEM;
153 goto err1;
154 }
155
156 idr_init(pr_ctxt->node_id);
157
158 pr_ctxt->stream_id = kzalloc(sizeof(struct idr), GFP_KERNEL);
159 if (!pr_ctxt->stream_id) {
160 status = -ENOMEM;
161 goto err2;
162 }
163
164 idr_init(pr_ctxt->stream_id);
165
166 filp->private_data = pr_ctxt;
167
168#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
169 atomic_inc(&bridge_cref);
170#endif
171 return 0;
172
173err2:
174 kfree(pr_ctxt->node_id);
175err1:
176 kfree(pr_ctxt);
177 return status;
178}
179
180
181
182
183
184static int bridge_release(struct inode *ip, struct file *filp)
185{
186 int status = 0;
187 struct process_context *pr_ctxt;
188
189 if (!filp->private_data) {
190 status = -EIO;
191 goto err;
192 }
193
194 pr_ctxt = filp->private_data;
195 flush_signals(current);
196 drv_remove_all_resources(pr_ctxt);
197 proc_detach(pr_ctxt);
198 kfree(pr_ctxt->node_id);
199 kfree(pr_ctxt->stream_id);
200 kfree(pr_ctxt);
201
202 filp->private_data = NULL;
203
204err:
205#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
206 if (!atomic_dec_return(&bridge_cref))
207 complete(&bridge_comp);
208#endif
209 return status;
210}
211
212
213static long bridge_ioctl(struct file *filp, unsigned int code,
214 unsigned long args)
215{
216 int status;
217 u32 retval = 0;
218 union trapped_args buf_in;
219
220#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
221 if (recover) {
222 status = -EIO;
223 goto err;
224 }
225#endif
226#ifdef CONFIG_PM
227 status = omap34_xxbridge_suspend_lockout(&bridge_suspend_data, filp);
228 if (status != 0)
229 return status;
230#endif
231
232 if (!filp->private_data) {
233 status = -EIO;
234 goto err;
235 }
236
237 status = copy_from_user(&buf_in, (union trapped_args *)args,
238 sizeof(union trapped_args));
239
240 if (!status) {
241 status = api_call_dev_ioctl(code, &buf_in, &retval,
242 filp->private_data);
243
244 if (!status) {
245 status = retval;
246 } else {
247 dev_dbg(bridge, "%s: IOCTL Failed, code: 0x%x "
248 "status 0x%x\n", __func__, code, status);
249 status = -1;
250 }
251
252 }
253
254err:
255 return status;
256}
257
258
259static int bridge_mmap(struct file *filp, struct vm_area_struct *vma)
260{
261 u32 status;
262
263
264 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
265
266 dev_dbg(bridge, "%s: vm filp %p start %lx end %lx page_prot %ulx "
267 "flags %lx\n", __func__, filp,
268 vma->vm_start, vma->vm_end, vma->vm_page_prot,
269 vma->vm_flags);
270
271 status = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
272 vma->vm_end - vma->vm_start,
273 vma->vm_page_prot);
274 if (status != 0)
275 status = -EAGAIN;
276
277 return status;
278}
279
280static const struct file_operations bridge_fops = {
281 .open = bridge_open,
282 .release = bridge_release,
283 .unlocked_ioctl = bridge_ioctl,
284 .mmap = bridge_mmap,
285 .llseek = noop_llseek,
286};
287
288#ifdef CONFIG_PM
289static u32 time_out = 1000;
290#ifdef CONFIG_TIDSPBRIDGE_DVFS
291s32 dsp_max_opps = VDD1_OPP5;
292#endif
293
294
295
296#ifdef CONFIG_TIDSPBRIDGE_DVFS
297const struct omap_opp vdd1_rate_table_bridge[] = {
298 {0, 0, 0},
299
300 {S125M, VDD1_OPP1, 0},
301
302 {S250M, VDD1_OPP2, 0},
303
304 {S500M, VDD1_OPP3, 0},
305
306 {S550M, VDD1_OPP4, 0},
307
308 {S600M, VDD1_OPP5, 0},
309};
310#endif
311#endif
312
313struct omap_dsp_platform_data *omap_dspbridge_pdata;
314
315u32 vdd1_dsp_freq[6][4] = {
316 {0, 0, 0, 0},
317
318 {0, 90000, 0, 86000},
319
320 {0, 180000, 80000, 170000},
321
322 {0, 360000, 160000, 340000},
323
324 {0, 396000, 325000, 376000},
325
326 {0, 430000, 355000, 430000},
327};
328
329#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
330static void bridge_recover(struct work_struct *work)
331{
332 struct dev_object *dev;
333 struct cfg_devnode *dev_node;
334 if (atomic_read(&bridge_cref)) {
335 INIT_COMPLETION(bridge_comp);
336 while (!wait_for_completion_timeout(&bridge_comp,
337 msecs_to_jiffies(REC_TIMEOUT)))
338 pr_info("%s:%d handle(s) still opened\n",
339 __func__, atomic_read(&bridge_cref));
340 }
341 dev = dev_get_first();
342 dev_get_dev_node(dev, &dev_node);
343 if (!dev_node || proc_auto_start(dev_node, dev))
344 pr_err("DSP could not be restarted\n");
345 recover = false;
346 complete_all(&bridge_open_comp);
347}
348
349void bridge_recover_schedule(void)
350{
351 INIT_COMPLETION(bridge_open_comp);
352 recover = true;
353 queue_work(bridge_rec_queue, &bridge_recovery_work);
354}
355#endif
356#ifdef CONFIG_TIDSPBRIDGE_DVFS
357static int dspbridge_scale_notification(struct notifier_block *op,
358 unsigned long val, void *ptr)
359{
360 struct omap_dsp_platform_data *pdata =
361 omap_dspbridge_dev->dev.platform_data;
362
363 if (CPUFREQ_POSTCHANGE == val && pdata->dsp_get_opp)
364 pwr_pm_post_scale(PRCM_VDD1, pdata->dsp_get_opp());
365
366 return 0;
367}
368
369static struct notifier_block iva_clk_notifier = {
370 .notifier_call = dspbridge_scale_notification,
371 NULL,
372};
373#endif
374
375
376
377
378
379
380
381
382static int omap3_bridge_startup(struct platform_device *pdev)
383{
384 struct omap_dsp_platform_data *pdata = pdev->dev.platform_data;
385 struct drv_data *drv_datap = NULL;
386 u32 phys_membase, phys_memsize;
387 int err;
388
389#ifdef CONFIG_TIDSPBRIDGE_RECOVERY
390 bridge_rec_queue = create_workqueue("bridge_rec_queue");
391 INIT_WORK(&bridge_recovery_work, bridge_recover);
392 INIT_COMPLETION(bridge_comp);
393#endif
394
395#ifdef CONFIG_PM
396
397 bridge_suspend_data.suspended = 0;
398 init_waitqueue_head(&bridge_suspend_data.suspend_wq);
399
400#ifdef CONFIG_TIDSPBRIDGE_DVFS
401 for (i = 0; i < 6; i++)
402 pdata->mpu_speed[i] = vdd1_rate_table_bridge[i].rate;
403
404 err = cpufreq_register_notifier(&iva_clk_notifier,
405 CPUFREQ_TRANSITION_NOTIFIER);
406 if (err)
407 pr_err("%s: clk_notifier_register failed for iva2_ck\n",
408 __func__);
409#endif
410#endif
411
412 dsp_clk_init();
413
414 drv_datap = kzalloc(sizeof(struct drv_data), GFP_KERNEL);
415 if (!drv_datap) {
416 err = -ENOMEM;
417 goto err1;
418 }
419
420 drv_datap->shm_size = shm_size;
421 drv_datap->tc_wordswapon = tc_wordswapon;
422
423 if (base_img) {
424 drv_datap->base_img = kmalloc(strlen(base_img) + 1, GFP_KERNEL);
425 if (!drv_datap->base_img) {
426 err = -ENOMEM;
427 goto err2;
428 }
429 strncpy(drv_datap->base_img, base_img, strlen(base_img) + 1);
430 }
431
432 dev_set_drvdata(bridge, drv_datap);
433
434 if (shm_size < 0x10000) {
435 err = -EINVAL;
436 pr_err("%s: shm size must be at least 64 KB\n", __func__);
437 goto err3;
438 }
439 dev_dbg(bridge, "%s: requested shm_size = 0x%x\n", __func__, shm_size);
440
441 phys_membase = pdata->phys_mempool_base;
442 phys_memsize = pdata->phys_mempool_size;
443 if (phys_membase > 0 && phys_memsize > 0)
444 mem_ext_phys_pool_init(phys_membase, phys_memsize);
445
446 if (tc_wordswapon)
447 dev_dbg(bridge, "%s: TC Word Swap is enabled\n", __func__);
448
449 driver_context = dsp_init(&err);
450 if (err) {
451 pr_err("DSP Bridge driver initialization failed\n");
452 goto err4;
453 }
454
455 return 0;
456
457err4:
458 mem_ext_phys_pool_release();
459err3:
460 kfree(drv_datap->base_img);
461err2:
462 kfree(drv_datap);
463err1:
464#ifdef CONFIG_TIDSPBRIDGE_DVFS
465 cpufreq_unregister_notifier(&iva_clk_notifier,
466 CPUFREQ_TRANSITION_NOTIFIER);
467#endif
468 dsp_clk_exit();
469
470 return err;
471}
472
473static int omap34_xx_bridge_probe(struct platform_device *pdev)
474{
475 int err;
476 dev_t dev = 0;
477#ifdef CONFIG_TIDSPBRIDGE_DVFS
478 int i = 0;
479#endif
480
481 omap_dspbridge_dev = pdev;
482
483
484 bridge = &omap_dspbridge_dev->dev;
485
486
487 err = omap3_bridge_startup(pdev);
488 if (err)
489 goto err1;
490
491
492 err = alloc_chrdev_region(&dev, 0, 1, "DspBridge");
493 if (err) {
494 pr_err("%s: Can't get major %d\n", __func__, driver_major);
495 goto err1;
496 }
497
498 cdev_init(&bridge_cdev, &bridge_fops);
499 bridge_cdev.owner = THIS_MODULE;
500
501 err = cdev_add(&bridge_cdev, dev, 1);
502 if (err) {
503 pr_err("%s: Failed to add bridge device\n", __func__);
504 goto err2;
505 }
506
507
508 bridge_class = class_create(THIS_MODULE, "ti_bridge");
509 if (IS_ERR(bridge_class)) {
510 pr_err("%s: Error creating bridge class\n", __func__);
511 goto err3;
512 }
513
514 driver_major = MAJOR(dev);
515 device_create(bridge_class, NULL, MKDEV(driver_major, 0),
516 NULL, "DspBridge");
517 pr_info("DSP Bridge driver loaded\n");
518
519 return 0;
520
521err3:
522 cdev_del(&bridge_cdev);
523err2:
524 unregister_chrdev_region(dev, 1);
525err1:
526 return err;
527}
528
529static int omap34_xx_bridge_remove(struct platform_device *pdev)
530{
531 dev_t devno;
532 int status = 0;
533 struct drv_data *drv_datap = dev_get_drvdata(bridge);
534
535
536 if (!drv_datap || !drv_datap->drv_object) {
537 status = -ENODATA;
538 pr_err("%s: Failed to retrieve the object handle\n", __func__);
539 goto func_cont;
540 }
541
542#ifdef CONFIG_TIDSPBRIDGE_DVFS
543 if (cpufreq_unregister_notifier(&iva_clk_notifier,
544 CPUFREQ_TRANSITION_NOTIFIER))
545 pr_err("%s: cpufreq_unregister_notifier failed for iva2_ck\n",
546 __func__);
547#endif
548
549 if (driver_context) {
550
551 dsp_deinit(driver_context);
552 driver_context = 0;
553 }
554
555 kfree(drv_datap);
556 dev_set_drvdata(bridge, NULL);
557
558func_cont:
559 mem_ext_phys_pool_release();
560
561 dsp_clk_exit();
562
563 devno = MKDEV(driver_major, 0);
564 cdev_del(&bridge_cdev);
565 unregister_chrdev_region(devno, 1);
566 if (bridge_class) {
567
568 device_destroy(bridge_class, MKDEV(driver_major, 0));
569 class_destroy(bridge_class);
570
571 }
572 return 0;
573}
574
575#ifdef CONFIG_PM
576static int bridge_suspend(struct platform_device *pdev, pm_message_t state)
577{
578 u32 status;
579 u32 command = PWR_EMERGENCYDEEPSLEEP;
580
581 status = pwr_sleep_dsp(command, time_out);
582 if (status)
583 return -1;
584
585 bridge_suspend_data.suspended = 1;
586 return 0;
587}
588
589static int bridge_resume(struct platform_device *pdev)
590{
591 u32 status;
592
593 status = pwr_wake_dsp(time_out);
594 if (status)
595 return -1;
596
597 bridge_suspend_data.suspended = 0;
598 wake_up(&bridge_suspend_data.suspend_wq);
599 return 0;
600}
601#endif
602
603static struct platform_driver bridge_driver = {
604 .driver = {
605 .name = "omap-dsp",
606 },
607 .probe = omap34_xx_bridge_probe,
608 .remove = omap34_xx_bridge_remove,
609#ifdef CONFIG_PM
610 .suspend = bridge_suspend,
611 .resume = bridge_resume,
612#endif
613};
614
615
616
617int drv_remove_all_resources(void *process_ctxt)
618{
619 int status = 0;
620 struct process_context *ctxt = (struct process_context *)process_ctxt;
621 drv_remove_all_strm_res_elements(ctxt);
622 drv_remove_all_node_res_elements(ctxt);
623 drv_remove_all_dmm_res_elements(ctxt);
624 ctxt->res_state = PROC_RES_FREED;
625 return status;
626}
627
628module_platform_driver(bridge_driver);
629