diff options
Diffstat (limited to 'drivers/media/video/tegra/mediaserver')
-rw-r--r-- | drivers/media/video/tegra/mediaserver/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/video/tegra/mediaserver/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/video/tegra/mediaserver/tegra_mediaserver.c | 556 |
3 files changed, 569 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/mediaserver/Kconfig b/drivers/media/video/tegra/mediaserver/Kconfig new file mode 100644 index 00000000000..9e60a5b49cd --- /dev/null +++ b/drivers/media/video/tegra/mediaserver/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | config TEGRA_MEDIASERVER | ||
2 | bool "Tegra Media Server support" | ||
3 | depends on ARCH_TEGRA && TEGRA_RPC | ||
4 | default y | ||
5 | help | ||
6 | Enables support for the multiple OpenMAX clients. Exports the | ||
7 | interface on the device node /dev/tegra_mediaserver. | ||
8 | |||
9 | If unsure, say Y | ||
10 | |||
diff --git a/drivers/media/video/tegra/mediaserver/Makefile b/drivers/media/video/tegra/mediaserver/Makefile new file mode 100644 index 00000000000..ed24e91932b --- /dev/null +++ b/drivers/media/video/tegra/mediaserver/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | obj-$(CONFIG_TEGRA_MEDIASERVER) += tegra_mediaserver.o | ||
3 | |||
diff --git a/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c new file mode 100644 index 00000000000..f6f5b1ec8b7 --- /dev/null +++ b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c | |||
@@ -0,0 +1,556 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 NVIDIA Corp. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/uaccess.h> | ||
21 | #include <linux/mm.h> | ||
22 | |||
23 | #include <linux/tegra_mediaserver.h> | ||
24 | #include "../avp/nvavp.h" | ||
25 | #include "../../../../video/tegra/nvmap/nvmap.h" | ||
26 | |||
27 | #define CHECK_STATUS(e, tag) \ | ||
28 | do { if (e < 0) goto tag; } while (0) | ||
29 | |||
30 | #define CHECK_NULL(ptr, tag) \ | ||
31 | do { if (!ptr) goto tag; } while (0) | ||
32 | |||
33 | #define CHECK_CONDITION(c, tag) \ | ||
34 | do { if (c) goto tag; } while (0) | ||
35 | |||
36 | struct tegra_mediasrv_block { | ||
37 | struct list_head entry; | ||
38 | struct tegra_mediaserver_block_info block; | ||
39 | }; | ||
40 | |||
41 | struct tegra_mediasrv_iram { | ||
42 | struct list_head entry; | ||
43 | struct tegra_mediaserver_iram_info iram; | ||
44 | }; | ||
45 | |||
46 | struct tegra_mediasrv_node { | ||
47 | struct tegra_mediasrv_info *mediasrv; | ||
48 | struct list_head blocks; | ||
49 | int nr_iram_shared; | ||
50 | }; | ||
51 | |||
52 | struct tegra_mediasrv_manager { | ||
53 | struct tegra_avp_lib lib; | ||
54 | struct tegra_rpc_info *rpc; | ||
55 | struct tegra_sema_info *sema; | ||
56 | }; | ||
57 | |||
58 | struct tegra_mediasrv_info { | ||
59 | int minor; | ||
60 | struct mutex lock; | ||
61 | struct nvmap_client *nvmap; | ||
62 | struct tegra_avp_info *avp; | ||
63 | struct tegra_mediasrv_manager manager; | ||
64 | int nr_nodes; | ||
65 | int nr_blocks; | ||
66 | struct tegra_mediaserver_iram_info iram; /* only one supported */ | ||
67 | int nr_iram_shared; | ||
68 | }; | ||
69 | |||
70 | static struct tegra_mediasrv_info *mediasrv_info; | ||
71 | |||
72 | |||
73 | /* | ||
74 | * File entry points | ||
75 | */ | ||
76 | static int mediasrv_open(struct inode *inode, struct file *file) | ||
77 | { | ||
78 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
79 | struct tegra_mediasrv_node *node = NULL; | ||
80 | struct tegra_mediasrv_manager *manager = &mediasrv->manager; | ||
81 | struct tegra_avp_lib *lib = &manager->lib; | ||
82 | int e; | ||
83 | |||
84 | node = kzalloc(sizeof(struct tegra_mediasrv_node), GFP_KERNEL); | ||
85 | CHECK_NULL(node, node_alloc_fail); | ||
86 | INIT_LIST_HEAD(&node->blocks); | ||
87 | node->mediasrv = mediasrv; | ||
88 | |||
89 | mutex_lock(&mediasrv->lock); | ||
90 | nonseekable_open(inode, file); | ||
91 | |||
92 | if (!mediasrv->nr_nodes) { | ||
93 | e = tegra_sema_open(&manager->sema); | ||
94 | CHECK_STATUS(e, fail); | ||
95 | |||
96 | e = tegra_rpc_open(&manager->rpc); | ||
97 | CHECK_STATUS(e, fail); | ||
98 | |||
99 | e = tegra_rpc_port_create(manager->rpc, "NVMM_MANAGER_SRV", | ||
100 | manager->sema); | ||
101 | CHECK_STATUS(e, fail); | ||
102 | |||
103 | e = tegra_avp_open(&mediasrv->avp); | ||
104 | CHECK_STATUS(e, fail); | ||
105 | |||
106 | memcpy(lib->name, "nvmm_manager.axf\0", | ||
107 | strlen("nvmm_manager.axf") + 1); | ||
108 | lib->args = &mediasrv; | ||
109 | lib->args_len = sizeof(unsigned long); | ||
110 | e = tegra_avp_load_lib(mediasrv->avp, lib); | ||
111 | CHECK_STATUS(e, fail); | ||
112 | |||
113 | e = tegra_rpc_port_connect(manager->rpc, 50000); | ||
114 | CHECK_STATUS(e, fail); | ||
115 | } | ||
116 | |||
117 | mediasrv->nr_nodes++; | ||
118 | try_module_get(THIS_MODULE); | ||
119 | |||
120 | mutex_unlock(&mediasrv->lock); | ||
121 | |||
122 | file->private_data = node; | ||
123 | |||
124 | return 0; | ||
125 | |||
126 | fail: | ||
127 | if (lib->handle) { | ||
128 | tegra_avp_unload_lib(mediasrv->avp, lib->handle); | ||
129 | lib->handle = 0; | ||
130 | } | ||
131 | |||
132 | if (mediasrv->avp) { | ||
133 | tegra_avp_release(mediasrv->avp); | ||
134 | mediasrv->avp = NULL; | ||
135 | } | ||
136 | |||
137 | if (manager->rpc) { | ||
138 | tegra_rpc_release(manager->rpc); | ||
139 | manager->rpc = NULL; | ||
140 | } | ||
141 | if (manager->sema) { | ||
142 | tegra_sema_release(manager->sema); | ||
143 | manager->sema = NULL; | ||
144 | } | ||
145 | |||
146 | kfree(node); | ||
147 | |||
148 | mutex_unlock(&mediasrv->lock); | ||
149 | return e; | ||
150 | |||
151 | node_alloc_fail: | ||
152 | e = -ENOMEM; | ||
153 | return e; | ||
154 | } | ||
155 | |||
156 | static int mediasrv_release(struct inode *inode, struct file *file) | ||
157 | { | ||
158 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
159 | struct tegra_mediasrv_node *node = file->private_data; | ||
160 | struct tegra_mediasrv_block *block; | ||
161 | struct list_head *entry; | ||
162 | struct list_head *temp; | ||
163 | u32 message[2]; | ||
164 | int e; | ||
165 | |||
166 | mutex_lock(&mediasrv->lock); | ||
167 | |||
168 | list_for_each_safe(entry, temp, &node->blocks) { | ||
169 | block = list_entry(entry, struct tegra_mediasrv_block, entry); | ||
170 | |||
171 | pr_info("Improperly closed block found!"); | ||
172 | pr_info(" NVMM Block Handle: 0x%08x\n", | ||
173 | block->block.nvmm_block_handle); | ||
174 | pr_info(" AVP Block Handle: 0x%08x\n", | ||
175 | block->block.avp_block_handle); | ||
176 | |||
177 | message[0] = 1; /* NvmmManagerMsgType_AbnormalTerm */ | ||
178 | message[1] = block->block.avp_block_handle; | ||
179 | |||
180 | e = tegra_rpc_write(mediasrv->manager.rpc, (u8 *)message, | ||
181 | sizeof(u32) * 2); | ||
182 | pr_info("Abnormal termination message result: %d\n", e); | ||
183 | |||
184 | if (block->block.avp_block_library_handle) { | ||
185 | e = tegra_avp_unload_lib(mediasrv->avp, | ||
186 | block->block.avp_block_library_handle); | ||
187 | pr_info("Unload block (0x%08x) result: %d\n", | ||
188 | block->block.avp_block_library_handle, e); | ||
189 | } | ||
190 | |||
191 | if (block->block.service_library_handle) { | ||
192 | e = tegra_avp_unload_lib(mediasrv->avp, | ||
193 | block->block.service_library_handle); | ||
194 | pr_info("Unload service (0x%08x) result: %d\n", | ||
195 | block->block.service_library_handle, e); | ||
196 | } | ||
197 | |||
198 | mediasrv->nr_blocks--; | ||
199 | list_del(entry); | ||
200 | kfree(block); | ||
201 | } | ||
202 | |||
203 | mediasrv->nr_iram_shared -= node->nr_iram_shared; | ||
204 | if (mediasrv->iram.rm_handle && !mediasrv->nr_iram_shared) { | ||
205 | pr_info("Improperly freed shared iram found!"); | ||
206 | nvmap_unpin_ids(mediasrv->nvmap, 1, &mediasrv->iram.rm_handle); | ||
207 | nvmap_free_handle_id(mediasrv->nvmap, mediasrv->iram.rm_handle); | ||
208 | mediasrv->iram.rm_handle = 0; | ||
209 | mediasrv->iram.physical_address = 0; | ||
210 | } | ||
211 | |||
212 | kfree(node); | ||
213 | mediasrv->nr_nodes--; | ||
214 | if (!mediasrv->nr_nodes) { | ||
215 | struct tegra_mediasrv_manager *manager = &mediasrv->manager; | ||
216 | |||
217 | tegra_avp_unload_lib(mediasrv->avp, manager->lib.handle); | ||
218 | manager->lib.handle = 0; | ||
219 | |||
220 | tegra_avp_release(mediasrv->avp); | ||
221 | mediasrv->avp = NULL; | ||
222 | |||
223 | tegra_rpc_release(manager->rpc); | ||
224 | manager->rpc = NULL; | ||
225 | |||
226 | tegra_sema_release(manager->sema); | ||
227 | manager->sema = NULL; | ||
228 | } | ||
229 | |||
230 | mutex_unlock(&mediasrv->lock); | ||
231 | module_put(THIS_MODULE); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int mediasrv_alloc(struct tegra_mediasrv_node *node, | ||
236 | union tegra_mediaserver_alloc_info *in, | ||
237 | union tegra_mediaserver_alloc_info *out) | ||
238 | { | ||
239 | struct tegra_mediasrv_info *mediasrv = node->mediasrv; | ||
240 | int e; | ||
241 | |||
242 | switch (in->in.tegra_mediaserver_resource_type) { | ||
243 | case TEGRA_MEDIASERVER_RESOURCE_BLOCK: | ||
244 | { | ||
245 | struct tegra_mediasrv_block *block; | ||
246 | |||
247 | block = kzalloc(sizeof(struct tegra_mediasrv_block), | ||
248 | GFP_KERNEL); | ||
249 | CHECK_NULL(block, block_alloc_fail); | ||
250 | |||
251 | block->block = in->in.u.block; | ||
252 | list_add(&block->entry, &node->blocks); | ||
253 | goto block_done; | ||
254 | |||
255 | block_alloc_fail: | ||
256 | e = -ENOMEM; | ||
257 | goto fail; | ||
258 | |||
259 | block_done: | ||
260 | mediasrv->nr_blocks++; | ||
261 | out->out.u.block.count = mediasrv->nr_blocks; | ||
262 | } | ||
263 | break; | ||
264 | |||
265 | case TEGRA_MEDIASERVER_RESOURCE_IRAM: | ||
266 | { | ||
267 | if (in->in.u.iram.tegra_mediaserver_iram_type == | ||
268 | TEGRA_MEDIASERVER_IRAM_SHARED) { | ||
269 | if (!mediasrv->nr_iram_shared) { | ||
270 | size_t align, size; | ||
271 | struct nvmap_handle_ref *r = NULL; | ||
272 | unsigned long id; | ||
273 | int physical_address; | ||
274 | |||
275 | size = PAGE_ALIGN(in->in.u.iram.size); | ||
276 | r = nvmap_create_handle(mediasrv->nvmap, size); | ||
277 | CHECK_CONDITION((r < 0), | ||
278 | iram_shared_handle_fail); | ||
279 | |||
280 | id = nvmap_ref_to_id(r); | ||
281 | |||
282 | align = max_t(size_t, in->in.u.iram.alignment, | ||
283 | PAGE_SIZE); | ||
284 | e = nvmap_alloc_handle_id(mediasrv->nvmap, id, | ||
285 | NVMAP_HEAP_CARVEOUT_IRAM, align, | ||
286 | NVMAP_HANDLE_WRITE_COMBINE); | ||
287 | CHECK_STATUS(e, iram_shared_alloc_fail); | ||
288 | |||
289 | physical_address = | ||
290 | nvmap_pin_ids(mediasrv->nvmap, 1, &id); | ||
291 | CHECK_CONDITION((physical_address < 0), | ||
292 | iram_shared_pin_fail); | ||
293 | |||
294 | mediasrv->iram.rm_handle = id; | ||
295 | mediasrv->iram.physical_address = | ||
296 | physical_address; | ||
297 | goto iram_shared_done; | ||
298 | |||
299 | iram_shared_pin_fail: | ||
300 | e = physical_address; | ||
301 | iram_shared_alloc_fail: | ||
302 | nvmap_free_handle_id(mediasrv->nvmap, id); | ||
303 | iram_shared_handle_fail: | ||
304 | goto fail; | ||
305 | } | ||
306 | |||
307 | iram_shared_done: | ||
308 | out->out.u.iram.rm_handle = mediasrv->iram.rm_handle; | ||
309 | out->out.u.iram.physical_address = | ||
310 | mediasrv->iram.physical_address; | ||
311 | mediasrv->nr_iram_shared++; | ||
312 | node->nr_iram_shared++; | ||
313 | } else if (in->in.u.iram.tegra_mediaserver_iram_type == | ||
314 | TEGRA_MEDIASERVER_IRAM_SCRATCH) { | ||
315 | e = -EINVAL; | ||
316 | goto fail; | ||
317 | } | ||
318 | } | ||
319 | break; | ||
320 | |||
321 | default: | ||
322 | { | ||
323 | e = -EINVAL; | ||
324 | goto fail; | ||
325 | } | ||
326 | break; | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | |||
331 | fail: | ||
332 | return e; | ||
333 | } | ||
334 | |||
335 | static void mediasrv_free(struct tegra_mediasrv_node *node, | ||
336 | union tegra_mediaserver_free_info *in) | ||
337 | { | ||
338 | struct tegra_mediasrv_info *mediasrv = node->mediasrv; | ||
339 | |||
340 | switch (in->in.tegra_mediaserver_resource_type) { | ||
341 | case TEGRA_MEDIASERVER_RESOURCE_BLOCK: | ||
342 | { | ||
343 | struct tegra_mediasrv_block *block = NULL; | ||
344 | struct tegra_mediasrv_block *temp; | ||
345 | struct list_head *entry; | ||
346 | |||
347 | list_for_each(entry, &node->blocks) { | ||
348 | temp = list_entry(entry, struct tegra_mediasrv_block, | ||
349 | entry); | ||
350 | if (temp->block.nvmm_block_handle != | ||
351 | in->in.u.nvmm_block_handle) | ||
352 | continue; | ||
353 | |||
354 | block = temp; | ||
355 | break; | ||
356 | } | ||
357 | |||
358 | CHECK_NULL(block, done); | ||
359 | list_del(&block->entry); | ||
360 | kfree(block); | ||
361 | } | ||
362 | break; | ||
363 | |||
364 | case TEGRA_MEDIASERVER_RESOURCE_IRAM: | ||
365 | { | ||
366 | if (in->in.u.iram_rm_handle == mediasrv->iram.rm_handle && | ||
367 | node->nr_iram_shared) { | ||
368 | node->nr_iram_shared--; | ||
369 | mediasrv->nr_iram_shared--; | ||
370 | |||
371 | if (!mediasrv->nr_iram_shared) { | ||
372 | nvmap_unpin_ids(mediasrv->nvmap, 1, | ||
373 | &mediasrv->iram.rm_handle); | ||
374 | nvmap_free_handle_id(mediasrv->nvmap, | ||
375 | mediasrv->iram.rm_handle); | ||
376 | mediasrv->iram.rm_handle = 0; | ||
377 | mediasrv->iram.physical_address = 0; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | else | ||
382 | goto done; | ||
383 | } | ||
384 | break; | ||
385 | } | ||
386 | |||
387 | done: | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | static int mediasrv_update_block_info( | ||
392 | struct tegra_mediasrv_node *node, | ||
393 | union tegra_mediaserver_update_block_info *in | ||
394 | ) | ||
395 | { | ||
396 | struct tegra_mediasrv_block *entry = NULL; | ||
397 | struct tegra_mediasrv_block *block = NULL; | ||
398 | int e; | ||
399 | |||
400 | list_for_each_entry(entry, &node->blocks, entry) { | ||
401 | if (entry->block.nvmm_block_handle != in->in.nvmm_block_handle) | ||
402 | continue; | ||
403 | |||
404 | block = entry; | ||
405 | break; | ||
406 | } | ||
407 | |||
408 | CHECK_NULL(block, fail); | ||
409 | |||
410 | block->block = in->in; | ||
411 | return 0; | ||
412 | |||
413 | fail: | ||
414 | e = -EINVAL; | ||
415 | return e; | ||
416 | } | ||
417 | |||
418 | static long mediasrv_unlocked_ioctl(struct file *file, unsigned int cmd, | ||
419 | unsigned long arg) | ||
420 | { | ||
421 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
422 | struct tegra_mediasrv_node *node = file->private_data; | ||
423 | int e = -ENODEV; | ||
424 | |||
425 | mutex_lock(&mediasrv->lock); | ||
426 | |||
427 | switch (cmd) { | ||
428 | case TEGRA_MEDIASERVER_IOCTL_ALLOC: | ||
429 | { | ||
430 | union tegra_mediaserver_alloc_info in, out; | ||
431 | e = copy_from_user(&in, (void __user *)arg, sizeof(in)); | ||
432 | CHECK_CONDITION(e, copy_fail); | ||
433 | e = mediasrv_alloc(node, &in, &out); | ||
434 | CHECK_STATUS(e, fail); | ||
435 | e = copy_to_user((void __user *)arg, &out, sizeof(out)); | ||
436 | CHECK_CONDITION(e, copy_fail); | ||
437 | } | ||
438 | break; | ||
439 | |||
440 | case TEGRA_MEDIASERVER_IOCTL_FREE: | ||
441 | { | ||
442 | union tegra_mediaserver_free_info in; | ||
443 | e = copy_from_user(&in, (void __user *)arg, sizeof(in)); | ||
444 | CHECK_CONDITION(e, copy_fail); | ||
445 | mediasrv_free(node, &in); | ||
446 | } | ||
447 | break; | ||
448 | |||
449 | case TEGRA_MEDIASERVER_IOCTL_UPDATE_BLOCK_INFO: | ||
450 | { | ||
451 | union tegra_mediaserver_update_block_info in; | ||
452 | e = copy_from_user(&in, (void __user *)arg, sizeof(in)); | ||
453 | CHECK_CONDITION(e, copy_fail); | ||
454 | e = mediasrv_update_block_info(node, &in); | ||
455 | CHECK_CONDITION(e, fail); | ||
456 | } | ||
457 | break; | ||
458 | |||
459 | default: | ||
460 | { | ||
461 | e = -ENODEV; | ||
462 | goto fail; | ||
463 | } | ||
464 | break; | ||
465 | } | ||
466 | |||
467 | mutex_unlock(&mediasrv->lock); | ||
468 | return 0; | ||
469 | |||
470 | copy_fail: | ||
471 | e = -EFAULT; | ||
472 | fail: | ||
473 | mutex_unlock(&mediasrv->lock); | ||
474 | return e; | ||
475 | } | ||
476 | |||
477 | /* | ||
478 | * Kernel structures and entry points | ||
479 | */ | ||
480 | static const struct file_operations mediaserver_fops = { | ||
481 | .owner = THIS_MODULE, | ||
482 | .open = mediasrv_open, | ||
483 | .release = mediasrv_release, | ||
484 | .unlocked_ioctl = mediasrv_unlocked_ioctl, | ||
485 | }; | ||
486 | |||
487 | static struct miscdevice mediaserver_misc_device = { | ||
488 | .minor = MISC_DYNAMIC_MINOR, | ||
489 | .name = "tegra_mediaserver", | ||
490 | .fops = &mediaserver_fops, | ||
491 | }; | ||
492 | |||
493 | static int __init tegra_mediaserver_init(void) | ||
494 | { | ||
495 | struct tegra_mediasrv_info *mediasrv; | ||
496 | int e = 0; | ||
497 | |||
498 | CHECK_NULL(!mediasrv_info, busy); | ||
499 | |||
500 | mediasrv = kzalloc(sizeof(struct tegra_mediasrv_info), GFP_KERNEL); | ||
501 | CHECK_NULL(mediasrv, alloc_fail); | ||
502 | |||
503 | mediasrv->nvmap = nvmap_create_client(nvmap_dev, "tegra_mediaserver"); | ||
504 | CHECK_NULL(mediasrv, nvmap_create_fail); | ||
505 | |||
506 | e = misc_register(&mediaserver_misc_device); | ||
507 | CHECK_STATUS(e, register_fail); | ||
508 | |||
509 | mediasrv->nr_nodes = 0; | ||
510 | mutex_init(&mediasrv->lock); | ||
511 | |||
512 | mediasrv_info = mediasrv; | ||
513 | goto done; | ||
514 | |||
515 | nvmap_create_fail: | ||
516 | e = -ENOMEM; | ||
517 | kfree(mediasrv); | ||
518 | goto done; | ||
519 | |||
520 | register_fail: | ||
521 | nvmap_client_put(mediasrv->nvmap); | ||
522 | kfree(mediasrv); | ||
523 | goto done; | ||
524 | |||
525 | alloc_fail: | ||
526 | e = -ENOMEM; | ||
527 | goto done; | ||
528 | |||
529 | busy: | ||
530 | e = -EBUSY; | ||
531 | goto done; | ||
532 | |||
533 | done: | ||
534 | return e; | ||
535 | } | ||
536 | |||
537 | void __exit tegra_mediaserver_cleanup(void) | ||
538 | { | ||
539 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
540 | int e; | ||
541 | |||
542 | e = misc_deregister(&mediaserver_misc_device); | ||
543 | CHECK_STATUS(e, fail); | ||
544 | |||
545 | nvmap_client_put(mediasrv->nvmap); | ||
546 | kfree(mediasrv); | ||
547 | mediasrv_info = NULL; | ||
548 | |||
549 | fail: | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | module_init(tegra_mediaserver_init); | ||
554 | module_exit(tegra_mediaserver_cleanup); | ||
555 | MODULE_LICENSE("GPL"); | ||
556 | |||