blob: 6fec0ad9eb0591da51d95234bb41df18a150b557 [file] [log] [blame]
/*
* drivers/amlogic/media/frame_provider/decoder/utils/vdec_input.c
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include "../../../stream_input/amports/amports_priv.h"
#include "vdec.h"
#include "vdec_input.h"
#include <asm/cacheflush.h>
#include <linux/crc32.h>
#define VFRAME_BLOCK_SIZE (512 * SZ_1K)/*512 for 1080p default init.*/
#define VFRAME_BLOCK_SIZE_4K (2 * SZ_1M) /*2M for 4K default.*/
#define VFRAME_BLOCK_SIZE_MAX (4 * SZ_1M)
#define VFRAME_BLOCK_PAGEALIGN 4
#define VFRAME_BLOCK_MIN_LEVEL (2 * SZ_1M)
#define VFRAME_BLOCK_MAX_LEVEL (8 * SZ_1M)
#define VFRAME_BLOCK_MAX_TOTAL_SIZE (16 * SZ_1M)
/*
2s for OMX
*/
#define MAX_FRAME_DURATION_S 2
#define VFRAME_BLOCK_HOLE (SZ_64K)
#define MIN_FRAME_PADDING_SIZE ((u32)(L1_CACHE_BYTES))
#define EXTRA_PADDING_SIZE (16 * SZ_1K) /*HEVC_PADDING_SIZE*/
#define MEM_NAME "VFRAME_INPUT"
//static int vdec_input_get_duration_u64(struct vdec_input_s *input);
static struct vframe_block_list_s *
vdec_input_alloc_new_block(struct vdec_input_s *input,
ulong phy_addr,
int size,
chunk_free free,
void* priv);
static int aml_copy_from_user(void *to, const void *from, ulong n)
{
int ret =0;
if (likely(access_ok(from, n)))
ret = copy_from_user(to, from, n);
else
memcpy(to, from, n);
return ret;
}
static int copy_from_user_to_phyaddr(void *virts, const char __user *buf,
u32 size, ulong phys, u32 pading, bool is_mapped)
{
u32 i, span = SZ_1M;
u32 count = size / PAGE_ALIGN(span);
u32 remain = size % PAGE_ALIGN(span);
ulong addr = phys;
u8 *p = virts;
if (is_mapped) {
if (aml_copy_from_user(p, buf, size))
return -EFAULT;
if (pading)
memset(p + size, 0, pading);
codec_mm_dma_flush(p, size + pading, DMA_TO_DEVICE);
return 0;
}
for (i = 0; i < count; i++) {
addr = phys + i * span;
p = codec_mm_vmap(addr, span);
if (!p)
return -1;
if (aml_copy_from_user(p, buf + i * span, span)) {
codec_mm_unmap_phyaddr(p);
return -EFAULT;
}
codec_mm_dma_flush(p, span, DMA_TO_DEVICE);
codec_mm_unmap_phyaddr(p);
}
if (!remain)
return 0;
span = size - remain;
addr = phys + span;
p = codec_mm_vmap(addr, remain + pading);
if (!p)
return -1;
if (aml_copy_from_user(p, buf + span, remain)) {
codec_mm_unmap_phyaddr(p);
return -EFAULT;
}
if (pading)
memset(p + remain, 0, pading);
codec_mm_dma_flush(p, remain + pading, DMA_TO_DEVICE);
codec_mm_unmap_phyaddr(p);
return 0;
}
static int vframe_chunk_fill(struct vdec_input_s *input,
struct vframe_chunk_s *chunk, const char *buf,
size_t count, struct vframe_block_list_s *block)
{
u8 *p = (u8 *)block->start_virt + block->wp;
if (block->type == VDEC_TYPE_FRAME_BLOCK) {
copy_from_user_to_phyaddr(p, buf, count,
block->start + block->wp,
chunk->pading_size,
block->is_mapped);
} else if (block->type == VDEC_TYPE_FRAME_CIRCULAR) {
size_t len = min((size_t)(block->size - block->wp), count);
u32 wp;
copy_from_user_to_phyaddr(p, buf, len,
block->start + block->wp, 0,
block->is_mapped);
p += len;
if (count > len) {
copy_from_user_to_phyaddr(p, buf + len,
count - len,
block->start, 0,
block->is_mapped);
p += count - len;
}
wp = block->wp + count;
if (wp >= block->size)
wp -= block->size;
len = min(block->size - wp, chunk->pading_size);
if (!block->is_mapped) {
p = codec_mm_vmap(block->start + wp, len);
memset(p, 0, len);
codec_mm_dma_flush(p, len, DMA_TO_DEVICE);
codec_mm_unmap_phyaddr(p);
} else {
memset(p, 0, len);
codec_mm_dma_flush(p, len, DMA_TO_DEVICE);
}
if (chunk->pading_size > len) {
p = (u8 *)block->start_virt;
if (!block->is_mapped) {
p = codec_mm_vmap(block->start,
chunk->pading_size - len);
memset(p, 0, chunk->pading_size - len);
codec_mm_dma_flush(p,
chunk->pading_size - len,
DMA_TO_DEVICE);
codec_mm_unmap_phyaddr(p);
} else {
memset(p, 0, chunk->pading_size - len);
codec_mm_dma_flush(p,
chunk->pading_size - len,
DMA_TO_DEVICE);
}
}
}
return 0;
}
static inline u32 vframe_block_space(struct vframe_block_list_s *block)
{
if (block->type == VDEC_TYPE_FRAME_BLOCK) {
return block->size - block->wp;
} else {
return (block->rp >= block->wp) ?
(block->rp - block->wp) :
(block->rp - block->wp + block->size);
}
}
static void vframe_block_add_chunk(struct vframe_block_list_s *block,
struct vframe_chunk_s *chunk)
{
block->wp += chunk->size + chunk->pading_size;
if (block->wp >= block->size)
block->wp -= block->size;
block->data_size += chunk->size;
block->chunk_count++;
chunk->block = block;
block->input->wr_block = block;
chunk->sequence = block->input->sequence;
block->input->sequence++;
}
static bool is_coherent_buff = 1;
static void vframe_block_free_block(struct vframe_block_list_s *block)
{
if (is_coherent_buff) {
if (block->mem_handle) {
codec_mm_dma_free_coherent(block->mem_handle);
}
} else {
if (block->addr) {
codec_mm_free_for_dma(MEM_NAME, block->addr);
}
}
/*
*pr_err("free block %d, size=%d\n", block->id, block->size);
*/
kfree(block);
}
static int vframe_block_init_alloc_storage(struct vdec_input_s *input,
struct vframe_block_list_s *block,
ulong phy_addr,
int size,
chunk_free free,
void *priv)
{
int alloc_size = input->default_block_size;
block->magic = 0x4b434c42;
block->input = input;
block->type = input->type;
/*
* todo: for different type use different size
*/
if (phy_addr) {
block->is_out_buf = 1;
block->start_virt = NULL;
block->start = phy_addr;
block->size = size;
block->free = free;
block->priv = priv;
} else {
alloc_size = PAGE_ALIGN(alloc_size);
if (is_coherent_buff) {
block->start_virt = codec_mm_dma_alloc_coherent(&block->mem_handle, &block->addr, alloc_size, MEM_NAME);
} else {
block->addr = codec_mm_alloc_for_dma_ex(
MEM_NAME,
alloc_size/PAGE_SIZE,
VFRAME_BLOCK_PAGEALIGN,
CODEC_MM_FLAGS_DMA_CPU | CODEC_MM_FLAGS_FOR_VDECODER,
input->id,
block->id);
}
if (!block->addr) {
pr_err("Input block allocation failed\n");
return -ENOMEM;
}
if (!is_coherent_buff)
block->start_virt = (void *)codec_mm_phys_to_virt(block->addr);
if (block->start_virt)
block->is_mapped = true;
block->start = block->addr;
block->size = alloc_size;
block->is_out_buf = 0;
block->free = NULL;
}
return 0;
}
void vdec_input_init(struct vdec_input_s *input, struct vdec_s *vdec)
{
INIT_LIST_HEAD(&input->vframe_block_list);
INIT_LIST_HEAD(&input->vframe_block_free_list);
INIT_LIST_HEAD(&input->vframe_chunk_list);
spin_lock_init(&input->lock);
input->id = vdec->id;
input->block_nums = 0;
input->vdec = vdec;
input->block_id_seq = 0;
input->size = 0;
input->default_block_size = VFRAME_BLOCK_SIZE;
snprintf(input->vdec_input_name, sizeof(input->vdec_input_name),
"vdec-input-%d", vdec->id);
}
int vdec_input_prepare_bufs(struct vdec_input_s *input,
int frame_width, int frame_height)
{
struct vframe_block_list_s *block;
int i;
unsigned long flags;
if (vdec_secure(input->vdec))
return 0;
if (input->size > 0)
return 0;
if (frame_width * frame_height >= 1920 * 1088) {
/*have add data before. ignore prepare buffers.*/
input->default_block_size = VFRAME_BLOCK_SIZE_4K;
}
/*prepared 3 buffers for smooth start.*/
for (i = 0; i < 3; i++) {
block = vdec_input_alloc_new_block(input, 0, 0, NULL, NULL);
if (!block)
break;
flags = vdec_input_lock(input);
list_move_tail(&block->list,
&input->vframe_block_free_list);
input->wr_block = NULL;
vdec_input_unlock(input, flags);
}
return 0;
}
static int vdec_input_dump_block_locked(
struct vframe_block_list_s *block,
char *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
if (!pbuf) {
pbuf = sbuf;
size = 512;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT("\tblock:[%d:%p]-addr=%p,vstart=%p,type=%d\n",
block->id,
block,
(void *)block->addr,
(void *)block->start_virt,
block->type);
BUFPRINT("\t-blocksize=%d,data=%d,wp=%d,rp=%d,chunk_count=%d\n",
block->size,
block->data_size,
block->wp,
block->rp,
block->chunk_count);
/*
BUFPRINT("\tlist=%p,next=%p,prev=%p\n",
&block->list,
block->list.next,
block->list.prev);
*/
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
int vdec_input_dump_blocks(struct vdec_input_s *input,
char *bufs, int size)
{
struct list_head *p, *tmp;
unsigned long flags;
char *lbuf = bufs;
char sbuf[256];
int s = 0;
if (size <= 0)
return 0;
if (!bufs)
lbuf = sbuf;
s += snprintf(lbuf + s, size - s,
"blocks:vdec-%d id:%d,bufsize=%d,dsize=%d,frames:%d,dur:%dms\n",
input->id,
input->block_nums,
input->size,
input->data_size,
input->have_frame_num,
vdec_input_get_duration_u64(input)/1000);
if (bufs)
lbuf += s;
else {
pr_info("%s", sbuf);
lbuf = NULL;
}
flags = vdec_input_lock(input);
/* dump input blocks */
list_for_each_safe(p, tmp, &input->vframe_block_list) {
struct vframe_block_list_s *block = list_entry(
p, struct vframe_block_list_s, list);
if (bufs != NULL) {
lbuf = bufs + s;
if (size - s < 128)
break;
}
s += vdec_input_dump_block_locked(block, lbuf, size - s);
}
list_for_each_safe(p, tmp, &input->vframe_block_free_list) {
struct vframe_block_list_s *block = list_entry(
p, struct vframe_block_list_s, list);
if (bufs != NULL) {
lbuf = bufs + s;
if (size - s < 128)
break;
}
s += vdec_input_dump_block_locked(block, lbuf, size - s);
}
vdec_input_unlock(input, flags);
return s;
}
static int vdec_input_dump_chunk_locked(
int id,
struct vframe_chunk_s *chunk,
char *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
if (!pbuf) {
pbuf = sbuf;
size = 512;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT(
"\t[%d][%lld:%p]-off=%d,size:%d,p:%d,\tpts64=%lld,addr=%p\n",
id,
chunk->sequence,
chunk->block,
chunk->offset,
chunk->size,
chunk->pading_size,
chunk->pts64,
(void *)(chunk->block->addr + chunk->offset));
/*
BUFPRINT("\tlist=%p,next=%p,prev=%p\n",
&chunk->list,
chunk->list.next,
chunk->list.prev);
*/
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
int vdec_input_dump_chunks(int id, struct vdec_input_s *input,
char *bufs, int size)
{
struct list_head *p, *tmp;
unsigned long flags;
char *lbuf = bufs;
char sbuf[256];
int s = 0;
int i = 0;
if (size <= 0)
return 0;
if (!bufs)
lbuf = sbuf;
s = snprintf(lbuf + s, size - s,
"[%d]blocks:vdec-%d id:%d,bufsize=%d,dsize=%d,frames:%d,maxframe:%d\n",
id,
input->id,
input->block_nums,
input->size,
input->data_size,
input->have_frame_num,
input->frame_max_size);
if (bufs)
lbuf += s;
if (!bufs) {
pr_info("%s", sbuf);
lbuf = NULL;
}
flags = vdec_input_lock(input);
/*dump chunks list infos.*/
list_for_each_safe(p, tmp, &input->vframe_chunk_list) {
struct vframe_chunk_s *chunk = list_entry(
p, struct vframe_chunk_s, list);
if (bufs != NULL)
lbuf = bufs + s;
s += vdec_input_dump_chunk_locked(id, chunk, lbuf, size - s);
i++;
if (i >= 10)
break;
}
vdec_input_unlock(input, flags);
return s;
}
int vdec_input_set_buffer(struct vdec_input_s *input, u32 start, u32 size)
{
if (input_frame_based(input))
return -EINVAL;
input->start = start;
input->size = size;
input->swap_rp = start;
if (vdec_secure(input->vdec))
input->swap_page_phys = codec_mm_alloc_for_dma("SWAP",
1, 0, CODEC_MM_FLAGS_TVP);
else {
input->swap_page = codec_mm_dma_alloc_coherent(&input->mem_handle,
(ulong *)&input->swap_page_phys,
PAGE_SIZE, MEM_NAME);
if (input->swap_page == NULL)
return -ENOMEM;
}
if (input->swap_page_phys == 0)
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL(vdec_input_set_buffer);
void vdec_input_set_type(struct vdec_input_s *input, int type, int target)
{
input->type = type;
input->target = target;
if (type == VDEC_TYPE_FRAME_CIRCULAR) {
/*alway used max block.*/
input->default_block_size = VFRAME_BLOCK_SIZE_MAX;
}
}
EXPORT_SYMBOL(vdec_input_set_type);
int vdec_input_get_status(struct vdec_input_s *input,
struct vdec_input_status_s *status)
{
unsigned long flags;
if (input->vdec == NULL)
return -EINVAL;
flags = vdec_input_lock(input);
if (list_empty(&input->vframe_block_list)) {
status->size = VFRAME_BLOCK_SIZE;
status->data_len = 0;
status->free_len = VFRAME_BLOCK_SIZE;
status->read_pointer = 0;
} else {
int r = VFRAME_BLOCK_MAX_LEVEL - vdec_input_level(input)
- VFRAME_BLOCK_HOLE;
status->size = input->size;
status->data_len = vdec_input_level(input);
status->free_len = (r > 0) ? r : 0;
status->read_pointer = input->total_rd_count;
}
vdec_input_unlock(input, flags);
return 0;
}
EXPORT_SYMBOL(vdec_input_get_status);
static void vdec_input_add_block(struct vdec_input_s *input,
struct vframe_block_list_s *block)
{
unsigned long flags;
flags = vdec_input_lock(input);
block->wp = 0;
block->id = input->block_id_seq++;
list_add_tail(&block->list, &input->vframe_block_list);
input->size += block->size;
input->block_nums++;
input->wr_block = block;
vdec_input_unlock(input, flags);
}
static inline void vdec_input_del_block_locked(struct vdec_input_s *input,
struct vframe_block_list_s *block)
{
list_del(&block->list);
input->size -= block->size;
input->block_nums--;
}
int vdec_input_level(struct vdec_input_s *input)
{
return input->total_wr_count - input->total_rd_count;
}
EXPORT_SYMBOL(vdec_input_level);
static struct vframe_block_list_s *
vdec_input_alloc_new_block(struct vdec_input_s *input,
ulong phy_addr,
int size,
chunk_free free,
void* priv)
{
struct vframe_block_list_s *block;
block = kzalloc(sizeof(struct vframe_block_list_s),
GFP_KERNEL);
if (block == NULL) {
input->no_mem_err_cnt++;
pr_err("vframe_block structure allocation failed\n");
return NULL;
}
if (vframe_block_init_alloc_storage(input,
block, phy_addr, size, free, priv) != 0) {
kfree(block);
pr_err("vframe_block storage allocation failed\n");
return NULL;
}
INIT_LIST_HEAD(&block->list);
vdec_input_add_block(input, block);
/*
*pr_info("vdec-%d:new block id=%d, total_blocks:%d, size=%d\n",
* input->id,
* block->id,
* input->block_nums,
* block->size);
*/
if (0 && input->size > VFRAME_BLOCK_MAX_LEVEL * 2) {
/*
used
*/
pr_info(
"input[%d] reach max: size:%d, blocks:%d",
input->id,
input->size,
input->block_nums);
pr_info("level:%d, wr:%lld,rd:%lld\n",
vdec_input_level(input),
input->total_wr_count,
input->total_rd_count);
vdec_input_dump_blocks(input, NULL, 0);
}
return block;
}
int vdec_input_get_duration_u64(struct vdec_input_s *input)
{
int duration = (input->last_inpts_u64 - input->last_comsumed_pts_u64);
if (input->last_in_nopts_cnt > 0 &&
input->last_comsumed_pts_u64 > 0 &&
input->last_duration > 0) {
duration += (input->last_in_nopts_cnt -
input->last_comsumed_no_pts_cnt) *
input->last_duration;
}
if (duration > 1000 * 1000000)/*> 1000S,I think jumped.*/
duration = 0;
if (duration <= 0 && input->last_duration > 0) {
/*..*/
duration = input->last_duration * input->have_frame_num;
}
if (duration < 0)
duration = 0;
return duration;
}
EXPORT_SYMBOL(vdec_input_get_duration_u64);
/*
ret >= 13: have enough buffer, blocked add more buffers
*/
static int vdec_input_have_blocks_enough(struct vdec_input_s *input)
{
int ret = 0;
if (vdec_input_level(input) > VFRAME_BLOCK_MIN_LEVEL)
ret += 1;
if (vdec_input_level(input) >= VFRAME_BLOCK_MAX_LEVEL)
ret += 2;
if (vdec_input_get_duration_u64(input) > MAX_FRAME_DURATION_S)
ret += 4;
if (input->have_frame_num > 30)
ret += 8;
else
ret -= 8;/*not enough frames.*/
if (input->size >= VFRAME_BLOCK_MAX_TOTAL_SIZE)
ret += 100;/*always bloced add more buffers.*/
return ret;
}
static int vdec_input_get_free_block(
struct vdec_input_s *input,
int size,/*frame size + pading*/
struct vframe_block_list_s **block_ret)
{
struct vframe_block_list_s *to_freeblock = NULL;
struct vframe_block_list_s *block = NULL;
unsigned long flags;
flags = vdec_input_lock(input);
/*get from free list.*/
if (!list_empty(&input->vframe_block_free_list)) {
block = list_entry(input->vframe_block_free_list.next,
struct vframe_block_list_s, list);
if (block->size < (size)) {
vdec_input_del_block_locked(input, block);
to_freeblock = block;
block = NULL;
} else {
list_move_tail(&block->list,
&input->vframe_block_list);
input->wr_block = block;/*swith to new block*/
}
}
vdec_input_unlock(input, flags);
if (to_freeblock) {
/*free the small block.*/
vframe_block_free_block(to_freeblock);
}
if (block) {
*block_ret = block;
return 0;
}
if (vdec_input_have_blocks_enough(input) > 13) {
/*buf fulled */
return -EAGAIN;
}
if (input->no_mem_err_cnt > 3) {
/*alloced failed more times.
*/
return -EAGAIN;
}
if (input->default_block_size <=
size * 2) {
int def_size = input->default_block_size;
do {
def_size *= 2;
} while ((def_size <= 2 * size) &&
(def_size <= VFRAME_BLOCK_SIZE_MAX));
if (def_size < size)
def_size = ALIGN(size + 64, (1 << 17));
/*128k aligned,same as codec_mm*/
input->default_block_size = def_size;
}
block = vdec_input_alloc_new_block(input, 0, 0, NULL, NULL);
if (!block) {
input->no_mem_err_cnt++;
return -EAGAIN;
}
input->no_mem_err_cnt = 0;
*block_ret = block;
return 0;
}
int vdec_input_add_chunk(struct vdec_input_s *input, const char *buf,
size_t count, u32 handle, chunk_free free, void* priv)
{
unsigned long flags;
struct vframe_chunk_s *chunk;
struct vdec_s *vdec = input->vdec;
struct vframe_block_list_s *block;
int need_pading_size = MIN_FRAME_PADDING_SIZE;
if (vdec_secure(vdec)) {
block = vdec_input_alloc_new_block(input, (ulong)buf,
PAGE_ALIGN(count + HEVC_PADDING_SIZE + 1),
free, priv); /*Add padding large than HEVC_PADDING_SIZE */
if (!block)
return -ENOMEM;
block->handle = handle;
} else {
#if 0
if (add_count == 0) {
add_count++;
memcpy(sps, buf, 30);
return 30;
} else if (add_count == 1) {
add_count++;
memcpy(pps, buf, 8);
return 8;
}
add_count++;
#endif
#if 0
pr_info("vdec_input_add_frame add %p, count=%d\n", buf, (int)count);
if (count >= 8) {
pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7]);
}
if (count >= 16) {
pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[8], buf[9], buf[10], buf[11],
buf[12], buf[13], buf[14], buf[15]);
}
if (count >= 24) {
pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[16], buf[17], buf[18], buf[19],
buf[20], buf[21], buf[22], buf[23]);
}
if (count >= 32) {
pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[24], buf[25], buf[26], buf[27],
buf[28], buf[29], buf[30], buf[31]);
}
#endif
if (input_stream_based(input))
return -EINVAL;
if (count < PAGE_SIZE) {
need_pading_size = PAGE_ALIGN(count + need_pading_size) -
count;
} else {
/*to 64 bytes aligned;*/
if (count & 0x3f)
need_pading_size += 64 - (count & 0x3f);
}
block = input->wr_block;
if (block &&
(vframe_block_space(block) > (count + need_pading_size))) {
/*this block have enough buffers.
do nothings.
*/
} else if (block && (block->type == VDEC_TYPE_FRAME_CIRCULAR)) {
/*in circular module.
only one block,.*/
return -EAGAIN;
} else if (block != NULL) {
/*have block but not enough space.
recycle the no enough blocks.*/
flags = vdec_input_lock(input);
if (input->wr_block == block &&
block->chunk_count == 0) {
block->rp = 0;
block->wp = 0;
/*block no data move to freelist*/
list_move_tail(&block->list,
&input->vframe_block_free_list);
input->wr_block = NULL;
}
vdec_input_unlock(input, flags);
block = NULL;
}
if (!block) {/*try new block.*/
int ret = vdec_input_get_free_block(input,
count + need_pading_size + EXTRA_PADDING_SIZE,
&block);
if (ret < 0)/*no enough block now.*/
return ret;
}
}
chunk = kzalloc(sizeof(struct vframe_chunk_s), GFP_KERNEL);
if (!chunk) {
pr_err("vframe_chunk structure allocation failed\n");
return -ENOMEM;
}
if ((vdec->hdr10p_data_valid == true) &&
(vdec->hdr10p_data_size != 0)) {
char *new_buf;
new_buf = vzalloc(vdec->hdr10p_data_size);
if (new_buf) {
memcpy(new_buf, vdec->hdr10p_data_buf, vdec->hdr10p_data_size);
chunk->hdr10p_data_buf = new_buf;
chunk->hdr10p_data_size = vdec->hdr10p_data_size;
} else {
pr_err("%s:hdr10p data vzalloc size(%d) failed\n",
__func__, vdec->hdr10p_data_size);
chunk->hdr10p_data_buf = NULL;
chunk->hdr10p_data_size = 0;
}
} else {
chunk->hdr10p_data_buf = NULL;
chunk->hdr10p_data_size = 0;
}
vdec->hdr10p_data_valid = false;
chunk->magic = 0x4b554843;
if (vdec->pts_valid) {
chunk->pts = vdec->pts;
chunk->pts64 = vdec->pts64;
}
if (vdec->timestamp_valid)
chunk->timestamp = vdec->timestamp;
if (vdec->pts_valid &&
input->last_inpts_u64 > 0 &&
input->last_in_nopts_cnt == 0) {
int d = (int)(chunk->pts64 - input->last_inpts_u64);
if (d > 0 && (d < input->last_duration))
input->last_duration = d;
/* alwasy: used the smallest duration;
if 60fps->30 fps.
maybe have warning value.
*/
}
chunk->pts_valid = vdec->pts_valid;
vdec->pts_valid = false;
INIT_LIST_HEAD(&chunk->list);
if (vdec_secure(vdec)) {
chunk->offset = 0;
chunk->size = count;
chunk->pading_size = PAGE_ALIGN(chunk->size + need_pading_size) -
chunk->size;
} else {
chunk->offset = block->wp;
chunk->size = count;
chunk->pading_size = need_pading_size;
if (vframe_chunk_fill(input, chunk, buf, count, block)) {
pr_err("vframe_chunk_fill failed\n");
kfree(chunk);
return -EFAULT;
}
}
flags = vdec_input_lock(input);
vframe_block_add_chunk(block, chunk);
list_add_tail(&chunk->list, &input->vframe_chunk_list);
input->data_size += chunk->size;
input->have_frame_num++;
if (input->have_frame_num == 1)
input->vdec_up(vdec);
ATRACE_COUNTER(input->vdec_input_name, input->have_frame_num);
if (chunk->pts_valid) {
input->last_inpts_u64 = chunk->pts64;
input->last_in_nopts_cnt = 0;
} else {
/*nopts*/
input->last_in_nopts_cnt++;
}
if (chunk->size > input->frame_max_size)
input->frame_max_size = chunk->size;
input->total_wr_count += count;
vdec_input_unlock(input, flags);
#if 0
if (add_count == 2)
input->total_wr_count += 38;
#endif
return count;
}
int vdec_input_add_frame(struct vdec_input_s *input, const char *buf,
size_t count)
{
int ret = 0;
struct drm_info drm;
struct vdec_s *vdec = input->vdec;
unsigned long phy_buf;
if (vdec_secure(vdec)) {
while (count > 0) {
if (count < sizeof(struct drm_info))
return -EIO;
if (copy_from_user((void*)&drm, buf + ret, sizeof(struct drm_info)))
return -EAGAIN;
if (!(drm.drm_flag & TYPE_DRMINFO_V2))
return -EIO; /*must drm info v2 version*/
phy_buf = (unsigned long) drm.drm_phy;
vdec_input_add_chunk(input, (char *)phy_buf,
(size_t)drm.drm_pktsize, drm.handle, NULL, NULL);
count -= sizeof(struct drm_info);
ret += sizeof(struct drm_info);
/* the drm frame data might include head infos and raw */
/* data thus the next drm unit still need a valid pts.*/
if (count >= sizeof(struct drm_info))
vdec->pts_valid = true;
}
} else {
ret = vdec_input_add_chunk(input, buf, count, 0, NULL, NULL);
}
return ret;
}
EXPORT_SYMBOL(vdec_input_add_frame);
int vdec_input_add_frame_with_dma(struct vdec_input_s *input, ulong addr,
size_t count, u32 handle, chunk_free free, void* priv)
{
struct vdec_s *vdec = input->vdec;
return vdec_secure(vdec) ?
vdec_input_add_chunk(input,
(char *)addr, count, handle, free, priv) : -1;
}
EXPORT_SYMBOL(vdec_input_add_frame_with_dma);
struct vframe_chunk_s *vdec_input_next_chunk(struct vdec_input_s *input)
{
struct vframe_chunk_s *chunk = NULL;
unsigned long flags;
flags = vdec_input_lock(input);
if (!list_empty(&input->vframe_chunk_list)) {
chunk = list_first_entry(&input->vframe_chunk_list,
struct vframe_chunk_s, list);
}
vdec_input_unlock(input, flags);
return chunk;
}
EXPORT_SYMBOL(vdec_input_next_chunk);
struct vframe_chunk_s *vdec_input_next_input_chunk(
struct vdec_input_s *input)
{
struct vframe_chunk_s *chunk = NULL;
struct list_head *p;
unsigned long flags;
flags = vdec_input_lock(input);
list_for_each(p, &input->vframe_chunk_list) {
struct vframe_chunk_s *c = list_entry(
p, struct vframe_chunk_s, list);
if ((c->flag & VFRAME_CHUNK_FLAG_CONSUMED) == 0) {
chunk = c;
break;
}
}
vdec_input_unlock(input, flags);
return chunk;
}
EXPORT_SYMBOL(vdec_input_next_input_chunk);
void vdec_input_release_chunk(struct vdec_input_s *input,
struct vframe_chunk_s *chunk)
{
struct vframe_chunk_s *p;
u32 chunk_valid = 0;
unsigned long flags;
struct vframe_block_list_s *block = chunk->block;
struct vframe_block_list_s *tofreeblock = NULL;
flags = vdec_input_lock(input);
list_for_each_entry(p, &input->vframe_chunk_list, list) {
if (p == chunk) {
chunk_valid = 1;
break;
}
}
/* 2 threads go here, the other done the deletion,so return*/
if (chunk_valid == 0) {
vdec_input_unlock(input, flags);
pr_err("%s chunk is deleted,so return.\n", __func__);
return;
}
list_del(&chunk->list);
input->have_frame_num--;
ATRACE_COUNTER(input->vdec_input_name, input->have_frame_num);
if (chunk->pts_valid) {
input->last_comsumed_no_pts_cnt = 0;
input->last_comsumed_pts_u64 = chunk->pts64;
} else
input->last_comsumed_no_pts_cnt++;
block->rp += chunk->size;
if (block->rp >= block->size)
block->rp -= block->size;
block->data_size -= chunk->size;
block->chunk_count--;
input->data_size -= chunk->size;
input->total_rd_count += chunk->size;
if (block->is_out_buf) {
list_move_tail(&block->list,
&input->vframe_block_free_list);
if (block->free) {
vdec_input_del_block_locked(input, block);
block->free(block->priv, block->handle);
kfree(block);
}
} else if (block->chunk_count == 0 &&
input->wr_block != block ) {/*don't free used block*/
if (block->size < input->default_block_size) {
vdec_input_del_block_locked(input, block);
tofreeblock = block;
} else {
block->rp = 0;
block->wp = 0;
list_move_tail(&block->list,
&input->vframe_block_free_list);
}
}
vdec_input_unlock(input, flags);
if (tofreeblock)
vframe_block_free_block(tofreeblock);
kfree(chunk);
}
EXPORT_SYMBOL(vdec_input_release_chunk);
unsigned long vdec_input_lock(struct vdec_input_s *input)
{
unsigned long flags;
spin_lock_irqsave(&input->lock, flags);
return flags;
}
EXPORT_SYMBOL(vdec_input_lock);
void vdec_input_unlock(struct vdec_input_s *input, unsigned long flags)
{
spin_unlock_irqrestore(&input->lock, flags);
}
EXPORT_SYMBOL(vdec_input_unlock);
void vdec_input_release(struct vdec_input_s *input)
{
struct list_head *p, *tmp;
/* release chunk data */
list_for_each_safe(p, tmp, &input->vframe_chunk_list) {
struct vframe_chunk_s *chunk = list_entry(
p, struct vframe_chunk_s, list);
vdec_input_release_chunk(input, chunk);
}
list_for_each_safe(p, tmp, &input->vframe_block_list) {
/*should never here.*/
list_move_tail(p, &input->vframe_block_free_list);
}
/* release input blocks */
list_for_each_safe(p, tmp, &input->vframe_block_free_list) {
struct vframe_block_list_s *block = list_entry(
p, struct vframe_block_list_s, list);
vdec_input_del_block_locked(input, block);
vframe_block_free_block(block);
}
/* release swap pages */
if (vdec_secure(input->vdec)) {
if (input->swap_page_phys)
codec_mm_free_for_dma("SWAP", input->swap_page_phys);
} else {
if (input->swap_page)
codec_mm_dma_free_coherent(input->mem_handle);
}
input->swap_page = NULL;
input->swap_page_phys = 0;
input->swap_valid = false;
}
EXPORT_SYMBOL(vdec_input_release);
u32 vdec_input_get_freed_handle(struct vdec_s *vdec)
{
struct vframe_block_list_s *block;
struct vdec_input_s *input = &vdec->input;
unsigned long flags;
u32 handle = 0;
if (!vdec)
return 0;
if (!vdec_secure(vdec))
return 0;
flags = vdec_input_lock(input);
do {
block = list_first_entry_or_null(&input->vframe_block_free_list,
struct vframe_block_list_s, list);
if (!block) {
break;
}
handle = block->handle;
vdec_input_del_block_locked(input, block);
if (block->free)
block->free(block->priv, handle);
kfree(block);
} while(!handle);
vdec_input_unlock(input, flags);
return handle;
}
EXPORT_SYMBOL(vdec_input_get_freed_handle);