| /* |
| * Copyright (C) 2020 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| * |
| * Description: |
| */ |
| |
| #include <linux/types.h> |
| #include <linux/delay.h> |
| #include <linux/videodev2.h> |
| #include <linux/sched/clock.h> |
| #include <uapi/linux/sched/types.h> |
| #include <linux/amlogic/meson_uvm_core.h> |
| #include <linux/amlogic/media/ge2d/ge2d.h> |
| #include <linux/amlogic/media/canvas/canvas_mgr.h> |
| |
| #include "../common/chips/decoder_cpu_ver_info.h" |
| #include "aml_vcodec_ge2d.h" |
| #include "aml_vcodec_adapt.h" |
| #include "vdec_drv_if.h" |
| |
| #define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2 |
| #include <trace/events/meson_atrace.h> |
| |
| #define GE2D_BUF_GET_IDX(ge2d_buf) (ge2d_buf->aml_buf->vb.vb2_buf.index) |
| #define INPUT_PORT 0 |
| #define OUTPUT_PORT 1 |
| |
| extern int dump_ge2d_input; |
| extern int ge2d_bypass_frames; |
| |
| enum GE2D_FLAG { |
| GE2D_FLAG_P = 0x1, |
| GE2D_FLAG_I = 0x2, |
| GE2D_FLAG_EOS = 0x4, |
| GE2D_FLAG_BUF_BY_PASS = 0x8, |
| GE2D_FLAG_MAX = 0x7FFFFFFF, |
| }; |
| |
| enum videocom_source_type { |
| DECODER_8BIT_NORMAL = 0, |
| DECODER_8BIT_BOTTOM, |
| DECODER_8BIT_TOP, |
| DECODER_10BIT_NORMAL, |
| DECODER_10BIT_BOTTOM, |
| DECODER_10BIT_TOP |
| }; |
| |
| #ifndef CONFIG_AMLOGIC_MEDIA_GE2D |
| inline void stretchblt_noalpha(struct ge2d_context_s *wq, |
| int src_x, int src_y, int src_w, int src_h, |
| int dst_x, int dst_y, int dst_w, int dst_h) { return; } |
| inline int ge2d_context_config_ex(struct ge2d_context_s *context, |
| struct config_para_ex_s *ge2d_config) { return -1; } |
| inline struct ge2d_context_s *create_ge2d_work_queue(void) { return NULL; } |
| inline int destroy_ge2d_work_queue(struct ge2d_context_s *ge2d_work_queue) { return -1; } |
| #endif |
| |
| static int get_source_type(struct vframe_s *vf) |
| { |
| enum videocom_source_type ret; |
| int interlace_mode; |
| |
| interlace_mode = vf->type & VIDTYPE_TYPEMASK; |
| |
| if ((vf->bitdepth & BITDEPTH_Y10) && |
| (!(vf->type & VIDTYPE_COMPRESS)) && |
| (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)) { |
| if (interlace_mode == VIDTYPE_INTERLACE_TOP) |
| ret = DECODER_10BIT_TOP; |
| else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM) |
| ret = DECODER_10BIT_BOTTOM; |
| else |
| ret = DECODER_10BIT_NORMAL; |
| } else { |
| if (interlace_mode == VIDTYPE_INTERLACE_TOP) |
| ret = DECODER_8BIT_TOP; |
| else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM) |
| ret = DECODER_8BIT_BOTTOM; |
| else |
| ret = DECODER_8BIT_NORMAL; |
| } |
| |
| return ret; |
| } |
| |
| static int get_input_format(struct vframe_s *vf) |
| { |
| int format = GE2D_FORMAT_M24_YUV420; |
| enum videocom_source_type soure_type; |
| |
| soure_type = get_source_type(vf); |
| |
| switch (soure_type) { |
| case DECODER_8BIT_NORMAL: |
| if (vf->type & VIDTYPE_VIU_422) |
| format = GE2D_FORMAT_S16_YUV422; |
| else if (vf->type & VIDTYPE_VIU_NV21) |
| format = GE2D_FORMAT_M24_NV21; |
| else if (vf->type & VIDTYPE_VIU_NV12) |
| format = GE2D_FORMAT_M24_NV12; |
| else if (vf->type & VIDTYPE_VIU_444) |
| format = GE2D_FORMAT_S24_YUV444; |
| else |
| format = GE2D_FORMAT_M24_YUV420; |
| break; |
| case DECODER_8BIT_BOTTOM: |
| if (vf->type & VIDTYPE_VIU_422) |
| format = GE2D_FORMAT_S16_YUV422 |
| | (GE2D_FORMAT_S16_YUV422B & (3 << 3)); |
| else if (vf->type & VIDTYPE_VIU_NV21) |
| format = GE2D_FORMAT_M24_NV21 |
| | (GE2D_FORMAT_M24_NV21B & (3 << 3)); |
| else if (vf->type & VIDTYPE_VIU_NV12) |
| format = GE2D_FORMAT_M24_NV12 |
| | (GE2D_FORMAT_M24_NV12B & (3 << 3)); |
| else if (vf->type & VIDTYPE_VIU_444) |
| format = GE2D_FORMAT_S24_YUV444 |
| | (GE2D_FORMAT_S24_YUV444B & (3 << 3)); |
| else |
| format = GE2D_FORMAT_M24_YUV420 |
| | (GE2D_FMT_M24_YUV420B & (3 << 3)); |
| break; |
| case DECODER_8BIT_TOP: |
| if (vf->type & VIDTYPE_VIU_422) |
| format = GE2D_FORMAT_S16_YUV422 |
| | (GE2D_FORMAT_S16_YUV422T & (3 << 3)); |
| else if (vf->type & VIDTYPE_VIU_NV21) |
| format = GE2D_FORMAT_M24_NV21 |
| | (GE2D_FORMAT_M24_NV21T & (3 << 3)); |
| else if (vf->type & VIDTYPE_VIU_NV12) |
| format = GE2D_FORMAT_M24_NV12 |
| | (GE2D_FORMAT_M24_NV12T & (3 << 3)); |
| else if (vf->type & VIDTYPE_VIU_444) |
| format = GE2D_FORMAT_S24_YUV444 |
| | (GE2D_FORMAT_S24_YUV444T & (3 << 3)); |
| else |
| format = GE2D_FORMAT_M24_YUV420 |
| | (GE2D_FMT_M24_YUV420T & (3 << 3)); |
| break; |
| case DECODER_10BIT_NORMAL: |
| if (vf->type & VIDTYPE_VIU_422) { |
| if (vf->bitdepth & FULL_PACK_422_MODE) |
| format = GE2D_FORMAT_S16_10BIT_YUV422; |
| else |
| format = GE2D_FORMAT_S16_12BIT_YUV422; |
| } |
| break; |
| case DECODER_10BIT_BOTTOM: |
| if (vf->type & VIDTYPE_VIU_422) { |
| if (vf->bitdepth & FULL_PACK_422_MODE) |
| format = GE2D_FORMAT_S16_10BIT_YUV422 |
| | (GE2D_FORMAT_S16_10BIT_YUV422B |
| & (3 << 3)); |
| else |
| format = GE2D_FORMAT_S16_12BIT_YUV422 |
| | (GE2D_FORMAT_S16_12BIT_YUV422B |
| & (3 << 3)); |
| } |
| break; |
| case DECODER_10BIT_TOP: |
| if (vf->type & VIDTYPE_VIU_422) { |
| if (vf->bitdepth & FULL_PACK_422_MODE) |
| format = GE2D_FORMAT_S16_10BIT_YUV422 |
| | (GE2D_FORMAT_S16_10BIT_YUV422T |
| & (3 << 3)); |
| else |
| format = GE2D_FORMAT_S16_12BIT_YUV422 |
| | (GE2D_FORMAT_S16_12BIT_YUV422T |
| & (3 << 3)); |
| } |
| break; |
| default: |
| format = GE2D_FORMAT_M24_YUV420; |
| } |
| return format; |
| } |
| |
| static int v4l_ge2d_empty_input_done(struct aml_v4l2_ge2d_buf *buf) |
| { |
| struct aml_v4l2_ge2d *ge2d = buf->caller_data; |
| struct vdec_v4l2_buffer *fb = NULL; |
| bool eos = false; |
| |
| if (!ge2d || !ge2d->ctx) { |
| v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, |
| "fatal %s %d ge2d:%px\n", |
| __func__, __LINE__, ge2d); |
| return -1; |
| } |
| |
| fb = &buf->aml_buf->frame_buffer; |
| eos = (buf->flag & GE2D_FLAG_EOS); |
| |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "ge2d_input done: vf:%px, idx: %d, flag(vf:%x ge2d:%x) %s, ts:%lld, " |
| "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n", |
| buf->vf, |
| buf->vf->index, |
| buf->vf->flag, |
| buf->flag, |
| eos ? "eos" : "", |
| buf->vf->timestamp, |
| kfifo_len(&ge2d->input), |
| kfifo_len(&ge2d->output), |
| kfifo_len(&ge2d->frame), |
| kfifo_len(&ge2d->in_done_q), |
| kfifo_len(&ge2d->out_done_q)); |
| |
| fb->task->recycle(fb->task, TASK_TYPE_GE2D); |
| |
| kfifo_put(&ge2d->input, buf); |
| |
| ATRACE_COUNTER("VC_IN_GE2D-1.recycle", fb->buf_idx); |
| |
| return 0; |
| } |
| |
| static int v4l_ge2d_fill_output_done(struct aml_v4l2_ge2d_buf *buf) |
| { |
| struct aml_v4l2_ge2d *ge2d = buf->caller_data; |
| struct vdec_v4l2_buffer *fb = NULL; |
| bool bypass = false; |
| bool eos = false; |
| |
| if (!ge2d || !ge2d->ctx) { |
| v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, |
| "fatal %s %d ge2d:%px\n", |
| __func__, __LINE__, ge2d); |
| return -1; |
| } |
| |
| fb = &buf->aml_buf->frame_buffer; |
| eos = (buf->flag & GE2D_FLAG_EOS); |
| bypass = (buf->flag & GE2D_FLAG_BUF_BY_PASS); |
| |
| /* recovery fb handle. */ |
| buf->vf->v4l_mem_handle = (ulong)fb; |
| |
| kfifo_put(&ge2d->out_done_q, buf); |
| |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "ge2d_output done: vf:%px, idx:%d, flag(vf:%x ge2d:%x) %s, ts:%lld, " |
| "in:%d, out:%d, vf:%d, in done:%d, out done:%d, wxh:%ux%u\n", |
| buf->vf, |
| buf->vf->index, |
| buf->vf->flag, |
| buf->flag, |
| eos ? "eos" : "", |
| buf->vf->timestamp, |
| kfifo_len(&ge2d->input), |
| kfifo_len(&ge2d->output), |
| kfifo_len(&ge2d->frame), |
| kfifo_len(&ge2d->in_done_q), |
| kfifo_len(&ge2d->out_done_q), |
| buf->vf->width, buf->vf->height); |
| |
| ATRACE_COUNTER("VC_OUT_GE2D-2.submit", fb->buf_idx); |
| |
| fb->task->submit(fb->task, TASK_TYPE_GE2D); |
| |
| ge2d->out_num[OUTPUT_PORT]++; |
| |
| return 0; |
| } |
| |
| static void ge2d_vf_get(void *caller, struct vframe_s **vf_out) |
| { |
| struct aml_v4l2_ge2d *ge2d = (struct aml_v4l2_ge2d *)caller; |
| struct aml_v4l2_ge2d_buf *buf = NULL; |
| struct vdec_v4l2_buffer *fb = NULL; |
| struct vframe_s *vf = NULL; |
| bool bypass = false; |
| bool eos = false; |
| |
| if (!ge2d || !ge2d->ctx) { |
| v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, |
| "fatal %s %d ge2d:%px\n", |
| __func__, __LINE__, ge2d); |
| return; |
| } |
| |
| if (kfifo_get(&ge2d->out_done_q, &buf)) { |
| fb = &buf->aml_buf->frame_buffer; |
| eos = (buf->flag & GE2D_FLAG_EOS); |
| bypass = (buf->flag & GE2D_FLAG_BUF_BY_PASS); |
| vf = buf->vf; |
| |
| if (eos) { |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_DETAIL, |
| "%s %d got eos\n", |
| __func__, __LINE__); |
| vf->type |= VIDTYPE_V4L_EOS; |
| vf->flag = VFRAME_FLAG_EMPTY_FRAME_V4L; |
| } |
| |
| *vf_out = vf; |
| |
| ATRACE_COUNTER("VC_OUT_GE2D-3.vf_get", fb->buf_idx); |
| |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "%s: vf:%px, index:%d, flag(vf:%x ge2d:%x), ts:%lld, type:%x, wxh:%ux%u\n", |
| __func__, vf, |
| vf->index, |
| vf->flag, |
| buf->flag, |
| vf->timestamp, vf->type, vf->width, vf->height); |
| } |
| } |
| |
| static void ge2d_vf_put(void *caller, struct vframe_s *vf) |
| { |
| struct aml_v4l2_ge2d *ge2d = (struct aml_v4l2_ge2d *)caller; |
| struct vdec_v4l2_buffer *fb = NULL; |
| struct aml_video_dec_buf *aml_buf = NULL; |
| struct aml_v4l2_ge2d_buf *buf = NULL; |
| bool bypass = false; |
| bool eos = false; |
| |
| fb = (struct vdec_v4l2_buffer *) vf->v4l_mem_handle; |
| aml_buf = container_of(fb, struct aml_video_dec_buf, frame_buffer); |
| buf = (struct aml_v4l2_ge2d_buf *) aml_buf->ge2d_buf_handle; |
| eos = (buf->flag & GE2D_FLAG_EOS); |
| bypass = (buf->flag & GE2D_FLAG_BUF_BY_PASS); |
| |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "%s: vf:%px, index:%d, flag(vf:%x ge2d:%x), ts:%lld\n", |
| __func__, vf, |
| vf->index, |
| vf->flag, |
| buf->flag, |
| vf->timestamp); |
| |
| ATRACE_COUNTER("VC_IN_GE2D-0.vf_put", fb->buf_idx); |
| |
| mutex_lock(&ge2d->output_lock); |
| kfifo_put(&ge2d->frame, vf); |
| kfifo_put(&ge2d->output, buf); |
| mutex_unlock(&ge2d->output_lock); |
| up(&ge2d->sem_out); |
| } |
| |
| static int aml_v4l2_ge2d_thread(void* param) |
| { |
| struct aml_v4l2_ge2d* ge2d = param; |
| struct aml_vcodec_ctx *ctx = ge2d->ctx; |
| struct config_para_ex_s ge2d_config; |
| u32 src_fmt = 0, dst_fmt = 0; |
| struct canvas_s cd; |
| ulong start_time; |
| |
| v4l_dbg(ctx, V4L_DEBUG_GE2D_DETAIL, "enter ge2d thread\n"); |
| while (ge2d->running) { |
| struct aml_v4l2_ge2d_buf *in_buf; |
| struct aml_v4l2_ge2d_buf *out_buf = NULL; |
| struct vframe_s *vf_out = NULL; |
| struct vdec_v4l2_buffer *fb; |
| |
| if (down_interruptible(&ge2d->sem_in)) |
| goto exit; |
| retry: |
| if (!ge2d->running) |
| break; |
| |
| if (kfifo_is_empty(&ge2d->output)) { |
| if (down_interruptible(&ge2d->sem_out)) |
| goto exit; |
| goto retry; |
| } |
| |
| mutex_lock(&ge2d->output_lock); |
| if (!kfifo_get(&ge2d->output, &out_buf)) { |
| mutex_unlock(&ge2d->output_lock); |
| v4l_dbg(ctx, 0, "ge2d can not get output\n"); |
| goto exit; |
| } |
| mutex_unlock(&ge2d->output_lock); |
| |
| /* bind v4l2 buffers */ |
| if (!out_buf->aml_buf) { |
| struct vdec_v4l2_buffer *out; |
| |
| if (!ctx->fb_ops.query(&ctx->fb_ops, &ge2d->fb_token)) { |
| usleep_range(500, 550); |
| mutex_lock(&ge2d->output_lock); |
| kfifo_put(&ge2d->output, out_buf); |
| mutex_unlock(&ge2d->output_lock); |
| goto retry; |
| } |
| |
| if (ctx->fb_ops.alloc(&ctx->fb_ops, ge2d->fb_token, &out, AML_FB_REQ_GE2D)) { |
| usleep_range(5000, 5500); |
| mutex_lock(&ge2d->output_lock); |
| kfifo_put(&ge2d->output, out_buf); |
| mutex_unlock(&ge2d->output_lock); |
| goto retry; |
| } |
| |
| out_buf->aml_buf = container_of(out, |
| struct aml_video_dec_buf, frame_buffer); |
| out_buf->aml_buf->ge2d_buf_handle = (ulong) out_buf; |
| v4l_dbg(ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "ge2d bind buf:%d to ge2d_buf:%px\n", |
| GE2D_BUF_GET_IDX(out_buf), out_buf); |
| |
| out->m.mem[0].bytes_used = out->m.mem[0].size; |
| out->m.mem[1].bytes_used = out->m.mem[1].size; |
| } |
| |
| /* safe to pop in_buf */ |
| if (!kfifo_get(&ge2d->in_done_q, &in_buf)) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "ge2d can not get input\n"); |
| goto exit; |
| } |
| |
| mutex_lock(&ge2d->output_lock); |
| if (!kfifo_get(&ge2d->frame, &vf_out)) { |
| mutex_unlock(&ge2d->output_lock); |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "ge2d can not get frame\n"); |
| goto exit; |
| } |
| mutex_unlock(&ge2d->output_lock); |
| |
| fb = &out_buf->aml_buf->frame_buffer; |
| fb->status = FB_ST_GE2D; |
| |
| /* fill output vframe information. */ |
| memcpy(vf_out, in_buf->vf, sizeof(*vf_out)); |
| memcpy(vf_out->canvas0_config, |
| in_buf->vf->canvas0_config, |
| 2 * sizeof(struct canvas_config_s)); |
| |
| vf_out->canvas0_config[0].phy_addr = fb->m.mem[0].addr; |
| if (fb->num_planes == 1) { |
| vf_out->canvas0_config[1].phy_addr = |
| fb->m.mem[0].addr + fb->m.mem[0].offset; |
| vf_out->canvas0_config[2].phy_addr = |
| fb->m.mem[0].addr + fb->m.mem[0].offset |
| + (fb->m.mem[0].offset >> 2); |
| } else { |
| vf_out->canvas0_config[1].phy_addr = |
| fb->m.mem[1].addr; |
| vf_out->canvas0_config[2].phy_addr = |
| fb->m.mem[2].addr; |
| } |
| |
| /* fill outbuf parms. */ |
| out_buf->vf = vf_out; |
| out_buf->flag = 0; |
| out_buf->caller_data = ge2d; |
| |
| /* fill inbuf parms. */ |
| in_buf->caller_data = ge2d; |
| |
| memset(&ge2d_config, 0, sizeof(ge2d_config)); |
| |
| src_fmt = get_input_format(in_buf->vf); |
| if (in_buf->vf->canvas0_config[0].endian == 7) |
| src_fmt |= GE2D_BIG_ENDIAN; |
| else |
| src_fmt |= GE2D_LITTLE_ENDIAN; |
| |
| /* negotiate format of destination */ |
| dst_fmt = get_input_format(in_buf->vf); |
| if (ge2d->work_mode & GE2D_MODE_CONVERT_NV12) |
| dst_fmt |= GE2D_FORMAT_M24_NV12; |
| else if (ge2d->work_mode & GE2D_MODE_CONVERT_NV21) |
| dst_fmt |= GE2D_FORMAT_M24_NV21; |
| |
| if (ge2d->work_mode & GE2D_MODE_CONVERT_LE) |
| dst_fmt |= GE2D_LITTLE_ENDIAN; |
| else |
| dst_fmt |= GE2D_BIG_ENDIAN; |
| |
| if ((dst_fmt & GE2D_COLOR_MAP_MASK) == GE2D_COLOR_MAP_NV12) { |
| vf_out->type |= VIDTYPE_VIU_NV12; |
| vf_out->type &= ~VIDTYPE_VIU_NV21; |
| } else if ((dst_fmt & GE2D_COLOR_MAP_MASK) == GE2D_COLOR_MAP_NV21) { |
| vf_out->type |= VIDTYPE_VIU_NV21; |
| vf_out->type &= ~VIDTYPE_VIU_NV12; |
| } |
| if ((dst_fmt & GE2D_ENDIAN_MASK) == GE2D_LITTLE_ENDIAN) { |
| vf_out->canvas0_config[0].endian = 0; |
| vf_out->canvas0_config[1].endian = 0; |
| vf_out->canvas0_config[2].endian = 0; |
| } else if ((dst_fmt & GE2D_ENDIAN_MASK) == GE2D_BIG_ENDIAN){ |
| vf_out->canvas0_config[0].endian = 7; |
| vf_out->canvas0_config[1].endian = 7; |
| vf_out->canvas0_config[2].endian = 7; |
| } |
| |
| start_time = local_clock(); |
| /* src canvas configure. */ |
| if ((in_buf->vf->canvas0Addr == 0) || |
| (in_buf->vf->canvas0Addr == (u32)-1)) { |
| canvas_config_config(ge2d->src_canvas_id[0], &in_buf->vf->canvas0_config[0]); |
| canvas_config_config(ge2d->src_canvas_id[1], &in_buf->vf->canvas0_config[1]); |
| canvas_config_config(ge2d->src_canvas_id[2], &in_buf->vf->canvas0_config[2]); |
| ge2d_config.src_para.canvas_index = |
| ge2d->src_canvas_id[0] | |
| ge2d->src_canvas_id[1] << 8 | |
| ge2d->src_canvas_id[2] << 16; |
| |
| ge2d_config.src_planes[0].addr = |
| in_buf->vf->canvas0_config[0].phy_addr; |
| ge2d_config.src_planes[0].w = |
| in_buf->vf->canvas0_config[0].width; |
| ge2d_config.src_planes[0].h = |
| in_buf->vf->canvas0_config[0].height; |
| ge2d_config.src_planes[1].addr = |
| in_buf->vf->canvas0_config[1].phy_addr; |
| ge2d_config.src_planes[1].w = |
| in_buf->vf->canvas0_config[1].width; |
| ge2d_config.src_planes[1].h = |
| in_buf->vf->canvas0_config[1].height; |
| ge2d_config.src_planes[2].addr = |
| in_buf->vf->canvas0_config[2].phy_addr; |
| ge2d_config.src_planes[2].w = |
| in_buf->vf->canvas0_config[2].width; |
| ge2d_config.src_planes[2].h = |
| in_buf->vf->canvas0_config[2].height; |
| } else { |
| ge2d_config.src_para.canvas_index = in_buf->vf->canvas0Addr; |
| } |
| ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID; |
| ge2d_config.src_para.format = src_fmt; |
| ge2d_config.src_para.fill_color_en = 0; |
| ge2d_config.src_para.fill_mode = 0; |
| ge2d_config.src_para.x_rev = 0; |
| ge2d_config.src_para.y_rev = 0; |
| ge2d_config.src_para.color = 0xffffffff; |
| ge2d_config.src_para.top = 0; |
| ge2d_config.src_para.left = 0; |
| ge2d_config.src_para.width = in_buf->vf->width; |
| if (in_buf->vf->type & VIDTYPE_INTERLACE) |
| ge2d_config.src_para.height = in_buf->vf->height >> 1; |
| else |
| ge2d_config.src_para.height = in_buf->vf->height; |
| |
| /* dst canvas configure. */ |
| canvas_config_config(ge2d->dst_canvas_id[0], &vf_out->canvas0_config[0]); |
| if ((ge2d_config.src_para.format & 0xfffff) == GE2D_FORMAT_M24_YUV420) { |
| vf_out->canvas0_config[1].width <<= 1; |
| } |
| canvas_config_config(ge2d->dst_canvas_id[1], &vf_out->canvas0_config[1]); |
| canvas_config_config(ge2d->dst_canvas_id[2], &vf_out->canvas0_config[2]); |
| ge2d_config.dst_para.canvas_index = |
| ge2d->dst_canvas_id[0] | |
| ge2d->dst_canvas_id[1] << 8; |
| canvas_read(ge2d->dst_canvas_id[0], &cd); |
| ge2d_config.dst_planes[0].addr = cd.addr; |
| ge2d_config.dst_planes[0].w = cd.width; |
| ge2d_config.dst_planes[0].h = cd.height; |
| canvas_read(ge2d->dst_canvas_id[1], &cd); |
| ge2d_config.dst_planes[1].addr = cd.addr; |
| ge2d_config.dst_planes[1].w = cd.width; |
| ge2d_config.dst_planes[1].h = cd.height; |
| |
| ge2d_config.dst_para.format = dst_fmt; |
| ge2d_config.dst_para.width = in_buf->vf->width; |
| ge2d_config.dst_para.height = in_buf->vf->height; |
| ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID; |
| ge2d_config.dst_para.fill_color_en = 0; |
| ge2d_config.dst_para.fill_mode = 0; |
| ge2d_config.dst_para.x_rev = 0; |
| ge2d_config.dst_para.y_rev = 0; |
| ge2d_config.dst_para.color = 0; |
| ge2d_config.dst_para.top = 0; |
| ge2d_config.dst_para.left = 0; |
| |
| /* other ge2d parameters configure. */ |
| ge2d_config.src_key.key_enable = 0; |
| ge2d_config.src_key.key_mask = 0; |
| ge2d_config.src_key.key_mode = 0; |
| ge2d_config.alu_const_color = 0; |
| ge2d_config.bitmask_en = 0; |
| ge2d_config.src1_gb_alpha = 0; |
| ge2d_config.dst_xy_swap = 0; |
| ge2d_config.src2_para.mem_type = CANVAS_TYPE_INVALID; |
| |
| ATRACE_COUNTER("VC_OUT_GE2D-1.handle_start", |
| in_buf->aml_buf->frame_buffer.buf_idx); |
| |
| v4l_dbg(ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "ge2d_handle start: dec vf:%px/%d, ge2d vf:%px/%d, iphy:%lx/%lx %dx%d ophy:%lx/%lx %dx%d, vf:%ux%u, fmt(src:%x, dst:%x), " |
| "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n", |
| in_buf->vf, in_buf->vf->index, |
| out_buf->vf, GE2D_BUF_GET_IDX(out_buf), |
| in_buf->vf->canvas0_config[0].phy_addr, |
| in_buf->vf->canvas0_config[1].phy_addr, |
| in_buf->vf->canvas0_config[0].width, |
| in_buf->vf->canvas0_config[0].height, |
| vf_out->canvas0_config[0].phy_addr, |
| vf_out->canvas0_config[1].phy_addr, |
| vf_out->canvas0_config[0].width, |
| vf_out->canvas0_config[0].height, |
| vf_out->width, vf_out->height, |
| src_fmt, dst_fmt, |
| kfifo_len(&ge2d->input), |
| kfifo_len(&ge2d->output), |
| kfifo_len(&ge2d->frame), |
| kfifo_len(&ge2d->in_done_q), |
| kfifo_len(&ge2d->out_done_q)); |
| |
| if (ge2d_context_config_ex(ge2d->ge2d_context, &ge2d_config) < 0) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "ge2d_context_config_ex error.\n"); |
| goto exit; |
| } |
| |
| if (!(in_buf->flag & GE2D_FLAG_EOS)) { |
| if (in_buf->vf->type & VIDTYPE_INTERLACE) { |
| stretchblt_noalpha(ge2d->ge2d_context, |
| 0, 0, in_buf->vf->width, in_buf->vf->height / 2, |
| 0, 0, in_buf->vf->width, in_buf->vf->height); |
| } else { |
| stretchblt_noalpha(ge2d->ge2d_context, |
| 0, 0, in_buf->vf->width, in_buf->vf->height, |
| 0, 0, in_buf->vf->width, in_buf->vf->height); |
| } |
| } |
| |
| //pr_info("consume time %d us\n", div64_u64(local_clock() - start_time, 1000)); |
| |
| v4l_ge2d_fill_output_done(out_buf); |
| v4l_ge2d_empty_input_done(in_buf); |
| |
| ge2d->in_num[INPUT_PORT]++; |
| ge2d->out_num[INPUT_PORT]++; |
| } |
| exit: |
| while (!kthread_should_stop()) { |
| usleep_range(1000, 2000); |
| } |
| |
| v4l_dbg(ctx, V4L_DEBUG_GE2D_DETAIL, "exit ge2d thread\n"); |
| |
| return 0; |
| } |
| |
| int aml_v4l2_ge2d_get_buf_num(u32 mode) |
| { |
| return 4; |
| } |
| |
| int aml_v4l2_ge2d_init( |
| struct aml_vcodec_ctx *ctx, |
| struct aml_ge2d_cfg_infos *cfg, |
| struct aml_v4l2_ge2d** ge2d_handle) |
| { |
| struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; |
| struct aml_v4l2_ge2d *ge2d; |
| u32 work_mode = cfg->mode; |
| u32 buf_size; |
| int i, ret; |
| |
| if (!cfg || !ge2d_handle) |
| return -EINVAL; |
| |
| ge2d = kzalloc(sizeof(*ge2d), GFP_KERNEL); |
| if (!ge2d) |
| return -ENOMEM; |
| |
| ge2d->work_mode = work_mode; |
| |
| /* default convert little endian. */ |
| if (!ge2d->work_mode) { |
| ge2d->work_mode = GE2D_MODE_CONVERT_LE; |
| } |
| |
| ge2d->ge2d_context = create_ge2d_work_queue(); |
| if (!ge2d->ge2d_context) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "ge2d_create_instance fail\n"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| INIT_KFIFO(ge2d->input); |
| INIT_KFIFO(ge2d->output); |
| INIT_KFIFO(ge2d->frame); |
| INIT_KFIFO(ge2d->out_done_q); |
| INIT_KFIFO(ge2d->in_done_q); |
| |
| ge2d->ctx = ctx; |
| buf_size = cfg->buf_size; |
| ge2d->buf_size = buf_size; |
| |
| /* setup output fifo */ |
| ret = kfifo_alloc(&ge2d->output, buf_size, GFP_KERNEL); |
| if (ret) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "alloc output fifo fail.\n"); |
| ret = -ENOMEM; |
| goto error2; |
| } |
| |
| ge2d->ovbpool = vzalloc(buf_size * sizeof(*ge2d->ovbpool)); |
| if (!ge2d->ovbpool) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "alloc output vb pool fail.\n"); |
| ret = -ENOMEM; |
| goto error3; |
| } |
| |
| /* setup vframe fifo */ |
| ret = kfifo_alloc(&ge2d->frame, buf_size, GFP_KERNEL); |
| if (ret) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "alloc ge2d vframe fifo fail.\n"); |
| ret = -ENOMEM; |
| goto error4; |
| } |
| |
| ge2d->vfpool = vzalloc(buf_size * sizeof(*ge2d->vfpool)); |
| if (!ge2d->vfpool) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "alloc vf pool fail.\n"); |
| ret = -ENOMEM; |
| goto error5; |
| } |
| |
| ret = kfifo_alloc(&ge2d->input, GE2D_FRAME_SIZE, GFP_KERNEL); |
| if (ret) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "alloc input fifo fail.\n"); |
| ret = -ENOMEM; |
| goto error6; |
| } |
| |
| ge2d->ivbpool = vzalloc(GE2D_FRAME_SIZE * sizeof(*ge2d->ivbpool)); |
| if (!ge2d->ivbpool) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "alloc input vb pool fail.\n"); |
| ret = -ENOMEM; |
| goto error7; |
| } |
| |
| for (i = 0 ; i < GE2D_FRAME_SIZE ; i++) { |
| kfifo_put(&ge2d->input, &ge2d->ivbpool[i]); |
| } |
| |
| for (i = 0 ; i < buf_size ; i++) { |
| kfifo_put(&ge2d->output, &ge2d->ovbpool[i]); |
| kfifo_put(&ge2d->frame, &ge2d->vfpool[i]); |
| } |
| |
| ge2d->src_canvas_id[0] = canvas_pool_map_alloc_canvas("v4ldec-ge2d"); |
| ge2d->src_canvas_id[1] = canvas_pool_map_alloc_canvas("v4ldec-ge2d"); |
| ge2d->src_canvas_id[2] = canvas_pool_map_alloc_canvas("v4ldec-ge2d"); |
| ge2d->dst_canvas_id[0] = canvas_pool_map_alloc_canvas("v4ldec-ge2d"); |
| ge2d->dst_canvas_id[1] = canvas_pool_map_alloc_canvas("v4ldec-ge2d"); |
| ge2d->dst_canvas_id[2] = canvas_pool_map_alloc_canvas("v4ldec-ge2d"); |
| if ((ge2d->src_canvas_id[0] <= 0) || |
| (ge2d->src_canvas_id[1] <= 0) || |
| (ge2d->src_canvas_id[2] <= 0) || |
| (ge2d->dst_canvas_id[0] <= 0) || |
| (ge2d->dst_canvas_id[1] <= 0) || |
| (ge2d->dst_canvas_id[2] <= 0)) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "canvas pool alloc fail. src(%d, %d, %d) dst(%d, %d, %d).\n", |
| ge2d->src_canvas_id[0], |
| ge2d->src_canvas_id[1], |
| ge2d->src_canvas_id[2], |
| ge2d->dst_canvas_id[0], |
| ge2d->dst_canvas_id[1], |
| ge2d->dst_canvas_id[2]); |
| goto error8; |
| } |
| |
| mutex_init(&ge2d->output_lock); |
| sema_init(&ge2d->sem_in, 0); |
| sema_init(&ge2d->sem_out, 0); |
| |
| ge2d->running = true; |
| ge2d->task = kthread_run(aml_v4l2_ge2d_thread, ge2d, |
| "%s", "aml-v4l2-ge2d"); |
| if (IS_ERR(ge2d->task)) { |
| ret = PTR_ERR(ge2d->task); |
| goto error9; |
| } |
| sched_setscheduler_nocheck(ge2d->task, SCHED_FIFO, ¶m); |
| |
| *ge2d_handle = ge2d; |
| |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, |
| "GE2D_CFG bsize:%d, wkm:%x, bm:%x, drm:%d\n", |
| ge2d->buf_size, |
| ge2d->work_mode, |
| ge2d->buffer_mode, |
| cfg->is_drm); |
| |
| return 0; |
| |
| error9: |
| if (ge2d->src_canvas_id[0] > 0) |
| canvas_pool_map_free_canvas(ge2d->src_canvas_id[0]); |
| if (ge2d->src_canvas_id[1] > 0) |
| canvas_pool_map_free_canvas(ge2d->src_canvas_id[1]); |
| if (ge2d->src_canvas_id[2] > 0) |
| canvas_pool_map_free_canvas(ge2d->src_canvas_id[2]); |
| if (ge2d->dst_canvas_id[0] > 0) |
| canvas_pool_map_free_canvas(ge2d->dst_canvas_id[0]); |
| if (ge2d->dst_canvas_id[1] > 0) |
| canvas_pool_map_free_canvas(ge2d->dst_canvas_id[1]); |
| if (ge2d->dst_canvas_id[2] > 0) |
| canvas_pool_map_free_canvas(ge2d->dst_canvas_id[2]); |
| error8: |
| vfree(ge2d->ivbpool); |
| error7: |
| kfifo_free(&ge2d->input); |
| error6: |
| vfree(ge2d->vfpool); |
| error5: |
| kfifo_free(&ge2d->frame); |
| error4: |
| vfree(ge2d->ovbpool); |
| error3: |
| kfifo_free(&ge2d->output); |
| error2: |
| destroy_ge2d_work_queue(ge2d->ge2d_context); |
| error: |
| kfree(ge2d); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(aml_v4l2_ge2d_init); |
| |
| int aml_v4l2_ge2d_destroy(struct aml_v4l2_ge2d* ge2d) |
| { |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_DETAIL, |
| "ge2d destroy begin\n"); |
| |
| ge2d->running = false; |
| up(&ge2d->sem_in); |
| up(&ge2d->sem_out); |
| kthread_stop(ge2d->task); |
| |
| destroy_ge2d_work_queue(ge2d->ge2d_context); |
| /* no more ge2d callback below this line */ |
| |
| kfifo_free(&ge2d->frame); |
| vfree(ge2d->vfpool); |
| kfifo_free(&ge2d->output); |
| vfree(ge2d->ovbpool); |
| kfifo_free(&ge2d->input); |
| vfree(ge2d->ivbpool); |
| mutex_destroy(&ge2d->output_lock); |
| |
| canvas_pool_map_free_canvas(ge2d->src_canvas_id[0]); |
| canvas_pool_map_free_canvas(ge2d->src_canvas_id[1]); |
| canvas_pool_map_free_canvas(ge2d->src_canvas_id[2]); |
| canvas_pool_map_free_canvas(ge2d->dst_canvas_id[0]); |
| canvas_pool_map_free_canvas(ge2d->dst_canvas_id[1]); |
| canvas_pool_map_free_canvas(ge2d->dst_canvas_id[2]); |
| |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_DETAIL, |
| "ge2d destroy done\n"); |
| |
| kfree(ge2d); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(aml_v4l2_ge2d_destroy); |
| |
| static int aml_v4l2_ge2d_push_vframe(struct aml_v4l2_ge2d* ge2d, struct vframe_s *vf) |
| { |
| struct aml_v4l2_ge2d_buf* in_buf; |
| struct vdec_v4l2_buffer *fb = NULL; |
| |
| if (!ge2d) |
| return -EINVAL; |
| |
| if (!kfifo_get(&ge2d->input, &in_buf)) { |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_CODEC_ERROR, |
| "cat not get free input buffer.\n"); |
| return -1; |
| } |
| |
| if (vf->type & VIDTYPE_V4L_EOS) |
| in_buf->flag |= GE2D_FLAG_EOS; |
| |
| v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR, |
| "ge2d_push_vframe: vf:%px, idx:%d, type:%x, ts:%lld\n", |
| vf, vf->index, vf->type, vf->timestamp); |
| |
| fb = (struct vdec_v4l2_buffer *)vf->v4l_mem_handle; |
| in_buf->aml_buf = container_of(fb, struct aml_video_dec_buf, frame_buffer); |
| in_buf->vf = vf; |
| |
| do { |
| unsigned int dw_mode = VDEC_DW_NO_AFBC; |
| struct file *fp; |
| |
| if (!dump_ge2d_input || ge2d->ctx->is_drm_mode) |
| break; |
| |
| if (vdec_if_get_param(ge2d->ctx, GET_PARAM_DW_MODE, &dw_mode)) |
| break; |
| |
| if (dw_mode == VDEC_DW_AFBC_ONLY) |
| break; |
| |
| fp = filp_open("/data/dec_dump_before.raw", |
| O_CREAT | O_RDWR | O_LARGEFILE | O_APPEND, 0600); |
| if (!IS_ERR(fp)) { |
| struct vb2_buffer *vb = &in_buf->aml_buf->vb.vb2_buf; |
| |
| kernel_write(fp,vb2_plane_vaddr(vb, 0),vb->planes[0].length, 0); |
| if (in_buf->aml_buf->frame_buffer.num_planes == 2) |
| kernel_write(fp,vb2_plane_vaddr(vb, 1), |
| vb->planes[1].length, 0); |
| dump_ge2d_input--; |
| filp_close(fp, NULL); |
| } |
| } while(0); |
| |
| ATRACE_COUNTER("VC_OUT_GE2D-0.receive", fb->buf_idx); |
| |
| kfifo_put(&ge2d->in_done_q, in_buf); |
| up(&ge2d->sem_in); |
| |
| return 0; |
| } |
| |
| static void fill_ge2d_buf_cb(void *v4l_ctx, void *fb_ctx) |
| { |
| struct aml_vcodec_ctx *ctx = |
| (struct aml_vcodec_ctx *)v4l_ctx; |
| struct vdec_v4l2_buffer *fb = |
| (struct vdec_v4l2_buffer *)fb_ctx; |
| int ret = -1; |
| |
| ret = aml_v4l2_ge2d_push_vframe(ctx->ge2d, fb->vframe); |
| if (ret < 0) { |
| v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, |
| "ge2d push vframe err, ret: %d\n", ret); |
| } |
| } |
| |
| static struct task_ops_s ge2d_ops = { |
| .type = TASK_TYPE_GE2D, |
| .get_vframe = ge2d_vf_get, |
| .put_vframe = ge2d_vf_put, |
| .fill_buffer = fill_ge2d_buf_cb, |
| }; |
| |
| struct task_ops_s *get_ge2d_ops(void) |
| { |
| return &ge2d_ops; |
| } |
| EXPORT_SYMBOL(get_ge2d_ops); |
| |