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