rk29: vpu: add author info
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rk29 / vpu_mem.c
1 /* arch/arm/mach-rk29/vpu_mem.c\r
2  *\r
3  * Copyright (C) 2010 ROCKCHIP, Inc.\r
4  * author: chenhengming chm@rock-chips.com\r
5  *\r
6  * This software is licensed under the terms of the GNU General Public\r
7  * License version 2, as published by the Free Software Foundation, and\r
8  * may be copied, distributed, and modified under those terms.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  */\r
16 \r
17 #include <linux/miscdevice.h>\r
18 #include <linux/platform_device.h>\r
19 #include <linux/fs.h>\r
20 #include <linux/file.h>\r
21 #include <linux/mm.h>\r
22 #include <linux/list.h>\r
23 #include <linux/debugfs.h>\r
24 #include <linux/mempolicy.h>\r
25 #include <linux/sched.h>\r
26 #include <asm/io.h>\r
27 #include <asm/uaccess.h>\r
28 #include <asm/cacheflush.h>\r
29 \r
30 #include <mach/vpu_mem.h>\r
31 \r
32 #define VPU_MEM_MIN_ALLOC               PAGE_SIZE\r
33 #define VPU_MEM_IS_PAGE_ALIGNED(addr)   (!((addr) & (~PAGE_MASK)))\r
34 \r
35 #define VPU_MEM_DEBUG                   0\r
36 #define VPU_MEM_DEBUG_MSGS              0\r
37 \r
38 #if VPU_MEM_DEBUG_MSGS\r
39 #define DLOG(fmt,args...) \\r
40         do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \\r
41                     ##args); } \\r
42         while (0)\r
43 #else\r
44 #define DLOG(x...) do {} while (0)\r
45 #endif\r
46 \r
47 /**\r
48  * struct for process session which connect to vpu_mem\r
49  *\r
50  * @author ChenHengming (2011-4-11)\r
51  */\r
52 typedef struct vpu_mem_session {\r
53     /* a list of memory region used posted by current process */\r
54     struct list_head list_used;\r
55     struct list_head list_post;\r
56     /* a linked list of data so we can access them for debugging */\r
57     struct list_head list_session;\r
58         /* process id of teh mapping process */\r
59         pid_t pid;\r
60 } vdm_session;\r
61 \r
62 /**\r
63  * global region info\r
64  */\r
65 typedef struct vpu_mem_region_info {\r
66     struct list_head index_list;        /* link to index list use for search */\r
67     int used;\r
68     int post;\r
69     int index;\r
70     int pfn;\r
71 } vdm_region;\r
72 \r
73 /**\r
74  * struct for region information\r
75  * this struct should be modified with bitmap lock\r
76  */\r
77 typedef struct vpu_mem_link_info {\r
78     struct list_head session_link;      /* link to vpu_mem_session list */\r
79     struct list_head status_link;       /* link to vdm_info.status list use for search */\r
80     vdm_region *region;\r
81     int link_post;\r
82     int link_used;\r
83     int index;\r
84     int pfn;\r
85 } vdm_link;\r
86 \r
87 /**\r
88  * struct for global vpu memory info\r
89  */\r
90 typedef struct vpu_mem_info {\r
91         struct miscdevice dev;\r
92         /* physical start address of the remaped vpu_mem space */\r
93         unsigned long base;\r
94         /* vitual start address of the remaped vpu_mem space */\r
95         unsigned char __iomem *vbase;\r
96         /* total size of the vpu_mem space */\r
97         unsigned long size;\r
98         /* number of entries in the vpu_mem space */\r
99         unsigned long num_entries;\r
100         /* indicates maps of this region should be cached, if a mix of\r
101          * cached and uncached is desired, set this and open the device with\r
102          * O_SYNC to get an uncached region */\r
103         unsigned cached;\r
104         unsigned buffered;\r
105         /*\r
106          * vdm_session init only store the free region but use a vdm_session for convenience\r
107          */\r
108     vdm_session status;\r
109         struct list_head list_index;        /* sort by index */\r
110     struct list_head list_free;         /* free region list */\r
111     struct list_head list_session;      /* session list */\r
112     struct rw_semaphore rw_sem;\r
113 } vdm_info;\r
114 \r
115 static vdm_info vpu_mem;\r
116 static int vpu_mem_count;\r
117 static int vpu_mem_over = 0;\r
118 \r
119 #define vdm_used                (vpu_mem.status.list_used)\r
120 #define vdm_post                (vpu_mem.status.list_post)\r
121 #define vdm_index               (vpu_mem.list_index)\r
122 #define vdm_free                (vpu_mem.list_free)\r
123 #define vdm_proc                (vpu_mem.list_session)\r
124 #define vdm_rwsem               (vpu_mem.rw_sem)\r
125 #define is_free_region(x)       ((0 == (x)->used) && (0 == (x)->post))\r
126 \r
127 /**\r
128  * vpu memory info dump:\r
129  * first dump global info, then dump each session info\r
130  *\r
131  * @author ChenHengming (2011-4-20)\r
132  */\r
133 static void dump_status(void)\r
134 {\r
135     vdm_link    *link, *tmp_link;\r
136     vdm_region  *region, *tmp_region;\r
137     vdm_session *session, *tmp_session;\r
138 \r
139     printk("vpu mem status dump :\n\n");\r
140 \r
141     // °´ index ´òÓ¡È«²¿ region\r
142     printk("region:\n");\r
143     list_for_each_entry_safe(region, tmp_region, &vdm_index, index_list) {\r
144         printk("        idx %6d pfn %6d used %3d post %3d\n",\r
145             region->index, region->pfn, region->used, region->post);\r
146     }\r
147     printk("free  :\n");\r
148     list_for_each_entry_safe(link, tmp_link, &vdm_free, status_link) {\r
149         printk("        idx %6d pfn %6d used %3d post %3d\n",\r
150             link->index, link->pfn, link->link_used, link->link_post);\r
151     }\r
152     printk("used  :\n");\r
153     list_for_each_entry_safe(link, tmp_link, &vdm_used, status_link) {\r
154         printk("        idx %6d pfn %6d used %3d post %3d\n",\r
155             link->index, link->pfn, link->link_used, link->link_post);\r
156     }\r
157     printk("post  :\n");\r
158     list_for_each_entry_safe(link, tmp_link, &vdm_post, status_link) {\r
159         printk("        idx %6d pfn %6d used %3d post %3d\n",\r
160             link->index, link->pfn, link->link_used, link->link_post);\r
161     }\r
162 \r
163     // ´òÓ¡ vpu_mem_info ÖеÄÈ«²¿ session µÄ region Õ¼ÓÃÇé¿ö\r
164     list_for_each_entry_safe(session, tmp_session, &vdm_proc, list_session) {\r
165         printk("pid: %d\n", session->pid);\r
166 \r
167         list_for_each_entry_safe(link, tmp_link, &session->list_used, session_link) {\r
168             printk("used: idx %6d pfn %6d used %3d\n",\r
169                 link->index, link->pfn, link->link_used);\r
170         }\r
171         list_for_each_entry_safe(link, tmp_link, &session->list_post, session_link) {\r
172             printk("post: idx %6d pfn %6d post %3d\n",\r
173                 link->index, link->pfn, link->link_post);\r
174         }\r
175     }\r
176 }\r
177 \r
178 /**\r
179  * find used link in a session\r
180  *\r
181  * @author ChenHengming (2011-4-18)\r
182  *\r
183  * @param session\r
184  * @param index\r
185  *\r
186  * @return vdm_link*\r
187  */\r
188 static vdm_link *find_used_link(vdm_session *session, int index)\r
189 {\r
190     vdm_link *pos, *n;\r
191 \r
192     list_for_each_entry_safe(pos, n, &session->list_used, session_link) {\r
193         if (index == pos->index) {\r
194             DLOG("found index %d ptr %x\n", index, pos);\r
195             return pos;\r
196         }\r
197     }\r
198 \r
199     return NULL;\r
200 }\r
201 \r
202 /**\r
203  * find post link from vpu_mem's vdm_post list\r
204  *\r
205  * @author ChenHengming (2011-4-18)\r
206  *\r
207  * @param index\r
208  *\r
209  * @return vdm_link*\r
210  */\r
211 static vdm_link *find_post_link(int index)\r
212 {\r
213     vdm_link *pos, *n;\r
214 \r
215     list_for_each_entry_safe(pos, n, &vdm_post, status_link) {\r
216         if (index == pos->index) {\r
217             return pos;\r
218         }\r
219     }\r
220 \r
221     return NULL;\r
222 }\r
223 \r
224 /**\r
225  * find free link from vpu_mem's vdm_free list\r
226  *\r
227  * @author Administrator (2011-4-19)\r
228  *\r
229  * @param index\r
230  *\r
231  * @return vdm_link*\r
232  */\r
233 static vdm_link *find_free_link(int index)\r
234 {\r
235     vdm_link *pos, *n;\r
236 \r
237     list_for_each_entry_safe(pos, n, &vdm_free, status_link) {\r
238         if (index == pos->index) {\r
239             return pos;\r
240         }\r
241     }\r
242 \r
243     return NULL;\r
244 }\r
245 /**\r
246  * insert a region into the index list for search\r
247  *\r
248  * @author ChenHengming (2011-4-18)\r
249  *\r
250  * @param region\r
251  *\r
252  * @return int\r
253  */\r
254 static int _insert_region_index(vdm_region *region)\r
255 {\r
256     int index = region->index;\r
257     int last = -1;\r
258     int next;\r
259     vdm_region *tmp, *n;\r
260 \r
261     if (list_empty(&vdm_index)) {\r
262         DLOG("index list is empty, insert first region\n");\r
263         list_add_tail(&region->index_list, &vdm_index);\r
264         return 0;\r
265     }\r
266 \r
267     list_for_each_entry_safe(tmp, n, &vdm_index, index_list) {\r
268         next = tmp->index;\r
269         DLOG("insert index %d pfn %d last %d next %d ptr %x\n", index, region->pfn, last, next, tmp);\r
270         if ((last < index) && (index < next))  {\r
271             DLOG("Done\n");\r
272             list_add_tail(&region->index_list, &tmp->index_list);\r
273             return 0;\r
274         }\r
275         last = next;\r
276     }\r
277 \r
278     printk(KERN_ERR "_insert_region_by_index %d fail!\n", index);\r
279     dump_status();\r
280     return -1;\r
281 }\r
282 \r
283 /**\r
284  * insert a link into vdm_free list, indexed by vdm_link->index\r
285  *\r
286  * @author ChenHengming (2011-4-20)\r
287  *\r
288  * @param link\r
289  */\r
290 static void _insert_link_status_free(vdm_link *link)\r
291 {\r
292     int index = link->index;\r
293     int last = -1;\r
294     int next;\r
295     vdm_link *tmp, *n;\r
296 \r
297     if (list_empty(&vdm_free)) {\r
298         DLOG("free list is empty, list_add_tail first region\n");\r
299         list_add_tail(&link->status_link, &vdm_free);\r
300         return ;\r
301     }\r
302 \r
303     list_for_each_entry_safe(tmp, n, &vdm_free, status_link) {\r
304         next = tmp->index;\r
305         if ((last < index) && (index < next))  {\r
306             DLOG("list_add_tail index %d pfn %d last %d next %d ptr %x\n", index, link->pfn, last, next, tmp);\r
307             list_add_tail(&link->status_link, &tmp->status_link);\r
308             return ;\r
309         }\r
310         last = next;\r
311     }\r
312     list_add_tail(&link->status_link, &tmp->status_link);\r
313     DLOG("list_add index %d pfn %d last %d ptr %x\n", index, link->pfn, last, tmp);\r
314     return ;\r
315 }\r
316 \r
317 static void _insert_link_status_post(vdm_link *link)\r
318 {\r
319     int index = link->index;\r
320     int last = -1;\r
321     int next;\r
322     vdm_link *tmp, *n;\r
323 \r
324     if (list_empty(&vdm_post)) {\r
325         DLOG("post list is empty, list_add_tail first region\n");\r
326         list_add_tail(&link->status_link, &vdm_post);\r
327         return ;\r
328     }\r
329 \r
330     list_for_each_entry_safe(tmp, n, &vdm_post, status_link) {\r
331         next = tmp->index;\r
332         if ((last < index) && (index < next))  {\r
333             DLOG("list_add_tail index %d pfn %d last %d next %d ptr %x\n", index, link->pfn, last, next, tmp);\r
334             list_add_tail(&link->status_link, &tmp->status_link);\r
335             return ;\r
336         }\r
337         last = next;\r
338     }\r
339 \r
340     list_add_tail(&link->status_link, &tmp->status_link);\r
341     DLOG("list_add index %d pfn %d last %d ptr %x\n", index, link->pfn, last, tmp);\r
342     return ;\r
343 }\r
344 \r
345 static void _insert_link_status_used(vdm_link *link)\r
346 {\r
347     int index = link->index;\r
348     int last = -1;\r
349     int next;\r
350     vdm_link *tmp, *n;\r
351 \r
352     if (list_empty(&vdm_used)) {\r
353         DLOG("used list is empty, list_add_tail first region\n");\r
354         list_add_tail(&link->status_link, &vdm_used);\r
355         return ;\r
356     }\r
357 \r
358     list_for_each_entry_safe(tmp, n, &vdm_used, status_link) {\r
359         next = tmp->index;\r
360         if ((last < index) && (index < next))  {\r
361             DLOG("list_add_tail index %d pfn %d last %d next %d ptr %x\n", index, link->pfn, last, next, tmp);\r
362             list_add_tail(&link->status_link, &tmp->status_link);\r
363             return ;\r
364         }\r
365         last = next;\r
366     }\r
367 \r
368     list_add_tail(&link->status_link, &tmp->status_link);\r
369     DLOG("list_add index %d pfn %d last %d ptr %x\n", index, link->pfn, last, tmp);\r
370     return ;\r
371 }\r
372 \r
373 static void _insert_link_session_used(vdm_link *link, vdm_session *session)\r
374 {\r
375     int index = link->index;\r
376     int last = -1;\r
377     int next;\r
378     vdm_link *tmp, *n;\r
379 \r
380     if (list_empty(&session->list_used)) {\r
381         DLOG("session used list is empty, list_add_tail first region\n");\r
382         list_add_tail(&link->session_link, &session->list_used);\r
383         return ;\r
384     }\r
385 \r
386     list_for_each_entry_safe(tmp, n, &session->list_used, session_link) {\r
387         next = tmp->index;\r
388         if ((last < index) && (index < next))  {\r
389             list_add_tail(&link->session_link, &tmp->session_link);\r
390             DLOG("list_add_tail index %d pfn %d last %d next %d ptr %x\n", index, link->pfn, last, next, tmp);\r
391             return ;\r
392         }\r
393         last = next;\r
394     }\r
395 \r
396     list_add_tail(&link->session_link, &tmp->session_link);\r
397     DLOG("list_add index %d pfn %d last %d ptr %x\n", index, link->pfn, last, tmp);\r
398     return ;\r
399 }\r
400 \r
401 static void _insert_link_session_post(vdm_link *link, vdm_session *session)\r
402 {\r
403     int index = link->index;\r
404     int last = -1;\r
405     int next;\r
406     vdm_link *tmp, *n;\r
407 \r
408     if (list_empty(&session->list_post)) {\r
409         DLOG("session post list is empty, list_add_tail first region\n");\r
410         list_add_tail(&link->session_link, &session->list_post);\r
411         return ;\r
412     }\r
413 \r
414     list_for_each_entry_safe(tmp, n, &session->list_post, session_link) {\r
415         next = tmp->index;\r
416         if ((last < index) && (index < next))  {\r
417             list_add_tail(&link->session_link, &tmp->session_link);\r
418             DLOG("list_add_tail index %d pfn %d last %d next %d ptr %x\n", index, link->pfn, last, next, tmp);\r
419             return ;\r
420         }\r
421         last = next;\r
422     }\r
423 \r
424     list_add_tail(&link->session_link, &tmp->session_link);\r
425     DLOG("list_add index %d pfn %d last %d ptr %x\n", index, link->pfn, last, tmp);\r
426     return ;\r
427 }\r
428 \r
429 static void _remove_free_region(vdm_region *region)\r
430 {\r
431     list_del_init(&region->index_list);\r
432     kfree(region);\r
433 }\r
434 \r
435 static void _remove_free_link(vdm_link *link)\r
436 {\r
437     list_del_init(&link->session_link);\r
438     list_del_init(&link->status_link);\r
439     kfree(link);\r
440 }\r
441 \r
442 static void _merge_two_region(vdm_region *dst, vdm_region *src)\r
443 {\r
444     vdm_link *dst_link = find_free_link(dst->index);\r
445     vdm_link *src_link = find_free_link(src->index);\r
446     dst->pfn        += src->pfn;\r
447     dst_link->pfn   += src_link->pfn;\r
448     _remove_free_link(src_link);\r
449     _remove_free_region(src);\r
450 }\r
451 \r
452 static void merge_free_region_and_link(vdm_region *region)\r
453 {\r
454     if (region->used || region->post) {\r
455         printk(KERN_ALERT "try to merge unfree region!\n");\r
456         return ;\r
457     } else {\r
458         vdm_region *neighbor;\r
459         struct list_head *tmp = region->index_list.next;\r
460         if (tmp != &vdm_index) {\r
461             neighbor = (vdm_region *)list_entry(tmp, vdm_region, index_list);\r
462             if (is_free_region(neighbor)) {\r
463                 DLOG("merge next\n");\r
464                 _merge_two_region(region, neighbor);\r
465             }\r
466         }\r
467         tmp = region->index_list.prev;\r
468         if (tmp != &vdm_index) {\r
469             neighbor = (vdm_region *)list_entry(tmp, vdm_region, index_list);\r
470             if (is_free_region(neighbor)) {\r
471                 DLOG("merge prev\n");\r
472                 _merge_two_region(neighbor, region);\r
473             }\r
474         }\r
475     }\r
476 }\r
477 \r
478 static void put_free_link(vdm_link *link)\r
479 {\r
480     list_del_init(&link->session_link);\r
481     list_del_init(&link->status_link);\r
482     _insert_link_status_free(link);\r
483 }\r
484 \r
485 static void put_used_link(vdm_link *link, vdm_session *session)\r
486 {\r
487     list_del_init(&link->session_link);\r
488     list_del_init(&link->status_link);\r
489     _insert_link_status_used(link);\r
490     _insert_link_session_used(link, session);\r
491 }\r
492 \r
493 static void put_post_link(vdm_link *link, vdm_session *session)\r
494 {\r
495     list_del_init(&link->session_link);\r
496     list_del_init(&link->status_link);\r
497     _insert_link_status_post(link);\r
498     _insert_link_session_post(link, session);\r
499 }\r
500 \r
501 /**\r
502  * Create a link and a region by index and pfn at a same time,\r
503  * and connect the link with the region\r
504  *\r
505  * @author ChenHengming (2011-4-20)\r
506  *\r
507  * @param index\r
508  * @param pfn\r
509  *\r
510  * @return vdm_link*\r
511  */\r
512 static vdm_link *new_link_by_index(int index, int pfn)\r
513 {\r
514     vdm_region *region = (vdm_region *)kmalloc(sizeof(vdm_region), GFP_KERNEL);\r
515     vdm_link   *link   = (vdm_link   *)kmalloc(sizeof(vdm_link  ), GFP_KERNEL);\r
516 \r
517     if ((NULL == region) || (NULL == link)) {\r
518         printk(KERN_ALERT "can not kmalloc vdm_region and vdm_link in %s", __FUNCTION__);\r
519         if (region) {\r
520             kfree(region);\r
521         }\r
522         if (link) {\r
523             kfree(link);\r
524         }\r
525         return NULL;\r
526     }\r
527 \r
528     region->post    = 0;\r
529     region->used    = 0;\r
530     region->index   = index;\r
531     region->pfn     = pfn;\r
532 \r
533     INIT_LIST_HEAD(&region->index_list);\r
534 \r
535     link->link_post = 0;\r
536     link->link_used = 0;\r
537     link->region    = region;\r
538     link->index     = region->index;\r
539     link->pfn       = region->pfn;\r
540     INIT_LIST_HEAD(&link->session_link);\r
541     INIT_LIST_HEAD(&link->status_link);\r
542 \r
543     return link;\r
544 }\r
545 \r
546 /**\r
547  * Create a link from a already exist region and connect to the\r
548  * region\r
549  *\r
550  * @author ChenHengming (2011-4-20)\r
551  *\r
552  * @param region\r
553  *\r
554  * @return vdm_link*\r
555  */\r
556 static vdm_link *new_link_by_region(vdm_region *region)\r
557 {\r
558     vdm_link *link = (vdm_link *)kmalloc(sizeof(vdm_link), GFP_KERNEL);\r
559     if (NULL == link) {\r
560         printk(KERN_ALERT "can not kmalloc vdm_region and vdm_link in %s", __FUNCTION__);\r
561         return NULL;\r
562     }\r
563 \r
564     link->link_post = 0;\r
565     link->link_used = 0;\r
566     link->region    = region;\r
567     link->index     = region->index;\r
568     link->pfn       = region->pfn;\r
569     INIT_LIST_HEAD(&link->session_link);\r
570     INIT_LIST_HEAD(&link->status_link);\r
571 \r
572     return link;\r
573 }\r
574 \r
575 /**\r
576  * Delete a link completely\r
577  *\r
578  * @author ChenHengming (2011-4-20)\r
579  *\r
580  * @param link\r
581  */\r
582 static void link_del(vdm_link *link)\r
583 {\r
584     list_del_init(&link->session_link);\r
585     list_del_init(&link->status_link);\r
586     kfree(link);\r
587 }\r
588 \r
589 /**\r
590  * Called by malloc, check whether a free link can by used for a\r
591  * len of pfn, if can then put a used link to status link\r
592  *\r
593  * @author ChenHengming (2011-4-20)\r
594  *\r
595  * @param link\r
596  * @param session\r
597  * @param pfn\r
598  *\r
599  * @return vdm_link*\r
600  */\r
601 static vdm_link *get_used_link_from_free_link(vdm_link *link, vdm_session *session, int pfn)\r
602 {\r
603     if (pfn > link->pfn) {\r
604         return NULL;\r
605     }\r
606     if (pfn == link->pfn) {\r
607         DLOG("pfn == link->pfn %d\n", pfn);\r
608         link->link_used     = 1;\r
609         link->region->used  = 1;\r
610         put_used_link(link, session);\r
611         return link;\r
612     } else {\r
613         vdm_link *used = new_link_by_index(link->index, pfn);\r
614         if (NULL == used)\r
615             return NULL;\r
616 \r
617         link->index         += pfn;\r
618         link->pfn           -= pfn;\r
619         link->region->index += pfn;\r
620         link->region->pfn   -= pfn;\r
621         used->link_used      = 1;\r
622         used->region->used   = 1;\r
623 \r
624         DLOG("used: index %d pfn %d ptr %x\n", used->index, used->pfn, used->region);\r
625         if (_insert_region_index(used->region)) {\r
626             printk(KERN_ALERT "fail to insert allocated region index %d pfn %d\n", used->index, used->pfn);\r
627             link_del(used);\r
628             link->index         -= pfn;\r
629             link->pfn           += pfn;\r
630             link->region->index -= pfn;\r
631             link->region->pfn   += pfn;\r
632             _remove_free_region(used->region);\r
633             _remove_free_link(used);\r
634             return NULL;\r
635         }\r
636         put_used_link(used, session);\r
637         return used;\r
638     }\r
639 }\r
640 \r
641 static int vpu_mem_release(struct inode *, struct file *);\r
642 static int vpu_mem_mmap(struct file *, struct vm_area_struct *);\r
643 static int vpu_mem_open(struct inode *, struct file *);\r
644 static long vpu_mem_ioctl(struct file *, unsigned int, unsigned long);\r
645 \r
646 struct file_operations vpu_mem_fops = {\r
647         .open = vpu_mem_open,\r
648     .mmap = vpu_mem_mmap,\r
649     .unlocked_ioctl = vpu_mem_ioctl,\r
650         .release = vpu_mem_release,\r
651 };\r
652 \r
653 int is_vpu_mem_file(struct file *file)\r
654 {\r
655         if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode))\r
656                 return 0;\r
657         if (unlikely(file->f_dentry->d_inode->i_rdev !=\r
658              MKDEV(MISC_MAJOR, vpu_mem.dev.minor)))\r
659                 return 0;\r
660         return 1;\r
661 }\r
662 \r
663 static long vpu_mem_allocate(struct file *file, unsigned int len)\r
664 {\r
665     vdm_link *free, *n;\r
666         unsigned int pfn = (len + VPU_MEM_MIN_ALLOC - 1)/VPU_MEM_MIN_ALLOC;\r
667     vdm_session *session = (vdm_session *)file->private_data;\r
668 \r
669     if (!is_vpu_mem_file(file)) {\r
670         printk(KERN_INFO "allocate vpu_mem session from invalid file\n");\r
671         return -ENODEV;\r
672     }\r
673 \r
674     list_for_each_entry_safe(free, n, &vdm_free, status_link) {\r
675         /* find match free buffer use it first */\r
676         vdm_link *used = get_used_link_from_free_link(free, session, pfn);\r
677         DLOG("search free buffer at index %d pfn %d for len %d\n", free->index, free->pfn, pfn);\r
678         if (NULL == used) {\r
679             continue;\r
680         } else {\r
681             DLOG("found buffer at index %d pfn %d for ptr %x\n", used->index, used->pfn, used);\r
682             return used->index;\r
683         }\r
684     }\r
685 \r
686         if (!vpu_mem_over) {\r
687         printk(KERN_INFO "vpu_mem: no space left to allocate!\n");\r
688         dump_status();\r
689         vpu_mem_over = 1;\r
690     }\r
691     return -1;\r
692 }\r
693 \r
694 static int vpu_mem_free(struct file *file, int index)\r
695 {\r
696     vdm_session *session = (vdm_session *)file->private_data;\r
697 \r
698     if (!is_vpu_mem_file(file)) {\r
699         printk(KERN_INFO "free vpu_mem session from invalid file.\n");\r
700         return -ENODEV;\r
701     }\r
702 \r
703         DLOG("searching for index %d\n", index);\r
704     {\r
705         vdm_link *link = find_used_link(session, index);\r
706         if (NULL == link) {\r
707             DLOG("no link of index %d searched\n", index);\r
708             return -1;\r
709         }\r
710         link->link_used--;\r
711         link->region->used--;\r
712         if (0 == link->link_used) {\r
713             if (is_free_region(link->region)) {\r
714                 put_free_link(link);\r
715                 merge_free_region_and_link(link->region);\r
716             } else {\r
717                 link_del(link);\r
718             }\r
719         }\r
720         }\r
721     return 0;\r
722 }\r
723 \r
724 static int vpu_mem_duplicate(struct file *file, int index)\r
725 {\r
726     vdm_session *session = (vdm_session *)file->private_data;\r
727         /* caller should hold the write lock on vpu_mem_sem! */\r
728     if (!is_vpu_mem_file(file)) {\r
729         printk(KERN_INFO "duplicate vpu_mem session from invalid file.\n");\r
730         return -ENODEV;\r
731     }\r
732 \r
733         DLOG("duplicate index %d\n", index);\r
734     {\r
735         vdm_link *post = find_post_link(index);\r
736         if (NULL == post) {\r
737             vdm_link *used = find_used_link(session, index);\r
738             if (NULL == used) {\r
739                 printk(KERN_ERR "try to duplicate unknown index %d\n", index);\r
740                 dump_status();\r
741                 return -1;\r
742             }\r
743             post = new_link_by_region(used->region);\r
744             post->link_post = 1;\r
745             post->region->post++;\r
746             put_post_link(post, session);\r
747         } else {\r
748             DLOG("duplicate posted index %d\n", index);\r
749             post->link_post++;\r
750             post->region->post++;\r
751         }\r
752     }\r
753 \r
754         return 0;\r
755 }\r
756 \r
757 static int vpu_mem_link(struct file *file, int index)\r
758 {\r
759     vdm_session *session = (vdm_session *)file->private_data;\r
760 \r
761         if (!is_vpu_mem_file(file)) {\r
762         printk(KERN_INFO "link vpu_mem session from invalid file.\n");\r
763         return -ENODEV;\r
764         }\r
765 \r
766     DLOG("link index %d\n", index);\r
767     {\r
768         vdm_link *post = find_post_link(index);\r
769         if (NULL == post) {\r
770             printk(KERN_ERR "try to link unknown index %d\n", index);\r
771             dump_status();\r
772             return -1;\r
773         } else {\r
774             vdm_link *used = find_used_link(session, index);\r
775             post->link_post--;\r
776             post->region->post--;\r
777             if (0 == post->link_post) {\r
778                 if (NULL == used) {\r
779                     post->link_used++;\r
780                     post->region->used++;\r
781                     put_used_link(post, session);\r
782                 } else {\r
783                     used->link_used++;\r
784                     used->region->used++;\r
785                     link_del(post);\r
786                 }\r
787             } else {\r
788                 if (NULL == used) {\r
789                     used = new_link_by_region(post->region);\r
790                     used->link_used++;\r
791                     used->region->used++;\r
792                     put_used_link(used, session);\r
793                 } else {\r
794                     used->link_used++;\r
795                     used->region->used++;\r
796                 }\r
797             }\r
798         }\r
799     }\r
800 \r
801         return 0;\r
802 }\r
803 \r
804 void vpu_mem_cache_opt(struct file *file, long index, unsigned int cmd)\r
805 {\r
806         vdm_session *session = (vdm_session *)file->private_data;\r
807         void *start, *end;\r
808 \r
809         if (!is_vpu_mem_file(file)) {\r
810                 return;\r
811         }\r
812 \r
813         if (!vpu_mem.cached || file->f_flags & O_SYNC)\r
814                 return;\r
815 \r
816         down_read(&vdm_rwsem);\r
817     do {\r
818         vdm_link *link = find_used_link(session, index);\r
819         if (NULL == link) {\r
820             pr_err("vpu_mem_cache_opt on non-exsist index %ld\n", index);\r
821             break;\r
822         }\r
823         start = vpu_mem.vbase + index * VPU_MEM_MIN_ALLOC;\r
824         end   = start + link->pfn * VPU_MEM_MIN_ALLOC;;\r
825         switch (cmd) {\r
826         case VPU_MEM_CACHE_FLUSH : {\r
827             dmac_flush_range(start, end);\r
828             break;\r
829         }\r
830         case VPU_MEM_CACHE_CLEAN : {\r
831             dmac_clean_range(start, end);\r
832             break;\r
833         }\r
834         case VPU_MEM_CACHE_INVALID : {\r
835             dmac_inv_range(start, end);\r
836             break;\r
837         }\r
838         default :\r
839             break;\r
840         }\r
841     } while (0);\r
842     up_read(&vdm_rwsem);\r
843 }\r
844 \r
845 static pgprot_t phys_mem_access_prot(struct file *file, pgprot_t vma_prot)\r
846 {\r
847 #ifdef pgprot_noncached\r
848         if (vpu_mem.cached == 0 || file->f_flags & O_SYNC)\r
849                 return pgprot_noncached(vma_prot);\r
850 #endif\r
851 #ifdef pgprot_ext_buffered\r
852         else if (vpu_mem.buffered)\r
853                 return pgprot_ext_buffered(vma_prot);\r
854 #endif\r
855         return vma_prot;\r
856 }\r
857 \r
858 static int vpu_mem_map_pfn_range(struct vm_area_struct *vma, unsigned long len)\r
859 {\r
860         DLOG("map len %lx\n", len);\r
861         BUG_ON(!VPU_MEM_IS_PAGE_ALIGNED(vma->vm_start));\r
862         BUG_ON(!VPU_MEM_IS_PAGE_ALIGNED(vma->vm_end));\r
863         BUG_ON(!VPU_MEM_IS_PAGE_ALIGNED(len));\r
864         if (io_remap_pfn_range(vma, vma->vm_start,\r
865                 vpu_mem.base >> PAGE_SHIFT,\r
866                 len, vma->vm_page_prot)) {\r
867                 return -EAGAIN;\r
868         }\r
869         return 0;\r
870 }\r
871 \r
872 static int vpu_mem_open(struct inode *inode, struct file *file)\r
873 {\r
874     vdm_session *session;\r
875     int ret = 0;\r
876 \r
877     DLOG("current %u file %p(%d)\n", current->pid, file, (int)file_count(file));\r
878     /* setup file->private_data to indicate its unmapped */\r
879     /*  you can only open a vpu_mem device one time */\r
880     if (file->private_data != NULL)\r
881             return -1;\r
882     session = kmalloc(sizeof(vdm_session), GFP_KERNEL);\r
883     if (!session) {\r
884         printk(KERN_ALERT "vpu_mem: unable to allocate memory for vpu_mem metadata.");\r
885         return -1;\r
886     }\r
887     session->pid = current->pid;\r
888     INIT_LIST_HEAD(&session->list_post);\r
889     INIT_LIST_HEAD(&session->list_used);\r
890 \r
891     file->private_data = session;\r
892 \r
893     down_write(&vdm_rwsem);\r
894     list_add_tail(&session->list_session, &vdm_proc);\r
895     up_write(&vdm_rwsem);\r
896     return ret;\r
897 }\r
898 \r
899 static int vpu_mem_mmap(struct file *file, struct vm_area_struct *vma)\r
900 {\r
901     vdm_session *session;\r
902         unsigned long vma_size =  vma->vm_end - vma->vm_start;\r
903         int ret = 0;\r
904 \r
905         if (vma->vm_pgoff || !VPU_MEM_IS_PAGE_ALIGNED(vma_size)) {\r
906                 printk(KERN_ALERT "vpu_mem: mmaps must be at offset zero, aligned"\r
907                                 " and a multiple of pages_size.\n");\r
908                 return -EINVAL;\r
909         }\r
910 \r
911         session = (vdm_session *)file->private_data;\r
912 \r
913     /* assert: vma_size must be the total size of the vpu_mem */\r
914         if (vpu_mem.size != vma_size) {\r
915                 printk(KERN_WARNING "vpu_mem: mmap size [%lu] does not match"\r
916                        "size of backing region [%lu].\n", vma_size, vpu_mem.size);\r
917                 ret = -EINVAL;\r
918                 goto error;\r
919         }\r
920 \r
921         vma->vm_pgoff = vpu_mem.base >> PAGE_SHIFT;\r
922         vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_page_prot);\r
923 \r
924         if (vpu_mem_map_pfn_range(vma, vma_size)) {\r
925                 printk(KERN_INFO "vpu_mem: mmap failed in kernel!\n");\r
926                 ret = -EAGAIN;\r
927                 goto error;\r
928         }\r
929 \r
930         session->pid = current->pid;\r
931 \r
932 error:\r
933         return ret;\r
934 }\r
935 \r
936 static int vpu_mem_release(struct inode *inode, struct file *file)\r
937 {\r
938         vdm_session *session = (vdm_session *)file->private_data;\r
939 \r
940     down_write(&vdm_rwsem);\r
941     {\r
942         vdm_link *link, *tmp_link;\r
943         //unsigned long flags = current->flags;\r
944         //printk("current->flags: %lx\n", flags);\r
945         list_del(&session->list_session);\r
946         file->private_data = NULL;\r
947 \r
948         list_for_each_entry_safe(link, tmp_link, &session->list_post, session_link) {\r
949             do {\r
950                 link->link_post--;\r
951                 link->region->post--;\r
952             } while (link->link_post);\r
953             if (find_free_link(link->index)) {\r
954                 link_del(link);\r
955             } else {\r
956                 put_free_link(link);\r
957             }\r
958             if (is_free_region(link->region)) {\r
959                 merge_free_region_and_link(link->region);\r
960             }\r
961         }\r
962         list_for_each_entry_safe(link, tmp_link, &session->list_used, session_link) {\r
963             do {\r
964                 link->link_used--;\r
965                 link->region->used--;\r
966             } while (link->link_used);\r
967             if (find_free_link(link->index)) {\r
968                 link_del(link);\r
969             } else {\r
970                 put_free_link(link);\r
971             }\r
972             if (is_free_region(link->region)) {\r
973                 merge_free_region_and_link(link->region);\r
974             }\r
975         }\r
976     }\r
977     up_write(&vdm_rwsem);\r
978     kfree(session);\r
979 \r
980     return 0;\r
981 }\r
982 \r
983 static long vpu_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)\r
984 {\r
985     long index, ret = 0;\r
986 \r
987         switch (cmd) {\r
988         case VPU_MEM_GET_PHYS:\r
989                 DLOG("get_phys\n");\r
990                 printk(KERN_INFO "vpu_mem: request for physical address of vpu_mem region "\r
991                                 "from process %d.\n", current->pid);\r
992                 if (copy_to_user((void __user *)arg, &vpu_mem.base, sizeof(vpu_mem.base)))\r
993                         return -EFAULT;\r
994                 break;\r
995         case VPU_MEM_GET_TOTAL_SIZE:\r
996                 DLOG("get total size\n");\r
997                 if (copy_to_user((void __user *)arg, &vpu_mem.size, sizeof(vpu_mem.size)))\r
998                         return -EFAULT;\r
999                 break;\r
1000     case VPU_MEM_ALLOCATE:\r
1001                 DLOG("allocate\n");\r
1002         {\r
1003             unsigned int size;\r
1004             if (copy_from_user(&size, (void __user *)arg, sizeof(size)))\r
1005                 return -EFAULT;\r
1006             down_write(&vdm_rwsem);\r
1007             ret = vpu_mem_allocate(file, size);\r
1008             up_write(&vdm_rwsem);\r
1009             DLOG("allocate at index %ld\n", ret);\r
1010             break;\r
1011         }\r
1012     case VPU_MEM_FREE:\r
1013         DLOG("mem free\n");\r
1014         {\r
1015             if (copy_from_user(&index, (void __user *)arg, sizeof(index)))\r
1016                 return -EFAULT;\r
1017             if (index >= vpu_mem.size)\r
1018                 return -EACCES;\r
1019             down_write(&vdm_rwsem);\r
1020             ret = vpu_mem_free(file, index);\r
1021             up_write(&vdm_rwsem);\r
1022             break;\r
1023         }\r
1024         case VPU_MEM_CACHE_FLUSH:\r
1025     case VPU_MEM_CACHE_CLEAN:\r
1026     case VPU_MEM_CACHE_INVALID:\r
1027         DLOG("flush\n");\r
1028                 {\r
1029                         if (copy_from_user(&index, (void __user *)arg, sizeof(index)))\r
1030                                 return -EFAULT;\r
1031             if (index < 0)\r
1032                 return -EINVAL;\r
1033                         vpu_mem_cache_opt(file, index, cmd);\r
1034                         break;\r
1035                 }\r
1036         case VPU_MEM_DUPLICATE:\r
1037         DLOG("duplicate\n");\r
1038                 {\r
1039                         if (copy_from_user(&index, (void __user *)arg, sizeof(index)))\r
1040                                 return -EFAULT;\r
1041             down_write(&vdm_rwsem);\r
1042                         ret = vpu_mem_duplicate(file, index);\r
1043             up_write(&vdm_rwsem);\r
1044                         break;\r
1045                 }\r
1046         case VPU_MEM_LINK:\r
1047         DLOG("link\n");\r
1048                 {\r
1049                         if (copy_from_user(&index, (void __user *)arg, sizeof(index)))\r
1050                                 return -EFAULT;\r
1051             down_write(&vdm_rwsem);\r
1052                         ret = vpu_mem_link(file, index);\r
1053             up_write(&vdm_rwsem);\r
1054                         break;\r
1055                 }\r
1056         default:\r
1057                 return -EINVAL;\r
1058         }\r
1059         return ret;\r
1060 }\r
1061 \r
1062 #if VPU_MEM_DEBUG\r
1063 static ssize_t debug_open(struct inode *inode, struct file *file)\r
1064 {\r
1065         file->private_data = inode->i_private;\r
1066         return 0;\r
1067 }\r
1068 \r
1069 static ssize_t debug_read(struct file *file, char __user *buf, size_t count,\r
1070                           loff_t *ppos)\r
1071 {\r
1072         vdm_region *region, *tmp_region;\r
1073         const int debug_bufmax = 4096;\r
1074         static char buffer[4096];\r
1075         int n = 0;\r
1076 \r
1077         DLOG("debug open\n");\r
1078         n = scnprintf(buffer, debug_bufmax,\r
1079                       "pid #: mapped regions (offset, len, used, post) ...\n");\r
1080         down_read(&vdm_rwsem);\r
1081     list_for_each_entry_safe(region, tmp_region, &vdm_index, index_list) {\r
1082         n += scnprintf(buffer + n, debug_bufmax - n,\r
1083                 "(%d,%d,%d,%d) ",\r
1084                 region->index, region->pfn, region->used, region->post);\r
1085         }\r
1086         up_read(&vdm_rwsem);\r
1087         n++;\r
1088         buffer[n] = 0;\r
1089         return simple_read_from_buffer(buf, count, ppos, buffer, n);\r
1090 }\r
1091 \r
1092 static struct file_operations debug_fops = {\r
1093         .read = debug_read,\r
1094         .open = debug_open,\r
1095 };\r
1096 #endif\r
1097 \r
1098 int vpu_mem_setup(struct vpu_mem_platform_data *pdata)\r
1099 {\r
1100     vdm_link *tmp = NULL;\r
1101         int err = 0;\r
1102 \r
1103     if (vpu_mem_count) {\r
1104                 printk(KERN_ALERT "Only one vpu_mem driver can be register!\n");\r
1105         goto err_cant_register_device;\r
1106     }\r
1107 \r
1108     memset(&vpu_mem, 0, sizeof(struct vpu_mem_info));\r
1109 \r
1110     vpu_mem.cached = pdata->cached;\r
1111     vpu_mem.buffered = pdata->buffered;\r
1112     vpu_mem.base = pdata->start;\r
1113     vpu_mem.size = pdata->size;\r
1114     init_rwsem(&vdm_rwsem);\r
1115     INIT_LIST_HEAD(&vdm_proc);\r
1116     INIT_LIST_HEAD(&vdm_used);\r
1117     INIT_LIST_HEAD(&vdm_post);\r
1118     INIT_LIST_HEAD(&vdm_free);\r
1119     INIT_LIST_HEAD(&vdm_index);\r
1120     vpu_mem.dev.name = pdata->name;\r
1121     vpu_mem.dev.minor = MISC_DYNAMIC_MINOR;\r
1122     vpu_mem.dev.fops = &vpu_mem_fops;\r
1123 \r
1124     err = misc_register(&vpu_mem.dev);\r
1125     if (err) {\r
1126         printk(KERN_ALERT "Unable to register vpu_mem driver!\n");\r
1127         goto err_cant_register_device;\r
1128     }\r
1129 \r
1130     vpu_mem.num_entries = vpu_mem.size / VPU_MEM_MIN_ALLOC;\r
1131 \r
1132     tmp = new_link_by_index(0, vpu_mem.num_entries);\r
1133     if (NULL == tmp) {\r
1134                 printk(KERN_ALERT "init free region failed\n");\r
1135         goto err_no_mem_for_metadata;\r
1136     }\r
1137     put_free_link(tmp);\r
1138     _insert_region_index(tmp->region);\r
1139 \r
1140     if (vpu_mem.cached)\r
1141         vpu_mem.vbase = ioremap_cached(vpu_mem.base, vpu_mem.size);\r
1142     #ifdef ioremap_ext_buffered\r
1143     else if (vpu_mem.buffered)\r
1144         vpu_mem.vbase = ioremap_ext_buffered(vpu_mem.base, vpu_mem.size);\r
1145     #endif\r
1146     else\r
1147         vpu_mem.vbase = ioremap(vpu_mem.base, vpu_mem.size);\r
1148 \r
1149     if (vpu_mem.vbase == 0)\r
1150         goto error_cant_remap;\r
1151 \r
1152     #if VPU_MEM_DEBUG\r
1153     debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)vpu_mem.dev.minor,\r
1154                         &debug_fops);\r
1155     #endif\r
1156     printk("%s: %d initialized\n", pdata->name, vpu_mem.dev.minor);\r
1157     vpu_mem_count++;\r
1158         return 0;\r
1159 error_cant_remap:\r
1160     if (tmp) {\r
1161         kfree(tmp);\r
1162     }\r
1163 err_no_mem_for_metadata:\r
1164         misc_deregister(&vpu_mem.dev);\r
1165 err_cant_register_device:\r
1166         return -1;\r
1167 }\r
1168 \r
1169 static int vpu_mem_probe(struct platform_device *pdev)\r
1170 {\r
1171         struct vpu_mem_platform_data *pdata;\r
1172 \r
1173         if (!pdev || !pdev->dev.platform_data) {\r
1174                 printk(KERN_ALERT "Unable to probe vpu_mem!\n");\r
1175                 return -1;\r
1176         }\r
1177         pdata = pdev->dev.platform_data;\r
1178         return vpu_mem_setup(pdata);\r
1179 }\r
1180 \r
1181 static int vpu_mem_remove(struct platform_device *pdev)\r
1182 {\r
1183         if (!pdev || !pdev->dev.platform_data) {\r
1184                 printk(KERN_ALERT "Unable to remove vpu_mem!\n");\r
1185                 return -1;\r
1186         }\r
1187     if (vpu_mem_count) {\r
1188             misc_deregister(&vpu_mem.dev);\r
1189         vpu_mem_count--;\r
1190     } else {\r
1191                 printk(KERN_ALERT "no vpu_mem to remove!\n");\r
1192     }\r
1193         return 0;\r
1194 }\r
1195 \r
1196 static struct platform_driver vpu_mem_driver = {\r
1197         .probe  = vpu_mem_probe,\r
1198         .remove = vpu_mem_remove,\r
1199         .driver = { .name = "vpu_mem" }\r
1200 };\r
1201 \r
1202 \r
1203 static int __init vpu_mem_init(void)\r
1204 {\r
1205         return platform_driver_register(&vpu_mem_driver);\r
1206 }\r
1207 \r
1208 static void __exit vpu_mem_exit(void)\r
1209 {\r
1210         platform_driver_unregister(&vpu_mem_driver);\r
1211 }\r
1212 \r
1213 module_init(vpu_mem_init);\r
1214 module_exit(vpu_mem_exit);\r
1215 \r
1216 #ifdef CONFIG_PROC_FS\r
1217 #include <linux/proc_fs.h>\r
1218 #include <linux/seq_file.h>\r
1219 \r
1220 static int proc_vpu_mem_show(struct seq_file *s, void *v)\r
1221 {\r
1222         if (vpu_mem_count) {\r
1223                 seq_printf(s, "vpu mem opened\n");\r
1224         } else {\r
1225                 seq_printf(s, "vpu mem closed\n");\r
1226         return 0;\r
1227         }\r
1228 \r
1229     down_read(&vdm_rwsem);\r
1230     {\r
1231         vdm_link    *link, *tmp_link;\r
1232         vdm_region  *region, *tmp_region;\r
1233         vdm_session *session, *tmp_session;\r
1234         // °´ index ´òÓ¡È«²¿ region\r
1235         seq_printf(s, "index:\n");\r
1236         list_for_each_entry_safe(region, tmp_region, &vdm_index, index_list) {\r
1237             seq_printf(s, "       idx %6d pfn %6d used %3d post %3d\n",\r
1238                 region->index, region->pfn, region->used, region->post);\r
1239         }\r
1240         if (list_empty(&vdm_free)) {\r
1241             seq_printf(s, "free : empty\n");\r
1242         } else {\r
1243             seq_printf(s, "free :\n");\r
1244             list_for_each_entry_safe(link, tmp_link, &vdm_free, status_link) {\r
1245                 seq_printf(s, "       idx %6d pfn %6d used %3d post %3d\n",\r
1246                     link->index, link->pfn, link->link_used, link->link_post);\r
1247             }\r
1248         }\r
1249         if (list_empty(&vdm_used)) {\r
1250             seq_printf(s, "used : empty\n");\r
1251         } else {\r
1252             seq_printf(s, "used :\n");\r
1253             list_for_each_entry_safe(link, tmp_link, &vdm_used, status_link) {\r
1254                 seq_printf(s, "       idx %6d pfn %6d used %3d post %3d\n",\r
1255                     link->index, link->pfn, link->link_used, link->link_post);\r
1256             }\r
1257         }\r
1258         if (list_empty(&vdm_post)) {\r
1259             seq_printf(s, "post : empty\n");\r
1260         } else {\r
1261             seq_printf(s, "post :\n");\r
1262             list_for_each_entry_safe(link, tmp_link, &vdm_post, status_link) {\r
1263                 seq_printf(s, "       idx %6d pfn %6d used %3d post %3d\n",\r
1264                     link->index, link->pfn, link->link_used, link->link_post);\r
1265             }\r
1266         }\r
1267 \r
1268         // ´òÓ¡ vpu_mem_info ÖеÄÈ«²¿ session µÄ region Õ¼ÓÃÇé¿ö\r
1269         list_for_each_entry_safe(session, tmp_session, &vdm_proc, list_session) {\r
1270             seq_printf(s, "\npid: %d\n", session->pid);\r
1271             if (list_empty(&session->list_used)) {\r
1272                 seq_printf(s, "used : empty\n");\r
1273             } else {\r
1274                 seq_printf(s, "used :\n");\r
1275                 list_for_each_entry_safe(link, tmp_link, &session->list_used, session_link) {\r
1276                     seq_printf(s, "       idx %6d pfn %6d used %3d\n",\r
1277                         link->index, link->pfn, link->link_used);\r
1278                 }\r
1279             }\r
1280             if (list_empty(&session->list_post)) {\r
1281                 seq_printf(s, "post : empty\n");\r
1282             } else {\r
1283                 seq_printf(s, "post :\n");\r
1284                 list_for_each_entry_safe(link, tmp_link, &session->list_post, session_link) {\r
1285                     seq_printf(s, "       idx %6d pfn %6d post %3d\n",\r
1286                         link->index, link->pfn, link->link_post);\r
1287                 }\r
1288             }\r
1289         }\r
1290     }\r
1291 \r
1292     up_read(&vdm_rwsem);\r
1293     return 0;\r
1294 }\r
1295 \r
1296 static int proc_vpu_mem_open(struct inode *inode, struct file *file)\r
1297 {\r
1298         return single_open(file, proc_vpu_mem_show, NULL);\r
1299 }\r
1300 \r
1301 static const struct file_operations proc_vpu_mem_fops = {\r
1302         .open           = proc_vpu_mem_open,\r
1303         .read           = seq_read,\r
1304         .llseek         = seq_lseek,\r
1305         .release        = single_release,\r
1306 };\r
1307 \r
1308 static int __init vpu_mem_proc_init(void)\r
1309 {\r
1310         proc_create("vpu_mem", 0, NULL, &proc_vpu_mem_fops);\r
1311         return 0;\r
1312 \r
1313 }\r
1314 late_initcall(vpu_mem_proc_init);\r
1315 #endif /* CONFIG_PROC_FS */\r
1316 \r