diff --git a/drivers/amlogic/media_modules/Makefile b/drivers/amlogic/media_modules/Makefile index f2a514848876..018c8feff897 100644 --- a/drivers/amlogic/media_modules/Makefile +++ b/drivers/amlogic/media_modules/Makefile @@ -2,3 +2,4 @@ obj-y += common/ obj-y += frame_provider/ obj-y += frame_sink/ obj-y += stream_input/ +obj-y += amvdec_ports/ diff --git a/drivers/amlogic/media_modules/amvdec_ports/Makefile b/drivers/amlogic/media_modules/amvdec_ports/Makefile new file mode 100644 index 000000000000..5ce0a7fcc114 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/Makefile @@ -0,0 +1,11 @@ +obj-m += amvdec_ports.o +amvdec_ports-objs += aml_vcodec_dec_drv.o +amvdec_ports-objs += aml_vcodec_dec.o +amvdec_ports-objs += aml_vcodec_dec_pm.o +amvdec_ports-objs += aml_vcodec_util.o +amvdec_ports-objs += aml_vcodec_adapt.o +amvdec_ports-objs += aml_vcodec_vfm.o +amvdec_ports-objs += vdec_drv_if.o +amvdec_ports-objs += decoder/vdec_h264_if.o +amvdec_ports-objs += decoder/h264_parse.o +amvdec_ports-objs += decoder/h264_stream.o diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_adapt.c b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_adapt.c new file mode 100644 index 000000000000..1c80c1127c87 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_adapt.c @@ -0,0 +1,719 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../stream_input/amports/adec.h" +#include "../stream_input/parser/streambuf.h" +#include "../stream_input/parser/streambuf_reg.h" +#include "../stream_input/parser/tsdemux.h" +#include "../stream_input/parser/psparser.h" +#include "../stream_input/parser/esparser.h" +#include "../frame_provider/decoder/utils/vdec.h" +#include "../common/media_clock/switch/amports_gate.h" +#include +#include "aml_vcodec_adapt.h" +#include + +#define DEFAULT_VIDEO_BUFFER_SIZE (1024 * 1024 * 3) +#define DEFAULT_VIDEO_BUFFER_SIZE_4K (1024 * 1024 * 6) +#define DEFAULT_VIDEO_BUFFER_SIZE_TVP (1024 * 1024 * 10) +#define DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP (1024 * 1024 * 15) +#define DEFAULT_AUDIO_BUFFER_SIZE (1024*768*2) +#define DEFAULT_SUBTITLE_BUFFER_SIZE (1024*256) + +#define PTS_OUTSIDE (1) +#define SYNC_OUTSIDE (2) +#define USE_V4L_PORTS (0x80) + +//#define DATA_DEBUG + +static int def_4k_vstreambuf_sizeM = + (DEFAULT_VIDEO_BUFFER_SIZE_4K >> 20); +static int def_vstreambuf_sizeM = + (DEFAULT_VIDEO_BUFFER_SIZE >> 20); + +static int slow_input = 0; + +static int use_bufferlevelx10000 = 10000; +static unsigned int amstream_buf_num = BUF_MAX_NUM; + +static struct stream_buf_s bufs[BUF_MAX_NUM] = { + { + .reg_base = VLD_MEM_VIFIFO_REG_BASE, + .type = BUF_TYPE_VIDEO, + .buf_start = 0, + .buf_size = DEFAULT_VIDEO_BUFFER_SIZE, + .default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = AIU_MEM_AIFIFO_REG_BASE, + .type = BUF_TYPE_AUDIO, + .buf_start = 0, + .buf_size = DEFAULT_AUDIO_BUFFER_SIZE, + .default_buf_size = DEFAULT_AUDIO_BUFFER_SIZE, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = 0, + .type = BUF_TYPE_SUBTITLE, + .buf_start = 0, + .buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE, + .default_buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = 0, + .type = BUF_TYPE_USERDATA, + .buf_start = 0, + .buf_size = 0, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = HEVC_STREAM_REG_BASE, + .type = BUF_TYPE_HEVC, + .buf_start = 0, + .buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K, + .default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K, + .first_tstamp = INVALID_PTS + }, +}; + +struct vdec_adapt_config { + struct vdec_s *vdec; + struct stream_port_s port; + struct dec_sysinfo dec_prop; +}; + +struct vdec_adapt_config g_cfg; + +static void set_cfg_info(struct aml_vdec_adapt *vdec) +{ + unsigned long sync_mode = (PTS_OUTSIDE | SYNC_OUTSIDE | USE_V4L_PORTS); + + g_cfg.port.type |= (PORT_TYPE_VIDEO | PORT_TYPE_ES); + g_cfg.port.flag |= PORT_FLAG_VFORMAT; + g_cfg.dec_prop.format = VFORMAT_H264; + g_cfg.dec_prop.width = 1920; + g_cfg.dec_prop.height = 1080; + g_cfg.dec_prop.rate = 3200; + g_cfg.dec_prop.param = (void *)sync_mode; +} + +static void set_system_info(struct vdec_s *vdec) +{ + vdec->sys_info = &g_cfg.dec_prop; + vdec->sys_info_store = g_cfg.dec_prop; +} + +static void set_vdec_mode(struct vdec_s *vdec) +{ + //vdec->type = VDEC_TYPE_SINGLE; + vdec->type = VDEC_TYPE_FRAME_BLOCK; +} + +static void set_vdec_vfm(struct vdec_s *vdec) +{ + vdec->frame_base_video_path = FRAME_BASE_PATH_V4L_VIDEO; +} + +static int enable_hardware(void) +{ + struct stream_port_s *port = &g_cfg.port; + + if (get_cpu_type() < MESON_CPU_MAJOR_ID_M6) + return -1; + + amports_switch_gate("demux", 1); + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + amports_switch_gate("parser_top", 1); + + if (port->type & PORT_TYPE_VIDEO) { + amports_switch_gate("vdec", 1); + + if (has_hevc_vdec()) { + if (port->type & (PORT_TYPE_MPTS | PORT_TYPE_HEVC)) { + amports_switch_gate("clk_hevc_mux", 1); + vdec_poweron(VDEC_HEVC); + } + + if ((port->type & PORT_TYPE_HEVC) == 0) { + amports_switch_gate("clk_vdec_mux", 1); + vdec_poweron(VDEC_1); + } + } else { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + amports_switch_gate("clk_vdec_mux", 1); + vdec_poweron(VDEC_1); + } + } + } + + return 0; +} + +static int disable_hardware(void) +{ + struct stream_port_s *port = &g_cfg.port; + + if (get_cpu_type() < MESON_CPU_MAJOR_ID_M6) + return -1; + + if ((port->type & PORT_TYPE_VIDEO) + && get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { +#ifndef CONFIG_AMLOGIC_MEDIA_MULTI_DEC + if (has_hevc_vdec()) + vdec_poweroff(VDEC_HEVC); + + vdec_poweroff(VDEC_1); +#else + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXLX + && port->vformat == VFORMAT_H264 + && bufs[BUF_TYPE_VIDEO].for_4k) + vdec_poweroff(VDEC_HEVC); + + if ((port->vformat == VFORMAT_HEVC + || port->vformat == VFORMAT_VP9)) + vdec_poweroff(VDEC_HEVC); + else + vdec_poweroff(VDEC_1); +#endif + amports_switch_gate("vdec", 0); + } + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + amports_switch_gate("parser_top", 0); + + amports_switch_gate("demux", 0); + + return 0; +} + +static int reset_canuse_buferlevel(int levelx10000) +{ + int i; + struct stream_buf_s *p = NULL; + + if (levelx10000 >= 0 && levelx10000 <= 10000) + use_bufferlevelx10000 = levelx10000; + else + use_bufferlevelx10000 = 10000; + for (i = 0; i < amstream_buf_num; i++) { + p = &bufs[i]; + p->canusebuf_size = ((p->buf_size / 1024) * + use_bufferlevelx10000 / 10000) * 1024; + p->canusebuf_size += 1023; + p->canusebuf_size &= ~1023; + + if (p->canusebuf_size > p->buf_size) + p->canusebuf_size = p->buf_size; + } + + return 0; +} + +static void change_vbufsize(struct vdec_s *vdec, + struct stream_buf_s *pvbuf) +{ + if (pvbuf->buf_start != 0) { + pr_info("streambuf is alloced before\n"); + return; + } + + if (pvbuf->for_4k) { + pvbuf->buf_size = def_4k_vstreambuf_sizeM * SZ_1M; + + if (vdec->port_flag & PORT_FLAG_DRM) + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP; + + if ((pvbuf->buf_size > 30 * SZ_1M) + && (codec_mm_get_total_size() < 220 * SZ_1M)) { + /*if less than 250M, used 20M for 4K & 265*/ + pvbuf->buf_size = pvbuf->buf_size >> 1; + } + } else if (pvbuf->buf_size > def_vstreambuf_sizeM * SZ_1M) { + if (vdec->port_flag & PORT_FLAG_DRM) + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_TVP; + } else { + pvbuf->buf_size = def_vstreambuf_sizeM * SZ_1M; + if (vdec->port_flag & PORT_FLAG_DRM) + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_TVP; + } + + reset_canuse_buferlevel(10000); +} + +static void user_buffer_init(void) +{ + struct stream_buf_s *pubuf = &bufs[BUF_TYPE_USERDATA]; + + pubuf->buf_size = 0; + pubuf->buf_start = 0; + pubuf->buf_wp = 0; + pubuf->buf_rp = 0; +} + +static void audio_component_release(struct stream_port_s *port, + struct stream_buf_s *pbuf, int release_num) +{ + switch (release_num) { + default: + case 0: + case 4: + esparser_release(pbuf); + case 3: + adec_release(port->vformat); + case 2: + stbuf_release(pbuf, false); + case 1: + ; + } +} + +static int audio_component_init(struct stream_port_s *port, + struct stream_buf_s *pbuf) +{ + int r; + + if ((port->flag & PORT_FLAG_AFORMAT) == 0) { + pr_err("aformat not set\n"); + return 0; + } + + r = stbuf_init(pbuf, NULL, false); + if (r < 0) + return r; + + r = adec_init(port); + if (r < 0) { + audio_component_release(port, pbuf, 2); + return r; + } + + if (port->type & PORT_TYPE_ES) { + r = esparser_init(pbuf, NULL); + if (r < 0) { + audio_component_release(port, pbuf, 3); + return r; + } + } + + pbuf->flag |= BUF_FLAG_IN_USE; + + return 0; +} + +static void video_component_release(struct stream_port_s *port, +struct stream_buf_s *pbuf, int release_num) +{ + struct vdec_adapt_config *cfg + = container_of(port, struct vdec_adapt_config, port); + struct vdec_s *vdec = cfg->vdec; + + struct vdec_s *slave = NULL; + bool is_multidec = !vdec_single(vdec); + + switch (release_num) { + default: + case 0: + case 4: { + if ((port->type & PORT_TYPE_FRAME) == 0) + esparser_release(pbuf); + } + + case 3: { + if (vdec->slave) + slave = vdec->slave; + vdec_release(vdec); + + if (slave) + vdec_release(slave); + vdec = NULL; + } + + case 2: { + if ((port->type & PORT_TYPE_FRAME) == 0) + stbuf_release(pbuf, is_multidec); + } + + case 1: + ; + } +} + +static int video_component_init(struct stream_port_s *port, + struct stream_buf_s *pbuf) +{ + int ret = -1; + struct vdec_adapt_config *cfg + = container_of(port, struct vdec_adapt_config, port); + struct vdec_s *vdec = cfg->vdec; + + if ((vdec->port_flag & PORT_FLAG_VFORMAT) == 0) { + pr_err("vformat not set\n"); + return -EPERM; + } + + if ((vdec->sys_info->height * vdec->sys_info->width) > 1920 * 1088 + || port->vformat == VFORMAT_H264_4K2K) { + pbuf->for_4k = 1; + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXLX + && port->vformat == VFORMAT_H264) + vdec_poweron(VDEC_HEVC); + } else + pbuf->for_4k = 0; + + if (port->type & PORT_TYPE_FRAME) { + ret = vdec_init(vdec, pbuf->for_4k); + if (ret < 0) { + pr_err("video_component_init %d, failed\n", __LINE__); + video_component_release(port, pbuf, 2); + return ret; + } + + return 0; + } + + change_vbufsize(vdec, pbuf); + + if (has_hevc_vdec()) { + if (port->type & PORT_TYPE_MPTS) { + if (pbuf->type == BUF_TYPE_HEVC) + vdec_poweroff(VDEC_1); + else + vdec_poweroff(VDEC_HEVC); + } + } + + ret = stbuf_init(pbuf, vdec, false); + if (ret < 0) { + pr_err("video_component_init %d, stbuf_init failed\n", __LINE__); + return ret; + } + + /* todo: set path based on port flag */ + ret = vdec_init(vdec, pbuf->for_4k); + if (ret < 0) { + pr_err("video_component_init %d, vdec_init failed\n", __LINE__); + video_component_release(port, pbuf, 2); + return ret; + } + + if (vdec_dual(vdec)) { + ret = vdec_init(vdec->slave, pbuf->for_4k); + if (ret < 0) { + pr_err("video_component_init %d, vdec_init failed\n", + __LINE__); + video_component_release(port, pbuf, 2); + return ret; + } + } + + if (port->type & PORT_TYPE_ES) { + ret = esparser_init(pbuf, vdec); + if (ret < 0) { + video_component_release(port, pbuf, 3); + pr_err("esparser_init() failed\n"); + return ret; + } + } + + pbuf->flag |= BUF_FLAG_IN_USE; + + vdec_connect(vdec); + + return 0; +} + +static int vdec_ports_release(struct stream_port_s *port) +{ + struct vdec_adapt_config *cfg + = container_of(port, struct vdec_adapt_config, port); + struct vdec_s *vdec = cfg->vdec; + + struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO]; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + //struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE]; + struct vdec_s *slave = NULL; + + if (has_hevc_vdec()) { + if (port->vformat == VFORMAT_HEVC + || port->vformat == VFORMAT_VP9) + pvbuf = &bufs[BUF_TYPE_HEVC]; + } + + if (port->type & PORT_TYPE_MPTS) { + tsync_pcr_stop(); + tsdemux_release(); + } + + if (port->type & PORT_TYPE_MPPS) + psparser_release(); + + if (port->type & PORT_TYPE_VIDEO) + video_component_release(port, pvbuf, 0); + + if (port->type & PORT_TYPE_AUDIO) + audio_component_release(port, pabuf, 0); + + if (port->type & PORT_TYPE_SUB) + //sub_port_release(port, psbuf); + + if (vdec) { + if (vdec->slave) + slave = vdec->slave; + + vdec_release(vdec); + + if (slave) + vdec_release(slave); + vdec = NULL; + } + + port->pcr_inited = 0; + port->flag = 0; + + return 0; +} + +static int vdec_ports_init(struct aml_vdec_adapt *vdec_adapt) +{ + int ret = -1; + struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO]; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + //struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE]; + struct stream_port_s *port = &g_cfg.port; + struct vdec_s *vdec = NULL; + + /* create the vdec instance.*/ + vdec = vdec_create(port, NULL); + if (IS_ERR_OR_NULL(vdec)) + return -1; + + /* set v4l2 ctx */ + vdec->private = vdec_adapt->ctx; + + g_cfg.vdec = vdec; + + /* set video format and sys info */ + vdec_set_format(vdec, g_cfg.dec_prop.format); //set by ioctl ??? + set_system_info(vdec); + set_vdec_mode(vdec); + set_vdec_vfm(vdec); + + stbuf_fetch_init(); + + user_buffer_init(); + + if ((port->type & PORT_TYPE_AUDIO) + && (port->flag & PORT_FLAG_AFORMAT)) { + ret = audio_component_init(port, pabuf); + if (ret < 0) { + pr_err("audio_component_init failed\n"); + goto error1; + } + } + + if ((port->type & PORT_TYPE_VIDEO) + && (vdec->port_flag & PORT_FLAG_VFORMAT)) { + pvbuf->for_4k = 0; + if (has_hevc_vdec()) { + if (port->vformat == VFORMAT_HEVC + || port->vformat == VFORMAT_VP9) + pvbuf = &bufs[BUF_TYPE_HEVC]; + } + + ret = video_component_init(port, pvbuf); + if (ret < 0) { + pr_err("video_component_init failed\n"); + goto error2; + } + } + + /* connect vdec at the end after all HW initialization */ + if ((port->type & PORT_TYPE_VIDEO) + && (vdec->port_flag & PORT_FLAG_VFORMAT)) + vdec_connect(vdec); + + return 0; + +//error3: + //video_component_release(port, pvbuf, 0); +error2: + audio_component_release(port, pabuf, 0); +error1: + return ret; +} + +int video_decoder_init(struct aml_vdec_adapt *vdec) +{ + int ret = -1; + + /* sets configure data */ + set_cfg_info(vdec); + + /* init hw and gate*/ + ret = enable_hardware(); + if (ret < 0) { + pr_info("enable hw fail.\n"); + goto out; + } + + /* init the buffer work space and connect vdec.*/ + ret = vdec_ports_init(vdec); + if (ret < 0) { + pr_info("vdec ports init fail.\n"); + goto out; + } +out: + return ret; +} + +int video_decoder_release(struct aml_vdec_adapt *vdec) +{ + int ret = -1; + struct stream_port_s *port = &g_cfg.port; + + ret = vdec_ports_release(port); + if (ret < 0) { + pr_info("vdec ports release fail.\n"); + goto out; + } + + /* disable gates */ + ret = disable_hardware(); + if (ret < 0) { + pr_info("disable hw fail.\n"); + goto out; + } +out: + return ret; +} + +int vdec_vbuf_write(struct file *file, const char *buf, unsigned int count) +{ + int ret = -1; + int try_cnt = 100; + struct stream_port_s *port = &g_cfg.port; + struct vdec_s *vdec = g_cfg.vdec; + struct stream_buf_s *pbuf = NULL; + + if (has_hevc_vdec()) { + pbuf = (port->type & PORT_TYPE_HEVC) ? &bufs[BUF_TYPE_HEVC] : + &bufs[BUF_TYPE_VIDEO]; + } else + pbuf = &bufs[BUF_TYPE_VIDEO]; + + /*if (!(port_get_inited(priv))) { + r = video_decoder_init(priv); + if (r < 0) + return r; + }*/ + + do { + if (vdec->port_flag & PORT_FLAG_DRM) + ret = drm_write(file, pbuf, buf, count); + else + ret = esparser_write(file, pbuf, buf, count); + } while (ret == -EAGAIN && try_cnt--); + + if (slow_input) { + pr_info("slow_input: es codec write size %x\n", ret); + msleep(10); + } + +#ifdef DATA_DEBUG + /* dump to file */ + //dump_write(vbuf, size); + //pr_info("vbuf: %p, size: %u, ret: %d\n", vbuf, size, ret); +#endif + + return ret; +} + +int is_need_to_buf(void) +{ + struct vdec_s *vdec = g_cfg.vdec; + + if (vdec->input.have_frame_num > 8) + return 0; + else + return 1; +} + +int vdec_vframe_write(struct file *file, const char *buf, + unsigned int count, unsigned long int timestamp) +{ + int ret = -1; + int try_cnt = 10; + struct vdec_s *vdec = g_cfg.vdec; + + /* set timestamp */ + vdec_set_timestamp(vdec, timestamp); + + do { + ret = vdec_write_vframe(vdec, buf, count); + if (ret == -EAGAIN) { + vdec_input_level(&vdec->input); + msleep(20); + } + } while (ret == -EAGAIN && try_cnt--); + + if (slow_input) { + pr_info("slow_input: frame codec write size %d\n", ret); + msleep(30); + } + +#ifdef DATA_DEBUG + /* dump to file */ + dump_write(buf, count); +#endif + aml_v4l2_debug(1, "vdec_vframe_write, vbuf: %p, size: %u, ret: %d, crc: %x\n", + buf, count, ret, crc32(0, buf, count)); + + return ret; +} + +void aml_decoder_flush(void) +{ + struct vdec_s *vdec = g_cfg.vdec; + + if (vdec) + vdec_set_eos(vdec, true); +} + +void aml_codec_reset(void) +{ + struct vdec_s *vdec = g_cfg.vdec; + + if (vdec) { + vdec_set_eos(vdec, false); + vdec_reset(vdec); + } +} + +bool is_decoder_ready(void) +{ + struct vdec_s *vdec = g_cfg.vdec; + int state = VDEC_STATUS_UNINITIALIZED; + + if (vdec) { + state = vdec_get_status(vdec); + + if (state == VDEC_STATUS_CONNECTED + || state == VDEC_STATUS_ACTIVE) + return true; + } + + return false; +} diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_adapt.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_adapt.h new file mode 100644 index 000000000000..9c0b05ba4ad7 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_adapt.h @@ -0,0 +1,40 @@ +#ifndef VDEC_ADAPT_H +#define VDEC_ADAPT_H + +#include +#include "aml_vcodec_drv.h" + +struct aml_vdec_adapt { + enum vformat_e id; + void *vsi; + int32_t failure; + uint32_t inst_addr; + unsigned int signaled; + struct aml_vcodec_ctx *ctx; + struct platform_device *dev; + wait_queue_head_t wq; + struct file *filp; + //ipi_handler_t handler; +}; + +int video_decoder_init(struct aml_vdec_adapt *vdec); + +int video_decoder_release(struct aml_vdec_adapt *vdec); + +int vdec_vbuf_write(struct file *file, const char *buf, unsigned int count); + +int vdec_vframe_write(struct file *file, const char *buf, + unsigned int count, unsigned long int pts); + +int is_need_to_buf(void); + +void aml_decoder_flush(void); + +void aml_codec_reset(void); + +extern void dump_write(const char __user *buf, size_t count); + +bool is_decoder_ready(void); + +#endif /* VDEC_ADAPT_H */ + diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec.c b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec.c new file mode 100644 index 000000000000..4a869edaaf5c --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec.c @@ -0,0 +1,1682 @@ +#include +#include +#include + +#include "aml_vcodec_drv.h" +#include "aml_vcodec_dec.h" +//#include "aml_vcodec_intr.h" +#include "aml_vcodec_util.h" +#include "vdec_drv_if.h" +#include "aml_vcodec_dec_pm.h" +#include +#include +#include +#include "aml_vcodec_adapt.h" + +#include "aml_vcodec_vfm.h" + +#define OUT_FMT_IDX 0 +#define CAP_FMT_IDX 1//3 + +#define AML_VDEC_MIN_W 64U +#define AML_VDEC_MIN_H 64U +#define DFT_CFG_WIDTH AML_VDEC_MIN_W +#define DFT_CFG_HEIGHT AML_VDEC_MIN_H + +//#define USEC_PER_SEC 1000000 + +#define call_void_memop(vb, op, args...) \ + do { \ + if ((vb)->vb2_queue->mem_ops->op) \ + (vb)->vb2_queue->mem_ops->op(args); \ + } while (0) + +static struct aml_video_fmt aml_video_formats[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .type = AML_FMT_DEC, + .num_planes = 1, + }, +/* { + .fourcc = V4L2_PIX_FMT_VP8, + .type = AML_FMT_DEC, + .num_planes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_VP9, + .type = AML_FMT_DEC, + .num_planes = 1, + },*/ + { + .fourcc = V4L2_PIX_FMT_NV12, + .type = AML_FMT_FRAME, + .num_planes = 2, + }, +}; + +static const struct aml_codec_framesizes aml_vdec_framesizes[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 16, + AML_VDEC_MIN_H, AML_VDEC_MAX_H, 16 }, + }, +/* { + .fourcc = V4L2_PIX_FMT_VP8, + .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 16, + AML_VDEC_MIN_H, AML_VDEC_MAX_H, 16 }, + }, + { + .fourcc = V4L2_PIX_FMT_VP9, + .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 16, + AML_VDEC_MIN_H, AML_VDEC_MAX_H, 16 }, + },*/ +}; + +#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(aml_vdec_framesizes) +#define NUM_FORMATS ARRAY_SIZE(aml_video_formats) + +static struct aml_video_fmt *aml_vdec_find_format(struct v4l2_format *f) +{ + struct aml_video_fmt *fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &aml_video_formats[k]; + if (fmt->fourcc == f->fmt.pix_mp.pixelformat) + return fmt; + } + + return NULL; +} + +static struct aml_q_data *aml_vdec_get_q_data(struct aml_vcodec_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->q_data[AML_Q_DATA_SRC]; + + return &ctx->q_data[AML_Q_DATA_DST]; +} + +static void aml_vdec_queue_res_chg_event(struct aml_vcodec_ctx *ctx) +{ + static const struct v4l2_event ev_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = + V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + aml_v4l2_debug(4, "[%d]", ctx->id); + v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); +} + +static void aml_vdec_flush_decoder(struct aml_vcodec_ctx *ctx) +{ + aml_v4l2_debug(1, "%s() [%d]\n", __FUNCTION__, __LINE__); + + aml_decoder_flush(); +} + +static void aml_vdec_pic_info_update(struct aml_vcodec_ctx *ctx) +{ + unsigned int dpbsize = 0; + int ret; + + if (vdec_if_get_param(ctx, + GET_PARAM_PIC_INFO, + &ctx->last_decoded_picinfo)) { + aml_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", + ctx->id); + return; + } + + if (ctx->last_decoded_picinfo.pic_w == 0 || + ctx->last_decoded_picinfo.pic_h == 0 || + ctx->last_decoded_picinfo.buf_w == 0 || + ctx->last_decoded_picinfo.buf_h == 0) { + aml_v4l2_err("Cannot get correct pic info"); + return; + } + + if ((ctx->last_decoded_picinfo.pic_w == ctx->picinfo.pic_w) || + (ctx->last_decoded_picinfo.pic_h == ctx->picinfo.pic_h)) + return; + + aml_v4l2_debug(4, + "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", + ctx->id, ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->last_decoded_picinfo.buf_w, + ctx->last_decoded_picinfo.buf_h); + + ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); + if (dpbsize == 0) + aml_v4l2_err("Incorrect dpb size, ret=%d", ret); + + ctx->dpb_size = dpbsize; +} + +int get_fb_from_queue(struct aml_vcodec_ctx *ctx, struct vdec_fb **out_fb) +{ + struct vb2_buffer *dst_buf = NULL; + struct vdec_fb *pfb; + struct aml_video_dec_buf *dst_buf_info, *info; + struct vb2_v4l2_buffer *dst_vb2_v4l2; + + aml_v4l2_debug(4,"%s() [%d]\n", __FUNCTION__, __LINE__); + + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + if (!dst_buf) { + pr_err("[%d] dst_buf is empty!!\n", ctx->id); + return -1; + } + + dst_vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf); + dst_buf_info = container_of(dst_vb2_v4l2, struct aml_video_dec_buf, vb); + + pfb = &dst_buf_info->frame_buffer; + //pfb->base_y.va = vb2_plane_vaddr(dst_buf, 0); + pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + pfb->base_y.va = phys_to_virt(dma_to_phys(v4l_get_dev_from_codec_mm(), pfb->base_y.dma_addr)); + pfb->base_y.size = ctx->picinfo.y_bs_sz + ctx->picinfo.y_len_sz; + + //pfb->base_c.va = vb2_plane_vaddr(dst_buf, 1); + pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 1); + pfb->base_c.va = phys_to_virt(dma_to_phys(v4l_get_dev_from_codec_mm(), pfb->base_c.dma_addr)); + pfb->base_c.size = ctx->picinfo.c_bs_sz + ctx->picinfo.c_len_sz; + pfb->status = 0; + + aml_v4l2_debug(4, + "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad y_size=%zx, c_size: %zx\n", + dst_buf->index, pfb, + pfb->base_y.va, &pfb->base_y.dma_addr, + &pfb->base_c.dma_addr, pfb->base_y.size, pfb->base_c.size); + + mutex_lock(&ctx->lock); + dst_buf_info->used = true; + mutex_unlock(&ctx->lock); + + v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + + *out_fb = pfb; + + info = container_of(pfb, struct aml_video_dec_buf, frame_buffer); + + return 0; +} +EXPORT_SYMBOL(get_fb_from_queue); + +int put_fb_to_queue(struct aml_vcodec_ctx *ctx, struct vdec_fb *in_fb) +{ + struct aml_video_dec_buf *dstbuf; + + pr_info("%s() [%d]\n", __FUNCTION__, __LINE__); + + if (in_fb == NULL) { + aml_v4l2_debug(4, " No free frame buffer"); + return -1; + } + + aml_v4l2_debug(4, "[%d] tmp_frame_addr = 0x%p", ctx->id, in_fb); + + dstbuf = container_of(in_fb, struct aml_video_dec_buf, frame_buffer); + + mutex_lock(&ctx->lock); + + if (!dstbuf->used) + goto out; + + aml_v4l2_debug(4, + "[%d]status=%x queue id=%d to rdy_queue", + ctx->id, in_fb->status, + dstbuf->vb.vb2_buf.index); + + v4l2_m2m_buf_queue(ctx->m2m_ctx, &dstbuf->vb); + + dstbuf->used = false; +out: + mutex_unlock(&ctx->lock); + + return 0; + +} +EXPORT_SYMBOL(put_fb_to_queue); + +void trans_vframe_to_user(struct aml_vcodec_ctx *ctx, struct vdec_fb *fb) +{ + int cnt; + struct aml_video_dec_buf *dstbuf = NULL; + struct vframe_s *vf = (struct vframe_s *)fb->vf_handle; + + dstbuf = container_of(fb, struct aml_video_dec_buf, frame_buffer); + if (!dstbuf->used) + return; + + aml_v4l2_debug(4,"%s() [%d], base_y: %lu, idx: %u\n", + __FUNCTION__, __LINE__, fb->base_y.size, dstbuf->vb.vb2_buf.index); + aml_v4l2_debug(4,"%s() [%d], base_c: %lu, idx: %u\n", + __FUNCTION__, __LINE__, fb->base_c.size, dstbuf->vb.vb2_buf.index); + + dstbuf->vb.vb2_buf.timestamp = vf->timestamp; + aml_v4l2_debug(4,"%s() [%d], timestamp: %llx\n", __FUNCTION__, __LINE__, + vf->timestamp); + + vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 0, fb->base_y.bytes_used); + vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1, fb->base_c.bytes_used); + + dstbuf->ready_to_display = true; + + aml_v4l2_debug(4,"%s() [%d], [%d]status=%x queue id=%d to done_list %d\n", + __FUNCTION__, __LINE__, + ctx->id, fb->status, + dstbuf->vb.vb2_buf.index, + dstbuf->queued_in_vb2); + + cnt = atomic_read(&dstbuf->vb.vb2_buf.vb2_queue->owned_by_drv_count); + aml_v4l2_debug(4,"%s() [%d], owned_by_drv_count: %d\n", __FUNCTION__, __LINE__, cnt); + aml_v4l2_debug(4,"%s() [%d], y_va: %p, vf_h: %lx\n", __FUNCTION__, __LINE__, + dstbuf->frame_buffer.base_y.va, dstbuf->frame_buffer.vf_handle); + + if (ctx->state == AML_STATE_FLUSH && + vf->flag == VFRAME_FLAG_EMPTY_FRAME_V4L) { + pr_info("recevie a empty frame after flush.\n"); + + dstbuf->lastframe = true; + dstbuf->vb.flags = V4L2_BUF_FLAG_LAST; + vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 0, 0); + vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1, 0); + ctx->has_receive_eos = true; + } + + aml_v4l2_debug(1, "%s() [%d]\n", __FUNCTION__, __LINE__); + + v4l2_m2m_buf_done(&dstbuf->vb, VB2_BUF_STATE_DONE); + + ctx->decoded_frame_cnt++; +} + +static int get_display_buffer(struct aml_vcodec_ctx *ctx, struct vdec_fb **out) +{ + int ret = -1; + + aml_v4l2_debug(4, "[%d]", ctx->id); + + ret = vdec_if_get_param(ctx, GET_PARAM_DISP_FRAME_BUFFER, out); + if (ret) { + aml_v4l2_err("[%d]Cannot get param : GET_PARAM_DISP_FRAME_BUFFER", ctx->id); + return -1; + } + + if (!*out) { + aml_v4l2_debug(4, "No display frame buffer"); + return -1; + } + + //aml_v4l2_debug(4,"%s() [%d]\n", __FUNCTION__, __LINE__); + + return ret; +} + +static void try_to_capture(struct aml_vcodec_ctx *ctx) +{ + int ret = -1; + int try_cnt = 100; + struct vdec_fb *fb = NULL; + struct aml_video_dec_buf *dstbuf; + int cnt = 0; + + do { + ret = get_display_buffer(ctx, &fb); + if (ret) { + aml_v4l2_debug(4,"%s() [%d], the que have no disp buf,ret: %d\n", + __FUNCTION__, __LINE__, ret); + break; + } + + dstbuf = container_of(fb, struct aml_video_dec_buf, frame_buffer); + dstbuf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;//need to change status + cnt = atomic_read(&dstbuf->vb.vb2_buf.vb2_queue->owned_by_drv_count); + aml_v4l2_debug(1, "%s() [%d], cnt: %d\n", __FUNCTION__, __LINE__, cnt); + + if (cnt) + trans_vframe_to_user(ctx, fb); + + //msleep(300); + //video_vf_put(fb);//recycle video buf + + /*ret = put_fb_to_queue(ctx, fb); + if (ret) + aml_v4l2_debug(4,"%s() [%d], put_fb_to_queue fail.\n", + __FUNCTION__, __LINE__);*/ + } while(try_cnt--); +} + +static void aml_vdec_worker(struct work_struct *work) +{ + struct aml_vcodec_ctx *ctx = + container_of(work, struct aml_vcodec_ctx, decode_work); + struct aml_vcodec_dev *dev = ctx->dev; + struct vb2_buffer *src_buf; + struct aml_vcodec_mem buf; + bool res_chg = false; + int ret; + struct aml_video_dec_buf *src_buf_info; + struct vb2_v4l2_buffer *src_vb2_v4l2; + + aml_v4l2_debug(4,"entry [%d] [%s]\n", __LINE__, __FUNCTION__); + + if ((ctx->state == AML_STATE_FLUSH || + ctx->state == AML_STATE_RESET) && + !is_decoder_ready()) { + pr_err("[%d] [%s] the decoder has no ready.\n", + __LINE__, __FUNCTION__); + return; + } + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + if (src_buf == NULL) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + aml_v4l2_debug(0, "[%d] src_buf empty!!", ctx->id); + return; + } + + src_vb2_v4l2 = container_of(src_buf, struct vb2_v4l2_buffer, vb2_buf); + src_buf_info = container_of(src_vb2_v4l2, struct aml_video_dec_buf, vb); + + if (src_buf_info->lastframe) { + //the empty data use to flushed the decoder. + aml_v4l2_debug(0, "Got empty flush input buffer."); + + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + return; + } + + buf.va = vb2_plane_vaddr(src_buf, 0); + buf.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + //buf.va = phys_to_virt(dma_to_phys(v4l_get_dev_from_codec_mm(), buf.dma_addr)); + + buf.size = (size_t)src_buf->planes[0].bytesused; + if (!buf.va) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + aml_v4l2_err("[%d] id=%d src_addr is NULL!!", + ctx->id, src_buf->index); + return; + } + aml_v4l2_debug(4, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", + ctx->id, buf.va, &buf.dma_addr, buf.size, src_buf); + + src_buf_info->used = true; + + //pr_err("%s() [%d], size: 0x%zx, crc: 0x%x\n", + //__FUNCTION__, __LINE__, buf.size, crc32(0, buf.va, buf.size)); + + /* pts = (time / 10e6) * (90k / fps) */ + aml_v4l2_debug(4,"[%d] [%s] timestamp: 0x%llx\n", __LINE__, __FUNCTION__, + src_buf->timestamp); + + ret = vdec_if_decode(ctx, &buf, src_buf->timestamp, &res_chg); + if (ret > 0) { + /* + * we only return src buffer with VB2_BUF_STATE_DONE + * when decode success without resolution change + */ + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(&src_buf_info->vb, VB2_BUF_STATE_DONE); + } else if (ret == -EAGAIN) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + return; + } else { + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + if (ret == -EIO) { + mutex_lock(&ctx->lock); + src_buf_info->error = true; + mutex_unlock(&ctx->lock); + } + v4l2_m2m_buf_done(&src_buf_info->vb, VB2_BUF_STATE_ERROR); + } + + if (!ret && res_chg) { + if (0) aml_vdec_pic_info_update(ctx); + /* + * On encountering a resolution change in the stream. + * The driver must first process and decode all + * remaining buffers from before the resolution change + * point, so call flush decode here + */ + if (0) aml_vdec_flush_decoder(ctx); + /* + * After all buffers containing decoded frames from + * before the resolution change point ready to be + * dequeued on the CAPTURE queue, the driver sends a + * V4L2_EVENT_SOURCE_CHANGE event for source change + * type V4L2_EVENT_SRC_CH_RESOLUTION + */ + if (0) aml_vdec_queue_res_chg_event(ctx); + } + + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); +} + +static void aml_vdec_worker_vf(struct work_struct *work) +{ + struct aml_vcodec_ctx *ctx = + container_of(work, struct aml_vcodec_ctx, decode_work_vf); + + aml_v4l2_debug(1, "[%d] [%s], state: %d \n", __LINE__, __FUNCTION__, ctx->state); + + if (ctx->state != AML_STATE_ABORT && + ctx->state != AML_STATE_FREE) + try_to_capture(ctx); +} + +static void aml_reset_worker(struct work_struct *work) +{ + struct aml_vcodec_ctx *ctx = + container_of(work, struct aml_vcodec_ctx, reset_work); + int try_cnt = 10; + int buf_ready_num; + + aml_v4l2_debug(1, "[%d] [%s] \n", __LINE__, __FUNCTION__); + + aml_vdec_lock(ctx); + + do { + buf_ready_num = v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx); + if (buf_ready_num < ctx->dpb_size) { + aml_v4l2_debug(4,"try to get more bufs, num: %d.\n", + buf_ready_num); + msleep(20); + } + } while (buf_ready_num < ctx->dpb_size && try_cnt--); + + if (buf_ready_num < ctx->dpb_size) { + aml_vdec_unlock(ctx); + pr_err("the buf has not enough to be used in decoder.\n"); + return; + } + + aml_codec_reset(); + + ctx->state = AML_STATE_HEADER; + + v4l2_m2m_try_schedule(ctx->m2m_ctx); + + aml_vdec_unlock(ctx); +} + +static int vidioc_try_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *cmd) +{ + switch (cmd->cmd) { + case V4L2_DEC_CMD_STOP: + case V4L2_DEC_CMD_START: + if (cmd->flags != 0) { + aml_v4l2_err("cmd->flags=%u", cmd->flags); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *cmd) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *src_vq, *dst_vq; + int ret; + + ret = vidioc_try_decoder_cmd(file, priv, cmd); + if (ret) + return ret; + + aml_v4l2_debug(1, "[%d] [%s], cmd: %u\n", __LINE__, __FUNCTION__, cmd->cmd); + dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + switch (cmd->cmd) { + case V4L2_DEC_CMD_STOP: + + if (ctx->state != AML_STATE_HEADER) + return 0; + + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!vb2_is_streaming(src_vq)) { + aml_v4l2_debug(0, "Output stream is off. No need to flush."); + return 0; + } + if (!vb2_is_streaming(dst_vq)) { + aml_v4l2_debug(0, "Capture stream is off. No need to flush."); + return 0; + } + + /* flush src */ + v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf->vb); + v4l2_m2m_try_schedule(ctx->m2m_ctx);//pay attention + + /* flush dst */ + aml_vdec_flush_decoder(ctx); + + ctx->state = AML_STATE_FLUSH; + break; + + case V4L2_DEC_CMD_START: + aml_v4l2_debug(4,"[%d] [%s] V4L2_DEC_CMD_START \n", __LINE__, __FUNCTION__); + vb2_clear_last_buffer_dequeued(dst_vq);//pay attention + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_decoder_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct v4l2_fh *fh = file->private_data; + struct aml_vcodec_ctx *ctx = fh_to_ctx(fh); + struct aml_vcodec_dev *dev = ctx->dev; + struct vb2_queue *q; + int ret = 0; + + ret = v4l2_m2m_ioctl_streamon(file, priv, i); + + q = v4l2_m2m_get_vq(fh->m2m_ctx, i); + + if (ctx->state == AML_STATE_FLUSH) { + if (!V4L2_TYPE_IS_OUTPUT(q->type)) { + queue_work(dev->reset_workqueue, &ctx->reset_work); + ctx->state = AML_STATE_RESET; + } + } + + return ret; +} + +void aml_vdec_unlock(struct aml_vcodec_ctx *ctx) +{ + mutex_unlock(&ctx->dev->dec_mutex); +} + +void aml_vdec_lock(struct aml_vcodec_ctx *ctx) +{ + mutex_lock(&ctx->dev->dec_mutex); +} + +void aml_vcodec_dec_release(struct aml_vcodec_ctx *ctx) +{ + vdec_if_deinit(ctx); + ctx->state = AML_STATE_FREE; +} + +void aml_vcodec_dec_set_default_params(struct aml_vcodec_ctx *ctx) +{ + struct aml_q_data *q_data; + + ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; + ctx->fh.m2m_ctx = ctx->m2m_ctx; + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + INIT_WORK(&ctx->decode_work, aml_vdec_worker); + INIT_WORK(&ctx->decode_work_vf, aml_vdec_worker_vf); + INIT_WORK(&ctx->reset_work, aml_reset_worker); + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->dev->dec_capability = VCODEC_CAPABILITY_4K_DISABLED;//disable 4k + + q_data = &ctx->q_data[AML_Q_DATA_SRC]; + memset(q_data, 0, sizeof(struct aml_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->fmt = &aml_video_formats[OUT_FMT_IDX]; + q_data->field = V4L2_FIELD_NONE; + + q_data->sizeimage[0] = (1024 * 1024);//DFT_CFG_WIDTH * DFT_CFG_HEIGHT; //1m + q_data->bytesperline[0] = 0; + + q_data = &ctx->q_data[AML_Q_DATA_DST]; + memset(q_data, 0, sizeof(struct aml_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->fmt = &aml_video_formats[CAP_FMT_IDX]; + q_data->field = V4L2_FIELD_NONE; + + v4l_bound_align_image(&q_data->coded_width, + AML_VDEC_MIN_W, + AML_VDEC_MAX_W, 4, + &q_data->coded_height, + AML_VDEC_MIN_H, + AML_VDEC_MAX_H, 5, 6); + + q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height; + q_data->bytesperline[0] = q_data->coded_width; + q_data->sizeimage[1] = q_data->sizeimage[0] / 2; + q_data->bytesperline[1] = q_data->coded_width; +} + +static int vidioc_vdec_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == AML_STATE_ABORT) { + aml_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_vdec_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == AML_STATE_ABORT) { + aml_v4l2_err("[%d] Call on DQBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + /*if (V4L2_TYPE_IS_OUTPUT(buf->type)) { + aml_v4l2_debug(4,"%s() [%d]\n", __FUNCTION__, __LINE__); + wait_event_interruptible_timeout(ctx->wq, is_need_to_buf(), + msecs_to_jiffies(200)); + }*/ + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_vdec_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, AML_VCODEC_DEC_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, AML_PLATFORM_STR, sizeof(cap->bus_info)); + strlcpy(cap->card, AML_PLATFORM_STR, sizeof(cap->card)); + + return 0; +} + +static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static int vidioc_try_fmt(struct v4l2_format *f, struct aml_video_fmt *fmt) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + int i; + + pix_fmt_mp->field = V4L2_FIELD_NONE; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pix_fmt_mp->num_planes = 1; + pix_fmt_mp->plane_fmt[0].bytesperline = 0; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + int tmp_w, tmp_h; + + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + AML_VDEC_MIN_H, + AML_VDEC_MAX_H); + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + AML_VDEC_MIN_W, + AML_VDEC_MAX_W); + + /* + * Find next closer width align 64, heign align 64, size align + * 64 rectangle + * Note: This only get default value, the real HW needed value + * only available when ctx in AML_STATE_HEADER state + */ + tmp_w = pix_fmt_mp->width; + tmp_h = pix_fmt_mp->height; + v4l_bound_align_image(&pix_fmt_mp->width, + AML_VDEC_MIN_W, + AML_VDEC_MAX_W, 6, + &pix_fmt_mp->height, + AML_VDEC_MIN_H, + AML_VDEC_MAX_H, 6, 9); + + if (pix_fmt_mp->width < tmp_w && + (pix_fmt_mp->width + 64) <= AML_VDEC_MAX_W) + pix_fmt_mp->width += 64; + if (pix_fmt_mp->height < tmp_h && + (pix_fmt_mp->height + 64) <= AML_VDEC_MAX_H) + pix_fmt_mp->height += 64; + + aml_v4l2_debug(4, + "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d", + tmp_w, tmp_h, pix_fmt_mp->width, + pix_fmt_mp->height, + pix_fmt_mp->width * pix_fmt_mp->height); + + pix_fmt_mp->num_planes = fmt->num_planes; + pix_fmt_mp->plane_fmt[0].sizeimage = + pix_fmt_mp->width * pix_fmt_mp->height; + pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; + + if (pix_fmt_mp->num_planes == 2) { + pix_fmt_mp->plane_fmt[1].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 2; + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->width; + } + } + + for (i = 0; i < pix_fmt_mp->num_planes; i++) + memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, + sizeof(pix_fmt_mp->plane_fmt[0].reserved)); + + pix_fmt_mp->flags = 0; + memset(&pix_fmt_mp->reserved, 0x0, sizeof(pix_fmt_mp->reserved)); + return 0; +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_video_fmt *fmt; + + fmt = aml_vdec_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = aml_video_formats[CAP_FMT_IDX].fourcc; + fmt = aml_vdec_find_format(f); + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + struct aml_video_fmt *fmt; + + fmt = aml_vdec_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = aml_video_formats[OUT_FMT_IDX].fourcc; + fmt = aml_vdec_find_format(f); + } + + if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { + aml_v4l2_err("sizeimage of output format must be given"); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_vdec_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + struct aml_q_data *q_data; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + q_data = &ctx->q_data[AML_Q_DATA_DST]; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->picinfo.pic_w; + s->r.height = ctx->picinfo.pic_h; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->picinfo.buf_w; + s->r.height = ctx->picinfo.buf_h; + break; + case V4L2_SEL_TGT_COMPOSE: + if (vdec_if_get_param(ctx, GET_PARAM_CROP_INFO, &(s->r))) { + /* set to default value if header info not ready yet*/ + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + } + break; + default: + return -EINVAL; + } + + if (ctx->state < AML_STATE_HEADER) { + /* set to default value if header info not ready yet*/ + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + return 0; + } + + return 0; +} + +static int vidioc_vdec_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->picinfo.pic_w; + s->r.height = ctx->picinfo.pic_h; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_vdec_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *pix_mp; + struct aml_q_data *q_data; + int ret = 0; + struct aml_video_fmt *fmt; + + aml_v4l2_debug(4, "[%d]", ctx->id); + + q_data = aml_vdec_get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + pix_mp = &f->fmt.pix_mp; + if ((f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && + vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) { + aml_v4l2_err("out_q_ctx buffers already requested"); + ret = -EBUSY; + } + + if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && + vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) { + aml_v4l2_err("cap_q_ctx buffers already requested"); + ret = -EBUSY; + } + + fmt = aml_vdec_find_format(f); + if (fmt == NULL) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + f->fmt.pix.pixelformat = + aml_video_formats[OUT_FMT_IDX].fourcc; + fmt = aml_vdec_find_format(f); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + f->fmt.pix.pixelformat = + aml_video_formats[CAP_FMT_IDX].fourcc; + fmt = aml_vdec_find_format(f); + } + } + + q_data->fmt = fmt; + vidioc_try_fmt(f, q_data->fmt); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage; + q_data->coded_width = pix_mp->width; + q_data->coded_height = pix_mp->height; + + aml_v4l2_debug(4, "%s() [%d], w: %d, h: %d, size: %d\n", + __func__, __LINE__, pix_mp->width, pix_mp->height, + pix_mp->plane_fmt[0].sizeimage); + + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quantization = f->fmt.pix_mp.quantization; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + + if (ctx->state == AML_STATE_FREE) { + ret = vdec_if_init(ctx, q_data->fmt->fourcc); + if (ret) { + aml_v4l2_err("[%d]: vdec_if_init() fail ret=%d", + ctx->id, ret); + return -EINVAL; + } + ctx->state = AML_STATE_INIT; + } + } + + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + int i = 0; + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (fsize->index != 0) + return -EINVAL; + + for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) { + if (fsize->pixel_format != aml_vdec_framesizes[i].fourcc) + continue; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = aml_vdec_framesizes[i].stepwise; + if (!(ctx->dev->dec_capability & + VCODEC_CAPABILITY_4K_DISABLED)) { + aml_v4l2_debug(4, "4K is enabled"); + fsize->stepwise.max_width = + VCODEC_DEC_4K_CODED_WIDTH; + fsize->stepwise.max_height = + VCODEC_DEC_4K_CODED_HEIGHT; + } + aml_v4l2_debug(4, "%x, %d %d %d %d %d %d", + ctx->dev->dec_capability, + fsize->stepwise.min_width, + fsize->stepwise.max_width, + fsize->stepwise.step_width, + fsize->stepwise.min_height, + fsize->stepwise.max_height, + fsize->stepwise.step_height); + return 0; + } + + return -EINVAL; +} + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) +{ + struct aml_video_fmt *fmt; + int i, j = 0; + + for (i = 0; i < NUM_FORMATS; i++) { + if (output_queue && (aml_video_formats[i].type != AML_FMT_DEC)) + continue; + if (!output_queue && + (aml_video_formats[i].type != AML_FMT_FRAME)) + continue; + + if (j == f->index) + break; + ++j; + } + + if (i == NUM_FORMATS) + return -EINVAL; + + fmt = &aml_video_formats[i]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int vidioc_vdec_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, false); +} + +static int vidioc_vdec_enum_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, true); +} + +static int vidioc_vdec_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct vb2_queue *vq; + struct aml_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + aml_v4l2_err("no vb2 queue for type=%d", f->type); + return -EINVAL; + } + + q_data = aml_vdec_get_q_data(ctx, f->type); + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->colorspace = ctx->colorspace; + pix_mp->ycbcr_enc = ctx->ycbcr_enc; + pix_mp->quantization = ctx->quantization; + pix_mp->xfer_func = ctx->xfer_func; + + if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && + (ctx->state >= AML_STATE_HEADER)) { + /* Until STREAMOFF is called on the CAPTURE queue + * (acknowledging the event), the driver operates as if + * the resolution hasn't changed yet. + * So we just return picinfo yet, and update picinfo in + * stop_streaming hook function + */ + /* it is used for alloc the decode buffer size. */ + q_data->sizeimage[0] = ctx->picinfo.y_len_sz; + q_data->sizeimage[1] = ctx->picinfo.c_len_sz; + + /* it is used for alloc the EGL image buffer size. */ + q_data->coded_width = ctx->picinfo.pic_w; + q_data->coded_height = ctx->picinfo.pic_h; + + q_data->bytesperline[0] = ctx->picinfo.buf_w; + q_data->bytesperline[1] = ctx->picinfo.buf_w; + + /* + * Width and height are set to the dimensions + * of the movie, the buffer is bigger and + * further processing stages should crop to this + * rectangle. + */ + pix_mp->width = q_data->coded_width; + pix_mp->height = q_data->coded_height; + + /* + * Set pixelformat to the format in which mt vcodec + * outputs the decoded frame + */ + pix_mp->num_planes = q_data->fmt->num_planes; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; + pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; + pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; + + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* + * This is run on OUTPUT + * The buffer contains compressed image + * so width and height have no meaning. + * Assign value here to pass v4l2-compliance test + */ + pix_mp->width = q_data->visible_width; + pix_mp->height = q_data->visible_height; + pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->num_planes = q_data->fmt->num_planes; + } else { + pix_mp->width = q_data->coded_width; + pix_mp->height = q_data->coded_height; + pix_mp->num_planes = q_data->fmt->num_planes; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; + pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; + pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; + + aml_v4l2_debug(4, "[%d] type=%d state=%d Format information could not be read, not ready yet!", + ctx->id, f->type, ctx->state); + } + + return 0; +} + +/*int vidioc_vdec_g_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct aml_vcodec_ctx *ctx = fh_to_ctx(fh); + + if (a->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE) + a->value = 20; + + return 0; +}*/ + +static int vb2ops_vdec_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct aml_q_data *q_data; + unsigned int i; + + q_data = aml_vdec_get_q_data(ctx, vq->type); + + if (q_data == NULL) { + aml_v4l2_err("vq->type=%d err\n", vq->type); + return -EINVAL; + } + + if (*nplanes) { + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < q_data->sizeimage[i]) + return -EINVAL; + //alloc_devs[i] = &ctx->dev->plat_dev->dev; + alloc_devs[i] = v4l_get_dev_from_codec_mm();//alloc mm from the codec mm + } + } else { + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + *nplanes = 2; + else + *nplanes = 1; + + for (i = 0; i < *nplanes; i++) { + sizes[i] = q_data->sizeimage[i]; + //alloc_devs[i] = &ctx->dev->plat_dev->dev; + alloc_devs[i] = v4l_get_dev_from_codec_mm();//alloc mm from the codec mm + } + } + + pr_info("[%d], type: %d, plane: %d, buf cnt: %d, size: [Y: %u, C: %u]\n", + ctx->id, vq->type, *nplanes, *nbuffers, sizes[0], sizes[1]); + + return 0; +} + +static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) +{ + struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct aml_q_data *q_data; + int i; + + aml_v4l2_debug(4, "[%d] (%d) id=%d", + ctx->id, vb->vb2_queue->type, vb->index); + + q_data = aml_vdec_get_q_data(ctx, vb->vb2_queue->type); + + for (i = 0; i < q_data->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + aml_v4l2_err("data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), + q_data->sizeimage[i]); + } + } + + return 0; +} + +static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) +{ + struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 = NULL; + struct aml_video_dec_buf *buf = NULL; + struct aml_vcodec_mem src_mem; + unsigned int dpb = 0; + + vb2_v4l2 = to_vb2_v4l2_buffer(vb); + buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb); + + aml_v4l2_debug(1, "%s(),[%d], vb: %p, type: %d, idx: %d, state: %d, used: %d\n", + __FUNCTION__, __LINE__, vb, vb->vb2_queue->type, + vb->index, vb->state, buf->used); + /* + * check if this buffer is ready to be used after decode + */ + if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + + aml_v4l2_debug(4, "%s() [%d], y_va: %p, vf_h: %lx\n", __FUNCTION__, __LINE__, + buf->frame_buffer.base_y.va, buf->frame_buffer.vf_handle); + + if (buf->used == false) { + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); + buf->queued_in_vb2 = true; + buf->queued_in_v4l2 = true; + buf->ready_to_display = false; + } else { + buf->queued_in_vb2 = false; + buf->queued_in_v4l2 = true; + buf->ready_to_display = false; + + /* recycle vf */ + video_vf_put(&buf->frame_buffer); + } + return; + } + + v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); + + if (ctx->state != AML_STATE_INIT) { + aml_v4l2_debug(4, "[%d] already init driver %d", + ctx->id, ctx->state); + return; + } + + vb2_v4l2 = to_vb2_v4l2_buffer(vb); + buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb); + if (buf->lastframe) { + /* This shouldn't happen. Just in case. */ + aml_v4l2_err("Invalid flush buffer.\n"); + v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + return; + } + + src_mem.va = vb2_plane_vaddr(vb, 0); + src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + src_mem.size = (size_t)vb->planes[0].bytesused; + if (vdec_if_probe(ctx, &src_mem, NULL)) + return; + + if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo)) { + pr_err("[%d] GET_PARAM_PICTURE_INFO err\n", ctx->id); + return; + } + + if (vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpb)) { + pr_err("[%d] GET_PARAM_DPB_SIZE err\n", ctx->id); + return; + } + + ctx->dpb_size = dpb; + ctx->last_decoded_picinfo = ctx->picinfo; + aml_vdec_queue_res_chg_event(ctx); + + ctx->state = AML_STATE_HEADER; + + aml_v4l2_debug(1, "pic info, w: %u, h: %u, y_s: %u, c_s: %u, DPB: %u\n", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, ctx->picinfo.y_len_sz, + ctx->picinfo.c_len_sz, ctx->dpb_size); +} + +static void vb2ops_vdec_buf_finish(struct vb2_buffer *vb) +{ + struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 = NULL; + struct aml_video_dec_buf *buf = NULL; + bool buf_error; + + //pr_err("entry [%d] [%s]\n", __LINE__, __FUNCTION__); + + vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb); + + mutex_lock(&ctx->lock); + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf->queued_in_v4l2 = false; + buf->queued_in_vb2 = false; + } + buf_error = buf->error; + mutex_unlock(&ctx->lock); + + if (buf_error) { + aml_v4l2_err("Unrecoverable error on buffer."); + ctx->state = AML_STATE_ABORT; + } +} + +static int vb2ops_vdec_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb, + struct vb2_v4l2_buffer, vb2_buf); + struct aml_video_dec_buf *buf = container_of(vb2_v4l2, + struct aml_video_dec_buf, vb); + unsigned int size, phy_addr = 0; + + aml_v4l2_debug(4, "(%d) id=%d", vb->vb2_queue->type, vb->index); + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf->used = false; + buf->ready_to_display = false; + buf->queued_in_v4l2 = false; + } else { + buf->lastframe = false; + } + + /* codec_mm buffers count */ + if (V4L2_TYPE_IS_OUTPUT(vb->type)) { + size = vb->planes[0].length; + phy_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + buf->mem[0] = v4l_reqbufs_from_codec_mm("v4l-input", + phy_addr, size, vb->index); + aml_v4l2_debug(1, "IN alloc, addr: %x, size: %u, idx: %u\n", + phy_addr, size, vb->index); + } else { + size = vb->planes[0].length; + phy_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + buf->mem[0] = v4l_reqbufs_from_codec_mm("v4l-output", + phy_addr, size, vb->index); + aml_v4l2_debug(1, "OUT Y alloc, addr: %x, size: %u, idx: %u\n", + phy_addr, size, vb->index); + + size = vb->planes[1].length; + phy_addr = vb2_dma_contig_plane_dma_addr(vb, 1); + buf->mem[1] = v4l_reqbufs_from_codec_mm("v4l-output", + phy_addr, size, vb->index); + aml_v4l2_debug(1, "OUT C alloc, addr: %x, size: %u, idx: %u\n", + phy_addr, size, vb->index); + } + + return 0; +} + +static void codec_mm_bufs_cnt_clean(struct vb2_queue *q) +{ + struct vb2_v4l2_buffer *vb2_v4l2 = NULL; + struct aml_video_dec_buf *buf = NULL; + int i; + + for (i = 0; i < q->num_buffers; ++i) { + vb2_v4l2 = to_vb2_v4l2_buffer(q->bufs[i]); + buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb); + if (IS_ERR_OR_NULL(buf->mem[0])) + return; + + if (V4L2_TYPE_IS_OUTPUT(q->bufs[i]->type)) { + v4l_freebufs_back_to_codec_mm("v4l-input", buf->mem[0]); + buf->mem[0] = NULL; + + aml_v4l2_debug(1, "IN clean, addr: %lx, size: %u, idx: %u\n", + buf->mem[0]->phy_addr, buf->mem[0]->buffer_size, i); + continue; + } + + v4l_freebufs_back_to_codec_mm("v4l-output", buf->mem[0]); + v4l_freebufs_back_to_codec_mm("v4l-output", buf->mem[1]); + buf->mem[0] = NULL; + buf->mem[1] = NULL; + + aml_v4l2_debug(1, "OUT Y clean, addr: %lx, size: %u, idx: %u\n", + buf->mem[0]->phy_addr, buf->mem[0]->buffer_size, i); + aml_v4l2_debug(1, "OUT C clean, addr: %lx, size: %u, idx: %u\n", + buf->mem[1]->phy_addr, buf->mem[1]->buffer_size, i); + } +} + +static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(q); + + ctx->has_receive_eos = false; + v4l2_m2m_set_dst_buffered(ctx->fh.m2m_ctx, true); + + return 0; +} + +static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) +{ + struct vb2_buffer *src_buf = NULL, *dst_buf = NULL; + struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(q); + int i; + + aml_v4l2_debug(1, "[%d] [%s], [%d] (%d) state=(%x) frame_cnt=%d\n", + __LINE__, __FUNCTION__, ctx->id, q->type, + ctx->state, ctx->decoded_frame_cnt); + + codec_mm_bufs_cnt_clean(q); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { + struct vb2_v4l2_buffer *vb2_v4l2 = NULL; + struct aml_video_dec_buf *buf_info = NULL; + int cnt; + + vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); + buf_info = container_of(vb2_v4l2, struct aml_video_dec_buf, vb); + + cnt = atomic_read(&src_buf->vb2_queue->owned_by_drv_count); + aml_v4l2_debug(4,"%s() [%d], owned_by_drv_count: %d, lastframe: %d\n", + __FUNCTION__, __LINE__, cnt, buf_info->lastframe); + + if (!buf_info->lastframe) + v4l2_m2m_buf_done(vb2_v4l2, VB2_BUF_STATE_QUEUED); + } + + return; + } + + while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { + int cnt; + + vb2_set_plane_payload(dst_buf, 0, 0); + vb2_set_plane_payload(dst_buf, 1, 0); + + cnt = atomic_read(&dst_buf->vb2_queue->owned_by_drv_count); + + aml_v4l2_debug(4,"%s() [%d], state: %d, cnt: %d\n", + __FUNCTION__, __LINE__, dst_buf->state, cnt); + + if (dst_buf->state == VB2_BUF_STATE_DONE || !cnt) { //discard vframe data + dst_buf->state = VB2_BUF_STATE_ACTIVE; + continue; + } + + dst_buf->state = VB2_BUF_STATE_ACTIVE;//need to change status + + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_QUEUED); + } + + /*capture*/ + for (i = 0; i < q->num_buffers; ++i) { + struct vb2_v4l2_buffer *vb2_v4l2 = NULL; + struct aml_video_dec_buf *buf_info = NULL; + + if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_QUEUED); + + vb2_v4l2 = to_vb2_v4l2_buffer(q->bufs[i]); + buf_info = container_of(vb2_v4l2, struct aml_video_dec_buf, vb); + + if (ctx->has_receive_eos) + buf_info->used = false; + } +} + +static void m2mops_vdec_device_run(void *priv) +{ + struct aml_vcodec_ctx *ctx = priv; + struct aml_vcodec_dev *dev = ctx->dev; + + aml_v4l2_debug(4,"%s() [%d]\n", __FUNCTION__, __LINE__); + + queue_work(dev->decode_workqueue, &ctx->decode_work); +} + +void vdec_device_vf_run(struct aml_vcodec_ctx *ctx) +{ + struct aml_vcodec_dev *dev = ctx->dev; + + aml_v4l2_debug(4,"%s() [%d]\n", __FUNCTION__, __LINE__); + + /*wake_up_interruptible(&ctx->wq);*/ + + queue_work(dev->decode_workqueue_vf, &ctx->decode_work_vf); +} + +static int m2mops_vdec_job_ready(void *m2m_priv) +{ + struct aml_vcodec_ctx *ctx = m2m_priv; + + aml_v4l2_debug(4, "[%d]", ctx->id); + + if (ctx->state == AML_STATE_ABORT) + return 0; + + if ((ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w) || + (ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h)) + return 0; + + if (ctx->state != AML_STATE_HEADER) + return 0; + + return 1; +} + +static void m2mops_vdec_job_abort(void *priv) +{ + struct aml_vcodec_ctx *ctx = priv; + + aml_v4l2_debug(1, "%s() [%d]\n", __FUNCTION__, __LINE__); + + ctx->state = AML_STATE_ABORT; +} + +static int aml_vdec_g_v_ctrl(struct v4l2_ctrl *ctrl) +{ + struct aml_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + int ret = 0; + + aml_v4l2_debug(4,"%s() [%d]\n", __FUNCTION__, __LINE__); + + switch (ctrl->id) { + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + if (ctx->state >= AML_STATE_HEADER) { + ctrl->val = ctx->dpb_size; + } else { + aml_v4l2_debug(0, "Seqinfo not ready"); + ctrl->val = 0; + } + break; + default: + ret = -EINVAL; + } + return ret; +} + +static const struct v4l2_ctrl_ops aml_vcodec_dec_ctrl_ops = { + .g_volatile_ctrl = aml_vdec_g_v_ctrl, +}; + +int aml_vcodec_dec_ctrls_setup(struct aml_vcodec_ctx *ctx) +{ + struct v4l2_ctrl *ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, 1); + + ctrl = v4l2_ctrl_new_std(&ctx->ctrl_hdl, + &aml_vcodec_dec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + 0, 32, 1, 1); + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + if (ctx->ctrl_hdl.error) { + aml_v4l2_err("Adding control failed %d", + ctx->ctrl_hdl.error); + return ctx->ctrl_hdl.error; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + return 0; +} + +static void m2mops_vdec_lock(void *m2m_priv) +{ + struct aml_vcodec_ctx *ctx = m2m_priv; + + aml_v4l2_debug(4, "[%d]", ctx->id); + mutex_lock(&ctx->dev->dev_mutex); +} + +static void m2mops_vdec_unlock(void *m2m_priv) +{ + struct aml_vcodec_ctx *ctx = m2m_priv; + + aml_v4l2_debug(4, "[%d]", ctx->id); + mutex_unlock(&ctx->dev->dev_mutex); +} + +const struct v4l2_m2m_ops aml_vdec_m2m_ops = { + .device_run = m2mops_vdec_device_run, + .job_ready = m2mops_vdec_job_ready, + .job_abort = m2mops_vdec_job_abort, + .lock = m2mops_vdec_lock, + .unlock = m2mops_vdec_unlock, +}; + +static const struct vb2_ops aml_vdec_vb2_ops = { + .queue_setup = vb2ops_vdec_queue_setup, + .buf_prepare = vb2ops_vdec_buf_prepare, + .buf_queue = vb2ops_vdec_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = vb2ops_vdec_buf_init, + .buf_finish = vb2ops_vdec_buf_finish, + .start_streaming = vb2ops_vdec_start_streaming, + .stop_streaming = vb2ops_vdec_stop_streaming, +}; + +const struct v4l2_ioctl_ops aml_vdec_ioctl_ops = { + .vidioc_streamon = vidioc_decoder_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,//?? + //.vidioc_g_ctrl = vidioc_vdec_g_ctrl, + + .vidioc_qbuf = vidioc_vdec_qbuf, + .vidioc_dqbuf = vidioc_vdec_dqbuf, + + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + + .vidioc_s_fmt_vid_cap_mplane = vidioc_vdec_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vidioc_vdec_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vidioc_vdec_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_vdec_g_fmt, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + + .vidioc_enum_fmt_vid_cap_mplane = vidioc_vdec_enum_fmt_vid_cap_mplane, + .vidioc_enum_fmt_vid_out_mplane = vidioc_vdec_enum_fmt_vid_out_mplane, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + + .vidioc_querycap = vidioc_vdec_querycap, + .vidioc_subscribe_event = vidioc_vdec_subscribe_evt, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_selection = vidioc_vdec_g_selection, + .vidioc_s_selection = vidioc_vdec_s_selection, + + .vidioc_decoder_cmd = vidioc_decoder_cmd, + .vidioc_try_decoder_cmd = vidioc_try_decoder_cmd, +}; + +int aml_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct aml_vcodec_ctx *ctx = priv; + int ret = 0; + + aml_v4l2_debug(4, "[%d]", ctx->id); + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct aml_video_dec_buf); + src_vq->ops = &aml_vdec_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + + ret = vb2_queue_init(src_vq); + if (ret) { + aml_v4l2_err("Failed to initialize videobuf2 queue(output)"); + return ret; + } + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct aml_video_dec_buf); + dst_vq->ops = &aml_vdec_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + aml_v4l2_err("Failed to initialize videobuf2 queue(capture)"); + } + + return ret; +} + diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec.h new file mode 100644 index 000000000000..ae9f1e9338fa --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec.h @@ -0,0 +1,80 @@ +#ifndef _AML_VCODEC_DEC_H_ +#define _AML_VCODEC_DEC_H_ + +#include +#include +#include + +#define VCODEC_CAPABILITY_4K_DISABLED 0x10 +#define VCODEC_DEC_4K_CODED_WIDTH 4096U +#define VCODEC_DEC_4K_CODED_HEIGHT 2304U +#define AML_VDEC_MAX_W 2048U +#define AML_VDEC_MAX_H 1088U + +#define AML_VDEC_IRQ_STATUS_DEC_SUCCESS 0x10000 +#define V4L2_BUF_FLAG_LAST 0x00100000 + +/** + * struct vdec_fb - decoder frame buffer + * @base_y : Y plane memory info + * @base_c : C plane memory info + * @status : frame buffer status (vdec_fb_status) + */ +struct vdec_fb { + unsigned long vf_handle; + struct aml_vcodec_mem base_y; + struct aml_vcodec_mem base_c; + unsigned int status; +}; + +/** + * struct aml_video_dec_buf - Private data related to each VB2 buffer. + * @b: VB2 buffer + * @list: link list + * @used: Capture buffer contain decoded frame data and keep in + * codec data structure + * @ready_to_display: Capture buffer not display yet + * @queued_in_vb2: Capture buffer is queue in vb2 + * @queued_in_v4l2: Capture buffer is in v4l2 driver, but not in vb2 + * queue yet + * @lastframe: Intput buffer is last buffer - EOS + * @error: An unrecoverable error occurs on this buffer. + * @frame_buffer: Decode status, and buffer information of Capture buffer + * + * Note : These status information help us track and debug buffer state + */ +struct aml_video_dec_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + + struct vdec_fb frame_buffer; + struct codec_mm_s *mem[2]; + bool used; + bool ready_to_display; + bool queued_in_vb2; + bool queued_in_v4l2; + bool lastframe; + bool error; +}; + +extern const struct v4l2_ioctl_ops aml_vdec_ioctl_ops; +extern const struct v4l2_m2m_ops aml_vdec_m2m_ops; + + +/* + * aml_vdec_lock/aml_vdec_unlock are for ctx instance to + * get/release lock before/after access decoder hw. + * aml_vdec_lock get decoder hw lock and set curr_ctx + * to ctx instance that get lock + */ +void aml_vdec_unlock(struct aml_vcodec_ctx *ctx); +void aml_vdec_lock(struct aml_vcodec_ctx *ctx); +int aml_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +void aml_vcodec_dec_set_default_params(struct aml_vcodec_ctx *ctx); +void aml_vcodec_dec_release(struct aml_vcodec_ctx *ctx); +int aml_vcodec_dec_ctrls_setup(struct aml_vcodec_ctx *ctx); + +void vdec_device_vf_run(struct aml_vcodec_ctx *ctx); + +#endif /* _AML_VCODEC_DEC_H_ */ diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_drv.c b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_drv.c new file mode 100644 index 000000000000..fda351ddab7c --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_drv.c @@ -0,0 +1,332 @@ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aml_vcodec_drv.h" +#include "aml_vcodec_dec.h" +#include "aml_vcodec_dec_pm.h" +//#include "aml_vcodec_intr.h" +#include "aml_vcodec_util.h" +#include "aml_vcodec_vfm.h" + +#define VDEC_HW_ACTIVE 0x10 +#define VDEC_IRQ_CFG 0x11 +#define VDEC_IRQ_CLR 0x10 +#define VDEC_IRQ_CFG_REG 0xa4 + +module_param(aml_v4l2_dbg_level, int, 0644); +module_param(aml_vcodec_dbg, bool, 0644); + +static int fops_vcodec_open(struct file *file) +{ + struct aml_vcodec_dev *dev = video_drvdata(file); + struct aml_vcodec_ctx *ctx = NULL; + struct aml_video_dec_buf *aml_buf = NULL; + int ret = 0; + struct vb2_queue *src_vq; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + aml_buf = kzalloc(sizeof(*aml_buf), GFP_KERNEL); + if (!aml_buf) { + kfree(ctx); + return -ENOMEM; + } + + mutex_lock(&dev->dev_mutex); + ctx->empty_flush_buf = aml_buf; + ctx->id = dev->id_counter++; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + INIT_LIST_HEAD(&ctx->list); + dev->filp = file; + ctx->dev = dev; + init_waitqueue_head(&ctx->queue); + mutex_init(&ctx->lock); + sema_init(&ctx->sem, 1); + init_waitqueue_head(&ctx->wq); + + ctx->type = AML_INST_DECODER; + ret = aml_vcodec_dec_ctrls_setup(ctx); + if (ret) { + aml_v4l2_err("Failed to setup mt vcodec controls\n"); + goto err_ctrls_setup; + } + ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx, + &aml_vcodec_dec_queue_init); + if (IS_ERR((__force void *)ctx->m2m_ctx)) { + ret = PTR_ERR((__force void *)ctx->m2m_ctx); + aml_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)\n", ret); + goto err_m2m_ctx_init; + } + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + ctx->empty_flush_buf->vb.vb2_buf.vb2_queue = src_vq; + ctx->empty_flush_buf->lastframe = true; + aml_vcodec_dec_set_default_params(ctx); + + list_add(&ctx->list, &dev->ctx_list); + + mutex_unlock(&dev->dev_mutex); + pr_info("[%d] %s decoder\n", ctx->id, dev_name(&dev->plat_dev->dev)); + + return ret; + + /* Deinit when failure occurred */ +//err_load_fw: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_m2m_ctx_init: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); +err_ctrls_setup: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx->empty_flush_buf); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int fops_vcodec_release(struct file *file) +{ + struct aml_vcodec_dev *dev = video_drvdata(file); + struct aml_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + + pr_info("[%d] release decoder\n", ctx->id); + mutex_lock(&dev->dev_mutex); + + /* + * Call v4l2_m2m_ctx_release before aml_vcodec_dec_release. First, it + * makes sure the worker thread is not running after vdec_if_deinit. + * Second, the decoder will be flushed and all the buffers will be + * returned in stop_streaming. + */ + v4l2_m2m_ctx_release(ctx->m2m_ctx); + aml_vcodec_dec_release(ctx); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + + list_del_init(&ctx->list); + kfree(ctx->empty_flush_buf); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + return 0; +} + +static const struct v4l2_file_operations aml_vcodec_fops = { + .owner = THIS_MODULE, + .open = fops_vcodec_open, + .release = fops_vcodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int aml_vcodec_probe(struct platform_device *pdev) +{ + struct aml_vcodec_dev *dev; + struct video_device *vfd_dec; + int ret = 0; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->ctx_list); + dev->plat_dev = pdev; + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get mt vcodec clock source"); + return ret; + } + + mutex_init(&dev->dec_mutex); + mutex_init(&dev->dev_mutex); + spin_lock_init(&dev->irqlock); + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", + "[/AML_V4L2_VDEC]"); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + aml_v4l2_err("v4l2_device_register err=%d\n", ret); + goto err_res; + } + + init_waitqueue_head(&dev->queue); + + vfd_dec = video_device_alloc(); + if (!vfd_dec) { + aml_v4l2_err("Failed to allocate video device\n"); + ret = -ENOMEM; + goto err_dec_alloc; + } + + vfd_dec->fops = &aml_vcodec_fops; + vfd_dec->ioctl_ops = &aml_vdec_ioctl_ops; + vfd_dec->release = video_device_release; + vfd_dec->lock = &dev->dev_mutex; + vfd_dec->v4l2_dev = &dev->v4l2_dev; + vfd_dec->vfl_dir = VFL_DIR_M2M; + vfd_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | + V4L2_CAP_STREAMING; + + snprintf(vfd_dec->name, sizeof(vfd_dec->name), "%s", + AML_VCODEC_DEC_NAME); + video_set_drvdata(vfd_dec, dev); + dev->vfd_dec = vfd_dec; + platform_set_drvdata(pdev, dev); + + dev->m2m_dev_dec = v4l2_m2m_init(&aml_vdec_m2m_ops); + if (IS_ERR((__force void *)dev->m2m_dev_dec)) { + aml_v4l2_err("Failed to init mem2mem dec device\n"); + ret = PTR_ERR((__force void *)dev->m2m_dev_dec); + goto err_dec_mem_init; + } + + dev->decode_workqueue = + alloc_ordered_workqueue(AML_VCODEC_DEC_NAME, + WQ_MEM_RECLAIM | WQ_FREEZABLE); + if (!dev->decode_workqueue) { + aml_v4l2_err("Failed to create decode workqueue\n"); + ret = -EINVAL; + goto err_event_workq; + } + + dev->decode_workqueue_vf = + alloc_ordered_workqueue("aml-vcodec-dec-vf", + WQ_MEM_RECLAIM | WQ_FREEZABLE); + if (!dev->decode_workqueue_vf) { + aml_v4l2_err("Failed to create decode workqueue\n"); + ret = -EINVAL; + destroy_workqueue(dev->decode_workqueue); + goto err_event_workq; + } + + dev->reset_workqueue = + alloc_ordered_workqueue("aml-vcodec-reset", + WQ_MEM_RECLAIM | WQ_FREEZABLE); + if (!dev->decode_workqueue_vf) { + aml_v4l2_err("Failed to create decode workqueue\n"); + ret = -EINVAL; + destroy_workqueue(dev->decode_workqueue_vf); + destroy_workqueue(dev->decode_workqueue); + goto err_event_workq; + } + + //dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); + + ret = video_register_device(vfd_dec, VFL_TYPE_GRABBER, 26); + if (ret) { + pr_err("Failed to register video device\n"); + goto err_dec_reg; + } + + pr_info("decoder registered as /dev/video%d\n", vfd_dec->num); + + return 0; + +err_dec_reg: + destroy_workqueue(dev->reset_workqueue); + destroy_workqueue(dev->decode_workqueue_vf); + destroy_workqueue(dev->decode_workqueue); +err_event_workq: + v4l2_m2m_release(dev->m2m_dev_dec); +err_dec_mem_init: + video_unregister_device(vfd_dec); +err_dec_alloc: + v4l2_device_unregister(&dev->v4l2_dev); +err_res: + + return ret; +} + +static const struct of_device_id aml_vcodec_match[] = { + {.compatible = "amlogic, vcodec-dec",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, aml_vcodec_match); + +static int aml_vcodec_dec_remove(struct platform_device *pdev) +{ + struct aml_vcodec_dev *dev = platform_get_drvdata(pdev); + + flush_workqueue(dev->decode_workqueue_vf); + destroy_workqueue(dev->decode_workqueue_vf); + + flush_workqueue(dev->decode_workqueue); + destroy_workqueue(dev->decode_workqueue); + + if (dev->m2m_dev_dec) + v4l2_m2m_release(dev->m2m_dev_dec); + + if (dev->vfd_dec) + video_unregister_device(dev->vfd_dec); + + v4l2_device_unregister(&dev->v4l2_dev); + + return 0; +} + +/*static void aml_vcodec_dev_release(struct device *dev) +{ +}*/ + +static struct platform_driver aml_vcodec_dec_driver = { + .probe = aml_vcodec_probe, + .remove = aml_vcodec_dec_remove, + .driver = { + .name = AML_VCODEC_DEC_NAME, + .of_match_table = aml_vcodec_match, + }, +}; + +/* +static struct platform_device aml_vcodec_dec_device = { + .name = AML_VCODEC_DEC_NAME, + .dev.release = aml_vcodec_dev_release, +};*/ + +module_platform_driver(aml_vcodec_dec_driver); + +/* +static int __init amvdec_ports_init(void) +{ + int ret; + + ret = platform_device_register(&aml_vcodec_dec_device); + if (ret) + return ret; + + ret = platform_driver_register(&aml_vcodec_dec_driver); + if (ret) + platform_device_unregister(&aml_vcodec_dec_device); + + return ret; +} + +static void __exit amvdec_ports_exit(void) +{ + platform_driver_unregister(&aml_vcodec_dec_driver); + platform_device_unregister(&aml_vcodec_dec_device); +} + +module_init(amvdec_ports_init); +module_exit(amvdec_ports_exit); +*/ + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("AML video codec V4L2 decoder driver"); diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_pm.c b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_pm.c new file mode 100644 index 000000000000..7b155d0cd2d0 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_pm.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include + +#include "aml_vcodec_dec_pm.h" +#include "aml_vcodec_util.h" +//#include "aml_vpu.h" + +int aml_vcodec_init_dec_pm(struct aml_vcodec_dev *amldev) +{ + struct device_node *node; + struct platform_device *pdev; + struct aml_vcodec_pm *pm; + int ret = 0; + + pdev = amldev->plat_dev; + pm = &amldev->pm; + pm->amldev = amldev; + node = of_parse_phandle(pdev->dev.of_node, "larb", 0); + if (!node) { + aml_v4l2_err("of_parse_phandle larb fail!"); + return -1; + } + + pdev = of_find_device_by_node(node); + if (WARN_ON(!pdev)) { + of_node_put(node); + return -1; + } + pm->larbvdec = &pdev->dev; + pdev = amldev->plat_dev; + pm->dev = &pdev->dev; + + pm->vcodecpll = devm_clk_get(&pdev->dev, "vcodecpll"); + if (IS_ERR(pm->vcodecpll)) { + aml_v4l2_err("devm_clk_get vcodecpll fail"); + ret = PTR_ERR(pm->vcodecpll); + } + + pm->univpll_d2 = devm_clk_get(&pdev->dev, "univpll_d2"); + if (IS_ERR(pm->univpll_d2)) { + aml_v4l2_err("devm_clk_get univpll_d2 fail"); + ret = PTR_ERR(pm->univpll_d2); + } + + pm->clk_cci400_sel = devm_clk_get(&pdev->dev, "clk_cci400_sel"); + if (IS_ERR(pm->clk_cci400_sel)) { + aml_v4l2_err("devm_clk_get clk_cci400_sel fail"); + ret = PTR_ERR(pm->clk_cci400_sel); + } + + pm->vdec_sel = devm_clk_get(&pdev->dev, "vdec_sel"); + if (IS_ERR(pm->vdec_sel)) { + aml_v4l2_err("devm_clk_get vdec_sel fail"); + ret = PTR_ERR(pm->vdec_sel); + } + + pm->vdecpll = devm_clk_get(&pdev->dev, "vdecpll"); + if (IS_ERR(pm->vdecpll)) { + aml_v4l2_err("devm_clk_get vdecpll fail"); + ret = PTR_ERR(pm->vdecpll); + } + + pm->vencpll = devm_clk_get(&pdev->dev, "vencpll"); + if (IS_ERR(pm->vencpll)) { + aml_v4l2_err("devm_clk_get vencpll fail"); + ret = PTR_ERR(pm->vencpll); + } + + pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); + if (IS_ERR(pm->venc_lt_sel)) { + aml_v4l2_err("devm_clk_get venc_lt_sel fail"); + ret = PTR_ERR(pm->venc_lt_sel); + } + + pm->vdec_bus_clk_src = devm_clk_get(&pdev->dev, "vdec_bus_clk_src"); + if (IS_ERR(pm->vdec_bus_clk_src)) { + aml_v4l2_err("devm_clk_get vdec_bus_clk_src"); + ret = PTR_ERR(pm->vdec_bus_clk_src); + } + + pm_runtime_enable(&pdev->dev); + + return ret; +} + +void aml_vcodec_release_dec_pm(struct aml_vcodec_dev *dev) +{ + pm_runtime_disable(dev->pm.dev); +} + +void aml_vcodec_dec_pw_on(struct aml_vcodec_pm *pm) +{ + int ret; + + ret = pm_runtime_get_sync(pm->dev); + if (ret) + aml_v4l2_err("pm_runtime_get_sync fail %d", ret); +} + +void aml_vcodec_dec_pw_off(struct aml_vcodec_pm *pm) +{ + int ret; + + ret = pm_runtime_put_sync(pm->dev); + if (ret) + aml_v4l2_err("pm_runtime_put_sync fail %d", ret); +} + +void aml_vcodec_dec_clock_on(struct aml_vcodec_pm *pm) +{ + int ret; + + ret = clk_set_rate(pm->vcodecpll, 1482 * 1000000); + if (ret) + aml_v4l2_err("clk_set_rate vcodecpll fail %d", ret); + + ret = clk_set_rate(pm->vencpll, 800 * 1000000); + if (ret) + aml_v4l2_err("clk_set_rate vencpll fail %d", ret); + + ret = clk_prepare_enable(pm->vcodecpll); + if (ret) + aml_v4l2_err("clk_prepare_enable vcodecpll fail %d", ret); + + ret = clk_prepare_enable(pm->vencpll); + if (ret) + aml_v4l2_err("clk_prepare_enable vencpll fail %d", ret); + + ret = clk_prepare_enable(pm->vdec_bus_clk_src); + if (ret) + aml_v4l2_err("clk_prepare_enable vdec_bus_clk_src fail %d", + ret); + + ret = clk_prepare_enable(pm->venc_lt_sel); + if (ret) + aml_v4l2_err("clk_prepare_enable venc_lt_sel fail %d", ret); + + ret = clk_set_parent(pm->venc_lt_sel, pm->vdec_bus_clk_src); + if (ret) + aml_v4l2_err("clk_set_parent venc_lt_sel vdec_bus_clk_src fail %d", + ret); + + ret = clk_prepare_enable(pm->univpll_d2); + if (ret) + aml_v4l2_err("clk_prepare_enable univpll_d2 fail %d", ret); + + ret = clk_prepare_enable(pm->clk_cci400_sel); + if (ret) + aml_v4l2_err("clk_prepare_enable clk_cci400_sel fail %d", ret); + + ret = clk_set_parent(pm->clk_cci400_sel, pm->univpll_d2); + if (ret) + aml_v4l2_err("clk_set_parent clk_cci400_sel univpll_d2 fail %d", + ret); + + ret = clk_prepare_enable(pm->vdecpll); + if (ret) + aml_v4l2_err("clk_prepare_enable vdecpll fail %d", ret); + + ret = clk_prepare_enable(pm->vdec_sel); + if (ret) + aml_v4l2_err("clk_prepare_enable vdec_sel fail %d", ret); + + ret = clk_set_parent(pm->vdec_sel, pm->vdecpll); + if (ret) + aml_v4l2_err("clk_set_parent vdec_sel vdecpll fail %d", ret); + + //ret = aml_smi_larb_get(pm->larbvdec); + if (ret) + aml_v4l2_err("aml_smi_larb_get larbvdec fail %d", ret); + +} + +void aml_vcodec_dec_clock_off(struct aml_vcodec_pm *pm) +{ + //aml_smi_larb_put(pm->larbvdec); + clk_disable_unprepare(pm->vdec_sel); + clk_disable_unprepare(pm->vdecpll); + clk_disable_unprepare(pm->univpll_d2); + clk_disable_unprepare(pm->clk_cci400_sel); + clk_disable_unprepare(pm->venc_lt_sel); + clk_disable_unprepare(pm->vdec_bus_clk_src); + clk_disable_unprepare(pm->vencpll); + clk_disable_unprepare(pm->vcodecpll); +} diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_pm.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_pm.h new file mode 100644 index 000000000000..ec277ddc3391 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_dec_pm.h @@ -0,0 +1,15 @@ + +#ifndef _AML_VCODEC_DEC_PM_H_ +#define _AML_VCODEC_DEC_PM_H_ + +#include "aml_vcodec_drv.h" + +int aml_vcodec_init_dec_pm(struct aml_vcodec_dev *dev); +void aml_vcodec_release_dec_pm(struct aml_vcodec_dev *dev); + +void aml_vcodec_dec_pw_on(struct aml_vcodec_pm *pm); +void aml_vcodec_dec_pw_off(struct aml_vcodec_pm *pm); +void aml_vcodec_dec_clock_on(struct aml_vcodec_pm *pm); +void aml_vcodec_dec_clock_off(struct aml_vcodec_pm *pm); + +#endif /* _AML_VCODEC_DEC_PM_H_ */ diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_drv.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_drv.h new file mode 100644 index 000000000000..4b9573091c84 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_drv.h @@ -0,0 +1,385 @@ +#ifndef _AML_VCODEC_DRV_H_ +#define _AML_VCODEC_DRV_H_ + +#include +#include +#include +#include +#include +#include +#include "aml_vcodec_util.h" + +#define AML_VCODEC_DRV_NAME "aml_vcodec_drv" +#define AML_VCODEC_DEC_NAME "aml-vcodec-dec" +#define AML_VCODEC_ENC_NAME "aml-vcodec-enc" +#define AML_PLATFORM_STR "platform:mt8173" + +#define AML_VCODEC_MAX_PLANES 3 +#define AML_V4L2_BENCHMARK 0 +#define WAIT_INTR_TIMEOUT_MS 1000 + +/** + * enum aml_hw_reg_idx - AML hw register base index + */ +enum aml_hw_reg_idx { + VDEC_SYS, + VDEC_MISC, + VDEC_LD, + VDEC_TOP, + VDEC_CM, + VDEC_AD, + VDEC_AV, + VDEC_PP, + VDEC_HWD, + VDEC_HWQ, + VDEC_HWB, + VDEC_HWG, + NUM_MAX_VDEC_REG_BASE, + /* h264 encoder */ + VENC_SYS = NUM_MAX_VDEC_REG_BASE, + /* vp8 encoder */ + VENC_LT_SYS, + NUM_MAX_VCODEC_REG_BASE +}; + +/** + * enum aml_instance_type - The type of an AML Vcodec instance. + */ +enum aml_instance_type { + AML_INST_DECODER = 0, + AML_INST_ENCODER = 1, +}; + +/** + * enum aml_instance_state - The state of an AML Vcodec instance. + * @AML_STATE_FREE - default state when instance is created + * @AML_STATE_INIT - vcodec instance is initialized + * @AML_STATE_HEADER - vdec had sps/pps header parsed or venc + * had sps/pps header encoded + * @AML_STATE_FLUSH - vdec is flushing. Only used by decoder + * @AML_STATE_ABORT - vcodec should be aborted + */ +enum aml_instance_state { + AML_STATE_FREE = 0, + AML_STATE_INIT = 1, + AML_STATE_HEADER = 2, + AML_STATE_FLUSH = 3, + AML_STATE_ABORT = 4, + AML_STATE_RESET = 5, +}; + +/** + * struct aml_encode_param - General encoding parameters type + */ +enum aml_encode_param { + AML_ENCODE_PARAM_NONE = 0, + AML_ENCODE_PARAM_BITRATE = (1 << 0), + AML_ENCODE_PARAM_FRAMERATE = (1 << 1), + AML_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), + AML_ENCODE_PARAM_FORCE_INTRA = (1 << 3), + AML_ENCODE_PARAM_GOP_SIZE = (1 << 4), +}; + +enum aml_fmt_type { + AML_FMT_DEC = 0, + AML_FMT_ENC = 1, + AML_FMT_FRAME = 2, +}; + +/** + * struct aml_video_fmt - Structure used to store information about pixelformats + */ +struct aml_video_fmt { + u32 fourcc; + enum aml_fmt_type type; + u32 num_planes; +}; + +/** + * struct aml_codec_framesizes - Structure used to store information about + * framesizes + */ +struct aml_codec_framesizes { + u32 fourcc; + struct v4l2_frmsize_stepwise stepwise; +}; + +/** + * struct aml_q_type - Type of queue + */ +enum aml_q_type { + AML_Q_DATA_SRC = 0, + AML_Q_DATA_DST = 1, +}; + +/** + * struct aml_q_data - Structure used to store information about queue + */ +struct aml_q_data { + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + enum v4l2_field field; + unsigned int bytesperline[AML_VCODEC_MAX_PLANES]; + unsigned int sizeimage[AML_VCODEC_MAX_PLANES]; + struct aml_video_fmt *fmt; +}; + +/** + * struct aml_enc_params - General encoding parameters + * @bitrate: target bitrate in bits per second + * @num_b_frame: number of b frames between p-frame + * @rc_frame: frame based rate control + * @rc_mb: macroblock based rate control + * @seq_hdr_mode: H.264 sequence header is encoded separately or joined + * with the first frame + * @intra_period: I frame period + * @gop_size: group of picture size, it's used as the intra frame period + * @framerate_num: frame rate numerator. ex: framerate_num=30 and + * framerate_denom=1 menas FPS is 30 + * @framerate_denom: frame rate denominator. ex: framerate_num=30 and + * framerate_denom=1 menas FPS is 30 + * @h264_max_qp: Max value for H.264 quantization parameter + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @force_intra: force/insert intra frame + */ +struct aml_enc_params { + unsigned int bitrate; + unsigned int num_b_frame; + unsigned int rc_frame; + unsigned int rc_mb; + unsigned int seq_hdr_mode; + unsigned int intra_period; + unsigned int gop_size; + unsigned int framerate_num; + unsigned int framerate_denom; + unsigned int h264_max_qp; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int force_intra; +}; + +/** + * struct aml_vcodec_pm - Power management data structure + */ +struct aml_vcodec_pm { + struct clk *vdec_bus_clk_src; + struct clk *vencpll; + + struct clk *vcodecpll; + struct clk *univpll_d2; + struct clk *clk_cci400_sel; + struct clk *vdecpll; + struct clk *vdec_sel; + struct clk *vencpll_d2; + struct clk *venc_sel; + struct clk *univpll1_d2; + struct clk *venc_lt_sel; + struct device *larbvdec; + struct device *larbvenc; + struct device *larbvenclt; + struct device *dev; + struct aml_vcodec_dev *amldev; +}; + +/** + * struct vdec_pic_info - picture size information + * @pic_w: picture width + * @pic_h: picture height + * @buf_w: picture buffer width (64 aligned up from pic_w) + * @buf_h: picture buffer heiht (64 aligned up from pic_h) + * @y_bs_sz: Y bitstream size + * @c_bs_sz: CbCr bitstream size + * @y_len_sz: additional size required to store decompress information for y + * plane + * @c_len_sz: additional size required to store decompress information for cbcr + * plane + * E.g. suppose picture size is 176x144, + * buffer size will be aligned to 176x160. + */ +struct vdec_pic_info { + unsigned int pic_w; + unsigned int pic_h; + unsigned int buf_w; + unsigned int buf_h; + unsigned int y_bs_sz; + unsigned int c_bs_sz; + unsigned int y_len_sz; + unsigned int c_len_sz; +}; + +/** + * struct aml_vcodec_ctx - Context (instance) private data. + * + * @type: type of the instance - decoder or encoder + * @dev: pointer to the aml_vcodec_dev of the device + * @list: link to ctx_list of aml_vcodec_dev + * @fh: struct v4l2_fh + * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context + * @q_data: store information of input and output queue + * of the context + * @id: index of the context that this structure describes + * @state: state of the context + * @param_change: indicate encode parameter type + * @enc_params: encoding parameters + * @dec_if: hooked decoder driver interface + * @enc_if: hoooked encoder driver interface + * @drv_handle: driver handle for specific decode/encode instance + * + * @picinfo: store picture info after header parsing + * @dpb_size: store dpb count after header parsing + * @int_cond: variable used by the waitqueue + * @int_type: type of the last interrupt + * @queue: waitqueue that can be used to wait for this context to + * finish + * @irq_status: irq status + * + * @ctrl_hdl: handler for v4l2 framework + * @decode_work: worker for the decoding + * @encode_work: worker for the encoding + * @last_decoded_picinfo: pic information get from latest decode + * @empty_flush_buf: a fake size-0 capture buffer that indicates flush + * + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @quantization: enum v4l2_quantization, colorspace quantization + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * @lock: protect variables accessed by V4L2 threads and worker thread such as + * aml_video_dec_buf. + */ +struct aml_vcodec_ctx { + enum aml_instance_type type; + struct aml_vcodec_dev *dev; + struct list_head list; + + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct aml_q_data q_data[2]; + int id; + enum aml_instance_state state; + enum aml_encode_param param_change; + struct aml_enc_params enc_params; + + const struct vdec_common_if *dec_if; + const struct venc_common_if *enc_if; + unsigned long drv_handle; + + struct vdec_pic_info picinfo; + int dpb_size; + + int int_cond; + int int_type; + wait_queue_head_t queue; + unsigned int irq_status; + + struct v4l2_ctrl_handler ctrl_hdl; + struct work_struct decode_work; + struct work_struct decode_work_vf; + struct work_struct encode_work; + struct work_struct reset_work; + struct vdec_pic_info last_decoded_picinfo; + struct aml_video_dec_buf *empty_flush_buf; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + int decoded_frame_cnt; + struct mutex lock; + struct semaphore sem; + wait_queue_head_t wq; + bool has_receive_eos; + +}; + +/** + * struct aml_vcodec_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_dec: Video device for decoder + * @vfd_enc: Video device for encoder. + * + * @m2m_dev_dec: m2m device for decoder + * @m2m_dev_enc: m2m device for encoder. + * @plat_dev: platform device + * @vpu_plat_dev: aml vpu platform device + * @alloc_ctx: VB2 allocator context + * (for allocations without kernel mapping). + * @ctx_list: list of struct aml_vcodec_ctx + * @irqlock: protect data access by irq handler and work thread + * @curr_ctx: The context that is waiting for codec hardware + * + * @reg_base: Mapped address of AML Vcodec registers. + * + * @id_counter: used to identify current opened instance + * + * @encode_workqueue: encode work queue + * + * @int_cond: used to identify interrupt condition happen + * @int_type: used to identify what kind of interrupt condition happen + * @dev_mutex: video_device lock + * @queue: waitqueue for waiting for completion of device commands + * + * @dec_irq: decoder irq resource + * @enc_irq: h264 encoder irq resource + * @enc_lt_irq: vp8 encoder irq resource + * + * @dec_mutex: decoder hardware lock + * @enc_mutex: encoder hardware lock. + * + * @pm: power management control + * @dec_capability: used to identify decode capability, ex: 4k + * @enc_capability: used to identify encode capability + */ +struct aml_vcodec_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_dec; + struct video_device *vfd_enc; + struct file *filp; + + struct v4l2_m2m_dev *m2m_dev_dec; + struct v4l2_m2m_dev *m2m_dev_enc; + struct platform_device *plat_dev; + struct platform_device *vpu_plat_dev;//?? + struct vb2_alloc_ctx *alloc_ctx;//?? + struct list_head ctx_list; + spinlock_t irqlock; + struct aml_vcodec_ctx *curr_ctx; + void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + + unsigned long id_counter; + + struct workqueue_struct *decode_workqueue; + struct workqueue_struct *decode_workqueue_vf; + struct workqueue_struct *encode_workqueue; + struct workqueue_struct *reset_workqueue; + int int_cond; + int int_type; + struct mutex dev_mutex; + wait_queue_head_t queue; + + int dec_irq; + int enc_irq; + int enc_lt_irq; + + struct mutex dec_mutex; + struct mutex enc_mutex; + + struct aml_vcodec_pm pm; + unsigned int dec_capability; + unsigned int enc_capability; +}; + +static inline struct aml_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct aml_vcodec_ctx, fh); +} + +static inline struct aml_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct aml_vcodec_ctx, ctrl_hdl); +} + +#endif /* _AML_VCODEC_DRV_H_ */ diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_util.c b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_util.c new file mode 100644 index 000000000000..b30dd2b051ea --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_util.c @@ -0,0 +1,103 @@ +#include + +#include "aml_vcodec_drv.h" +#include "aml_vcodec_util.h" +//#include "aml_vpu.h" + +/* For encoder, this will enable logs in venc/*/ +bool aml_vcodec_dbg; +EXPORT_SYMBOL(aml_vcodec_dbg); + +/* The log level of v4l2 encoder or decoder driver. + * That is, files under aml-vcodec/. + */ +int aml_v4l2_dbg_level; +EXPORT_SYMBOL(aml_v4l2_dbg_level); + +void __iomem *aml_vcodec_get_reg_addr(struct aml_vcodec_ctx *data, + unsigned int reg_idx) +{ + struct aml_vcodec_ctx *ctx = (struct aml_vcodec_ctx *)data; + + if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) { + aml_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); + return NULL; + } + return ctx->dev->reg_base[reg_idx]; +} +EXPORT_SYMBOL(aml_vcodec_get_reg_addr); + +int aml_vcodec_mem_alloc(struct aml_vcodec_ctx *data, + struct aml_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct aml_vcodec_ctx *ctx = (struct aml_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + //mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); + mem->va = codec_mm_dma_alloc_coherent(dev_name(dev), size, + &mem->dma_addr, GFP_KERNEL, 0); + if (!mem->va) { + aml_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), + size); + return -ENOMEM; + } + + memset(mem->va, 0, size); + + aml_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + aml_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + aml_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + + return 0; +} +EXPORT_SYMBOL(aml_vcodec_mem_alloc); + +void aml_vcodec_mem_free(struct aml_vcodec_ctx *data, + struct aml_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct aml_vcodec_ctx *ctx = (struct aml_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + if (!mem->va) { + aml_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), + size); + return; + } + + aml_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + aml_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + aml_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + + dma_free_coherent(dev, size, mem->va, mem->dma_addr); + mem->va = NULL; + mem->dma_addr = 0; + mem->size = 0; +} +EXPORT_SYMBOL(aml_vcodec_mem_free); + +void aml_vcodec_set_curr_ctx(struct aml_vcodec_dev *dev, + struct aml_vcodec_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->irqlock, flags); + dev->curr_ctx = ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); +} +EXPORT_SYMBOL(aml_vcodec_set_curr_ctx); + +struct aml_vcodec_ctx *aml_vcodec_get_curr_ctx(struct aml_vcodec_dev *dev) +{ + unsigned long flags; + struct aml_vcodec_ctx *ctx; + + spin_lock_irqsave(&dev->irqlock, flags); + ctx = dev->curr_ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); + return ctx; +} +EXPORT_SYMBOL(aml_vcodec_get_curr_ctx); diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_util.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_util.h new file mode 100644 index 000000000000..861c8e6a0286 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_util.h @@ -0,0 +1,77 @@ +#ifndef _AML_VCODEC_UTIL_H_ +#define _AML_VCODEC_UTIL_H_ + +#include +#include +#include + +#define DEBUG + +struct aml_vcodec_mem { + size_t size; + void *va; + dma_addr_t dma_addr; + unsigned int bytes_used; +}; + +struct aml_vcodec_ctx; +struct aml_vcodec_dev; + +extern int aml_v4l2_dbg_level; +extern bool aml_vcodec_dbg; + + +#if defined(DEBUG) + +#define aml_v4l2_debug(level, fmt, args...) \ + do { \ + if (aml_v4l2_dbg_level >= level) \ + pr_info("[AML_V4L2] level=%d %s(),%d: " fmt "\n",\ + level, __func__, __LINE__, ##args); \ + } while (0) + +#define aml_v4l2_debug_enter() aml_v4l2_debug(3, "+") +#define aml_v4l2_debug_leave() aml_v4l2_debug(3, "-") + +#define aml_vcodec_debug(h, fmt, args...) \ + do { \ + if (aml_vcodec_dbg) \ + pr_info("[AML_VCODEC][%d]: %s() " fmt "\n", \ + ((struct aml_vcodec_ctx *)h->ctx)->id, \ + __func__, ##args); \ + } while (0) + +#define aml_vcodec_debug_enter(h) aml_vcodec_debug(h, "+") +#define aml_vcodec_debug_leave(h) aml_vcodec_debug(h, "-") + +#else + +#define aml_v4l2_debug(level, fmt, args...) +#define aml_v4l2_debug_enter() +#define aml_v4l2_debug_leave() + +#define aml_vcodec_debug(h, fmt, args...) +#define aml_vcodec_debug_enter(h) +#define aml_vcodec_debug_leave(h) + +#endif + +#define aml_v4l2_err(fmt, args...) \ + pr_err("[AML_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \ + ##args) + +#define aml_vcodec_err(h, fmt, args...) \ + pr_err("[AML_VCODEC][ERROR][%d]: %s() " fmt "\n", \ + ((struct aml_vcodec_ctx *)h->ctx)->id, __func__, ##args) + +void __iomem *aml_vcodec_get_reg_addr(struct aml_vcodec_ctx *data, + unsigned int reg_idx); +int aml_vcodec_mem_alloc(struct aml_vcodec_ctx *data, + struct aml_vcodec_mem *mem); +void aml_vcodec_mem_free(struct aml_vcodec_ctx *data, + struct aml_vcodec_mem *mem); +void aml_vcodec_set_curr_ctx(struct aml_vcodec_dev *dev, + struct aml_vcodec_ctx *ctx); +struct aml_vcodec_ctx *aml_vcodec_get_curr_ctx(struct aml_vcodec_dev *dev); + +#endif /* _AML_VCODEC_UTIL_H_ */ diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.c b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.c new file mode 100644 index 000000000000..0ee4be4d3f75 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.c @@ -0,0 +1,179 @@ +#include "aml_vcodec_vfm.h" +#include "aml_vcodec_vfq.h" +#include "aml_vcodec_util.h" +#include + +#define RECEIVER_NAME "v4l2-video" +#define PROVIDER_NAME "v4l2-video" + +static struct vframe_s *vdec_vf_peek(void *op_arg) +{ + struct vcodec_vfm_s *vfm = (struct vcodec_vfm_s *)op_arg; + + return vfq_peek(&vfm->vf_que); +} + +static struct vframe_s *vdec_vf_get(void *op_arg) +{ + struct vcodec_vfm_s *vfm = (struct vcodec_vfm_s *)op_arg; + + return vfq_pop(&vfm->vf_que); +} + +static void vdec_vf_put(struct vframe_s *vf, void *op_arg) +{ + struct vcodec_vfm_s *vfm = (struct vcodec_vfm_s *)op_arg; + + vf_put(vf, vfm->recv_name); + vf_notify_provider(vfm->recv_name, VFRAME_EVENT_RECEIVER_PUT, NULL); +} + +static int vdec_event_cb(int type, void *data, void *private_data) +{ + + if (type & VFRAME_EVENT_RECEIVER_PUT) { + } else if (type & VFRAME_EVENT_RECEIVER_GET) { + } else if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT) { + } + return 0; +} + +static int vdec_vf_states(struct vframe_states *states, void *op_arg) +{ + struct vcodec_vfm_s *vfm = (struct vcodec_vfm_s *)op_arg; + + states->vf_pool_size = POOL_SIZE; + states->buf_recycle_num = 0; + states->buf_free_num = POOL_SIZE - vfq_level(&vfm->vf_que); + states->buf_avail_num = vfq_level(&vfm->vf_que); + + return 0; +} + +void video_vf_put(struct vdec_fb *fb) +{ + struct vframe_provider_s *vfp = vf_get_provider(RECEIVER_NAME); + struct vframe_s *vf = (struct vframe_s *)fb->vf_handle; + + aml_v4l2_debug(4, "%s() [%d], vfp: %p, vf: %p, cnt: %d\n", + __FUNCTION__, __LINE__, vfp, vf, atomic_read(&vf->use_cnt)); + + if (vfp && vf && atomic_dec_and_test(&vf->use_cnt)) + vf_put(vf, RECEIVER_NAME); +} + +static const struct vframe_operations_s vf_provider = { + .peek = vdec_vf_peek, + .get = vdec_vf_get, + .put = vdec_vf_put, + .event_cb = vdec_event_cb, + .vf_states = vdec_vf_states, +}; + +static int video_receiver_event_fun(int type, void *data, void *private_data) +{ + int ret = 0; + struct vframe_states states; + struct vcodec_vfm_s *vfm = (struct vcodec_vfm_s *)private_data; + + aml_v4l2_debug(4, "%s() [%d], type: %d, vfm: %p\n", + __FUNCTION__, __LINE__, type, vfm); + + switch (type) { + case VFRAME_EVENT_PROVIDER_UNREG: { + if (vf_get_receiver(vfm->prov_name)) { + aml_v4l2_debug(4, "%s() [%d] unreg %s provider.\n", + __FUNCTION__, __LINE__, vfm->prov_name); + vf_unreg_provider(&vfm->vf_prov); + } + + vfq_init(&vfm->vf_que, POOL_SIZE + 1, &vfm->pool[0]); + + break; + } + + case VFRAME_EVENT_PROVIDER_START: { + if (vf_get_receiver(vfm->prov_name)) { + aml_v4l2_debug(4, "%s() [%d] reg %s provider.\n", + __FUNCTION__, __LINE__, vfm->prov_name); + vf_provider_init(&vfm->vf_prov, vfm->prov_name, + &vf_provider, vfm); + vf_reg_provider(&vfm->vf_prov); + vf_notify_receiver(vfm->prov_name, + VFRAME_EVENT_PROVIDER_START, NULL); + } + + vfq_init(&vfm->vf_que, POOL_SIZE + 1, &vfm->pool[0]); + + break; + } + + case VFRAME_EVENT_PROVIDER_QUREY_STATE: { + vdec_vf_states(&states, vfm); + if (states.buf_avail_num > 0) + ret = RECEIVER_ACTIVE; + break; + } + + case VFRAME_EVENT_PROVIDER_VFRAME_READY: { + if (vfq_level(&vfm->vf_que) > POOL_SIZE - 1) + ret = -1; + + if (!vf_peek(vfm->recv_name)) + ret = -1; + + vfm->vf = vf_get(vfm->recv_name); + if (!vfm->vf) + ret = -1; + + vfq_push(&vfm->vf_que, vfm->vf); + + /*vf_notify_receiver(vfm->prov_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);*/ + + /* schedule capture work. */ + vdec_device_vf_run(vfm->ctx); + aml_v4l2_debug(1, "%s() [%d] VFRAME_EVENT_PROVIDER_VFRAME_READY.\n", + __FUNCTION__, __LINE__); + + break; + } + + default: + aml_v4l2_debug(4, "the vf event is %d .", type); + } + + return ret; +} + +static const struct vframe_receiver_op_s vf_receiver = { + .event_cb = video_receiver_event_fun +}; + +struct vframe_s *peek_video_frame(struct vcodec_vfm_s *vfm) +{ + return vfq_peek(&vfm->vf_que); +} + +struct vframe_s *get_video_frame(struct vcodec_vfm_s *vfm) +{ + return vfq_pop(&vfm->vf_que); +} + +int vcodec_vfm_init(struct vcodec_vfm_s *vfm) +{ + memcpy(vfm->recv_name, RECEIVER_NAME, sizeof(RECEIVER_NAME)); + memcpy(vfm->prov_name, PROVIDER_NAME, sizeof(PROVIDER_NAME)); + + vf_receiver_init(&vfm->vf_recv, vfm->recv_name, &vf_receiver, vfm); + vf_reg_receiver(&vfm->vf_recv); + + return 0; +} + +void vcodec_vfm_release(struct vcodec_vfm_s *vfm) +{ + vf_unreg_receiver(&vfm->vf_recv); + vf_unreg_provider(&vfm->vf_prov); +} + diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.h new file mode 100644 index 000000000000..a47916c36aea --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfm.h @@ -0,0 +1,37 @@ +#ifndef __AML_VCODEC_VFM_H_ +#define __AML_VCODEC_VFM_H_ + +#include "aml_vcodec_vfq.h" +#include "aml_vcodec_drv.h" +#include "aml_vcodec_dec.h" +#include +#include + +#define VF_NAME_SIZE (32) +#define POOL_SIZE (16) + +struct vcodec_vfm_s { + struct aml_vcodec_ctx *ctx; + struct vfq_s vf_que; + struct vframe_s *vf; + struct vframe_s *pool[POOL_SIZE + 1]; + char recv_name[VF_NAME_SIZE]; + char prov_name[VF_NAME_SIZE]; + struct vframe_provider_s vf_prov; + struct vframe_receiver_s vf_recv; +}; + +int vcodec_vfm_init(struct vcodec_vfm_s *vfm); + +void vcodec_vfm_release(struct vcodec_vfm_s *vfm); + +struct vframe_s *peek_video_frame(struct vcodec_vfm_s *vfm); + +struct vframe_s *get_video_frame(struct vcodec_vfm_s *vfm); + +int get_fb_from_queue(struct aml_vcodec_ctx *ctx, struct vdec_fb **out_fb); +int put_fb_to_queue(struct aml_vcodec_ctx *ctx, struct vdec_fb *in_fb); + +void video_vf_put(struct vdec_fb *fb); + +#endif /* __AML_VCODEC_VFM_H_ */ diff --git a/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfq.h b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfq.h new file mode 100644 index 000000000000..2d44b3009c9d --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/aml_vcodec_vfq.h @@ -0,0 +1,93 @@ +#ifndef __AML_VCODEC_VFQ_H_ +#define __AML_VCODEC_VFQ_H_ + +#include +#include + +struct vfq_s { + int rp; + int wp; + int size; + int pre_rp; + int pre_wp; + struct vframe_s **pool; +}; + +static inline void vfq_lookup_start(struct vfq_s *q) +{ + q->pre_rp = q->rp; + q->pre_wp = q->wp; +} +static inline void vfq_lookup_end(struct vfq_s *q) +{ + q->rp = q->pre_rp; + q->wp = q->pre_wp; +} + +static inline void vfq_init(struct vfq_s *q, u32 size, struct vframe_s **pool) +{ + q->rp = q->wp = 0; + q->size = size; + q->pool = pool; +} + +static inline bool vfq_empty(struct vfq_s *q) +{ + return q->rp == q->wp; +} + +static inline void vfq_push(struct vfq_s *q, struct vframe_s *vf) +{ + int wp = q->wp; + + /*ToDo*/ + smp_mb(); + + q->pool[wp] = vf; + + /*ToDo*/ + smp_wmb(); + + q->wp = (wp == (q->size - 1)) ? 0 : (wp + 1); +} + +static inline struct vframe_s *vfq_pop(struct vfq_s *q) +{ + struct vframe_s *vf; + int rp; + + if (vfq_empty(q)) + return NULL; + + rp = q->rp; + + /*ToDo*/ + smp_rmb(); + + vf = q->pool[rp]; + + /*ToDo*/ + smp_mb(); + + q->rp = (rp == (q->size - 1)) ? 0 : (rp + 1); + + return vf; +} + +static inline struct vframe_s *vfq_peek(struct vfq_s *q) +{ + return (vfq_empty(q)) ? NULL : q->pool[q->rp]; +} + +static inline int vfq_level(struct vfq_s *q) +{ + int level = q->wp - q->rp; + + if (level < 0) + level += q->size; + + return level; +} + +#endif /* __AML_VCODEC_VFQ_H_ */ + diff --git a/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.c b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.c new file mode 100644 index 000000000000..1c3b73dfb7be --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.c @@ -0,0 +1,389 @@ +/* + * drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.c + * + * 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. + * + */ + +#include +#include "h264_parse.h" +#include "h264_stream.h" + +static unsigned char h264_exp_golomb_bits[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static unsigned char ZZ_SCAN[16] = { + 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +static unsigned char ZZ_SCAN8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + +unsigned int h264_u(struct h264_stream_t *s, unsigned int n) +{ + //if (n % 8 == 0) { + // return h264_stream_read_bytes(s, n / 8); + //} + return h264_stream_read_bits(s, n); +} + +unsigned int h264_ue(struct h264_stream_t *s) +{ + unsigned int bits, read; + unsigned char coded; + + bits = 0; + while (true) { + if (h264_stream_bytes_remaining(s) < 1) { + read = h264_stream_peek_bits(s, s->bit_pos) << + (8 - s->bit_pos); + break; + } + + read = h264_stream_peek_bits(s, 8); + if (bits > 16) + break; + + if (read) + break; + + h264_stream_read_bits(s, 8); + bits += 8; + } + + coded = h264_exp_golomb_bits[read]; + h264_stream_read_bits(s, coded); + bits += coded; + return h264_stream_read_bits(s, bits + 1) - 1; +} + +int h264_se(struct h264_stream_t *s) +{ + unsigned int ret; + + ret = h264_ue(s); + if (!(ret & 0x1)) { + ret >>= 1; + return (int)(-ret); + } + + return (ret + 1) >> 1; +} + +void h264_f(struct h264_stream_t *s, unsigned int n, unsigned int pattern) +{ + unsigned int val = h264_u(s, n); + + if (val != pattern) { + pr_err("fixed-pattern doesn't match. expected: %x actual: %x\n", + pattern, (unsigned int)val); + return; + } +} + +void h264_rbsp_trailing_bits(struct h264_stream_t *s) +{ + h264_f(s, 1, 1); + h264_f(s, s->bit_pos, 0); +} + +// syntax for scaling list matrix values +void Scaling_List(int *scalingList, int sizeOfScalingList, + bool *UseDefaultScalingMatrix, struct h264_stream_t *s) +{ + int j, scanj; + int delta_scale, lastScale, nextScale; + + lastScale = 8; + nextScale = 8; + + for (j = 0; j < sizeOfScalingList; j++) { + scanj = (sizeOfScalingList == 16) ? ZZ_SCAN[j] : ZZ_SCAN8[j]; + + if (nextScale != 0) { + delta_scale = h264_ue(s); + nextScale = (lastScale + delta_scale + 256) % 256; + *UseDefaultScalingMatrix = + (bool) (scanj == 0 && nextScale == 0); + } + + scalingList[scanj] = (nextScale == 0) ? lastScale : nextScale; + lastScale = scalingList[scanj]; + } +} + +void h264_sps_parse(struct h264_stream_t *s, struct h264_SPS_t *sps) +{ + unsigned int i, n_ScalingList; + + sps->profile_idc = h264_u(s, 8); + + if ((sps->profile_idc != BASELINE) && + (sps->profile_idc != MAIN) && + (sps->profile_idc != EXTENDED) && + (sps->profile_idc != FREXT_HP) && + (sps->profile_idc != FREXT_Hi10P) && + (sps->profile_idc != FREXT_Hi422) && + (sps->profile_idc != FREXT_Hi444) && + (sps->profile_idc != FREXT_CAVLC444) + && (sps->profile_idc != MVC_HIGH) + && (sps->profile_idc != STEREO_HIGH)) { + pr_err("Invalid Profile IDC (%d) encountered.\n", + sps->profile_idc); + return; + } + + sps->constrained_set0_flag = h264_u(s, 1); + sps->constrained_set1_flag = h264_u(s, 1); + sps->constrained_set2_flag = h264_u(s, 1); + h264_u(s, 5); // reserved_zero_5bits + sps->level_idc = h264_u(s, 8); + sps->seq_parameter_set_id = h264_ue(s); + + // Fidelity Range Extensions stuff + sps->chroma_format_idc = 1; + sps->bit_depth_luma_minus8 = 0; + sps->bit_depth_chroma_minus8 = 0; + sps->lossless_qpprime_flag = 0; + sps->separate_colour_plane_flag = 0; + + if ((sps->profile_idc == FREXT_HP) || + (sps->profile_idc == FREXT_Hi10P) || + (sps->profile_idc == FREXT_Hi422) || + (sps->profile_idc == FREXT_Hi444) || + (sps->profile_idc == FREXT_CAVLC444)) { + sps->chroma_format_idc = h264_ue(s); + + if (sps->chroma_format_idc == YUV444) + sps->separate_colour_plane_flag = h264_u(s, 1); + + sps->bit_depth_luma_minus8 = h264_ue(s); + sps->bit_depth_chroma_minus8 = h264_ue(s); + //checking; + if ((sps->bit_depth_luma_minus8 + 8 > sizeof(unsigned short) * 8) || + (sps->bit_depth_chroma_minus8 + 8 > sizeof(unsigned short) * 8)) { + pr_err("Source picture has higher bit depth than imgpel data type.\n"); + pr_err("Please recompile with larger data type for imgpel.\n"); + } + + sps->lossless_qpprime_flag = h264_u(s, 1); + sps->seq_scaling_matrix_present_flag = h264_u(s, 1); + if (sps->seq_scaling_matrix_present_flag) { + n_ScalingList = (sps->chroma_format_idc != YUV444) ? 8 : 12; + for (i = 0; i < n_ScalingList; i++) { + sps->seq_scaling_list_present_flag[i] = h264_u(s, 1); + if (sps->seq_scaling_list_present_flag[i]) { + if (i < 6) + Scaling_List(sps->ScalingList4x4[i], 16, + &sps->UseDefaultScalingMatrix4x4Flag[i], s); + else + Scaling_List(sps->ScalingList8x8[i - 6], + 64, &sps->UseDefaultScalingMatrix8x8Flag[i - 6], s); + } + } + } + } + + sps->log2_max_frame_num_minus4 = h264_ue(s); + sps->pic_order_cnt_type = h264_ue(s); + if (sps->pic_order_cnt_type == 0) { + sps->log2_max_pic_order_cnt_lsb_minus4 = h264_ue(s); + } else if (sps->pic_order_cnt_type == 1) { + sps->delta_pic_order_always_zero_flag = h264_se(s); + sps->offset_for_non_ref_pic = h264_se(s); + sps->offset_for_top_to_bottom_field = h264_se(s); + sps->num_ref_frames_in_poc_cycle = h264_se(s); + for (i = 0; i < sps->num_ref_frames_in_poc_cycle; ++i) + sps->offset_for_ref_frame[i] = h264_se(s); + } + + sps->num_ref_frames = h264_ue(s); + sps->gaps_in_frame_num_value_allowed_flag = h264_u(s, 1); + sps->pic_width_in_mbs_minus1 = h264_ue(s); + sps->pic_height_in_map_units_minus1 = h264_ue(s); + sps->frame_mbs_only_flag = h264_u(s, 1); + if (!sps->frame_mbs_only_flag) + sps->mb_adaptive_frame_field_flag = h264_u(s, 1); + + sps->direct_8x8_inference_flag = h264_u(s, 1); + sps->frame_cropping_flag = h264_u(s, 1); + if (sps->frame_cropping_flag) { + sps->frame_crop_left_offset = h264_ue(s); + sps->frame_crop_right_offset = h264_ue(s); + sps->frame_crop_top_offset = h264_ue(s); + sps->frame_crop_bottom_offset = h264_ue(s); + } + + sps->vui_parameters_present_flag = h264_u(s, 1); + //if (sps->vui_parameters_present_flag) { + // sps->vui_parameters = h264_vui_parameters(s); + //} + h264_rbsp_trailing_bits(s); +} + +void h264_pps_parse(struct h264_stream_t *s, struct h264_PPS_t *pps) +{ + pps->pic_parameter_set_id = h264_ue(s); + pps->seq_parameter_set_id = h264_ue(s); + pps->entropy_coding_mode_flag = h264_u(s, 1); + pps->pic_order_present_flag = h264_u(s, 1); + pps->num_slice_groups_minus1 = h264_ue(s); + if (pps->num_slice_groups_minus1 > 0) { + pps->slice_group_map_type = h264_ue(s); + if (pps->slice_group_map_type == 0) { + pps->run_length_minus1 = h264_ue(s); + } else if (pps->slice_group_map_type == 2) { + pps->top_left = h264_ue(s); + pps->bottom_right = h264_ue(s); + } else if (pps->slice_group_map_type == 3 || + pps->slice_group_map_type == 4 || + pps->slice_group_map_type == 5) { + pps->slice_group_change_direction_flag = h264_u(s, 1); + pps->slice_group_change_rate_minus1 = h264_ue(s); + } else if (pps->slice_group_map_type == 6) { + pps->pic_size_in_map_units_minus1 = h264_ue(s); + pps->slice_group_id = h264_ue(s); + } + } + pps->num_ref_idx_l0_active_minus1 = h264_ue(s); + pps->num_ref_idx_l1_active_minus1 = h264_ue(s); + pps->weighted_pred_flag = h264_u(s, 1); + pps->weighted_bipred_idc = h264_u(s, 2); + pps->pic_init_qp_minus26 = h264_se(s); + pps->pic_init_qs_minus26 = h264_se(s); + pps->chroma_qp_index_offset = h264_se(s); + pps->deblocking_filter_control_present_flag = h264_u(s, 1); + pps->constrained_intra_pred_flag = h264_u(s, 1); + pps->redundant_pic_cnt_present_flag = h264_u(s, 1); + h264_rbsp_trailing_bits(s); +} + +void h264_sps_info(struct h264_SPS_t *sps) +{ + int i; + + pr_info("sequence_parameter_set {\n"); + pr_info(" profile_idc: %d\n", sps->profile_idc); + pr_info(" constraint_set0_flag: %d\n", sps->constrained_set0_flag); + pr_info(" constraint_set1_flag: %d\n", sps->constrained_set1_flag); + pr_info(" constraint_set2_flag: %d\n", sps->constrained_set2_flag); + pr_info(" level_idc: %d\n", sps->level_idc); + pr_info(" seq_parameter_set_id: %d\n", sps->seq_parameter_set_id); + + pr_info(" log2_max_frame_num_minus4: %d\n", + sps->log2_max_frame_num_minus4); + pr_info(" pic_order_cnt_type: %d\n", sps->pic_order_cnt_type); + if (sps->pic_order_cnt_type == 0) { + pr_info(" log2_max_pic_order_cnt_lsb_minus4: %d\n", + sps->log2_max_pic_order_cnt_lsb_minus4); + } else if (sps->pic_order_cnt_type == 1) { + pr_info(" delta_pic_order_always_zero_flag: %d\n", + sps->delta_pic_order_always_zero_flag); + pr_info(" offset_for_non_ref_pic: %d\n", + sps->offset_for_non_ref_pic); + pr_info(" offset_for_top_to_bottom_field: %d\n", + sps->offset_for_top_to_bottom_field); + pr_info(" num_ref_frames_in_pic_order_cnt_cycle: %d\n", + sps->num_ref_frames_in_poc_cycle); + for (i = 0; i < sps->num_ref_frames_in_poc_cycle; ++i) { + pr_info(" offset_for_ref_frame[%d]: %d\n", i, + sps->offset_for_ref_frame[i]); + } + } + pr_info(" num_ref_frames: %d\n", sps->num_ref_frames); + pr_info(" gaps_in_frame_num_value_allowed_flag: %d\n", + sps->gaps_in_frame_num_value_allowed_flag); + pr_info(" pic_width_in_mbs_minus1: %d\n", + sps->pic_width_in_mbs_minus1); + pr_info(" pic_height_in_map_units_minus1: %d\n", + sps->pic_height_in_map_units_minus1); + pr_info(" frame_mbs_only_flag: %d\n", + sps->frame_mbs_only_flag); + pr_info(" mb_adaptive_frame_field_flag: %d\n", + sps->mb_adaptive_frame_field_flag); + pr_info(" direct_8x8_inference_flag: %d\n", + sps->direct_8x8_inference_flag); + pr_info(" frame_cropping_flag: %d\n", + sps->frame_cropping_flag); + if (sps->frame_cropping_flag) { + pr_info(" frame_crop_left_offset: %d\n", + sps->frame_crop_left_offset); + pr_info(" frame_crop_right_offset: %d\n", + sps->frame_crop_right_offset); + pr_info(" frame_crop_top_offset: %d\n", + sps->frame_crop_top_offset); + pr_info(" frame_crop_bottom_offset: %d\n", + sps->frame_crop_bottom_offset); + } + pr_info(" vui_parameters_present_flag: %d\n", + sps->vui_parameters_present_flag); + //if (sps->vui_parameters_present_flag) { + // h264_print_vui_parameters(sps->vui_parameters); + //} + + pr_info(" }\n"); +} + +void h264_pps_info(struct h264_PPS_t *pps) +{ + pr_info("pic_parameter_set {\n"); + pr_info(" pic_parameter_set_id: %d\n", + pps->pic_parameter_set_id); + pr_info(" seq_parameter_set_id: %d\n", + pps->seq_parameter_set_id); + pr_info(" entropy_coding_mode_flag: %d\n", + pps->entropy_coding_mode_flag); + pr_info(" pic_order_present_flag: %d\n", + pps->pic_order_present_flag); + pr_info(" num_slice_groups_minus1: %d\n", + pps->num_slice_groups_minus1); + // FIXME: Code for slice groups is missing here. + pr_info(" num_ref_idx_l0_active_minus1: %d\n", + pps->num_ref_idx_l0_active_minus1); + pr_info(" num_ref_idx_l1_active_minus1: %d\n", + pps->num_ref_idx_l1_active_minus1); + pr_info(" weighted_pred_flag: %d\n", pps->weighted_pred_flag); + pr_info(" weighted_bipred_idc: %d\n", pps->weighted_bipred_idc); + pr_info(" pic_init_qp_minus26: %d\n", pps->pic_init_qp_minus26); + pr_info(" pic_init_qs_minus26: %d\n", pps->pic_init_qs_minus26); + pr_info(" chroma_qp_index_offset: %d\n", + pps->chroma_qp_index_offset); + pr_info(" deblocking_filter_control_present_flag: %d\n", + pps->deblocking_filter_control_present_flag); + pr_info(" constrained_intra_pred_flag: %d\n", + pps->constrained_intra_pred_flag); + pr_info(" redundant_pic_cnt_present_flag: %d\n", + pps->redundant_pic_cnt_present_flag); + pr_info(" }\n"); +} + diff --git a/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.h b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.h new file mode 100644 index 000000000000..e54e4d3665ff --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.h @@ -0,0 +1,141 @@ +/* + * drivers/amlogic/media_modules/amvdec_ports/decoder/h264_parse.h + * + * 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. + * + */ + +#ifndef _H264_PARSE_H +#define _H264_PARSE_H + +#include "h264_stream.h" + +enum color_model { + CM_UNKNOWN = -1, + CM_YUV = 0, + CM_RGB = 1, + CM_XYZ = 2 +}; + +enum color_format { + CF_UNKNOWN = -1, //!< Unknown color format + YUV400 = 0, //!< Monochrome + YUV420 = 1, //!< 4:2:0 + YUV422 = 2, //!< 4:2:2 + YUV444 = 3 //!< 4:4:4 +}; + +enum pixel_format { + PF_UNKNOWN = -1, //!< Unknown color ordering + UYVY = 0, //!< UYVY + YUY2 = 1, //!< YUY2 + YUYV = 1, //!< YUYV + YVYU = 2, //!< YVYU + BGR = 3, //!< BGR + V210 = 4 //!< Video Clarity 422 format (10 bits) +}; + +//AVC Profile IDC definitions +enum profile_idc{ + NO_PROFILE = 0, //!< disable profile checking for experimental coding (enables FRExt, but disables MV) + FREXT_CAVLC444 = 44, //!< YUV 4:4:4/14 "CAVLC 4:4:4" + BASELINE = 66, //!< YUV 4:2:0/8 "Baseline" + MAIN = 77, //!< YUV 4:2:0/8 "Main" + EXTENDED = 88, //!< YUV 4:2:0/8 "Extended" + FREXT_HP = 100, //!< YUV 4:2:0/8 "High" + FREXT_Hi10P = 110, //!< YUV 4:2:0/10 "High 10" + FREXT_Hi422 = 122, //!< YUV 4:2:2/10 "High 4:2:2" + FREXT_Hi444 = 244, //!< YUV 4:4:4/14 "High 4:4:4" + MVC_HIGH = 118, //!< YUV 4:2:0/8 "Multiview High" + STEREO_HIGH = 128 //!< YUV 4:2:0/8 "Stereo High" +}; + +/* sequence parameter set */ +struct h264_SPS_t { + bool vailid; + unsigned int profile_idc; + bool constrained_set0_flag; + bool constrained_set1_flag; + bool constrained_set2_flag; + bool constrained_set3_flag; + unsigned int level_idc; + unsigned int seq_parameter_set_id; + unsigned int chroma_format_idc; + bool seq_scaling_matrix_present_flag; + int seq_scaling_list_present_flag[12]; + int ScalingList4x4[6][16]; + int ScalingList8x8[6][64]; + bool UseDefaultScalingMatrix4x4Flag[6]; + bool UseDefaultScalingMatrix8x8Flag[6]; + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int log2_max_frame_num_minus4; + unsigned int pic_order_cnt_type; + unsigned int log2_max_pic_order_cnt_lsb_minus4; + bool delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + unsigned int num_ref_frames_in_poc_cycle; + int offset_for_ref_frame[255]; + int num_ref_frames; + bool gaps_in_frame_num_value_allowed_flag; + unsigned int pic_width_in_mbs_minus1; + unsigned int pic_height_in_map_units_minus1; + bool frame_mbs_only_flag; + bool mb_adaptive_frame_field_flag; + bool direct_8x8_inference_flag; + bool frame_cropping_flag; + unsigned int frame_crop_left_offset; + unsigned int frame_crop_right_offset; + unsigned int frame_crop_top_offset; + unsigned int frame_crop_bottom_offset; + bool vui_parameters_present_flag; + //h264_vui_parameters_t *vui_parameters; + unsigned separate_colour_plane_flag; + int lossless_qpprime_flag; +}; + +/* pic parameter set */ +struct h264_PPS_t { + int pic_parameter_set_id; + int seq_parameter_set_id; + int entropy_coding_mode_flag; + int pic_order_present_flag; + int num_slice_groups_minus1; + int slice_group_map_type; + int run_length_minus1; + int top_left; + int bottom_right; + int slice_group_change_direction_flag; + int slice_group_change_rate_minus1; + int pic_size_in_map_units_minus1; + int slice_group_id; + int num_ref_idx_l0_active_minus1; + int num_ref_idx_l1_active_minus1; + int weighted_pred_flag; + int weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + int deblocking_filter_control_present_flag; + int constrained_intra_pred_flag; + int redundant_pic_cnt_present_flag; +}; + +void h264_sps_parse(struct h264_stream_t *s, struct h264_SPS_t *sps); +void h264_pps_parse(struct h264_stream_t *s, struct h264_PPS_t *pps); + +void h264_sps_info(struct h264_SPS_t *sps); +void h264_pps_info(struct h264_PPS_t *pps); + +#endif //_H264_PARSE_H diff --git a/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.c b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.c new file mode 100644 index 000000000000..3061568309fc --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.c @@ -0,0 +1,111 @@ +/* + * drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.c + * + * 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. + * + */ + +#include +#include +#include "h264_stream.h" + +void h264_stream_set(struct h264_stream_t *s, unsigned char *data, int size) +{ + s->data = data; + s->size = size; + s->bit_pos = 7; + s->byte_pos = 0; +} + +unsigned int h264_stream_read_bits(struct h264_stream_t *s, unsigned int n) +{ + unsigned int ret = 0; + unsigned char b = 0; + int i; + + if (n == 0) + return 0; + + for (i = 0; i < n; ++i) { + if (h264_stream_bits_remaining(s) == 0) + ret <<= n - i - 1; + + b = s->data[s->byte_pos]; + if (n - i <= 32) + ret = ret << 1 | BITAT(b, s->bit_pos); + + if (s->bit_pos == 0) { + s->bit_pos = 7; + s->byte_pos++; + } else + s->bit_pos--; + } + + return ret; +} + +unsigned int h264_stream_peek_bits(struct h264_stream_t *s, unsigned int n) +{ + int prev_bit_pos = s->bit_pos; + int prev_byte_pos = s->byte_pos; + unsigned int ret = h264_stream_read_bits(s, n); + + s->bit_pos = prev_bit_pos; + s->byte_pos = prev_byte_pos; + + return ret; +} + +unsigned int h264_stream_read_bytes(struct h264_stream_t *s, unsigned int n) +{ + unsigned int ret = 0; + int i; + + if (n == 0) + return 0; + + for (i = 0; i < n; ++i) { + if (h264_stream_bytes_remaining(s) == 0) { + ret <<= (n - i - 1) * 8; + break; + } + + if (n - i <= 4) + ret = ret << 8 | s->data[s->byte_pos]; + + s->byte_pos++; + } + + return ret; +} + +unsigned int h264_stream_peek_bytes(struct h264_stream_t *s, unsigned int n) +{ + int prev_byte_pos = s->byte_pos; + unsigned int ret = h264_stream_read_bytes(s, n); + + s->byte_pos = prev_byte_pos; + + return ret; +} + +int h264_stream_bits_remaining(struct h264_stream_t *s) +{ + return (s->size - s->byte_pos) * 8 + s->bit_pos; +} + +int h264_stream_bytes_remaining(struct h264_stream_t *s) +{ + return s->size - s->byte_pos; +} + diff --git a/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.h b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.h new file mode 100644 index 000000000000..d6d2eac88b93 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.h @@ -0,0 +1,39 @@ +/* + * drivers/amlogic/media_modules/amvdec_ports/decoder/h264_stream.h + * + * 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. + * + */ + +#ifndef _H264_STREAM_H +#define _H264_STREAM_H + +#include "utils.h" + +struct h264_stream_t { + unsigned char *data; + unsigned int size; + int bit_pos; + int byte_pos; +}; + +void h264_stream_set(struct h264_stream_t *s, unsigned char *data, int size); +unsigned int h264_stream_read_bits(struct h264_stream_t *s, unsigned int n); +unsigned int h264_stream_peek_bits(struct h264_stream_t *s, unsigned int n); +unsigned int h264_stream_read_bytes(struct h264_stream_t *s, unsigned int n); +unsigned int h264_stream_peek_bytes(struct h264_stream_t *s, unsigned int n); +int h264_stream_bits_remaining(struct h264_stream_t *s); +int h264_stream_bytes_remaining(struct h264_stream_t *s); + +#endif //_H264_STREAM_H + diff --git a/drivers/amlogic/media_modules/amvdec_ports/decoder/utils.h b/drivers/amlogic/media_modules/amvdec_ports/decoder/utils.h new file mode 100644 index 000000000000..26b155271916 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/decoder/utils.h @@ -0,0 +1,31 @@ +/* + * drivers/amlogic/media_modules/amvdec_ports/decoder/utils.h + * + * 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. + * + */ + +#ifndef _UTILS_H +#define _UTILS_H + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CLAMP(x, low, high) \ + (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#define BITAT(x, n) ((x & (1 << n)) == (1 << n)) + +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; + +#endif //_UTILS_H diff --git a/drivers/amlogic/media_modules/amvdec_ports/decoder/vdec_h264_if.c b/drivers/amlogic/media_modules/amvdec_ports/decoder/vdec_h264_if.c new file mode 100644 index 000000000000..799835de827b --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/decoder/vdec_h264_if.c @@ -0,0 +1,698 @@ +#include +#include +#include +#include +#include +#include "../vdec_drv_if.h" +#include "../aml_vcodec_util.h" +#include "../aml_vcodec_dec.h" +//#include "../aml_vcodec_intr.h" +#include "../aml_vcodec_adapt.h" +#include "../vdec_drv_base.h" +#include "../aml_vcodec_vfm.h" +#include "h264_stream.h" +#include "h264_parse.h" + +/* h264 NALU type */ +#define NAL_NON_IDR_SLICE 0x01 +#define NAL_IDR_SLICE 0x05 +#define NAL_H264_SEI 0x06 +#define NAL_H264_SPS 0x07 +#define NAL_H264_PPS 0x08 + +#define NAL_TYPE(value) ((value) & 0x1F) + +#define BUF_PREDICTION_SZ (64 * 1024)//(32 * 1024) + +#define MB_UNIT_LEN 16 + +/* motion vector size (bytes) for every macro block */ +#define HW_MB_STORE_SZ 64 + +#define H264_MAX_FB_NUM 17 +#define HDR_PARSING_BUF_SZ 1024 + +/** + * struct h264_fb - h264 decode frame buffer information + * @vdec_fb_va : virtual address of struct vdec_fb + * @y_fb_dma : dma address of Y frame buffer (luma) + * @c_fb_dma : dma address of C frame buffer (chroma) + * @poc : picture order count of frame buffer + * @reserved : for 8 bytes alignment + */ +struct h264_fb { + uint64_t vdec_fb_va; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + int32_t poc; + uint32_t reserved; +}; + +/** + * struct h264_ring_fb_list - ring frame buffer list + * @fb_list : frame buffer arrary + * @read_idx : read index + * @write_idx : write index + * @count : buffer count in list + */ +struct h264_ring_fb_list { + struct h264_fb fb_list[H264_MAX_FB_NUM]; + unsigned int read_idx; + unsigned int write_idx; + unsigned int count; + unsigned int reserved; +}; + +/** + * struct vdec_h264_dec_info - decode information + * @dpb_sz : decoding picture buffer size + * @resolution_changed : resoltion change happen + * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer + * @reserved : for 8 bytes alignment + * @bs_dma : Input bit-stream buffer dma address + * @y_fb_dma : Y frame buffer dma address + * @c_fb_dma : C frame buffer dma address + * @vdec_fb_va : VDEC frame buffer struct virtual address + */ +struct vdec_h264_dec_info { + uint32_t dpb_sz; + uint32_t resolution_changed; + uint32_t realloc_mv_buf; + uint32_t reserved; + uint64_t bs_dma; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + uint64_t vdec_fb_va; +}; + +/** + * struct vdec_h264_vsi - shared memory for decode information exchange + * between VPU and Host. + * The memory is allocated by VPU then mapping to Host + * in vpu_dec_init() and freed in vpu_dec_deinit() + * by VPU. + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @hdr_buf : Header parsing buffer (AP-W, VPU-R) + * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) + * @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R) + * @list_free : free frame buffer ring list (AP-W/R, VPU-W) + * @list_disp : display frame buffer ring list (AP-R, VPU-W) + * @dec : decode information (AP-R, VPU-W) + * @pic : picture information (AP-R, VPU-W) + * @crop : crop information (AP-R, VPU-W) + */ +struct vdec_h264_vsi { + unsigned char hdr_buf[HDR_PARSING_BUF_SZ]; + int sps_size; + int pps_size; + int sei_size; + int head_offset; + uint64_t pred_buf_dma; + uint64_t mv_buf_dma[H264_MAX_FB_NUM]; + struct h264_ring_fb_list list_free; + struct h264_ring_fb_list list_disp; + struct vdec_h264_dec_info dec; + struct vdec_pic_info pic; + struct v4l2_rect crop; +}; + +/** + * struct vdec_h264_inst - h264 decoder instance + * @num_nalu : how many nalus be decoded + * @ctx : point to aml_vcodec_ctx + * @pred_buf : HW working predication buffer + * @mv_buf : HW working motion vector buffer + * @vpu : VPU instance + * @vsi : VPU shared information + */ +struct vdec_h264_inst { + unsigned int num_nalu; + struct aml_vcodec_ctx *ctx; + struct aml_vcodec_mem pred_buf; + struct aml_vcodec_mem mv_buf[H264_MAX_FB_NUM]; + //struct vdec_vpu_inst vpu; + struct aml_vdec_adapt vdec; + struct vdec_h264_vsi *vsi; + struct vcodec_vfm_s vfm; +}; + +static DEFINE_MUTEX(mutex); + +#define DUMP_FILE_NAME "/data/dump/dump.tmp" +static struct file *filp; +static loff_t file_pos; + +void dump_write(const char __user *buf, size_t count) +{ + mm_segment_t old_fs; + + if (!filp) + return; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + if (count != vfs_write(filp, buf, count, &file_pos)) + pr_err("Failed to write file\n"); + + set_fs(old_fs); +} + +void dump_init(void) +{ + filp = filp_open(DUMP_FILE_NAME, O_CREAT | O_RDWR, 0644); + if (IS_ERR(filp)) { + pr_err("open dump file failed\n"); + filp = NULL; + } +} + +void dump_deinit(void) +{ + if (filp) { + filp_close(filp, current->files); + filp = NULL; + file_pos = 0; + } +} + +static void get_pic_info(struct vdec_h264_inst *inst, + struct vdec_pic_info *pic) +{ + *pic = inst->vsi->pic; + + aml_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", + pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); + aml_vcodec_debug(inst, "Y(%d, %d), C(%d, %d)", pic->y_bs_sz, + pic->y_len_sz, pic->c_bs_sz, pic->c_len_sz); +} + +static void get_crop_info(struct vdec_h264_inst *inst, struct v4l2_rect *cr) +{ + cr->left = inst->vsi->crop.left; + cr->top = inst->vsi->crop.top; + cr->width = inst->vsi->crop.width; + cr->height = inst->vsi->crop.height; + + aml_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); +} + +static void get_dpb_size(struct vdec_h264_inst *inst, unsigned int *dpb_sz) +{ + *dpb_sz = inst->vsi->dec.dpb_sz; + aml_vcodec_debug(inst, "sz=%d", *dpb_sz); +} + +static int find_start_code(unsigned char *data, unsigned int data_sz) +{ + if (data_sz > 3 && data[0] == 0 && data[1] == 0 && data[2] == 1) + return 3; + + if (data_sz > 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && + data[3] == 1) + return 4; + + return -1; +} + +static int vdec_h264_init(struct aml_vcodec_ctx *ctx, unsigned long *h_vdec) +{ + struct vdec_h264_inst *inst = NULL; + int err = -1; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + + inst->vdec.id = VFORMAT_H264; + inst->vdec.dev = ctx->dev->vpu_plat_dev; + inst->vdec.filp = ctx->dev->filp; + inst->vdec.ctx = ctx; + inst->vfm.ctx = ctx; + + vcodec_vfm_init(&inst->vfm); + + //inst->vpu.handler = vpu_dec_ipi_handler; + + err = video_decoder_init(&inst->vdec); + if (err) { + aml_vcodec_err(inst, "vdec_h264 init err=%d", err); + //goto error_free_inst; + } + + //inst->vsi = (struct vdec_h264_vsi *)inst->vpu.vsi; + + //probe info from the decoder hw debug_tmp + inst->vsi = kzalloc(sizeof(struct vdec_h264_vsi), GFP_KERNEL); + + //debug_tmp + inst->vsi->pic.pic_w = 1920; + inst->vsi->pic.pic_h = 1088; + inst->vsi->pic.buf_w = 1920; + inst->vsi->pic.buf_h = 1088; + inst->vsi->pic.y_bs_sz = 0; + inst->vsi->pic.y_len_sz = (1920 * 1088); + inst->vsi->pic.c_bs_sz = 0; + inst->vsi->pic.c_len_sz = (1920 * 1088 / 2); + + //err = allocate_predication_buf(inst); + //if (err) + //goto error_deinit; + + aml_vcodec_debug(inst, "H264 Instance >> %p", inst); + + *h_vdec = (unsigned long)inst; + + dump_init(); + + return 0; + +//error_deinit: + //vpu_dec_deinit(&inst->vpu); + +//error_free_inst: + kfree(inst->vsi); + kfree(inst); + return err; +} + +static int refer_buffer_num(int level_idc, int poc_cnt, + int mb_width, int mb_height) +{ + int max_ref_num = 27; + int size, size_margin = 7; + int pic_size = mb_width * mb_height * 384; + + switch (level_idc) { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + case 52: + default: + size = 70778880; + break; + } + + size /= pic_size; + size = size + size_margin; /* need more buffers */ + + if (poc_cnt > size) + size = poc_cnt; + + if (size > max_ref_num) + size = max_ref_num; + + return size; +} + +static void fill_vdec_params(struct h264_SPS_t *sps, struct vdec_h264_vsi *vsi) +{ + struct vdec_pic_info *pic = &vsi->pic; + struct vdec_h264_dec_info *dec = &vsi->dec; + struct v4l2_rect *rect = &vsi->crop; + unsigned int mb_w, mb_h, width, height; + unsigned int crop_unit_x = 0, crop_unit_y = 0; + unsigned int poc_cnt = 0; + + mb_w = sps->pic_width_in_mbs_minus1 + 1; + mb_h = sps->pic_height_in_map_units_minus1 + 1; + + width = mb_w << 4; // 16 + height = (2 - sps->frame_mbs_only_flag) * (mb_h << 4); + + if (sps->frame_cropping_flag) { + if (0 == sps->chroma_format_idc) {// monochrome + crop_unit_x = 1; + crop_unit_y = 2 - sps->frame_mbs_only_flag; + } else if (1 == sps->chroma_format_idc) {// 4:2:0 + crop_unit_x = 2; + crop_unit_y = 2 * (2 - sps->frame_mbs_only_flag); + } else if (2 == sps->chroma_format_idc) {// 4:2:2 + crop_unit_x = 2; + crop_unit_y = 2 - sps->frame_mbs_only_flag; + } else {// 3 == sps.chroma_format_idc // 4:4:4 + crop_unit_x = 1; + crop_unit_y = 2 - sps->frame_mbs_only_flag; + } + } + + width -= crop_unit_x * (sps->frame_crop_left_offset + + sps->frame_crop_right_offset); + height -= crop_unit_y * (sps->frame_crop_top_offset + + sps->frame_crop_bottom_offset); + + /* fill image size that be used for EGL. */ + pic->pic_w = width; + pic->pic_h = height; + + /* calc visible ares. */ + rect->left = 0; + rect->top = 0; + rect->width = pic->pic_w; + rect->height = pic->pic_h; + + /* config canvas size that be used for decoder. */ + pic->buf_w = ALIGN(mb_w, 4) << 4; + pic->buf_h = ALIGN(mb_h, 4) << 4; + pic->y_len_sz = pic->buf_w * pic->buf_h; + pic->c_len_sz = pic->y_len_sz >> 1; + + /* calc DPB size */ + poc_cnt = sps->pic_order_cnt_type; + if (!poc_cnt) + poc_cnt = (sps->log2_max_pic_order_cnt_lsb_minus4 + 4) << 1; + + dec->dpb_sz = refer_buffer_num(sps->level_idc, poc_cnt, mb_w, mb_h); + + pr_info("the stream infos, width: %d, height: %d, dpb: %d\n", + pic->pic_w, pic->pic_h, dec->dpb_sz); +} + +static int vdec_h264_probe(unsigned long h_vdec, + struct aml_vcodec_mem *bs, void *out) +{ + struct vdec_h264_inst *inst = + (struct vdec_h264_inst *)h_vdec; + struct h264_stream_t s; + struct h264_SPS_t *sps; + unsigned int nal_type, nal_idx; + int real_data_pos, real_data_size; + unsigned char *buf = (unsigned char *)bs->va; + unsigned int size = bs->size; + + nal_idx = find_start_code(buf, size); + if (nal_idx < 0) + return -1; + + nal_type = NAL_TYPE(buf[nal_idx]); + if (nal_type != NAL_H264_SPS) + return -1; + + /* start code plus nal type. */ + real_data_pos = nal_idx + 1; + real_data_size = size - real_data_pos; + + sps = kzalloc(sizeof(struct h264_SPS_t), GFP_KERNEL); + if (IS_ERR_OR_NULL(sps)) + return -ENOMEM; + + h264_stream_set(&s, &buf[real_data_pos], real_data_size); + h264_sps_parse(&s, sps); + //h264_sps_info(sps); + + fill_vdec_params(sps, inst->vsi); + + kfree(sps); + + return 0; +} + +static void vdec_h264_deinit(unsigned long h_vdec) +{ + struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; + + mutex_lock(&mutex); + + aml_vcodec_debug_enter(inst); + + video_decoder_release(&inst->vdec); + + vcodec_vfm_release(&inst->vfm); + + dump_deinit(); + + kfree(inst->vsi); + kfree(inst); + inst = NULL; + + mutex_unlock(&mutex); +} + +static int vdec_h264_get_fb(struct vdec_h264_inst *inst, struct vdec_fb **out) +{ + return get_fb_from_queue(inst->ctx, out); +} + +static void vdec_h264_get_vf(struct vdec_h264_inst *inst, struct vdec_fb **out) +{ + struct vframe_s *vf = NULL; + struct vdec_fb *fb = NULL; + + aml_vcodec_debug(inst, "%s() [%d], vfm: %p\n", + __FUNCTION__, __LINE__, &inst->vfm); + + vf = peek_video_frame(&inst->vfm); + if (!vf) { + aml_vcodec_debug(inst, "there is no vframe.\n"); + *out = NULL; + return; + } + + vf = get_video_frame(&inst->vfm); + if (!vf) { + aml_vcodec_debug(inst, "the vframe is avalid.\n"); + *out = NULL; + return; + } + + atomic_set(&vf->use_cnt, 1); + + aml_vcodec_debug(inst, "%s() [%d], vf: %p, v4l_mem_handle: %lx\n", + __FUNCTION__, __LINE__, vf, vf->v4l_mem_handle); + + fb = (struct vdec_fb *)vf->v4l_mem_handle; + fb->vf_handle = (unsigned long)vf; + fb->status = FB_ST_DISPLAY; + + *out = fb; + + //dump_write(fb->base_y.va, fb->base_y.bytes_used); + //dump_write(fb->base_c.va, fb->base_c.bytes_used); + + aml_vcodec_debug(inst, "%s() [%d], va: %p, phy: %x, size: %lu\n", + __FUNCTION__, __LINE__, fb->base_y.va, + (unsigned int)virt_to_phys(fb->base_y.va), fb->base_y.size); + aml_vcodec_debug(inst, "%s() [%d], va: %p, phy: %x, size: %lu\n", + __FUNCTION__, __LINE__, fb->base_c.va, + (unsigned int)virt_to_phys(fb->base_c.va), fb->base_c.size); +} + +static int vdec_h264_decode(unsigned long h_vdec, struct aml_vcodec_mem *bs, + unsigned long int timestamp, bool *res_chg) +{ + struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; + struct aml_vdec_adapt *vdec = &inst->vdec; + int nal_start_idx = 0; + int err = 0; + unsigned int nal_start; + unsigned int nal_type; + unsigned char *buf; + unsigned int buf_sz; + int ret = 0; + + /* bs NULL means flush decoder */ + if (bs == NULL) + return 0;//vpu_dec_reset(vpu); + + buf = (unsigned char *)bs->va; + buf_sz = bs->size; + nal_start_idx = find_start_code(buf, buf_sz); + if (nal_start_idx < 0) + goto err_free_fb_out; + + nal_start = buf[nal_start_idx]; + nal_type = NAL_TYPE(buf[nal_start_idx]); + aml_v4l2_debug(1, "NALU type: %d, size: %u\n", nal_type, buf_sz); + + if (nal_type == NAL_H264_SPS) { + if (buf_sz > HDR_PARSING_BUF_SZ) { + err = -EILSEQ; + goto err_free_fb_out; + } + inst->vsi->sps_size = buf_sz; + memcpy(inst->vsi->hdr_buf + inst->vsi->head_offset, buf, buf_sz); + inst->vsi->head_offset += inst->vsi->sps_size; + } else if (nal_type == NAL_H264_PPS) { + //buf_sz -= nal_start_idx; + if (buf_sz > HDR_PARSING_BUF_SZ) { + err = -EILSEQ; + goto err_free_fb_out; + } + inst->vsi->pps_size = buf_sz; + memcpy(inst->vsi->hdr_buf + inst->vsi->head_offset, buf, buf_sz); + inst->vsi->head_offset += inst->vsi->pps_size; + } else if (nal_type == NAL_H264_SEI) { + if (buf_sz > HDR_PARSING_BUF_SZ) { + err = -EILSEQ; + goto err_free_fb_out; + } + inst->vsi->sei_size = buf_sz; + memcpy(inst->vsi->hdr_buf + inst->vsi->head_offset, buf, buf_sz); + inst->vsi->head_offset += inst->vsi->sei_size; + } else { + char *write_buf = vmalloc(inst->vsi->head_offset + buf_sz); + + memcpy(write_buf, inst->vsi->hdr_buf, inst->vsi->head_offset); + memcpy(write_buf + inst->vsi->head_offset, buf, buf_sz); + + ret = vdec_vframe_write(vdec->filp, write_buf, + inst->vsi->head_offset + buf_sz, timestamp); + + aml_vcodec_debug(inst, + "%s() [%d], buf: %p, buf size: %u, write to: %d\n", + __FUNCTION__, __LINE__, write_buf, + inst->vsi->head_offset + buf_sz, ret); + + inst->vsi->head_offset = 0; + inst->vsi->sps_size = 0; + inst->vsi->pps_size = 0; + inst->vsi->sei_size = 0; + + vfree(write_buf); + } + + //err = vpu_dec_start(vpu, data, 2); + if (err) + goto err_free_fb_out; + +#if 0 + *res_chg = inst->vsi->dec.resolution_changed; + if (*res_chg) { + struct vdec_pic_info pic; + + aml_vcodec_debug(inst, "- resolution changed -"); + get_pic_info(inst, &pic); + + if (inst->vsi->dec.realloc_mv_buf) { + err = alloc_mv_buf(inst, &pic); + if (err) + goto err_free_fb_out; + } + } +#endif + if (nal_type == NAL_NON_IDR_SLICE || nal_type == NAL_IDR_SLICE) { + /* wait decoder done interrupt */ + //err = aml_vcodec_wait_for_done_ctx(inst->ctx, aml_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS); + if (err) + goto err_free_fb_out; + + //vpu_dec_end(vpu); + } + + aml_vcodec_debug(inst, "\n - NALU[%d] type=%d -\n", inst->num_nalu, + nal_type); + return ret; + +err_free_fb_out: + //put_fb_to_free(inst, fb); + aml_vcodec_err(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); + return err; +} + +static int vdec_h264_get_param(unsigned long h_vdec, + enum vdec_get_param_type type, void *out) +{ + int ret = 0; + struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; + + mutex_lock(&mutex); + + if (!inst) { + pr_err("the h264 inst of dec is void.\n"); + mutex_unlock(&mutex); + return -1; + } + + switch (type) { + case GET_PARAM_DISP_FRAME_BUFFER: + //vdec_h264_get_fb(inst, &inst->vsi->list_disp, true, out); + vdec_h264_get_vf(inst, out); + break; + + case GET_PARAM_FREE_FRAME_BUFFER: + ret = vdec_h264_get_fb(inst, out); + break; + + case GET_PARAM_PIC_INFO: + get_pic_info(inst, out); + break; + + case GET_PARAM_DPB_SIZE: + get_dpb_size(inst, out); + break; + + case GET_PARAM_CROP_INFO: + get_crop_info(inst, out); + break; + + default: + aml_vcodec_err(inst, "invalid get parameter type=%d", type); + return -EINVAL; + } + + mutex_unlock(&mutex); + + return ret; +} + +static struct vdec_common_if vdec_h264_if = { + vdec_h264_init, + vdec_h264_probe, + vdec_h264_decode, + vdec_h264_get_param, + vdec_h264_deinit, +}; + +struct vdec_common_if *get_h264_dec_comm_if(void); + +struct vdec_common_if *get_h264_dec_comm_if(void) +{ + return &vdec_h264_if; +} diff --git a/drivers/amlogic/media_modules/amvdec_ports/test/Android.mk b/drivers/amlogic/media_modules/amvdec_ports/test/Android.mk new file mode 100644 index 000000000000..ecfbc5ccc09c --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/test/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := vcode_m2m +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := vcodec_m2m_test.c +LOCAL_ARM_MODE := arm + +LOCAL_C_INCLUDES := \ + $(JNI_H_INCLUDE) \ + $(ANDROID_BUILD_TOP)/vendor/amlogic/external/ffmpeg + +LOCAL_SHARED_LIBRARIES := \ + libamffmpeg + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_PREBUILT_LIBS:= \ +# libavcodec:ffmpeg/lib/libavcodec.so \ + +include $(BUILD_MULTI_PREBUILT) diff --git a/drivers/amlogic/media_modules/amvdec_ports/test/vcodec_m2m_test.c b/drivers/amlogic/media_modules/amvdec_ports/test/vcodec_m2m_test.c new file mode 100644 index 000000000000..a96165a707a9 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/test/vcodec_m2m_test.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define INBUF_SIZE (4096) +#define DUMP_DIR "/mnt/video_frames" + +typedef struct VcodecCtx { + AVFormatContext *fmt_ctx; + AVStream *stream; + int nb_streams; + int vst_idx; + char *filename; + pthread_t tid; + pthread_mutex_t pthread_mutex; + pthread_cond_t pthread_cond; +} VcodecCtx; + +static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, + char *filename) +{ + FILE *f; + int i; + + printf("wrap: %d, xsize: %d, ysize: %d, filename: %s\n", wrap, xsize, ysize, filename); + + f = fopen(filename,"w+"); + fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); + + for (i = 0; i < ysize; i++) + fwrite(buf + i * wrap, 1, xsize, f); + fclose(f); +} + +static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, + const char *filename) +{ + char buf[1024]; + int ret; + + ret = avcodec_send_packet(dec_ctx, pkt); + if (ret < 0) { + fprintf(stderr, "Error sending a packet for decoding\n"); + return; + } + + while (ret >= 0) { + ret = avcodec_receive_frame(dec_ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + fprintf(stderr, "Error during decoding, ret: %s\n", av_err2str(ret)); + break; + } + + //fprintf(stderr, "saving frame %3d\n", dec_ctx->frame_number); + fflush(stdout); + + /* the picture is allocated by the decoder. no need to free it */ + snprintf(buf, sizeof(buf), "%s/frame-%d", filename, dec_ctx->frame_number); + pgm_save(frame->data[0], frame->linesize[0], + frame->width, frame->height, buf); + } +} + +static void* read_thread(void *arg) +{ + int ret, err; + AVFormatContext *ic = NULL; + AVCodecContext *dec_ctx = NULL; + AVStream *stream = NULL; + AVCodec *codec = NULL; + AVPacket pkt1, *pkt = &pkt1; + AVFrame *frame = NULL; + int vst_idx = 0; + int has_video = 0; + unsigned int st_idx = 0; + VcodecCtx *vctx = arg; + + printf("entry read thread, tid: %ld.\n", vctx->tid); + + ic = avformat_alloc_context(); + if (!ic) { + fprintf(stderr, "Could not allocate avformat context.\n"); + return NULL; + } + + err = avformat_open_input(&ic, vctx->filename, NULL, NULL); + if (err < 0) { + fprintf(stderr, "Could not open avformat input.\n"); + return NULL; + } + + err = avformat_find_stream_info(ic, NULL); + if (err < 0) { + fprintf(stderr, "find stream info err.\n"); + return NULL; + } + + for (st_idx = 0; st_idx < ic->nb_streams; st_idx++) { + AVStream *st = ic->streams[st_idx]; + + enum AVMediaType type = st->codecpar->codec_type; + st->discard = AVDISCARD_ALL; + + if (type == AVMEDIA_TYPE_VIDEO) { + st->discard = AVDISCARD_NONE; + vctx->vst_idx = st_idx; + has_video = 1; + break; + } + } + + if (!has_video) { + fprintf(stderr, "no video stream.\n"); + return NULL; + } + + stream = ic->streams[vctx->vst_idx]; + + //codec = avcodec_find_decoder(stream->codecpar->codec_id); + codec = avcodec_find_decoder_by_name("h264_v4l2m2m"); + if (!codec) { + fprintf(stderr, "Unsupported codec with id %d for input stream %d\n", + stream->codecpar->codec_id, stream->index); + return NULL; + } + + dec_ctx = avcodec_alloc_context3(codec); + if (!dec_ctx) { + fprintf(stderr, "Could not allocate video codec context\n"); + return NULL; + } + + err = avcodec_parameters_to_context(dec_ctx, stream->codecpar); + if (err < 0) { + fprintf(stderr, "Could not set paras to context\n"); + return NULL; + } + + av_codec_set_pkt_timebase(dec_ctx, stream->time_base); + dec_ctx->framerate = stream->avg_frame_rate; + + if (avcodec_open2(dec_ctx, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec for input stream %d\n", + stream->index); + return NULL; + } + + printf("fmt ctx: %p, stream: %p, video st idx: %d, num: %d\n", + ic, stream, vst_idx, ic->nb_streams); + printf("format: %s\n",ic->iformat->name); + + ic->flags |= AVFMT_FLAG_GENPTS; + ic->debug = 0xff; + + //if (ic->pb) + // ic->pb->eof_reached = 0; + + frame = av_frame_alloc(); + if (!frame) { + fprintf(stderr, "Could not allocate video frame\n"); + return NULL; + } + + for (;;) { + ret = av_read_frame(ic, pkt); + if (ret < 0) { + if (ret == AVERROR_EOF || avio_feof(ic->pb)) { + printf("read data end, ret: %d.\n", ret); + break; + } + + if (ic->pb && ic->pb->error) + break; + + printf("read data fail, ret: %d.\n", ret); + continue; + } + + if (pkt->stream_index == vctx->vst_idx) { + //packet_queue_put(&is->audioq, pkt); + printf("read video data size: %d.\n", pkt->size); + if (pkt->size) + decode(dec_ctx, frame, pkt, DUMP_DIR); + } else + av_packet_unref(pkt); + } + + /* flush the decoder */ + decode(dec_ctx, frame, NULL, DUMP_DIR); + + avcodec_free_context(&dec_ctx); + av_frame_free(&frame); + + if (ic) + avformat_close_input(&ic); + + printf("read thread exit.\n"); + + pthread_mutex_lock(&vctx->pthread_mutex); + pthread_cond_signal(&vctx->pthread_cond); + pthread_mutex_unlock(&vctx->pthread_mutex); + + return NULL; +} + + +static int open_input_file(const char *filename) +{ + int ret; + VcodecCtx *vctx; + pthread_t pid = pthread_self(); + + vctx = av_mallocz(sizeof(VcodecCtx)); + if (!vctx) + return -1; + + vctx->filename = av_strdup(filename); + if (!vctx->filename) + return -1; + + pthread_mutex_init(&vctx->pthread_mutex, NULL); + pthread_cond_init(&vctx->pthread_cond, NULL); + + ret = pthread_create(&vctx->tid, NULL, read_thread, (void *)vctx); + if (ret == 0) { + pthread_setname_np(pid, "read_thread"); + + pthread_mutex_lock(&vctx->pthread_mutex); + pthread_cond_wait(&vctx->pthread_cond, &vctx->pthread_mutex); + pthread_mutex_unlock(&vctx->pthread_mutex); + } + + printf("creat the read thread, ret: %d.\n", ret); + + return 0; +} + +int main(int argc, char **argv) +{ + int ret; + const char *filename, *outfilename; + + if (argc < 2) { + fprintf(stderr, "Usage: %s \n ==> %s/frame-123\n", argv[0], DUMP_DIR); + exit(0); + } + + filename = argv[1]; + outfilename = argv[2]; + + mkdir(DUMP_DIR, 0664); + + /*set debug level*/ + //av_log_set_level(AV_LOG_DEBUG); + + av_register_all(); + + ret = open_input_file(filename); + if (ret < 0) { + fprintf(stderr, "open input file fail.\n"); + goto out; + } + +out: + return ret < 0; +} diff --git a/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_base.h b/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_base.h new file mode 100644 index 000000000000..66d2917299ab --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_base.h @@ -0,0 +1,45 @@ +#ifndef _VDEC_DRV_BASE_ +#define _VDEC_DRV_BASE_ + +#include "aml_vcodec_drv.h" + +#include "vdec_drv_if.h" + +struct vdec_common_if { + /** + * (*init)() - initialize decode driver + * @ctx : [in] aml v4l2 context + * @h_vdec : [out] driver handle + */ + int (*init)(struct aml_vcodec_ctx *ctx, unsigned long *h_vdec); + + int (*probe)(unsigned long h_vdec, + struct aml_vcodec_mem *bs, void *out); + + /** + * (*decode)() - trigger decode + * @h_vdec : [in] driver handle + * @bs : [in] input bitstream + * @fb : [in] frame buffer to store decoded frame + * @res_chg : [out] resolution change happen + */ + int (*decode)(unsigned long h_vdec, struct aml_vcodec_mem *bs, + unsigned long int pts, bool *res_chg); + + /** + * (*get_param)() - get driver's parameter + * @h_vdec : [in] driver handle + * @type : [in] input parameter type + * @out : [out] buffer to store query result + */ + int (*get_param)(unsigned long h_vdec, + enum vdec_get_param_type type, void *out); + + /** + * (*deinit)() - deinitialize driver. + * @h_vdec : [in] driver handle to be deinit + */ + void (*deinit)(unsigned long h_vdec); +}; + +#endif diff --git a/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_if.c b/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_if.c new file mode 100644 index 000000000000..d6ccb63331a3 --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_if.c @@ -0,0 +1,119 @@ +#include +#include +#include + +#include "vdec_drv_if.h" +#include "aml_vcodec_dec.h" +#include "vdec_drv_base.h" +#include "aml_vcodec_dec_pm.h" +//#include "aml_vpu.h" + +const struct vdec_common_if *get_h264_dec_comm_if(void); +const struct vdec_common_if *get_vp8_dec_comm_if(void); +const struct vdec_common_if *get_vp9_dec_comm_if(void); + +int vdec_if_init(struct aml_vcodec_ctx *ctx, unsigned int fourcc) +{ + int ret = 0; + + switch (fourcc) { + case V4L2_PIX_FMT_H264: + ctx->dec_if = get_h264_dec_comm_if(); + break; + /*case V4L2_PIX_FMT_VP8: + ctx->dec_if = get_vp8_dec_comm_if(); + break; + case V4L2_PIX_FMT_VP9: + ctx->dec_if = get_vp9_dec_comm_if(); + break;*/ + default: + return -EINVAL; + } + + aml_vdec_lock(ctx); + //aml_vcodec_dec_clock_on(&ctx->dev->pm);//debug_tmp + ret = ctx->dec_if->init(ctx, &ctx->drv_handle); + //aml_vcodec_dec_clock_off(&ctx->dev->pm); + aml_vdec_unlock(ctx); + + return ret; +} + +int vdec_if_probe(struct aml_vcodec_ctx *ctx, + struct aml_vcodec_mem *bs, void *out) +{ + int ret = 0; + + aml_vdec_lock(ctx); + ret = ctx->dec_if->probe(ctx->drv_handle, bs, out); + aml_vdec_unlock(ctx); + + return ret; +} + +int vdec_if_decode(struct aml_vcodec_ctx *ctx, struct aml_vcodec_mem *bs, + unsigned long int timestamp, bool *res_chg) +{ + int ret = 0; + + if (bs) { + if ((bs->dma_addr & 63) != 0) { + aml_v4l2_err("bs dma_addr should 64 byte align"); + return -EINVAL; + } + } + + /*if (fb) { + if (((fb->base_y.dma_addr & 511) != 0) || + ((fb->base_c.dma_addr & 511) != 0)) { + aml_v4l2_err("frame buffer dma_addr should 512 byte align"); + return -EINVAL; + } + }*/ + + if (ctx->drv_handle == 0) + return -EIO; + + aml_vdec_lock(ctx); + + aml_vcodec_set_curr_ctx(ctx->dev, ctx); + //aml_vcodec_dec_clock_on(&ctx->dev->pm);//debug_tmp + enable_irq(ctx->dev->dec_irq); + ret = ctx->dec_if->decode(ctx->drv_handle, bs, timestamp, res_chg); + //disable_irq(ctx->dev->dec_irq); + //aml_vcodec_dec_clock_off(&ctx->dev->pm); + aml_vcodec_set_curr_ctx(ctx->dev, NULL); + + aml_vdec_unlock(ctx); + + return ret; +} + +int vdec_if_get_param(struct aml_vcodec_ctx *ctx, + enum vdec_get_param_type type, void *out) +{ + int ret = 0; + + if (ctx->drv_handle == 0) + return -EIO; + + aml_vdec_lock(ctx); + ret = ctx->dec_if->get_param(ctx->drv_handle, type, out); + aml_vdec_unlock(ctx); + + return ret; +} + +void vdec_if_deinit(struct aml_vcodec_ctx *ctx) +{ + if (ctx->drv_handle == 0) + return; + + aml_vdec_lock(ctx); + //aml_vcodec_dec_clock_on(&ctx->dev->pm); + ctx->dec_if->deinit(ctx->drv_handle); + //aml_vcodec_dec_clock_off(&ctx->dev->pm); + aml_vdec_unlock(ctx); + + ctx->drv_handle = 0; +} diff --git a/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_if.h b/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_if.h new file mode 100644 index 000000000000..025e2cb5d29a --- /dev/null +++ b/drivers/amlogic/media_modules/amvdec_ports/vdec_drv_if.h @@ -0,0 +1,91 @@ +#ifndef _VDEC_DRV_IF_H_ +#define _VDEC_DRV_IF_H_ + +#include "aml_vcodec_drv.h" +#include "aml_vcodec_dec.h" +#include "aml_vcodec_util.h" + + +/** + * struct vdec_fb_status - decoder frame buffer status + * @FB_ST_NORMAL : initial state + * @FB_ST_DISPLAY : frmae buffer is ready to be displayed + * @FB_ST_FREE : frame buffer is not used by decoder any more + */ +enum vdec_fb_status { + FB_ST_NORMAL = 0, + FB_ST_DISPLAY = (1 << 0), + FB_ST_FREE = (1 << 1) +}; + +/* For GET_PARAM_DISP_FRAME_BUFFER and GET_PARAM_FREE_FRAME_BUFFER, + * the caller does not own the returned buffer. The buffer will not be + * released before vdec_if_deinit. + * GET_PARAM_DISP_FRAME_BUFFER : get next displayable frame buffer, + * struct vdec_fb** + * GET_PARAM_FREE_FRAME_BUFFER : get non-referenced framebuffer, vdec_fb** + * GET_PARAM_PIC_INFO : get picture info, struct vdec_pic_info* + * GET_PARAM_CROP_INFO : get crop info, struct v4l2_crop* + * GET_PARAM_DPB_SIZE : get dpb size, unsigned int* + */ +enum vdec_get_param_type { + GET_PARAM_DISP_FRAME_BUFFER, + GET_PARAM_FREE_FRAME_BUFFER, + GET_PARAM_PIC_INFO, + GET_PARAM_CROP_INFO, + GET_PARAM_DPB_SIZE +}; + +/** + * struct vdec_fb_node - decoder frame buffer node + * @list : list to hold this node + * @fb : point to frame buffer (vdec_fb), fb could point to frame buffer and + * working buffer this is for maintain buffers in different state + */ +struct vdec_fb_node { + struct list_head list; + struct vdec_fb *fb; +}; + +/** + * vdec_if_init() - initialize decode driver + * @ctx : [in] v4l2 context + * @fourcc : [in] video format fourcc, V4L2_PIX_FMT_H264/VP8/VP9.. + */ +int vdec_if_init(struct aml_vcodec_ctx *ctx, unsigned int fourcc); + +int vdec_if_probe(struct aml_vcodec_ctx *ctx, + struct aml_vcodec_mem *bs, void *out); + +/** + * vdec_if_deinit() - deinitialize decode driver + * @ctx : [in] v4l2 context + * + */ +void vdec_if_deinit(struct aml_vcodec_ctx *ctx); + +/** + * vdec_if_decode() - trigger decode + * @ctx : [in] v4l2 context + * @bs : [in] input bitstream + * @fb : [in] frame buffer to store decoded frame, when null menas parse + * header only + * @res_chg : [out] resolution change happens if current bs have different + * picture width/height + * Note: To flush the decoder when reaching EOF, set input bitstream as NULL. + * + * Return: 0 on success. -EIO on unrecoverable error. + */ +int vdec_if_decode(struct aml_vcodec_ctx *ctx, struct aml_vcodec_mem *bs, + unsigned long int timestamp, bool *res_chg); + +/** + * vdec_if_get_param() - get driver's parameter + * @ctx : [in] v4l2 context + * @type : [in] input parameter type + * @out : [out] buffer to store query result + */ +int vdec_if_get_param(struct aml_vcodec_ctx *ctx, enum vdec_get_param_type type, + void *out); + +#endif diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.c b/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.c index 3ec89d64509d..88ae74954920 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.c +++ b/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.c @@ -1657,6 +1657,7 @@ static void insert_picture_in_dpb(struct h264_dpb_stru *p_H264_Dpb, fs->pts = p->pts; fs->pts64 = p->pts64; + fs->timestamp = p->timestamp; } void reset_frame_store(struct h264_dpb_stru *p_H264_Dpb, diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.h b/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.h index dc3824f451a5..87641cbb9b1b 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.h +++ b/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/h264_dpb.h @@ -19,6 +19,7 @@ #define RRINT_FLAG_RPM 0x0400 #define DEBUG_DISABLE_RUNREADY_RMBUF 0x0800 #define PRINT_FLAG_DUMP_BUFSPEC 0x1000 +#define PRINT_FLAG_V4L_DETAIL 0x8000 #define DISABLE_ERROR_HANDLE 0x10000 #define DEBUG_DUMP_STAT 0x80000 @@ -646,6 +647,7 @@ struct StorablePicture { u32 pts; u64 pts64; + u64 timestamp; unsigned char data_flag; }; @@ -709,6 +711,7 @@ struct FrameStore { u32 pts; u64 pts64; + u64 timestamp; }; diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/vmh264.c b/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/vmh264.c index 690c04c3ba35..cb4e1b2850e0 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/vmh264.c +++ b/drivers/amlogic/media_modules/frame_provider/decoder/h264_multi/vmh264.c @@ -57,6 +57,7 @@ #include #include #include "../utils/config_parser.h" +#include "../../../amvdec_ports/vdec_drv_base.h" #undef pr_info #define pr_info printk @@ -765,6 +766,8 @@ struct vdec_h264_hw_s { struct mh264_ud_record_wait_node_t free_nodes[MAX_FREE_USERDATA_NODES]; int wait_for_udr_send; #endif + bool is_used_v4l; + void *v4l2_ctx; }; static u32 again_threshold = 0x40; @@ -812,7 +815,6 @@ static void h264_clear_dpb(struct vdec_h264_hw_s *hw); #define MAX_FRAME_4K_NUM 0x1200 #define FRAME_MMU_MAP_SIZE (MAX_FRAME_4K_NUM * 4) - /* 0:linear 1:32x32 2:64x32 ; m8baby test1902 */ static u32 mem_map_mode = H265_MEM_MAP_MODE; @@ -1551,23 +1553,113 @@ static int alloc_one_buf_spec(struct vdec_h264_hw_s *hw, int i) return 0; } +static int v4l_get_fb(struct aml_vcodec_ctx *ctx, struct vdec_fb **out) +{ + int ret = 0; + + ret = ctx->dec_if->get_param(ctx->drv_handle, + GET_PARAM_FREE_FRAME_BUFFER, out); + if (ret) + pr_err("get frame buffer failed.\n"); + + return ret; +} + +static int alloc_one_buf_spec_from_queue(struct vdec_h264_hw_s *hw, int idx) +{ + int ret = 0; + struct buffer_spec_s *bs = &hw->buffer_spec[idx]; + struct canvas_config_s *y_canvas_cfg = NULL; + struct canvas_config_s *c_canvas_cfg = NULL; + struct vdec_fb *fb = NULL; + unsigned int y_addr, c_addr; + + int buf_size = (hw->mb_total << 8) + (hw->mb_total << 7); + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], buf size: %d\n", __FUNCTION__, __LINE__, buf_size); + + if (IS_ERR_OR_NULL(hw->v4l2_ctx)) { + pr_err("the v4l context has err.\n"); + return -1; + } + + if (bs->cma_alloc_addr) + return 0; + + ret = v4l_get_fb(hw->v4l2_ctx, &fb); + if (ret) { + pr_err("get fb fail.\n"); + return ret; + } + + bs->cma_alloc_addr = (unsigned long)fb; + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], cma alloc addr: 0x%x\n", + __FUNCTION__, __LINE__, bs->cma_alloc_addr); + + y_addr = virt_to_phys(fb->base_y.va); + c_addr = virt_to_phys(fb->base_c.va); + + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], y_addr: %x, va: %p\n", + __FUNCTION__, __LINE__, y_addr, fb->base_y.va); + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], c_addr: %x, va: %p\n", + __FUNCTION__, __LINE__, c_addr, fb->base_c.va); + + bs->y_addr = y_addr; + bs->u_addr = c_addr; + bs->v_addr = c_addr; + + y_canvas_cfg = &bs->canvas_config[0]; + c_canvas_cfg = &bs->canvas_config[1]; + + y_canvas_cfg->phy_addr = y_addr; + y_canvas_cfg->width = hw->mb_width << 4; + y_canvas_cfg->height = hw->mb_height << 4; + y_canvas_cfg->block_mode = CANVAS_BLKMODE_LINEAR; + fb->base_y.bytes_used = y_canvas_cfg->width * y_canvas_cfg->height; + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], y_w: %d, y_h: %d\n", __FUNCTION__, __LINE__, + y_canvas_cfg->width,y_canvas_cfg->height); + + c_canvas_cfg->phy_addr = c_addr; + c_canvas_cfg->width = hw->mb_width << 4; + c_canvas_cfg->height = hw->mb_height << 3; + c_canvas_cfg->block_mode = CANVAS_BLKMODE_LINEAR; + fb->base_c.bytes_used = c_canvas_cfg->width * c_canvas_cfg->height; + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], c_w: %d, c_h: %d\n", __FUNCTION__, __LINE__, + c_canvas_cfg->width, c_canvas_cfg->height); + + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s, alloc buf for bufspec%d\n", __FUNCTION__, idx); + + return ret; +} + static void config_decode_canvas(struct vdec_h264_hw_s *hw, int i) { - canvas_config(hw->buffer_spec[i]. + canvas_config_ex(hw->buffer_spec[i]. y_canvas_index, hw->buffer_spec[i].y_addr, hw->mb_width << 4, hw->mb_height << 4, CANVAS_ADDR_NOWRAP, - CANVAS_BLKMODE_32X32); + hw->is_used_v4l ? CANVAS_BLKMODE_LINEAR : + CANVAS_BLKMODE_32X32, + hw->is_used_v4l ? 7 : 0); - canvas_config(hw->buffer_spec[i]. + canvas_config_ex(hw->buffer_spec[i]. u_canvas_index, hw->buffer_spec[i].u_addr, hw->mb_width << 4, hw->mb_height << 3, CANVAS_ADDR_NOWRAP, - CANVAS_BLKMODE_32X32); + hw->is_used_v4l ? CANVAS_BLKMODE_LINEAR : + CANVAS_BLKMODE_32X32, + hw->is_used_v4l ? 7 : 0); + WRITE_VREG(ANC0_CANVAS_ADDR + hw->buffer_spec[i].canvas_pos, spec2canvas(&hw->buffer_spec[i])); } @@ -1773,9 +1865,11 @@ static void dealloc_buf_specs(struct vdec_h264_hw_s *hw, if (!hw->mmu_enable) { if (hw->buffer_spec[i].cma_alloc_addr) { - decoder_bmmu_box_free_idx( - hw->bmmu_box, - i); + if (!hw->is_used_v4l) { + decoder_bmmu_box_free_idx( + hw->bmmu_box, + i); + } spin_lock_irqsave (&hw->bufspec_lock, flags); hw->buffer_spec[i].cma_alloc_addr = 0; @@ -1837,7 +1931,10 @@ unsigned char have_free_buf_spec(struct vdec_s *vdec) dealloc_buf_specs(hw, 0); if (max_alloc_buf_count == 0 || allocated_count < max_alloc_buf_count) { - if (alloc_one_buf_spec(hw, index) >= 0) + if (hw->is_used_v4l) { + if (alloc_one_buf_spec_from_queue(hw, index) >= 0) + ret = 1; + } else if (alloc_one_buf_spec(hw, index) >= 0) ret = 1; } mutex_unlock(&vmh264_mutex); @@ -1995,7 +2092,17 @@ int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame) vf->duration_pulldown = 0; vf->pts = frame->pts; vf->pts_us64 = frame->pts64; + vf->timestamp = frame->timestamp; vf->index = VF_INDEX(frame->index, buffer_index); + + if (hw->is_used_v4l) { + vf->v4l_mem_handle + = hw->buffer_spec[buffer_index].cma_alloc_addr; + dpb_print(DECODE_ID(hw), PRINT_FLAG_V4L_DETAIL, + "%s() [%d], v4l mem handle: 0x%x\n", + __FUNCTION__, __LINE__, vf->v4l_mem_handle); + } + if (hw->mmu_enable) { if (hw->double_write_mode & 0x10) { /* double write only */ @@ -2040,6 +2147,7 @@ int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame) VIDTYPE_COMPRESS | VIDTYPE_VIU_FIELD; vf->canvas0Addr = vf->canvas1Addr = 0; } + vf->bitdepth = BITDEPTH_Y8 | BITDEPTH_U8 | BITDEPTH_V8; @@ -2106,6 +2214,38 @@ int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame) return 0; } +int notify_v4l_eos(struct vdec_s *vdec) +{ + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + struct vframe_s *vf = NULL; + struct vdec_fb *fb = NULL; + + if (hw->is_used_v4l && hw->eos) { + if (kfifo_get(&hw->newframe_q, &vf) == 0 || vf == NULL) { + dpb_print(DECODE_ID(hw), PRINT_FLAG_ERROR, + "%s fatal error, no available buffer slot.\n", + __func__); + return -1; + } + + if (v4l_get_fb(hw->v4l2_ctx, &fb)) { + pr_err("get fb fail.\n"); + return -1; + } + + vf->timestamp = ULONG_MAX; + vf->v4l_mem_handle = (unsigned long)fb; + vf->flag = VFRAME_FLAG_EMPTY_FRAME_V4L; + + kfifo_put(&hw->display_q, (const struct vframe_s *)vf); + pr_info("264 EOS notify \n"); + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + } + + return 0; +} + /****************** * Hardware config */ @@ -3070,6 +3210,7 @@ static void vh264_vf_put(struct vframe_s *vf, void *op_arg) struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; int buf_spec_num; int frame_index; + if (vf == (&hw->vframe_dummy)) return; if (vf->index == -1) { @@ -3477,10 +3618,17 @@ static int vh264_set_params(struct vdec_h264_hw_s *hw, if (!hw->mmu_enable) { config_buf_specs(vdec); i = get_buf_spec_by_canvas_pos(hw, 0); - if (alloc_one_buf_spec(hw, i) >= 0) - config_decode_canvas(hw, i); - else - ret = -1; + + if (hw->is_used_v4l) { + ret = alloc_one_buf_spec_from_queue(hw, i); + if (!ret) + config_decode_canvas(hw, i); + } else { + if (alloc_one_buf_spec(hw, i) >= 0) + config_decode_canvas(hw, i); + else + ret = -1; + } } else { if (hw->double_write_mode) { config_buf_specs_ex(vdec); @@ -3548,11 +3696,22 @@ static int vh264_set_params(struct vdec_h264_hw_s *hw, if (!hw->mmu_enable) { mutex_lock(&vmh264_mutex); if (ret >= 0 && hw->decode_pic_count == 0) { + int buf_cnt; /* h264_reconfig: alloc later*/ - for (j = 1; j < hw->dpb.mDPB.size; j++) { + buf_cnt = hw->dpb.mDPB.size; + + for (j = 1; j < buf_cnt; j++) { i = get_buf_spec_by_canvas_pos(hw, j); - if (alloc_one_buf_spec(hw, i) < 0) + + if (hw->is_used_v4l && + alloc_one_buf_spec_from_queue + (hw, i) < 0) { + if (j++ != buf_cnt) + ret = -1; break; + } else if (alloc_one_buf_spec(hw, i) < 0) + break; + config_decode_canvas(hw, i); } } @@ -4212,6 +4371,8 @@ pic_done_proc: hw->chunk->pts; p_H264_Dpb->mVideo.dec_picture->pts64 = hw->chunk->pts64; + p_H264_Dpb->mVideo.dec_picture->timestamp = + hw->chunk->timestamp; #ifdef MH264_USERDATA_ENABLE vmh264_udc_fill_vpts(hw, p_H264_Dpb->mSlice.slice_type, @@ -5110,11 +5271,17 @@ static void vh264_local_init(struct vdec_h264_hw_s *hw) hw->frame_dur = 96000/30; } - pr_info - ("H264 sysinfo: %dx%d duration=%d, pts_outside=%d\n", - hw->frame_width, hw->frame_height, hw->frame_dur, hw->pts_outside); - pr_debug("sync_outside=%d, use_idr_framerate=%d\n", - hw->sync_outside, hw->use_idr_framerate); + hw->is_used_v4l = (((unsigned long) + hw->vh264_amstream_dec_info.param & 0x80) >> 7); + + if (hw->is_used_v4l) + mem_map_mode = CANVAS_BLKMODE_LINEAR; + + pr_info("H264 sysinfo: %dx%d duration=%d, pts_outside=%d\n", + hw->frame_width, hw->frame_height, hw->frame_dur, hw->pts_outside); + pr_debug("sync_outside=%d, use_idr_framerate=%d, is_used_v4l: %d\n", + hw->sync_outside, hw->use_idr_framerate, hw->is_used_v4l); + if (i_only_flag & 0x100) hw->i_only = i_only_flag & 0xff; @@ -6300,7 +6467,10 @@ result_done: amhevc_stop(); hw->eos = 1; flush_dpb(p_H264_Dpb); + if (hw->is_used_v4l) + notify_v4l_eos(hw_to_vdec(hw)); vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); + vdec_clean_input(vdec); } else if (hw->dec_result == DEC_RESULT_FORCE_EXIT) { dpb_print(DECODE_ID(hw), PRINT_FLAG_VDEC_STATUS, "%s: force exit\n", @@ -6619,7 +6789,27 @@ static void run(struct vdec_s *vdec, unsigned long mask, static void reset(struct vdec_s *vdec) { - pr_info("ammvdec_h264: reset.\n"); + struct vdec_h264_hw_s *hw = + (struct vdec_h264_hw_s *)vdec->private; + + cancel_work_sync(&hw->work); + cancel_work_sync(&hw->notify_work); + cancel_work_sync(&hw->user_data_work); + if (hw->stat & STAT_VDEC_RUN) { + amhevc_stop(); + hw->stat &= ~STAT_VDEC_RUN; + } + + if (hw->stat & STAT_TIMER_ARM) { + del_timer_sync(&hw->check_timer); + hw->stat &= ~STAT_TIMER_ARM; + } + hw->eos = 0; + hw->decode_pic_count = 0; + hw->dec_result = DEC_RESULT_NONE; + reset_process_time(hw); + h264_reset_bufmgr(vdec); + dpb_print(DECODE_ID(hw), 0, "%s\n", __func__); } static void h264_reconfig(struct vdec_h264_hw_s *hw) @@ -6856,6 +7046,10 @@ static int ammvdec_h264_probe(struct platform_device *pdev) } hw->id = pdev->id; hw->platform_dev = pdev; + + /* the ctx from v4l2 driver. */ + hw->v4l2_ctx = pdata->private; + platform_set_drvdata(pdev, pdata); hw->mmu_enable = 0; @@ -7057,6 +7251,7 @@ static int ammvdec_h264_remove(struct platform_device *pdev) vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_DISCONNECTED); ammvdec_h264_mmu_release(hw); h264_free_hw_stru(&pdev->dev, (void *)hw); + return 0; } @@ -7138,7 +7333,6 @@ static void __exit ammvdec_h264_driver_remove_module(void) } /****************************************/ - module_param(h264_debug_flag, uint, 0664); MODULE_PARM_DESC(h264_debug_flag, "\n ammvdec_h264 h264_debug_flag\n"); diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.c b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.c index e7fb7514ea55..dd3a94e9c226 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.c +++ b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.c @@ -526,6 +526,13 @@ int vdec_set_pts(struct vdec_s *vdec, u32 pts) } EXPORT_SYMBOL(vdec_set_pts); +void vdec_set_timestamp(struct vdec_s *vdec, u64 timestamp) +{ + vdec->timestamp = timestamp; + vdec->timestamp_valid = true; +} +EXPORT_SYMBOL(vdec_set_timestamp); + int vdec_set_pts64(struct vdec_s *vdec, u64 pts64) { vdec->pts64 = pts64; @@ -537,6 +544,12 @@ int vdec_set_pts64(struct vdec_s *vdec, u64 pts64) } EXPORT_SYMBOL(vdec_set_pts64); +int vdec_get_status(struct vdec_s *vdec) +{ + return vdec->status; +} +EXPORT_SYMBOL(vdec_get_status); + void vdec_set_status(struct vdec_s *vdec, int status) { //trace_vdec_set_status(vdec, status);/*DEBUG_TMP*/ @@ -1554,6 +1567,11 @@ s32 vdec_init(struct vdec_s *vdec, int is_4k) "ppmgr amlvideo.1 amvide2"); snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE, "vdec-map-%d", vdec->id); + } else if (p->frame_base_video_path == FRAME_BASE_PATH_V4L_VIDEO) { + snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE, + "%s %s", vdec->vf_provider_name, "v4l2-video"); + snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE, + "vdec-map-%d", vdec->id); } if (vfm_map_add(vdec->vfm_map_id, @@ -1710,6 +1728,11 @@ int vdec_reset(struct vdec_s *vdec) vdec_input_release(&vdec->input); + vdec_input_init(&vdec->input, vdec); + + vdec_input_prepare_bufs(&vdec->input, vdec->sys_info->width, + vdec->sys_info->height); + vf_reg_provider(&vdec->vframe_provider); vf_notify_receiver(vdec->vf_provider_name, VFRAME_EVENT_PROVIDER_START, vdec); diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.h b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.h index 90158f300eae..a5db7617430e 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.h +++ b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec.h @@ -175,6 +175,8 @@ struct vdec_s { u32 pts; u64 pts64; bool pts_valid; + u64 timestamp; + bool timestamp_valid; int flag; int sched; int need_more_data; @@ -417,4 +419,8 @@ int vdec_get_debug_flags(void); unsigned char is_mult_inc(unsigned int); +int vdec_get_status(struct vdec_s *vdec); + +void vdec_set_timestamp(struct vdec_s *vdec, u64 timestamp); + #endif /* VDEC_H */ diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.c b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.c index 439e513677d3..74dcb879b9f0 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.c +++ b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.c @@ -47,7 +47,7 @@ #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 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); @@ -551,7 +551,7 @@ static struct vframe_block_list_s * } return block; } -static int vdec_input_get_duration_u64(struct vdec_input_s *input) +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 && @@ -571,6 +571,8 @@ static int vdec_input_get_duration_u64(struct vdec_input_s *input) duration = 0; return duration; } +EXPORT_SYMBOL(vdec_input_get_duration_u64); + /* ret >= 13: have enough buffer, blocked add more buffers */ @@ -758,6 +760,10 @@ int vdec_input_add_frame(struct vdec_input_s *input, const char *buf, 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) { diff --git a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.h b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.h index d95e13f97c42..e370cffa67a9 100644 --- a/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.h +++ b/drivers/amlogic/media_modules/frame_provider/decoder/utils/vdec_input.h @@ -49,6 +49,8 @@ struct vframe_chunk_s { u32 pading_size; u64 pts64; bool pts_valid; + u64 timestamp; + bool timestamp_valid; u64 sequence; struct vframe_block_list_s *block; }; @@ -161,4 +163,6 @@ int vdec_input_dump_chunks(struct vdec_input_s *input, int vdec_input_dump_blocks(struct vdec_input_s *input, char *bufs, int size); +int vdec_input_get_duration_u64(struct vdec_input_s *input); + #endif /* VDEC_INPUT_H */ diff --git a/drivers/amlogic/media_modules/stream_input/amports/adec.c b/drivers/amlogic/media_modules/stream_input/amports/adec.c index b1bd0726fad8..923128f9dfed 100644 --- a/drivers/amlogic/media_modules/stream_input/amports/adec.c +++ b/drivers/amlogic/media_modules/stream_input/amports/adec.c @@ -230,6 +230,7 @@ s32 adec_init(struct stream_port_s *port) astream_dev->format = NULL; return 0; } +EXPORT_SYMBOL(adec_init); s32 adec_release(enum aformat_e vf) { @@ -242,6 +243,7 @@ s32 adec_release(enum aformat_e vf) return 0; } +EXPORT_SYMBOL(adec_release); int amstream_adec_show_fun(const char *trigger, int id, char *sbuf, int size) { diff --git a/drivers/amlogic/media_modules/stream_input/parser/esparser.c b/drivers/amlogic/media_modules/stream_input/parser/esparser.c index f04b9decb969..3fd881c81941 100644 --- a/drivers/amlogic/media_modules/stream_input/parser/esparser.c +++ b/drivers/amlogic/media_modules/stream_input/parser/esparser.c @@ -609,6 +609,7 @@ Err_1: mutex_unlock(&esparser_mutex); return r; } +EXPORT_SYMBOL(esparser_init); void esparser_audio_reset_s(struct stream_buf_s *buf) { @@ -714,6 +715,7 @@ void esparser_release(struct stream_buf_s *buf) buf->flag &= ~BUF_FLAG_PARSER; pts_stop(pts_type); } +EXPORT_SYMBOL(esparser_release); ssize_t drm_write(struct file *file, struct stream_buf_s *stbuf, const char __user *buf, size_t count) @@ -821,6 +823,8 @@ ssize_t drm_write(struct file *file, struct stream_buf_s *stbuf, return re_count; } +EXPORT_SYMBOL(drm_write); + /* *flags: *1:phy @@ -914,7 +918,7 @@ ssize_t esparser_write(struct file *file, } return esparser_write_ex(file, stbuf, buf, count, 0); } - +EXPORT_SYMBOL(esparser_write); void esparser_sub_reset(void) { diff --git a/drivers/amlogic/media_modules/stream_input/parser/psparser.c b/drivers/amlogic/media_modules/stream_input/parser/psparser.c index c3b63c181a98..9f5ef8d46428 100644 --- a/drivers/amlogic/media_modules/stream_input/parser/psparser.c +++ b/drivers/amlogic/media_modules/stream_input/parser/psparser.c @@ -1025,6 +1025,7 @@ void psparser_release(void) pr_info("psparser release subtitle info\n"); #endif } +EXPORT_SYMBOL(psparser_release); ssize_t psparser_write(struct file *file, struct stream_buf_s *vbuf, diff --git a/drivers/amlogic/media_modules/stream_input/parser/streambuf.c b/drivers/amlogic/media_modules/stream_input/parser/streambuf.c index 14991a940335..256afb99a80a 100644 --- a/drivers/amlogic/media_modules/stream_input/parser/streambuf.c +++ b/drivers/amlogic/media_modules/stream_input/parser/streambuf.c @@ -183,6 +183,7 @@ int stbuf_fetch_init(void) } return 0; } +EXPORT_SYMBOL(stbuf_fetch_init); void stbuf_fetch_release(void) { @@ -362,6 +363,7 @@ s32 stbuf_init(struct stream_buf_s *buf, struct vdec_s *vdec, bool is_multi) MEM_CTRL_EMPTY_EN); return 0; } +EXPORT_SYMBOL(stbuf_init); void stbuf_vdec2_init(struct stream_buf_s *buf) { @@ -425,6 +427,7 @@ void stbuf_release(struct stream_buf_s *buf, bool is_multi) } buf->flag &= ~BUF_FLAG_IN_USE; } +EXPORT_SYMBOL(stbuf_release); u32 stbuf_sub_rp_get(void) { diff --git a/drivers/amlogic/media_modules/stream_input/parser/tsdemux.c b/drivers/amlogic/media_modules/stream_input/parser/tsdemux.c index a93396f350c0..e4e10e4d8a97 100644 --- a/drivers/amlogic/media_modules/stream_input/parser/tsdemux.c +++ b/drivers/amlogic/media_modules/stream_input/parser/tsdemux.c @@ -747,6 +747,7 @@ void tsdemux_release(void) amports_switch_gate("demux", 0); } +EXPORT_SYMBOL(tsdemux_release); static int limited_delay_check(struct file *file, struct stream_buf_s *vbuf,