blob: 8550575d259d9b21bb8db9c822d259ddc7fb13a9
1 | /* |
2 | * drivers/amlogic/media/frame_provider/decoder/utils/vdec_input.c |
3 | * |
4 | * Copyright (C) 2016 Amlogic, Inc. All rights reserved. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. |
15 | * |
16 | */ |
17 | |
18 | #include <linux/uaccess.h> |
19 | #include <linux/list.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/dma-mapping.h> |
22 | #include <linux/amlogic/media/codec_mm/codec_mm.h> |
23 | |
24 | #include "../../../stream_input/amports/amports_priv.h" |
25 | #include "vdec.h" |
26 | #include "vdec_input.h" |
27 | |
28 | #include <asm/cacheflush.h> |
29 | #include <linux/crc32.h> |
30 | |
31 | |
32 | #define VFRAME_BLOCK_SIZE (512 * SZ_1K)/*512 for 1080p default init.*/ |
33 | #define VFRAME_BLOCK_SIZE_4K (2 * SZ_1M) /*2M for 4K default.*/ |
34 | #define VFRAME_BLOCK_SIZE_MAX (4 * SZ_1M) |
35 | |
36 | #define VFRAME_BLOCK_PAGEALIGN 4 |
37 | #define VFRAME_BLOCK_MIN_LEVEL (2 * SZ_1M) |
38 | #define VFRAME_BLOCK_MAX_LEVEL (8 * SZ_1M) |
39 | #define VFRAME_BLOCK_MAX_TOTAL_SIZE (16 * SZ_1M) |
40 | |
41 | /* |
42 | 2s for OMX |
43 | */ |
44 | #define MAX_FRAME_DURATION_S 2 |
45 | |
46 | |
47 | #define VFRAME_BLOCK_HOLE (SZ_64K) |
48 | |
49 | #define MIN_FRAME_PADDING_SIZE ((u32)(L1_CACHE_BYTES)) |
50 | |
51 | #define EXTRA_PADDING_SIZE (16 * SZ_1K) /*HEVC_PADDING_SIZE*/ |
52 | |
53 | #define MEM_NAME "VFRAME_INPUT" |
54 | |
55 | //static int vdec_input_get_duration_u64(struct vdec_input_s *input); |
56 | static struct vframe_block_list_s * |
57 | vdec_input_alloc_new_block(struct vdec_input_s *input, |
58 | ulong phy_addr, |
59 | int size); |
60 | |
61 | static int copy_from_user_to_phyaddr(void *virts, const char __user *buf, |
62 | u32 size, ulong phys, u32 pading, bool is_mapped) |
63 | { |
64 | u32 i, span = SZ_1M; |
65 | u32 count = size / PAGE_ALIGN(span); |
66 | u32 remain = size % PAGE_ALIGN(span); |
67 | ulong addr = phys; |
68 | u8 *p = virts; |
69 | |
70 | if (is_mapped) { |
71 | if (copy_from_user(p, buf, size)) |
72 | return -EFAULT; |
73 | |
74 | if (pading) |
75 | memset(p + size, 0, pading); |
76 | |
77 | codec_mm_dma_flush(p, size + pading, DMA_TO_DEVICE); |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | for (i = 0; i < count; i++) { |
83 | addr = phys + i * span; |
84 | p = codec_mm_vmap(addr, span); |
85 | if (!p) |
86 | return -1; |
87 | |
88 | if (copy_from_user(p, buf + i * span, span)) { |
89 | codec_mm_unmap_phyaddr(p); |
90 | return -EFAULT; |
91 | } |
92 | |
93 | codec_mm_dma_flush(p, span, DMA_TO_DEVICE); |
94 | codec_mm_unmap_phyaddr(p); |
95 | } |
96 | |
97 | if (!remain) |
98 | return 0; |
99 | |
100 | span = size - remain; |
101 | addr = phys + span; |
102 | p = codec_mm_vmap(addr, remain + pading); |
103 | if (!p) |
104 | return -1; |
105 | |
106 | if (copy_from_user(p, buf + span, remain)) { |
107 | codec_mm_unmap_phyaddr(p); |
108 | return -EFAULT; |
109 | } |
110 | |
111 | if (pading) |
112 | memset(p + remain, 0, pading); |
113 | |
114 | codec_mm_dma_flush(p, remain + pading, DMA_TO_DEVICE); |
115 | codec_mm_unmap_phyaddr(p); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static int vframe_chunk_fill(struct vdec_input_s *input, |
121 | struct vframe_chunk_s *chunk, const char *buf, |
122 | size_t count, struct vframe_block_list_s *block) |
123 | { |
124 | u8 *p = (u8 *)block->start_virt + block->wp; |
125 | if (block->type == VDEC_TYPE_FRAME_BLOCK) { |
126 | copy_from_user_to_phyaddr(p, buf, count, |
127 | block->start + block->wp, |
128 | chunk->pading_size, |
129 | block->is_mapped); |
130 | } else if (block->type == VDEC_TYPE_FRAME_CIRCULAR) { |
131 | size_t len = min((size_t)(block->size - block->wp), count); |
132 | u32 wp; |
133 | |
134 | copy_from_user_to_phyaddr(p, buf, len, |
135 | block->start + block->wp, 0, |
136 | block->is_mapped); |
137 | p += len; |
138 | |
139 | if (count > len) { |
140 | copy_from_user_to_phyaddr(p, buf + len, |
141 | count - len, |
142 | block->start, 0, |
143 | block->is_mapped); |
144 | |
145 | p += count - len; |
146 | } |
147 | |
148 | wp = block->wp + count; |
149 | if (wp >= block->size) |
150 | wp -= block->size; |
151 | |
152 | len = min(block->size - wp, chunk->pading_size); |
153 | |
154 | if (!block->is_mapped) { |
155 | p = codec_mm_vmap(block->start + wp, len); |
156 | memset(p, 0, len); |
157 | codec_mm_dma_flush(p, len, DMA_TO_DEVICE); |
158 | codec_mm_unmap_phyaddr(p); |
159 | } else { |
160 | memset(p, 0, len); |
161 | codec_mm_dma_flush(p, len, DMA_TO_DEVICE); |
162 | } |
163 | |
164 | if (chunk->pading_size > len) { |
165 | p = (u8 *)block->start_virt; |
166 | |
167 | if (!block->is_mapped) { |
168 | p = codec_mm_vmap(block->start, |
169 | chunk->pading_size - len); |
170 | memset(p, 0, chunk->pading_size - len); |
171 | codec_mm_dma_flush(p, |
172 | chunk->pading_size - len, |
173 | DMA_TO_DEVICE); |
174 | codec_mm_unmap_phyaddr(p); |
175 | } else { |
176 | memset(p, 0, chunk->pading_size - len); |
177 | codec_mm_dma_flush(p, |
178 | chunk->pading_size - len, |
179 | DMA_TO_DEVICE); |
180 | } |
181 | } |
182 | } |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static inline u32 vframe_block_space(struct vframe_block_list_s *block) |
188 | { |
189 | if (block->type == VDEC_TYPE_FRAME_BLOCK) { |
190 | return block->size - block->wp; |
191 | } else { |
192 | return (block->rp >= block->wp) ? |
193 | (block->rp - block->wp) : |
194 | (block->rp - block->wp + block->size); |
195 | } |
196 | } |
197 | |
198 | static void vframe_block_add_chunk(struct vframe_block_list_s *block, |
199 | struct vframe_chunk_s *chunk) |
200 | { |
201 | block->wp += chunk->size + chunk->pading_size; |
202 | if (block->wp >= block->size) |
203 | block->wp -= block->size; |
204 | block->data_size += chunk->size; |
205 | block->chunk_count++; |
206 | chunk->block = block; |
207 | block->input->wr_block = block; |
208 | chunk->sequence = block->input->sequence; |
209 | block->input->sequence++; |
210 | } |
211 | |
212 | static void vframe_block_free_block(struct vframe_block_list_s *block) |
213 | { |
214 | if (block->addr) { |
215 | codec_mm_free_for_dma(MEM_NAME, block->addr); |
216 | } |
217 | /* |
218 | *pr_err("free block %d, size=%d\n", block->id, block->size); |
219 | */ |
220 | kfree(block); |
221 | } |
222 | |
223 | static int vframe_block_init_alloc_storage(struct vdec_input_s *input, |
224 | struct vframe_block_list_s *block, |
225 | ulong phy_addr, |
226 | int size) |
227 | { |
228 | int alloc_size = input->default_block_size; |
229 | block->magic = 0x4b434c42; |
230 | block->input = input; |
231 | block->type = input->type; |
232 | |
233 | /* |
234 | * todo: for different type use different size |
235 | */ |
236 | if (phy_addr) { |
237 | block->is_out_buf = 1; |
238 | block->start_virt = NULL; |
239 | block->start = phy_addr; |
240 | block->size = size; |
241 | } else { |
242 | alloc_size = PAGE_ALIGN(alloc_size); |
243 | block->addr = codec_mm_alloc_for_dma_ex( |
244 | MEM_NAME, |
245 | alloc_size/PAGE_SIZE, |
246 | VFRAME_BLOCK_PAGEALIGN, |
247 | CODEC_MM_FLAGS_DMA_CPU | CODEC_MM_FLAGS_FOR_VDECODER, |
248 | input->id, |
249 | block->id); |
250 | |
251 | if (!block->addr) { |
252 | pr_err("Input block allocation failed\n"); |
253 | return -ENOMEM; |
254 | } |
255 | |
256 | block->start_virt = (void *)codec_mm_phys_to_virt(block->addr); |
257 | if (block->start_virt) |
258 | block->is_mapped = true; |
259 | block->start = block->addr; |
260 | block->size = alloc_size; |
261 | block->is_out_buf = 0; |
262 | } |
263 | |
264 | return 0; |
265 | } |
266 | |
267 | void vdec_input_init(struct vdec_input_s *input, struct vdec_s *vdec) |
268 | { |
269 | INIT_LIST_HEAD(&input->vframe_block_list); |
270 | INIT_LIST_HEAD(&input->vframe_block_free_list); |
271 | INIT_LIST_HEAD(&input->vframe_chunk_list); |
272 | spin_lock_init(&input->lock); |
273 | input->id = vdec->id; |
274 | input->block_nums = 0; |
275 | input->vdec = vdec; |
276 | input->block_id_seq = 0; |
277 | input->size = 0; |
278 | input->default_block_size = VFRAME_BLOCK_SIZE; |
279 | } |
280 | int vdec_input_prepare_bufs(struct vdec_input_s *input, |
281 | int frame_width, int frame_height) |
282 | { |
283 | struct vframe_block_list_s *block; |
284 | int i; |
285 | unsigned long flags; |
286 | |
287 | if (vdec_secure(input->vdec)) |
288 | return 0; |
289 | if (input->size > 0) |
290 | return 0; |
291 | if (frame_width * frame_height >= 1920 * 1088) { |
292 | /*have add data before. ignore prepare buffers.*/ |
293 | input->default_block_size = VFRAME_BLOCK_SIZE_4K; |
294 | } |
295 | /*prepared 3 buffers for smooth start.*/ |
296 | for (i = 0; i < 3; i++) { |
297 | block = vdec_input_alloc_new_block(input, 0, 0); |
298 | if (!block) |
299 | break; |
300 | flags = vdec_input_lock(input); |
301 | list_move_tail(&block->list, |
302 | &input->vframe_block_free_list); |
303 | input->wr_block = NULL; |
304 | vdec_input_unlock(input, flags); |
305 | } |
306 | return 0; |
307 | } |
308 | |
309 | static int vdec_input_dump_block_locked( |
310 | struct vframe_block_list_s *block, |
311 | char *buf, int size) |
312 | { |
313 | char *pbuf = buf; |
314 | char sbuf[512]; |
315 | int tsize = 0; |
316 | int s; |
317 | if (!pbuf) { |
318 | pbuf = sbuf; |
319 | size = 512; |
320 | } |
321 | #define BUFPRINT(args...) \ |
322 | do {\ |
323 | s = snprintf(pbuf, size - tsize, args);\ |
324 | tsize += s;\ |
325 | pbuf += s; \ |
326 | } while (0) |
327 | |
328 | BUFPRINT("\tblock:[%d:%p]-addr=%p,vstart=%p,type=%d\n", |
329 | block->id, |
330 | block, |
331 | (void *)block->addr, |
332 | (void *)block->start_virt, |
333 | block->type); |
334 | BUFPRINT("\t-blocksize=%d,data=%d,wp=%d,rp=%d,chunk_count=%d\n", |
335 | block->size, |
336 | block->data_size, |
337 | block->wp, |
338 | block->rp, |
339 | block->chunk_count); |
340 | /* |
341 | BUFPRINT("\tlist=%p,next=%p,prev=%p\n", |
342 | &block->list, |
343 | block->list.next, |
344 | block->list.prev); |
345 | */ |
346 | #undef BUFPRINT |
347 | if (!buf) |
348 | pr_info("%s", sbuf); |
349 | return tsize; |
350 | } |
351 | |
352 | int vdec_input_dump_blocks(struct vdec_input_s *input, |
353 | char *bufs, int size) |
354 | { |
355 | struct list_head *p, *tmp; |
356 | unsigned long flags; |
357 | char *lbuf = bufs; |
358 | char sbuf[256]; |
359 | int s = 0; |
360 | |
361 | if (size <= 0) |
362 | return 0; |
363 | if (!bufs) |
364 | lbuf = sbuf; |
365 | s += snprintf(lbuf + s, size - s, |
366 | "blocks:vdec-%d id:%d,bufsize=%d,dsize=%d,frames:%d,dur:%dms\n", |
367 | input->id, |
368 | input->block_nums, |
369 | input->size, |
370 | input->data_size, |
371 | input->have_frame_num, |
372 | vdec_input_get_duration_u64(input)/1000); |
373 | if (bufs) |
374 | lbuf += s; |
375 | else { |
376 | pr_info("%s", sbuf); |
377 | lbuf = NULL; |
378 | } |
379 | |
380 | flags = vdec_input_lock(input); |
381 | /* dump input blocks */ |
382 | list_for_each_safe(p, tmp, &input->vframe_block_list) { |
383 | struct vframe_block_list_s *block = list_entry( |
384 | p, struct vframe_block_list_s, list); |
385 | if (bufs != NULL) { |
386 | lbuf = bufs + s; |
387 | if (size - s < 128) |
388 | break; |
389 | } |
390 | s += vdec_input_dump_block_locked(block, lbuf, size - s); |
391 | } |
392 | list_for_each_safe(p, tmp, &input->vframe_block_free_list) { |
393 | struct vframe_block_list_s *block = list_entry( |
394 | p, struct vframe_block_list_s, list); |
395 | if (bufs != NULL) { |
396 | lbuf = bufs + s; |
397 | if (size - s < 128) |
398 | break; |
399 | } |
400 | s += vdec_input_dump_block_locked(block, lbuf, size - s); |
401 | } |
402 | vdec_input_unlock(input, flags); |
403 | return s; |
404 | } |
405 | |
406 | static int vdec_input_dump_chunk_locked( |
407 | int id, |
408 | struct vframe_chunk_s *chunk, |
409 | char *buf, int size) |
410 | { |
411 | char *pbuf = buf; |
412 | char sbuf[512]; |
413 | int tsize = 0; |
414 | int s; |
415 | if (!pbuf) { |
416 | pbuf = sbuf; |
417 | size = 512; |
418 | } |
419 | #define BUFPRINT(args...) \ |
420 | do {\ |
421 | s = snprintf(pbuf, size - tsize, args);\ |
422 | tsize += s;\ |
423 | pbuf += s; \ |
424 | } while (0) |
425 | |
426 | BUFPRINT( |
427 | "\t[%d][%lld:%p]-off=%d,size:%d,p:%d,\tpts64=%lld,addr=%p\n", |
428 | id, |
429 | chunk->sequence, |
430 | chunk->block, |
431 | chunk->offset, |
432 | chunk->size, |
433 | chunk->pading_size, |
434 | chunk->pts64, |
435 | (void *)(chunk->block->addr + chunk->offset)); |
436 | /* |
437 | BUFPRINT("\tlist=%p,next=%p,prev=%p\n", |
438 | &chunk->list, |
439 | chunk->list.next, |
440 | chunk->list.prev); |
441 | */ |
442 | #undef BUFPRINT |
443 | if (!buf) |
444 | pr_info("%s", sbuf); |
445 | return tsize; |
446 | } |
447 | |
448 | int vdec_input_dump_chunks(int id, struct vdec_input_s *input, |
449 | char *bufs, int size) |
450 | { |
451 | |
452 | struct list_head *p, *tmp; |
453 | unsigned long flags; |
454 | char *lbuf = bufs; |
455 | char sbuf[256]; |
456 | int s = 0; |
457 | int i = 0; |
458 | |
459 | if (size <= 0) |
460 | return 0; |
461 | if (!bufs) |
462 | lbuf = sbuf; |
463 | s = snprintf(lbuf + s, size - s, |
464 | "[%d]blocks:vdec-%d id:%d,bufsize=%d,dsize=%d,frames:%d,maxframe:%d\n", |
465 | id, |
466 | input->id, |
467 | input->block_nums, |
468 | input->size, |
469 | input->data_size, |
470 | input->have_frame_num, |
471 | input->frame_max_size); |
472 | if (bufs) |
473 | lbuf += s; |
474 | if (!bufs) { |
475 | pr_info("%s", sbuf); |
476 | lbuf = NULL; |
477 | } |
478 | flags = vdec_input_lock(input); |
479 | /*dump chunks list infos.*/ |
480 | list_for_each_safe(p, tmp, &input->vframe_chunk_list) { |
481 | struct vframe_chunk_s *chunk = list_entry( |
482 | p, struct vframe_chunk_s, list); |
483 | if (bufs != NULL) |
484 | lbuf = bufs + s; |
485 | s += vdec_input_dump_chunk_locked(id, chunk, lbuf, size - s); |
486 | i++; |
487 | if (i >= 10) |
488 | break; |
489 | } |
490 | vdec_input_unlock(input, flags); |
491 | return s; |
492 | } |
493 | |
494 | |
495 | |
496 | int vdec_input_set_buffer(struct vdec_input_s *input, u32 start, u32 size) |
497 | { |
498 | if (input_frame_based(input)) |
499 | return -EINVAL; |
500 | |
501 | input->start = start; |
502 | input->size = size; |
503 | input->swap_rp = start; |
504 | |
505 | if (vdec_secure(input->vdec)) |
506 | input->swap_page_phys = codec_mm_alloc_for_dma("SWAP", |
507 | 1, 0, CODEC_MM_FLAGS_TVP); |
508 | else { |
509 | input->swap_page = alloc_page(GFP_KERNEL); |
510 | if (input->swap_page) { |
511 | input->swap_page_phys = |
512 | page_to_phys(input->swap_page); |
513 | } |
514 | } |
515 | |
516 | if (input->swap_page_phys == 0) |
517 | return -ENOMEM; |
518 | |
519 | return 0; |
520 | } |
521 | EXPORT_SYMBOL(vdec_input_set_buffer); |
522 | |
523 | void vdec_input_set_type(struct vdec_input_s *input, int type, int target) |
524 | { |
525 | input->type = type; |
526 | input->target = target; |
527 | if (type == VDEC_TYPE_FRAME_CIRCULAR) { |
528 | /*alway used max block.*/ |
529 | input->default_block_size = VFRAME_BLOCK_SIZE_MAX; |
530 | } |
531 | } |
532 | EXPORT_SYMBOL(vdec_input_set_type); |
533 | |
534 | int vdec_input_get_status(struct vdec_input_s *input, |
535 | struct vdec_input_status_s *status) |
536 | { |
537 | unsigned long flags; |
538 | |
539 | if (input->vdec == NULL) |
540 | return -EINVAL; |
541 | |
542 | flags = vdec_input_lock(input); |
543 | |
544 | if (list_empty(&input->vframe_block_list)) { |
545 | status->size = VFRAME_BLOCK_SIZE; |
546 | status->data_len = 0; |
547 | status->free_len = VFRAME_BLOCK_SIZE; |
548 | status->read_pointer = 0; |
549 | } else { |
550 | int r = VFRAME_BLOCK_MAX_LEVEL - vdec_input_level(input) |
551 | - VFRAME_BLOCK_HOLE; |
552 | status->size = input->size; |
553 | status->data_len = vdec_input_level(input); |
554 | status->free_len = (r > 0) ? r : 0; |
555 | status->read_pointer = input->total_rd_count; |
556 | } |
557 | |
558 | vdec_input_unlock(input, flags); |
559 | |
560 | return 0; |
561 | } |
562 | EXPORT_SYMBOL(vdec_input_get_status); |
563 | |
564 | static void vdec_input_add_block(struct vdec_input_s *input, |
565 | struct vframe_block_list_s *block) |
566 | { |
567 | unsigned long flags; |
568 | |
569 | flags = vdec_input_lock(input); |
570 | block->wp = 0; |
571 | block->id = input->block_id_seq++; |
572 | list_add_tail(&block->list, &input->vframe_block_list); |
573 | input->size += block->size; |
574 | input->block_nums++; |
575 | input->wr_block = block; |
576 | vdec_input_unlock(input, flags); |
577 | } |
578 | |
579 | static inline void vdec_input_del_block_locked(struct vdec_input_s *input, |
580 | struct vframe_block_list_s *block) |
581 | { |
582 | list_del(&block->list); |
583 | input->size -= block->size; |
584 | input->block_nums--; |
585 | } |
586 | |
587 | int vdec_input_level(struct vdec_input_s *input) |
588 | { |
589 | return input->total_wr_count - input->total_rd_count; |
590 | } |
591 | EXPORT_SYMBOL(vdec_input_level); |
592 | |
593 | static struct vframe_block_list_s * |
594 | vdec_input_alloc_new_block(struct vdec_input_s *input, |
595 | ulong phy_addr, |
596 | int size) |
597 | { |
598 | struct vframe_block_list_s *block; |
599 | block = kzalloc(sizeof(struct vframe_block_list_s), |
600 | GFP_KERNEL); |
601 | if (block == NULL) { |
602 | input->no_mem_err_cnt++; |
603 | pr_err("vframe_block structure allocation failed\n"); |
604 | return NULL; |
605 | } |
606 | |
607 | if (vframe_block_init_alloc_storage(input, |
608 | block, phy_addr, size) != 0) { |
609 | kfree(block); |
610 | pr_err("vframe_block storage allocation failed\n"); |
611 | return NULL; |
612 | } |
613 | |
614 | INIT_LIST_HEAD(&block->list); |
615 | |
616 | vdec_input_add_block(input, block); |
617 | |
618 | /* |
619 | *pr_info("vdec-%d:new block id=%d, total_blocks:%d, size=%d\n", |
620 | * input->id, |
621 | * block->id, |
622 | * input->block_nums, |
623 | * block->size); |
624 | */ |
625 | if (0 && input->size > VFRAME_BLOCK_MAX_LEVEL * 2) { |
626 | /* |
627 | used |
628 | */ |
629 | pr_info( |
630 | "input[%d] reach max: size:%d, blocks:%d", |
631 | input->id, |
632 | input->size, |
633 | input->block_nums); |
634 | pr_info("level:%d, wr:%lld,rd:%lld\n", |
635 | vdec_input_level(input), |
636 | input->total_wr_count, |
637 | input->total_rd_count); |
638 | vdec_input_dump_blocks(input, NULL, 0); |
639 | } |
640 | return block; |
641 | } |
642 | int vdec_input_get_duration_u64(struct vdec_input_s *input) |
643 | { |
644 | int duration = (input->last_inpts_u64 - input->last_comsumed_pts_u64); |
645 | if (input->last_in_nopts_cnt > 0 && |
646 | input->last_comsumed_pts_u64 > 0 && |
647 | input->last_duration > 0) { |
648 | duration += (input->last_in_nopts_cnt - |
649 | input->last_comsumed_no_pts_cnt) * |
650 | input->last_duration; |
651 | } |
652 | if (duration > 1000 * 1000000)/*> 1000S,I think jumped.*/ |
653 | duration = 0; |
654 | if (duration <= 0 && input->last_duration > 0) { |
655 | /*..*/ |
656 | duration = input->last_duration * input->have_frame_num; |
657 | } |
658 | if (duration < 0) |
659 | duration = 0; |
660 | return duration; |
661 | } |
662 | EXPORT_SYMBOL(vdec_input_get_duration_u64); |
663 | |
664 | /* |
665 | ret >= 13: have enough buffer, blocked add more buffers |
666 | */ |
667 | static int vdec_input_have_blocks_enough(struct vdec_input_s *input) |
668 | { |
669 | int ret = 0; |
670 | if (vdec_input_level(input) > VFRAME_BLOCK_MIN_LEVEL) |
671 | ret += 1; |
672 | if (vdec_input_level(input) >= VFRAME_BLOCK_MAX_LEVEL) |
673 | ret += 2; |
674 | if (vdec_input_get_duration_u64(input) > MAX_FRAME_DURATION_S) |
675 | ret += 4; |
676 | if (input->have_frame_num > 30) |
677 | ret += 8; |
678 | else |
679 | ret -= 8;/*not enough frames.*/ |
680 | if (input->size >= VFRAME_BLOCK_MAX_TOTAL_SIZE) |
681 | ret += 100;/*always bloced add more buffers.*/ |
682 | |
683 | return ret; |
684 | } |
685 | static int vdec_input_get_free_block( |
686 | struct vdec_input_s *input, |
687 | int size,/*frame size + pading*/ |
688 | struct vframe_block_list_s **block_ret) |
689 | { |
690 | struct vframe_block_list_s *to_freeblock = NULL; |
691 | struct vframe_block_list_s *block = NULL; |
692 | unsigned long flags; |
693 | flags = vdec_input_lock(input); |
694 | /*get from free list.*/ |
695 | if (!list_empty(&input->vframe_block_free_list)) { |
696 | block = list_entry(input->vframe_block_free_list.next, |
697 | struct vframe_block_list_s, list); |
698 | if (block->size < (size)) { |
699 | vdec_input_del_block_locked(input, block); |
700 | to_freeblock = block; |
701 | block = NULL; |
702 | } else { |
703 | list_move_tail(&block->list, |
704 | &input->vframe_block_list); |
705 | input->wr_block = block;/*swith to new block*/ |
706 | } |
707 | } |
708 | vdec_input_unlock(input, flags); |
709 | if (to_freeblock) { |
710 | /*free the small block.*/ |
711 | vframe_block_free_block(to_freeblock); |
712 | } |
713 | if (block) { |
714 | *block_ret = block; |
715 | return 0; |
716 | } |
717 | |
718 | if (vdec_input_have_blocks_enough(input) > 13) { |
719 | /*buf fulled */ |
720 | return -EAGAIN; |
721 | } |
722 | if (input->no_mem_err_cnt > 3) { |
723 | /*alloced failed more times. |
724 | */ |
725 | return -EAGAIN; |
726 | } |
727 | if (input->default_block_size <= |
728 | size * 2) { |
729 | int def_size = input->default_block_size; |
730 | do { |
731 | def_size *= 2; |
732 | } while ((def_size <= 2 * size) && |
733 | (def_size <= VFRAME_BLOCK_SIZE_MAX)); |
734 | if (def_size < size) |
735 | def_size = ALIGN(size + 64, (1 << 17)); |
736 | /*128k aligned,same as codec_mm*/ |
737 | input->default_block_size = def_size; |
738 | } |
739 | block = vdec_input_alloc_new_block(input, 0, 0); |
740 | if (!block) { |
741 | input->no_mem_err_cnt++; |
742 | return -EAGAIN; |
743 | } |
744 | input->no_mem_err_cnt = 0; |
745 | *block_ret = block; |
746 | return 0; |
747 | } |
748 | |
749 | int vdec_input_add_chunk(struct vdec_input_s *input, const char *buf, |
750 | size_t count, u32 handle) |
751 | { |
752 | unsigned long flags; |
753 | struct vframe_chunk_s *chunk; |
754 | struct vdec_s *vdec = input->vdec; |
755 | struct vframe_block_list_s *block; |
756 | |
757 | int need_pading_size = MIN_FRAME_PADDING_SIZE; |
758 | |
759 | if (vdec_secure(vdec)) { |
760 | block = vdec_input_alloc_new_block(input, (ulong)buf, |
761 | PAGE_ALIGN(count + HEVC_PADDING_SIZE + 1)); /*Add padding large than HEVC_PADDING_SIZE */ |
762 | if (!block) |
763 | return -ENOMEM; |
764 | block->handle = handle; |
765 | } else { |
766 | #if 0 |
767 | if (add_count == 0) { |
768 | add_count++; |
769 | memcpy(sps, buf, 30); |
770 | return 30; |
771 | } else if (add_count == 1) { |
772 | add_count++; |
773 | memcpy(pps, buf, 8); |
774 | return 8; |
775 | } |
776 | add_count++; |
777 | #endif |
778 | |
779 | #if 0 |
780 | pr_info("vdec_input_add_frame add %p, count=%d\n", buf, (int)count); |
781 | |
782 | if (count >= 8) { |
783 | pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", |
784 | buf[0], buf[1], buf[2], buf[3], |
785 | buf[4], buf[5], buf[6], buf[7]); |
786 | } |
787 | if (count >= 16) { |
788 | pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", |
789 | buf[8], buf[9], buf[10], buf[11], |
790 | buf[12], buf[13], buf[14], buf[15]); |
791 | } |
792 | if (count >= 24) { |
793 | pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", |
794 | buf[16], buf[17], buf[18], buf[19], |
795 | buf[20], buf[21], buf[22], buf[23]); |
796 | } |
797 | if (count >= 32) { |
798 | pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", |
799 | buf[24], buf[25], buf[26], buf[27], |
800 | buf[28], buf[29], buf[30], buf[31]); |
801 | } |
802 | #endif |
803 | if (input_stream_based(input)) |
804 | return -EINVAL; |
805 | |
806 | if (count < PAGE_SIZE) { |
807 | need_pading_size = PAGE_ALIGN(count + need_pading_size) - |
808 | count; |
809 | } else { |
810 | /*to 64 bytes aligned;*/ |
811 | if (count & 0x3f) |
812 | need_pading_size += 64 - (count & 0x3f); |
813 | } |
814 | block = input->wr_block; |
815 | if (block && |
816 | (vframe_block_space(block) > (count + need_pading_size))) { |
817 | /*this block have enough buffers. |
818 | do nothings. |
819 | */ |
820 | } else if (block && (block->type == VDEC_TYPE_FRAME_CIRCULAR)) { |
821 | /*in circular module. |
822 | only one block,.*/ |
823 | return -EAGAIN; |
824 | } else if (block != NULL) { |
825 | /*have block but not enough space. |
826 | recycle the no enough blocks.*/ |
827 | flags = vdec_input_lock(input); |
828 | if (input->wr_block == block && |
829 | block->chunk_count == 0) { |
830 | block->rp = 0; |
831 | block->wp = 0; |
832 | /*block no data move to freelist*/ |
833 | list_move_tail(&block->list, |
834 | &input->vframe_block_free_list); |
835 | input->wr_block = NULL; |
836 | } |
837 | vdec_input_unlock(input, flags); |
838 | block = NULL; |
839 | } |
840 | if (!block) {/*try new block.*/ |
841 | int ret = vdec_input_get_free_block(input, |
842 | count + need_pading_size + EXTRA_PADDING_SIZE, |
843 | &block); |
844 | if (ret < 0)/*no enough block now.*/ |
845 | return ret; |
846 | } |
847 | } |
848 | |
849 | chunk = kzalloc(sizeof(struct vframe_chunk_s), GFP_KERNEL); |
850 | |
851 | if (!chunk) { |
852 | pr_err("vframe_chunk structure allocation failed\n"); |
853 | return -ENOMEM; |
854 | } |
855 | |
856 | chunk->magic = 0x4b554843; |
857 | if (vdec->pts_valid) { |
858 | chunk->pts = vdec->pts; |
859 | chunk->pts64 = vdec->pts64; |
860 | } |
861 | |
862 | if (vdec->timestamp_valid) |
863 | chunk->timestamp = vdec->timestamp; |
864 | |
865 | if (vdec->pts_valid && |
866 | input->last_inpts_u64 > 0 && |
867 | input->last_in_nopts_cnt == 0) { |
868 | int d = (int)(chunk->pts64 - input->last_inpts_u64); |
869 | if (d > 0 && (d < input->last_duration)) |
870 | input->last_duration = d; |
871 | /* alwasy: used the smallest duration; |
872 | if 60fps->30 fps. |
873 | maybe have warning value. |
874 | */ |
875 | } |
876 | chunk->pts_valid = vdec->pts_valid; |
877 | vdec->pts_valid = false; |
878 | INIT_LIST_HEAD(&chunk->list); |
879 | |
880 | if (vdec_secure(vdec)) { |
881 | chunk->offset = 0; |
882 | chunk->size = count; |
883 | chunk->pading_size = PAGE_ALIGN(chunk->size + need_pading_size) - |
884 | chunk->size; |
885 | } else { |
886 | chunk->offset = block->wp; |
887 | chunk->size = count; |
888 | chunk->pading_size = need_pading_size; |
889 | if (vframe_chunk_fill(input, chunk, buf, count, block)) { |
890 | pr_err("vframe_chunk_fill failed\n"); |
891 | kfree(chunk); |
892 | return -EFAULT; |
893 | } |
894 | |
895 | } |
896 | |
897 | |
898 | flags = vdec_input_lock(input); |
899 | |
900 | vframe_block_add_chunk(block, chunk); |
901 | |
902 | list_add_tail(&chunk->list, &input->vframe_chunk_list); |
903 | input->data_size += chunk->size; |
904 | input->have_frame_num++; |
905 | |
906 | if (input->have_frame_num == 1) |
907 | input->vdec_up(vdec); |
908 | ATRACE_COUNTER(MEM_NAME, input->have_frame_num); |
909 | if (chunk->pts_valid) { |
910 | input->last_inpts_u64 = chunk->pts64; |
911 | input->last_in_nopts_cnt = 0; |
912 | } else { |
913 | /*nopts*/ |
914 | input->last_in_nopts_cnt++; |
915 | } |
916 | if (chunk->size > input->frame_max_size) |
917 | input->frame_max_size = chunk->size; |
918 | input->total_wr_count += count; |
919 | vdec_input_unlock(input, flags); |
920 | #if 0 |
921 | if (add_count == 2) |
922 | input->total_wr_count += 38; |
923 | #endif |
924 | |
925 | return count; |
926 | } |
927 | |
928 | int vdec_input_add_frame(struct vdec_input_s *input, const char *buf, |
929 | size_t count) |
930 | { |
931 | int ret = 0; |
932 | struct drm_info drm; |
933 | struct vdec_s *vdec = input->vdec; |
934 | unsigned long phy_buf; |
935 | |
936 | if (vdec_secure(vdec)) { |
937 | while (count > 0) { |
938 | if (count < sizeof(struct drm_info)) |
939 | return -EIO; |
940 | if (copy_from_user(&drm, buf + ret, sizeof(struct drm_info))) |
941 | return -EAGAIN; |
942 | if (!(drm.drm_flag & TYPE_DRMINFO_V2)) |
943 | return -EIO; /*must drm info v2 version*/ |
944 | phy_buf = (unsigned long) drm.drm_phy; |
945 | vdec_input_add_chunk(input, (char *)phy_buf, |
946 | (size_t)drm.drm_pktsize, drm.handle); |
947 | count -= sizeof(struct drm_info); |
948 | ret += sizeof(struct drm_info); |
949 | |
950 | /* the drm frame data might include head infos and raw */ |
951 | /* data thus the next drm unit still need a valid pts.*/ |
952 | if (count >= sizeof(struct drm_info)) |
953 | vdec->pts_valid = true; |
954 | } |
955 | } else { |
956 | ret = vdec_input_add_chunk(input, buf, count, 0); |
957 | } |
958 | |
959 | return ret; |
960 | } |
961 | EXPORT_SYMBOL(vdec_input_add_frame); |
962 | |
963 | struct vframe_chunk_s *vdec_input_next_chunk(struct vdec_input_s *input) |
964 | { |
965 | struct vframe_chunk_s *chunk = NULL; |
966 | unsigned long flags; |
967 | flags = vdec_input_lock(input); |
968 | if (!list_empty(&input->vframe_chunk_list)) { |
969 | chunk = list_first_entry(&input->vframe_chunk_list, |
970 | struct vframe_chunk_s, list); |
971 | } |
972 | vdec_input_unlock(input, flags); |
973 | return chunk; |
974 | } |
975 | EXPORT_SYMBOL(vdec_input_next_chunk); |
976 | |
977 | struct vframe_chunk_s *vdec_input_next_input_chunk( |
978 | struct vdec_input_s *input) |
979 | { |
980 | struct vframe_chunk_s *chunk = NULL; |
981 | struct list_head *p; |
982 | unsigned long flags; |
983 | flags = vdec_input_lock(input); |
984 | |
985 | list_for_each(p, &input->vframe_chunk_list) { |
986 | struct vframe_chunk_s *c = list_entry( |
987 | p, struct vframe_chunk_s, list); |
988 | if ((c->flag & VFRAME_CHUNK_FLAG_CONSUMED) == 0) { |
989 | chunk = c; |
990 | break; |
991 | } |
992 | } |
993 | vdec_input_unlock(input, flags); |
994 | return chunk; |
995 | } |
996 | EXPORT_SYMBOL(vdec_input_next_input_chunk); |
997 | |
998 | void vdec_input_release_chunk(struct vdec_input_s *input, |
999 | struct vframe_chunk_s *chunk) |
1000 | { |
1001 | struct vframe_chunk_s *p; |
1002 | u32 chunk_valid = 0; |
1003 | unsigned long flags; |
1004 | struct vframe_block_list_s *block = chunk->block; |
1005 | struct vframe_block_list_s *tofreeblock = NULL; |
1006 | flags = vdec_input_lock(input); |
1007 | |
1008 | list_for_each_entry(p, &input->vframe_chunk_list, list) { |
1009 | if (p == chunk) { |
1010 | chunk_valid = 1; |
1011 | break; |
1012 | } |
1013 | } |
1014 | /* 2 threads go here, the other done the deletion,so return*/ |
1015 | if (chunk_valid == 0) { |
1016 | vdec_input_unlock(input, flags); |
1017 | pr_err("%s chunk is deleted,so return.\n", __func__); |
1018 | return; |
1019 | } |
1020 | |
1021 | list_del(&chunk->list); |
1022 | input->have_frame_num--; |
1023 | ATRACE_COUNTER(MEM_NAME, input->have_frame_num); |
1024 | if (chunk->pts_valid) { |
1025 | input->last_comsumed_no_pts_cnt = 0; |
1026 | input->last_comsumed_pts_u64 = chunk->pts64; |
1027 | } else |
1028 | input->last_comsumed_no_pts_cnt++; |
1029 | block->rp += chunk->size; |
1030 | if (block->rp >= block->size) |
1031 | block->rp -= block->size; |
1032 | block->data_size -= chunk->size; |
1033 | block->chunk_count--; |
1034 | input->data_size -= chunk->size; |
1035 | input->total_rd_count += chunk->size; |
1036 | if (block->is_out_buf) { |
1037 | list_move_tail(&block->list, |
1038 | &input->vframe_block_free_list); |
1039 | } else if (block->chunk_count == 0 && |
1040 | input->wr_block != block ) {/*don't free used block*/ |
1041 | if (block->size < input->default_block_size) { |
1042 | vdec_input_del_block_locked(input, block); |
1043 | tofreeblock = block; |
1044 | } else { |
1045 | block->rp = 0; |
1046 | block->wp = 0; |
1047 | list_move_tail(&block->list, |
1048 | &input->vframe_block_free_list); |
1049 | } |
1050 | } |
1051 | |
1052 | vdec_input_unlock(input, flags); |
1053 | if (tofreeblock) |
1054 | vframe_block_free_block(tofreeblock); |
1055 | kfree(chunk); |
1056 | } |
1057 | EXPORT_SYMBOL(vdec_input_release_chunk); |
1058 | |
1059 | unsigned long vdec_input_lock(struct vdec_input_s *input) |
1060 | { |
1061 | unsigned long flags; |
1062 | |
1063 | spin_lock_irqsave(&input->lock, flags); |
1064 | |
1065 | return flags; |
1066 | } |
1067 | EXPORT_SYMBOL(vdec_input_lock); |
1068 | |
1069 | void vdec_input_unlock(struct vdec_input_s *input, unsigned long flags) |
1070 | { |
1071 | spin_unlock_irqrestore(&input->lock, flags); |
1072 | } |
1073 | EXPORT_SYMBOL(vdec_input_unlock); |
1074 | |
1075 | void vdec_input_release(struct vdec_input_s *input) |
1076 | { |
1077 | struct list_head *p, *tmp; |
1078 | |
1079 | /* release chunk data */ |
1080 | list_for_each_safe(p, tmp, &input->vframe_chunk_list) { |
1081 | struct vframe_chunk_s *chunk = list_entry( |
1082 | p, struct vframe_chunk_s, list); |
1083 | vdec_input_release_chunk(input, chunk); |
1084 | } |
1085 | list_for_each_safe(p, tmp, &input->vframe_block_list) { |
1086 | /*should never here.*/ |
1087 | list_move_tail(p, &input->vframe_block_free_list); |
1088 | } |
1089 | /* release input blocks */ |
1090 | list_for_each_safe(p, tmp, &input->vframe_block_free_list) { |
1091 | struct vframe_block_list_s *block = list_entry( |
1092 | p, struct vframe_block_list_s, list); |
1093 | vdec_input_del_block_locked(input, block); |
1094 | vframe_block_free_block(block); |
1095 | } |
1096 | |
1097 | /* release swap pages */ |
1098 | if (input->swap_page_phys) { |
1099 | if (vdec_secure(input->vdec)) |
1100 | codec_mm_free_for_dma("SWAP", input->swap_page_phys); |
1101 | else |
1102 | __free_page(input->swap_page); |
1103 | input->swap_page = NULL; |
1104 | input->swap_page_phys = 0; |
1105 | } |
1106 | input->swap_valid = false; |
1107 | } |
1108 | EXPORT_SYMBOL(vdec_input_release); |
1109 | |
1110 | u32 vdec_input_get_freed_handle(struct vdec_s *vdec) |
1111 | { |
1112 | struct vframe_block_list_s *block; |
1113 | struct vdec_input_s *input = &vdec->input; |
1114 | unsigned long flags; |
1115 | u32 handle = 0; |
1116 | |
1117 | if (!vdec_secure(vdec)) |
1118 | return 0; |
1119 | |
1120 | flags = vdec_input_lock(input); |
1121 | block = list_first_entry_or_null(&input->vframe_block_free_list, |
1122 | struct vframe_block_list_s, list); |
1123 | |
1124 | if (block) { |
1125 | handle = block->handle; |
1126 | vdec_input_del_block_locked(input, block); |
1127 | kfree(block); |
1128 | } |
1129 | vdec_input_unlock(input, flags); |
1130 | return handle; |
1131 | } |
1132 | EXPORT_SYMBOL(vdec_input_get_freed_handle); |
1133 | |
1134 | |
1135 |