]>
Commit | Line | Data |
---|---|---|
7d55524d ORL |
1 | /* |
2 | * drv.c | |
3 | * | |
4 | * DSP-BIOS Bridge driver support functions for TI OMAP processors. | |
5 | * | |
6 | * DSP/BIOS Bridge resource allocation module. | |
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 | */ | |
2094f12d | 18 | #include <linux/types.h> |
0005391f | 19 | #include <linux/list.h> |
7d55524d ORL |
20 | |
21 | /* ----------------------------------- Host OS */ | |
22 | #include <dspbridge/host_os.h> | |
23 | ||
24 | /* ----------------------------------- DSP/BIOS Bridge */ | |
7d55524d ORL |
25 | #include <dspbridge/dbdefs.h> |
26 | ||
27 | /* ----------------------------------- Trace & Debug */ | |
28 | #include <dspbridge/dbc.h> | |
29 | ||
7d55524d ORL |
30 | /* ----------------------------------- This */ |
31 | #include <dspbridge/drv.h> | |
32 | #include <dspbridge/dev.h> | |
33 | ||
34 | #include <dspbridge/node.h> | |
35 | #include <dspbridge/proc.h> | |
36 | #include <dspbridge/strm.h> | |
37 | #include <dspbridge/nodepriv.h> | |
38 | #include <dspbridge/dspchnl.h> | |
39 | #include <dspbridge/resourcecleanup.h> | |
40 | ||
41 | /* ----------------------------------- Defines, Data Structures, Typedefs */ | |
42 | struct drv_object { | |
0005391f IN |
43 | struct list_head dev_list; |
44 | struct list_head dev_node_string; | |
7d55524d ORL |
45 | }; |
46 | ||
47 | /* | |
48 | * This is the Device Extension. Named with the Prefix | |
49 | * DRV_ since it is living in this module | |
50 | */ | |
51 | struct drv_ext { | |
52 | struct list_head link; | |
53 | char sz_string[MAXREGPATHLENGTH]; | |
54 | }; | |
55 | ||
56 | /* ----------------------------------- Globals */ | |
57 | static s32 refs; | |
58 | static bool ext_phys_mem_pool_enabled; | |
59 | struct ext_phys_mem_pool { | |
60 | u32 phys_mem_base; | |
61 | u32 phys_mem_size; | |
62 | u32 virt_mem_base; | |
63 | u32 next_phys_alloc_ptr; | |
64 | }; | |
65 | static struct ext_phys_mem_pool ext_mem_pool; | |
66 | ||
67 | /* ----------------------------------- Function Prototypes */ | |
68 | static int request_bridge_resources(struct cfg_hostres *res); | |
69 | ||
70 | ||
71 | /* GPP PROCESS CLEANUP CODE */ | |
72 | ||
0624f52f | 73 | static int drv_proc_free_node_res(int id, void *p, void *data); |
7d55524d ORL |
74 | |
75 | /* Allocate and add a node resource element | |
76 | * This function is called from .Node_Allocate. */ | |
e6890692 RS |
77 | int drv_insert_node_res_element(void *hnode, void *node_resource, |
78 | void *process_ctxt) | |
7d55524d ORL |
79 | { |
80 | struct node_res_object **node_res_obj = | |
e6890692 RS |
81 | (struct node_res_object **)node_resource; |
82 | struct process_context *ctxt = (struct process_context *)process_ctxt; | |
7d55524d | 83 | int status = 0; |
0624f52f | 84 | int retval; |
7d55524d ORL |
85 | |
86 | *node_res_obj = kzalloc(sizeof(struct node_res_object), GFP_KERNEL); | |
0624f52f ER |
87 | if (!*node_res_obj) { |
88 | status = -ENOMEM; | |
89 | goto func_end; | |
90 | } | |
7d55524d | 91 | |
ee4317f7 | 92 | (*node_res_obj)->node = hnode; |
0624f52f ER |
93 | retval = idr_get_new(ctxt->node_id, *node_res_obj, |
94 | &(*node_res_obj)->id); | |
95 | if (retval == -EAGAIN) { | |
96 | if (!idr_pre_get(ctxt->node_id, GFP_KERNEL)) { | |
97 | pr_err("%s: OUT OF MEMORY\n", __func__); | |
98 | status = -ENOMEM; | |
99 | goto func_end; | |
7d55524d | 100 | } |
7d55524d | 101 | |
0624f52f ER |
102 | retval = idr_get_new(ctxt->node_id, *node_res_obj, |
103 | &(*node_res_obj)->id); | |
104 | } | |
105 | if (retval) { | |
106 | pr_err("%s: FAILED, IDR is FULL\n", __func__); | |
107 | status = -EFAULT; | |
7d55524d | 108 | } |
0624f52f ER |
109 | func_end: |
110 | if (status) | |
111 | kfree(*node_res_obj); | |
7d55524d ORL |
112 | |
113 | return status; | |
114 | } | |
115 | ||
116 | /* Release all Node resources and its context | |
0624f52f ER |
117 | * Actual Node De-Allocation */ |
118 | static int drv_proc_free_node_res(int id, void *p, void *data) | |
7d55524d | 119 | { |
0624f52f ER |
120 | struct process_context *ctxt = data; |
121 | int status; | |
122 | struct node_res_object *node_res_obj = p; | |
7d55524d ORL |
123 | u32 node_state; |
124 | ||
0624f52f | 125 | if (node_res_obj->node_allocated) { |
ee4317f7 | 126 | node_state = node_get_state(node_res_obj->node); |
0624f52f ER |
127 | if (node_state <= NODE_DELETING) { |
128 | if ((node_state == NODE_RUNNING) || | |
129 | (node_state == NODE_PAUSED) || | |
130 | (node_state == NODE_TERMINATING)) | |
131 | node_terminate | |
ee4317f7 | 132 | (node_res_obj->node, &status); |
7d55524d | 133 | |
0624f52f | 134 | node_delete(node_res_obj, ctxt); |
7d55524d ORL |
135 | } |
136 | } | |
0624f52f ER |
137 | |
138 | return 0; | |
7d55524d ORL |
139 | } |
140 | ||
141 | /* Release all Mapped and Reserved DMM resources */ | |
e6890692 | 142 | int drv_remove_all_dmm_res_elements(void *process_ctxt) |
7d55524d | 143 | { |
e6890692 | 144 | struct process_context *ctxt = (struct process_context *)process_ctxt; |
7d55524d ORL |
145 | int status = 0; |
146 | struct dmm_map_object *temp_map, *map_obj; | |
a2890350 | 147 | struct dmm_rsv_object *temp_rsv, *rsv_obj; |
7d55524d ORL |
148 | |
149 | /* Free DMM mapped memory resources */ | |
150 | list_for_each_entry_safe(map_obj, temp_map, &ctxt->dmm_map_list, link) { | |
a534f17b | 151 | status = proc_un_map(ctxt->processor, |
7d55524d | 152 | (void *)map_obj->dsp_addr, ctxt); |
b66e0986 | 153 | if (status) |
7d55524d ORL |
154 | pr_err("%s: proc_un_map failed!" |
155 | " status = 0x%xn", __func__, status); | |
156 | } | |
a2890350 FC |
157 | |
158 | /* Free DMM reserved memory resources */ | |
159 | list_for_each_entry_safe(rsv_obj, temp_rsv, &ctxt->dmm_rsv_list, link) { | |
a534f17b | 160 | status = proc_un_reserve_memory(ctxt->processor, (void *) |
a2890350 FC |
161 | rsv_obj->dsp_reserved_addr, |
162 | ctxt); | |
163 | if (status) | |
164 | pr_err("%s: proc_un_reserve_memory failed!" | |
165 | " status = 0x%xn", __func__, status); | |
166 | } | |
7d55524d ORL |
167 | return status; |
168 | } | |
169 | ||
170 | /* Update Node allocation status */ | |
e6890692 | 171 | void drv_proc_node_update_status(void *node_resource, s32 status) |
7d55524d ORL |
172 | { |
173 | struct node_res_object *node_res_obj = | |
e6890692 RS |
174 | (struct node_res_object *)node_resource; |
175 | DBC_ASSERT(node_resource != NULL); | |
7d55524d ORL |
176 | node_res_obj->node_allocated = status; |
177 | } | |
178 | ||
179 | /* Update Node Heap status */ | |
e6890692 | 180 | void drv_proc_node_update_heap_status(void *node_resource, s32 status) |
7d55524d ORL |
181 | { |
182 | struct node_res_object *node_res_obj = | |
e6890692 RS |
183 | (struct node_res_object *)node_resource; |
184 | DBC_ASSERT(node_resource != NULL); | |
7d55524d ORL |
185 | node_res_obj->heap_allocated = status; |
186 | } | |
187 | ||
188 | /* Release all Node resources and its context | |
189 | * This is called from .bridge_release. | |
190 | */ | |
e6890692 | 191 | int drv_remove_all_node_res_elements(void *process_ctxt) |
7d55524d | 192 | { |
0624f52f | 193 | struct process_context *ctxt = process_ctxt; |
7d55524d | 194 | |
0624f52f ER |
195 | idr_for_each(ctxt->node_id, drv_proc_free_node_res, ctxt); |
196 | idr_destroy(ctxt->node_id); | |
7d55524d | 197 | |
0624f52f | 198 | return 0; |
7d55524d ORL |
199 | } |
200 | ||
201 | /* Allocate the STRM resource element | |
202 | * This is called after the actual resource is allocated | |
203 | */ | |
c8c1ad8c RS |
204 | int drv_proc_insert_strm_res_element(void *stream_obj, |
205 | void *strm_res, void *process_ctxt) | |
7d55524d ORL |
206 | { |
207 | struct strm_res_object **pstrm_res = | |
c8c1ad8c | 208 | (struct strm_res_object **)strm_res; |
e6890692 | 209 | struct process_context *ctxt = (struct process_context *)process_ctxt; |
7d55524d | 210 | int status = 0; |
4ec09714 | 211 | int retval; |
7d55524d ORL |
212 | |
213 | *pstrm_res = kzalloc(sizeof(struct strm_res_object), GFP_KERNEL); | |
4ec09714 | 214 | if (*pstrm_res == NULL) { |
7d55524d | 215 | status = -EFAULT; |
4ec09714 | 216 | goto func_end; |
7d55524d | 217 | } |
7d55524d | 218 | |
ee4317f7 | 219 | (*pstrm_res)->stream = stream_obj; |
4ec09714 ER |
220 | retval = idr_get_new(ctxt->stream_id, *pstrm_res, |
221 | &(*pstrm_res)->id); | |
222 | if (retval == -EAGAIN) { | |
223 | if (!idr_pre_get(ctxt->stream_id, GFP_KERNEL)) { | |
224 | pr_err("%s: OUT OF MEMORY\n", __func__); | |
225 | status = -ENOMEM; | |
226 | goto func_end; | |
227 | } | |
7d55524d | 228 | |
4ec09714 ER |
229 | retval = idr_get_new(ctxt->stream_id, *pstrm_res, |
230 | &(*pstrm_res)->id); | |
7d55524d | 231 | } |
4ec09714 ER |
232 | if (retval) { |
233 | pr_err("%s: FAILED, IDR is FULL\n", __func__); | |
234 | status = -EPERM; | |
235 | } | |
236 | ||
237 | func_end: | |
7d55524d ORL |
238 | return status; |
239 | } | |
240 | ||
4ec09714 | 241 | static int drv_proc_free_strm_res(int id, void *p, void *process_ctxt) |
7d55524d | 242 | { |
4ec09714 ER |
243 | struct process_context *ctxt = process_ctxt; |
244 | struct strm_res_object *strm_res = p; | |
7d55524d ORL |
245 | struct stream_info strm_info; |
246 | struct dsp_streaminfo user; | |
247 | u8 **ap_buffer = NULL; | |
248 | u8 *buf_ptr; | |
249 | u32 ul_bytes; | |
250 | u32 dw_arg; | |
251 | s32 ul_buf_size; | |
252 | ||
4ec09714 ER |
253 | if (strm_res->num_bufs) { |
254 | ap_buffer = kmalloc((strm_res->num_bufs * | |
255 | sizeof(u8 *)), GFP_KERNEL); | |
256 | if (ap_buffer) { | |
257 | strm_free_buffer(strm_res, | |
258 | ap_buffer, | |
259 | strm_res->num_bufs, | |
260 | ctxt); | |
261 | kfree(ap_buffer); | |
7d55524d | 262 | } |
7d55524d | 263 | } |
4ec09714 ER |
264 | strm_info.user_strm = &user; |
265 | user.number_bufs_in_stream = 0; | |
ee4317f7 | 266 | strm_get_info(strm_res->stream, &strm_info, sizeof(strm_info)); |
4ec09714 | 267 | while (user.number_bufs_in_stream--) |
ee4317f7 | 268 | strm_reclaim(strm_res->stream, &buf_ptr, &ul_bytes, |
4ec09714 ER |
269 | (u32 *) &ul_buf_size, &dw_arg); |
270 | strm_close(strm_res, ctxt); | |
271 | return 0; | |
7d55524d ORL |
272 | } |
273 | ||
4ec09714 ER |
274 | /* Release all Stream resources and its context |
275 | * This is called from .bridge_release. | |
276 | */ | |
277 | int drv_remove_all_strm_res_elements(void *process_ctxt) | |
7d55524d | 278 | { |
4ec09714 | 279 | struct process_context *ctxt = process_ctxt; |
7d55524d | 280 | |
4ec09714 ER |
281 | idr_for_each(ctxt->stream_id, drv_proc_free_strm_res, ctxt); |
282 | idr_destroy(ctxt->stream_id); | |
7d55524d | 283 | |
4ec09714 | 284 | return 0; |
7d55524d ORL |
285 | } |
286 | ||
287 | /* Updating the stream resource element */ | |
c8c1ad8c | 288 | int drv_proc_update_strm_res(u32 num_bufs, void *strm_resources) |
7d55524d ORL |
289 | { |
290 | int status = 0; | |
291 | struct strm_res_object **strm_res = | |
c8c1ad8c | 292 | (struct strm_res_object **)strm_resources; |
7d55524d ORL |
293 | |
294 | (*strm_res)->num_bufs = num_bufs; | |
295 | return status; | |
296 | } | |
297 | ||
298 | /* GPP PROCESS CLEANUP CODE END */ | |
299 | ||
300 | /* | |
301 | * ======== = drv_create ======== = | |
302 | * Purpose: | |
303 | * DRV Object gets created only once during Driver Loading. | |
304 | */ | |
e6bf74f0 | 305 | int drv_create(struct drv_object **drv_obj) |
7d55524d ORL |
306 | { |
307 | int status = 0; | |
308 | struct drv_object *pdrv_object = NULL; | |
b87561f7 | 309 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
7d55524d | 310 | |
e436d07d | 311 | DBC_REQUIRE(drv_obj != NULL); |
7d55524d ORL |
312 | DBC_REQUIRE(refs > 0); |
313 | ||
314 | pdrv_object = kzalloc(sizeof(struct drv_object), GFP_KERNEL); | |
315 | if (pdrv_object) { | |
316 | /* Create and Initialize List of device objects */ | |
0005391f IN |
317 | INIT_LIST_HEAD(&pdrv_object->dev_list); |
318 | INIT_LIST_HEAD(&pdrv_object->dev_node_string); | |
7d55524d ORL |
319 | } else { |
320 | status = -ENOMEM; | |
321 | } | |
b87561f7 IGC |
322 | /* Store the DRV Object in the driver data */ |
323 | if (!status) { | |
324 | if (drv_datap) { | |
325 | drv_datap->drv_object = (void *)pdrv_object; | |
326 | } else { | |
327 | status = -EPERM; | |
328 | pr_err("%s: Failed to store DRV object\n", __func__); | |
329 | } | |
330 | } | |
331 | ||
a741ea6e | 332 | if (!status) { |
e436d07d | 333 | *drv_obj = pdrv_object; |
7d55524d | 334 | } else { |
7d55524d ORL |
335 | /* Free the DRV Object */ |
336 | kfree(pdrv_object); | |
337 | } | |
338 | ||
b66e0986 | 339 | DBC_ENSURE(status || pdrv_object); |
7d55524d ORL |
340 | return status; |
341 | } | |
342 | ||
343 | /* | |
344 | * ======== drv_exit ======== | |
345 | * Purpose: | |
346 | * Discontinue usage of the DRV module. | |
347 | */ | |
348 | void drv_exit(void) | |
349 | { | |
350 | DBC_REQUIRE(refs > 0); | |
351 | ||
352 | refs--; | |
353 | ||
354 | DBC_ENSURE(refs >= 0); | |
355 | } | |
356 | ||
357 | /* | |
358 | * ======== = drv_destroy ======== = | |
359 | * purpose: | |
360 | * Invoked during bridge de-initialization | |
361 | */ | |
e6890692 | 362 | int drv_destroy(struct drv_object *driver_obj) |
7d55524d ORL |
363 | { |
364 | int status = 0; | |
e6890692 | 365 | struct drv_object *pdrv_object = (struct drv_object *)driver_obj; |
b87561f7 | 366 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
7d55524d ORL |
367 | |
368 | DBC_REQUIRE(refs > 0); | |
369 | DBC_REQUIRE(pdrv_object); | |
370 | ||
7d55524d | 371 | kfree(pdrv_object); |
b87561f7 IGC |
372 | /* Update the DRV Object in the driver data */ |
373 | if (drv_datap) { | |
374 | drv_datap->drv_object = NULL; | |
375 | } else { | |
376 | status = -EPERM; | |
377 | pr_err("%s: Failed to store DRV object\n", __func__); | |
378 | } | |
7d55524d ORL |
379 | |
380 | return status; | |
381 | } | |
382 | ||
383 | /* | |
384 | * ======== drv_get_dev_object ======== | |
385 | * Purpose: | |
386 | * Given a index, returns a handle to DevObject from the list. | |
387 | */ | |
388 | int drv_get_dev_object(u32 index, struct drv_object *hdrv_obj, | |
e436d07d | 389 | struct dev_object **device_obj) |
7d55524d ORL |
390 | { |
391 | int status = 0; | |
b3d23688 | 392 | #ifdef CONFIG_TIDSPBRIDGE_DEBUG |
7d55524d ORL |
393 | /* used only for Assertions and debug messages */ |
394 | struct drv_object *pdrv_obj = (struct drv_object *)hdrv_obj; | |
395 | #endif | |
396 | struct dev_object *dev_obj; | |
397 | u32 i; | |
398 | DBC_REQUIRE(pdrv_obj); | |
e436d07d | 399 | DBC_REQUIRE(device_obj != NULL); |
7d55524d ORL |
400 | DBC_REQUIRE(index >= 0); |
401 | DBC_REQUIRE(refs > 0); | |
0005391f | 402 | DBC_ASSERT(!(list_empty(&pdrv_obj->dev_list))); |
7d55524d ORL |
403 | |
404 | dev_obj = (struct dev_object *)drv_get_first_dev_object(); | |
405 | for (i = 0; i < index; i++) { | |
406 | dev_obj = | |
407 | (struct dev_object *)drv_get_next_dev_object((u32) dev_obj); | |
408 | } | |
409 | if (dev_obj) { | |
e436d07d | 410 | *device_obj = (struct dev_object *)dev_obj; |
7d55524d | 411 | } else { |
e436d07d | 412 | *device_obj = NULL; |
7d55524d ORL |
413 | status = -EPERM; |
414 | } | |
415 | ||
416 | return status; | |
417 | } | |
418 | ||
419 | /* | |
420 | * ======== drv_get_first_dev_object ======== | |
421 | * Purpose: | |
422 | * Retrieve the first Device Object handle from an internal linked list of | |
423 | * of DEV_OBJECTs maintained by DRV. | |
424 | */ | |
425 | u32 drv_get_first_dev_object(void) | |
426 | { | |
427 | u32 dw_dev_object = 0; | |
428 | struct drv_object *pdrv_obj; | |
73b87a91 | 429 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
7d55524d | 430 | |
73b87a91 IGC |
431 | if (drv_datap && drv_datap->drv_object) { |
432 | pdrv_obj = drv_datap->drv_object; | |
0005391f IN |
433 | if (!list_empty(&pdrv_obj->dev_list)) |
434 | dw_dev_object = (u32) pdrv_obj->dev_list.next; | |
73b87a91 IGC |
435 | } else { |
436 | pr_err("%s: Failed to retrieve the object handle\n", __func__); | |
7d55524d ORL |
437 | } |
438 | ||
439 | return dw_dev_object; | |
440 | } | |
441 | ||
442 | /* | |
443 | * ======== DRV_GetFirstDevNodeString ======== | |
444 | * Purpose: | |
445 | * Retrieve the first Device Extension from an internal linked list of | |
446 | * of Pointer to dev_node Strings maintained by DRV. | |
447 | */ | |
448 | u32 drv_get_first_dev_extension(void) | |
449 | { | |
450 | u32 dw_dev_extension = 0; | |
451 | struct drv_object *pdrv_obj; | |
73b87a91 | 452 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
7d55524d | 453 | |
73b87a91 IGC |
454 | if (drv_datap && drv_datap->drv_object) { |
455 | pdrv_obj = drv_datap->drv_object; | |
0005391f | 456 | if (!list_empty(&pdrv_obj->dev_node_string)) { |
7d55524d | 457 | dw_dev_extension = |
0005391f | 458 | (u32) pdrv_obj->dev_node_string.next; |
7d55524d | 459 | } |
73b87a91 IGC |
460 | } else { |
461 | pr_err("%s: Failed to retrieve the object handle\n", __func__); | |
7d55524d ORL |
462 | } |
463 | ||
464 | return dw_dev_extension; | |
465 | } | |
466 | ||
467 | /* | |
468 | * ======== drv_get_next_dev_object ======== | |
469 | * Purpose: | |
470 | * Retrieve the next Device Object handle from an internal linked list of | |
471 | * of DEV_OBJECTs maintained by DRV, after having previously called | |
472 | * drv_get_first_dev_object() and zero or more DRV_GetNext. | |
473 | */ | |
474 | u32 drv_get_next_dev_object(u32 hdev_obj) | |
475 | { | |
476 | u32 dw_next_dev_object = 0; | |
477 | struct drv_object *pdrv_obj; | |
73b87a91 | 478 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
0005391f | 479 | struct list_head *curr; |
7d55524d | 480 | |
73b87a91 IGC |
481 | if (drv_datap && drv_datap->drv_object) { |
482 | pdrv_obj = drv_datap->drv_object; | |
0005391f IN |
483 | if (!list_empty(&pdrv_obj->dev_list)) { |
484 | curr = (struct list_head *)hdev_obj; | |
485 | if (list_is_last(curr, &pdrv_obj->dev_list)) | |
486 | return 0; | |
487 | dw_next_dev_object = (u32) curr->next; | |
7d55524d | 488 | } |
73b87a91 IGC |
489 | } else { |
490 | pr_err("%s: Failed to retrieve the object handle\n", __func__); | |
7d55524d | 491 | } |
73b87a91 | 492 | |
7d55524d ORL |
493 | return dw_next_dev_object; |
494 | } | |
495 | ||
496 | /* | |
497 | * ======== drv_get_next_dev_extension ======== | |
498 | * Purpose: | |
499 | * Retrieve the next Device Extension from an internal linked list of | |
500 | * of pointer to DevNodeString maintained by DRV, after having previously | |
501 | * called drv_get_first_dev_extension() and zero or more | |
502 | * drv_get_next_dev_extension(). | |
503 | */ | |
e6890692 | 504 | u32 drv_get_next_dev_extension(u32 dev_extension) |
7d55524d ORL |
505 | { |
506 | u32 dw_dev_extension = 0; | |
507 | struct drv_object *pdrv_obj; | |
73b87a91 | 508 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
0005391f | 509 | struct list_head *curr; |
7d55524d | 510 | |
73b87a91 IGC |
511 | if (drv_datap && drv_datap->drv_object) { |
512 | pdrv_obj = drv_datap->drv_object; | |
0005391f IN |
513 | if (!list_empty(&pdrv_obj->dev_node_string)) { |
514 | curr = (struct list_head *)dev_extension; | |
515 | if (list_is_last(curr, &pdrv_obj->dev_node_string)) | |
516 | return 0; | |
517 | dw_dev_extension = (u32) curr->next; | |
7d55524d | 518 | } |
73b87a91 IGC |
519 | } else { |
520 | pr_err("%s: Failed to retrieve the object handle\n", __func__); | |
7d55524d ORL |
521 | } |
522 | ||
523 | return dw_dev_extension; | |
524 | } | |
525 | ||
526 | /* | |
527 | * ======== drv_init ======== | |
528 | * Purpose: | |
529 | * Initialize DRV module private state. | |
530 | */ | |
531 | int drv_init(void) | |
532 | { | |
533 | s32 ret = 1; /* function return value */ | |
534 | ||
535 | DBC_REQUIRE(refs >= 0); | |
536 | ||
537 | if (ret) | |
538 | refs++; | |
539 | ||
540 | DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs >= 0))); | |
541 | ||
542 | return ret; | |
543 | } | |
544 | ||
545 | /* | |
546 | * ======== drv_insert_dev_object ======== | |
547 | * Purpose: | |
548 | * Insert a DevObject into the list of Manager object. | |
549 | */ | |
e6890692 | 550 | int drv_insert_dev_object(struct drv_object *driver_obj, |
7d55524d ORL |
551 | struct dev_object *hdev_obj) |
552 | { | |
e6890692 | 553 | struct drv_object *pdrv_object = (struct drv_object *)driver_obj; |
7d55524d ORL |
554 | |
555 | DBC_REQUIRE(refs > 0); | |
556 | DBC_REQUIRE(hdev_obj != NULL); | |
557 | DBC_REQUIRE(pdrv_object); | |
7d55524d | 558 | |
0005391f | 559 | list_add_tail((struct list_head *)hdev_obj, &pdrv_object->dev_list); |
7d55524d | 560 | |
a741ea6e | 561 | return 0; |
7d55524d ORL |
562 | } |
563 | ||
564 | /* | |
565 | * ======== drv_remove_dev_object ======== | |
566 | * Purpose: | |
567 | * Search for and remove a DeviceObject from the given list of DRV | |
568 | * objects. | |
569 | */ | |
e6890692 | 570 | int drv_remove_dev_object(struct drv_object *driver_obj, |
7d55524d ORL |
571 | struct dev_object *hdev_obj) |
572 | { | |
573 | int status = -EPERM; | |
e6890692 | 574 | struct drv_object *pdrv_object = (struct drv_object *)driver_obj; |
7d55524d ORL |
575 | struct list_head *cur_elem; |
576 | ||
577 | DBC_REQUIRE(refs > 0); | |
578 | DBC_REQUIRE(pdrv_object); | |
579 | DBC_REQUIRE(hdev_obj != NULL); | |
580 | ||
0005391f | 581 | DBC_REQUIRE(!list_empty(&pdrv_object->dev_list)); |
7d55524d ORL |
582 | |
583 | /* Search list for p_proc_object: */ | |
0005391f | 584 | list_for_each(cur_elem, &pdrv_object->dev_list) { |
7d55524d ORL |
585 | /* If found, remove it. */ |
586 | if ((struct dev_object *)cur_elem == hdev_obj) { | |
0005391f | 587 | list_del(cur_elem); |
7d55524d ORL |
588 | status = 0; |
589 | break; | |
590 | } | |
591 | } | |
7d55524d ORL |
592 | |
593 | return status; | |
594 | } | |
595 | ||
596 | /* | |
597 | * ======== drv_request_resources ======== | |
598 | * Purpose: | |
599 | * Requests resources from the OS. | |
600 | */ | |
aa09b091 | 601 | int drv_request_resources(u32 dw_context, u32 *dev_node_strg) |
7d55524d ORL |
602 | { |
603 | int status = 0; | |
604 | struct drv_object *pdrv_object; | |
605 | struct drv_ext *pszdev_node; | |
73b87a91 | 606 | struct drv_data *drv_datap = dev_get_drvdata(bridge); |
7d55524d ORL |
607 | |
608 | DBC_REQUIRE(dw_context != 0); | |
aa09b091 | 609 | DBC_REQUIRE(dev_node_strg != NULL); |
7d55524d ORL |
610 | |
611 | /* | |
25985edc | 612 | * Allocate memory to hold the string. This will live until |
7d55524d ORL |
613 | * it is freed in the Release resources. Update the driver object |
614 | * list. | |
615 | */ | |
616 | ||
73b87a91 IGC |
617 | if (!drv_datap || !drv_datap->drv_object) |
618 | status = -ENODATA; | |
619 | else | |
620 | pdrv_object = drv_datap->drv_object; | |
621 | ||
a741ea6e | 622 | if (!status) { |
7d55524d ORL |
623 | pszdev_node = kzalloc(sizeof(struct drv_ext), GFP_KERNEL); |
624 | if (pszdev_node) { | |
7d55524d ORL |
625 | strncpy(pszdev_node->sz_string, |
626 | (char *)dw_context, MAXREGPATHLENGTH - 1); | |
627 | pszdev_node->sz_string[MAXREGPATHLENGTH - 1] = '\0'; | |
628 | /* Update the Driver Object List */ | |
aa09b091 | 629 | *dev_node_strg = (u32) pszdev_node->sz_string; |
0005391f IN |
630 | list_add_tail(&pszdev_node->link, |
631 | &pdrv_object->dev_node_string); | |
7d55524d ORL |
632 | } else { |
633 | status = -ENOMEM; | |
aa09b091 | 634 | *dev_node_strg = 0; |
7d55524d ORL |
635 | } |
636 | } else { | |
637 | dev_dbg(bridge, "%s: Failed to get Driver Object from Registry", | |
638 | __func__); | |
aa09b091 | 639 | *dev_node_strg = 0; |
7d55524d ORL |
640 | } |
641 | ||
a741ea6e | 642 | DBC_ENSURE((!status && dev_node_strg != NULL && |
0005391f | 643 | !list_empty(&pdrv_object->dev_node_string)) || |
b66e0986 | 644 | (status && *dev_node_strg == 0)); |
7d55524d ORL |
645 | |
646 | return status; | |
647 | } | |
648 | ||
649 | /* | |
650 | * ======== drv_release_resources ======== | |
651 | * Purpose: | |
652 | * Releases resources from the OS. | |
653 | */ | |
654 | int drv_release_resources(u32 dw_context, struct drv_object *hdrv_obj) | |
655 | { | |
656 | int status = 0; | |
7d55524d ORL |
657 | struct drv_ext *pszdev_node; |
658 | ||
659 | /* | |
660 | * Irrespective of the status go ahead and clean it | |
661 | * The following will over write the status. | |
662 | */ | |
663 | for (pszdev_node = (struct drv_ext *)drv_get_first_dev_extension(); | |
664 | pszdev_node != NULL; pszdev_node = (struct drv_ext *) | |
665 | drv_get_next_dev_extension((u32) pszdev_node)) { | |
7d55524d ORL |
666 | if ((u32) pszdev_node == dw_context) { |
667 | /* Found it */ | |
668 | /* Delete from the Driver object list */ | |
0005391f IN |
669 | list_del(&pszdev_node->link); |
670 | kfree(pszdev_node); | |
7d55524d ORL |
671 | break; |
672 | } | |
7d55524d ORL |
673 | } |
674 | return status; | |
675 | } | |
676 | ||
677 | /* | |
678 | * ======== request_bridge_resources ======== | |
679 | * Purpose: | |
680 | * Reserves shared memory for bridge. | |
681 | */ | |
682 | static int request_bridge_resources(struct cfg_hostres *res) | |
683 | { | |
7d55524d ORL |
684 | struct cfg_hostres *host_res = res; |
685 | ||
686 | /* num_mem_windows must not be more than CFG_MAXMEMREGISTERS */ | |
687 | host_res->num_mem_windows = 2; | |
688 | ||
689 | /* First window is for DSP internal memory */ | |
5108de0a RS |
690 | dev_dbg(bridge, "mem_base[0] 0x%x\n", host_res->mem_base[0]); |
691 | dev_dbg(bridge, "mem_base[3] 0x%x\n", host_res->mem_base[3]); | |
692 | dev_dbg(bridge, "dmmu_base %p\n", host_res->dmmu_base); | |
7d55524d ORL |
693 | |
694 | /* for 24xx base port is not mapping the mamory for DSP | |
695 | * internal memory TODO Do a ioremap here */ | |
696 | /* Second window is for DSP external memory shared with MPU */ | |
697 | ||
698 | /* These are hard-coded values */ | |
699 | host_res->birq_registers = 0; | |
700 | host_res->birq_attrib = 0; | |
5108de0a | 701 | host_res->offset_for_monitor = 0; |
b4da7fc3 | 702 | host_res->chnl_offset = 0; |
7d55524d | 703 | /* CHNL_MAXCHANNELS */ |
5108de0a | 704 | host_res->num_chnls = CHNL_MAXCHANNELS; |
b4da7fc3 | 705 | host_res->chnl_buf_size = 0x400; |
7d55524d | 706 | |
a741ea6e | 707 | return 0; |
7d55524d ORL |
708 | } |
709 | ||
710 | /* | |
711 | * ======== drv_request_bridge_res_dsp ======== | |
712 | * Purpose: | |
713 | * Reserves shared memory for bridge. | |
714 | */ | |
715 | int drv_request_bridge_res_dsp(void **phost_resources) | |
716 | { | |
717 | int status = 0; | |
718 | struct cfg_hostres *host_res; | |
719 | u32 dw_buff_size; | |
720 | u32 dma_addr; | |
721 | u32 shm_size; | |
722 | struct drv_data *drv_datap = dev_get_drvdata(bridge); | |
723 | ||
724 | dw_buff_size = sizeof(struct cfg_hostres); | |
725 | ||
726 | host_res = kzalloc(dw_buff_size, GFP_KERNEL); | |
727 | ||
728 | if (host_res != NULL) { | |
729 | request_bridge_resources(host_res); | |
730 | /* num_mem_windows must not be more than CFG_MAXMEMREGISTERS */ | |
731 | host_res->num_mem_windows = 4; | |
732 | ||
5108de0a RS |
733 | host_res->mem_base[0] = 0; |
734 | host_res->mem_base[2] = (u32) ioremap(OMAP_DSP_MEM1_BASE, | |
7d55524d | 735 | OMAP_DSP_MEM1_SIZE); |
5108de0a | 736 | host_res->mem_base[3] = (u32) ioremap(OMAP_DSP_MEM2_BASE, |
7d55524d | 737 | OMAP_DSP_MEM2_SIZE); |
5108de0a | 738 | host_res->mem_base[4] = (u32) ioremap(OMAP_DSP_MEM3_BASE, |
7d55524d | 739 | OMAP_DSP_MEM3_SIZE); |
5108de0a | 740 | host_res->per_base = ioremap(OMAP_PER_CM_BASE, |
7d55524d | 741 | OMAP_PER_CM_SIZE); |
5108de0a | 742 | host_res->per_pm_base = (u32) ioremap(OMAP_PER_PRM_BASE, |
7d55524d | 743 | OMAP_PER_PRM_SIZE); |
b4da7fc3 | 744 | host_res->core_pm_base = (u32) ioremap(OMAP_CORE_PRM_BASE, |
7d55524d | 745 | OMAP_CORE_PRM_SIZE); |
5108de0a | 746 | host_res->dmmu_base = ioremap(OMAP_DMMU_BASE, |
9d4f81a7 | 747 | OMAP_DMMU_SIZE); |
7d55524d | 748 | |
5108de0a RS |
749 | dev_dbg(bridge, "mem_base[0] 0x%x\n", |
750 | host_res->mem_base[0]); | |
751 | dev_dbg(bridge, "mem_base[1] 0x%x\n", | |
752 | host_res->mem_base[1]); | |
753 | dev_dbg(bridge, "mem_base[2] 0x%x\n", | |
754 | host_res->mem_base[2]); | |
755 | dev_dbg(bridge, "mem_base[3] 0x%x\n", | |
756 | host_res->mem_base[3]); | |
757 | dev_dbg(bridge, "mem_base[4] 0x%x\n", | |
758 | host_res->mem_base[4]); | |
759 | dev_dbg(bridge, "dmmu_base %p\n", host_res->dmmu_base); | |
7d55524d ORL |
760 | |
761 | shm_size = drv_datap->shm_size; | |
762 | if (shm_size >= 0x10000) { | |
763 | /* Allocate Physically contiguous, | |
764 | * non-cacheable memory */ | |
5108de0a | 765 | host_res->mem_base[1] = |
7d55524d ORL |
766 | (u32) mem_alloc_phys_mem(shm_size, 0x100000, |
767 | &dma_addr); | |
5108de0a | 768 | if (host_res->mem_base[1] == 0) { |
7d55524d ORL |
769 | status = -ENOMEM; |
770 | pr_err("shm reservation Failed\n"); | |
771 | } else { | |
5108de0a RS |
772 | host_res->mem_length[1] = shm_size; |
773 | host_res->mem_phys[1] = dma_addr; | |
7d55524d ORL |
774 | |
775 | dev_dbg(bridge, "%s: Bridge shm address 0x%x " | |
776 | "dma_addr %x size %x\n", __func__, | |
5108de0a | 777 | host_res->mem_base[1], |
7d55524d ORL |
778 | dma_addr, shm_size); |
779 | } | |
780 | } | |
a741ea6e | 781 | if (!status) { |
7d55524d ORL |
782 | /* These are hard-coded values */ |
783 | host_res->birq_registers = 0; | |
784 | host_res->birq_attrib = 0; | |
5108de0a | 785 | host_res->offset_for_monitor = 0; |
b4da7fc3 | 786 | host_res->chnl_offset = 0; |
7d55524d | 787 | /* CHNL_MAXCHANNELS */ |
5108de0a | 788 | host_res->num_chnls = CHNL_MAXCHANNELS; |
b4da7fc3 | 789 | host_res->chnl_buf_size = 0x400; |
7d55524d ORL |
790 | dw_buff_size = sizeof(struct cfg_hostres); |
791 | } | |
792 | *phost_resources = host_res; | |
793 | } | |
794 | /* End Mem alloc */ | |
795 | return status; | |
796 | } | |
797 | ||
fb6aabb7 | 798 | void mem_ext_phys_pool_init(u32 pool_phys_base, u32 pool_size) |
7d55524d ORL |
799 | { |
800 | u32 pool_virt_base; | |
801 | ||
802 | /* get the virtual address for the physical memory pool passed */ | |
fb6aabb7 | 803 | pool_virt_base = (u32) ioremap(pool_phys_base, pool_size); |
7d55524d ORL |
804 | |
805 | if ((void **)pool_virt_base == NULL) { | |
806 | pr_err("%s: external physical memory map failed\n", __func__); | |
807 | ext_phys_mem_pool_enabled = false; | |
808 | } else { | |
fb6aabb7 RS |
809 | ext_mem_pool.phys_mem_base = pool_phys_base; |
810 | ext_mem_pool.phys_mem_size = pool_size; | |
7d55524d | 811 | ext_mem_pool.virt_mem_base = pool_virt_base; |
fb6aabb7 | 812 | ext_mem_pool.next_phys_alloc_ptr = pool_phys_base; |
7d55524d ORL |
813 | ext_phys_mem_pool_enabled = true; |
814 | } | |
815 | } | |
816 | ||
817 | void mem_ext_phys_pool_release(void) | |
818 | { | |
819 | if (ext_phys_mem_pool_enabled) { | |
820 | iounmap((void *)(ext_mem_pool.virt_mem_base)); | |
821 | ext_phys_mem_pool_enabled = false; | |
822 | } | |
823 | } | |
824 | ||
825 | /* | |
826 | * ======== mem_ext_phys_mem_alloc ======== | |
827 | * Purpose: | |
828 | * Allocate physically contiguous, uncached memory from external memory pool | |
829 | */ | |
830 | ||
e6bf74f0 | 831 | static void *mem_ext_phys_mem_alloc(u32 bytes, u32 align, u32 * phys_addr) |
7d55524d ORL |
832 | { |
833 | u32 new_alloc_ptr; | |
834 | u32 offset; | |
835 | u32 virt_addr; | |
836 | ||
837 | if (align == 0) | |
838 | align = 1; | |
839 | ||
840 | if (bytes > ((ext_mem_pool.phys_mem_base + ext_mem_pool.phys_mem_size) | |
841 | - ext_mem_pool.next_phys_alloc_ptr)) { | |
13b18c29 | 842 | phys_addr = NULL; |
7d55524d ORL |
843 | return NULL; |
844 | } else { | |
845 | offset = (ext_mem_pool.next_phys_alloc_ptr & (align - 1)); | |
846 | if (offset == 0) | |
847 | new_alloc_ptr = ext_mem_pool.next_phys_alloc_ptr; | |
848 | else | |
849 | new_alloc_ptr = (ext_mem_pool.next_phys_alloc_ptr) + | |
850 | (align - offset); | |
851 | if ((new_alloc_ptr + bytes) <= | |
852 | (ext_mem_pool.phys_mem_base + ext_mem_pool.phys_mem_size)) { | |
853 | /* we can allocate */ | |
13b18c29 | 854 | *phys_addr = new_alloc_ptr; |
7d55524d ORL |
855 | ext_mem_pool.next_phys_alloc_ptr = |
856 | new_alloc_ptr + bytes; | |
857 | virt_addr = | |
858 | ext_mem_pool.virt_mem_base + (new_alloc_ptr - | |
859 | ext_mem_pool. | |
860 | phys_mem_base); | |
861 | return (void *)virt_addr; | |
862 | } else { | |
13b18c29 | 863 | *phys_addr = 0; |
7d55524d ORL |
864 | return NULL; |
865 | } | |
866 | } | |
867 | } | |
868 | ||
869 | /* | |
870 | * ======== mem_alloc_phys_mem ======== | |
871 | * Purpose: | |
872 | * Allocate physically contiguous, uncached memory | |
873 | */ | |
0cd343a4 | 874 | void *mem_alloc_phys_mem(u32 byte_size, u32 align_mask, |
e6bf74f0 | 875 | u32 *physical_address) |
7d55524d ORL |
876 | { |
877 | void *va_mem = NULL; | |
878 | dma_addr_t pa_mem; | |
879 | ||
880 | if (byte_size > 0) { | |
881 | if (ext_phys_mem_pool_enabled) { | |
0cd343a4 | 882 | va_mem = mem_ext_phys_mem_alloc(byte_size, align_mask, |
7d55524d ORL |
883 | (u32 *) &pa_mem); |
884 | } else | |
885 | va_mem = dma_alloc_coherent(NULL, byte_size, &pa_mem, | |
886 | GFP_KERNEL); | |
887 | if (va_mem == NULL) | |
13b18c29 | 888 | *physical_address = 0; |
7d55524d | 889 | else |
13b18c29 | 890 | *physical_address = pa_mem; |
7d55524d ORL |
891 | } |
892 | return va_mem; | |
893 | } | |
894 | ||
895 | /* | |
896 | * ======== mem_free_phys_mem ======== | |
897 | * Purpose: | |
898 | * Free the given block of physically contiguous memory. | |
899 | */ | |
318b5df9 | 900 | void mem_free_phys_mem(void *virtual_address, u32 physical_address, |
7d55524d ORL |
901 | u32 byte_size) |
902 | { | |
318b5df9 | 903 | DBC_REQUIRE(virtual_address != NULL); |
7d55524d ORL |
904 | |
905 | if (!ext_phys_mem_pool_enabled) | |
318b5df9 | 906 | dma_free_coherent(NULL, byte_size, virtual_address, |
13b18c29 | 907 | physical_address); |
7d55524d | 908 | } |