]>
Commit | Line | Data |
---|---|---|
7d55524d ORL |
1 | /* |
2 | * drv_interface.c | |
3 | * | |
4 | * DSP-BIOS Bridge driver support functions for TI OMAP processors. | |
5 | * | |
6 | * DSP/BIOS Bridge driver interface. | |
7 | * | |
8 | * Copyright (C) 2005-2006 Texas Instruments, Inc. | |
9 | * | |
10 | * This package is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
15 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
16 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
17 | */ | |
18 | ||
19 | /* ----------------------------------- Host OS */ | |
20 | ||
21 | #include <dspbridge/host_os.h> | |
2094f12d | 22 | #include <linux/types.h> |
7d55524d ORL |
23 | #include <linux/platform_device.h> |
24 | #include <linux/pm.h> | |
25 | ||
26 | #ifdef MODULE | |
27 | #include <linux/module.h> | |
28 | #endif | |
29 | ||
30 | #include <linux/device.h> | |
31 | #include <linux/init.h> | |
32 | #include <linux/moduleparam.h> | |
33 | #include <linux/cdev.h> | |
34 | ||
35 | /* ----------------------------------- DSP/BIOS Bridge */ | |
7d55524d ORL |
36 | #include <dspbridge/dbdefs.h> |
37 | ||
38 | /* ----------------------------------- Trace & Debug */ | |
39 | #include <dspbridge/dbc.h> | |
40 | ||
41 | /* ----------------------------------- OS Adaptation Layer */ | |
42 | #include <dspbridge/services.h> | |
43 | #include <dspbridge/clk.h> | |
44 | #include <dspbridge/sync.h> | |
45 | ||
46 | /* ----------------------------------- Platform Manager */ | |
47 | #include <dspbridge/dspapi-ioctl.h> | |
48 | #include <dspbridge/dspapi.h> | |
49 | #include <dspbridge/dspdrv.h> | |
50 | ||
51 | /* ----------------------------------- Resource Manager */ | |
52 | #include <dspbridge/pwr.h> | |
53 | ||
54 | /* ----------------------------------- This */ | |
55 | #include <drv_interface.h> | |
56 | ||
57 | #include <dspbridge/cfg.h> | |
58 | #include <dspbridge/resourcecleanup.h> | |
59 | #include <dspbridge/chnl.h> | |
60 | #include <dspbridge/proc.h> | |
61 | #include <dspbridge/dev.h> | |
62 | #include <dspbridge/drvdefs.h> | |
63 | #include <dspbridge/drv.h> | |
64 | ||
b3d23688 | 65 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
66 | #include <mach-omap2/omap3-opp.h> |
67 | #endif | |
68 | ||
69 | #define BRIDGE_NAME "C6410" | |
70 | /* ----------------------------------- Globals */ | |
71 | #define DRIVER_NAME "DspBridge" | |
72 | #define DSPBRIDGE_VERSION "0.3" | |
73 | s32 dsp_debug; | |
74 | ||
75 | struct platform_device *omap_dspbridge_dev; | |
76 | struct device *bridge; | |
77 | ||
78 | /* This is a test variable used by Bridge to test different sleep states */ | |
79 | s32 dsp_test_sleepstate; | |
80 | ||
81 | static struct cdev bridge_cdev; | |
82 | ||
83 | static struct class *bridge_class; | |
84 | ||
85 | static u32 driver_context; | |
86 | static s32 driver_major; | |
87 | static char *base_img; | |
88 | char *iva_img; | |
89 | static s32 shm_size = 0x500000; /* 5 MB */ | |
90 | static int tc_wordswapon; /* Default value is always false */ | |
b3d23688 | 91 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
92 | #define REC_TIMEOUT 5000 /*recovery timeout in msecs */ |
93 | static atomic_t bridge_cref; /* number of bridge open handles */ | |
94 | static struct workqueue_struct *bridge_rec_queue; | |
95 | static struct work_struct bridge_recovery_work; | |
96 | static DECLARE_COMPLETION(bridge_comp); | |
97 | static DECLARE_COMPLETION(bridge_open_comp); | |
98 | static bool recover; | |
99 | #endif | |
100 | ||
101 | #ifdef CONFIG_PM | |
102 | struct omap34_xx_bridge_suspend_data { | |
103 | int suspended; | |
104 | wait_queue_head_t suspend_wq; | |
105 | }; | |
106 | ||
107 | static struct omap34_xx_bridge_suspend_data bridge_suspend_data; | |
108 | ||
109 | static int omap34_xxbridge_suspend_lockout(struct omap34_xx_bridge_suspend_data | |
110 | *s, struct file *f) | |
111 | { | |
112 | if ((s)->suspended) { | |
113 | if ((f)->f_flags & O_NONBLOCK) | |
114 | return -EPERM; | |
115 | wait_event_interruptible((s)->suspend_wq, (s)->suspended == 0); | |
116 | } | |
117 | return 0; | |
118 | } | |
119 | #endif | |
120 | ||
121 | module_param(dsp_debug, int, 0); | |
122 | MODULE_PARM_DESC(dsp_debug, "Wait after loading DSP image. default = false"); | |
123 | ||
124 | module_param(dsp_test_sleepstate, int, 0); | |
125 | MODULE_PARM_DESC(dsp_test_sleepstate, "DSP Sleep state = 0"); | |
126 | ||
127 | module_param(base_img, charp, 0); | |
128 | MODULE_PARM_DESC(base_img, "DSP base image, default = NULL"); | |
129 | ||
130 | module_param(shm_size, int, 0); | |
131 | MODULE_PARM_DESC(shm_size, "shm size, default = 4 MB, minimum = 64 KB"); | |
132 | ||
133 | module_param(tc_wordswapon, int, 0); | |
134 | MODULE_PARM_DESC(tc_wordswapon, "TC Word Swap Option. default = 0"); | |
135 | ||
136 | MODULE_AUTHOR("Texas Instruments"); | |
137 | MODULE_LICENSE("GPL"); | |
138 | MODULE_VERSION(DSPBRIDGE_VERSION); | |
139 | ||
140 | static char *driver_name = DRIVER_NAME; | |
141 | ||
142 | static const struct file_operations bridge_fops = { | |
143 | .open = bridge_open, | |
144 | .release = bridge_release, | |
145 | .unlocked_ioctl = bridge_ioctl, | |
146 | .mmap = bridge_mmap, | |
6038f373 | 147 | .llseek = noop_llseek, |
7d55524d ORL |
148 | }; |
149 | ||
150 | #ifdef CONFIG_PM | |
151 | static u32 time_out = 1000; | |
b3d23688 | 152 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
153 | s32 dsp_max_opps = VDD1_OPP5; |
154 | #endif | |
155 | ||
156 | /* Maximum Opps that can be requested by IVA */ | |
157 | /*vdd1 rate table */ | |
b3d23688 | 158 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
159 | const struct omap_opp vdd1_rate_table_bridge[] = { |
160 | {0, 0, 0}, | |
161 | /*OPP1 */ | |
162 | {S125M, VDD1_OPP1, 0}, | |
163 | /*OPP2 */ | |
164 | {S250M, VDD1_OPP2, 0}, | |
165 | /*OPP3 */ | |
166 | {S500M, VDD1_OPP3, 0}, | |
167 | /*OPP4 */ | |
168 | {S550M, VDD1_OPP4, 0}, | |
169 | /*OPP5 */ | |
170 | {S600M, VDD1_OPP5, 0}, | |
171 | }; | |
172 | #endif | |
173 | #endif | |
174 | ||
175 | struct dspbridge_platform_data *omap_dspbridge_pdata; | |
176 | ||
177 | u32 vdd1_dsp_freq[6][4] = { | |
178 | {0, 0, 0, 0}, | |
179 | /*OPP1 */ | |
180 | {0, 90000, 0, 86000}, | |
181 | /*OPP2 */ | |
182 | {0, 180000, 80000, 170000}, | |
183 | /*OPP3 */ | |
184 | {0, 360000, 160000, 340000}, | |
185 | /*OPP4 */ | |
186 | {0, 396000, 325000, 376000}, | |
187 | /*OPP5 */ | |
188 | {0, 430000, 355000, 430000}, | |
189 | }; | |
190 | ||
b3d23688 | 191 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
192 | static void bridge_recover(struct work_struct *work) |
193 | { | |
194 | struct dev_object *dev; | |
195 | struct cfg_devnode *dev_node; | |
196 | if (atomic_read(&bridge_cref)) { | |
197 | INIT_COMPLETION(bridge_comp); | |
198 | while (!wait_for_completion_timeout(&bridge_comp, | |
199 | msecs_to_jiffies(REC_TIMEOUT))) | |
200 | pr_info("%s:%d handle(s) still opened\n", | |
201 | __func__, atomic_read(&bridge_cref)); | |
202 | } | |
203 | dev = dev_get_first(); | |
204 | dev_get_dev_node(dev, &dev_node); | |
b66e0986 | 205 | if (!dev_node || proc_auto_start(dev_node, dev)) |
7d55524d ORL |
206 | pr_err("DSP could not be restarted\n"); |
207 | recover = false; | |
208 | complete_all(&bridge_open_comp); | |
209 | } | |
210 | ||
211 | void bridge_recover_schedule(void) | |
212 | { | |
213 | INIT_COMPLETION(bridge_open_comp); | |
214 | recover = true; | |
215 | queue_work(bridge_rec_queue, &bridge_recovery_work); | |
216 | } | |
217 | #endif | |
b3d23688 | 218 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
219 | static int dspbridge_scale_notification(struct notifier_block *op, |
220 | unsigned long val, void *ptr) | |
221 | { | |
222 | struct dspbridge_platform_data *pdata = | |
223 | omap_dspbridge_dev->dev.platform_data; | |
224 | ||
225 | if (CPUFREQ_POSTCHANGE == val && pdata->dsp_get_opp) | |
226 | pwr_pm_post_scale(PRCM_VDD1, pdata->dsp_get_opp()); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | static struct notifier_block iva_clk_notifier = { | |
232 | .notifier_call = dspbridge_scale_notification, | |
233 | NULL, | |
234 | }; | |
235 | #endif | |
236 | ||
237 | /** | |
238 | * omap3_bridge_startup() - perform low lever initializations | |
239 | * @pdev: pointer to platform device | |
240 | * | |
241 | * Initializes recovery, PM and DVFS required data, before calling | |
242 | * clk and memory init routines. | |
243 | */ | |
244 | static int omap3_bridge_startup(struct platform_device *pdev) | |
245 | { | |
246 | struct dspbridge_platform_data *pdata = pdev->dev.platform_data; | |
247 | struct drv_data *drv_datap = NULL; | |
248 | u32 phys_membase, phys_memsize; | |
249 | int err; | |
250 | ||
b3d23688 | 251 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
252 | bridge_rec_queue = create_workqueue("bridge_rec_queue"); |
253 | INIT_WORK(&bridge_recovery_work, bridge_recover); | |
254 | INIT_COMPLETION(bridge_comp); | |
255 | #endif | |
256 | ||
257 | #ifdef CONFIG_PM | |
258 | /* Initialize the wait queue */ | |
259 | bridge_suspend_data.suspended = 0; | |
260 | init_waitqueue_head(&bridge_suspend_data.suspend_wq); | |
261 | ||
b3d23688 | 262 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
263 | for (i = 0; i < 6; i++) |
264 | pdata->mpu_speed[i] = vdd1_rate_table_bridge[i].rate; | |
265 | ||
266 | err = cpufreq_register_notifier(&iva_clk_notifier, | |
267 | CPUFREQ_TRANSITION_NOTIFIER); | |
268 | if (err) | |
269 | pr_err("%s: clk_notifier_register failed for iva2_ck\n", | |
270 | __func__); | |
271 | #endif | |
272 | #endif | |
273 | ||
274 | dsp_clk_init(); | |
275 | services_init(); | |
276 | ||
277 | drv_datap = kzalloc(sizeof(struct drv_data), GFP_KERNEL); | |
278 | if (!drv_datap) { | |
279 | err = -ENOMEM; | |
280 | goto err1; | |
281 | } | |
282 | ||
283 | drv_datap->shm_size = shm_size; | |
284 | drv_datap->tc_wordswapon = tc_wordswapon; | |
285 | ||
286 | if (base_img) { | |
287 | drv_datap->base_img = kmalloc(strlen(base_img) + 1, GFP_KERNEL); | |
288 | if (!drv_datap->base_img) { | |
289 | err = -ENOMEM; | |
290 | goto err2; | |
291 | } | |
292 | strncpy(drv_datap->base_img, base_img, strlen(base_img) + 1); | |
293 | } | |
294 | ||
295 | dev_set_drvdata(bridge, drv_datap); | |
296 | ||
297 | if (shm_size < 0x10000) { /* 64 KB */ | |
298 | err = -EINVAL; | |
299 | pr_err("%s: shm size must be at least 64 KB\n", __func__); | |
300 | goto err3; | |
301 | } | |
302 | dev_dbg(bridge, "%s: requested shm_size = 0x%x\n", __func__, shm_size); | |
303 | ||
304 | phys_membase = pdata->phys_mempool_base; | |
305 | phys_memsize = pdata->phys_mempool_size; | |
306 | if (phys_membase > 0 && phys_memsize > 0) | |
307 | mem_ext_phys_pool_init(phys_membase, phys_memsize); | |
308 | ||
309 | if (tc_wordswapon) | |
310 | dev_dbg(bridge, "%s: TC Word Swap is enabled\n", __func__); | |
311 | ||
312 | driver_context = dsp_init(&err); | |
313 | if (err) { | |
314 | pr_err("DSP Bridge driver initialization failed\n"); | |
315 | goto err4; | |
316 | } | |
317 | ||
318 | return 0; | |
319 | ||
320 | err4: | |
321 | mem_ext_phys_pool_release(); | |
322 | err3: | |
323 | kfree(drv_datap->base_img); | |
324 | err2: | |
325 | kfree(drv_datap); | |
326 | err1: | |
b3d23688 | 327 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
328 | cpufreq_unregister_notifier(&iva_clk_notifier, |
329 | CPUFREQ_TRANSITION_NOTIFIER); | |
330 | #endif | |
331 | dsp_clk_exit(); | |
332 | services_exit(); | |
333 | ||
334 | return err; | |
335 | } | |
336 | ||
337 | static int __devinit omap34_xx_bridge_probe(struct platform_device *pdev) | |
338 | { | |
339 | int err; | |
340 | dev_t dev = 0; | |
b3d23688 | 341 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
342 | int i = 0; |
343 | #endif | |
344 | ||
345 | omap_dspbridge_dev = pdev; | |
346 | ||
347 | /* Global bridge device */ | |
348 | bridge = &omap_dspbridge_dev->dev; | |
349 | ||
350 | /* Bridge low level initializations */ | |
351 | err = omap3_bridge_startup(pdev); | |
352 | if (err) | |
353 | goto err1; | |
354 | ||
355 | /* use 2.6 device model */ | |
356 | err = alloc_chrdev_region(&dev, 0, 1, driver_name); | |
357 | if (err) { | |
358 | pr_err("%s: Can't get major %d\n", __func__, driver_major); | |
359 | goto err1; | |
360 | } | |
361 | ||
362 | cdev_init(&bridge_cdev, &bridge_fops); | |
363 | bridge_cdev.owner = THIS_MODULE; | |
364 | ||
365 | err = cdev_add(&bridge_cdev, dev, 1); | |
366 | if (err) { | |
367 | pr_err("%s: Failed to add bridge device\n", __func__); | |
368 | goto err2; | |
369 | } | |
370 | ||
371 | /* udev support */ | |
372 | bridge_class = class_create(THIS_MODULE, "ti_bridge"); | |
373 | if (IS_ERR(bridge_class)) { | |
374 | pr_err("%s: Error creating bridge class\n", __func__); | |
375 | goto err3; | |
376 | } | |
377 | ||
378 | driver_major = MAJOR(dev); | |
379 | device_create(bridge_class, NULL, MKDEV(driver_major, 0), | |
380 | NULL, "DspBridge"); | |
381 | pr_info("DSP Bridge driver loaded\n"); | |
382 | ||
383 | return 0; | |
384 | ||
385 | err3: | |
386 | cdev_del(&bridge_cdev); | |
387 | err2: | |
388 | unregister_chrdev_region(dev, 1); | |
389 | err1: | |
390 | return err; | |
391 | } | |
392 | ||
393 | static int __devexit omap34_xx_bridge_remove(struct platform_device *pdev) | |
394 | { | |
395 | dev_t devno; | |
396 | bool ret; | |
397 | int status = 0; | |
398 | void *hdrv_obj = NULL; | |
399 | ||
400 | status = cfg_get_object((u32 *) &hdrv_obj, REG_DRV_OBJECT); | |
b66e0986 | 401 | if (status) |
7d55524d ORL |
402 | goto func_cont; |
403 | ||
b3d23688 | 404 | #ifdef CONFIG_TIDSPBRIDGE_DVFS |
7d55524d ORL |
405 | if (cpufreq_unregister_notifier(&iva_clk_notifier, |
406 | CPUFREQ_TRANSITION_NOTIFIER)) | |
407 | pr_err("%s: cpufreq_unregister_notifier failed for iva2_ck\n", | |
408 | __func__); | |
b3d23688 | 409 | #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */ |
7d55524d ORL |
410 | |
411 | if (driver_context) { | |
412 | /* Put the DSP in reset state */ | |
413 | ret = dsp_deinit(driver_context); | |
414 | driver_context = 0; | |
415 | DBC_ASSERT(ret == true); | |
416 | } | |
417 | ||
418 | func_cont: | |
419 | mem_ext_phys_pool_release(); | |
420 | ||
421 | dsp_clk_exit(); | |
422 | services_exit(); | |
423 | ||
424 | devno = MKDEV(driver_major, 0); | |
425 | cdev_del(&bridge_cdev); | |
426 | unregister_chrdev_region(devno, 1); | |
427 | if (bridge_class) { | |
428 | /* remove the device from sysfs */ | |
429 | device_destroy(bridge_class, MKDEV(driver_major, 0)); | |
430 | class_destroy(bridge_class); | |
431 | ||
432 | } | |
433 | return 0; | |
434 | } | |
435 | ||
436 | #ifdef CONFIG_PM | |
437 | static int BRIDGE_SUSPEND(struct platform_device *pdev, pm_message_t state) | |
438 | { | |
439 | u32 status; | |
440 | u32 command = PWR_EMERGENCYDEEPSLEEP; | |
441 | ||
442 | status = pwr_sleep_dsp(command, time_out); | |
b66e0986 | 443 | if (status) |
7d55524d ORL |
444 | return -1; |
445 | ||
446 | bridge_suspend_data.suspended = 1; | |
447 | return 0; | |
448 | } | |
449 | ||
450 | static int BRIDGE_RESUME(struct platform_device *pdev) | |
451 | { | |
452 | u32 status; | |
453 | ||
454 | status = pwr_wake_dsp(time_out); | |
b66e0986 | 455 | if (status) |
7d55524d ORL |
456 | return -1; |
457 | ||
458 | bridge_suspend_data.suspended = 0; | |
459 | wake_up(&bridge_suspend_data.suspend_wq); | |
460 | return 0; | |
461 | } | |
462 | #else | |
463 | #define BRIDGE_SUSPEND NULL | |
464 | #define BRIDGE_RESUME NULL | |
465 | #endif | |
466 | ||
467 | static struct platform_driver bridge_driver = { | |
468 | .driver = { | |
469 | .name = BRIDGE_NAME, | |
470 | }, | |
471 | .probe = omap34_xx_bridge_probe, | |
472 | .remove = __devexit_p(omap34_xx_bridge_remove), | |
473 | .suspend = BRIDGE_SUSPEND, | |
474 | .resume = BRIDGE_RESUME, | |
475 | }; | |
476 | ||
477 | static int __init bridge_init(void) | |
478 | { | |
479 | return platform_driver_register(&bridge_driver); | |
480 | } | |
481 | ||
482 | static void __exit bridge_exit(void) | |
483 | { | |
484 | platform_driver_unregister(&bridge_driver); | |
485 | } | |
486 | ||
487 | /* | |
488 | * This function is called when an application opens handle to the | |
489 | * bridge driver. | |
490 | */ | |
491 | static int bridge_open(struct inode *ip, struct file *filp) | |
492 | { | |
493 | int status = 0; | |
494 | struct process_context *pr_ctxt = NULL; | |
495 | ||
496 | /* | |
497 | * Allocate a new process context and insert it into global | |
498 | * process context list. | |
499 | */ | |
500 | ||
b3d23688 | 501 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
502 | if (recover) { |
503 | if (filp->f_flags & O_NONBLOCK || | |
504 | wait_for_completion_interruptible(&bridge_open_comp)) | |
505 | return -EBUSY; | |
506 | } | |
507 | #endif | |
508 | pr_ctxt = kzalloc(sizeof(struct process_context), GFP_KERNEL); | |
509 | if (pr_ctxt) { | |
510 | pr_ctxt->res_state = PROC_RES_ALLOCATED; | |
511 | spin_lock_init(&pr_ctxt->dmm_map_lock); | |
512 | INIT_LIST_HEAD(&pr_ctxt->dmm_map_list); | |
513 | spin_lock_init(&pr_ctxt->dmm_rsv_lock); | |
514 | INIT_LIST_HEAD(&pr_ctxt->dmm_rsv_list); | |
0624f52f ER |
515 | |
516 | pr_ctxt->node_id = kzalloc(sizeof(struct idr), GFP_KERNEL); | |
4ec09714 | 517 | if (pr_ctxt->node_id) { |
0624f52f | 518 | idr_init(pr_ctxt->node_id); |
4ec09714 ER |
519 | } else { |
520 | status = -ENOMEM; | |
521 | goto err; | |
522 | } | |
523 | ||
524 | pr_ctxt->stream_id = kzalloc(sizeof(struct idr), GFP_KERNEL); | |
525 | if (pr_ctxt->stream_id) | |
526 | idr_init(pr_ctxt->stream_id); | |
0624f52f ER |
527 | else |
528 | status = -ENOMEM; | |
7d55524d ORL |
529 | } else { |
530 | status = -ENOMEM; | |
531 | } | |
4ec09714 | 532 | err: |
7d55524d | 533 | filp->private_data = pr_ctxt; |
b3d23688 | 534 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
535 | if (!status) |
536 | atomic_inc(&bridge_cref); | |
537 | #endif | |
538 | return status; | |
539 | } | |
540 | ||
541 | /* | |
542 | * This function is called when an application closes handle to the bridge | |
543 | * driver. | |
544 | */ | |
545 | static int bridge_release(struct inode *ip, struct file *filp) | |
546 | { | |
547 | int status = 0; | |
548 | struct process_context *pr_ctxt; | |
549 | ||
550 | if (!filp->private_data) { | |
551 | status = -EIO; | |
552 | goto err; | |
553 | } | |
554 | ||
555 | pr_ctxt = filp->private_data; | |
556 | flush_signals(current); | |
557 | drv_remove_all_resources(pr_ctxt); | |
558 | proc_detach(pr_ctxt); | |
559 | kfree(pr_ctxt); | |
560 | ||
561 | filp->private_data = NULL; | |
562 | ||
563 | err: | |
b3d23688 | 564 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
565 | if (!atomic_dec_return(&bridge_cref)) |
566 | complete(&bridge_comp); | |
567 | #endif | |
568 | return status; | |
569 | } | |
570 | ||
571 | /* This function provides IO interface to the bridge driver. */ | |
572 | static long bridge_ioctl(struct file *filp, unsigned int code, | |
573 | unsigned long args) | |
574 | { | |
575 | int status; | |
576 | u32 retval = 0; | |
0cd343a4 | 577 | union trapped_args buf_in; |
7d55524d ORL |
578 | |
579 | DBC_REQUIRE(filp != NULL); | |
b3d23688 | 580 | #ifdef CONFIG_TIDSPBRIDGE_RECOVERY |
7d55524d ORL |
581 | if (recover) { |
582 | status = -EIO; | |
583 | goto err; | |
584 | } | |
585 | #endif | |
586 | #ifdef CONFIG_PM | |
587 | status = omap34_xxbridge_suspend_lockout(&bridge_suspend_data, filp); | |
588 | if (status != 0) | |
589 | return status; | |
590 | #endif | |
591 | ||
592 | if (!filp->private_data) { | |
593 | status = -EIO; | |
594 | goto err; | |
595 | } | |
596 | ||
0cd343a4 RS |
597 | status = copy_from_user(&buf_in, (union trapped_args *)args, |
598 | sizeof(union trapped_args)); | |
7d55524d ORL |
599 | |
600 | if (!status) { | |
601 | status = api_call_dev_ioctl(code, &buf_in, &retval, | |
602 | filp->private_data); | |
603 | ||
a741ea6e | 604 | if (!status) { |
7d55524d ORL |
605 | status = retval; |
606 | } else { | |
607 | dev_dbg(bridge, "%s: IOCTL Failed, code: 0x%x " | |
608 | "status 0x%x\n", __func__, code, status); | |
609 | status = -1; | |
610 | } | |
611 | ||
612 | } | |
613 | ||
614 | err: | |
615 | return status; | |
616 | } | |
617 | ||
618 | /* This function maps kernel space memory to user space memory. */ | |
619 | static int bridge_mmap(struct file *filp, struct vm_area_struct *vma) | |
620 | { | |
621 | u32 offset = vma->vm_pgoff << PAGE_SHIFT; | |
622 | u32 status; | |
623 | ||
624 | DBC_ASSERT(vma->vm_start < vma->vm_end); | |
625 | ||
626 | vma->vm_flags |= VM_RESERVED | VM_IO; | |
627 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | |
628 | ||
629 | dev_dbg(bridge, "%s: vm filp %p offset %x start %lx end %lx page_prot " | |
630 | "%lx flags %lx\n", __func__, filp, offset, | |
631 | vma->vm_start, vma->vm_end, vma->vm_page_prot, vma->vm_flags); | |
632 | ||
633 | status = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | |
634 | vma->vm_end - vma->vm_start, | |
635 | vma->vm_page_prot); | |
636 | if (status != 0) | |
637 | status = -EAGAIN; | |
638 | ||
639 | return status; | |
640 | } | |
641 | ||
642 | /* To remove all process resources before removing the process from the | |
643 | * process context list */ | |
e6890692 | 644 | int drv_remove_all_resources(void *process_ctxt) |
7d55524d ORL |
645 | { |
646 | int status = 0; | |
e6890692 | 647 | struct process_context *ctxt = (struct process_context *)process_ctxt; |
7d55524d ORL |
648 | drv_remove_all_strm_res_elements(ctxt); |
649 | drv_remove_all_node_res_elements(ctxt); | |
650 | drv_remove_all_dmm_res_elements(ctxt); | |
651 | ctxt->res_state = PROC_RES_FREED; | |
652 | return status; | |
653 | } | |
654 | ||
655 | /* Bridge driver initialization and de-initialization functions */ | |
656 | module_init(bridge_init); | |
657 | module_exit(bridge_exit); |