/*
* Copyright (c) 2016 MediaTek Inc.
* Author: PC Chen <pc.chen@mediatek.com>
* Tiffany Lin <tiffany.lin@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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 <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
#include "mtk_vcodec_drv.h"
#include "mtk_vcodec_dec.h"
#include "mtk_vcodec_intr.h"
#include "mtk_vcodec_util.h"
#include "vdec_drv_if.h"
#include "mtk_vcodec_dec_pm.h"
#define OUT_FMT_IDX 0
#define CAP_FMT_IDX 3
#define MTK_VDEC_MIN_W 64U
#define MTK_VDEC_MIN_H 64U
#define DFT_CFG_WIDTH MTK_VDEC_MIN_W
#define DFT_CFG_HEIGHT MTK_VDEC_MIN_H
static struct mtk_video_fmt mtk_video_formats[] = {
{
.fourcc = V4L2_PIX_FMT_H264,
.type = MTK_FMT_DEC,
.num_planes = 1,
},
{
.fourcc = V4L2_PIX_FMT_VP8,
.type = MTK_FMT_DEC,
.num_planes = 1,
},
{
.fourcc = V4L2_PIX_FMT_VP9,
.type = MTK_FMT_DEC,
.num_planes = 1,
},
{
.fourcc = V4L2_PIX_FMT_MT21C,
.type = MTK_FMT_FRAME,
.num_planes = 2,
},
};
static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = {
{
.fourcc = V4L2_PIX_FMT_H264,
.stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16,
MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 },
},
{
.fourcc = V4L2_PIX_FMT_VP8,
.stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16,
MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 },
},
{
.fourcc = V4L2_PIX_FMT_VP9,
.stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16,
MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 },
},
};
#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes)
#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats)
static struct mtk_video_fmt *mtk_vdec_find_format(struct v4l2_format *f)
{
struct mtk_video_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
fmt = &mtk_video_formats[k];
if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
return fmt;
}
return NULL;
}
static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_ctx *ctx,
enum v4l2_buf_type type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
return &ctx->q_data[MTK_Q_DATA_SRC];
return &ctx->q_data[MTK_Q_DATA_DST];
}
/*
* This function tries to clean all display buffers, the buffers will return
* in display order.
* Note the buffers returned from codec driver may still be in driver's
* reference list.
*/
static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx)
{
struct vdec_fb *disp_frame_buffer = NULL;
struct mtk_video_dec_buf *dstbuf;
mtk_v4l2_debug(3, "[%d]", ctx->id);
if (vdec_if_get_param(ctx,
GET_PARAM_DISP_FRAME_BUFFER,
&disp_frame_buffer)) {
mtk_v4l2_err("[%d]Cannot get param : GET_PARAM_DISP_FRAME_BUFFER",
ctx->id);
return NULL;
}
if (disp_frame_buffer == NULL) {
mtk_v4l2_debug(3, "No display frame buffer");
return NULL;
}
dstbuf = container_of(disp_frame_buffer, struct mtk_video_dec_buf,
frame_buffer);
mutex_lock(&ctx->lock);
if (dstbuf->used) {
vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 0,
ctx->picinfo.y_bs_sz);
vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1,
ctx->picinfo.c_bs_sz);
dstbuf->ready_to_display = true;
mtk_v4l2_debug(2,
"[%d]status=%x queue id=%d to done_list %d",
ctx->id, disp_frame_buffer->status,
dstbuf->vb.vb2_buf.index,
dstbuf->queued_in_vb2);
v4l2_m2m_buf_done(&dstbuf->vb, VB2_BUF_STATE_DONE);
ctx->decoded_frame_cnt++;
}
mutex_unlock(&ctx->lock);
return &dstbuf->vb.vb2_buf;
}
/*
* This function tries to clean all capture buffers that are not used as
* reference buffers by codec driver any more
* In this case, we need re-queue buffer to vb2 buffer if user space
* already returns this buffer to v4l2 or this buffer is just the output of
* previous sps/pps/resolution change decode, or do nothing if user
* space still owns this buffer
*/
static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx)
{
struct mtk_video_dec_buf *dstbuf;
struct vdec_fb *free_frame_buffer = NULL;
if (vdec_if_get_param(ctx,
GET_PARAM_FREE_FRAME_BUFFER,
&free_frame_buffer)) {
mtk_v4l2_err("[%d] Error!! Cannot get param", ctx->id);
return NULL;
}
if (free_frame_buffer == NULL) {
mtk_v4l2_debug(3, " No free frame buffer");
return NULL;
}
mtk_v4l2_debug(3, "[%d] tmp_frame_addr = 0x%p",
ctx->id, free_frame_buffer);
dstbuf = container_of(free_frame_buffer, struct mtk_video_dec_buf,
frame_buffer);
mutex_lock(&ctx->lock);
if (dstbuf->used) {
if ((dstbuf->queued_in_vb2) &&
(dstbuf->queued_in_v4l2) &&
(free_frame_buffer->status == FB_ST_FREE)) {
/*
* After decode sps/pps or non-display buffer, we don't
* need to return capture buffer to user space, but
* just re-queue this capture buffer to vb2 queue.
* This reduce overheads that dq/q unused capture
* buffer. In this case, queued_in_vb2 = true.
*/
mtk_v4l2_debug(2,
"[%d]status=%x queue id=%d to rdy_queue %d",
ctx->id, free_frame_buffer->status,
dstbuf->vb.vb2_buf.index,
dstbuf->queued_in_vb2);
v4l2_m2m_buf_queue(ctx->m2m_ctx, &dstbuf->vb);
} else if ((dstbuf->queued_in_vb2 == false) &&
(dstbuf->queued_in_v4l2 == true)) {
/*
* If buffer in v4l2 driver but not in vb2 queue yet,
* and we get this buffer from free_list, it means
* that codec driver do not use this buffer as
* reference buffer anymore. We should q buffer to vb2
* queue, so later work thread could get this buffer
* for decode. In this case, queued_in_vb2 = false
* means this buffer is not from previous decode
* output.
*/
mtk_v4l2_debug(2,
"[%d]status=%x queue id=%d to rdy_queue",
ctx->id, free_frame_buffer->status,
dstbuf->vb.vb2_buf.index);
v4l2_m2m_buf_queue(ctx->m2m_ctx, &dstbuf->vb);
dstbuf->queued_in_vb2 = true;
} else {
/*
* Codec driver do not need to reference this capture
* buffer and this buffer is not in v4l2 driver.
* Then we don't need to do any thing, just add log when
* we need to debug buffer flow.
* When this buffer q from user space, it could
* directly q to vb2 buffer
*/
mtk_v4l2_debug(3, "[%d]status=%x err queue id=%d %d %d",
ctx->id, free_frame_buffer->status,
dstbuf->vb.vb2_buf.index,
dstbuf->queued_in_vb2,
dstbuf->queued_in_v4l2);
}
dstbuf->used = false;
}
mutex_unlock(&ctx->lock);
return &dstbuf->vb.vb2_buf;
}
static void clean_display_buffer(struct mtk_vcodec_ctx *ctx)
{
struct vb2_buffer *framptr;
do {
framptr = get_display_buffer(ctx);
} while (framptr);
}
static void clean_free_buffer(struct mtk_vcodec_ctx *ctx)
{
struct vb2_buffer *framptr;
do {
framptr = get_free_buffer(ctx);
} while (framptr);
}
static void mtk_vdec_queue_res_chg_event(struct mtk_vcodec_ctx *ctx)
{
static const struct v4l2_event ev
|