blob: 08cd0d77527c360055919eb90c45859b893d0c86 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/amlogic/media/ge2d/ge2d.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include "vdec.h"
#include "vdec_ge2d_utils.h"
#include "../../../common/chips/decoder_cpu_ver_info.h"
static u32 vdec_ge2d_debug = 0;
#ifndef CONFIG_AMLOGIC_MEDIA_GE2D
inline struct ge2d_context_s *create_ge2d_work_queue(void) { return NULL; }
inline int destroy_ge2d_work_queue(struct ge2d_context_s *ge2d_work_queue) { return -1; }
#endif
enum videocom_source_type {
DECODER_8BIT_NORMAL = 0,
DECODER_8BIT_BOTTOM,
DECODER_8BIT_TOP,
DECODER_10BIT_NORMAL,
DECODER_10BIT_BOTTOM,
DECODER_10BIT_TOP
};
static void vdec_canvas_cache_free(struct vdec_canvas_cache *canche)
{
int i = -1;
for (i = 0; i < ARRAY_SIZE(canche->res); i++) {
if (canche->res[i].cid > 0) {
if (vdec_ge2d_debug)
pr_info("canvas-free, name:%s, canvas id:%d\n",
canche->res[i].name,
canche->res[i].cid);
canvas_pool_map_free_canvas(canche->res[i].cid);
canche->res[i].cid = 0;
}
}
}
static void vdec_canvas_cache_put(struct vdec_ge2d *ge2d)
{
struct vdec_canvas_cache *canche = &ge2d->canche;
mutex_lock(&canche->lock);
pr_info("canvas-put, ref:%d\n", canche->ref);
canche->ref--;
if (canche->ref == 0) {
vdec_canvas_cache_free(canche);
}
mutex_unlock(&canche->lock);
}
static int vdec_canvas_cache_get(struct vdec_ge2d *ge2d, char *usr)
{
struct vdec_canvas_cache *canche = &ge2d->canche;
int i;
mutex_lock(&canche->lock);
canche->ref++;
for (i = 0; i < ARRAY_SIZE(canche->res); i++) {
if (canche->res[i].cid <= 0) {
snprintf(canche->res[i].name, 32, "%s-%d", usr, i);
canche->res[i].cid =
canvas_pool_map_alloc_canvas(canche->res[i].name);
}
if (vdec_ge2d_debug)
pr_info("canvas-alloc, name:%s, canvas id:%d\n",
canche->res[i].name,
canche->res[i].cid);
if (canche->res[i].cid <= 0) {
pr_err("canvas-fail, name:%s, canvas id:%d.\n",
canche->res[i].name,
canche->res[i].cid);
mutex_unlock(&canche->lock);
goto err;
}
}
if (vdec_ge2d_debug)
pr_info("canvas-get, ref:%d\n", canche->ref);
mutex_unlock(&canche->lock);
return 0;
err:
vdec_canvas_cache_put(ge2d);
return -1;
}
static int vdec_canvas_cache_init(struct vdec_ge2d *ge2d)
{
ge2d->canche.ref = 0;
mutex_init(&ge2d->canche.lock);
pr_info("canvas-init, ref:%d\n", ge2d->canche.ref);
return 0;
}
static int get_source_type(struct vframe_s *vf)
{
enum videocom_source_type ret;
int interlace_mode;
interlace_mode = vf->type & VIDTYPE_TYPEMASK;
if ((vf->bitdepth & BITDEPTH_Y10) &&
(!(vf->type & VIDTYPE_COMPRESS)) &&
(get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)) {
if (interlace_mode == VIDTYPE_INTERLACE_TOP)
ret = DECODER_10BIT_TOP;
else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
ret = DECODER_10BIT_BOTTOM;
else
ret = DECODER_10BIT_NORMAL;
} else {
if (interlace_mode == VIDTYPE_INTERLACE_TOP)
ret = DECODER_8BIT_TOP;
else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
ret = DECODER_8BIT_BOTTOM;
else
ret = DECODER_8BIT_NORMAL;
}
return ret;
}
static int get_input_format(struct vframe_s *vf)
{
int format = GE2D_FORMAT_M24_YUV420;
enum videocom_source_type soure_type;
soure_type = get_source_type(vf);
switch (soure_type) {
case DECODER_8BIT_NORMAL:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422;
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21;
else if (vf->type & VIDTYPE_VIU_NV12)
format = GE2D_FORMAT_M24_NV12;
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444;
else
format = GE2D_FORMAT_M24_YUV420;
break;
case DECODER_8BIT_BOTTOM:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422
| (GE2D_FORMAT_S16_YUV422B & (3 << 3));
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21
| (GE2D_FORMAT_M24_NV21B & (3 << 3));
else if (vf->type & VIDTYPE_VIU_NV12)
format = GE2D_FORMAT_M24_NV12
| (GE2D_FORMAT_M24_NV12B & (3 << 3));
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444
| (GE2D_FORMAT_S24_YUV444B & (3 << 3));
else
format = GE2D_FORMAT_M24_YUV420
| (GE2D_FMT_M24_YUV420B & (3 << 3));
break;
case DECODER_8BIT_TOP:
if (vf->type & VIDTYPE_VIU_422)
format = GE2D_FORMAT_S16_YUV422
| (GE2D_FORMAT_S16_YUV422T & (3 << 3));
else if (vf->type & VIDTYPE_VIU_NV21)
format = GE2D_FORMAT_M24_NV21
| (GE2D_FORMAT_M24_NV21T & (3 << 3));
else if (vf->type & VIDTYPE_VIU_NV12)
format = GE2D_FORMAT_M24_NV12
| (GE2D_FORMAT_M24_NV12T & (3 << 3));
else if (vf->type & VIDTYPE_VIU_444)
format = GE2D_FORMAT_S24_YUV444
| (GE2D_FORMAT_S24_YUV444T & (3 << 3));
else
format = GE2D_FORMAT_M24_YUV420
| (GE2D_FMT_M24_YUV420T & (3 << 3));
break;
case DECODER_10BIT_NORMAL:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422;
else
format = GE2D_FORMAT_S16_12BIT_YUV422;
}
break;
case DECODER_10BIT_BOTTOM:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422
| (GE2D_FORMAT_S16_10BIT_YUV422B
& (3 << 3));
else
format = GE2D_FORMAT_S16_12BIT_YUV422
| (GE2D_FORMAT_S16_12BIT_YUV422B
& (3 << 3));
}
break;
case DECODER_10BIT_TOP:
if (vf->type & VIDTYPE_VIU_422) {
if (vf->bitdepth & FULL_PACK_422_MODE)
format = GE2D_FORMAT_S16_10BIT_YUV422
| (GE2D_FORMAT_S16_10BIT_YUV422T
& (3 << 3));
else
format = GE2D_FORMAT_S16_12BIT_YUV422
| (GE2D_FORMAT_S16_12BIT_YUV422T
& (3 << 3));
}
break;
default:
format = GE2D_FORMAT_M24_YUV420;
}
return format;
}
int vdec_ge2d_init(struct vdec_ge2d** ge2d_handle, int mode)
{
int ret;
struct vdec_ge2d *ge2d;
if (!ge2d_handle)
return -EINVAL;
ge2d = kzalloc(sizeof(*ge2d), GFP_KERNEL);
if (!ge2d)
return -ENOMEM;
vdec_canvas_cache_init(ge2d);
ge2d->work_mode = mode;
if (!ge2d->work_mode) {
ge2d->work_mode = GE2D_MODE_CONVERT_LE;
}
pr_info("vdec_ge2d_init work_mode:%d\n", ge2d->work_mode);
ge2d->ge2d_context = create_ge2d_work_queue();
if (!ge2d->ge2d_context) {
pr_err("ge2d_create_instance fail\n");
ret = -EINVAL;
goto error;
}
if (vdec_canvas_cache_get(ge2d, "vdec-ge2d-dec") < 0) {
pr_err("canvas pool alloc fail. src(%d, %d, %d) dst(%d, %d, %d).\n",
ge2d->canche.res[0].cid,
ge2d->canche.res[1].cid,
ge2d->canche.res[2].cid,
ge2d->canche.res[3].cid,
ge2d->canche.res[4].cid,
ge2d->canche.res[5].cid);
ret = -ENOMEM;
goto error1;
}
*ge2d_handle = ge2d;
return 0;
error1:
destroy_ge2d_work_queue(ge2d->ge2d_context);
error:
kfree(ge2d);
return ret;
}
EXPORT_SYMBOL(vdec_ge2d_init);
int vdec_ge2d_copy_data(struct vdec_ge2d *ge2d, struct vdec_ge2d_info *ge2d_info)
{
struct config_para_ex_s ge2d_config;
u32 src_fmt = 0, dst_fmt = 0;
struct canvas_s cd;
if (!ge2d)
return -1;
if (vdec_ge2d_debug)
pr_info("vdec_ge2d_copy_data start\n");
memset(&ge2d_config, 0, sizeof(ge2d_config));
src_fmt = get_input_format(ge2d_info->dst_vf);
if (ge2d_info->src_canvas0_config[0].endian == 7)
src_fmt |= GE2D_BIG_ENDIAN;
else
src_fmt |= GE2D_LITTLE_ENDIAN;
/* negotiate format of destination */
dst_fmt = get_input_format(ge2d_info->dst_vf);
if (ge2d->work_mode & GE2D_MODE_CONVERT_NV12)
dst_fmt |= GE2D_FORMAT_M24_NV12;
else if (ge2d->work_mode & GE2D_MODE_CONVERT_NV21)
dst_fmt |= GE2D_FORMAT_M24_NV21;
if (ge2d->work_mode & GE2D_MODE_CONVERT_LE)
dst_fmt |= GE2D_LITTLE_ENDIAN;
else
dst_fmt |= GE2D_BIG_ENDIAN;
if ((dst_fmt & GE2D_COLOR_MAP_MASK) == GE2D_COLOR_MAP_NV12) {
ge2d_info->dst_vf->type |= VIDTYPE_VIU_NV12;
ge2d_info->dst_vf->type &= ~VIDTYPE_VIU_NV21;
} else if ((dst_fmt & GE2D_COLOR_MAP_MASK) == GE2D_COLOR_MAP_NV21) {
ge2d_info->dst_vf->type |= VIDTYPE_VIU_NV21;
ge2d_info->dst_vf->type &= ~VIDTYPE_VIU_NV12;
}
if ((dst_fmt & GE2D_ENDIAN_MASK) == GE2D_LITTLE_ENDIAN) {
ge2d_info->dst_vf->canvas0_config[0].endian = 0;
ge2d_info->dst_vf->canvas0_config[1].endian = 0;
ge2d_info->dst_vf->canvas0_config[2].endian = 0;
} else if ((dst_fmt & GE2D_ENDIAN_MASK) == GE2D_BIG_ENDIAN){
ge2d_info->dst_vf->canvas0_config[0].endian = 7;
ge2d_info->dst_vf->canvas0_config[1].endian = 7;
ge2d_info->dst_vf->canvas0_config[2].endian = 7;
}
ge2d_info->dst_vf->mem_sec = ge2d_info->dst_vf->flag & VFRAME_FLAG_VIDEO_SECURE ? 1 : 0;
mutex_lock(&ge2d->canche.lock);
/* src canvas configure. */
if ((ge2d_info->dst_vf->canvas0Addr == 0) ||
(ge2d_info->dst_vf->canvas0Addr == (u32)-1)) {
canvas_config_config(ge2d->canche.res[0].cid, &ge2d_info->src_canvas0_config[0]);
canvas_config_config(ge2d->canche.res[1].cid, &ge2d_info->src_canvas0_config[1]);
canvas_config_config(ge2d->canche.res[2].cid, &ge2d_info->src_canvas0_config[2]);
ge2d_config.src_para.canvas_index =
ge2d->canche.res[0].cid |
ge2d->canche.res[1].cid << 8 |
ge2d->canche.res[2].cid << 16;
ge2d_config.src_planes[0].addr =
ge2d_info->src_canvas0_config[0].phy_addr;
ge2d_config.src_planes[0].w =
ge2d_info->src_canvas0_config[0].width;
ge2d_config.src_planes[0].h =
ge2d_info->src_canvas0_config[0].height;
ge2d_config.src_planes[1].addr =
ge2d_info->src_canvas0_config[1].phy_addr;
ge2d_config.src_planes[1].w =
ge2d_info->src_canvas0_config[1].width;
ge2d_config.src_planes[1].h =
ge2d_info->src_canvas0_config[1].height;
ge2d_config.src_planes[2].addr =
ge2d_info->src_canvas0_config[2].phy_addr;
ge2d_config.src_planes[2].w =
ge2d_info->src_canvas0_config[2].width;
ge2d_config.src_planes[2].h =
ge2d_info->src_canvas0_config[2].height;
} else {
ge2d_config.src_para.canvas_index = ge2d_info->src_canvas0Addr;
}
ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config.src_para.format = src_fmt;
ge2d_config.src_para.fill_color_en = 0;
ge2d_config.src_para.fill_mode = 0;
ge2d_config.src_para.x_rev = 0;
ge2d_config.src_para.y_rev = 0;
ge2d_config.src_para.color = 0xffffffff;
ge2d_config.src_para.top = 0;
ge2d_config.src_para.left = 0;
ge2d_config.src_para.width = ge2d_info->dst_vf->width;
if (ge2d_info->dst_vf->type & VIDTYPE_INTERLACE)
ge2d_config.src_para.height = ge2d_info->dst_vf->height >> 1;
else
ge2d_config.src_para.height = ge2d_info->dst_vf->height;
/* dst canvas configure. */
canvas_config_config(ge2d->canche.res[3].cid, &ge2d_info->dst_vf->canvas0_config[0]);
if ((ge2d_config.src_para.format & 0xfffff) == GE2D_FORMAT_M24_YUV420) {
ge2d_info->dst_vf->canvas0_config[1].width <<= 1;
}
canvas_config_config(ge2d->canche.res[4].cid, &ge2d_info->dst_vf->canvas0_config[1]);
canvas_config_config(ge2d->canche.res[5].cid, &ge2d_info->dst_vf->canvas0_config[2]);
ge2d_config.dst_para.canvas_index =
ge2d->canche.res[3].cid |
ge2d->canche.res[4].cid << 8;
canvas_read(ge2d->canche.res[3].cid, &cd);
ge2d_config.dst_planes[0].addr = cd.addr;
ge2d_config.dst_planes[0].w = cd.width;
ge2d_config.dst_planes[0].h = cd.height;
canvas_read(ge2d->canche.res[4].cid, &cd);
ge2d_config.dst_planes[1].addr = cd.addr;
ge2d_config.dst_planes[1].w = cd.width;
ge2d_config.dst_planes[1].h = cd.height;
ge2d_config.dst_para.format = dst_fmt;
ge2d_config.dst_para.width = ge2d_info->dst_vf->width;
ge2d_config.dst_para.height = ge2d_info->dst_vf->height;
ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config.dst_para.fill_color_en = 0;
ge2d_config.dst_para.fill_mode = 0;
ge2d_config.dst_para.x_rev = 0;
ge2d_config.dst_para.y_rev = 0;
ge2d_config.dst_para.color = 0;
ge2d_config.dst_para.top = 0;
ge2d_config.dst_para.left = 0;
/* other ge2d parameters configure. */
ge2d_config.src_key.key_enable = 0;
ge2d_config.src_key.key_mask = 0;
ge2d_config.src_key.key_mode = 0;
ge2d_config.alu_const_color = 0;
ge2d_config.bitmask_en = 0;
ge2d_config.src1_gb_alpha = 0;
ge2d_config.dst_xy_swap = 0;
ge2d_config.src2_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config.mem_sec = ge2d_info->dst_vf->flag & VFRAME_FLAG_VIDEO_SECURE ? 1 : 0;
if (ge2d_context_config_ex(ge2d->ge2d_context, &ge2d_config) < 0) {
pr_err("vdec_ge2d_context_config_ex error.\n");
mutex_unlock(&ge2d->canche.lock);
return -1;
}
if (!(ge2d_info->dst_vf->type & VIDTYPE_V4L_EOS)) {
if (ge2d_info->dst_vf->type & VIDTYPE_INTERLACE) {
stretchblt_noalpha(ge2d->ge2d_context,
0, 0, ge2d_info->dst_vf->width, ge2d_info->dst_vf->height / 2,
0, 0, ge2d_info->dst_vf->width, ge2d_info->dst_vf->height);
} else {
stretchblt_noalpha(ge2d->ge2d_context,
0, 0, ge2d_info->dst_vf->width, ge2d_info->dst_vf->height,
0, 0, ge2d_info->dst_vf->width, ge2d_info->dst_vf->height);
}
}
mutex_unlock(&ge2d->canche.lock);
if (vdec_ge2d_debug)
pr_info("vdec_ge2d_copy_data done\n");
return 0;
}
EXPORT_SYMBOL(vdec_ge2d_copy_data);
int vdec_ge2d_destroy(struct vdec_ge2d *ge2d)
{
if (!ge2d)
return -1;
pr_info("vdec ge2d destroy begin\n");
destroy_ge2d_work_queue(ge2d->ge2d_context);
vdec_canvas_cache_put(ge2d);
kfree(ge2d);
pr_info("vdec ge2d destroy done\n");
return 0;
}
EXPORT_SYMBOL(vdec_ge2d_destroy);
module_param(vdec_ge2d_debug, int, 0664);
MODULE_PARM_DESC(vdec_ge2d_debug, "\n vdec_ge2d_debug\n");