From 64441979bda6c2d17fa40c022f7debec6c328793 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 13 Mar 2022 11:18:11 +0100 Subject: media: platform: rename coda/ to chips-media/ As the end goal is to have platform drivers split by vendor, rename coda/ to chips-media/. Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/Makefile | 4 +- drivers/media/platform/chips-media/Kconfig | 17 + drivers/media/platform/chips-media/Makefile | 6 + drivers/media/platform/chips-media/coda-bit.c | 2660 +++++++++++++++++ drivers/media/platform/chips-media/coda-common.c | 3341 ++++++++++++++++++++++ drivers/media/platform/chips-media/coda-gdi.c | 146 + drivers/media/platform/chips-media/coda-h264.c | 429 +++ drivers/media/platform/chips-media/coda-jpeg.c | 1546 ++++++++++ drivers/media/platform/chips-media/coda-mpeg2.c | 87 + drivers/media/platform/chips-media/coda-mpeg4.c | 87 + drivers/media/platform/chips-media/coda.h | 396 +++ drivers/media/platform/chips-media/coda_regs.h | 563 ++++ drivers/media/platform/chips-media/imx-vdoa.c | 352 +++ drivers/media/platform/chips-media/imx-vdoa.h | 50 + drivers/media/platform/chips-media/trace.h | 175 ++ drivers/media/platform/coda/Kconfig | 17 - drivers/media/platform/coda/Makefile | 6 - drivers/media/platform/coda/coda-bit.c | 2660 ----------------- drivers/media/platform/coda/coda-common.c | 3341 ---------------------- drivers/media/platform/coda/coda-gdi.c | 146 - drivers/media/platform/coda/coda-h264.c | 429 --- drivers/media/platform/coda/coda-jpeg.c | 1546 ---------- drivers/media/platform/coda/coda-mpeg2.c | 87 - drivers/media/platform/coda/coda-mpeg4.c | 87 - drivers/media/platform/coda/coda.h | 396 --- drivers/media/platform/coda/coda_regs.h | 563 ---- drivers/media/platform/coda/imx-vdoa.c | 352 --- drivers/media/platform/coda/imx-vdoa.h | 50 - drivers/media/platform/coda/trace.h | 175 -- 31 files changed, 9859 insertions(+), 9859 deletions(-) create mode 100644 drivers/media/platform/chips-media/Kconfig create mode 100644 drivers/media/platform/chips-media/Makefile create mode 100644 drivers/media/platform/chips-media/coda-bit.c create mode 100644 drivers/media/platform/chips-media/coda-common.c create mode 100644 drivers/media/platform/chips-media/coda-gdi.c create mode 100644 drivers/media/platform/chips-media/coda-h264.c create mode 100644 drivers/media/platform/chips-media/coda-jpeg.c create mode 100644 drivers/media/platform/chips-media/coda-mpeg2.c create mode 100644 drivers/media/platform/chips-media/coda-mpeg4.c create mode 100644 drivers/media/platform/chips-media/coda.h create mode 100644 drivers/media/platform/chips-media/coda_regs.h create mode 100644 drivers/media/platform/chips-media/imx-vdoa.c create mode 100644 drivers/media/platform/chips-media/imx-vdoa.h create mode 100644 drivers/media/platform/chips-media/trace.h delete mode 100644 drivers/media/platform/coda/Kconfig delete mode 100644 drivers/media/platform/coda/Makefile delete mode 100644 drivers/media/platform/coda/coda-bit.c delete mode 100644 drivers/media/platform/coda/coda-common.c delete mode 100644 drivers/media/platform/coda/coda-gdi.c delete mode 100644 drivers/media/platform/coda/coda-h264.c delete mode 100644 drivers/media/platform/coda/coda-jpeg.c delete mode 100644 drivers/media/platform/coda/coda-mpeg2.c delete mode 100644 drivers/media/platform/coda/coda-mpeg4.c delete mode 100644 drivers/media/platform/coda/coda.h delete mode 100644 drivers/media/platform/coda/coda_regs.h delete mode 100644 drivers/media/platform/coda/imx-vdoa.c delete mode 100644 drivers/media/platform/coda/imx-vdoa.h delete mode 100644 drivers/media/platform/coda/trace.h diff --git a/MAINTAINERS b/MAINTAINERS index 848640546398..84bc106acc57 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4771,7 +4771,7 @@ M: Philipp Zabel L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/coda.yaml -F: drivers/media/platform/coda/ +F: drivers/media/platform/chips-media/ CODE OF CONDUCT M: Greg Kroah-Hartman diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 721f27ef0130..24a43cef18de 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -71,7 +71,7 @@ source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/aspeed/Kconfig" source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/cadence/Kconfig" -source "drivers/media/platform/coda/Kconfig" +source "drivers/media/platform/chips-media/Kconfig" source "drivers/media/platform/davinci/Kconfig" source "drivers/media/platform/exynos-gsc/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 20b07ae3ebf1..8d6e15fad0b1 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -6,12 +6,12 @@ # Place here, alphabetically sorted by directory # (e. g. LC_ALL=C sort Makefile) obj-y += allegro-dvt/ -obj-y += aspeed/ obj-y += am437x/ obj-y += amphion/ +obj-y += aspeed/ obj-y += atmel/ obj-y += cadence/ -obj-y += coda/ +obj-y += chips-media/ obj-y += davinci/ obj-y += exynos-gsc/ obj-y += exynos4-is/ diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig new file mode 100644 index 000000000000..ae495fd7b25a --- /dev/null +++ b/drivers/media/platform/chips-media/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_CODA + tristate "Chips&Media Coda multi-standard codec IP" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) + select SRAM + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_JPEG_HELPER + select V4L2_MEM2MEM_DEV + select GENERIC_ALLOCATOR + help + Coda is a range of video codec IPs that supports + H.264, MPEG-4, and other video formats. + +config VIDEO_IMX_VDOA + def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile new file mode 100644 index 000000000000..bbb16425a875 --- /dev/null +++ b/drivers/media/platform/chips-media/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o + +obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o +obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o diff --git a/drivers/media/platform/chips-media/coda-bit.c b/drivers/media/platform/chips-media/coda-bit.c new file mode 100644 index 000000000000..c484c008ab02 --- /dev/null +++ b/drivers/media/platform/chips-media/coda-bit.c @@ -0,0 +1,2660 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - BIT processor functions + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "coda.h" +#include "imx-vdoa.h" +#define CREATE_TRACE_POINTS +#include "trace.h" + +#define CODA_PARA_BUF_SIZE (10 * 1024) +#define CODA7_PS_BUF_SIZE 0x28000 +#define CODA9_PS_SAVE_SIZE (512 * 1024) + +#define CODA_DEFAULT_GAMMA 4096 +#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ + +static void coda_free_bitstream_buffer(struct coda_ctx *ctx); + +static inline int coda_is_initialized(struct coda_dev *dev) +{ + return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0; +} + +static inline unsigned long coda_isbusy(struct coda_dev *dev) +{ + return coda_read(dev, CODA_REG_BIT_BUSY); +} + +static int coda_wait_timeout(struct coda_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while (coda_isbusy(dev)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +static void coda_command_async(struct coda_ctx *ctx, int cmd) +{ + struct coda_dev *dev = ctx->dev; + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541 || + dev->devtype->product == CODA_960) { + /* Restore context related registers to CODA */ + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + coda_write(dev, ctx->frame_mem_ctrl, + CODA_REG_BIT_FRAME_MEM_CTRL); + coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); + } + + if (dev->devtype->product == CODA_960) { + coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + } + + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + + coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); + coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); + + trace_coda_bit_run(ctx, cmd); + + coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); +} + +static int coda_command_sync(struct coda_ctx *ctx, int cmd) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + lockdep_assert_held(&dev->coda_mutex); + + coda_command_async(ctx, cmd); + ret = coda_wait_timeout(dev); + trace_coda_bit_done(ctx); + + return ret; +} + +int coda_hw_reset(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + unsigned long timeout; + unsigned int idx; + int ret; + + lockdep_assert_held(&dev->coda_mutex); + + if (!dev->rstc) + return -ENOENT; + + idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); + + if (dev->devtype->product == CODA_960) { + timeout = jiffies + msecs_to_jiffies(100); + coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); + while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { + if (time_after(jiffies, timeout)) + return -ETIME; + cpu_relax(); + } + } + + ret = reset_control_reset(dev->rstc); + if (ret < 0) + return ret; + + if (dev->devtype->product == CODA_960) + coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + ret = coda_wait_timeout(dev); + coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); + + return ret; +} + +static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr; + + rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + kfifo->out = (kfifo->in & ~kfifo->mask) | + (rd_ptr - ctx->bitstream.paddr); + if (kfifo->out > kfifo->in) + kfifo->out -= kfifo->mask + 1; +} + +static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr, wr_ptr; + + rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); + coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr; + + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static int coda_h264_bitstream_pad(struct coda_ctx *ctx, u32 size) +{ + unsigned char *buf; + u32 n; + + if (size < 6) + size = 6; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + coda_h264_filler_nal(size, buf); + n = kfifo_in(&ctx->bitstream_fifo, buf, size); + kfree(buf); + + return (n < size) ? -ENOSPC : 0; +} + +int coda_bitstream_flush(struct coda_ctx *ctx) +{ + int ret; + + if (ctx->inst_type != CODA_INST_DECODER || !ctx->use_bit) + return 0; + + ret = coda_command_sync(ctx, CODA_COMMAND_DEC_BUF_FLUSH); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to flush bitstream\n"); + return ret; + } + + kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, + ctx->bitstream.size); + coda_kfifo_sync_to_device_full(ctx); + + return 0; +} + +static int coda_bitstream_queue(struct coda_ctx *ctx, const u8 *buf, u32 size) +{ + u32 n = kfifo_in(&ctx->bitstream_fifo, buf, size); + + return (n < size) ? -ENOSPC : 0; +} + +static u32 coda_buffer_parse_headers(struct coda_ctx *ctx, + struct vb2_v4l2_buffer *src_buf, + u32 payload) +{ + u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + u32 size = 0; + + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_MPEG2: + size = coda_mpeg2_parse_headers(ctx, vaddr, payload); + break; + case V4L2_PIX_FMT_MPEG4: + size = coda_mpeg4_parse_headers(ctx, vaddr, payload); + break; + default: + break; + } + + return size; +} + +static bool coda_bitstream_try_queue(struct coda_ctx *ctx, + struct vb2_v4l2_buffer *src_buf) +{ + unsigned long payload = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + int ret; + int i; + + if (coda_get_bitstream_payload(ctx) + payload + 512 >= + ctx->bitstream.size) + return false; + + if (!vaddr) { + v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); + return true; + } + + if (ctx->qsequence == 0 && payload < 512) { + /* + * Add padding after the first buffer, if it is too small to be + * fetched by the CODA, by repeating the headers. Without + * repeated headers, or the first frame already queued, decoder + * sequence initialization fails with error code 0x2000 on i.MX6 + * or error code 0x1 on i.MX51. + */ + u32 header_size = coda_buffer_parse_headers(ctx, src_buf, + payload); + + if (header_size) { + coda_dbg(1, ctx, "pad with %u-byte header\n", + header_size); + for (i = payload; i < 512; i += header_size) { + ret = coda_bitstream_queue(ctx, vaddr, + header_size); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, + "bitstream buffer overflow\n"); + return false; + } + if (ctx->dev->devtype->product == CODA_960) + break; + } + } else { + coda_dbg(1, ctx, + "could not parse header, sequence initialization might fail\n"); + } + + /* Add padding before the first buffer, if it is too small */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) + coda_h264_bitstream_pad(ctx, 512 - payload); + } + + ret = coda_bitstream_queue(ctx, vaddr, payload); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); + return false; + } + + src_buf->sequence = ctx->qsequence++; + + /* Sync read pointer to device */ + if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) + coda_kfifo_sync_to_device_write(ctx); + + /* Set the stream-end flag after the last buffer is queued */ + if (src_buf->flags & V4L2_BUF_FLAG_LAST) + coda_bit_stream_end_flag(ctx); + ctx->hold = false; + + return true; +} + +void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) +{ + struct vb2_v4l2_buffer *src_buf; + struct coda_buffer_meta *meta; + u32 start; + + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) + return; + + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { + /* + * Only queue two JPEGs into the bitstream buffer to keep + * latency low. We need at least one complete buffer and the + * header of another buffer (for prescan) in the bitstream. + */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && + ctx->num_metas > 1) + break; + + if (ctx->num_internal_frames && + ctx->num_metas >= ctx->num_internal_frames) { + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + + /* + * If we managed to fill in at least a full reorder + * window of buffers (num_internal_frames is a + * conservative estimate for this) and the bitstream + * prefetcher has at least 2 256 bytes periods beyond + * the first buffer to fetch, we can safely stop queuing + * in order to limit the decoder drain latency. + */ + if (coda_bitstream_can_fetch_past(ctx, meta->end)) + break; + } + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + /* Drop frames that do not start/end with a SOI/EOI markers */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && + !coda_jpeg_check_buffer(ctx, &src_buf->vb2_buf)) { + v4l2_err(&ctx->dev->v4l2_dev, + "dropping invalid JPEG frame %d\n", + ctx->qsequence); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (buffer_list) { + struct v4l2_m2m_buffer *m2m_buf; + + m2m_buf = container_of(src_buf, + struct v4l2_m2m_buffer, + vb); + list_add_tail(&m2m_buf->list, buffer_list); + } else { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + } + continue; + } + + /* Dump empty buffers */ + if (!vb2_get_plane_payload(&src_buf->vb2_buf, 0)) { + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + continue; + } + + /* Buffer start position */ + start = ctx->bitstream_fifo.kfifo.in; + + if (coda_bitstream_try_queue(ctx, src_buf)) { + /* + * Source buffer is queued in the bitstream ringbuffer; + * queue the timestamp and mark source buffer as done + */ + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + + meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if (meta) { + meta->sequence = src_buf->sequence; + meta->timecode = src_buf->timecode; + meta->timestamp = src_buf->vb2_buf.timestamp; + meta->start = start; + meta->end = ctx->bitstream_fifo.kfifo.in; + meta->last = src_buf->flags & V4L2_BUF_FLAG_LAST; + if (meta->last) + coda_dbg(1, ctx, "marking last meta"); + spin_lock(&ctx->buffer_meta_lock); + list_add_tail(&meta->list, + &ctx->buffer_meta_list); + ctx->num_metas++; + spin_unlock(&ctx->buffer_meta_lock); + + trace_coda_bit_queue(ctx, src_buf, meta); + } + + if (buffer_list) { + struct v4l2_m2m_buffer *m2m_buf; + + m2m_buf = container_of(src_buf, + struct v4l2_m2m_buffer, + vb); + list_add_tail(&m2m_buf->list, buffer_list); + } else { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } + } else { + break; + } + } +} + +void coda_bit_stream_end_flag(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + /* If this context is currently running, update the hardware flag */ + if ((dev->devtype->product == CODA_960) && + coda_isbusy(dev) && + (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + } +} + +static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) +{ + struct coda_dev *dev = ctx->dev; + u32 *p = ctx->parabuf.vaddr; + + if (dev->devtype->product == CODA_DX6) + p[index] = value; + else + p[index ^ 1] = value; +} + +static inline int coda_alloc_context_buf(struct coda_ctx *ctx, + struct coda_aux_buf *buf, size_t size, + const char *name) +{ + return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); +} + + +static void coda_free_framebuffers(struct coda_ctx *ctx) +{ + int i; + + for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) + coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i].buf); +} + +static int coda_alloc_framebuffers(struct coda_ctx *ctx, + struct coda_q_data *q_data, u32 fourcc) +{ + struct coda_dev *dev = ctx->dev; + unsigned int ysize, ycbcr_size; + int ret; + int i; + + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) + ysize = round_up(q_data->rect.width, 16) * + round_up(q_data->rect.height, 16); + else + ysize = round_up(q_data->rect.width, 8) * q_data->rect.height; + + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ycbcr_size = round_up(ysize, 4096) + ysize / 2; + else + ycbcr_size = ysize + ysize / 2; + + /* Allocate frame buffers */ + for (i = 0; i < ctx->num_internal_frames; i++) { + size_t size = ycbcr_size; + char *name; + + /* Add space for mvcol buffers */ + if (dev->devtype->product != CODA_DX6 && + (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0))) + size += ysize / 4; + name = kasprintf(GFP_KERNEL, "fb%d", i); + if (!name) { + coda_free_framebuffers(ctx); + return -ENOMEM; + } + ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i].buf, + size, name); + kfree(name); + if (ret < 0) { + coda_free_framebuffers(ctx); + return ret; + } + } + + /* Register frame buffers in the parameter buffer */ + for (i = 0; i < ctx->num_internal_frames; i++) { + u32 y, cb, cr, mvcol; + + /* Start addresses of Y, Cb, Cr planes */ + y = ctx->internal_frames[i].buf.paddr; + cb = y + ysize; + cr = y + ysize + ysize/4; + mvcol = y + ysize + ysize/4 + ysize/4; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) { + cb = round_up(cb, 4096); + mvcol = cb + ysize/2; + cr = 0; + /* Packed 20-bit MSB of base addresses */ + /* YYYYYCCC, CCyyyyyc, cccc.... */ + y = (y & 0xfffff000) | cb >> 20; + cb = (cb & 0x000ff000) << 12; + } + coda_parabuf_write(ctx, i * 3 + 0, y); + coda_parabuf_write(ctx, i * 3 + 1, cb); + coda_parabuf_write(ctx, i * 3 + 2, cr); + + if (dev->devtype->product == CODA_DX6) + continue; + + /* mvcol buffer for h.264 and mpeg4 */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) + coda_parabuf_write(ctx, 96 + i, mvcol); + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0) + coda_parabuf_write(ctx, 97, mvcol); + } + + return 0; +} + +static void coda_free_context_buffers(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + coda_free_aux_buf(dev, &ctx->slicebuf); + coda_free_aux_buf(dev, &ctx->psbuf); + if (dev->devtype->product != CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + coda_free_aux_buf(dev, &ctx->parabuf); +} + +static int coda_alloc_context_buffers(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + struct coda_dev *dev = ctx->dev; + size_t size; + int ret; + + if (!ctx->parabuf.vaddr) { + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, + CODA_PARA_BUF_SIZE, "parabuf"); + if (ret < 0) + return ret; + } + + if (dev->devtype->product == CODA_DX6) + return 0; + + if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { + /* worst case slice size */ + size = (DIV_ROUND_UP(q_data->rect.width, 16) * + DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; + ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, + "slicebuf"); + if (ret < 0) + goto err; + } + + if (!ctx->psbuf.vaddr && (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541)) { + ret = coda_alloc_context_buf(ctx, &ctx->psbuf, + CODA7_PS_BUF_SIZE, "psbuf"); + if (ret < 0) + goto err; + } + + if (!ctx->workbuf.vaddr) { + size = dev->devtype->workbuf_size; + if (dev->devtype->product == CODA_960 && + q_data->fourcc == V4L2_PIX_FMT_H264) + size += CODA9_PS_SAVE_SIZE; + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, + "workbuf"); + if (ret < 0) + goto err; + } + + return 0; + +err: + coda_free_context_buffers(ctx); + return ret; +} + +static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + int header_code, u8 *header, int *size) +{ + struct vb2_buffer *vb = &buf->vb2_buf; + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct v4l2_rect *r; + size_t bufsize; + int ret; + int i; + + if (dev->devtype->product == CODA_960) + memset(vb2_plane_vaddr(vb, 0), 0, 64); + + coda_write(dev, vb2_dma_contig_plane_dma_addr(vb, 0), + CODA_CMD_ENC_HEADER_BB_START); + bufsize = vb2_plane_size(vb, 0); + if (dev->devtype->product == CODA_960) + bufsize /= 1024; + coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); + if (dev->devtype->product == CODA_960 && + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 && + header_code == CODA_HEADER_H264_SPS) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + r = &q_data_src->rect; + + if (r->width % 16 || r->height % 16) { + u32 crop_right = round_up(r->width, 16) - r->width; + u32 crop_bottom = round_up(r->height, 16) - r->height; + + coda_write(dev, crop_right, + CODA9_CMD_ENC_HEADER_FRAME_CROP_H); + coda_write(dev, crop_bottom, + CODA9_CMD_ENC_HEADER_FRAME_CROP_V); + header_code |= CODA9_HEADER_FRAME_CROP; + } + } + coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); + ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); + return ret; + } + + if (dev->devtype->product == CODA_960) { + for (i = 63; i > 0; i--) + if (((char *)vb2_plane_vaddr(vb, 0))[i] != 0) + break; + *size = i + 1; + } else { + *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - + coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); + } + memcpy(header, vb2_plane_vaddr(vb, 0), *size); + + return 0; +} + +static u32 coda_slice_mode(struct coda_ctx *ctx) +{ + int size, unit; + + switch (ctx->params.slice_mode) { + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: + default: + return 0; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB: + size = ctx->params.slice_max_mb; + unit = 1; + break; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES: + size = ctx->params.slice_max_bits; + unit = 0; + break; + } + + return ((size & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET) | + ((unit & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET) | + ((1 & CODA_SLICING_MODE_MASK) << CODA_SLICING_MODE_OFFSET); +} + +static int coda_enc_param_change(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 change_enable = 0; + u32 success; + int ret; + + if (ctx->params.gop_size_changed) { + change_enable |= CODA_PARAM_CHANGE_RC_GOP; + coda_write(dev, ctx->params.gop_size, + CODA_CMD_ENC_PARAM_RC_GOP); + ctx->gopcounter = ctx->params.gop_size - 1; + ctx->params.gop_size_changed = false; + } + if (ctx->params.h264_intra_qp_changed) { + coda_dbg(1, ctx, "parameter change: intra Qp %u\n", + ctx->params.h264_intra_qp); + + if (ctx->params.bitrate) { + change_enable |= CODA_PARAM_CHANGE_RC_INTRA_QP; + coda_write(dev, ctx->params.h264_intra_qp, + CODA_CMD_ENC_PARAM_RC_INTRA_QP); + } + ctx->params.h264_intra_qp_changed = false; + } + if (ctx->params.bitrate_changed) { + coda_dbg(1, ctx, "parameter change: bitrate %u kbit/s\n", + ctx->params.bitrate); + change_enable |= CODA_PARAM_CHANGE_RC_BITRATE; + coda_write(dev, ctx->params.bitrate, + CODA_CMD_ENC_PARAM_RC_BITRATE); + ctx->params.bitrate_changed = false; + } + if (ctx->params.framerate_changed) { + coda_dbg(1, ctx, "parameter change: frame rate %u/%u Hz\n", + ctx->params.framerate & 0xffff, + (ctx->params.framerate >> 16) + 1); + change_enable |= CODA_PARAM_CHANGE_RC_FRAME_RATE; + coda_write(dev, ctx->params.framerate, + CODA_CMD_ENC_PARAM_RC_FRAME_RATE); + ctx->params.framerate_changed = false; + } + if (ctx->params.intra_refresh_changed) { + coda_dbg(1, ctx, "parameter change: intra refresh MBs %u\n", + ctx->params.intra_refresh); + change_enable |= CODA_PARAM_CHANGE_INTRA_MB_NUM; + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_PARAM_INTRA_MB_NUM); + ctx->params.intra_refresh_changed = false; + } + if (ctx->params.slice_mode_changed) { + change_enable |= CODA_PARAM_CHANGE_SLICE_MODE; + coda_write(dev, coda_slice_mode(ctx), + CODA_CMD_ENC_PARAM_SLICE_MODE); + ctx->params.slice_mode_changed = false; + } + + if (!change_enable) + return 0; + + coda_write(dev, change_enable, CODA_CMD_ENC_PARAM_CHANGE_ENABLE); + + ret = coda_command_sync(ctx, CODA_COMMAND_RC_CHANGE_PARAMETER); + if (ret < 0) + return ret; + + success = coda_read(dev, CODA_RET_ENC_PARAM_CHANGE_SUCCESS); + if (success != 1) + coda_dbg(1, ctx, "parameter change failed: %u\n", success); + + return 0; +} + +static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) +{ + phys_addr_t ret; + + size = round_up(size, 1024); + if (size > iram->remaining) + return 0; + iram->remaining -= size; + + ret = iram->next_paddr; + iram->next_paddr += size; + + return ret; +} + +static void coda_setup_iram(struct coda_ctx *ctx) +{ + struct coda_iram_info *iram_info = &ctx->iram_info; + struct coda_dev *dev = ctx->dev; + int w64, w128; + int mb_width; + int dbk_bits; + int bit_bits; + int ip_bits; + int me_bits; + + memset(iram_info, 0, sizeof(*iram_info)); + iram_info->next_paddr = dev->iram.paddr; + iram_info->remaining = dev->iram.size; + + if (!dev->iram.vaddr) + return; + + switch (dev->devtype->product) { + case CODA_HX4: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE; + me_bits = CODA7_USE_HOST_ME_ENABLE; + break; + case CODA_7541: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + me_bits = CODA7_USE_HOST_ME_ENABLE | CODA7_USE_ME_ENABLE; + break; + case CODA_960: + dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; + bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + me_bits = 0; + break; + default: /* CODA_DX6 */ + return; + } + + if (ctx->inst_type == CODA_INST_ENCODER) { + struct coda_q_data *q_data_src; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + mb_width = DIV_ROUND_UP(q_data_src->rect.width, 16); + w128 = mb_width * 128; + w64 = mb_width * 64; + + /* Prioritize in case IRAM is too small for everything */ + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + iram_info->search_ram_size = round_up(mb_width * 16 * + 36 + 2048, 1024); + iram_info->search_ram_paddr = coda_iram_alloc(iram_info, + iram_info->search_ram_size); + if (!iram_info->search_ram_paddr) { + pr_err("IRAM is smaller than the search ram size\n"); + goto out; + } + iram_info->axi_sram_use |= me_bits; + } + + /* Only H.264BP and H.263P3 are considered */ + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64); + if (!iram_info->buf_dbk_c_use) + goto out; + iram_info->axi_sram_use |= dbk_bits; + + iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_bit_use) + goto out; + iram_info->axi_sram_use |= bit_bits; + + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; + + /* OVL and BTP disabled for encoder */ + } else if (ctx->inst_type == CODA_INST_DECODER) { + struct coda_q_data *q_data_dst; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + mb_width = DIV_ROUND_UP(q_data_dst->width, 16); + w128 = mb_width * 128; + + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_dbk_c_use) + goto out; + iram_info->axi_sram_use |= dbk_bits; + + iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_bit_use) + goto out; + iram_info->axi_sram_use |= bit_bits; + + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; + + /* OVL and BTP unused as there is no VC1 support yet */ + } + +out: + if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) + coda_dbg(1, ctx, "IRAM smaller than needed\n"); + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + /* TODO - Enabling these causes picture errors on CODA7541 */ + if (ctx->inst_type == CODA_INST_DECODER) { + /* fw 1.4.50 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_IP_ENABLE); + } else { + /* fw 13.4.29 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_HOST_DBK_ENABLE | + CODA7_USE_IP_ENABLE | + CODA7_USE_DBK_ENABLE); + } + } +} + +static u32 coda_supported_firmwares[] = { + CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), + CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), + CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 9), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), + CODA_FIRMWARE_VERNUM(CODA_960, 3, 1, 1), +}; + +static bool coda_firmware_supported(u32 vernum) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) + if (vernum == coda_supported_firmwares[i]) + return true; + return false; +} + +int coda_check_firmware(struct coda_dev *dev) +{ + u16 product, major, minor, release; + u32 data; + int ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); + coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); + if (coda_wait_timeout(dev)) { + v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); + ret = -EIO; + goto err_run_cmd; + } + + if (dev->devtype->product == CODA_960) { + data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); + v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", + data); + } + + /* Check we are compatible with the loaded firmware */ + data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); + product = CODA_FIRMWARE_PRODUCT(data); + major = CODA_FIRMWARE_MAJOR(data); + minor = CODA_FIRMWARE_MINOR(data); + release = CODA_FIRMWARE_RELEASE(data); + + clk_disable_unprepare(dev->clk_per); + clk_disable_unprepare(dev->clk_ahb); + + if (product != dev->devtype->product) { + v4l2_err(&dev->v4l2_dev, + "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n", + coda_product_name(dev->devtype->product), + coda_product_name(product), major, minor, release); + return -EINVAL; + } + + v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", + coda_product_name(product)); + + if (coda_firmware_supported(data)) { + v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", + major, minor, release); + } else { + v4l2_warn(&dev->v4l2_dev, + "Unsupported firmware version: %u.%u.%u\n", + major, minor, release); + } + + return 0; + +err_run_cmd: + clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) +{ + u32 cache_size, cache_config; + + if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) { + /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ + cache_size = 0x20262024; + cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; + } else { + /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */ + cache_size = 0x02440243; + cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET; + } + coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); + if (fourcc == V4L2_PIX_FMT_NV12 || fourcc == V4L2_PIX_FMT_YUYV) { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } else { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } + coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG); +} + +/* + * Encoder context operations + */ + +static int coda_encoder_reqbufs(struct coda_ctx *ctx, + struct v4l2_requestbuffers *rb) +{ + struct coda_q_data *q_data_src; + int ret; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return 0; + + if (rb->count) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + } else { + coda_free_context_buffers(ctx); + } + + return 0; +} + +static int coda_start_encoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct vb2_v4l2_buffer *buf; + int gamma, ret, value; + u32 dst_fourcc; + int num_fb; + u32 stride; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + bitstream_buf = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); + bitstream_size = q_data_dst->sizeimage; + + if (!coda_is_initialized(dev)) { + v4l2_err(v4l2_dev, "coda is not initialized.\n"); + return -EFAULT; + } + + if (dst_fourcc == V4L2_PIX_FMT_JPEG) { + if (!ctx->params.jpeg_qmat_tab[0]) + ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[1]) + ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); + coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); + } + + mutex_lock(&dev->coda_mutex); + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + switch (dev->devtype->product) { + case CODA_DX6: + coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | + CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; + case CODA_960: + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + fallthrough; + case CODA_HX4: + case CODA_7541: + coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | + CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; + } + + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (q_data_src->fourcc == V4L2_PIX_FMT_NV12) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; + coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); + + if (dev->devtype->product == CODA_DX6) { + /* Configure the coda */ + coda_write(dev, dev->iram.paddr, + CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); + } + + /* Could set rotation here if needed */ + value = 0; + switch (dev->devtype->product) { + case CODA_DX6: + value = (q_data_src->rect.width & CODADX6_PICWIDTH_MASK) + << CODADX6_PICWIDTH_OFFSET; + value |= (q_data_src->rect.height & CODADX6_PICHEIGHT_MASK) + << CODA_PICHEIGHT_OFFSET; + break; + case CODA_HX4: + case CODA_7541: + if (dst_fourcc == V4L2_PIX_FMT_H264) { + value = (round_up(q_data_src->rect.width, 16) & + CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; + value |= (round_up(q_data_src->rect.height, 16) & + CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; + break; + } + fallthrough; + case CODA_960: + value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) + << CODA7_PICWIDTH_OFFSET; + value |= (q_data_src->rect.height & CODA7_PICHEIGHT_MASK) + << CODA_PICHEIGHT_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); + if (dst_fourcc == V4L2_PIX_FMT_JPEG) + ctx->params.framerate = 0; + coda_write(dev, ctx->params.framerate, + CODA_CMD_ENC_SEQ_SRC_F_RATE); + + ctx->params.codec_mode = ctx->codec->mode; + switch (dst_fourcc) { + case V4L2_PIX_FMT_MPEG4: + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_MPEG4, + CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_MPEG4, + CODA_CMD_ENC_SEQ_COD_STD); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); + break; + case V4L2_PIX_FMT_H264: + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_H264, + CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_H264, + CODA_CMD_ENC_SEQ_COD_STD); + value = ((ctx->params.h264_disable_deblocking_filter_idc & + CODA_264PARAM_DISABLEDEBLK_MASK) << + CODA_264PARAM_DISABLEDEBLK_OFFSET) | + ((ctx->params.h264_slice_alpha_c0_offset_div2 & + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | + ((ctx->params.h264_slice_beta_offset_div2 & + CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | + (ctx->params.h264_constrained_intra_pred_flag << + CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) | + (ctx->params.h264_chroma_qp_index_offset & + CODA_264PARAM_CHROMAQPOFFSET_MASK); + coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); + break; + case V4L2_PIX_FMT_JPEG: + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_PARA); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_EN); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET); + + coda_jpeg_write_tables(ctx); + break; + default: + v4l2_err(v4l2_dev, + "dst format (0x%08x) invalid.\n", dst_fourcc); + ret = -EINVAL; + goto out; + } + + /* + * slice mode and GOP size registers are used for thumb size/offset + * in JPEG mode + */ + if (dst_fourcc != V4L2_PIX_FMT_JPEG) { + value = coda_slice_mode(ctx); + coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); + value = ctx->params.gop_size; + coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); + } + + if (ctx->params.bitrate && (ctx->params.frame_rc_enable || + ctx->params.mb_rc_enable)) { + ctx->params.bitrate_changed = false; + ctx->params.h264_intra_qp_changed = false; + + /* Rate control enabled */ + value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) + << CODA_RATECONTROL_BITRATE_OFFSET; + value |= 1 & CODA_RATECONTROL_ENABLE_MASK; + value |= (ctx->params.vbv_delay & + CODA_RATECONTROL_INITIALDELAY_MASK) + << CODA_RATECONTROL_INITIALDELAY_OFFSET; + if (dev->devtype->product == CODA_960) + value |= BIT(31); /* disable autoskip */ + } else { + value = 0; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); + + coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_SEQ_INTRA_REFRESH); + + coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); + coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); + + + value = 0; + if (dev->devtype->product == CODA_960) + gamma = CODA9_DEFAULT_GAMMA; + else + gamma = CODA_DEFAULT_GAMMA; + if (gamma > 0) { + coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, + CODA_CMD_ENC_SEQ_RC_GAMMA); + } + + if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { + coda_write(dev, + ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | + ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, + CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); + } + if (dev->devtype->product == CODA_960) { + if (ctx->params.h264_max_qp) + value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; + if (CODA_DEFAULT_GAMMA > 0) + value |= 1 << CODA9_OPTION_GAMMA_OFFSET; + } else { + if (CODA_DEFAULT_GAMMA > 0) { + if (dev->devtype->product == CODA_DX6) + value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; + else + value |= 1 << CODA7_OPTION_GAMMA_OFFSET; + } + if (ctx->params.h264_min_qp) + value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; + if (ctx->params.h264_max_qp) + value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); + + if (ctx->params.frame_rc_enable && !ctx->params.mb_rc_enable) + value = 1; + else + value = 0; + coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); + + coda_setup_iram(ctx); + + if (dst_fourcc == V4L2_PIX_FMT_H264) { + switch (dev->devtype->product) { + case CODA_DX6: + value = FMO_SLICE_SAVE_BUF_SIZE << 7; + coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); + break; + case CODA_HX4: + case CODA_7541: + coda_write(dev, ctx->iram_info.search_ram_paddr, + CODA7_CMD_ENC_SEQ_SEARCH_BASE); + coda_write(dev, ctx->iram_info.search_ram_size, + CODA7_CMD_ENC_SEQ_SEARCH_SIZE); + break; + case CODA_960: + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); + } + } + + ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); + if (ret < 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + goto out; + } + + if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); + ret = -EFAULT; + goto out; + } + ctx->initialized = 1; + + if (dst_fourcc != V4L2_PIX_FMT_JPEG) { + if (dev->devtype->product == CODA_960) + ctx->num_internal_frames = 4; + else + ctx->num_internal_frames = 2; + ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); + goto out; + } + num_fb = 2; + stride = q_data_src->bytesperline; + } else { + ctx->num_internal_frames = 0; + num_fb = 0; + stride = 0; + } + coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE); + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + coda_write(dev, q_data_src->bytesperline, + CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); + } + if (dev->devtype->product != CODA_DX6) { + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + + coda9_set_frame_cache(ctx, q_data_src->fourcc); + + /* FIXME */ + coda_write(dev, ctx->internal_frames[2].buf.paddr, + CODA9_CMD_SET_FRAME_SUBSAMP_A); + coda_write(dev, ctx->internal_frames[3].buf.paddr, + CODA9_CMD_SET_FRAME_SUBSAMP_B); + } + } + + ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); + if (ret < 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + goto out; + } + + coda_dbg(1, ctx, "start encoding %dx%d %4.4s->%4.4s @ %d/%d Hz\n", + q_data_src->rect.width, q_data_src->rect.height, + (char *)&ctx->codec->src_fourcc, (char *)&dst_fourcc, + ctx->params.framerate & 0xffff, + (ctx->params.framerate >> 16) + 1); + + /* Save stream headers */ + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + /* + * Get SPS in the first frame and copy it to an + * intermediate buffer. + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + /* + * If visible width or height are not aligned to macroblock + * size, the crop_right and crop_bottom SPS fields must be set + * to the difference between visible and coded size. This is + * only supported by CODA960 firmware. All others do not allow + * writing frame cropping parameters, so we have to manually + * fix up the SPS RBSP (Sequence Parameter Set Raw Byte + * Sequence Payload) ourselves. + */ + if (ctx->dev->devtype->product != CODA_960 && + ((q_data_src->rect.width % 16) || + (q_data_src->rect.height % 16))) { + ret = coda_h264_sps_fixup(ctx, q_data_src->rect.width, + q_data_src->rect.height, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0], + sizeof(ctx->vpu_header[0])); + if (ret < 0) + goto out; + } + + /* + * Get PPS in the first frame and copy it to an + * intermediate buffer. + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + /* + * Length of H.264 headers is variable and thus it might not be + * aligned for the coda to append the encoded frame. In that is + * the case a filler NAL must be added to header 2. + */ + ctx->vpu_header_size[2] = coda_h264_padding( + (ctx->vpu_header_size[0] + + ctx->vpu_header_size[1]), + ctx->vpu_header[2]); + break; + case V4L2_PIX_FMT_MPEG4: + /* + * Get VOS in the first frame and copy it to an + * intermediate buffer + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, + &ctx->vpu_header[2][0], + &ctx->vpu_header_size[2]); + if (ret < 0) + goto out; + break; + default: + /* No more formats need to save headers at the moment */ + break; + } + +out: + mutex_unlock(&dev->coda_mutex); + return ret; +} + +static int coda_prepare_encode(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + int force_ipicture; + int quant_param = 0; + u32 pic_stream_buffer_addr, pic_stream_buffer_size; + u32 rot_mode = 0; + u32 dst_fourcc; + u32 reg; + int ret; + + ret = coda_enc_param_change(ctx); + if (ret < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "parameter change failed: %d\n", + ret); + } + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + src_buf->sequence = ctx->osequence; + dst_buf->sequence = ctx->osequence; + ctx->osequence++; + + force_ipicture = ctx->params.force_ipicture; + if (force_ipicture) + ctx->params.force_ipicture = false; + else if (ctx->params.gop_size != 0 && + (src_buf->sequence % ctx->params.gop_size) == 0) + force_ipicture = 1; + + /* + * Workaround coda firmware BUG that only marks the first + * frame as IDR. This is a problem for some decoders that can't + * recover when a frame is lost. + */ + if (!force_ipicture) { + src_buf->flags |= V4L2_BUF_FLAG_PFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; + } else { + src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; + } + + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + + /* + * Copy headers in front of the first frame and forced I frames for + * H.264 only. In MPEG4 they are already copied by the CODA. + */ + if (src_buf->sequence == 0 || force_ipicture) { + pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]; + pic_stream_buffer_size = q_data_dst->sizeimage - + ctx->vpu_header_size[0] - + ctx->vpu_header_size[1] - + ctx->vpu_header_size[2]; + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0), + &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0], &ctx->vpu_header[1][0], + ctx->vpu_header_size[1]); + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0] + ctx->vpu_header_size[1], + &ctx->vpu_header[2][0], ctx->vpu_header_size[2]); + } else { + pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + pic_stream_buffer_size = q_data_dst->sizeimage; + } + + if (force_ipicture) { + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + quant_param = ctx->params.h264_intra_qp; + break; + case V4L2_PIX_FMT_MPEG4: + quant_param = ctx->params.mpeg4_intra_qp; + break; + case V4L2_PIX_FMT_JPEG: + quant_param = 30; + break; + default: + v4l2_warn(&ctx->dev->v4l2_dev, + "cannot set intra qp, fmt not supported\n"); + break; + } + } else { + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + quant_param = ctx->params.h264_inter_qp; + break; + case V4L2_PIX_FMT_MPEG4: + quant_param = ctx->params.mpeg4_inter_qp; + break; + default: + v4l2_warn(&ctx->dev->v4l2_dev, + "cannot set inter qp, fmt not supported\n"); + break; + } + } + + /* submit */ + if (ctx->params.rot_mode) + rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; + coda_write(dev, rot_mode, CODA_CMD_ENC_PIC_ROT_MODE); + coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); + + if (dev->devtype->product == CODA_960) { + coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); + coda_write(dev, q_data_src->bytesperline, + CODA9_CMD_ENC_PIC_SRC_STRIDE); + coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); + + reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y; + } else { + reg = CODA_CMD_ENC_PIC_SRC_ADDR_Y; + } + coda_write_base(ctx, q_data_src, src_buf, reg); + + coda_write(dev, force_ipicture << 1 & 0x2, + CODA_CMD_ENC_PIC_OPTION); + + coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); + coda_write(dev, pic_stream_buffer_size / 1024, + CODA_CMD_ENC_PIC_BB_SIZE); + + if (!ctx->streamon_out) { + /* After streamoff on the output side, set stream end flag */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + } + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, ctx->iram_info.axi_sram_use, + CODA7_REG_BIT_AXI_SRAM_USE); + + trace_coda_enc_pic_run(ctx, src_buf); + + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + return 0; +} + +static char coda_frame_type_char(u32 flags) +{ + return (flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : + (flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : + (flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '?'; +} + +static void coda_finish_encode(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr, start_ptr; + + if (ctx->aborting) + return; + + /* + * Lock to make sure that an encoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + trace_coda_enc_pic_done(ctx, dst_buf); + + /* Get results from the coda */ + start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); + wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + + /* Calculate bytesused field */ + if (dst_buf->sequence == 0 || + src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]); + } else { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); + } + + coda_dbg(1, ctx, "frame size = %u\n", wr_ptr - start_ptr); + + coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); + coda_read(dev, CODA_RET_ENC_PIC_FLAG); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_LAST); + if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); + mutex_unlock(&ctx->wakeup_mutex); + + ctx->gopcounter--; + if (ctx->gopcounter < 0) + ctx->gopcounter = ctx->params.gop_size - 1; + + coda_dbg(1, ctx, "job finished: encoded %c frame (%d)%s\n", + coda_frame_type_char(dst_buf->flags), dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); +} + +static void coda_seq_end_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); + struct coda_dev *dev = ctx->dev; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + if (ctx->initialized == 0) + goto out; + + coda_dbg(1, ctx, "%s: sent command 'SEQ_END' to coda\n", __func__); + if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_END failed\n"); + } + + /* + * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing + * from the output stream after the h.264 decoder has run. Resetting the + * hardware after the decoder has finished seems to help. + */ + if (dev->devtype->product == CODA_960) + coda_hw_reset(ctx); + + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + coda_free_framebuffers(ctx); + + ctx->initialized = 0; + +out: + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static void coda_bit_release(struct coda_ctx *ctx) +{ + mutex_lock(&ctx->buffer_mutex); + coda_free_framebuffers(ctx); + coda_free_context_buffers(ctx); + coda_free_bitstream_buffer(ctx); + mutex_unlock(&ctx->buffer_mutex); +} + +const struct coda_context_ops coda_bit_encode_ops = { + .queue_init = coda_encoder_queue_init, + .reqbufs = coda_encoder_reqbufs, + .start_streaming = coda_start_encoding, + .prepare_run = coda_prepare_encode, + .finish_run = coda_finish_encode, + .seq_end_work = coda_seq_end_work, + .release = coda_bit_release, +}; + +/* + * Decoder context operations + */ + +static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + if (ctx->bitstream.vaddr) + return 0; + + ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); + ctx->bitstream.vaddr = dma_alloc_wc(ctx->dev->dev, ctx->bitstream.size, + &ctx->bitstream.paddr, GFP_KERNEL); + if (!ctx->bitstream.vaddr) { + v4l2_err(&ctx->dev->v4l2_dev, + "failed to allocate bitstream ringbuffer"); + return -ENOMEM; + } + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + return 0; +} + +static void coda_free_bitstream_buffer(struct coda_ctx *ctx) +{ + if (ctx->bitstream.vaddr == NULL) + return; + + dma_free_wc(ctx->dev->dev, ctx->bitstream.size, ctx->bitstream.vaddr, + ctx->bitstream.paddr); + ctx->bitstream.vaddr = NULL; + kfifo_init(&ctx->bitstream_fifo, NULL, 0); +} + +static int coda_decoder_reqbufs(struct coda_ctx *ctx, + struct v4l2_requestbuffers *rb) +{ + struct coda_q_data *q_data_src; + int ret; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return 0; + + if (rb->count) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + ret = coda_alloc_bitstream_buffer(ctx, q_data_src); + if (ret < 0) { + coda_free_context_buffers(ctx); + return ret; + } + } else { + coda_free_bitstream_buffer(ctx); + coda_free_context_buffers(ctx); + } + + return 0; +} + +static bool coda_reorder_enable(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int profile; + + if (dev->devtype->product != CODA_HX4 && + dev->devtype->product != CODA_7541 && + dev->devtype->product != CODA_960) + return false; + + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) + return false; + + if (ctx->codec->src_fourcc != V4L2_PIX_FMT_H264) + return true; + + profile = coda_h264_profile(ctx->params.h264_profile_idc); + if (profile < 0) + v4l2_warn(&dev->v4l2_dev, "Unknown H264 Profile: %u\n", + ctx->params.h264_profile_idc); + + /* Baseline profile does not support reordering */ + return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; +} + +static void coda_decoder_drop_used_metas(struct coda_ctx *ctx) +{ + struct coda_buffer_meta *meta, *tmp; + + /* + * All metas that end at or before the RD pointer (fifo out), + * are now consumed by the VPU and should be released. + */ + spin_lock(&ctx->buffer_meta_lock); + list_for_each_entry_safe(meta, tmp, &ctx->buffer_meta_list, list) { + if (ctx->bitstream_fifo.kfifo.out >= meta->end) { + coda_dbg(2, ctx, "releasing meta: seq=%d start=%d end=%d\n", + meta->sequence, meta->start, meta->end); + + list_del(&meta->list); + ctx->num_metas--; + ctx->first_frame_sequence++; + kfree(meta); + } + } + spin_unlock(&ctx->buffer_meta_lock); +} + +static int __coda_decoder_seq_init(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct coda_dev *dev = ctx->dev; + int width, height; + u32 src_fourcc, dst_fourcc; + u32 val; + int ret; + + lockdep_assert_held(&dev->coda_mutex); + + coda_dbg(1, ctx, "Video Data Order Adapter: %s\n", + ctx->use_vdoa ? "Enabled" : "Disabled"); + + /* Start decoding */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + bitstream_buf = ctx->bitstream.paddr; + bitstream_size = ctx->bitstream.size; + src_fourcc = q_data_src->fourcc; + dst_fourcc = q_data_dst->fourcc; + + /* Update coda bitstream read and write pointers from kfifo */ + coda_kfifo_sync_to_device_full(ctx); + + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | + ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); + coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); + + ctx->display_idx = -1; + ctx->frm_dis_flg = 0; + coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); + coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); + val = 0; + if (coda_reorder_enable(ctx)) + val |= CODA_REORDER_ENABLE; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) + val |= CODA_NO_INT_ENABLE; + coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); + + ctx->params.codec_mode = ctx->codec->mode; + if (dev->devtype->product == CODA_960 && + src_fourcc == V4L2_PIX_FMT_MPEG4) + ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; + else + ctx->params.codec_mode_aux = 0; + if (src_fourcc == V4L2_PIX_FMT_MPEG4) { + coda_write(dev, CODA_MP4_CLASS_MPEG4, + CODA_CMD_DEC_SEQ_MP4_ASP_CLASS); + } + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + coda_write(dev, ctx->psbuf.paddr, + CODA_CMD_DEC_SEQ_PS_BB_START); + coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), + CODA_CMD_DEC_SEQ_PS_BB_SIZE); + } + if (dev->devtype->product == CODA_960) { + coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); + coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); + } + } + if (src_fourcc == V4L2_PIX_FMT_JPEG) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_JPG_THUMB_EN); + if (dev->devtype->product != CODA_960) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); + + ctx->bit_stream_param = CODA_BIT_DEC_SEQ_INIT_ESCAPE; + ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); + ctx->bit_stream_param = 0; + if (ret) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + return ret; + } + ctx->sequence_offset = ~0U; + ctx->initialized = 1; + ctx->first_frame_sequence = 0; + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + /* + * After updating the read pointer, we need to check if + * any metas are consumed and should be released. + */ + coda_decoder_drop_used_metas(ctx); + + if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_INIT failed, error code = 0x%x\n", + coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); + return -EAGAIN; + } + + val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); + if (dev->devtype->product == CODA_DX6) { + width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; + height = val & CODADX6_PICHEIGHT_MASK; + } else { + width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; + height = val & CODA7_PICHEIGHT_MASK; + } + + if (width > q_data_dst->bytesperline || height > q_data_dst->height) { + v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", + width, height, q_data_dst->bytesperline, + q_data_dst->height); + return -EINVAL; + } + + width = round_up(width, 16); + height = round_up(height, 16); + + coda_dbg(1, ctx, "start decoding: %dx%d\n", width, height); + + ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); + /* + * If the VDOA is used, the decoder needs one additional frame, + * because the frames are freed when the next frame is decoded. + * Otherwise there are visible errors in the decoded frames (green + * regions in displayed frames) and a broken order of frames (earlier + * frames are sporadically displayed after later frames). + */ + if (ctx->use_vdoa) + ctx->num_internal_frames += 1; + if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { + v4l2_err(&dev->v4l2_dev, + "not enough framebuffers to decode (%d < %d)\n", + CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); + return -EINVAL; + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); + + q_data_dst->rect.left = (left_right >> 10) & 0x3ff; + q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; + q_data_dst->rect.width = width - q_data_dst->rect.left - + (left_right & 0x3ff); + q_data_dst->rect.height = height - q_data_dst->rect.top - + (top_bottom & 0x3ff); + } + + if (dev->devtype->product != CODA_DX6) { + u8 profile, level; + + val = coda_read(dev, CODA7_RET_DEC_SEQ_HEADER_REPORT); + profile = val & 0xff; + level = (val >> 8) & 0x7f; + + if (profile || level) + coda_update_profile_level_ctrls(ctx, profile, level); + } + + return 0; +} + +static void coda_dec_seq_init_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, + struct coda_ctx, seq_init_work); + struct coda_dev *dev = ctx->dev; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + if (!ctx->initialized) + __coda_decoder_seq_init(ctx); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static int __coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + struct coda_dev *dev = ctx->dev; + u32 src_fourcc, dst_fourcc; + int ret; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + src_fourcc = q_data_src->fourcc; + dst_fourcc = q_data_dst->fourcc; + + if (!ctx->initialized) { + ret = __coda_decoder_seq_init(ctx); + if (ret < 0) + return ret; + } else { + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | + ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); + } + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); + return ret; + } + + /* Tell the decoder how many frame buffers we allocated. */ + coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, round_up(q_data_dst->rect.width, 16), + CODA_CMD_SET_FRAME_BUF_STRIDE); + + if (dev->devtype->product != CODA_DX6) { + /* Set secondary AXI IRAM */ + coda_setup_iram(ctx); + + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + + coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); + coda9_set_frame_cache(ctx, dst_fourcc); + } + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + coda_write(dev, ctx->slicebuf.paddr, + CODA_CMD_SET_FRAME_SLICE_BB_START); + coda_write(dev, ctx->slicebuf.size / 1024, + CODA_CMD_SET_FRAME_SLICE_BB_SIZE); + } + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); + } else if (dev->devtype->product == CODA_960) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); + } + + if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { + v4l2_err(&ctx->dev->v4l2_dev, + "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&dev->coda_mutex); + ret = __coda_start_decoding(ctx); + mutex_unlock(&dev->coda_mutex); + + return ret; +} + +static int coda_prepare_decode(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *dst_buf; + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_dst; + struct coda_buffer_meta *meta; + u32 rot_mode = 0; + u32 reg_addr, reg_stride; + + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + /* Try to copy source buffer contents into the bitstream ringbuffer */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + + if (coda_get_bitstream_payload(ctx) < 512 && + (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { + coda_dbg(1, ctx, "bitstream payload: %d, skipping\n", + coda_get_bitstream_payload(ctx)); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return -EAGAIN; + } + + /* Run coda_start_decoding (again) if not yet initialized */ + if (!ctx->initialized) { + int ret = __coda_start_decoding(ctx); + + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return -EAGAIN; + } else { + ctx->initialized = 1; + } + } + + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + + if (ctx->use_vdoa && + ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + vdoa_device_run(ctx->vdoa, + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0), + ctx->internal_frames[ctx->display_idx].buf.paddr); + } else { + if (dev->devtype->product == CODA_960) { + /* + * It was previously assumed that the CODA960 has an + * internal list of 64 buffer entries that contains + * both the registered internal frame buffers as well + * as the rotator buffer output, and that the ROT_INDEX + * register must be set to a value between the last + * internal frame buffers' index and 64. + * At least on firmware version 3.1.1 it turns out that + * setting ROT_INDEX to any value >= 32 causes CODA + * hangups that it can not recover from with the SRC VPU + * reset. + * It does appear to work however, to just set it to a + * fixed value in the [ctx->num_internal_frames, 31] + * range, for example CODA_MAX_FRAMEBUFFERS. + */ + coda_write(dev, CODA_MAX_FRAMEBUFFERS, + CODA9_CMD_DEC_PIC_ROT_INDEX); + + reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y; + reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE; + } else { + reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y; + reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE; + } + coda_write_base(ctx, q_data_dst, dst_buf, reg_addr); + coda_write(dev, q_data_dst->bytesperline, reg_stride); + + rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; + } + + coda_write(dev, rot_mode, CODA_CMD_DEC_PIC_ROT_MODE); + + switch (dev->devtype->product) { + case CODA_DX6: + /* TBD */ + case CODA_HX4: + case CODA_7541: + coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); + break; + case CODA_960: + /* 'hardcode to use interrupt disable mode'? */ + coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); + break; + } + + coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); + + coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); + coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, ctx->iram_info.axi_sram_use, + CODA7_REG_BIT_AXI_SRAM_USE); + + spin_lock(&ctx->buffer_meta_lock); + meta = list_first_entry_or_null(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + + if (meta && ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) { + + /* If this is the last buffer in the bitstream, add padding */ + if (meta->end == ctx->bitstream_fifo.kfifo.in) { + static unsigned char buf[512]; + unsigned int pad; + + /* Pad to multiple of 256 and then add 256 more */ + pad = ((0 - meta->end) & 0xff) + 256; + + memset(buf, 0xff, sizeof(buf)); + + kfifo_in(&ctx->bitstream_fifo, buf, pad); + } + } + spin_unlock(&ctx->buffer_meta_lock); + + coda_kfifo_sync_to_device_full(ctx); + + /* Clear decode success flag */ + coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); + + /* Clear error return value */ + coda_write(dev, 0, CODA_RET_DEC_PIC_ERR_MB); + + trace_coda_dec_pic_run(ctx, meta); + + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + return 0; +} + +static void coda_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct coda_q_data *q_data_dst; + struct vb2_v4l2_buffer *dst_buf; + struct coda_buffer_meta *meta; + int width, height; + int decoded_idx; + int display_idx; + struct coda_internal_frame *decoded_frame = NULL; + u32 src_fourcc; + int success; + u32 err_mb; + int err_vdoa = 0; + u32 val; + + if (ctx->aborting) + return; + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + /* + * in stream-end mode, the read pointer can overshoot the write pointer + * by up to 512 bytes + */ + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { + if (coda_get_bitstream_payload(ctx) >= ctx->bitstream.size - 512) + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_fourcc = q_data_src->fourcc; + + val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); + if (val != 1) + pr_err("DEC_PIC_SUCCESS = %d\n", val); + + success = val & 0x1; + if (!success) + v4l2_err(&dev->v4l2_dev, "decode failed\n"); + + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (val & (1 << 3)) + v4l2_err(&dev->v4l2_dev, + "insufficient PS buffer space (%d bytes)\n", + ctx->psbuf.size); + if (val & (1 << 2)) + v4l2_err(&dev->v4l2_dev, + "insufficient slice buffer space (%d bytes)\n", + ctx->slicebuf.size); + } + + val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); + width = (val >> 16) & 0xffff; + height = val & 0xffff; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + /* frame crop information */ + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); + + if (left_right == 0xffffffff && top_bottom == 0xffffffff) { + /* Keep current crop information */ + } else { + struct v4l2_rect *rect = &q_data_dst->rect; + + rect->left = left_right >> 16 & 0xffff; + rect->top = top_bottom >> 16 & 0xffff; + rect->width = width - rect->left - + (left_right & 0xffff); + rect->height = height - rect->top - + (top_bottom & 0xffff); + } + } else { + /* no cropping */ + } + + err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); + if (err_mb > 0) { + if (__ratelimit(&dev->mb_err_rs)) + coda_dbg(1, ctx, "errors in %d macroblocks\n", err_mb); + v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, + v4l2_ctrl_g_ctrl(ctx->mb_err_cnt_ctrl) + err_mb); + } + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); + if (val == 0) { + /* not enough bitstream data */ + coda_dbg(1, ctx, "prescan failed: %d\n", val); + ctx->hold = true; + return; + } + } + + /* Wait until the VDOA finished writing the previous display frame */ + if (ctx->use_vdoa && + ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + err_vdoa = vdoa_wait_for_completion(ctx->vdoa); + } + + ctx->frm_dis_flg = coda_read(dev, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + /* The previous display frame was copied out and can be overwritten */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + ctx->frm_dis_flg &= ~(1 << ctx->display_idx); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + } + + /* + * The index of the last decoded frame, not necessarily in + * display order, and the index of the next display frame. + * The latter could have been decoded in a previous run. + */ + decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); + display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); + + if (decoded_idx == -1) { + /* no frame was decoded, but we might have a display frame */ + if (display_idx >= 0 && display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; + else if (ctx->display_idx < 0) + ctx->hold = true; + } else if (decoded_idx == -2) { + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; + /* no frame was decoded, we still return remaining buffers */ + } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "decoded frame index out of range: %d\n", decoded_idx); + } else { + int sequence; + + decoded_frame = &ctx->internal_frames[decoded_idx]; + + val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM); + if (ctx->sequence_offset == -1) + ctx->sequence_offset = val; + + sequence = val + ctx->first_frame_sequence + - ctx->sequence_offset; + spin_lock(&ctx->buffer_meta_lock); + if (!list_empty(&ctx->buffer_meta_list)) { + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + list_del(&meta->list); + ctx->num_metas--; + spin_unlock(&ctx->buffer_meta_lock); + /* + * Clamp counters to 16 bits for comparison, as the HW + * counter rolls over at this point for h.264. This + * may be different for other formats, but using 16 bits + * should be enough to detect most errors and saves us + * from doing different things based on the format. + */ + if ((sequence & 0xffff) != (meta->sequence & 0xffff)) { + v4l2_err(&dev->v4l2_dev, + "sequence number mismatch (%d(%d) != %d)\n", + sequence, ctx->sequence_offset, + meta->sequence); + } + decoded_frame->meta = *meta; + kfree(meta); + } else { + spin_unlock(&ctx->buffer_meta_lock); + v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); + memset(&decoded_frame->meta, 0, + sizeof(struct coda_buffer_meta)); + decoded_frame->meta.sequence = sequence; + decoded_frame->meta.last = false; + ctx->sequence_offset++; + } + + trace_coda_dec_pic_done(ctx, &decoded_frame->meta); + + val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; + decoded_frame->type = (val == 0) ? V4L2_BUF_FLAG_KEYFRAME : + (val == 1) ? V4L2_BUF_FLAG_PFRAME : + V4L2_BUF_FLAG_BFRAME; + + decoded_frame->error = err_mb; + } + + if (display_idx == -1) { + /* + * no more frames to be decoded, but there could still + * be rotator output to dequeue + */ + ctx->hold = true; + } else if (display_idx == -3) { + /* possibly prescan failure */ + } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "presentation frame index out of range: %d\n", + display_idx); + } + + /* If a frame was copied out, return it */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + struct coda_internal_frame *ready_frame; + + ready_frame = &ctx->internal_frames[ctx->display_idx]; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->osequence++; + + dst_buf->field = V4L2_FIELD_NONE; + dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + dst_buf->flags |= ready_frame->type; + meta = &ready_frame->meta; + if (meta->last && !coda_reorder_enable(ctx)) { + /* + * If this was the last decoded frame, and reordering + * is disabled, this will be the last display frame. + */ + coda_dbg(1, ctx, "last meta, marking as last frame\n"); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + } else if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG && + display_idx == -1) { + /* + * If there is no designated presentation frame anymore, + * this frame has to be the last one. + */ + coda_dbg(1, ctx, + "no more frames to return, marking as last frame\n"); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + } + dst_buf->timecode = meta->timecode; + dst_buf->vb2_buf.timestamp = meta->timestamp; + + trace_coda_dec_rot_done(ctx, dst_buf, meta); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + q_data_dst->sizeimage); + + if (ready_frame->error || err_vdoa) + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); + else + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); + + if (decoded_frame) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, returned %c frame %u (%u/%u)%s\n", + coda_frame_type_char(decoded_frame->type), + decoded_frame->meta.sequence, + coda_frame_type_char(dst_buf->flags), + ready_frame->meta.sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d), returned %c frame %u (%u/%u)%s\n", + decoded_idx, + coda_frame_type_char(dst_buf->flags), + ready_frame->meta.sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } + } else { + if (decoded_frame) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, no frame returned (%d)\n", + coda_frame_type_char(decoded_frame->type), + decoded_frame->meta.sequence, + ctx->display_idx); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d) or returned (%d)\n", + decoded_idx, ctx->display_idx); + } + } + + /* The rotator will copy the current display frame next time */ + ctx->display_idx = display_idx; + + /* + * The current decode run might have brought the bitstream fill level + * below the size where we can start the next decode run. As userspace + * might have filled the output queue completely and might thus be + * blocked, we can't rely on the next qbuf to trigger the bitstream + * refill. Check if we have data to refill the bitstream now. + */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); +} + +static void coda_decode_timeout(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *dst_buf; + + /* + * For now this only handles the case where we would deadlock with + * userspace, i.e. userspace issued DEC_CMD_STOP and waits for EOS, + * but after a failed decode run we would hold the context and wait for + * userspace to queue more buffers. + */ + if (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG)) + return; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->qsequence - 1; + + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); +} + +const struct coda_context_ops coda_bit_decode_ops = { + .queue_init = coda_decoder_queue_init, + .reqbufs = coda_decoder_reqbufs, + .start_streaming = coda_start_decoding, + .prepare_run = coda_prepare_decode, + .finish_run = coda_finish_decode, + .run_timeout = coda_decode_timeout, + .seq_init_work = coda_dec_seq_init_work, + .seq_end_work = coda_seq_end_work, + .release = coda_bit_release, +}; + +irqreturn_t coda_irq_handler(int irq, void *data) +{ + struct coda_dev *dev = data; + struct coda_ctx *ctx; + + /* read status register to attend the IRQ */ + coda_read(dev, CODA_REG_BIT_INT_STATUS); + coda_write(dev, 0, CODA_REG_BIT_INT_REASON); + coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, + CODA_REG_BIT_INT_CLEAR); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (ctx == NULL) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + trace_coda_bit_done(ctx); + + if (ctx->aborting) { + coda_dbg(1, ctx, "task has been aborted\n"); + } + + if (coda_isbusy(ctx->dev)) { + coda_dbg(1, ctx, "coda is still busy!!!!\n"); + return IRQ_NONE; + } + + complete(&ctx->completion); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/chips-media/coda-common.c b/drivers/media/platform/chips-media/coda-common.c new file mode 100644 index 000000000000..a57822b05070 --- /dev/null +++ b/drivers/media/platform/chips-media/coda-common.c @@ -0,0 +1,3341 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coda.h" +#include "imx-vdoa.h" + +#define CODA_NAME "coda" + +#define CODADX6_MAX_INSTANCES 4 +#define CODA_MAX_FORMATS 5 + +#define CODA_ISRAM_SIZE (2048 * 2) + +#define MIN_W 48 +#define MIN_H 16 + +#define S_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN 1 /* multiple of 2 */ +#define H_ALIGN 1 /* multiple of 2 */ + +#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) + +int coda_debug; +module_param(coda_debug, int, 0644); +MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); + +static int disable_tiling; +module_param(disable_tiling, int, 0644); +MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); + +static int disable_vdoa; +module_param(disable_vdoa, int, 0644); +MODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion"); + +static int enable_bwb = 0; +module_param(enable_bwb, int, 0644); +MODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain streams"); + +void coda_write(struct coda_dev *dev, u32 data, u32 reg) +{ + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, + "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); + writel(data, dev->regs_base + reg); +} + +unsigned int coda_read(struct coda_dev *dev, u32 reg) +{ + u32 data; + + data = readl(dev->regs_base + reg); + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, + "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); + return data; +} + +void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, + struct vb2_v4l2_buffer *buf, unsigned int reg_y) +{ + u32 base_y = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); + u32 base_cb, base_cr; + + switch (q_data->fourcc) { + case V4L2_PIX_FMT_YUYV: + /* Fallthrough: IN -H264-> CODA -NV12 MB-> VDOA -YUYV-> OUT */ + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: + default: + base_cb = base_y + q_data->bytesperline * q_data->height; + base_cr = base_cb + q_data->bytesperline * q_data->height / 4; + break; + case V4L2_PIX_FMT_YVU420: + /* Switch Cb and Cr for YVU420 format */ + base_cr = base_y + q_data->bytesperline * q_data->height; + base_cb = base_cr + q_data->bytesperline * q_data->height / 4; + break; + case V4L2_PIX_FMT_YUV422P: + base_cb = base_y + q_data->bytesperline * q_data->height; + base_cr = base_cb + q_data->bytesperline * q_data->height / 2; + } + + coda_write(ctx->dev, base_y, reg_y); + coda_write(ctx->dev, base_cb, reg_y + 4); + coda_write(ctx->dev, base_cr, reg_y + 8); +} + +#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ + { mode, src_fourcc, dst_fourcc, max_w, max_h } + +/* + * Arrays of codecs supported by each given version of Coda: + * i.MX27 -> codadx6 + * i.MX51 -> codahx4 + * i.MX53 -> coda7 + * i.MX6 -> coda960 + * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants + */ +static const struct coda_codec codadx6_codecs[] = { + CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), +}; + +static const struct coda_codec codahx4_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720), +}; + +static const struct coda_codec coda7_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), + CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), + CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), +}; + +static const struct coda_codec coda9_codecs[] = { + CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088), + CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088), + CODA_CODEC(CODA9_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), + CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), +}; + +struct coda_video_device { + const char *name; + enum coda_inst_type type; + const struct coda_context_ops *ops; + bool direct; + u32 src_formats[CODA_MAX_FORMATS]; + u32 dst_formats[CODA_MAX_FORMATS]; +}; + +static const struct coda_video_device coda_bit_encoder = { + .name = "coda-video-encoder", + .type = CODA_INST_ENCODER, + .ops = &coda_bit_encode_ops, + .src_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + }, + .dst_formats = { + V4L2_PIX_FMT_H264, + V4L2_PIX_FMT_MPEG4, + }, +}; + +static const struct coda_video_device coda_bit_jpeg_encoder = { + .name = "coda-jpeg-encoder", + .type = CODA_INST_ENCODER, + .ops = &coda_bit_encode_ops, + .src_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, + .dst_formats = { + V4L2_PIX_FMT_JPEG, + }, +}; + +static const struct coda_video_device coda_bit_decoder = { + .name = "coda-video-decoder", + .type = CODA_INST_DECODER, + .ops = &coda_bit_decode_ops, + .src_formats = { + V4L2_PIX_FMT_H264, + V4L2_PIX_FMT_MPEG2, + V4L2_PIX_FMT_MPEG4, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + /* + * If V4L2_PIX_FMT_YUYV should be default, + * set_default_params() must be adjusted. + */ + V4L2_PIX_FMT_YUYV, + }, +}; + +static const struct coda_video_device coda_bit_jpeg_decoder = { + .name = "coda-jpeg-decoder", + .type = CODA_INST_DECODER, + .ops = &coda_bit_decode_ops, + .src_formats = { + V4L2_PIX_FMT_JPEG, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, +}; + +static const struct coda_video_device coda9_jpeg_encoder = { + .name = "coda-jpeg-encoder", + .type = CODA_INST_ENCODER, + .ops = &coda9_jpeg_encode_ops, + .direct = true, + .src_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_GREY, + }, + .dst_formats = { + V4L2_PIX_FMT_JPEG, + }, +}; + +static const struct coda_video_device coda9_jpeg_decoder = { + .name = "coda-jpeg-decoder", + .type = CODA_INST_DECODER, + .ops = &coda9_jpeg_decode_ops, + .direct = true, + .src_formats = { + V4L2_PIX_FMT_JPEG, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, +}; + +static const struct coda_video_device *codadx6_video_devices[] = { + &coda_bit_encoder, +}; + +static const struct coda_video_device *codahx4_video_devices[] = { + &coda_bit_encoder, + &coda_bit_decoder, +}; + +static const struct coda_video_device *coda7_video_devices[] = { + &coda_bit_jpeg_encoder, + &coda_bit_jpeg_decoder, + &coda_bit_encoder, + &coda_bit_decoder, +}; + +static const struct coda_video_device *coda9_video_devices[] = { + &coda9_jpeg_encoder, + &coda9_jpeg_decoder, + &coda_bit_encoder, + &coda_bit_decoder, +}; + +/* + * Normalize all supported YUV 4:2:0 formats to the value used in the codec + * tables. + */ +static u32 coda_format_normalize_yuv(u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUYV: + return V4L2_PIX_FMT_YUV420; + default: + return fourcc; + } +} + +static const struct coda_codec *coda_find_codec(struct coda_dev *dev, + int src_fourcc, int dst_fourcc) +{ + const struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + int k; + + src_fourcc = coda_format_normalize_yuv(src_fourcc); + dst_fourcc = coda_format_normalize_yuv(dst_fourcc); + if (src_fourcc == dst_fourcc) + return NULL; + + for (k = 0; k < num_codecs; k++) { + if (codecs[k].src_fourcc == src_fourcc && + codecs[k].dst_fourcc == dst_fourcc) + break; + } + + if (k == num_codecs) + return NULL; + + return &codecs[k]; +} + +static void coda_get_max_dimensions(struct coda_dev *dev, + const struct coda_codec *codec, + int *max_w, int *max_h) +{ + const struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + unsigned int w, h; + int k; + + if (codec) { + w = codec->max_w; + h = codec->max_h; + } else { + for (k = 0, w = 0, h = 0; k < num_codecs; k++) { + w = max(w, codecs[k].max_w); + h = max(h, codecs[k].max_h); + } + } + + if (max_w) + *max_w = w; + if (max_h) + *max_h = h; +} + +static const struct coda_video_device *to_coda_video_device(struct video_device + *vdev) +{ + struct coda_dev *dev = video_get_drvdata(vdev); + unsigned int i = vdev - dev->vfd; + + if (i >= dev->devtype->num_vdevs) + return NULL; + + return dev->devtype->vdevs[i]; +} + +const char *coda_product_name(int product) +{ + static char buf[9]; + + switch (product) { + case CODA_DX6: + return "CodaDx6"; + case CODA_HX4: + return "CodaHx4"; + case CODA_7541: + return "CODA7541"; + case CODA_960: + return "CODA960"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", product); + return buf; + } +} + +static struct vdoa_data *coda_get_vdoa_data(void) +{ + struct device_node *vdoa_node; + struct platform_device *vdoa_pdev; + struct vdoa_data *vdoa_data = NULL; + + vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa"); + if (!vdoa_node) + return NULL; + + vdoa_pdev = of_find_device_by_node(vdoa_node); + if (!vdoa_pdev) + goto out; + + vdoa_data = platform_get_drvdata(vdoa_pdev); + if (!vdoa_data) + vdoa_data = ERR_PTR(-EPROBE_DEFER); + + put_device(&vdoa_pdev->dev); +out: + of_node_put(vdoa_node); + + return vdoa_data; +} + +/* + * V4L2 ioctl() operations. + */ +static int coda_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + strscpy(cap->driver, CODA_NAME, sizeof(cap->driver)); + strscpy(cap->card, coda_product_name(ctx->dev->devtype->product), + sizeof(cap->card)); + strscpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); + return 0; +} + +static const u32 coda_formats_420[CODA_MAX_FORMATS] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, +}; + +static int coda_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + const struct coda_video_device *cvd = to_coda_video_device(vdev); + struct coda_ctx *ctx = fh_to_ctx(priv); + const u32 *formats; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + formats = cvd->src_formats; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + struct coda_q_data *q_data_src; + struct vb2_queue *src_vq; + + formats = cvd->dst_formats; + + /* + * If the source format is already fixed, only allow the same + * chroma subsampling. + */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && + vb2_is_streaming(src_vq)) { + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420) { + formats = coda_formats_420; + } else if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_422) { + f->pixelformat = V4L2_PIX_FMT_YUV422P; + return f->index ? -EINVAL : 0; + } + } + } else { + return -EINVAL; + } + + if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) + return -EINVAL; + + /* Skip YUYV if the vdoa is not available */ + if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + formats[f->index] == V4L2_PIX_FMT_YUYV) + return -EINVAL; + + f->pixelformat = formats[f->index]; + + return 0; +} + +static int coda_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_q_data *q_data; + struct coda_ctx *ctx = fh_to_ctx(priv); + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = q_data->fourcc; + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.bytesperline = q_data->bytesperline; + + f->fmt.pix.sizeimage = q_data->sizeimage; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + + return 0; +} + +static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f) +{ + struct coda_q_data *q_data; + const u32 *formats; + int i; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + formats = ctx->cvd->src_formats; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + formats = ctx->cvd->dst_formats; + else + return -EINVAL; + + for (i = 0; i < CODA_MAX_FORMATS; i++) { + /* Skip YUYV if the vdoa is not available */ + if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + formats[i] == V4L2_PIX_FMT_YUYV) + continue; + + if (formats[i] == f->fmt.pix.pixelformat) { + f->fmt.pix.pixelformat = formats[i]; + return 0; + } + } + + /* Fall back to currently set pixelformat */ + q_data = get_q_data(ctx, f->type); + f->fmt.pix.pixelformat = q_data->fourcc; + + return 0; +} + +static int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f, + bool *use_vdoa) +{ + int err; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!use_vdoa) + return -EINVAL; + + if (!ctx->vdoa) { + *use_vdoa = false; + return 0; + } + + err = vdoa_context_configure(NULL, round_up(f->fmt.pix.width, 16), + f->fmt.pix.height, f->fmt.pix.pixelformat); + if (err) { + *use_vdoa = false; + return 0; + } + + *use_vdoa = true; + return 0; +} + +static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage, + u32 width, u32 height) +{ + /* + * This is a rough estimate for sensible compressed buffer + * sizes (between 1 and 16 bits per pixel). This could be + * improved by better format specific worst case estimates. + */ + return round_up(clamp(sizeimage, width * height / 8, + width * height * 2), PAGE_SIZE); +} + +static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, + struct v4l2_format *f) +{ + struct coda_dev *dev = ctx->dev; + unsigned int max_w, max_h; + enum v4l2_field field; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != field) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported */ + f->fmt.pix.field = field; + + coda_get_max_dimensions(dev, codec, &max_w, &max_h); + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, + &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, + S_ALIGN); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + /* + * Frame stride must be at least multiple of 8, + * but multiple of 16 for h.264 or JPEG 4:2:x + */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + break; + case V4L2_PIX_FMT_YUV422P: + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 2; + break; + case V4L2_PIX_FMT_GREY: + /* keep 16 pixel alignment of 8-bit pixel data */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_MPEG2: + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx, + f->fmt.pix.sizeimage, + f->fmt.pix.width, + f->fmt.pix.height); + break; + default: + BUG(); + } + + return 0; +} + +static int coda_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_q_data *q_data_src; + const struct coda_codec *codec; + struct vb2_queue *src_vq; + int ret; + bool use_vdoa; + + ret = coda_try_pixelformat(ctx, f); + if (ret < 0) + return ret; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + /* + * If the source format is already fixed, only allow the same output + * resolution. When decoding JPEG images, we also have to make sure to + * use the same chroma subsampling. + */ + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (vb2_is_streaming(src_vq)) { + f->fmt.pix.width = q_data_src->width; + f->fmt.pix.height = q_data_src->height; + + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420 && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + else if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_422) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + } + } + + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + f->fmt.pix.pixelformat); + if (!codec) + return -EINVAL; + + ret = coda_try_fmt(ctx, codec, f); + if (ret < 0) + return ret; + + /* The decoders always write complete macroblocks or MCUs */ + if (ctx->inst_type == CODA_INST_DECODER) { + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.height = round_up(f->fmt.pix.height, 16); + if (codec->src_fourcc == V4L2_PIX_FMT_JPEG && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 2; + } else { + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + } + + ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa); + if (ret < 0) + return ret; + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + if (!use_vdoa) + return -EINVAL; + + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + } + } + + return 0; +} + +static void coda_set_default_colorspace(struct v4l2_pix_format *fmt) +{ + enum v4l2_colorspace colorspace; + + if (fmt->pixelformat == V4L2_PIX_FMT_JPEG) + colorspace = V4L2_COLORSPACE_JPEG; + else if (fmt->width <= 720 && fmt->height <= 576) + colorspace = V4L2_COLORSPACE_SMPTE170M; + else + colorspace = V4L2_COLORSPACE_REC709; + + fmt->colorspace = colorspace; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; +} + +static int coda_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + struct coda_dev *dev = ctx->dev; + const struct coda_q_data *q_data_dst; + const struct coda_codec *codec; + int ret; + + ret = coda_try_pixelformat(ctx, f); + if (ret < 0) + return ret; + + if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) + coda_set_default_colorspace(&f->fmt.pix); + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc); + + return coda_try_fmt(ctx, codec, f); +} + +static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, + struct v4l2_rect *r) +{ + struct coda_q_data *q_data; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s: %s queue busy: %d\n", + __func__, v4l2_type_names[f->type], vq->num_buffers); + return -EBUSY; + } + + q_data->fourcc = f->fmt.pix.pixelformat; + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + q_data->bytesperline = f->fmt.pix.bytesperline; + q_data->sizeimage = f->fmt.pix.sizeimage; + if (r) { + q_data->rect = *r; + } else { + q_data->rect.left = 0; + q_data->rect.top = 0; + q_data->rect.width = f->fmt.pix.width; + q_data->rect.height = f->fmt.pix.height; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; + break; + case V4L2_PIX_FMT_NV12: + if (!disable_tiling && ctx->use_bit && + ctx->dev->devtype->product == CODA_960) { + ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; + break; + } + fallthrough; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV422P: + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; + break; + default: + break; + } + + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP && + !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) && + ctx->use_vdoa) + vdoa_context_configure(ctx->vdoa, + round_up(f->fmt.pix.width, 16), + f->fmt.pix.height, + f->fmt.pix.pixelformat); + else + ctx->use_vdoa = false; + + coda_dbg(1, ctx, "Setting %s format, wxh: %dx%d, fmt: %4.4s %c\n", + v4l2_type_names[f->type], q_data->width, q_data->height, + (char *)&q_data->fourcc, + (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) ? 'L' : 'T'); + + return 0; +} + +static int coda_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + struct coda_q_data *q_data_src; + const struct coda_codec *codec; + struct v4l2_rect r; + int ret; + + ret = coda_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + r.left = 0; + r.top = 0; + r.width = q_data_src->width; + r.height = q_data_src->height; + + ret = coda_s_fmt(ctx, f, &r); + if (ret) + return ret; + + if (ctx->inst_type != CODA_INST_ENCODER) + return 0; + + /* Setting the coded format determines the selected codec */ + codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + f->fmt.pix.pixelformat); + if (!codec) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); + return -EINVAL; + } + ctx->codec = codec; + + ctx->colorspace = f->fmt.pix.colorspace; + ctx->xfer_func = f->fmt.pix.xfer_func; + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->quantization = f->fmt.pix.quantization; + + return 0; +} + +static int coda_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_codec *codec; + struct v4l2_format f_cap; + struct vb2_queue *dst_vq; + int ret; + + ret = coda_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ret = coda_s_fmt(ctx, f, NULL); + if (ret) + return ret; + + ctx->colorspace = f->fmt.pix.colorspace; + ctx->xfer_func = f->fmt.pix.xfer_func; + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->quantization = f->fmt.pix.quantization; + + if (ctx->inst_type != CODA_INST_DECODER) + return 0; + + /* Setting the coded format determines the selected codec */ + codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, + V4L2_PIX_FMT_YUV420); + if (!codec) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); + return -EINVAL; + } + ctx->codec = codec; + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (!dst_vq) + return -EINVAL; + + /* + * Setting the capture queue format is not possible while the capture + * queue is still busy. This is not an error, but the user will have to + * make sure themselves that the capture format is set correctly before + * starting the output queue again. + */ + if (vb2_is_busy(dst_vq)) + return 0; + + memset(&f_cap, 0, sizeof(f_cap)); + f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + coda_g_fmt(file, priv, &f_cap); + f_cap.fmt.pix.width = f->fmt.pix.width; + f_cap.fmt.pix.height = f->fmt.pix.height; + + return coda_s_fmt_vid_cap(file, priv, &f_cap); +} + +static int coda_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb); + if (ret) + return ret; + + /* + * Allow to allocate instance specific per-context buffers, such as + * bitstream ringbuffer, slice buffer, work buffer, etc. if needed. + */ + if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs) + return ctx->ops->reqbufs(ctx, rb); + + return 0; +} + +static int coda_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + if (ctx->inst_type == CODA_INST_DECODER && + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + buf->flags &= ~V4L2_BUF_FLAG_LAST; + + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); +} + +static int coda_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); + + if (ctx->inst_type == CODA_INST_DECODER && + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + buf->flags &= ~V4L2_BUF_FLAG_LAST; + + return ret; +} + +void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state) +{ + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + if (buf->flags & V4L2_BUF_FLAG_LAST) + v4l2_event_queue_fh(&ctx->fh, &eos_event); + + v4l2_m2m_buf_done(buf, state); +} + +static int coda_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + struct v4l2_rect r, *rsel; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + r.left = 0; + r.top = 0; + r.width = q_data->width; + r.height = q_data->height; + rsel = &q_data->rect; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + rsel = &r; + fallthrough; + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + ctx->inst_type == CODA_INST_DECODER) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + rsel = &r; + fallthrough; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + ctx->inst_type == CODA_INST_ENCODER) + return -EINVAL; + break; + default: + return -EINVAL; + } + + s->r = *rsel; + + return 0; +} + +static int coda_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (ctx->inst_type == CODA_INST_ENCODER && + s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + s->r.left = 0; + s->r.top = 0; + s->r.width = clamp(s->r.width, 2U, q_data->width); + s->r.height = clamp(s->r.height, 2U, q_data->height); + + if (s->flags & V4L2_SEL_FLAG_LE) { + s->r.width = round_up(s->r.width, 2); + s->r.height = round_up(s->r.height, 2); + } else { + s->r.width = round_down(s->r.width, 2); + s->r.height = round_down(s->r.height, 2); + } + + q_data->rect = s->r; + + coda_dbg(1, ctx, "Setting crop rectangle: %dx%d\n", + s->r.width, s->r.height); + + return 0; + } + fallthrough; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_COMPOSE: + return coda_g_selection(file, fh, s); + default: + /* v4l2-compliance expects this to fail for read-only targets */ + return -EINVAL; + } +} + +static int coda_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + + if (ctx->inst_type != CODA_INST_ENCODER) + return -ENOTTY; + + return v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); +} + +static void coda_wake_up_capture_queue(struct coda_ctx *ctx) +{ + struct vb2_queue *dst_vq; + + coda_dbg(1, ctx, "waking up capture queue\n"); + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_vq->last_buffer_dequeued = true; + wake_up(&dst_vq->done_wq); +} + +static int coda_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct vb2_v4l2_buffer *buf; + int ret; + + ret = coda_try_encoder_cmd(file, fh, ec); + if (ret < 0) + return ret; + + mutex_lock(&ctx->wakeup_mutex); + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (buf) { + /* + * If the last output buffer is still on the queue, make sure + * that decoder finish_run will see the last flag and report it + * to userspace. + */ + buf->flags |= V4L2_BUF_FLAG_LAST; + } else { + /* Set the stream-end flag on this context */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + /* + * If the last output buffer has already been taken from the + * queue, wake up the capture queue and signal end of stream + * via the -EPIPE mechanism. + */ + coda_wake_up_capture_queue(ctx); + } + mutex_unlock(&ctx->wakeup_mutex); + + return 0; +} + +static int coda_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + + if (ctx->inst_type != CODA_INST_DECODER) + return -ENOTTY; + + return v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); +} + +static bool coda_mark_last_meta(struct coda_ctx *ctx) +{ + struct coda_buffer_meta *meta; + + coda_dbg(1, ctx, "marking last meta\n"); + + spin_lock(&ctx->buffer_meta_lock); + if (list_empty(&ctx->buffer_meta_list)) { + spin_unlock(&ctx->buffer_meta_lock); + return false; + } + + meta = list_last_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, + list); + meta->last = true; + + spin_unlock(&ctx->buffer_meta_lock); + return true; +} + +static bool coda_mark_last_dst_buf(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *buf; + struct vb2_buffer *dst_vb; + struct vb2_queue *dst_vq; + unsigned long flags; + + coda_dbg(1, ctx, "marking last capture buffer\n"); + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + spin_lock_irqsave(&dst_vq->done_lock, flags); + if (list_empty(&dst_vq->done_list)) { + spin_unlock_irqrestore(&dst_vq->done_lock, flags); + return false; + } + + dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer, + done_entry); + buf = to_vb2_v4l2_buffer(dst_vb); + buf->flags |= V4L2_BUF_FLAG_LAST; + + spin_unlock_irqrestore(&dst_vq->done_lock, flags); + return true; +} + +static int coda_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *buf; + struct vb2_queue *dst_vq; + bool stream_end; + bool wakeup; + int ret; + + ret = coda_try_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + switch (dc->cmd) { + case V4L2_DEC_CMD_START: + mutex_lock(&dev->coda_mutex); + mutex_lock(&ctx->bitstream_mutex); + coda_bitstream_flush(ctx); + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + vb2_clear_last_buffer_dequeued(dst_vq); + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + mutex_unlock(&dev->coda_mutex); + break; + case V4L2_DEC_CMD_STOP: + stream_end = false; + wakeup = false; + + mutex_lock(&ctx->wakeup_mutex); + + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (buf) { + coda_dbg(1, ctx, "marking last pending buffer\n"); + + /* Mark last buffer */ + buf->flags |= V4L2_BUF_FLAG_LAST; + + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0) { + coda_dbg(1, ctx, "all remaining buffers queued\n"); + stream_end = true; + } + } else { + if (ctx->use_bit) + if (coda_mark_last_meta(ctx)) + stream_end = true; + else + wakeup = true; + else + if (!coda_mark_last_dst_buf(ctx)) + wakeup = true; + } + + if (stream_end) { + coda_dbg(1, ctx, "all remaining buffers queued\n"); + + /* Set the stream-end flag on this context */ + coda_bit_stream_end_flag(ctx); + ctx->hold = false; + v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); + } + + if (wakeup) { + /* If there is no buffer in flight, wake up */ + coda_wake_up_capture_queue(ctx); + } + + mutex_unlock(&ctx->wakeup_mutex); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int coda_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data_dst; + const struct coda_codec *codec; + + if (ctx->inst_type != CODA_INST_ENCODER) + return -ENOTTY; + + if (fsize->index) + return -EINVAL; + + if (coda_format_normalize_yuv(fsize->pixel_format) == + V4L2_PIX_FMT_YUV420) { + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + codec = coda_find_codec(ctx->dev, fsize->pixel_format, + q_data_dst->fourcc); + } else { + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, + fsize->pixel_format); + } + if (!codec) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = MIN_W; + fsize->stepwise.max_width = codec->max_w; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = MIN_H; + fsize->stepwise.max_height = codec->max_h; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int coda_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *f) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + int i; + + if (f->index) + return -EINVAL; + + /* Disallow YUYV if the vdoa is not available */ + if (!ctx->vdoa && f->pixel_format == V4L2_PIX_FMT_YUYV) + return -EINVAL; + + for (i = 0; i < CODA_MAX_FORMATS; i++) { + if (f->pixel_format == ctx->cvd->src_formats[i] || + f->pixel_format == ctx->cvd->dst_formats[i]) + break; + } + if (i == CODA_MAX_FORMATS) + return -EINVAL; + + f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + f->stepwise.min.numerator = 1; + f->stepwise.min.denominator = 65535; + f->stepwise.max.numerator = 65536; + f->stepwise.max.denominator = 1; + f->stepwise.step.numerator = 1; + f->stepwise.step.denominator = 1; + + return 0; +} + +static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + tpf = &a->parm.output.timeperframe; + tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; + tpf->numerator = 1 + (ctx->params.framerate >> + CODA_FRATE_DIV_OFFSET); + + return 0; +} + +/* + * Approximate timeperframe v4l2_fract with values that can be written + * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. + */ +static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) +{ + struct v4l2_fract s = *timeperframe; + struct v4l2_fract f0; + struct v4l2_fract f1 = { 1, 0 }; + struct v4l2_fract f2 = { 0, 1 }; + unsigned int i, div, s_denominator; + + /* Lower bound is 1/65535 */ + if (s.numerator == 0 || s.denominator / s.numerator > 65535) { + timeperframe->numerator = 1; + timeperframe->denominator = 65535; + return; + } + + /* Upper bound is 65536/1 */ + if (s.denominator == 0 || s.numerator / s.denominator > 65536) { + timeperframe->numerator = 65536; + timeperframe->denominator = 1; + return; + } + + /* Reduce fraction to lowest terms */ + div = gcd(s.numerator, s.denominator); + if (div > 1) { + s.numerator /= div; + s.denominator /= div; + } + + if (s.numerator <= 65536 && s.denominator < 65536) { + *timeperframe = s; + return; + } + + /* Find successive convergents from continued fraction expansion */ + while (f2.numerator <= 65536 && f2.denominator < 65536) { + f0 = f1; + f1 = f2; + + /* Stop when f2 exactly equals timeperframe */ + if (s.numerator == 0) + break; + + i = s.denominator / s.numerator; + + f2.numerator = f0.numerator + i * f1.numerator; + f2.denominator = f0.denominator + i * f2.denominator; + + s_denominator = s.numerator; + s.numerator = s.denominator % s.numerator; + s.denominator = s_denominator; + } + + *timeperframe = f1; +} + +static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) +{ + return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | + timeperframe->denominator; +} + +static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + tpf = &a->parm.output.timeperframe; + coda_approximate_timeperframe(tpf); + ctx->params.framerate = coda_timeperframe_to_frate(tpf); + ctx->params.framerate_changed = true; + + return 0; +} + +static int coda_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (ctx->inst_type == CODA_INST_DECODER) + return v4l2_event_subscribe(fh, sub, 0, NULL); + else + return -EINVAL; + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops coda_ioctl_ops = { + .vidioc_querycap = coda_querycap, + + .vidioc_enum_fmt_vid_cap = coda_enum_fmt, + .vidioc_g_fmt_vid_cap = coda_g_fmt, + .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = coda_enum_fmt, + .vidioc_g_fmt_vid_out = coda_g_fmt, + .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, + + .vidioc_reqbufs = coda_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + + .vidioc_qbuf = coda_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = coda_dqbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_g_selection = coda_g_selection, + .vidioc_s_selection = coda_s_selection, + + .vidioc_try_encoder_cmd = coda_try_encoder_cmd, + .vidioc_encoder_cmd = coda_encoder_cmd, + .vidioc_try_decoder_cmd = coda_try_decoder_cmd, + .vidioc_decoder_cmd = coda_decoder_cmd, + + .vidioc_g_parm = coda_g_parm, + .vidioc_s_parm = coda_s_parm, + + .vidioc_enum_framesizes = coda_enum_framesizes, + .vidioc_enum_frameintervals = coda_enum_frameintervals, + + .vidioc_subscribe_event = coda_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Mem-to-mem operations. + */ + +static void coda_device_run(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + struct coda_dev *dev = ctx->dev; + + queue_work(dev->workqueue, &ctx->pic_run_work); +} + +static void coda_pic_run_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + ret = ctx->ops->prepare_run(ctx); + if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) { + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + /* job_finish scheduled by prepare_decode */ + return; + } + + if (!wait_for_completion_timeout(&ctx->completion, + msecs_to_jiffies(1000))) { + if (ctx->use_bit) { + dev_err(dev->dev, "CODA PIC_RUN timeout\n"); + + ctx->hold = true; + + coda_hw_reset(ctx); + } + + if (ctx->ops->run_timeout) + ctx->ops->run_timeout(ctx); + } else { + ctx->ops->finish_run(ctx); + } + + if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) && + ctx->ops->seq_end_work) + queue_work(dev->workqueue, &ctx->seq_end_work); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); +} + +static int coda_job_ready(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); + + /* + * For both 'P' and 'key' frame cases 1 picture + * and 1 frame are needed. In the decoder case, + * the compressed frame can be in the bitstream. + */ + if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { + coda_dbg(1, ctx, "not ready: not enough vid-out buffers.\n"); + return 0; + } + + if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { + coda_dbg(1, ctx, "not ready: not enough vid-cap buffers.\n"); + return 0; + } + + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { + bool stream_end = ctx->bit_stream_param & + CODA_BIT_STREAM_END_FLAG; + int num_metas = ctx->num_metas; + struct coda_buffer_meta *meta; + unsigned int count; + + count = hweight32(ctx->frm_dis_flg); + if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) { + coda_dbg(1, ctx, + "not ready: all internal buffers in use: %d/%d (0x%x)", + count, ctx->num_internal_frames, + ctx->frm_dis_flg); + return 0; + } + + if (ctx->hold && !src_bufs) { + coda_dbg(1, ctx, + "not ready: on hold for more buffers.\n"); + return 0; + } + + if (!stream_end && (num_metas + src_bufs) < 2) { + coda_dbg(1, ctx, + "not ready: need 2 buffers available (queue:%d + bitstream:%d)\n", + num_metas, src_bufs); + return 0; + } + + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + if (!coda_bitstream_can_fetch_past(ctx, meta->end) && + !stream_end) { + coda_dbg(1, ctx, + "not ready: not enough bitstream data to read past %u (%u)\n", + meta->end, ctx->bitstream_fifo.kfifo.in); + return 0; + } + } + + if (ctx->aborting) { + coda_dbg(1, ctx, "not ready: aborting\n"); + return 0; + } + + coda_dbg(2, ctx, "job ready\n"); + + return 1; +} + +static void coda_job_abort(void *priv) +{ + struct coda_ctx *ctx = priv; + + ctx->aborting = 1; + + coda_dbg(1, ctx, "job abort\n"); +} + +static const struct v4l2_m2m_ops coda_m2m_ops = { + .device_run = coda_device_run, + .job_ready = coda_job_ready, + .job_abort = coda_job_abort, +}; + +static void set_default_params(struct coda_ctx *ctx) +{ + unsigned int max_w, max_h, usize, csize; + + ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0], + ctx->cvd->dst_formats[0]); + max_w = min(ctx->codec->max_w, 1920U); + max_h = min(ctx->codec->max_h, 1088U); + usize = max_w * max_h * 3 / 2; + csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); + + ctx->params.codec_mode = ctx->codec->mode; + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG) + ctx->colorspace = V4L2_COLORSPACE_JPEG; + else + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + ctx->params.framerate = 30; + + /* Default formats for output and input queues */ + ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; + ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; + ctx->q_data[V4L2_M2M_SRC].width = max_w; + ctx->q_data[V4L2_M2M_SRC].height = max_h; + ctx->q_data[V4L2_M2M_DST].width = max_w; + ctx->q_data[V4L2_M2M_DST].height = max_h; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { + ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; + ctx->q_data[V4L2_M2M_SRC].sizeimage = usize; + ctx->q_data[V4L2_M2M_DST].bytesperline = 0; + ctx->q_data[V4L2_M2M_DST].sizeimage = csize; + } else { + ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; + ctx->q_data[V4L2_M2M_SRC].sizeimage = csize; + ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; + ctx->q_data[V4L2_M2M_DST].sizeimage = usize; + } + ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; + ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; + ctx->q_data[V4L2_M2M_DST].rect.width = max_w; + ctx->q_data[V4L2_M2M_DST].rect.height = max_h; + + /* + * Since the RBC2AXI logic only supports a single chroma plane, + * macroblock tiling only works for to NV12 pixel format. + */ + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; +} + +/* + * Queue operations + */ +static int coda_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(vq); + struct coda_q_data *q_data; + unsigned int size; + + q_data = get_q_data(ctx, vq->type); + size = q_data->sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + coda_dbg(1, ctx, "get %d buffer(s) of size %d each.\n", *nbuffers, + size); + + return 0; +} + +static int coda_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + v4l2_warn(&ctx->dev->v4l2_dev, + "%s field isn't supported\n", __func__); + return -EINVAL; + } + } + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + v4l2_warn(&ctx->dev->v4l2_dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + return 0; +} + +static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) +{ + if (!ctrl) + return; + + v4l2_ctrl_lock(ctrl); + + /* + * Extend the control range if the parsed stream contains a known but + * unsupported value or level. + */ + if (value > ctrl->maximum) { + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } else if (value < ctrl->minimum) { + __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } + + __v4l2_ctrl_s_ctrl(ctrl, value); + + v4l2_ctrl_unlock(ctrl); +} + +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc) +{ + const char * const *profile_names; + const char * const *level_names; + struct v4l2_ctrl *profile_ctrl; + struct v4l2_ctrl *level_ctrl; + const char *codec_name; + u32 profile_cid; + u32 level_cid; + int profile; + int level; + + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_H264: + codec_name = "H264"; + profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + profile_ctrl = ctx->h264_profile_ctrl; + level_ctrl = ctx->h264_level_ctrl; + profile = coda_h264_profile(profile_idc); + level = coda_h264_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG2: + codec_name = "MPEG-2"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; + profile_ctrl = ctx->mpeg2_profile_ctrl; + level_ctrl = ctx->mpeg2_level_ctrl; + profile = coda_mpeg2_profile(profile_idc); + level = coda_mpeg2_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG4: + codec_name = "MPEG-4"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; + profile_ctrl = ctx->mpeg4_profile_ctrl; + level_ctrl = ctx->mpeg4_level_ctrl; + profile = coda_mpeg4_profile(profile_idc); + level = coda_mpeg4_level(level_idc); + break; + default: + return; + } + + profile_names = v4l2_ctrl_get_menu(profile_cid); + level_names = v4l2_ctrl_get_menu(level_cid); + + if (profile < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", + codec_name, profile_idc); + } else { + coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, + profile_names[profile]); + coda_update_menu_ctrl(profile_ctrl, profile); + } + + if (level < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", + codec_name, level_idc); + } else { + coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, + level_names[level]); + coda_update_menu_ctrl(level_ctrl, level); + } +} + +static void coda_queue_source_change_event(struct coda_ctx *ctx) +{ + static const struct v4l2_event source_change_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + v4l2_event_queue_fh(&ctx->fh, &source_change_event); +} + +static void coda_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_queue *vq = vb->vb2_queue; + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + /* + * In the decoder case, immediately try to copy the buffer into the + * bitstream ringbuffer and mark it as ready to be dequeued. + */ + if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* + * For backwards compatibility, queuing an empty buffer marks + * the stream end + */ + if (vb2_get_plane_payload(vb, 0) == 0) + coda_bit_stream_end_flag(ctx); + + if (q_data->fourcc == V4L2_PIX_FMT_H264) { + /* + * Unless already done, try to obtain profile_idc and + * level_idc from the SPS header. This allows to decide + * whether to enable reordering during sequence + * initialization. + */ + if (!ctx->params.h264_profile_idc) { + coda_sps_parse_profile(ctx, vb); + coda_update_profile_level_ctrls(ctx, + ctx->params.h264_profile_idc, + ctx->params.h264_level_idc); + } + } + + mutex_lock(&ctx->bitstream_mutex); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + if (vb2_is_streaming(vb->vb2_queue)) + /* This set buf->sequence = ctx->qsequence++ */ + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + + if (!ctx->initialized) { + /* + * Run sequence initialization in case the queued + * buffer contained headers. + */ + if (vb2_is_streaming(vb->vb2_queue) && + ctx->ops->seq_init_work) { + queue_work(ctx->dev->workqueue, + &ctx->seq_init_work); + flush_work(&ctx->seq_init_work); + } + + if (ctx->initialized) + coda_queue_source_change_event(ctx); + } + } else { + if ((ctx->inst_type == CODA_INST_ENCODER || !ctx->use_bit) && + vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + vbuf->sequence = ctx->qsequence++; + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + } +} + +int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, + size_t size, const char *name, struct dentry *parent) +{ + buf->vaddr = dma_alloc_coherent(dev->dev, size, &buf->paddr, + GFP_KERNEL); + if (!buf->vaddr) { + v4l2_err(&dev->v4l2_dev, + "Failed to allocate %s buffer of size %zu\n", + name, size); + return -ENOMEM; + } + + buf->size = size; + + if (name && parent) { + buf->blob.data = buf->vaddr; + buf->blob.size = size; + buf->dentry = debugfs_create_blob(name, 0444, parent, + &buf->blob); + } + + return 0; +} + +void coda_free_aux_buf(struct coda_dev *dev, + struct coda_aux_buf *buf) +{ + if (buf->vaddr) { + dma_free_coherent(dev->dev, buf->size, buf->vaddr, buf->paddr); + buf->vaddr = NULL; + buf->size = 0; + debugfs_remove(buf->dentry); + buf->dentry = NULL; + } +} + +static int coda_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(q); + struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + struct v4l2_m2m_buffer *m2m_buf, *tmp; + struct vb2_v4l2_buffer *buf; + struct list_head list; + int ret = 0; + + if (count < 1) + return -EINVAL; + + coda_dbg(1, ctx, "start streaming %s\n", v4l2_type_names[q->type]); + + INIT_LIST_HEAD(&list); + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { + /* copy the buffers that were queued before streamon */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx, &list); + mutex_unlock(&ctx->bitstream_mutex); + + if (ctx->dev->devtype->product != CODA_960 && + coda_get_bitstream_payload(ctx) < 512) { + v4l2_err(v4l2_dev, "start payload < 512\n"); + ret = -EINVAL; + goto err; + } + + if (!ctx->initialized) { + /* Run sequence initialization */ + if (ctx->ops->seq_init_work) { + queue_work(ctx->dev->workqueue, + &ctx->seq_init_work); + flush_work(&ctx->seq_init_work); + } + } + } + + /* + * Check the first input JPEG buffer to determine chroma + * subsampling. + */ + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { + buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + ret = coda_jpeg_decode_header(ctx, &buf->vb2_buf); + if (ret < 0) { + v4l2_err(v4l2_dev, + "failed to decode JPEG header: %d\n", + ret); + goto err; + } + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + q_data_dst->width = round_up(q_data_src->width, 16); + q_data_dst->height = round_up(q_data_src->height, 16); + q_data_dst->bytesperline = q_data_dst->width; + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420) { + q_data_dst->sizeimage = + q_data_dst->bytesperline * + q_data_dst->height * 3 / 2; + if (q_data_dst->fourcc != V4L2_PIX_FMT_YUV420) + q_data_dst->fourcc = V4L2_PIX_FMT_NV12; + } else { + q_data_dst->sizeimage = + q_data_dst->bytesperline * + q_data_dst->height * 2; + q_data_dst->fourcc = V4L2_PIX_FMT_YUV422P; + } + q_data_dst->rect.left = 0; + q_data_dst->rect.top = 0; + q_data_dst->rect.width = q_data_src->width; + q_data_dst->rect.height = q_data_src->height; + } + ctx->streamon_out = 1; + } else { + ctx->streamon_cap = 1; + } + + /* Don't start the coda unless both queues are on */ + if (!(ctx->streamon_out && ctx->streamon_cap)) + goto out; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if ((q_data_src->rect.width != q_data_dst->width && + round_up(q_data_src->rect.width, 16) != q_data_dst->width) || + (q_data_src->rect.height != q_data_dst->height && + round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { + v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", + q_data_src->rect.width, q_data_src->rect.height, + q_data_dst->width, q_data_dst->height); + ret = -EINVAL; + goto err; + } + + /* Allow BIT decoder device_run with no new buffers queued */ + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) + v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); + + ctx->gopcounter = ctx->params.gop_size - 1; + + if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG) + ctx->params.gop_size = 1; + ctx->gopcounter = ctx->params.gop_size - 1; + /* Only decoders have this control */ + if (ctx->mb_err_cnt_ctrl) + v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0); + + ret = ctx->ops->start_streaming(ctx); + if (ctx->inst_type == CODA_INST_DECODER) { + if (ret == -EAGAIN) + goto out; + } + if (ret < 0) + goto err; + +out: + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + list_for_each_entry_safe(m2m_buf, tmp, &list, list) { + list_del(&m2m_buf->list); + v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_DONE); + } + } + return 0; + +err: + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + list_for_each_entry_safe(m2m_buf, tmp, &list, list) { + list_del(&m2m_buf->list); + v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_QUEUED); + } + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } else { + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } + return ret; +} + +static void coda_stop_streaming(struct vb2_queue *q) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(q); + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *buf; + bool stop; + + stop = ctx->streamon_out && ctx->streamon_cap; + + coda_dbg(1, ctx, "stop streaming %s\n", v4l2_type_names[q->type]); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + ctx->streamon_out = 0; + + coda_bit_stream_end_flag(ctx); + + ctx->qsequence = 0; + + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } else { + ctx->streamon_cap = 0; + + ctx->osequence = 0; + ctx->sequence_offset = 0; + + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + if (stop) { + struct coda_buffer_meta *meta; + + if (ctx->ops->seq_end_work) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); + } + spin_lock(&ctx->buffer_meta_lock); + while (!list_empty(&ctx->buffer_meta_list)) { + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + list_del(&meta->list); + kfree(meta); + } + ctx->num_metas = 0; + spin_unlock(&ctx->buffer_meta_lock); + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + ctx->runcounter = 0; + ctx->aborting = 0; + ctx->hold = false; + } + + if (!ctx->streamon_out && !ctx->streamon_cap) + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; +} + +static const struct vb2_ops coda_qops = { + .queue_setup = coda_queue_setup, + .buf_prepare = coda_buf_prepare, + .buf_queue = coda_buf_queue, + .start_streaming = coda_start_streaming, + .stop_streaming = coda_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int coda_s_ctrl(struct v4l2_ctrl *ctrl) +{ + const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); + struct coda_ctx *ctx = + container_of(ctrl->handler, struct coda_ctx, ctrls); + + if (val_names) + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", + ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); + else + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", + ctrl->id, ctrl->name, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + if (ctrl->val) + ctx->params.rot_mode |= CODA_MIR_HOR; + else + ctx->params.rot_mode &= ~CODA_MIR_HOR; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + ctx->params.rot_mode |= CODA_MIR_VER; + else + ctx->params.rot_mode &= ~CODA_MIR_VER; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctx->params.bitrate = ctrl->val / 1000; + ctx->params.bitrate_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctx->params.gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + ctx->params.h264_intra_qp = ctrl->val; + ctx->params.h264_intra_qp_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + ctx->params.h264_inter_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctx->params.h264_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctx->params.h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + ctx->params.h264_slice_alpha_c0_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + ctx->params.h264_slice_beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + ctx->params.h264_constrained_intra_pred_flag = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + ctx->params.frame_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + ctx->params.mb_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + ctx->params.h264_chroma_qp_index_offset = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + /* TODO: switch between baseline and constrained baseline */ + if (ctx->inst_type == CODA_INST_ENCODER) + ctx->params.h264_profile_idc = 66; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + /* nothing to do, this is set by the encoder */ + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: + ctx->params.mpeg4_intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: + ctx->params.mpeg4_inter_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + /* nothing to do, these are fixed */ + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + ctx->params.slice_mode = ctrl->val; + ctx->params.slice_mode_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + ctx->params.slice_max_mb = ctrl->val; + ctx->params.slice_mode_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + ctx->params.slice_max_bits = ctrl->val * 8; + ctx->params.slice_mode_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + ctx->params.intra_refresh = ctrl->val; + ctx->params.intra_refresh_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + ctx->params.force_ipicture = true; + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + coda_set_jpeg_compression_quality(ctx, ctrl->val); + break; + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->params.jpeg_restart_interval = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_DELAY: + ctx->params.vbv_delay = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); + break; + default: + coda_dbg(1, ctx, "Invalid control, id=%d, val=%d\n", + ctrl->id, ctrl->val); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops coda_ctrl_ops = { + .s_ctrl = coda_s_ctrl, +}; + +static void coda_encode_ctrls(struct coda_ctx *ctx) +{ + int max_gop_size = (ctx->dev->devtype->product == CODA_DX6) ? 60 : 99; + + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, max_gop_size, 1, 16); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); + if (ctx->dev->devtype->product != CODA_960) { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); + } + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, + 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, + 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) { + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_3_1, + ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1)), + V4L2_MPEG_VIDEO_H264_LEVEL_3_1); + } + if (ctx->dev->devtype->product == CODA_960) { + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_4_0, + ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0)), + V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + } + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541 || + ctx->dev->devtype->product == CODA_960) { + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + ~(1 << V4L2_MPEG_VIDEO_MPEG4_LEVEL_5), + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); + } + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, + 500); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, + 1920 * 1088 / 256, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); + /* + * The maximum VBV size value is 0x7fffffff bits, + * one bit less than 262144 KiB + */ + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); +} + +static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) +{ + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); +} + +static void coda_decode_ctrls(struct coda_ctx *ctx) +{ + u8 max; + + ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + if (ctx->h264_profile_ctrl) + ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + else if (ctx->dev->devtype->product == CODA_960) + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + else + return; + ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); + if (ctx->h264_level_ctrl) + ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); + if (ctx->mpeg2_profile_ctrl) + ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); + if (ctx->mpeg2_level_ctrl) + ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); + if (ctx->mpeg4_profile_ctrl) + ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); + if (ctx->mpeg4_level_ctrl) + ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; +} + +static const struct v4l2_ctrl_config coda_mb_err_cnt_ctrl_config = { + .id = V4L2_CID_CODA_MB_ERR_CNT, + .name = "Macroblocks Error Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0x7fffffff, + .step = 1, +}; + +static int coda_ctrls_setup(struct coda_ctx *ctx) +{ + v4l2_ctrl_handler_init(&ctx->ctrls, 2); + + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ctx->inst_type == CODA_INST_ENCODER) { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 1, 1, 1); + if (ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) + coda_jpeg_encode_ctrls(ctx); + else + coda_encode_ctrls(ctx); + } else { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + 1, 1, 1, 1); + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) + coda_decode_ctrls(ctx); + + ctx->mb_err_cnt_ctrl = v4l2_ctrl_new_custom(&ctx->ctrls, + &coda_mb_err_cnt_ctrl_config, + NULL); + if (ctx->mb_err_cnt_ctrl) + ctx->mb_err_cnt_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + } + + if (ctx->ctrls.error) { + v4l2_err(&ctx->dev->v4l2_dev, + "control initialization error (%d)", + ctx->ctrls.error); + return -EINVAL; + } + + return v4l2_ctrl_handler_setup(&ctx->ctrls); +} + +static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) +{ + vq->drv_priv = ctx; + vq->ops = &coda_qops; + vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + vq->lock = &ctx->dev->dev_mutex; + /* One way to indicate end-of-stream for coda is to set the + * bytesused == 0. However by default videobuf2 handles bytesused + * equal to 0 as a special case and changes its value to the size + * of the buffer. Set the allow_zero_bytesused flag, so + * that videobuf2 will keep the value of bytesused intact. + */ + vq->allow_zero_bytesused = 1; + /* + * We might be fine with no buffers on some of the queues, but that + * would need to be reflected in job_ready(). Currently we expect all + * queues to have at least one buffer queued. + */ + vq->min_buffers_needed = 1; + vq->dev = ctx->dev->dev; + + return vb2_queue_init(vq); +} + +int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = coda_queue_init(priv, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return coda_queue_init(priv, dst_vq); +} + +int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->mem_ops = &vb2_vmalloc_memops; + + ret = coda_queue_init(priv, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return coda_queue_init(priv, dst_vq); +} + +/* + * File operations + */ + +static int coda_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct coda_dev *dev = video_get_drvdata(vdev); + struct coda_ctx *ctx; + unsigned int max = ~0; + char *name; + int ret; + int idx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (dev->devtype->product == CODA_DX6) + max = CODADX6_MAX_INSTANCES - 1; + idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL); + if (idx < 0) { + ret = idx; + goto err_coda_max; + } + + name = kasprintf(GFP_KERNEL, "context%d", idx); + if (!name) { + ret = -ENOMEM; + goto err_coda_name_init; + } + + ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); + kfree(name); + + ctx->cvd = to_coda_video_device(vdev); + ctx->inst_type = ctx->cvd->type; + ctx->ops = ctx->cvd->ops; + ctx->use_bit = !ctx->cvd->direct; + init_completion(&ctx->completion); + INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); + if (ctx->ops->seq_init_work) + INIT_WORK(&ctx->seq_init_work, ctx->ops->seq_init_work); + if (ctx->ops->seq_end_work) + INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + ctx->dev = dev; + ctx->idx = idx; + + coda_dbg(1, ctx, "open instance (%p)\n", ctx); + + switch (dev->devtype->product) { + case CODA_960: + /* + * Enabling the BWB when decoding can hang the firmware with + * certain streams. The issue was tracked as ENGR00293425 by + * Freescale. As a workaround, disable BWB for all decoders. + * The enable_bwb module parameter allows to override this. + */ + if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) + ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; + fallthrough; + case CODA_HX4: + case CODA_7541: + ctx->reg_idx = 0; + break; + default: + ctx->reg_idx = idx; + } + if (ctx->dev->vdoa && !disable_vdoa) { + ctx->vdoa = vdoa_context_create(dev->vdoa); + if (!ctx->vdoa) + v4l2_warn(&dev->v4l2_dev, + "Failed to create vdoa context: not using vdoa"); + } + ctx->use_vdoa = false; + + /* Power up and upload firmware if necessary */ + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); + goto err_pm_get; + } + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_enable; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + set_default_params(ctx); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + ctx->ops->queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + + v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", + __func__, ret); + goto err_ctx_init; + } + + ret = coda_ctrls_setup(ctx); + if (ret) { + v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); + goto err_ctrls_setup; + } + + ctx->fh.ctrl_handler = &ctx->ctrls; + + mutex_init(&ctx->bitstream_mutex); + mutex_init(&ctx->buffer_mutex); + mutex_init(&ctx->wakeup_mutex); + INIT_LIST_HEAD(&ctx->buffer_meta_list); + spin_lock_init(&ctx->buffer_meta_lock); + + return 0; + +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +err_ctx_init: + clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_enable: + pm_runtime_put_sync(dev->dev); +err_pm_get: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); +err_coda_name_init: + ida_free(&dev->ida, ctx->idx); +err_coda_max: + kfree(ctx); + return ret; +} + +static int coda_release(struct file *file) +{ + struct coda_dev *dev = video_drvdata(file); + struct coda_ctx *ctx = fh_to_ctx(file->private_data); + + coda_dbg(1, ctx, "release instance (%p)\n", ctx); + + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) + coda_bit_stream_end_flag(ctx); + + /* If this instance is running, call .job_abort and wait for it to end */ + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + if (ctx->vdoa) + vdoa_context_destroy(ctx->vdoa); + + /* In case the instance was not running, we still need to call SEQ_END */ + if (ctx->ops->seq_end_work) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); + } + + if (ctx->dev->devtype->product == CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + + v4l2_ctrl_handler_free(&ctx->ctrls); + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + pm_runtime_put_sync(dev->dev); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + ida_free(&dev->ida, ctx->idx); + if (ctx->ops->release) + ctx->ops->release(ctx); + debugfs_remove_recursive(ctx->debugfs_entry); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations coda_fops = { + .owner = THIS_MODULE, + .open = coda_open, + .release = coda_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int coda_hw_init(struct coda_dev *dev) +{ + u32 data; + u16 *p; + int i, ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + reset_control_reset(dev->rstc); + + /* + * Copy the first CODA_ISRAM_SIZE in the internal SRAM. + * The 16-bit chars in the code buffer are in memory access + * order, re-sort them to CODA order for register download. + * Data in this SRAM survives a reboot. + */ + p = (u16 *)dev->codebuf.vaddr; + if (dev->devtype->product == CODA_DX6) { + for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { + data = CODA_DOWN_ADDRESS_SET(i) | + CODA_DOWN_DATA_SET(p[i ^ 1]); + coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); + } + } else { + for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { + data = CODA_DOWN_ADDRESS_SET(i) | + CODA_DOWN_DATA_SET(p[round_down(i, 4) + + 3 - (i % 4)]); + coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); + } + } + + /* Clear registers */ + for (i = 0; i < 64; i++) + coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); + + /* Tell the BIT where to find everything it needs */ + if (dev->devtype->product == CODA_960 || + dev->devtype->product == CODA_7541 || + dev->devtype->product == CODA_HX4) { + coda_write(dev, dev->tempbuf.paddr, + CODA_REG_BIT_TEMP_BUF_ADDR); + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + } else { + coda_write(dev, dev->workbuf.paddr, + CODA_REG_BIT_WORK_BUF_ADDR); + } + coda_write(dev, dev->codebuf.paddr, + CODA_REG_BIT_CODE_BUF_ADDR); + coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); + + /* Set default values */ + switch (dev->devtype->product) { + case CODA_DX6: + coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, + CODA_REG_BIT_STREAM_CTRL); + break; + default: + coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, + CODA_REG_BIT_STREAM_CTRL); + } + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_FRAME_ENABLE_BWB, + CODA_REG_BIT_FRAME_MEM_CTRL); + else + coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); + + coda_write(dev, CODA_INT_INTERRUPT_ENABLE, + CODA_REG_BIT_INT_ENABLE); + + /* Reset VPU and start processor */ + data = coda_read(dev, CODA_REG_BIT_CODE_RESET); + data |= CODA_REG_RESET_ENABLE; + coda_write(dev, data, CODA_REG_BIT_CODE_RESET); + udelay(10); + data &= ~CODA_REG_RESET_ENABLE; + coda_write(dev, data, CODA_REG_BIT_CODE_RESET); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + + return 0; + +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +static int coda_register_device(struct coda_dev *dev, int i) +{ + struct video_device *vfd = &dev->vfd[i]; + const char *name; + int ret; + + if (i >= dev->devtype->num_vdevs) + return -EINVAL; + name = dev->devtype->vdevs[i]->name; + + strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); + vfd->fops = &coda_fops; + vfd->ioctl_ops = &coda_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + video_set_drvdata(vfd, dev); + + /* Not applicable, use the selection API instead */ + v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); + v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); + v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (!ret) + v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", + name, video_device_node_name(vfd)); + return ret; +} + +static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, + size_t size) +{ + u32 *src = (u32 *)buf; + + /* Check if the firmware has a 16-byte Freescale header, skip it */ + if (buf[0] == 'M' && buf[1] == 'X') + src += 4; + /* + * Check whether the firmware is in native order or pre-reordered for + * memory access. The first instruction opcode always is 0xe40e. + */ + if (__le16_to_cpup((__le16 *)src) == 0xe40e) { + u32 *dst = dev->codebuf.vaddr; + int i; + + /* Firmware in native order, reorder while copying */ + if (dev->devtype->product == CODA_DX6) { + for (i = 0; i < (size - 16) / 4; i++) + dst[i] = (src[i] << 16) | (src[i] >> 16); + } else { + for (i = 0; i < (size - 16) / 4; i += 2) { + dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16); + dst[i + 1] = (src[i] << 16) | (src[i] >> 16); + } + } + } else { + /* Copy the already reordered firmware image */ + memcpy(dev->codebuf.vaddr, src, size); + } +} + +static void coda_fw_callback(const struct firmware *fw, void *context); + +static int coda_firmware_request(struct coda_dev *dev) +{ + char *fw; + + if (dev->firmware >= ARRAY_SIZE(dev->devtype->firmware)) + return -EINVAL; + + fw = dev->devtype->firmware[dev->firmware]; + + dev_dbg(dev->dev, "requesting firmware '%s' for %s\n", fw, + coda_product_name(dev->devtype->product)); + + return request_firmware_nowait(THIS_MODULE, true, fw, dev->dev, + GFP_KERNEL, dev, coda_fw_callback); +} + +static void coda_fw_callback(const struct firmware *fw, void *context) +{ + struct coda_dev *dev = context; + int i, ret; + + if (!fw) { + dev->firmware++; + ret = coda_firmware_request(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); + goto put_pm; + } + return; + } + if (dev->firmware > 0) { + /* + * Since we can't suppress warnings for failed asynchronous + * firmware requests, report that the fallback firmware was + * found. + */ + dev_info(dev->dev, "Using fallback firmware %s\n", + dev->devtype->firmware[dev->firmware]); + } + + /* allocate auxiliary per-device code buffer for the BIT processor */ + ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", + dev->debugfs_root); + if (ret < 0) + goto put_pm; + + coda_copy_firmware(dev, fw->data, fw->size); + release_firmware(fw); + + ret = coda_hw_init(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); + goto put_pm; + } + + ret = coda_check_firmware(dev); + if (ret < 0) + goto put_pm; + + dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); + goto put_pm; + } + + for (i = 0; i < dev->devtype->num_vdevs; i++) { + ret = coda_register_device(dev, i); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to register %s video device: %d\n", + dev->devtype->vdevs[i]->name, ret); + goto rel_vfd; + } + } + + pm_runtime_put_sync(dev->dev); + return; + +rel_vfd: + while (--i >= 0) + video_unregister_device(&dev->vfd[i]); + v4l2_m2m_release(dev->m2m_dev); +put_pm: + pm_runtime_put_sync(dev->dev); +} + +enum coda_platform { + CODA_IMX27, + CODA_IMX51, + CODA_IMX53, + CODA_IMX6Q, + CODA_IMX6DL, +}; + +static const struct coda_devtype coda_devdata[] = { + [CODA_IMX27] = { + .firmware = { + "vpu_fw_imx27_TO2.bin", + "vpu/vpu_fw_imx27_TO2.bin", + "v4l-codadx6-imx27.bin" + }, + .product = CODA_DX6, + .codecs = codadx6_codecs, + .num_codecs = ARRAY_SIZE(codadx6_codecs), + .vdevs = codadx6_video_devices, + .num_vdevs = ARRAY_SIZE(codadx6_video_devices), + .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, + .iram_size = 0xb000, + }, + [CODA_IMX51] = { + .firmware = { + "vpu_fw_imx51.bin", + "vpu/vpu_fw_imx51.bin", + "v4l-codahx4-imx51.bin" + }, + .product = CODA_HX4, + .codecs = codahx4_codecs, + .num_codecs = ARRAY_SIZE(codahx4_codecs), + .vdevs = codahx4_video_devices, + .num_vdevs = ARRAY_SIZE(codahx4_video_devices), + .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, + }, + [CODA_IMX53] = { + .firmware = { + "vpu_fw_imx53.bin", + "vpu/vpu_fw_imx53.bin", + "v4l-coda7541-imx53.bin" + }, + .product = CODA_7541, + .codecs = coda7_codecs, + .num_codecs = ARRAY_SIZE(coda7_codecs), + .vdevs = coda7_video_devices, + .num_vdevs = ARRAY_SIZE(coda7_video_devices), + .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, + }, + [CODA_IMX6Q] = { + .firmware = { + "vpu_fw_imx6q.bin", + "vpu/vpu_fw_imx6q.bin", + "v4l-coda960-imx6q.bin" + }, + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .vdevs = coda9_video_devices, + .num_vdevs = ARRAY_SIZE(coda9_video_devices), + .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, + .iram_size = 0x21000, + }, + [CODA_IMX6DL] = { + .firmware = { + "vpu_fw_imx6d.bin", + "vpu/vpu_fw_imx6d.bin", + "v4l-coda960-imx6dl.bin" + }, + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .vdevs = coda9_video_devices, + .num_vdevs = ARRAY_SIZE(coda9_video_devices), + .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, + .iram_size = 0x1f000, /* leave 4k for suspend code */ + }, +}; + +static const struct of_device_id coda_dt_ids[] = { + { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, + { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, + { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, + { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, + { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, coda_dt_ids); + +static int coda_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct gen_pool *pool; + struct coda_dev *dev; + int ret, irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->devtype = of_device_get_match_data(&pdev->dev); + + dev->dev = &pdev->dev; + dev->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(dev->clk_per)) { + dev_err(&pdev->dev, "Could not get per clock\n"); + return PTR_ERR(dev->clk_per); + } + + dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(dev->clk_ahb)) { + dev_err(&pdev->dev, "Could not get ahb clock\n"); + return PTR_ERR(dev->clk_ahb); + } + + /* Get memory for physical registers */ + dev->regs_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->regs_base)) + return PTR_ERR(dev->regs_base); + + /* IRQ */ + irq = platform_get_irq_byname(pdev, "bit"); + if (irq < 0) + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, + CODA_NAME "-video", dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; + } + + /* JPEG IRQ */ + if (dev->devtype->product == CODA_960) { + irq = platform_get_irq_byname(pdev, "jpeg"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + coda9_jpeg_irq_handler, + IRQF_ONESHOT, CODA_NAME "-jpeg", + dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request jpeg irq\n"); + return ret; + } + } + + dev->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, + NULL); + if (IS_ERR(dev->rstc)) { + ret = PTR_ERR(dev->rstc); + dev_err(&pdev->dev, "failed get reset control: %d\n", ret); + return ret; + } + + /* Get IRAM pool from device tree */ + pool = of_gen_pool_get(np, "iram", 0); + if (!pool) { + dev_err(&pdev->dev, "iram pool not available\n"); + return -ENOMEM; + } + dev->iram_pool = pool; + + /* Get vdoa_data if supported by the platform */ + dev->vdoa = coda_get_vdoa_data(); + if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + ratelimit_default_init(&dev->mb_err_rs); + mutex_init(&dev->dev_mutex); + mutex_init(&dev->coda_mutex); + ida_init(&dev->ida); + + dev->debugfs_root = debugfs_create_dir("coda", NULL); + + /* allocate auxiliary per-device buffers for the BIT processor */ + if (dev->devtype->product == CODA_DX6) { + ret = coda_alloc_aux_buf(dev, &dev->workbuf, + dev->devtype->workbuf_size, "workbuf", + dev->debugfs_root); + if (ret < 0) + goto err_v4l2_register; + } + + if (dev->devtype->tempbuf_size) { + ret = coda_alloc_aux_buf(dev, &dev->tempbuf, + dev->devtype->tempbuf_size, "tempbuf", + dev->debugfs_root); + if (ret < 0) + goto err_v4l2_register; + } + + dev->iram.size = dev->devtype->iram_size; + dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, + &dev->iram.paddr); + if (!dev->iram.vaddr) { + dev_warn(&pdev->dev, "unable to alloc iram\n"); + } else { + memset(dev->iram.vaddr, 0, dev->iram.size); + dev->iram.blob.data = dev->iram.vaddr; + dev->iram.blob.size = dev->iram.size; + dev->iram.dentry = debugfs_create_blob("iram", 0444, + dev->debugfs_root, + &dev->iram.blob); + } + + dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!dev->workqueue) { + dev_err(&pdev->dev, "unable to alloc workqueue\n"); + ret = -ENOMEM; + goto err_v4l2_register; + } + + platform_set_drvdata(pdev, dev); + + /* + * Start activated so we can directly call coda_hw_init in + * coda_fw_callback regardless of whether CONFIG_PM is + * enabled or whether the device is associated with a PM domain. + */ + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = coda_firmware_request(dev); + if (ret) + goto err_alloc_workqueue; + return 0; + +err_alloc_workqueue: + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + destroy_workqueue(dev->workqueue); +err_v4l2_register: + v4l2_device_unregister(&dev->v4l2_dev); + return ret; +} + +static int coda_remove(struct platform_device *pdev) +{ + struct coda_dev *dev = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) { + if (video_get_drvdata(&dev->vfd[i])) + video_unregister_device(&dev->vfd[i]); + } + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + pm_runtime_disable(&pdev->dev); + v4l2_device_unregister(&dev->v4l2_dev); + destroy_workqueue(dev->workqueue); + if (dev->iram.vaddr) + gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, + dev->iram.size); + coda_free_aux_buf(dev, &dev->codebuf); + coda_free_aux_buf(dev, &dev->tempbuf); + coda_free_aux_buf(dev, &dev->workbuf); + debugfs_remove_recursive(dev->debugfs_root); + ida_destroy(&dev->ida); + return 0; +} + +#ifdef CONFIG_PM +static int coda_runtime_resume(struct device *dev) +{ + struct coda_dev *cdev = dev_get_drvdata(dev); + int ret = 0; + + if (dev->pm_domain && cdev->codebuf.vaddr) { + ret = coda_hw_init(cdev); + if (ret) + v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); + } + + return ret; +} +#endif + +static const struct dev_pm_ops coda_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) +}; + +static struct platform_driver coda_driver = { + .probe = coda_probe, + .remove = coda_remove, + .driver = { + .name = CODA_NAME, + .of_match_table = coda_dt_ids, + .pm = &coda_pm_ops, + }, +}; + +module_platform_driver(coda_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Martin "); +MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); diff --git a/drivers/media/platform/chips-media/coda-gdi.c b/drivers/media/platform/chips-media/coda-gdi.c new file mode 100644 index 000000000000..59d65daca153 --- /dev/null +++ b/drivers/media/platform/chips-media/coda-gdi.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2014 Philipp Zabel, Pengutronix + */ + +#include +#include "coda.h" + +#define XY2_INVERT BIT(7) +#define XY2_ZERO BIT(6) +#define XY2_TB_XOR BIT(5) +#define XY2_XYSEL BIT(4) +#define XY2_Y (1 << 4) +#define XY2_X (0 << 4) + +#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((XY2_##luma_sel) | (luma_bit)) << 8 | \ + (XY2_##chroma_sel) | (chroma_bit)) + +static const u16 xy2ca_zero_map[16] = { + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +static const u16 xy2ca_tiled_map[16] = { + XY2(Y, 0, Y, 0), + XY2(Y, 1, Y, 1), + XY2(Y, 2, Y, 2), + XY2(Y, 3, X, 3), + XY2(X, 3, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +/* + * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock + * start offset (macroblock size is 16x16 for luma, 16x8 for chroma). + * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused. + */ + +#define RBC_CA (0 << 4) +#define RBC_BA (1 << 4) +#define RBC_RA (2 << 4) +#define RBC_ZERO (3 << 4) + +#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((RBC_##luma_sel) | (luma_bit)) << 6 | \ + (RBC_##chroma_sel) | (chroma_bit)) + +static const u16 rbc2axi_tiled_map[32] = { + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(CA, 0, CA, 0), + RBC(CA, 1, CA, 1), + RBC(CA, 2, CA, 2), + RBC(CA, 3, CA, 3), + RBC(CA, 4, CA, 8), + RBC(CA, 8, CA, 9), + RBC(CA, 9, CA, 10), + RBC(CA, 10, CA, 11), + RBC(CA, 11, CA, 12), + RBC(CA, 12, CA, 13), + RBC(CA, 13, CA, 14), + RBC(CA, 14, CA, 15), + RBC(CA, 15, RA, 0), + RBC(RA, 0, RA, 1), + RBC(RA, 1, RA, 2), + RBC(RA, 2, RA, 3), + RBC(RA, 3, RA, 4), + RBC(RA, 4, RA, 5), + RBC(RA, 5, RA, 6), + RBC(RA, 6, RA, 7), + RBC(RA, 7, RA, 8), + RBC(RA, 8, RA, 9), + RBC(RA, 9, RA, 10), + RBC(RA, 10, RA, 11), + RBC(RA, 11, RA, 12), + RBC(RA, 12, RA, 13), + RBC(RA, 13, RA, 14), + RBC(RA, 14, RA, 15), + RBC(RA, 15, ZERO, 0), +}; + +void coda_set_gdi_regs(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + const u16 *xy2ca_map; + u32 xy2rbc_config; + int i; + + switch (ctx->tiled_map_type) { + case GDI_LINEAR_FRAME_MAP: + default: + xy2ca_map = xy2ca_zero_map; + xy2rbc_config = 0; + break; + case GDI_TILED_FRAME_MB_RASTER_MAP: + xy2ca_map = xy2ca_tiled_map; + xy2rbc_config = CODA9_XY2RBC_TILED_MAP | + CODA9_XY2RBC_CA_INC_HOR | + (16 - 1) << 12 | (8 - 1) << 4; + break; + } + + for (i = 0; i < 16; i++) + coda_write(dev, xy2ca_map[i], + CODA9_GDI_XY2_CAS_0 + 4 * i); + for (i = 0; i < 4; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_BA_0 + 4 * i); + for (i = 0; i < 16; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_RAS_0 + 4 * i); + coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); + if (xy2rbc_config) { + for (i = 0; i < 32; i++) + coda_write(dev, rbc2axi_tiled_map[i], + CODA9_GDI_RBC2_AXI_0 + 4 * i); + } +} diff --git a/drivers/media/platform/chips-media/coda-h264.c b/drivers/media/platform/chips-media/coda-h264.c new file mode 100644 index 000000000000..8bd0aa8af114 --- /dev/null +++ b/drivers/media/platform/chips-media/coda-h264.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - H.264 helper functions + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + */ + +#include +#include +#include + +#include "coda.h" + +static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; + +static const u8 *coda_find_nal_header(const u8 *buf, const u8 *end) +{ + u32 val = 0xffffffff; + + do { + val = val << 8 | *buf++; + if (buf >= end) + return NULL; + } while (val != 0x00000001); + + return buf; +} + +int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + const u8 *buf = vb2_plane_vaddr(vb, 0); + const u8 *end = buf + vb2_get_plane_payload(vb, 0); + + /* Find SPS header */ + do { + buf = coda_find_nal_header(buf, end); + if (!buf) + return -EINVAL; + } while ((*buf++ & 0x1f) != 0x7); + + ctx->params.h264_profile_idc = buf[0]; + ctx->params.h264_level_idc = buf[2]; + + return 0; +} + +int coda_h264_filler_nal(int size, char *p) +{ + if (size < 6) + return -EINVAL; + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + p[4] = 0x0c; + memset(p + 5, 0xff, size - 6); + /* Add rbsp stop bit and trailing at the end */ + p[size - 1] = 0x80; + + return 0; +} + +int coda_h264_padding(int size, char *p) +{ + int nal_size; + int diff; + + diff = size - (size & ~0x7); + if (diff == 0) + return 0; + + nal_size = coda_filler_size[diff]; + coda_h264_filler_nal(nal_size, p); + + return nal_size; +} + +int coda_h264_profile(int profile_idc) +{ + switch (profile_idc) { + case 66: return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case 77: return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case 88: return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; + case 100: return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + default: return -EINVAL; + } +} + +int coda_h264_level(int level_idc) +{ + switch (level_idc) { + case 10: return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + case 9: return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + case 11: return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + case 12: return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + case 13: return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + case 20: return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + case 21: return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + case 22: return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + case 30: return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + case 31: return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + case 32: return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + case 40: return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + case 41: return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case 42: return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case 50: return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case 51: return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + default: return -EINVAL; + } +} + +struct rbsp { + char *buf; + int size; + int pos; +}; + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + return (rbsp->buf[ofs] >> shift) & 1; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, int bit) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->buf[ofs] &= ~(1 << shift); + rbsp->buf[ofs] |= bit << shift; + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int num, int *val) +{ + int i, ret; + int tmp = 0; + + if (num > 32) + return -EINVAL; + + for (i = 0; i < num; i++) { + ret = rbsp_read_bit(rbsp); + if (ret < 0) + return ret; + tmp |= ret << (num - i - 1); + } + + if (val) + *val = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int num, int value) +{ + int ret; + + while (num--) { + ret = rbsp_write_bit(rbsp, (value >> num) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *val) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (val) + *val = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int value) +{ + int i; + int ret; + int tmp = value + 1; + int leading_zero_bits = fls(tmp) - 1; + + for (i = 0; i < leading_zero_bits; i++) { + ret = rbsp_write_bit(rbsp, 0); + if (ret) + return ret; + } + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, tmp); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *val) +{ + unsigned int tmp; + int ret; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (val) { + if (tmp & 1) + *val = (tmp + 1) / 2; + else + *val = -(tmp / 2); + } + + return 0; +} + +/** + * coda_h264_sps_fixup - fixes frame cropping values in h.264 SPS + * @ctx: encoder context + * @width: visible width + * @height: visible height + * @buf: buffer containing h.264 SPS RBSP, starting with NAL header + * @size: modified RBSP size return value + * @max_size: available size in buf + * + * Rewrites the frame cropping values in an h.264 SPS RBSP correctly for the + * given visible width and height. + */ +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size) +{ + int profile_idc; + unsigned int pic_order_cnt_type; + int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1; + int frame_mbs_only_flag, frame_cropping_flag; + int vui_parameters_present_flag; + unsigned int crop_right, crop_bottom; + struct rbsp sps; + int pos; + int ret; + + if (*size < 8 || *size >= max_size) + return -EINVAL; + + sps.buf = buf + 5; /* Skip NAL header */ + sps.size = *size - 5; + + profile_idc = sps.buf[0]; + /* Skip constraint_set[0-5]_flag, reserved_zero_2bits */ + /* Skip level_idc */ + sps.pos = 24; + + /* seq_parameter_set_id */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || + profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || + profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || + profile_idc == 135) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling profile_idc %d not implemented\n", + __func__, profile_idc); + return -EINVAL; + } + + /* log2_max_frame_num_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, &pic_order_cnt_type); + if (ret) + return ret; + + if (pic_order_cnt_type == 0) { + /* log2_max_pic_order_cnt_lsb_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + } else if (pic_order_cnt_type == 1) { + unsigned int i, num_ref_frames_in_pic_order_cnt_cycle; + + /* delta_pic_order_always_zero_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + /* offset_for_non_ref_pic */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + /* offset_for_top_to_bottom_field */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, + &num_ref_frames_in_pic_order_cnt_cycle); + if (ret) + return ret; + for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + /* offset_for_ref_frame */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + } + } + + /* max_num_ref_frames */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + /* gaps_in_frame_num_value_allowed_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + ret = rbsp_read_uev(&sps, &pic_width_in_mbs_minus1); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &pic_height_in_map_units_minus1); + if (ret) + return ret; + frame_mbs_only_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (!frame_mbs_only_flag) { + /* mb_adaptive_frame_field_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + } + /* direct_8x8_inference_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + + /* Mark position of the frame cropping flag */ + pos = sps.pos; + frame_cropping_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (frame_cropping_flag) { + unsigned int crop_left, crop_top; + + ret = rbsp_read_uev(&sps, &crop_left); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_right); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_top); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_bottom); + if (ret) + return ret; + } + vui_parameters_present_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (vui_parameters_present_flag) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling vui_parameters not implemented\n", + __func__); + return -EINVAL; + } + + crop_right = round_up(width, 16) - width; + crop_bottom = round_up(height, 16) - height; + crop_right /= 2; + if (frame_mbs_only_flag) + crop_bottom /= 2; + else + crop_bottom /= 4; + + + sps.size = max_size - 5; + sps.pos = pos; + frame_cropping_flag = 1; + ret = rbsp_write_bit(&sps, frame_cropping_flag); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_left */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_right); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_top */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_bottom); + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 0); /* vui_parameters_present_flag */ + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 1); + if (ret) + return ret; + + *size = 5 + DIV_ROUND_UP(sps.pos, 8); + + return 0; +} diff --git a/drivers/media/platform/chips-media/coda-jpeg.c b/drivers/media/platform/chips-media/coda-jpeg.c new file mode 100644 index 000000000000..a72f4655e5ad --- /dev/null +++ b/drivers/media/platform/chips-media/coda-jpeg.c @@ -0,0 +1,1546 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - JPEG support functions + * + * Copyright (C) 2014 Philipp Zabel, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "coda.h" +#include "trace.h" + +#define SOI_MARKER 0xffd8 +#define APP9_MARKER 0xffe9 +#define DRI_MARKER 0xffdd +#define DQT_MARKER 0xffdb +#define DHT_MARKER 0xffc4 +#define SOF_MARKER 0xffc0 +#define SOS_MARKER 0xffda +#define EOI_MARKER 0xffd9 + +enum { + CODA9_JPEG_FORMAT_420, + CODA9_JPEG_FORMAT_422, + CODA9_JPEG_FORMAT_224, + CODA9_JPEG_FORMAT_444, + CODA9_JPEG_FORMAT_400, +}; + +struct coda_huff_tab { + u8 luma_dc[16 + 12]; + u8 chroma_dc[16 + 12]; + u8 luma_ac[16 + 162]; + u8 chroma_ac[16 + 162]; + + /* DC Luma, DC Chroma, AC Luma, AC Chroma */ + s16 min[4 * 16]; + s16 max[4 * 16]; + s8 ptr[4 * 16]; +}; + +#define CODA9_JPEG_ENC_HUFF_DATA_SIZE (256 + 256 + 16 + 16) + +/* + * Typical Huffman tables for 8-bit precision luminance and + * chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3 + */ + +static const unsigned char luma_dc[16 + 12] = { + /* bits */ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* values */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, +}; + +static const unsigned char chroma_dc[16 + 12] = { + /* bits */ + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* values */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, +}; + +static const unsigned char luma_ac[16 + 162 + 2] = { + /* bits */ + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + /* values */ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, /* padded to 32-bit */ +}; + +static const unsigned char chroma_ac[16 + 162 + 2] = { + /* bits */ + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, + /* values */ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, /* padded to 32-bit */ +}; + +/* + * Quantization tables for luminance and chrominance components in + * zig-zag scan order from the Freescale i.MX VPU libraries + */ + +static unsigned char luma_q[64] = { + 0x06, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x05, + 0x05, 0x06, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0b, + 0x08, 0x06, 0x06, 0x08, 0x0b, 0x0c, 0x0a, 0x0a, + 0x0b, 0x0a, 0x0a, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, +}; + +static unsigned char chroma_q[64] = { + 0x07, 0x07, 0x07, 0x0d, 0x0c, 0x0d, 0x18, 0x10, + 0x10, 0x18, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, + 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, +}; + +static const unsigned char width_align[] = { + [CODA9_JPEG_FORMAT_420] = 16, + [CODA9_JPEG_FORMAT_422] = 16, + [CODA9_JPEG_FORMAT_224] = 8, + [CODA9_JPEG_FORMAT_444] = 8, + [CODA9_JPEG_FORMAT_400] = 8, +}; + +static const unsigned char height_align[] = { + [CODA9_JPEG_FORMAT_420] = 16, + [CODA9_JPEG_FORMAT_422] = 8, + [CODA9_JPEG_FORMAT_224] = 16, + [CODA9_JPEG_FORMAT_444] = 8, + [CODA9_JPEG_FORMAT_400] = 8, +}; + +static int coda9_jpeg_chroma_format(u32 pixfmt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + return CODA9_JPEG_FORMAT_420; + case V4L2_PIX_FMT_YUV422P: + return CODA9_JPEG_FORMAT_422; + case V4L2_PIX_FMT_YUV444: + return CODA9_JPEG_FORMAT_444; + case V4L2_PIX_FMT_GREY: + return CODA9_JPEG_FORMAT_400; + } + return -EINVAL; +} + +struct coda_memcpy_desc { + int offset; + const void *src; + size_t len; +}; + +static void coda_memcpy_parabuf(void *parabuf, + const struct coda_memcpy_desc *desc) +{ + u32 *dst = parabuf + desc->offset; + const u32 *src = desc->src; + int len = desc->len / 4; + int i; + + for (i = 0; i < len; i += 2) { + dst[i + 1] = swab32(src[i]); + dst[i] = swab32(src[i + 1]); + } +} + +int coda_jpeg_write_tables(struct coda_ctx *ctx) +{ + int i; + static const struct coda_memcpy_desc huff[8] = { + { 0, luma_dc, sizeof(luma_dc) }, + { 32, luma_ac, sizeof(luma_ac) }, + { 216, chroma_dc, sizeof(chroma_dc) }, + { 248, chroma_ac, sizeof(chroma_ac) }, + }; + struct coda_memcpy_desc qmat[3] = { + { 512, ctx->params.jpeg_qmat_tab[0], 64 }, + { 576, ctx->params.jpeg_qmat_tab[1], 64 }, + { 640, ctx->params.jpeg_qmat_tab[1], 64 }, + }; + + /* Write huffman tables to parameter memory */ + for (i = 0; i < ARRAY_SIZE(huff); i++) + coda_memcpy_parabuf(ctx->parabuf.vaddr, huff + i); + + /* Write Q-matrix to parameter memory */ + for (i = 0; i < ARRAY_SIZE(qmat); i++) + coda_memcpy_parabuf(ctx->parabuf.vaddr, qmat + i); + + return 0; +} + +bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + void *vaddr = vb2_plane_vaddr(vb, 0); + u16 soi, eoi; + int len, i; + + soi = be16_to_cpup((__be16 *)vaddr); + if (soi != SOI_MARKER) + return false; + + len = vb2_get_plane_payload(vb, 0); + vaddr += len - 2; + for (i = 0; i < 32; i++) { + eoi = be16_to_cpup((__be16 *)(vaddr - i)); + if (eoi == EOI_MARKER) { + if (i > 0) + vb2_set_plane_payload(vb, 0, len - i); + return true; + } + } + + return false; +} + +static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num); + +int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + struct coda_dev *dev = ctx->dev; + u8 *buf = vb2_plane_vaddr(vb, 0); + size_t len = vb2_get_plane_payload(vb, 0); + struct v4l2_jpeg_scan_header scan_header; + struct v4l2_jpeg_reference quantization_tables[4] = { }; + struct v4l2_jpeg_reference huffman_tables[4] = { }; + struct v4l2_jpeg_header header = { + .scan = &scan_header, + .quantization_tables = quantization_tables, + .huffman_tables = huffman_tables, + }; + struct coda_q_data *q_data_src; + struct coda_huff_tab *huff_tab; + int i, j, ret; + + ret = v4l2_jpeg_parse_header(buf, len, &header); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to parse header\n"); + return ret; + } + + ctx->params.jpeg_restart_interval = header.restart_interval; + + /* check frame header */ + if (header.frame.height > ctx->codec->max_h || + header.frame.width > ctx->codec->max_w) { + v4l2_err(&dev->v4l2_dev, "invalid dimensions: %dx%d\n", + header.frame.width, header.frame.height); + return -EINVAL; + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (header.frame.height != q_data_src->height || + header.frame.width != q_data_src->width) { + v4l2_err(&dev->v4l2_dev, + "dimensions don't match format: %dx%d\n", + header.frame.width, header.frame.height); + return -EINVAL; + } + + if (header.frame.num_components != 3) { + v4l2_err(&dev->v4l2_dev, + "unsupported number of components: %d\n", + header.frame.num_components); + return -EINVAL; + } + + /* install quantization tables */ + if (quantization_tables[3].start) { + v4l2_err(&dev->v4l2_dev, + "only 3 quantization tables supported\n"); + return -EINVAL; + } + for (i = 0; i < 3; i++) { + if (!quantization_tables[i].start) + continue; + if (quantization_tables[i].length != 64) { + v4l2_err(&dev->v4l2_dev, + "only 8-bit quantization tables supported\n"); + continue; + } + if (!ctx->params.jpeg_qmat_tab[i]) { + ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[i]) + return -ENOMEM; + } + memcpy(ctx->params.jpeg_qmat_tab[i], + quantization_tables[i].start, 64); + } + + /* install Huffman tables */ + for (i = 0; i < 4; i++) { + if (!huffman_tables[i].start) { + v4l2_err(&dev->v4l2_dev, "missing Huffman table\n"); + return -EINVAL; + } + /* AC tables should be between 17 -> 178, DC between 17 -> 28 */ + if (huffman_tables[i].length < 17 || + huffman_tables[i].length > 178 || + ((i & 2) == 0 && huffman_tables[i].length > 28)) { + v4l2_err(&dev->v4l2_dev, + "invalid Huffman table %d length: %zu\n", + i, huffman_tables[i].length); + return -EINVAL; + } + } + huff_tab = ctx->params.jpeg_huff_tab; + if (!huff_tab) { + huff_tab = kzalloc(sizeof(struct coda_huff_tab), GFP_KERNEL); + if (!huff_tab) + return -ENOMEM; + ctx->params.jpeg_huff_tab = huff_tab; + } + + memset(huff_tab, 0, sizeof(*huff_tab)); + memcpy(huff_tab->luma_dc, huffman_tables[0].start, huffman_tables[0].length); + memcpy(huff_tab->chroma_dc, huffman_tables[1].start, huffman_tables[1].length); + memcpy(huff_tab->luma_ac, huffman_tables[2].start, huffman_tables[2].length); + memcpy(huff_tab->chroma_ac, huffman_tables[3].start, huffman_tables[3].length); + + /* check scan header */ + for (i = 0; i < scan_header.num_components; i++) { + struct v4l2_jpeg_scan_component_spec *scan_component; + + scan_component = &scan_header.component[i]; + for (j = 0; j < header.frame.num_components; j++) { + if (header.frame.component[j].component_identifier == + scan_component->component_selector) + break; + } + if (j == header.frame.num_components) + continue; + + ctx->params.jpeg_huff_dc_index[j] = + scan_component->dc_entropy_coding_table_selector; + ctx->params.jpeg_huff_ac_index[j] = + scan_component->ac_entropy_coding_table_selector; + } + + /* Generate Huffman table information */ + for (i = 0; i < 4; i++) + coda9_jpeg_gen_dec_huff_tab(ctx, i); + + /* start of entropy coded segment */ + ctx->jpeg_ecs_offset = header.ecs_offset; + + switch (header.frame.subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + ctx->params.jpeg_chroma_subsampling = header.frame.subsampling; + break; + default: + v4l2_err(&dev->v4l2_dev, "chroma subsampling not supported: %d", + header.frame.subsampling); + return -EINVAL; + } + + return 0; +} + +static inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits, + int num_values) +{ + s8 *values = (s8 *)(bits + 16); + int huff_length, i; + + for (huff_length = 0, i = 0; i < 16; i++) + huff_length += bits[i]; + for (i = huff_length; i < num_values; i++) + values[i] = -1; + for (i = 0; i < num_values; i++) + coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA); +} + +static int coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx) +{ + struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; + struct coda_dev *dev = ctx->dev; + s16 *huff_min = huff_tab->min; + s16 *huff_max = huff_tab->max; + s8 *huff_ptr = huff_tab->ptr; + int i; + + /* MIN Tables */ + coda_write(dev, 0x003, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_min[i], CODA9_REG_JPEG_HUFF_DATA); + + /* MAX Tables */ + coda_write(dev, 0x403, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x440, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_max[i], CODA9_REG_JPEG_HUFF_DATA); + + /* PTR Tables */ + coda_write(dev, 0x803, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x880, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_ptr[i], CODA9_REG_JPEG_HUFF_DATA); + + /* VAL Tables: DC Luma, DC Chroma, AC Luma, AC Chroma */ + coda_write(dev, 0xc03, CODA9_REG_JPEG_HUFF_CTRL); + coda9_jpeg_write_huff_values(dev, huff_tab->luma_dc, 12); + coda9_jpeg_write_huff_values(dev, huff_tab->chroma_dc, 12); + coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162); + coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162); + coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL); + return 0; +} + +static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev, + u8 *qmat, int index) +{ + int i; + + coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); + for (i = 0; i < 64; i++) + coda_write(dev, qmat[i], CODA9_REG_JPEG_QMAT_DATA); + coda_write(dev, 0, CODA9_REG_JPEG_QMAT_CTRL); +} + +static void coda9_jpeg_qmat_setup(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int *qmat_index = ctx->params.jpeg_qmat_index; + u8 **qmat_tab = ctx->params.jpeg_qmat_tab; + + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[0]], 0x00); + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[1]], 0x40); + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[2]], 0x80); +} + +static void coda9_jpeg_dec_bbc_gbu_setup(struct coda_ctx *ctx, + struct vb2_buffer *buf, u32 ecs_offset) +{ + struct coda_dev *dev = ctx->dev; + int page_ptr, word_ptr, bit_ptr; + u32 bbc_base_addr, end_addr; + int bbc_cur_pos; + int ret, val; + + bbc_base_addr = vb2_dma_contig_plane_dma_addr(buf, 0); + end_addr = bbc_base_addr + vb2_get_plane_payload(buf, 0); + + page_ptr = ecs_offset / 256; + word_ptr = (ecs_offset % 256) / 4; + if (page_ptr & 1) + word_ptr += 64; + bit_ptr = (ecs_offset % 4) * 8; + if (word_ptr & 1) + bit_ptr += 32; + word_ptr &= ~0x1; + + coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_WR_PTR); + coda_write(dev, bbc_base_addr, CODA9_REG_JPEG_BBC_BAS_ADDR); + + /* Leave 3 256-byte page margin to avoid a BBC interrupt */ + coda_write(dev, end_addr + 256 * 3 + 256, CODA9_REG_JPEG_BBC_END_ADDR); + val = DIV_ROUND_UP(vb2_plane_size(buf, 0), 256) + 3; + coda_write(dev, BIT(31) | val, CODA9_REG_JPEG_BBC_STRM_CTRL); + + bbc_cur_pos = page_ptr; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), + CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); + do { + ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); + } while (ret == 1); + + bbc_cur_pos++; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), + CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); + do { + ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); + } while (ret == 1); + + bbc_cur_pos++; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); + + coda_write(dev, 0, CODA9_REG_JPEG_GBU_TT_CNT); + coda_write(dev, word_ptr, CODA9_REG_JPEG_GBU_WD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); + coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); + if (page_ptr & 1) { + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBHR); + } else { + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); + } + coda_write(dev, 4, CODA9_REG_JPEG_GBU_CTRL); + coda_write(dev, bit_ptr, CODA9_REG_JPEG_GBU_FF_RPTR); + coda_write(dev, 3, CODA9_REG_JPEG_GBU_CTRL); +} + +static const int bus_req_num[] = { + [CODA9_JPEG_FORMAT_420] = 2, + [CODA9_JPEG_FORMAT_422] = 3, + [CODA9_JPEG_FORMAT_224] = 3, + [CODA9_JPEG_FORMAT_444] = 4, + [CODA9_JPEG_FORMAT_400] = 4, +}; + +#define MCU_INFO(mcu_block_num, comp_num, comp0_info, comp1_info, comp2_info) \ + (((mcu_block_num) << CODA9_JPEG_MCU_BLOCK_NUM_OFFSET) | \ + ((comp_num) << CODA9_JPEG_COMP_NUM_OFFSET) | \ + ((comp0_info) << CODA9_JPEG_COMP0_INFO_OFFSET) | \ + ((comp1_info) << CODA9_JPEG_COMP1_INFO_OFFSET) | \ + ((comp2_info) << CODA9_JPEG_COMP2_INFO_OFFSET)) + +static const u32 mcu_info[] = { + [CODA9_JPEG_FORMAT_420] = MCU_INFO(6, 3, 10, 5, 5), + [CODA9_JPEG_FORMAT_422] = MCU_INFO(4, 3, 9, 5, 5), + [CODA9_JPEG_FORMAT_224] = MCU_INFO(4, 3, 6, 5, 5), + [CODA9_JPEG_FORMAT_444] = MCU_INFO(3, 3, 5, 5, 5), + [CODA9_JPEG_FORMAT_400] = MCU_INFO(1, 1, 5, 0, 0), +}; + +/* + * Convert Huffman table specifcations to tables of codes and code lengths. + * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] + * + * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ +static int coda9_jpeg_gen_enc_huff_tab(struct coda_ctx *ctx, int tab_num, + int *ehufsi, int *ehufco) +{ + int i, j, k, lastk, si, code, maxsymbol; + const u8 *bits, *huffval; + struct { + int size[256]; + int code[256]; + } *huff; + static const unsigned char *huff_tabs[4] = { + luma_dc, luma_ac, chroma_dc, chroma_ac, + }; + int ret = -EINVAL; + + huff = kzalloc(sizeof(*huff), GFP_KERNEL); + if (!huff) + return -ENOMEM; + + bits = huff_tabs[tab_num]; + huffval = huff_tabs[tab_num] + 16; + + maxsymbol = tab_num & 1 ? 256 : 16; + + /* Figure C.1 - Generation of table of Huffman code sizes */ + k = 0; + for (i = 1; i <= 16; i++) { + j = bits[i - 1]; + if (k + j > maxsymbol) + goto out; + while (j--) + huff->size[k++] = i; + } + lastk = k; + + /* Figure C.2 - Generation of table of Huffman codes */ + k = 0; + code = 0; + si = huff->size[0]; + while (k < lastk) { + while (huff->size[k] == si) { + huff->code[k++] = code; + code++; + } + if (code >= (1 << si)) + goto out; + code <<= 1; + si++; + } + + /* Figure C.3 - Ordering procedure for encoding procedure code tables */ + for (k = 0; k < lastk; k++) { + i = huffval[k]; + if (i >= maxsymbol || ehufsi[i]) + goto out; + ehufco[i] = huff->code[k]; + ehufsi[i] = huff->size[k]; + } + + ret = 0; +out: + kfree(huff); + return ret; +} + +#define DC_TABLE_INDEX0 0 +#define AC_TABLE_INDEX0 1 +#define DC_TABLE_INDEX1 2 +#define AC_TABLE_INDEX1 3 + +static u8 *coda9_jpeg_get_huff_bits(struct coda_ctx *ctx, int tab_num) +{ + struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; + + if (!huff_tab) + return NULL; + + switch (tab_num) { + case DC_TABLE_INDEX0: return huff_tab->luma_dc; + case AC_TABLE_INDEX0: return huff_tab->luma_ac; + case DC_TABLE_INDEX1: return huff_tab->chroma_dc; + case AC_TABLE_INDEX1: return huff_tab->chroma_ac; + } + + return NULL; +} + +static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num) +{ + int ptr_cnt = 0, huff_code = 0, zero_flag = 0, data_flag = 0; + u8 *huff_bits; + s16 *huff_max; + s16 *huff_min; + s8 *huff_ptr; + int ofs; + int i; + + huff_bits = coda9_jpeg_get_huff_bits(ctx, tab_num); + if (!huff_bits) + return -EINVAL; + + /* DC/AC Luma, DC/AC Chroma -> DC Luma/Chroma, AC Luma/Chroma */ + ofs = ((tab_num & 1) << 1) | ((tab_num >> 1) & 1); + ofs *= 16; + + huff_ptr = ctx->params.jpeg_huff_tab->ptr + ofs; + huff_max = ctx->params.jpeg_huff_tab->max + ofs; + huff_min = ctx->params.jpeg_huff_tab->min + ofs; + + for (i = 0; i < 16; i++) { + if (huff_bits[i]) { + huff_ptr[i] = ptr_cnt; + ptr_cnt += huff_bits[i]; + huff_min[i] = huff_code; + huff_max[i] = huff_code + (huff_bits[i] - 1); + data_flag = 1; + zero_flag = 0; + } else { + huff_ptr[i] = -1; + huff_min[i] = -1; + huff_max[i] = -1; + zero_flag = 1; + } + + if (data_flag == 1) { + if (zero_flag == 1) + huff_code <<= 1; + else + huff_code = (huff_max[i] + 1) << 1; + } + } + + return 0; +} + +static int coda9_jpeg_load_huff_tab(struct coda_ctx *ctx) +{ + struct { + int size[4][256]; + int code[4][256]; + } *huff; + u32 *huff_data; + int i, j; + int ret; + + huff = kzalloc(sizeof(*huff), GFP_KERNEL); + if (!huff) + return -ENOMEM; + + /* Generate all four (luma/chroma DC/AC) code/size lookup tables */ + for (i = 0; i < 4; i++) { + ret = coda9_jpeg_gen_enc_huff_tab(ctx, i, huff->size[i], + huff->code[i]); + if (ret) + goto out; + } + + if (!ctx->params.jpeg_huff_data) { + ctx->params.jpeg_huff_data = + kzalloc(sizeof(u32) * CODA9_JPEG_ENC_HUFF_DATA_SIZE, + GFP_KERNEL); + if (!ctx->params.jpeg_huff_data) { + ret = -ENOMEM; + goto out; + } + } + huff_data = ctx->params.jpeg_huff_data; + + for (j = 0; j < 4; j++) { + /* Store Huffman lookup tables in AC0, AC1, DC0, DC1 order */ + int t = (j == 0) ? AC_TABLE_INDEX0 : + (j == 1) ? AC_TABLE_INDEX1 : + (j == 2) ? DC_TABLE_INDEX0 : + DC_TABLE_INDEX1; + /* DC tables only have 16 entries */ + int len = (j < 2) ? 256 : 16; + + for (i = 0; i < len; i++) { + if (huff->size[t][i] == 0 && huff->code[t][i] == 0) + *(huff_data++) = 0; + else + *(huff_data++) = + ((huff->size[t][i] - 1) << 16) | + huff->code[t][i]; + } + } + + ret = 0; +out: + kfree(huff); + return ret; +} + +static void coda9_jpeg_write_huff_tab(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 *huff_data = ctx->params.jpeg_huff_data; + int i; + + /* Write Huffman size/code lookup tables in AC0, AC1, DC0, DC1 order */ + coda_write(dev, 0x3, CODA9_REG_JPEG_HUFF_CTRL); + for (i = 0; i < CODA9_JPEG_ENC_HUFF_DATA_SIZE; i++) + coda_write(dev, *(huff_data++), CODA9_REG_JPEG_HUFF_DATA); + coda_write(dev, 0x0, CODA9_REG_JPEG_HUFF_CTRL); +} + +static inline void coda9_jpeg_write_qmat_quotients(struct coda_dev *dev, + u8 *qmat, int index) +{ + int i; + + coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); + for (i = 0; i < 64; i++) + coda_write(dev, 0x80000 / qmat[i], CODA9_REG_JPEG_QMAT_DATA); + coda_write(dev, index, CODA9_REG_JPEG_QMAT_CTRL); +} + +static void coda9_jpeg_load_qmat_tab(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u8 *luma_tab; + u8 *chroma_tab; + + luma_tab = ctx->params.jpeg_qmat_tab[0]; + if (!luma_tab) + luma_tab = luma_q; + + chroma_tab = ctx->params.jpeg_qmat_tab[1]; + if (!chroma_tab) + chroma_tab = chroma_q; + + coda9_jpeg_write_qmat_quotients(dev, luma_tab, 0x00); + coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x40); + coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x80); +} + +struct coda_jpeg_stream { + u8 *curr; + u8 *end; +}; + +static inline int coda_jpeg_put_byte(u8 byte, struct coda_jpeg_stream *stream) +{ + if (stream->curr >= stream->end) + return -EINVAL; + + *stream->curr++ = byte; + + return 0; +} + +static inline int coda_jpeg_put_word(u16 word, struct coda_jpeg_stream *stream) +{ + if (stream->curr + sizeof(__be16) > stream->end) + return -EINVAL; + + put_unaligned_be16(word, stream->curr); + stream->curr += sizeof(__be16); + + return 0; +} + +static int coda_jpeg_put_table(u16 marker, u8 index, const u8 *table, + size_t len, struct coda_jpeg_stream *stream) +{ + int i, ret; + + ret = coda_jpeg_put_word(marker, stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(3 + len, stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(index, stream); + for (i = 0; i < len && ret == 0; i++) + ret = coda_jpeg_put_byte(table[i], stream); + + return ret; +} + +static int coda_jpeg_define_quantization_table(struct coda_ctx *ctx, u8 index, + struct coda_jpeg_stream *stream) +{ + return coda_jpeg_put_table(DQT_MARKER, index, + ctx->params.jpeg_qmat_tab[index], 64, + stream); +} + +static int coda_jpeg_define_huffman_table(u8 index, const u8 *table, size_t len, + struct coda_jpeg_stream *stream) +{ + return coda_jpeg_put_table(DHT_MARKER, index, table, len, stream); +} + +static int coda9_jpeg_encode_header(struct coda_ctx *ctx, int len, u8 *buf) +{ + struct coda_jpeg_stream stream = { buf, buf + len }; + struct coda_q_data *q_data_src; + int chroma_format, comp_num; + int i, ret, pad; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); + if (chroma_format < 0) + return 0; + + /* Start Of Image */ + ret = coda_jpeg_put_word(SOI_MARKER, &stream); + if (ret < 0) + return ret; + + /* Define Restart Interval */ + if (ctx->params.jpeg_restart_interval) { + ret = coda_jpeg_put_word(DRI_MARKER, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(4, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(ctx->params.jpeg_restart_interval, + &stream); + if (ret < 0) + return ret; + } + + /* Define Quantization Tables */ + ret = coda_jpeg_define_quantization_table(ctx, 0x00, &stream); + if (ret < 0) + return ret; + if (chroma_format != CODA9_JPEG_FORMAT_400) { + ret = coda_jpeg_define_quantization_table(ctx, 0x01, &stream); + if (ret < 0) + return ret; + } + + /* Define Huffman Tables */ + ret = coda_jpeg_define_huffman_table(0x00, luma_dc, 16 + 12, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_define_huffman_table(0x10, luma_ac, 16 + 162, &stream); + if (ret < 0) + return ret; + if (chroma_format != CODA9_JPEG_FORMAT_400) { + ret = coda_jpeg_define_huffman_table(0x01, chroma_dc, 16 + 12, + &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_define_huffman_table(0x11, chroma_ac, 16 + 162, + &stream); + if (ret < 0) + return ret; + } + + /* Start Of Frame */ + ret = coda_jpeg_put_word(SOF_MARKER, &stream); + if (ret < 0) + return ret; + comp_num = (chroma_format == CODA9_JPEG_FORMAT_400) ? 1 : 3; + ret = coda_jpeg_put_word(8 + comp_num * 3, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(0x08, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(q_data_src->height, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(q_data_src->width, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(comp_num, &stream); + if (ret < 0) + return ret; + for (i = 0; i < comp_num; i++) { + static unsigned char subsampling[5][3] = { + [CODA9_JPEG_FORMAT_420] = { 0x22, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_422] = { 0x21, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_224] = { 0x12, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_444] = { 0x11, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_400] = { 0x11 }, + }; + + /* Component identifier, matches SOS */ + ret = coda_jpeg_put_byte(i + 1, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(subsampling[chroma_format][i], + &stream); + if (ret < 0) + return ret; + /* Chroma table index */ + ret = coda_jpeg_put_byte((i == 0) ? 0 : 1, &stream); + if (ret < 0) + return ret; + } + + /* Pad to multiple of 8 bytes */ + pad = (stream.curr - buf) % 8; + if (pad) { + pad = 8 - pad; + while (pad--) { + ret = coda_jpeg_put_byte(0x00, &stream); + if (ret < 0) + return ret; + } + } + + return stream.curr - buf; +} + +/* + * Scale quantization table using nonlinear scaling factor + * u8 qtab[64], scale [50,190] + */ +static void coda_scale_quant_table(u8 *q_tab, int scale) +{ + unsigned int temp; + int i; + + for (i = 0; i < 64; i++) { + temp = DIV_ROUND_CLOSEST((unsigned int)q_tab[i] * scale, 100); + if (temp <= 0) + temp = 1; + if (temp > 255) + temp = 255; + q_tab[i] = (unsigned char)temp; + } +} + +void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality) +{ + unsigned int scale; + + ctx->params.jpeg_quality = quality; + + /* Clip quality setting to [5,100] interval */ + if (quality > 100) + quality = 100; + if (quality < 5) + quality = 5; + + /* + * Non-linear scaling factor: + * [5,50] -> [1000..100], [51,100] -> [98..0] + */ + if (quality < 50) + scale = 5000 / quality; + else + scale = 200 - 2 * quality; + + if (ctx->params.jpeg_qmat_tab[0]) { + memcpy(ctx->params.jpeg_qmat_tab[0], luma_q, 64); + coda_scale_quant_table(ctx->params.jpeg_qmat_tab[0], scale); + } + if (ctx->params.jpeg_qmat_tab[1]) { + memcpy(ctx->params.jpeg_qmat_tab[1], chroma_q, 64); + coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale); + } +} + +/* + * Encoder context operations + */ + +static int coda9_jpeg_start_encoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + ret = coda9_jpeg_load_huff_tab(ctx); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n"); + return ret; + } + if (!ctx->params.jpeg_qmat_tab[0]) + ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[1]) + ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); + coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); + + return 0; +} + +static int coda9_jpeg_prepare_encode(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 start_addr, end_addr; + u16 aligned_width, aligned_height; + bool chroma_interleave; + int chroma_format; + int header_len; + int ret; + ktime_t timeout; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) + vb2_set_plane_payload(&src_buf->vb2_buf, 0, + vb2_plane_size(&src_buf->vb2_buf, 0)); + + src_buf->sequence = ctx->osequence; + dst_buf->sequence = ctx->osequence; + ctx->osequence++; + + src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; + + coda_set_gdi_regs(ctx); + + start_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + end_addr = start_addr + vb2_plane_size(&dst_buf->vb2_buf, 0); + + chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); + if (chroma_format < 0) + return chroma_format; + + /* Round image dimensions to multiple of MCU size */ + aligned_width = round_up(q_data_src->width, width_align[chroma_format]); + aligned_height = round_up(q_data_src->height, + height_align[chroma_format]); + if (aligned_width != q_data_src->bytesperline) { + v4l2_err(&dev->v4l2_dev, "wrong stride: %d instead of %d\n", + aligned_width, q_data_src->bytesperline); + } + + header_len = + coda9_jpeg_encode_header(ctx, + vb2_plane_size(&dst_buf->vb2_buf, 0), + vb2_plane_vaddr(&dst_buf->vb2_buf, 0)); + if (header_len < 0) + return header_len; + + coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_BAS_ADDR); + coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_END_ADDR); + coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_WR_PTR); + coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_RD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_CUR_POS); + /* 64 words per 256-byte page */ + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, start_addr, CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_INT_ADDR); + + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BT_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_WD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); + coda_write(dev, BIT(31) | ((end_addr - start_addr - header_len) / 256), + CODA9_REG_JPEG_BBC_STRM_CTRL); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_CTRL); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_FF_RPTR); + coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); + + chroma_interleave = (q_data_src->fourcc == V4L2_PIX_FMT_NV12); + coda_write(dev, CODA9_JPEG_PIC_CTRL_TC_DIRECTION | + CODA9_JPEG_PIC_CTRL_ENCODER_EN, CODA9_REG_JPEG_PIC_CTRL); + coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); + coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA9_REG_JPEG_RST_INTVAL); + coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); + + coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); + + coda9_jpeg_write_huff_tab(ctx); + coda9_jpeg_load_qmat_tab(ctx); + + if (ctx->params.rot_mode & CODA_ROT_90) { + aligned_width = aligned_height; + aligned_height = q_data_src->bytesperline; + if (chroma_format == CODA9_JPEG_FORMAT_422) + chroma_format = CODA9_JPEG_FORMAT_224; + else if (chroma_format == CODA9_JPEG_FORMAT_224) + chroma_format = CODA9_JPEG_FORMAT_422; + } + /* These need to be multiples of MCU size */ + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_REG_JPEG_PIC_SIZE); + coda_write(dev, ctx->params.rot_mode ? + (CODA_ROT_MIR_ENABLE | ctx->params.rot_mode) : 0, + CODA9_REG_JPEG_ROT_INFO); + + coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); + + coda_write(dev, 1, CODA9_GDI_CONTROL); + timeout = ktime_add_us(ktime_get(), 100000); + do { + ret = coda_read(dev, CODA9_GDI_STATUS); + if (ktime_compare(ktime_get(), timeout) > 0) { + v4l2_err(&dev->v4l2_dev, "timeout waiting for GDI\n"); + return -ETIMEDOUT; + } + } while (!ret); + + coda_write(dev, (chroma_format << 17) | (chroma_interleave << 16) | + q_data_src->bytesperline, CODA9_GDI_INFO_CONTROL); + /* The content of this register seems to be irrelevant: */ + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_GDI_INFO_PIC_SIZE); + + coda_write_base(ctx, q_data_src, src_buf, CODA9_GDI_INFO_BASE_Y); + + coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); + coda_write(dev, 0, CODA9_GDI_CONTROL); + coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); + + coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + + trace_coda_jpeg_run(ctx, src_buf); + + coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); + + return 0; +} + +static void coda9_jpeg_finish_encode(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr, start_ptr; + u32 err_mb; + + if (ctx->aborting) { + coda_write(ctx->dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + return; + } + + /* + * Lock to make sure that an encoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + trace_coda_jpeg_done(ctx, dst_buf); + + /* + * Set plane payload to the number of bytes written out + * by the JPEG processing unit + */ + start_ptr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) + coda_dbg(1, ctx, "ERRMB: 0x%x\n", err_mb); + + coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_DONE); + mutex_unlock(&ctx->wakeup_mutex); + + coda_dbg(1, ctx, "job finished: encoded frame (%u)%s\n", + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); + + /* + * Reset JPEG processing unit after each encode run to work + * around hangups when switching context between encoder and + * decoder. + */ + coda_hw_reset(ctx); +} + +static void coda9_jpeg_encode_timeout(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 end_addr, wr_ptr; + + /* Handle missing BBC overflow interrupt via timeout */ + end_addr = coda_read(dev, CODA9_REG_JPEG_BBC_END_ADDR); + wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); + if (wr_ptr >= end_addr - 256) { + v4l2_err(&dev->v4l2_dev, "JPEG too large for capture buffer\n"); + coda9_jpeg_finish_encode(ctx); + return; + } + + coda_hw_reset(ctx); +} + +static void coda9_jpeg_release(struct coda_ctx *ctx) +{ + int i; + + if (ctx->params.jpeg_qmat_tab[0] == luma_q) + ctx->params.jpeg_qmat_tab[0] = NULL; + if (ctx->params.jpeg_qmat_tab[1] == chroma_q) + ctx->params.jpeg_qmat_tab[1] = NULL; + for (i = 0; i < 3; i++) + kfree(ctx->params.jpeg_qmat_tab[i]); + kfree(ctx->params.jpeg_huff_data); + kfree(ctx->params.jpeg_huff_tab); +} + +const struct coda_context_ops coda9_jpeg_encode_ops = { + .queue_init = coda_encoder_queue_init, + .start_streaming = coda9_jpeg_start_encoding, + .prepare_run = coda9_jpeg_prepare_encode, + .finish_run = coda9_jpeg_finish_encode, + .run_timeout = coda9_jpeg_encode_timeout, + .release = coda9_jpeg_release, +}; + +/* + * Decoder context operations + */ + +static int coda9_jpeg_start_decoding(struct coda_ctx *ctx) +{ + ctx->params.jpeg_qmat_index[0] = 0; + ctx->params.jpeg_qmat_index[1] = 1; + ctx->params.jpeg_qmat_index[2] = 1; + ctx->params.jpeg_qmat_tab[0] = luma_q; + ctx->params.jpeg_qmat_tab[1] = chroma_q; + /* nothing more to do here */ + + /* TODO: we could already scan the first header to get the chroma + * format. + */ + + return 0; +} + +static int coda9_jpeg_prepare_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int aligned_width, aligned_height; + int chroma_format; + int ret; + u32 val, dst_fourcc; + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + int chroma_interleave; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) + vb2_set_plane_payload(&src_buf->vb2_buf, 0, + vb2_plane_size(&src_buf->vb2_buf, 0)); + + chroma_format = coda9_jpeg_chroma_format(q_data_dst->fourcc); + if (chroma_format < 0) { + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return chroma_format; + } + + ret = coda_jpeg_decode_header(ctx, &src_buf->vb2_buf); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to decode JPEG header: %d\n", + ret); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return ret; + } + + /* Round image dimensions to multiple of MCU size */ + aligned_width = round_up(q_data_src->width, width_align[chroma_format]); + aligned_height = round_up(q_data_src->height, height_align[chroma_format]); + if (aligned_width != q_data_dst->bytesperline) { + v4l2_err(&dev->v4l2_dev, "stride mismatch: %d != %d\n", + aligned_width, q_data_dst->bytesperline); + } + + coda_set_gdi_regs(ctx); + + val = ctx->params.jpeg_huff_ac_index[0] << 12 | + ctx->params.jpeg_huff_ac_index[1] << 11 | + ctx->params.jpeg_huff_ac_index[2] << 10 | + ctx->params.jpeg_huff_dc_index[0] << 9 | + ctx->params.jpeg_huff_dc_index[1] << 8 | + ctx->params.jpeg_huff_dc_index[2] << 7; + if (ctx->params.jpeg_huff_tab) + val |= CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN; + coda_write(dev, val, CODA9_REG_JPEG_PIC_CTRL); + + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_REG_JPEG_PIC_SIZE); + + chroma_interleave = (dst_fourcc == V4L2_PIX_FMT_NV12); + coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); + coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); + coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); + coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); + coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA9_REG_JPEG_RST_INTVAL); + + if (ctx->params.jpeg_huff_tab) { + ret = coda9_jpeg_dec_huff_setup(ctx); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to set up Huffman tables: %d\n", ret); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return ret; + } + } + + coda9_jpeg_qmat_setup(ctx); + + coda9_jpeg_dec_bbc_gbu_setup(ctx, &src_buf->vb2_buf, + ctx->jpeg_ecs_offset); + + coda_write(dev, 0, CODA9_REG_JPEG_RST_INDEX); + coda_write(dev, 0, CODA9_REG_JPEG_RST_COUNT); + + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_Y); + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CB); + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CR); + + coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); + + coda_write(dev, 1, CODA9_GDI_CONTROL); + do { + ret = coda_read(dev, CODA9_GDI_STATUS); + } while (!ret); + + val = (chroma_format << 17) | (chroma_interleave << 16) | + q_data_dst->bytesperline; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + val |= 3 << 20; + coda_write(dev, val, CODA9_GDI_INFO_CONTROL); + + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_GDI_INFO_PIC_SIZE); + + coda_write_base(ctx, q_data_dst, dst_buf, CODA9_GDI_INFO_BASE_Y); + + coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); + coda_write(dev, 0, CODA9_GDI_CONTROL); + coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); + + trace_coda_jpeg_run(ctx, src_buf); + + coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); + + return 0; +} + +static void coda9_jpeg_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *dst_buf, *src_buf; + struct coda_q_data *q_data_dst; + u32 err_mb; + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) + v4l2_err(&dev->v4l2_dev, "ERRMB: 0x%x\n", err_mb); + + coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + + /* + * Lock to make sure that a decoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->osequence++; + + trace_coda_jpeg_done(ctx, dst_buf); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_DONE); + + mutex_unlock(&ctx->wakeup_mutex); + + coda_dbg(1, ctx, "job finished: decoded frame (%u)%s\n", + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); + + /* + * Reset JPEG processing unit after each decode run to work + * around hangups when switching context between encoder and + * decoder. + */ + coda_hw_reset(ctx); +} + +const struct coda_context_ops coda9_jpeg_decode_ops = { + .queue_init = coda_encoder_queue_init, /* non-bitstream operation */ + .start_streaming = coda9_jpeg_start_decoding, + .prepare_run = coda9_jpeg_prepare_decode, + .finish_run = coda9_jpeg_finish_decode, + .release = coda9_jpeg_release, +}; + +irqreturn_t coda9_jpeg_irq_handler(int irq, void *data) +{ + struct coda_dev *dev = data; + struct coda_ctx *ctx; + int status; + int err_mb; + + status = coda_read(dev, CODA9_REG_JPEG_PIC_STATUS); + if (status == 0) + return IRQ_HANDLED; + coda_write(dev, status, CODA9_REG_JPEG_PIC_STATUS); + + if (status & CODA9_JPEG_STATUS_OVERFLOW) + v4l2_err(&dev->v4l2_dev, "JPEG overflow\n"); + + if (status & CODA9_JPEG_STATUS_BBC_INT) + v4l2_err(&dev->v4l2_dev, "JPEG BBC interrupt\n"); + + if (status & CODA9_JPEG_STATUS_ERROR) { + v4l2_err(&dev->v4l2_dev, "JPEG error\n"); + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) { + v4l2_err(&dev->v4l2_dev, + "ERRMB: 0x%x: rst idx %d, mcu pos (%d,%d)\n", + err_mb, err_mb >> 24, (err_mb >> 12) & 0xfff, + err_mb & 0xfff); + } + } + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + mutex_unlock(&dev->coda_mutex); + return IRQ_HANDLED; + } + + complete(&ctx->completion); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/chips-media/coda-mpeg2.c b/drivers/media/platform/chips-media/coda-mpeg2.c new file mode 100644 index 000000000000..6f3f6721d286 --- /dev/null +++ b/drivers/media/platform/chips-media/coda-mpeg2.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-2 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include +#include +#include "coda.h" + +int coda_mpeg2_profile(int profile_idc) +{ + switch (profile_idc) { + case 5: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN; + case 3: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE; + case 1: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH; + default: + return -EINVAL; + } +} + +int coda_mpeg2_level(int level_idc) +{ + switch (level_idc) { + case 10: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW; + case 8: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN; + case 6: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH; + default: + return -EINVAL; + } +} + +/* + * Check if the buffer starts with the MPEG-2 sequence header (with or without + * quantization matrix) and extension header, for example: + * + * 00 00 01 b3 2d 01 e0 34 08 8b a3 81 + * 10 11 11 12 12 12 13 13 13 13 14 14 14 14 14 15 + * 15 15 15 15 15 16 16 16 16 16 16 16 17 17 17 17 + * 17 17 17 17 18 18 18 19 18 18 18 19 1a 1a 1a 1a + * 19 1b 1b 1b 1b 1b 1c 1c 1c 1c 1e 1e 1e 1f 1f 21 + * 00 00 01 b5 14 8a 00 01 00 00 + * + * or: + * + * 00 00 01 b3 08 00 40 15 ff ff e0 28 + * 00 00 01 b5 14 8a 00 01 00 00 + * + * Returns the detected header size in bytes or 0. + */ +u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) +{ + static const u8 sequence_header_start[4] = { 0x00, 0x00, 0x01, 0xb3 }; + static const union { + u8 extension_start[4]; + u8 start_code_prefix[3]; + } u = { { 0x00, 0x00, 0x01, 0xb5 } }; + + if (size < 22 || + memcmp(buf, sequence_header_start, 4) != 0) + return 0; + + if ((size == 22 || + (size >= 25 && memcmp(buf + 22, u.start_code_prefix, 3) == 0)) && + memcmp(buf + 12, u.extension_start, 4) == 0) + return 22; + + if ((size == 86 || + (size > 89 && memcmp(buf + 86, u.start_code_prefix, 3) == 0)) && + memcmp(buf + 76, u.extension_start, 4) == 0) + return 86; + + return 0; +} diff --git a/drivers/media/platform/chips-media/coda-mpeg4.c b/drivers/media/platform/chips-media/coda-mpeg4.c new file mode 100644 index 000000000000..483a4fba1b4f --- /dev/null +++ b/drivers/media/platform/chips-media/coda-mpeg4.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-4 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include +#include + +#include "coda.h" + +int coda_mpeg4_profile(int profile_idc) +{ + switch (profile_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE; + case 15: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE; + case 11: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY; + default: + return -EINVAL; + } +} + +int coda_mpeg4_level(int level_idc) +{ + switch (level_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_0; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_1; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_2; + case 3: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_3; + case 4: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_4; + case 5: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_5; + default: + return -EINVAL; + } +} + +/* + * Check if the buffer starts with the MPEG-4 visual object sequence and visual + * object headers, for example: + * + * 00 00 01 b0 f1 + * 00 00 01 b5 a9 13 00 00 01 00 00 00 01 20 08 + * d4 8d 88 00 f5 04 04 08 14 30 3f + * + * Returns the detected header size in bytes or 0. + */ +u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) +{ + static const u8 vos_start[4] = { 0x00, 0x00, 0x01, 0xb0 }; + static const union { + u8 vo_start[4]; + u8 start_code_prefix[3]; + } u = { { 0x00, 0x00, 0x01, 0xb5 } }; + + if (size < 30 || + memcmp(buf, vos_start, 4) != 0 || + memcmp(buf + 5, u.vo_start, 4) != 0) + return 0; + + if (size == 30 || + (size >= 33 && memcmp(buf + 30, u.start_code_prefix, 3) == 0)) + return 30; + + if (size == 31 || + (size >= 34 && memcmp(buf + 31, u.start_code_prefix, 3) == 0)) + return 31; + + if (size == 32 || + (size >= 35 && memcmp(buf + 32, u.start_code_prefix, 3) == 0)) + return 32; + + return 0; +} diff --git a/drivers/media/platform/chips-media/coda.h b/drivers/media/platform/chips-media/coda.h new file mode 100644 index 000000000000..dcf35641c603 --- /dev/null +++ b/drivers/media/platform/chips-media/coda.h @@ -0,0 +1,396 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix + */ + +#ifndef __CODA_H__ +#define __CODA_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "coda_regs.h" + +#define CODA_MAX_FRAMEBUFFERS 19 +#define FMO_SLICE_SAVE_BUF_SIZE (32) + +/* + * This control allows applications to read the per-stream + * (i.e. per-context) Macroblocks Error Count. This value + * is CODA specific. + */ +#define V4L2_CID_CODA_MB_ERR_CNT (V4L2_CID_USER_CODA_BASE + 0) + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +enum coda_inst_type { + CODA_INST_ENCODER, + CODA_INST_DECODER, +}; + +enum coda_product { + CODA_DX6 = 0xf001, + CODA_HX4 = 0xf00a, + CODA_7541 = 0xf012, + CODA_960 = 0xf020, +}; + +struct coda_video_device; + +struct coda_devtype { + char *firmware[3]; + enum coda_product product; + const struct coda_codec *codecs; + unsigned int num_codecs; + const struct coda_video_device **vdevs; + unsigned int num_vdevs; + size_t workbuf_size; + size_t tempbuf_size; + size_t iram_size; +}; + +struct coda_aux_buf { + void *vaddr; + dma_addr_t paddr; + u32 size; + struct debugfs_blob_wrapper blob; + struct dentry *dentry; +}; + +struct coda_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd[6]; + struct device *dev; + const struct coda_devtype *devtype; + int firmware; + struct vdoa_data *vdoa; + + void __iomem *regs_base; + struct clk *clk_per; + struct clk *clk_ahb; + struct reset_control *rstc; + + struct coda_aux_buf codebuf; + struct coda_aux_buf tempbuf; + struct coda_aux_buf workbuf; + struct gen_pool *iram_pool; + struct coda_aux_buf iram; + + struct mutex dev_mutex; + struct mutex coda_mutex; + struct workqueue_struct *workqueue; + struct v4l2_m2m_dev *m2m_dev; + struct ida ida; + struct dentry *debugfs_root; + struct ratelimit_state mb_err_rs; +}; + +struct coda_codec { + u32 mode; + u32 src_fourcc; + u32 dst_fourcc; + u32 max_w; + u32 max_h; +}; + +struct coda_huff_tab; + +struct coda_params { + u8 rot_mode; + u8 h264_intra_qp; + u8 h264_inter_qp; + u8 h264_min_qp; + u8 h264_max_qp; + u8 h264_disable_deblocking_filter_idc; + s8 h264_slice_alpha_c0_offset_div2; + s8 h264_slice_beta_offset_div2; + bool h264_constrained_intra_pred_flag; + s8 h264_chroma_qp_index_offset; + u8 h264_profile_idc; + u8 h264_level_idc; + u8 mpeg2_profile_idc; + u8 mpeg2_level_idc; + u8 mpeg4_intra_qp; + u8 mpeg4_inter_qp; + u8 gop_size; + int intra_refresh; + enum v4l2_jpeg_chroma_subsampling jpeg_chroma_subsampling; + u8 jpeg_quality; + u8 jpeg_restart_interval; + u8 *jpeg_qmat_tab[3]; + int jpeg_qmat_index[3]; + int jpeg_huff_dc_index[3]; + int jpeg_huff_ac_index[3]; + u32 *jpeg_huff_data; + struct coda_huff_tab *jpeg_huff_tab; + int codec_mode; + int codec_mode_aux; + enum v4l2_mpeg_video_multi_slice_mode slice_mode; + u32 framerate; + u16 bitrate; + u16 vbv_delay; + u32 vbv_size; + u32 slice_max_bits; + u32 slice_max_mb; + bool force_ipicture; + bool gop_size_changed; + bool bitrate_changed; + bool framerate_changed; + bool h264_intra_qp_changed; + bool intra_refresh_changed; + bool slice_mode_changed; + bool frame_rc_enable; + bool mb_rc_enable; +}; + +struct coda_buffer_meta { + struct list_head list; + u32 sequence; + struct v4l2_timecode timecode; + u64 timestamp; + unsigned int start; + unsigned int end; + bool last; +}; + +/* Per-queue, driver-specific private data */ +struct coda_q_data { + unsigned int width; + unsigned int height; + unsigned int bytesperline; + unsigned int sizeimage; + unsigned int fourcc; + struct v4l2_rect rect; +}; + +struct coda_iram_info { + u32 axi_sram_use; + phys_addr_t buf_bit_use; + phys_addr_t buf_ip_ac_dc_use; + phys_addr_t buf_dbk_y_use; + phys_addr_t buf_dbk_c_use; + phys_addr_t buf_ovl_use; + phys_addr_t buf_btp_use; + phys_addr_t search_ram_paddr; + int search_ram_size; + int remaining; + phys_addr_t next_paddr; +}; + +#define GDI_LINEAR_FRAME_MAP 0 +#define GDI_TILED_FRAME_MB_RASTER_MAP 1 + +struct coda_ctx; + +struct coda_context_ops { + int (*queue_init)(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + int (*reqbufs)(struct coda_ctx *ctx, struct v4l2_requestbuffers *rb); + int (*start_streaming)(struct coda_ctx *ctx); + int (*prepare_run)(struct coda_ctx *ctx); + void (*finish_run)(struct coda_ctx *ctx); + void (*run_timeout)(struct coda_ctx *ctx); + void (*seq_init_work)(struct work_struct *work); + void (*seq_end_work)(struct work_struct *work); + void (*release)(struct coda_ctx *ctx); +}; + +struct coda_internal_frame { + struct coda_aux_buf buf; + struct coda_buffer_meta meta; + u32 type; + u32 error; +}; + +struct coda_ctx { + struct coda_dev *dev; + struct mutex buffer_mutex; + struct work_struct pic_run_work; + struct work_struct seq_init_work; + struct work_struct seq_end_work; + struct completion completion; + const struct coda_video_device *cvd; + const struct coda_context_ops *ops; + int aborting; + int initialized; + int streamon_out; + int streamon_cap; + u32 qsequence; + u32 osequence; + u32 sequence_offset; + struct coda_q_data q_data[2]; + enum coda_inst_type inst_type; + const struct coda_codec *codec; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + struct coda_params params; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *h264_profile_ctrl; + struct v4l2_ctrl *h264_level_ctrl; + struct v4l2_ctrl *mpeg2_profile_ctrl; + struct v4l2_ctrl *mpeg2_level_ctrl; + struct v4l2_ctrl *mpeg4_profile_ctrl; + struct v4l2_ctrl *mpeg4_level_ctrl; + struct v4l2_ctrl *mb_err_cnt_ctrl; + struct v4l2_fh fh; + int gopcounter; + int runcounter; + int jpeg_ecs_offset; + char vpu_header[3][64]; + int vpu_header_size[3]; + struct kfifo bitstream_fifo; + struct mutex bitstream_mutex; + struct coda_aux_buf bitstream; + bool hold; + struct coda_aux_buf parabuf; + struct coda_aux_buf psbuf; + struct coda_aux_buf slicebuf; + struct coda_internal_frame internal_frames[CODA_MAX_FRAMEBUFFERS]; + struct list_head buffer_meta_list; + spinlock_t buffer_meta_lock; + int num_metas; + unsigned int first_frame_sequence; + struct coda_aux_buf workbuf; + int num_internal_frames; + int idx; + int reg_idx; + struct coda_iram_info iram_info; + int tiled_map_type; + u32 bit_stream_param; + u32 frm_dis_flg; + u32 frame_mem_ctrl; + u32 para_change; + int display_idx; + struct dentry *debugfs_entry; + bool use_bit; + bool use_vdoa; + struct vdoa_ctx *vdoa; + /* + * wakeup mutex used to serialize encoder stop command and finish_run, + * ensures that finish_run always either flags the last returned buffer + * or wakes up the capture queue to signal EOS afterwards. + */ + struct mutex wakeup_mutex; +}; + +extern int coda_debug; + +#define coda_dbg(level, ctx, fmt, arg...) \ + do { \ + if (coda_debug >= (level)) \ + v4l2_dbg((level), coda_debug, &(ctx)->dev->v4l2_dev, \ + "%u: " fmt, (ctx)->idx, ##arg); \ + } while (0) + +void coda_write(struct coda_dev *dev, u32 data, u32 reg); +unsigned int coda_read(struct coda_dev *dev, u32 reg); +void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, + struct vb2_v4l2_buffer *buf, unsigned int reg_y); + +int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, + size_t size, const char *name, struct dentry *parent); +void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf); + +int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +int coda_hw_reset(struct coda_ctx *ctx); + +void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list); + +void coda_set_gdi_regs(struct coda_ctx *ctx); + +static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &(ctx->q_data[V4L2_M2M_SRC]); + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &(ctx->q_data[V4L2_M2M_DST]); + default: + return NULL; + } +} + +const char *coda_product_name(int product); + +int coda_check_firmware(struct coda_dev *dev); + +static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx) +{ + return kfifo_len(&ctx->bitstream_fifo); +} + +/* + * The bitstream prefetcher needs to read at least 2 256 byte periods past + * the desired bitstream position for all data to reach the decoder. + */ +static inline bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, + unsigned int pos) +{ + return (int)(ctx->bitstream_fifo.kfifo.in - ALIGN(pos, 256)) > 512; +} + +bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, unsigned int pos); +int coda_bitstream_flush(struct coda_ctx *ctx); + +void coda_bit_stream_end_flag(struct coda_ctx *ctx); + +void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state); + +int coda_h264_filler_nal(int size, char *p); +int coda_h264_padding(int size, char *p); +int coda_h264_profile(int profile_idc); +int coda_h264_level(int level_idc); +int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size); + +int coda_mpeg2_profile(int profile_idc); +int coda_mpeg2_level(int level_idc); +u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); +int coda_mpeg4_profile(int profile_idc); +int coda_mpeg4_level(int level_idc); +u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); + +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc); + +bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_jpeg_write_tables(struct coda_ctx *ctx); +void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); + +extern const struct coda_context_ops coda_bit_encode_ops; +extern const struct coda_context_ops coda_bit_decode_ops; +extern const struct coda_context_ops coda9_jpeg_encode_ops; +extern const struct coda_context_ops coda9_jpeg_decode_ops; + +irqreturn_t coda_irq_handler(int irq, void *data); +irqreturn_t coda9_jpeg_irq_handler(int irq, void *data); + +#endif /* __CODA_H__ */ diff --git a/drivers/media/platform/chips-media/coda_regs.h b/drivers/media/platform/chips-media/coda_regs.h new file mode 100644 index 000000000000..db81a904cf3f --- /dev/null +++ b/drivers/media/platform/chips-media/coda_regs.h @@ -0,0 +1,563 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * linux/drivers/media/platform/chips-media/coda_regs.h + * + * Copyright (C) 2012 Vista Silicon SL + * Javier Martin + * Xavier Duret + */ + +#ifndef _REGS_CODA_H_ +#define _REGS_CODA_H_ + +/* HW registers */ +#define CODA_REG_BIT_CODE_RUN 0x000 +#define CODA_REG_RUN_ENABLE (1 << 0) +#define CODA_REG_BIT_CODE_DOWN 0x004 +#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16) +#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff) +#define CODA_REG_BIT_HOST_IN_REQ 0x008 +#define CODA_REG_BIT_INT_CLEAR 0x00c +#define CODA_REG_BIT_INT_CLEAR_SET 0x1 +#define CODA_REG_BIT_INT_STATUS 0x010 +#define CODA_REG_BIT_CODE_RESET 0x014 +#define CODA_REG_RESET_ENABLE (1 << 0) +#define CODA_REG_BIT_CUR_PC 0x018 +#define CODA9_REG_BIT_SW_RESET 0x024 +#define CODA9_SW_RESET_BPU_CORE 0x008 +#define CODA9_SW_RESET_BPU_BUS 0x010 +#define CODA9_SW_RESET_VCE_CORE 0x020 +#define CODA9_SW_RESET_VCE_BUS 0x040 +#define CODA9_SW_RESET_GDI_CORE 0x080 +#define CODA9_SW_RESET_GDI_BUS 0x100 +#define CODA9_REG_BIT_SW_RESET_STATUS 0x034 + +/* Static SW registers */ +#define CODA_REG_BIT_CODE_BUF_ADDR 0x100 +#define CODA_REG_BIT_WORK_BUF_ADDR 0x104 +#define CODA_REG_BIT_PARA_BUF_ADDR 0x108 +#define CODA_REG_BIT_STREAM_CTRL 0x10c +#define CODA7_STREAM_BUF_PIC_RESET (1 << 4) +#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3) +#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3) +#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2) +#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5) +#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4) +#define CODADX6_STREAM_CHKDIS_OFFSET (1 << 1) +#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) +#define CODA_STREAM_ENDIAN_SELECT (1 << 0) +#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 +#define CODA9_FRAME_ENABLE_BWB (1 << 12) +#define CODA9_FRAME_TILED2LINEAR (1 << 11) +#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) +#define CODA_IMAGE_ENDIAN_SELECT (1 << 0) +#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 +#define CODA_BIT_STREAM_END_FLAG (1 << 2) +#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0) +#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 +#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) +#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) +#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x)) +#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 +#define CODA7_REG_BIT_AXI_SRAM_USE 0x140 +#define CODA9_USE_HOST_BTP_ENABLE (1 << 13) +#define CODA9_USE_HOST_OVL_ENABLE (1 << 12) +#define CODA7_USE_HOST_ME_ENABLE (1 << 11) +#define CODA9_USE_HOST_DBK_ENABLE (3 << 10) +#define CODA7_USE_HOST_OVL_ENABLE (1 << 10) +#define CODA7_USE_HOST_DBK_ENABLE (1 << 9) +#define CODA9_USE_HOST_IP_ENABLE (1 << 9) +#define CODA7_USE_HOST_IP_ENABLE (1 << 8) +#define CODA9_USE_HOST_BIT_ENABLE (1 << 8) +#define CODA7_USE_HOST_BIT_ENABLE (1 << 7) +#define CODA9_USE_BTP_ENABLE (1 << 5) +#define CODA7_USE_ME_ENABLE (1 << 4) +#define CODA9_USE_OVL_ENABLE (1 << 4) +#define CODA7_USE_OVL_ENABLE (1 << 3) +#define CODA9_USE_DBK_ENABLE (3 << 2) +#define CODA7_USE_DBK_ENABLE (1 << 2) +#define CODA7_USE_IP_ENABLE (1 << 1) +#define CODA7_USE_BIT_ENABLE (1 << 0) + +#define CODA_REG_BIT_BUSY 0x160 +#define CODA_REG_BIT_BUSY_FLAG 1 +#define CODA_REG_BIT_RUN_COMMAND 0x164 +#define CODA_COMMAND_SEQ_INIT 1 +#define CODA_COMMAND_SEQ_END 2 +#define CODA_COMMAND_PIC_RUN 3 +#define CODA_COMMAND_SET_FRAME_BUF 4 +#define CODA_COMMAND_ENCODE_HEADER 5 +#define CODA_COMMAND_ENC_PARA_SET 6 +#define CODA_COMMAND_DEC_PARA_SET 7 +#define CODA_COMMAND_DEC_BUF_FLUSH 8 +#define CODA_COMMAND_RC_CHANGE_PARAMETER 9 +#define CODA_COMMAND_FIRMWARE_GET 0xf +#define CODA_REG_BIT_RUN_INDEX 0x168 +#define CODA_INDEX_SET(x) ((x) & 0x3) +#define CODA_REG_BIT_RUN_COD_STD 0x16c +#define CODADX6_MODE_DECODE_MP4 0 +#define CODADX6_MODE_ENCODE_MP4 1 +#define CODADX6_MODE_DECODE_H264 2 +#define CODADX6_MODE_ENCODE_H264 3 +#define CODA7_MODE_DECODE_H264 0 +#define CODA7_MODE_DECODE_VC1 1 +#define CODA7_MODE_DECODE_MP2 2 +#define CODA7_MODE_DECODE_MP4 3 +#define CODA7_MODE_DECODE_DV3 3 +#define CODA7_MODE_DECODE_RV 4 +#define CODA7_MODE_DECODE_MJPG 5 +#define CODA7_MODE_ENCODE_H264 8 +#define CODA7_MODE_ENCODE_MP4 11 +#define CODA7_MODE_ENCODE_MJPG 13 +#define CODA9_MODE_DECODE_H264 0 +#define CODA9_MODE_DECODE_VC1 1 +#define CODA9_MODE_DECODE_MP2 2 +#define CODA9_MODE_DECODE_MP4 3 +#define CODA9_MODE_DECODE_DV3 3 +#define CODA9_MODE_DECODE_RV 4 +#define CODA9_MODE_DECODE_AVS 5 +#define CODA9_MODE_DECODE_MJPG 6 +#define CODA9_MODE_DECODE_VPX 7 +#define CODA9_MODE_ENCODE_H264 8 +#define CODA9_MODE_ENCODE_MP4 11 +#define CODA9_MODE_ENCODE_MJPG 13 +#define CODA_MODE_INVALID 0xffff +#define CODA_REG_BIT_INT_ENABLE 0x170 +#define CODA_INT_INTERRUPT_ENABLE (1 << 3) +#define CODA_REG_BIT_INT_REASON 0x174 +#define CODA7_REG_BIT_RUN_AUX_STD 0x178 +#define CODA_MP4_AUX_MPEG4 0 +#define CODA_MP4_AUX_DIVX3 1 +#define CODA_VPX_AUX_THO 0 +#define CODA_VPX_AUX_VP6 1 +#define CODA_VPX_AUX_VP8 2 +#define CODA_H264_AUX_AVC 0 +#define CODA_H264_AUX_MVC 1 + +/* + * Commands' mailbox: + * registers with offsets in the range 0x180-0x1d0 + * have different meaning depending on the command being + * issued. + */ + +/* Decoder Sequence Initialization */ +#define CODA_CMD_DEC_SEQ_BB_START 0x180 +#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184 +#define CODA_CMD_DEC_SEQ_OPTION 0x188 +#define CODA_NO_INT_ENABLE (1 << 10) +#define CODA_REORDER_ENABLE (1 << 1) +#define CODADX6_QP_REPORT (1 << 0) +#define CODA7_MP4_DEBLK_ENABLE (1 << 0) +#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c +#define CODA_CMD_DEC_SEQ_START_BYTE 0x190 +#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 +#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 +#define CODA_CMD_DEC_SEQ_JPG_THUMB_EN 0x19c +#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c +#define CODA_MP4_CLASS_MPEG4 0 +#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c +#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0 + +#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0 +#define CODA9_RET_DEC_SEQ_BITRATE 0x1b4 +#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0 +#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */ +#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4 +#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8 +#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8 +#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc +#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0 +#define CODA_RET_DEC_SEQ_INFO 0x1d4 +#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8 +#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc +#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0 +#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0 +#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4 +#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 +#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 +#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 +#define CODA7_RET_DEC_SEQ_HEADER_REPORT 0x1ec + +/* Decoder Picture Run */ +#define CODA_CMD_DEC_PIC_ROT_MODE 0x180 +#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184 +#define CODA9_CMD_DEC_PIC_ROT_INDEX 0x184 +#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188 +#define CODA9_CMD_DEC_PIC_ROT_ADDR_Y 0x188 +#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c +#define CODA9_CMD_DEC_PIC_ROT_ADDR_CB 0x18c +#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190 +#define CODA9_CMD_DEC_PIC_ROT_ADDR_CR 0x190 +#define CODA9_CMD_DEC_PIC_ROT_STRIDE 0x1b8 + +#define CODA_CMD_DEC_PIC_OPTION 0x194 +#define CODA_PRE_SCAN_EN (1 << 0) +#define CODA_PRE_SCAN_MODE_DECODE (0 << 1) +#define CODA_PRE_SCAN_MODE_RETURN (1 << 1) +#define CODA_IFRAME_SEARCH_EN (1 << 2) +#define CODA_SKIP_FRAME_MODE (0x3 << 3) +#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198 +#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c +#define CODA_CMD_DEC_PIC_BB_START 0x1a0 +#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4 +#define CODA_RET_DEC_PIC_SIZE 0x1bc +#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0 +#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4 +#define CODA_RET_DEC_PIC_ERR_MB 0x1c8 +#define CODA_RET_DEC_PIC_TYPE 0x1cc +#define CODA_PIC_TYPE_MASK 0x7 +#define CODA_PIC_TYPE_MASK_VC1 0x3f +#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3) +#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6) +#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16) +#define CODA7_PIC_TYPE_INTERLACED (1 << 18) +#define CODA_RET_DEC_PIC_POST 0x1d0 +#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0 +#define CODA_RET_DEC_PIC_OPTION 0x1d4 +#define CODA_RET_DEC_PIC_SUCCESS 0x1d8 +#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc +#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0 +#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4 +#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec + +#define CODA9_RET_DEC_PIC_VP8_PIC_REPORT 0x1e8 +#define CODA9_RET_DEC_PIC_ASPECT 0x1f0 +#define CODA9_RET_DEC_PIC_VP8_SCALE_INFO 0x1f0 +#define CODA9_RET_DEC_PIC_FRATE_NR 0x1f4 +#define CODA9_RET_DEC_PIC_FRATE_DR 0x1f8 + +/* Encoder Sequence Initialization */ +#define CODA_CMD_ENC_SEQ_BB_START 0x180 +#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 +#define CODA_CMD_ENC_SEQ_OPTION 0x188 +#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9 +#define CODA9_OPTION_MVC_PREFIX_NAL_OFFSET 9 +#define CODA7_OPTION_GAMMA_OFFSET 8 +#define CODA9_OPTION_MVC_PARASET_REFRESH_OFFSET 8 +#define CODA7_OPTION_RCQPMAX_OFFSET 7 +#define CODA9_OPTION_GAMMA_OFFSET 7 +#define CODADX6_OPTION_GAMMA_OFFSET 7 +#define CODA7_OPTION_RCQPMIN_OFFSET 6 +#define CODA9_OPTION_RCQPMAX_OFFSET 6 +#define CODA_OPTION_LIMITQP_OFFSET 6 +#define CODA_OPTION_RCINTRAQP_OFFSET 5 +#define CODA_OPTION_FMO_OFFSET 4 +#define CODA9_OPTION_MVC_INTERVIEW_OFFSET 4 +#define CODA_OPTION_AVC_AUD_OFFSET 2 +#define CODA_OPTION_SLICEREPORT_OFFSET 1 +#define CODA_CMD_ENC_SEQ_COD_STD 0x18c +#define CODA_STD_MPEG4 0 +#define CODA9_STD_H264 0 +#define CODA_STD_H263 1 +#define CODA_STD_H264 2 +#define CODA9_STD_MPEG4 3 + +#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190 +#define CODA7_PICWIDTH_OFFSET 16 +#define CODA7_PICWIDTH_MASK 0xffff +#define CODADX6_PICWIDTH_OFFSET 10 +#define CODADX6_PICWIDTH_MASK 0x3ff +#define CODA_PICHEIGHT_OFFSET 0 +#define CODADX6_PICHEIGHT_MASK 0x3ff +#define CODA7_PICHEIGHT_MASK 0xffff +#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 +#define CODA_FRATE_RES_OFFSET 0 +#define CODA_FRATE_RES_MASK 0xffff +#define CODA_FRATE_DIV_OFFSET 16 +#define CODA_FRATE_DIV_MASK 0xffff +#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 +#define CODA_MP4PARAM_VERID_OFFSET 6 +#define CODA_MP4PARAM_VERID_MASK 0x01 +#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2 +#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07 +#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1 +#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01 +#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0 +#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_263_PARA 0x19c +#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2 +#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01 +#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1 +#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01 +#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0 +#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0 +#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12 +#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f +#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8 +#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f +#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6 +#define CODA_264PARAM_DISABLEDEBLK_MASK 0x03 +#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5 +#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01 +#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0 +#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f +#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4 +#define CODA_SLICING_SIZE_OFFSET 2 +#define CODA_SLICING_SIZE_MASK 0x3fffffff +#define CODA_SLICING_UNIT_OFFSET 1 +#define CODA_SLICING_UNIT_MASK 0x01 +#define CODA_SLICING_MODE_OFFSET 0 +#define CODA_SLICING_MODE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8 +#define CODA_GOP_SIZE_OFFSET 0 +#define CODA_GOP_SIZE_MASK 0x3f +#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac +#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31 +#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01 +#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16 +#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7fff +#define CODA_RATECONTROL_BITRATE_OFFSET 1 +#define CODA_RATECONTROL_BITRATE_MASK 0x7fff +#define CODA_RATECONTROL_ENABLE_OFFSET 0 +#define CODA_RATECONTROL_ENABLE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0 +#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4 +#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8 +#define CODA_FMOPARAM_TYPE_OFFSET 4 +#define CODA_FMOPARAM_TYPE_MASK 1 +#define CODA_FMOPARAM_SLICENUM_OFFSET 0 +#define CODA_FMOPARAM_SLICENUM_MASK 0x0f +#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc +#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8 +#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc +#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4 +#define CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX 0x1c8 +#define CODA_QPMIN_OFFSET 8 +#define CODA_QPMIN_MASK 0x3f +#define CODA_QPMAX_OFFSET 0 +#define CODA_QPMAX_MASK 0x3f +#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc +#define CODA_GAMMA_OFFSET 0 +#define CODA_GAMMA_MASK 0xffff +#define CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE 0x1d0 +#define CODA9_CMD_ENC_SEQ_INTRA_WEIGHT 0x1d4 +#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8 +#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0 + +#define CODA_CMD_ENC_SEQ_JPG_PARA 0x198 +#define CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL 0x19C +#define CODA_CMD_ENC_SEQ_JPG_THUMB_EN 0x1a0 +#define CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE 0x1a4 +#define CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET 0x1a8 + +/* Encoder Parameter Change */ +#define CODA_CMD_ENC_PARAM_CHANGE_ENABLE 0x180 +#define CODA_PARAM_CHANGE_RC_GOP BIT(0) +#define CODA_PARAM_CHANGE_RC_INTRA_QP BIT(1) +#define CODA_PARAM_CHANGE_RC_BITRATE BIT(2) +#define CODA_PARAM_CHANGE_RC_FRAME_RATE BIT(3) +#define CODA_PARAM_CHANGE_INTRA_MB_NUM BIT(4) +#define CODA_PARAM_CHANGE_SLICE_MODE BIT(5) +#define CODA_PARAM_CHANGE_HEC_MODE BIT(6) +#define CODA_CMD_ENC_PARAM_RC_GOP 0x184 +#define CODA_CMD_ENC_PARAM_RC_INTRA_QP 0x188 +#define CODA_CMD_ENC_PARAM_RC_BITRATE 0x18c +#define CODA_CMD_ENC_PARAM_RC_FRAME_RATE 0x190 +#define CODA_CMD_ENC_PARAM_INTRA_MB_NUM 0x194 +#define CODA_CMD_ENC_PARAM_SLICE_MODE 0x198 +#define CODA_CMD_ENC_PARAM_HEC_MODE 0x19c +#define CODA_RET_ENC_PARAM_CHANGE_SUCCESS 0x1c0 + +/* Encoder Picture Run */ +#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180 +#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184 +#define CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC 0x1a4 +#define CODA9_CMD_ENC_PIC_SRC_ADDR_Y 0x1a8 +#define CODA9_CMD_ENC_PIC_SRC_ADDR_CB 0x1ac +#define CODA9_CMD_ENC_PIC_SRC_ADDR_CR 0x1b0 +#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180 +#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184 +#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188 +#define CODA_CMD_ENC_PIC_QS 0x18c +#define CODA_CMD_ENC_PIC_ROT_MODE 0x190 +#define CODA_ROT_MIR_ENABLE (1 << 4) +#define CODA_ROT_0 (0x0 << 0) +#define CODA_ROT_90 (0x1 << 0) +#define CODA_ROT_180 (0x2 << 0) +#define CODA_ROT_270 (0x3 << 0) +#define CODA_MIR_NONE (0x0 << 2) +#define CODA_MIR_VER (0x1 << 2) +#define CODA_MIR_HOR (0x2 << 2) +#define CODA_MIR_VER_HOR (0x3 << 2) +#define CODA_CMD_ENC_PIC_OPTION 0x194 +#define CODA_FORCE_IPICTURE BIT(1) +#define CODA_REPORT_MB_INFO BIT(3) +#define CODA_REPORT_MV_INFO BIT(4) +#define CODA_REPORT_SLICE_INFO BIT(5) +#define CODA_CMD_ENC_PIC_BB_START 0x198 +#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c +#define CODA_RET_ENC_FRAME_NUM 0x1c0 +#define CODA_RET_ENC_PIC_TYPE 0x1c4 +#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8 +#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc +#define CODA_RET_ENC_PIC_FLAG 0x1d0 +#define CODA_RET_ENC_PIC_SUCCESS 0x1d8 + +/* Set Frame Buffer */ +#define CODA_CMD_SET_FRAME_BUF_NUM 0x180 +#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184 +#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188 +#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c +#define CODA9_CMD_SET_FRAME_SUBSAMP_A 0x188 +#define CODA9_CMD_SET_FRAME_SUBSAMP_B 0x18c +#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190 +#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194 +#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198 +#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c +#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0 +#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4 +#define CODA9_CMD_SET_FRAME_AXI_BTP_ADDR 0x1a4 +#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 +#define CODA9_CMD_SET_FRAME_CACHE_SIZE 0x1a8 +#define CODA9_CMD_SET_FRAME_CACHE_CONFIG 0x1ac +#define CODA9_CACHE_BYPASS_OFFSET 28 +#define CODA9_CACHE_DUALCONF_OFFSET 26 +#define CODA9_CACHE_PAGEMERGE_OFFSET 24 +#define CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET 16 +#define CODA9_CACHE_CB_BUFFER_SIZE_OFFSET 8 +#define CODA9_CACHE_CR_BUFFER_SIZE_OFFSET 0 +#define CODA9_CMD_SET_FRAME_SUBSAMP_A_MVC 0x1b0 +#define CODA9_CMD_SET_FRAME_SUBSAMP_B_MVC 0x1b4 +#define CODA9_CMD_SET_FRAME_DP_BUF_BASE 0x1b0 +#define CODA9_CMD_SET_FRAME_DP_BUF_SIZE 0x1b4 +#define CODA9_CMD_SET_FRAME_MAX_DEC_SIZE 0x1b8 +#define CODA9_CMD_SET_FRAME_DELAY 0x1bc + +/* Encoder Header */ +#define CODA_CMD_ENC_HEADER_CODE 0x180 +#define CODA_GAMMA_OFFSET 0 +#define CODA_HEADER_H264_SPS 0 +#define CODA_HEADER_H264_PPS 1 +#define CODA_HEADER_MP4V_VOL 0 +#define CODA_HEADER_MP4V_VOS 1 +#define CODA_HEADER_MP4V_VIS 2 +#define CODA9_HEADER_FRAME_CROP (1 << 3) +#define CODA_CMD_ENC_HEADER_BB_START 0x184 +#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188 +#define CODA9_CMD_ENC_HEADER_FRAME_CROP_H 0x18c +#define CODA9_CMD_ENC_HEADER_FRAME_CROP_V 0x190 + +/* Get Version */ +#define CODA_CMD_FIRMWARE_VERNUM 0x1c0 +#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff) +#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f) +#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f) +#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff) +#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \ + ((product) << 16 | ((major) << 12) | \ + ((minor) << 8) | (release)) +#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4 + +#define CODA9_GDMA_BASE 0x1000 +#define CODA9_GDI_CONTROL (CODA9_GDMA_BASE + 0x034) +#define CODA9_GDI_PIC_INIT_HOST (CODA9_GDMA_BASE + 0x038) +#define CODA9_GDI_STATUS (CODA9_GDMA_BASE + 0x080) +#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0) +#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac) + +#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0) +#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4) + +#define CODA9_GDI_INFO_CONTROL (CODA9_GDMA_BASE + 0x400) +#define CODA9_GDI_INFO_PIC_SIZE (CODA9_GDMA_BASE + 0x404) +#define CODA9_GDI_INFO_BASE_Y (CODA9_GDMA_BASE + 0x408) +#define CODA9_GDI_INFO_BASE_CB (CODA9_GDMA_BASE + 0x40c) +#define CODA9_GDI_INFO_BASE_CR (CODA9_GDMA_BASE + 0x410) + +#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800) +#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c) + +#define CODA9_GDI_XY2_BA_0 (CODA9_GDMA_BASE + 0x840) +#define CODA9_GDI_XY2_BA_1 (CODA9_GDMA_BASE + 0x844) +#define CODA9_GDI_XY2_BA_2 (CODA9_GDMA_BASE + 0x848) +#define CODA9_GDI_XY2_BA_3 (CODA9_GDMA_BASE + 0x84c) + +#define CODA9_GDI_XY2_RAS_0 (CODA9_GDMA_BASE + 0x850) +#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) + +#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) +#define CODA9_XY2RBC_SEPARATE_MAP BIT(19) +#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18) +#define CODA9_XY2RBC_TILED_MAP BIT(17) +#define CODA9_XY2RBC_CA_INC_HOR BIT(16) +#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) +#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) +#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920) + +#define CODA9_JPEG_BASE 0x3000 +#define CODA9_REG_JPEG_PIC_START (CODA9_JPEG_BASE + 0x000) +#define CODA9_REG_JPEG_PIC_STATUS (CODA9_JPEG_BASE + 0x004) +#define CODA9_JPEG_STATUS_OVERFLOW BIT(3) +#define CODA9_JPEG_STATUS_BBC_INT BIT(2) +#define CODA9_JPEG_STATUS_ERROR BIT(1) +#define CODA9_JPEG_STATUS_DONE BIT(0) +#define CODA9_REG_JPEG_PIC_ERRMB (CODA9_JPEG_BASE + 0x008) +#define CODA9_JPEG_ERRMB_RESTART_IDX_MASK (0xf << 24) +#define CODA9_JPEG_ERRMB_MCU_POS_X_MASK (0xfff << 12) +#define CODA9_JPEG_ERRMB_MCU_POS_Y_MASK 0xfff +#define CODA9_REG_JPEG_PIC_CTRL (CODA9_JPEG_BASE + 0x010) +#define CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN BIT(6) +#define CODA9_JPEG_PIC_CTRL_TC_DIRECTION BIT(4) +#define CODA9_JPEG_PIC_CTRL_ENCODER_EN BIT(3) +#define CODA9_REG_JPEG_PIC_SIZE (CODA9_JPEG_BASE + 0x014) +#define CODA9_REG_JPEG_MCU_INFO (CODA9_JPEG_BASE + 0x018) +#define CODA9_JPEG_MCU_BLOCK_NUM_OFFSET 16 +#define CODA9_JPEG_COMP_NUM_OFFSET 12 +#define CODA9_JPEG_COMP0_INFO_OFFSET 8 +#define CODA9_JPEG_COMP1_INFO_OFFSET 4 +#define CODA9_JPEG_COMP2_INFO_OFFSET 0 +#define CODA9_REG_JPEG_ROT_INFO (CODA9_JPEG_BASE + 0x01c) +#define CODA9_JPEG_ROT_MIR_ENABLE BIT(4) +#define CODA9_JPEG_ROT_MIR_MODE_MASK 0xf +#define CODA9_REG_JPEG_SCL_INFO (CODA9_JPEG_BASE + 0x020) +#define CODA9_JPEG_SCL_ENABLE BIT(4) +#define CODA9_JPEG_SCL_HOR_MODE_MASK (0x3 << 2) +#define CODA9_JPEG_SCL_VER_MODE_MASK (0x3 << 0) +#define CODA9_REG_JPEG_IF_INFO (CODA9_JPEG_BASE + 0x024) +#define CODA9_JPEG_SENS_IF_CLR BIT(1) +#define CODA9_JPEG_DISP_IF_CLR BIT(0) +#define CODA9_REG_JPEG_OP_INFO (CODA9_JPEG_BASE + 0x02c) +#define CODA9_JPEG_BUS_REQ_NUM_OFFSET 0 +#define CODA9_JPEG_BUS_REQ_NUM_MASK 0x7 +#define CODA9_REG_JPEG_DPB_CONFIG (CODA9_JPEG_BASE + 0x030) +#define CODA9_REG_JPEG_DPB_BASE00 (CODA9_JPEG_BASE + 0x040) +#define CODA9_REG_JPEG_HUFF_CTRL (CODA9_JPEG_BASE + 0x080) +#define CODA9_REG_JPEG_HUFF_ADDR (CODA9_JPEG_BASE + 0x084) +#define CODA9_REG_JPEG_HUFF_DATA (CODA9_JPEG_BASE + 0x088) +#define CODA9_REG_JPEG_QMAT_CTRL (CODA9_JPEG_BASE + 0x090) +#define CODA9_REG_JPEG_QMAT_ADDR (CODA9_JPEG_BASE + 0x094) +#define CODA9_REG_JPEG_QMAT_DATA (CODA9_JPEG_BASE + 0x098) +#define CODA9_REG_JPEG_RST_INTVAL (CODA9_JPEG_BASE + 0x0b0) +#define CODA9_REG_JPEG_RST_INDEX (CODA9_JPEG_BASE + 0x0b4) +#define CODA9_REG_JPEG_RST_COUNT (CODA9_JPEG_BASE + 0x0b8) +#define CODA9_REG_JPEG_DPCM_DIFF_Y (CODA9_JPEG_BASE + 0x0f0) +#define CODA9_REG_JPEG_DPCM_DIFF_CB (CODA9_JPEG_BASE + 0x0f4) +#define CODA9_REG_JPEG_DPCM_DIFF_CR (CODA9_JPEG_BASE + 0x0f8) +#define CODA9_REG_JPEG_GBU_CTRL (CODA9_JPEG_BASE + 0x100) +#define CODA9_REG_JPEG_GBU_BT_PTR (CODA9_JPEG_BASE + 0x110) +#define CODA9_REG_JPEG_GBU_WD_PTR (CODA9_JPEG_BASE + 0x114) +#define CODA9_REG_JPEG_GBU_TT_CNT (CODA9_JPEG_BASE + 0x118) +#define CODA9_REG_JPEG_GBU_BBSR (CODA9_JPEG_BASE + 0x140) +#define CODA9_REG_JPEG_GBU_BBER (CODA9_JPEG_BASE + 0x144) +#define CODA9_REG_JPEG_GBU_BBIR (CODA9_JPEG_BASE + 0x148) +#define CODA9_REG_JPEG_GBU_BBHR (CODA9_JPEG_BASE + 0x14c) +#define CODA9_REG_JPEG_GBU_BCNT (CODA9_JPEG_BASE + 0x158) +#define CODA9_REG_JPEG_GBU_FF_RPTR (CODA9_JPEG_BASE + 0x160) +#define CODA9_REG_JPEG_GBU_FF_WPTR (CODA9_JPEG_BASE + 0x164) +#define CODA9_REG_JPEG_BBC_END_ADDR (CODA9_JPEG_BASE + 0x208) +#define CODA9_REG_JPEG_BBC_WR_PTR (CODA9_JPEG_BASE + 0x20c) +#define CODA9_REG_JPEG_BBC_RD_PTR (CODA9_JPEG_BASE + 0x210) +#define CODA9_REG_JPEG_BBC_EXT_ADDR (CODA9_JPEG_BASE + 0x214) +#define CODA9_REG_JPEG_BBC_INT_ADDR (CODA9_JPEG_BASE + 0x218) +#define CODA9_REG_JPEG_BBC_DATA_CNT (CODA9_JPEG_BASE + 0x21c) +#define CODA9_REG_JPEG_BBC_COMMAND (CODA9_JPEG_BASE + 0x220) +#define CODA9_REG_JPEG_BBC_BUSY (CODA9_JPEG_BASE + 0x224) +#define CODA9_REG_JPEG_BBC_CTRL (CODA9_JPEG_BASE + 0x228) +#define CODA9_REG_JPEG_BBC_CUR_POS (CODA9_JPEG_BASE + 0x22c) +#define CODA9_REG_JPEG_BBC_BAS_ADDR (CODA9_JPEG_BASE + 0x230) +#define CODA9_REG_JPEG_BBC_STRM_CTRL (CODA9_JPEG_BASE + 0x234) +#define CODA9_REG_JPEG_BBC_FLUSH_CMD (CODA9_JPEG_BASE + 0x238) + +#endif diff --git a/drivers/media/platform/chips-media/imx-vdoa.c b/drivers/media/platform/chips-media/imx-vdoa.c new file mode 100644 index 000000000000..c70871bae193 --- /dev/null +++ b/drivers/media/platform/chips-media/imx-vdoa.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i.MX6 Video Data Order Adapter (VDOA) + * + * Copyright (C) 2014 Philipp Zabel + * Copyright (C) 2016 Pengutronix, Michael Tretter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-vdoa.h" + +#define VDOA_NAME "imx-vdoa" + +#define VDOAC 0x00 +#define VDOASRR 0x04 +#define VDOAIE 0x08 +#define VDOAIST 0x0c +#define VDOAFP 0x10 +#define VDOAIEBA00 0x14 +#define VDOAIEBA01 0x18 +#define VDOAIEBA02 0x1c +#define VDOAIEBA10 0x20 +#define VDOAIEBA11 0x24 +#define VDOAIEBA12 0x28 +#define VDOASL 0x2c +#define VDOAIUBO 0x30 +#define VDOAVEBA0 0x34 +#define VDOAVEBA1 0x38 +#define VDOAVEBA2 0x3c +#define VDOAVUBO 0x40 +#define VDOASR 0x44 + +#define VDOAC_ISEL BIT(6) +#define VDOAC_PFS BIT(5) +#define VDOAC_SO BIT(4) +#define VDOAC_SYNC BIT(3) +#define VDOAC_NF BIT(2) +#define VDOAC_BNDM_MASK 0x3 +#define VDOAC_BAND_HEIGHT_8 0x0 +#define VDOAC_BAND_HEIGHT_16 0x1 +#define VDOAC_BAND_HEIGHT_32 0x2 + +#define VDOASRR_START BIT(1) +#define VDOASRR_SWRST BIT(0) + +#define VDOAIE_EITERR BIT(1) +#define VDOAIE_EIEOT BIT(0) + +#define VDOAIST_TERR BIT(1) +#define VDOAIST_EOT BIT(0) + +#define VDOAFP_FH_MASK (0x1fff << 16) +#define VDOAFP_FW_MASK (0x3fff) + +#define VDOASL_VSLY_MASK (0x3fff << 16) +#define VDOASL_ISLY_MASK (0x7fff) + +#define VDOASR_ERRW BIT(4) +#define VDOASR_EOB BIT(3) +#define VDOASR_CURRENT_FRAME (0x3 << 1) +#define VDOASR_CURRENT_BUFFER BIT(1) + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +struct vdoa_data { + struct vdoa_ctx *curr_ctx; + struct device *dev; + struct clk *vdoa_clk; + void __iomem *regs; +}; + +struct vdoa_q_data { + unsigned int width; + unsigned int height; + unsigned int bytesperline; + unsigned int sizeimage; + u32 pixelformat; +}; + +struct vdoa_ctx { + struct vdoa_data *vdoa; + struct completion completion; + struct vdoa_q_data q_data[2]; + unsigned int submitted_job; + unsigned int completed_job; +}; + +static irqreturn_t vdoa_irq_handler(int irq, void *data) +{ + struct vdoa_data *vdoa = data; + struct vdoa_ctx *curr_ctx; + u32 val; + + /* Disable interrupts */ + writel(0, vdoa->regs + VDOAIE); + + curr_ctx = vdoa->curr_ctx; + if (!curr_ctx) { + dev_warn(vdoa->dev, + "Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + val = readl(vdoa->regs + VDOAIST); + writel(val, vdoa->regs + VDOAIST); + if (val & VDOAIST_TERR) { + val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW; + dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read"); + } else if (!(val & VDOAIST_EOT)) { + dev_warn(vdoa->dev, "Spurious interrupt\n"); + } + curr_ctx->completed_job++; + complete(&curr_ctx->completion); + + return IRQ_HANDLED; +} + +int vdoa_wait_for_completion(struct vdoa_ctx *ctx) +{ + struct vdoa_data *vdoa = ctx->vdoa; + + if (ctx->submitted_job == ctx->completed_job) + return 0; + + if (!wait_for_completion_timeout(&ctx->completion, + msecs_to_jiffies(300))) { + dev_err(vdoa->dev, + "Timeout waiting for transfer result\n"); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL(vdoa_wait_for_completion); + +void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src) +{ + struct vdoa_q_data *src_q_data, *dst_q_data; + struct vdoa_data *vdoa = ctx->vdoa; + u32 val; + + if (vdoa->curr_ctx) + vdoa_wait_for_completion(vdoa->curr_ctx); + + vdoa->curr_ctx = ctx; + + reinit_completion(&ctx->completion); + ctx->submitted_job++; + + src_q_data = &ctx->q_data[V4L2_M2M_SRC]; + dst_q_data = &ctx->q_data[V4L2_M2M_DST]; + + /* Progressive, no sync, 1 frame per run */ + if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV) + val = VDOAC_PFS; + else + val = 0; + writel(val, vdoa->regs + VDOAC); + + writel(dst_q_data->height << 16 | dst_q_data->width, + vdoa->regs + VDOAFP); + + val = dst; + writel(val, vdoa->regs + VDOAIEBA00); + + writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline, + vdoa->regs + VDOASL); + + if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 || + dst_q_data->pixelformat == V4L2_PIX_FMT_NV21) + val = dst_q_data->bytesperline * dst_q_data->height; + else + val = 0; + writel(val, vdoa->regs + VDOAIUBO); + + val = src; + writel(val, vdoa->regs + VDOAVEBA0); + val = round_up(src_q_data->bytesperline * src_q_data->height, 4096); + writel(val, vdoa->regs + VDOAVUBO); + + /* Enable interrupts and start transfer */ + writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE); + writel(VDOASRR_START, vdoa->regs + VDOASRR); +} +EXPORT_SYMBOL(vdoa_device_run); + +struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) +{ + struct vdoa_ctx *ctx; + int err; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + err = clk_prepare_enable(vdoa->vdoa_clk); + if (err) { + kfree(ctx); + return NULL; + } + + init_completion(&ctx->completion); + ctx->vdoa = vdoa; + + return ctx; +} +EXPORT_SYMBOL(vdoa_context_create); + +void vdoa_context_destroy(struct vdoa_ctx *ctx) +{ + struct vdoa_data *vdoa = ctx->vdoa; + + if (vdoa->curr_ctx == ctx) { + vdoa_wait_for_completion(vdoa->curr_ctx); + vdoa->curr_ctx = NULL; + } + + clk_disable_unprepare(vdoa->vdoa_clk); + kfree(ctx); +} +EXPORT_SYMBOL(vdoa_context_destroy); + +int vdoa_context_configure(struct vdoa_ctx *ctx, + unsigned int width, unsigned int height, + u32 pixelformat) +{ + struct vdoa_q_data *src_q_data; + struct vdoa_q_data *dst_q_data; + + if (width < 16 || width > 8192 || width % 16 != 0 || + height < 16 || height > 4096 || height % 16 != 0) + return -EINVAL; + + if (pixelformat != V4L2_PIX_FMT_YUYV && + pixelformat != V4L2_PIX_FMT_NV12) + return -EINVAL; + + /* If no context is passed, only check if the format is valid */ + if (!ctx) + return 0; + + src_q_data = &ctx->q_data[V4L2_M2M_SRC]; + dst_q_data = &ctx->q_data[V4L2_M2M_DST]; + + src_q_data->width = width; + src_q_data->height = height; + src_q_data->bytesperline = width; + src_q_data->sizeimage = + round_up(src_q_data->bytesperline * height, 4096) + + src_q_data->bytesperline * height / 2; + + dst_q_data->width = width; + dst_q_data->height = height; + dst_q_data->pixelformat = pixelformat; + switch (pixelformat) { + case V4L2_PIX_FMT_YUYV: + dst_q_data->bytesperline = width * 2; + dst_q_data->sizeimage = dst_q_data->bytesperline * height; + break; + case V4L2_PIX_FMT_NV12: + default: + dst_q_data->bytesperline = width; + dst_q_data->sizeimage = + dst_q_data->bytesperline * height * 3 / 2; + break; + } + + return 0; +} +EXPORT_SYMBOL(vdoa_context_configure); + +static int vdoa_probe(struct platform_device *pdev) +{ + struct vdoa_data *vdoa; + int ret; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "DMA enable failed\n"); + return ret; + } + + vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL); + if (!vdoa) + return -ENOMEM; + + vdoa->dev = &pdev->dev; + + vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL); + if (IS_ERR(vdoa->vdoa_clk)) { + dev_err(vdoa->dev, "Failed to get clock\n"); + return PTR_ERR(vdoa->vdoa_clk); + } + + vdoa->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vdoa->regs)) + return PTR_ERR(vdoa->regs); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, + vdoa_irq_handler, IRQF_ONESHOT, + "vdoa", vdoa); + if (ret < 0) { + dev_err(vdoa->dev, "Failed to get irq\n"); + return ret; + } + + platform_set_drvdata(pdev, vdoa); + + return 0; +} + +static int vdoa_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id vdoa_dt_ids[] = { + { .compatible = "fsl,imx6q-vdoa" }, + {} +}; +MODULE_DEVICE_TABLE(of, vdoa_dt_ids); + +static struct platform_driver vdoa_driver = { + .probe = vdoa_probe, + .remove = vdoa_remove, + .driver = { + .name = VDOA_NAME, + .of_match_table = vdoa_dt_ids, + }, +}; + +module_platform_driver(vdoa_driver); + +MODULE_DESCRIPTION("Video Data Order Adapter"); +MODULE_AUTHOR("Philipp Zabel "); +MODULE_ALIAS("platform:imx-vdoa"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/chips-media/imx-vdoa.h b/drivers/media/platform/chips-media/imx-vdoa.h new file mode 100644 index 000000000000..a62eab476d58 --- /dev/null +++ b/drivers/media/platform/chips-media/imx-vdoa.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Pengutronix + */ + +#ifndef IMX_VDOA_H +#define IMX_VDOA_H + +struct vdoa_data; +struct vdoa_ctx; + +#if (defined CONFIG_VIDEO_IMX_VDOA || defined CONFIG_VIDEO_IMX_VDOA_MODULE) + +struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa); +int vdoa_context_configure(struct vdoa_ctx *ctx, + unsigned int width, unsigned int height, + u32 pixelformat); +void vdoa_context_destroy(struct vdoa_ctx *ctx); + +void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src); +int vdoa_wait_for_completion(struct vdoa_ctx *ctx); + +#else + +static inline struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) +{ + return NULL; +} + +static inline int vdoa_context_configure(struct vdoa_ctx *ctx, + unsigned int width, + unsigned int height, + u32 pixelformat) +{ + return 0; +} + +static inline void vdoa_context_destroy(struct vdoa_ctx *ctx) { }; + +static inline void vdoa_device_run(struct vdoa_ctx *ctx, + dma_addr_t dst, dma_addr_t src) { }; + +static inline int vdoa_wait_for_completion(struct vdoa_ctx *ctx) +{ + return 0; +}; + +#endif + +#endif /* IMX_VDOA_H */ diff --git a/drivers/media/platform/chips-media/trace.h b/drivers/media/platform/chips-media/trace.h new file mode 100644 index 000000000000..19f98e6dafb9 --- /dev/null +++ b/drivers/media/platform/chips-media/trace.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM coda + +#if !defined(__CODA_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __CODA_TRACE_H__ + +#include +#include + +#include "coda.h" + +TRACE_EVENT(coda_bit_run, + TP_PROTO(struct coda_ctx *ctx, int cmd), + + TP_ARGS(ctx, cmd), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, ctx) + __field(int, cmd) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->ctx = ctx->idx; + __entry->cmd = cmd; + ), + + TP_printk("minor = %d, ctx = %d, cmd = %d", + __entry->minor, __entry->ctx, __entry->cmd) +); + +TRACE_EVENT(coda_bit_done, + TP_PROTO(struct coda_ctx *ctx), + + TP_ARGS(ctx), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) +); + +DECLARE_EVENT_CLASS(coda_buf_class, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + + TP_ARGS(ctx, buf), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->vb2_buf.index; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, ctx = %d", + __entry->minor, __entry->index, __entry->ctx) +); + +DEFINE_EVENT(coda_buf_class, coda_enc_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +DEFINE_EVENT(coda_buf_class, coda_enc_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +DECLARE_EVENT_CLASS(coda_buf_meta_class, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + + TP_ARGS(ctx, buf, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->vb2_buf.index; + __entry->start = meta->start & ctx->bitstream_fifo.kfifo.mask; + __entry->end = meta->end & ctx->bitstream_fifo.kfifo.mask; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->index, __entry->start, __entry->end, + __entry->ctx) +); + +DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) +); + +DECLARE_EVENT_CLASS(coda_meta_class, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + + TP_ARGS(ctx, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->start = meta ? (meta->start & + ctx->bitstream_fifo.kfifo.mask) : 0; + __entry->end = meta ? (meta->end & + ctx->bitstream_fifo.kfifo.mask) : 0; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->start, __entry->end, __entry->ctx) +); + +DEFINE_EVENT(coda_meta_class, coda_dec_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + TP_ARGS(ctx, meta) +); + +DEFINE_EVENT(coda_meta_class, coda_dec_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + TP_ARGS(ctx, meta) +); + +DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) +); + +DEFINE_EVENT(coda_buf_class, coda_jpeg_run, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +DEFINE_EVENT(coda_buf_class, coda_jpeg_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +#endif /* __CODA_TRACE_H__ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include diff --git a/drivers/media/platform/coda/Kconfig b/drivers/media/platform/coda/Kconfig deleted file mode 100644 index ae495fd7b25a..000000000000 --- a/drivers/media/platform/coda/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_CODA - tristate "Chips&Media Coda multi-standard codec IP" - depends on V4L_MEM2MEM_DRIVERS - depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) - select SRAM - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_JPEG_HELPER - select V4L2_MEM2MEM_DEV - select GENERIC_ALLOCATOR - help - Coda is a range of video codec IPs that supports - H.264, MPEG-4, and other video formats. - -config VIDEO_IMX_VDOA - def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile deleted file mode 100644 index bbb16425a875..000000000000 --- a/drivers/media/platform/coda/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o - -obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o -obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c deleted file mode 100644 index c484c008ab02..000000000000 --- a/drivers/media/platform/coda/coda-bit.c +++ /dev/null @@ -1,2660 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - BIT processor functions - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "coda.h" -#include "imx-vdoa.h" -#define CREATE_TRACE_POINTS -#include "trace.h" - -#define CODA_PARA_BUF_SIZE (10 * 1024) -#define CODA7_PS_BUF_SIZE 0x28000 -#define CODA9_PS_SAVE_SIZE (512 * 1024) - -#define CODA_DEFAULT_GAMMA 4096 -#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ - -static void coda_free_bitstream_buffer(struct coda_ctx *ctx); - -static inline int coda_is_initialized(struct coda_dev *dev) -{ - return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0; -} - -static inline unsigned long coda_isbusy(struct coda_dev *dev) -{ - return coda_read(dev, CODA_REG_BIT_BUSY); -} - -static int coda_wait_timeout(struct coda_dev *dev) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - while (coda_isbusy(dev)) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - } - return 0; -} - -static void coda_command_async(struct coda_ctx *ctx, int cmd) -{ - struct coda_dev *dev = ctx->dev; - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541 || - dev->devtype->product == CODA_960) { - /* Restore context related registers to CODA */ - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - coda_write(dev, ctx->frm_dis_flg, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - coda_write(dev, ctx->frame_mem_ctrl, - CODA_REG_BIT_FRAME_MEM_CTRL); - coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); - } - - if (dev->devtype->product == CODA_960) { - coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - } - - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - - coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); - coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); - coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); - - trace_coda_bit_run(ctx, cmd); - - coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); -} - -static int coda_command_sync(struct coda_ctx *ctx, int cmd) -{ - struct coda_dev *dev = ctx->dev; - int ret; - - lockdep_assert_held(&dev->coda_mutex); - - coda_command_async(ctx, cmd); - ret = coda_wait_timeout(dev); - trace_coda_bit_done(ctx); - - return ret; -} - -int coda_hw_reset(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - unsigned long timeout; - unsigned int idx; - int ret; - - lockdep_assert_held(&dev->coda_mutex); - - if (!dev->rstc) - return -ENOENT; - - idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); - - if (dev->devtype->product == CODA_960) { - timeout = jiffies + msecs_to_jiffies(100); - coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); - while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - } - - ret = reset_control_reset(dev->rstc); - if (ret < 0) - return ret; - - if (dev->devtype->product == CODA_960) - coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - ret = coda_wait_timeout(dev); - coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); - - return ret; -} - -static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 rd_ptr; - - rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - kfifo->out = (kfifo->in & ~kfifo->mask) | - (rd_ptr - ctx->bitstream.paddr); - if (kfifo->out > kfifo->in) - kfifo->out -= kfifo->mask + 1; -} - -static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 rd_ptr, wr_ptr; - - rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); - coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); - coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); -} - -static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr; - - wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); - coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); -} - -static int coda_h264_bitstream_pad(struct coda_ctx *ctx, u32 size) -{ - unsigned char *buf; - u32 n; - - if (size < 6) - size = 6; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - coda_h264_filler_nal(size, buf); - n = kfifo_in(&ctx->bitstream_fifo, buf, size); - kfree(buf); - - return (n < size) ? -ENOSPC : 0; -} - -int coda_bitstream_flush(struct coda_ctx *ctx) -{ - int ret; - - if (ctx->inst_type != CODA_INST_DECODER || !ctx->use_bit) - return 0; - - ret = coda_command_sync(ctx, CODA_COMMAND_DEC_BUF_FLUSH); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, "failed to flush bitstream\n"); - return ret; - } - - kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, - ctx->bitstream.size); - coda_kfifo_sync_to_device_full(ctx); - - return 0; -} - -static int coda_bitstream_queue(struct coda_ctx *ctx, const u8 *buf, u32 size) -{ - u32 n = kfifo_in(&ctx->bitstream_fifo, buf, size); - - return (n < size) ? -ENOSPC : 0; -} - -static u32 coda_buffer_parse_headers(struct coda_ctx *ctx, - struct vb2_v4l2_buffer *src_buf, - u32 payload) -{ - u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); - u32 size = 0; - - switch (ctx->codec->src_fourcc) { - case V4L2_PIX_FMT_MPEG2: - size = coda_mpeg2_parse_headers(ctx, vaddr, payload); - break; - case V4L2_PIX_FMT_MPEG4: - size = coda_mpeg4_parse_headers(ctx, vaddr, payload); - break; - default: - break; - } - - return size; -} - -static bool coda_bitstream_try_queue(struct coda_ctx *ctx, - struct vb2_v4l2_buffer *src_buf) -{ - unsigned long payload = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); - int ret; - int i; - - if (coda_get_bitstream_payload(ctx) + payload + 512 >= - ctx->bitstream.size) - return false; - - if (!vaddr) { - v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); - return true; - } - - if (ctx->qsequence == 0 && payload < 512) { - /* - * Add padding after the first buffer, if it is too small to be - * fetched by the CODA, by repeating the headers. Without - * repeated headers, or the first frame already queued, decoder - * sequence initialization fails with error code 0x2000 on i.MX6 - * or error code 0x1 on i.MX51. - */ - u32 header_size = coda_buffer_parse_headers(ctx, src_buf, - payload); - - if (header_size) { - coda_dbg(1, ctx, "pad with %u-byte header\n", - header_size); - for (i = payload; i < 512; i += header_size) { - ret = coda_bitstream_queue(ctx, vaddr, - header_size); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, - "bitstream buffer overflow\n"); - return false; - } - if (ctx->dev->devtype->product == CODA_960) - break; - } - } else { - coda_dbg(1, ctx, - "could not parse header, sequence initialization might fail\n"); - } - - /* Add padding before the first buffer, if it is too small */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - coda_h264_bitstream_pad(ctx, 512 - payload); - } - - ret = coda_bitstream_queue(ctx, vaddr, payload); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); - return false; - } - - src_buf->sequence = ctx->qsequence++; - - /* Sync read pointer to device */ - if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) - coda_kfifo_sync_to_device_write(ctx); - - /* Set the stream-end flag after the last buffer is queued */ - if (src_buf->flags & V4L2_BUF_FLAG_LAST) - coda_bit_stream_end_flag(ctx); - ctx->hold = false; - - return true; -} - -void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) -{ - struct vb2_v4l2_buffer *src_buf; - struct coda_buffer_meta *meta; - u32 start; - - if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) - return; - - while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { - /* - * Only queue two JPEGs into the bitstream buffer to keep - * latency low. We need at least one complete buffer and the - * header of another buffer (for prescan) in the bitstream. - */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && - ctx->num_metas > 1) - break; - - if (ctx->num_internal_frames && - ctx->num_metas >= ctx->num_internal_frames) { - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - - /* - * If we managed to fill in at least a full reorder - * window of buffers (num_internal_frames is a - * conservative estimate for this) and the bitstream - * prefetcher has at least 2 256 bytes periods beyond - * the first buffer to fetch, we can safely stop queuing - * in order to limit the decoder drain latency. - */ - if (coda_bitstream_can_fetch_past(ctx, meta->end)) - break; - } - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - - /* Drop frames that do not start/end with a SOI/EOI markers */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && - !coda_jpeg_check_buffer(ctx, &src_buf->vb2_buf)) { - v4l2_err(&ctx->dev->v4l2_dev, - "dropping invalid JPEG frame %d\n", - ctx->qsequence); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - if (buffer_list) { - struct v4l2_m2m_buffer *m2m_buf; - - m2m_buf = container_of(src_buf, - struct v4l2_m2m_buffer, - vb); - list_add_tail(&m2m_buf->list, buffer_list); - } else { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - } - continue; - } - - /* Dump empty buffers */ - if (!vb2_get_plane_payload(&src_buf->vb2_buf, 0)) { - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - continue; - } - - /* Buffer start position */ - start = ctx->bitstream_fifo.kfifo.in; - - if (coda_bitstream_try_queue(ctx, src_buf)) { - /* - * Source buffer is queued in the bitstream ringbuffer; - * queue the timestamp and mark source buffer as done - */ - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - - meta = kmalloc(sizeof(*meta), GFP_KERNEL); - if (meta) { - meta->sequence = src_buf->sequence; - meta->timecode = src_buf->timecode; - meta->timestamp = src_buf->vb2_buf.timestamp; - meta->start = start; - meta->end = ctx->bitstream_fifo.kfifo.in; - meta->last = src_buf->flags & V4L2_BUF_FLAG_LAST; - if (meta->last) - coda_dbg(1, ctx, "marking last meta"); - spin_lock(&ctx->buffer_meta_lock); - list_add_tail(&meta->list, - &ctx->buffer_meta_list); - ctx->num_metas++; - spin_unlock(&ctx->buffer_meta_lock); - - trace_coda_bit_queue(ctx, src_buf, meta); - } - - if (buffer_list) { - struct v4l2_m2m_buffer *m2m_buf; - - m2m_buf = container_of(src_buf, - struct v4l2_m2m_buffer, - vb); - list_add_tail(&m2m_buf->list, buffer_list); - } else { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } - } else { - break; - } - } -} - -void coda_bit_stream_end_flag(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - - /* If this context is currently running, update the hardware flag */ - if ((dev->devtype->product == CODA_960) && - coda_isbusy(dev) && - (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - } -} - -static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) -{ - struct coda_dev *dev = ctx->dev; - u32 *p = ctx->parabuf.vaddr; - - if (dev->devtype->product == CODA_DX6) - p[index] = value; - else - p[index ^ 1] = value; -} - -static inline int coda_alloc_context_buf(struct coda_ctx *ctx, - struct coda_aux_buf *buf, size_t size, - const char *name) -{ - return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); -} - - -static void coda_free_framebuffers(struct coda_ctx *ctx) -{ - int i; - - for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) - coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i].buf); -} - -static int coda_alloc_framebuffers(struct coda_ctx *ctx, - struct coda_q_data *q_data, u32 fourcc) -{ - struct coda_dev *dev = ctx->dev; - unsigned int ysize, ycbcr_size; - int ret; - int i; - - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || - ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) - ysize = round_up(q_data->rect.width, 16) * - round_up(q_data->rect.height, 16); - else - ysize = round_up(q_data->rect.width, 8) * q_data->rect.height; - - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ycbcr_size = round_up(ysize, 4096) + ysize / 2; - else - ycbcr_size = ysize + ysize / 2; - - /* Allocate frame buffers */ - for (i = 0; i < ctx->num_internal_frames; i++) { - size_t size = ycbcr_size; - char *name; - - /* Add space for mvcol buffers */ - if (dev->devtype->product != CODA_DX6 && - (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || - (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0))) - size += ysize / 4; - name = kasprintf(GFP_KERNEL, "fb%d", i); - if (!name) { - coda_free_framebuffers(ctx); - return -ENOMEM; - } - ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i].buf, - size, name); - kfree(name); - if (ret < 0) { - coda_free_framebuffers(ctx); - return ret; - } - } - - /* Register frame buffers in the parameter buffer */ - for (i = 0; i < ctx->num_internal_frames; i++) { - u32 y, cb, cr, mvcol; - - /* Start addresses of Y, Cb, Cr planes */ - y = ctx->internal_frames[i].buf.paddr; - cb = y + ysize; - cr = y + ysize + ysize/4; - mvcol = y + ysize + ysize/4 + ysize/4; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) { - cb = round_up(cb, 4096); - mvcol = cb + ysize/2; - cr = 0; - /* Packed 20-bit MSB of base addresses */ - /* YYYYYCCC, CCyyyyyc, cccc.... */ - y = (y & 0xfffff000) | cb >> 20; - cb = (cb & 0x000ff000) << 12; - } - coda_parabuf_write(ctx, i * 3 + 0, y); - coda_parabuf_write(ctx, i * 3 + 1, cb); - coda_parabuf_write(ctx, i * 3 + 2, cr); - - if (dev->devtype->product == CODA_DX6) - continue; - - /* mvcol buffer for h.264 and mpeg4 */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - coda_parabuf_write(ctx, 96 + i, mvcol); - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0) - coda_parabuf_write(ctx, 97, mvcol); - } - - return 0; -} - -static void coda_free_context_buffers(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - - coda_free_aux_buf(dev, &ctx->slicebuf); - coda_free_aux_buf(dev, &ctx->psbuf); - if (dev->devtype->product != CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - coda_free_aux_buf(dev, &ctx->parabuf); -} - -static int coda_alloc_context_buffers(struct coda_ctx *ctx, - struct coda_q_data *q_data) -{ - struct coda_dev *dev = ctx->dev; - size_t size; - int ret; - - if (!ctx->parabuf.vaddr) { - ret = coda_alloc_context_buf(ctx, &ctx->parabuf, - CODA_PARA_BUF_SIZE, "parabuf"); - if (ret < 0) - return ret; - } - - if (dev->devtype->product == CODA_DX6) - return 0; - - if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { - /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->rect.width, 16) * - DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; - ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, - "slicebuf"); - if (ret < 0) - goto err; - } - - if (!ctx->psbuf.vaddr && (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541)) { - ret = coda_alloc_context_buf(ctx, &ctx->psbuf, - CODA7_PS_BUF_SIZE, "psbuf"); - if (ret < 0) - goto err; - } - - if (!ctx->workbuf.vaddr) { - size = dev->devtype->workbuf_size; - if (dev->devtype->product == CODA_960 && - q_data->fourcc == V4L2_PIX_FMT_H264) - size += CODA9_PS_SAVE_SIZE; - ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, - "workbuf"); - if (ret < 0) - goto err; - } - - return 0; - -err: - coda_free_context_buffers(ctx); - return ret; -} - -static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - int header_code, u8 *header, int *size) -{ - struct vb2_buffer *vb = &buf->vb2_buf; - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_src; - struct v4l2_rect *r; - size_t bufsize; - int ret; - int i; - - if (dev->devtype->product == CODA_960) - memset(vb2_plane_vaddr(vb, 0), 0, 64); - - coda_write(dev, vb2_dma_contig_plane_dma_addr(vb, 0), - CODA_CMD_ENC_HEADER_BB_START); - bufsize = vb2_plane_size(vb, 0); - if (dev->devtype->product == CODA_960) - bufsize /= 1024; - coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); - if (dev->devtype->product == CODA_960 && - ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 && - header_code == CODA_HEADER_H264_SPS) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - r = &q_data_src->rect; - - if (r->width % 16 || r->height % 16) { - u32 crop_right = round_up(r->width, 16) - r->width; - u32 crop_bottom = round_up(r->height, 16) - r->height; - - coda_write(dev, crop_right, - CODA9_CMD_ENC_HEADER_FRAME_CROP_H); - coda_write(dev, crop_bottom, - CODA9_CMD_ENC_HEADER_FRAME_CROP_V); - header_code |= CODA9_HEADER_FRAME_CROP; - } - } - coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); - ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); - return ret; - } - - if (dev->devtype->product == CODA_960) { - for (i = 63; i > 0; i--) - if (((char *)vb2_plane_vaddr(vb, 0))[i] != 0) - break; - *size = i + 1; - } else { - *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - } - memcpy(header, vb2_plane_vaddr(vb, 0), *size); - - return 0; -} - -static u32 coda_slice_mode(struct coda_ctx *ctx) -{ - int size, unit; - - switch (ctx->params.slice_mode) { - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: - default: - return 0; - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB: - size = ctx->params.slice_max_mb; - unit = 1; - break; - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES: - size = ctx->params.slice_max_bits; - unit = 0; - break; - } - - return ((size & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET) | - ((unit & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET) | - ((1 & CODA_SLICING_MODE_MASK) << CODA_SLICING_MODE_OFFSET); -} - -static int coda_enc_param_change(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u32 change_enable = 0; - u32 success; - int ret; - - if (ctx->params.gop_size_changed) { - change_enable |= CODA_PARAM_CHANGE_RC_GOP; - coda_write(dev, ctx->params.gop_size, - CODA_CMD_ENC_PARAM_RC_GOP); - ctx->gopcounter = ctx->params.gop_size - 1; - ctx->params.gop_size_changed = false; - } - if (ctx->params.h264_intra_qp_changed) { - coda_dbg(1, ctx, "parameter change: intra Qp %u\n", - ctx->params.h264_intra_qp); - - if (ctx->params.bitrate) { - change_enable |= CODA_PARAM_CHANGE_RC_INTRA_QP; - coda_write(dev, ctx->params.h264_intra_qp, - CODA_CMD_ENC_PARAM_RC_INTRA_QP); - } - ctx->params.h264_intra_qp_changed = false; - } - if (ctx->params.bitrate_changed) { - coda_dbg(1, ctx, "parameter change: bitrate %u kbit/s\n", - ctx->params.bitrate); - change_enable |= CODA_PARAM_CHANGE_RC_BITRATE; - coda_write(dev, ctx->params.bitrate, - CODA_CMD_ENC_PARAM_RC_BITRATE); - ctx->params.bitrate_changed = false; - } - if (ctx->params.framerate_changed) { - coda_dbg(1, ctx, "parameter change: frame rate %u/%u Hz\n", - ctx->params.framerate & 0xffff, - (ctx->params.framerate >> 16) + 1); - change_enable |= CODA_PARAM_CHANGE_RC_FRAME_RATE; - coda_write(dev, ctx->params.framerate, - CODA_CMD_ENC_PARAM_RC_FRAME_RATE); - ctx->params.framerate_changed = false; - } - if (ctx->params.intra_refresh_changed) { - coda_dbg(1, ctx, "parameter change: intra refresh MBs %u\n", - ctx->params.intra_refresh); - change_enable |= CODA_PARAM_CHANGE_INTRA_MB_NUM; - coda_write(dev, ctx->params.intra_refresh, - CODA_CMD_ENC_PARAM_INTRA_MB_NUM); - ctx->params.intra_refresh_changed = false; - } - if (ctx->params.slice_mode_changed) { - change_enable |= CODA_PARAM_CHANGE_SLICE_MODE; - coda_write(dev, coda_slice_mode(ctx), - CODA_CMD_ENC_PARAM_SLICE_MODE); - ctx->params.slice_mode_changed = false; - } - - if (!change_enable) - return 0; - - coda_write(dev, change_enable, CODA_CMD_ENC_PARAM_CHANGE_ENABLE); - - ret = coda_command_sync(ctx, CODA_COMMAND_RC_CHANGE_PARAMETER); - if (ret < 0) - return ret; - - success = coda_read(dev, CODA_RET_ENC_PARAM_CHANGE_SUCCESS); - if (success != 1) - coda_dbg(1, ctx, "parameter change failed: %u\n", success); - - return 0; -} - -static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) -{ - phys_addr_t ret; - - size = round_up(size, 1024); - if (size > iram->remaining) - return 0; - iram->remaining -= size; - - ret = iram->next_paddr; - iram->next_paddr += size; - - return ret; -} - -static void coda_setup_iram(struct coda_ctx *ctx) -{ - struct coda_iram_info *iram_info = &ctx->iram_info; - struct coda_dev *dev = ctx->dev; - int w64, w128; - int mb_width; - int dbk_bits; - int bit_bits; - int ip_bits; - int me_bits; - - memset(iram_info, 0, sizeof(*iram_info)); - iram_info->next_paddr = dev->iram.paddr; - iram_info->remaining = dev->iram.size; - - if (!dev->iram.vaddr) - return; - - switch (dev->devtype->product) { - case CODA_HX4: - dbk_bits = CODA7_USE_HOST_DBK_ENABLE; - bit_bits = CODA7_USE_HOST_BIT_ENABLE; - ip_bits = CODA7_USE_HOST_IP_ENABLE; - me_bits = CODA7_USE_HOST_ME_ENABLE; - break; - case CODA_7541: - dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; - bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; - ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; - me_bits = CODA7_USE_HOST_ME_ENABLE | CODA7_USE_ME_ENABLE; - break; - case CODA_960: - dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; - bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; - ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; - me_bits = 0; - break; - default: /* CODA_DX6 */ - return; - } - - if (ctx->inst_type == CODA_INST_ENCODER) { - struct coda_q_data *q_data_src; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - mb_width = DIV_ROUND_UP(q_data_src->rect.width, 16); - w128 = mb_width * 128; - w64 = mb_width * 64; - - /* Prioritize in case IRAM is too small for everything */ - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - iram_info->search_ram_size = round_up(mb_width * 16 * - 36 + 2048, 1024); - iram_info->search_ram_paddr = coda_iram_alloc(iram_info, - iram_info->search_ram_size); - if (!iram_info->search_ram_paddr) { - pr_err("IRAM is smaller than the search ram size\n"); - goto out; - } - iram_info->axi_sram_use |= me_bits; - } - - /* Only H.264BP and H.263P3 are considered */ - iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64); - iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64); - if (!iram_info->buf_dbk_c_use) - goto out; - iram_info->axi_sram_use |= dbk_bits; - - iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_bit_use) - goto out; - iram_info->axi_sram_use |= bit_bits; - - iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_ip_ac_dc_use) - goto out; - iram_info->axi_sram_use |= ip_bits; - - /* OVL and BTP disabled for encoder */ - } else if (ctx->inst_type == CODA_INST_DECODER) { - struct coda_q_data *q_data_dst; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - mb_width = DIV_ROUND_UP(q_data_dst->width, 16); - w128 = mb_width * 128; - - iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128); - iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_dbk_c_use) - goto out; - iram_info->axi_sram_use |= dbk_bits; - - iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_bit_use) - goto out; - iram_info->axi_sram_use |= bit_bits; - - iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_ip_ac_dc_use) - goto out; - iram_info->axi_sram_use |= ip_bits; - - /* OVL and BTP unused as there is no VC1 support yet */ - } - -out: - if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) - coda_dbg(1, ctx, "IRAM smaller than needed\n"); - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - /* TODO - Enabling these causes picture errors on CODA7541 */ - if (ctx->inst_type == CODA_INST_DECODER) { - /* fw 1.4.50 */ - iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | - CODA7_USE_IP_ENABLE); - } else { - /* fw 13.4.29 */ - iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | - CODA7_USE_HOST_DBK_ENABLE | - CODA7_USE_IP_ENABLE | - CODA7_USE_DBK_ENABLE); - } - } -} - -static u32 coda_supported_firmwares[] = { - CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), - CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), - CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 9), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), - CODA_FIRMWARE_VERNUM(CODA_960, 3, 1, 1), -}; - -static bool coda_firmware_supported(u32 vernum) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) - if (vernum == coda_supported_firmwares[i]) - return true; - return false; -} - -int coda_check_firmware(struct coda_dev *dev) -{ - u16 product, major, minor, release; - u32 data; - int ret; - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); - coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); - coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); - if (coda_wait_timeout(dev)) { - v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); - ret = -EIO; - goto err_run_cmd; - } - - if (dev->devtype->product == CODA_960) { - data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); - v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", - data); - } - - /* Check we are compatible with the loaded firmware */ - data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); - product = CODA_FIRMWARE_PRODUCT(data); - major = CODA_FIRMWARE_MAJOR(data); - minor = CODA_FIRMWARE_MINOR(data); - release = CODA_FIRMWARE_RELEASE(data); - - clk_disable_unprepare(dev->clk_per); - clk_disable_unprepare(dev->clk_ahb); - - if (product != dev->devtype->product) { - v4l2_err(&dev->v4l2_dev, - "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n", - coda_product_name(dev->devtype->product), - coda_product_name(product), major, minor, release); - return -EINVAL; - } - - v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", - coda_product_name(product)); - - if (coda_firmware_supported(data)) { - v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", - major, minor, release); - } else { - v4l2_warn(&dev->v4l2_dev, - "Unsupported firmware version: %u.%u.%u\n", - major, minor, release); - } - - return 0; - -err_run_cmd: - clk_disable_unprepare(dev->clk_ahb); -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - return ret; -} - -static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) -{ - u32 cache_size, cache_config; - - if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) { - /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ - cache_size = 0x20262024; - cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; - } else { - /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */ - cache_size = 0x02440243; - cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET; - } - coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); - if (fourcc == V4L2_PIX_FMT_NV12 || fourcc == V4L2_PIX_FMT_YUYV) { - cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | - 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; - } else { - cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | - 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; - } - coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG); -} - -/* - * Encoder context operations - */ - -static int coda_encoder_reqbufs(struct coda_ctx *ctx, - struct v4l2_requestbuffers *rb) -{ - struct coda_q_data *q_data_src; - int ret; - - if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return 0; - - if (rb->count) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - } else { - coda_free_context_buffers(ctx); - } - - return 0; -} - -static int coda_start_encoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - struct coda_q_data *q_data_src, *q_data_dst; - u32 bitstream_buf, bitstream_size; - struct vb2_v4l2_buffer *buf; - int gamma, ret, value; - u32 dst_fourcc; - int num_fb; - u32 stride; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - bitstream_buf = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); - bitstream_size = q_data_dst->sizeimage; - - if (!coda_is_initialized(dev)) { - v4l2_err(v4l2_dev, "coda is not initialized.\n"); - return -EFAULT; - } - - if (dst_fourcc == V4L2_PIX_FMT_JPEG) { - if (!ctx->params.jpeg_qmat_tab[0]) - ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[1]) - ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); - coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); - } - - mutex_lock(&dev->coda_mutex); - - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); - switch (dev->devtype->product) { - case CODA_DX6: - coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | - CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); - break; - case CODA_960: - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - fallthrough; - case CODA_HX4: - case CODA_7541: - coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | - CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); - break; - } - - ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | - CODA9_FRAME_TILED2LINEAR); - if (q_data_src->fourcc == V4L2_PIX_FMT_NV12) - ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; - coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); - - if (dev->devtype->product == CODA_DX6) { - /* Configure the coda */ - coda_write(dev, dev->iram.paddr, - CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); - } - - /* Could set rotation here if needed */ - value = 0; - switch (dev->devtype->product) { - case CODA_DX6: - value = (q_data_src->rect.width & CODADX6_PICWIDTH_MASK) - << CODADX6_PICWIDTH_OFFSET; - value |= (q_data_src->rect.height & CODADX6_PICHEIGHT_MASK) - << CODA_PICHEIGHT_OFFSET; - break; - case CODA_HX4: - case CODA_7541: - if (dst_fourcc == V4L2_PIX_FMT_H264) { - value = (round_up(q_data_src->rect.width, 16) & - CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (round_up(q_data_src->rect.height, 16) & - CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; - break; - } - fallthrough; - case CODA_960: - value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) - << CODA7_PICWIDTH_OFFSET; - value |= (q_data_src->rect.height & CODA7_PICHEIGHT_MASK) - << CODA_PICHEIGHT_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); - if (dst_fourcc == V4L2_PIX_FMT_JPEG) - ctx->params.framerate = 0; - coda_write(dev, ctx->params.framerate, - CODA_CMD_ENC_SEQ_SRC_F_RATE); - - ctx->params.codec_mode = ctx->codec->mode; - switch (dst_fourcc) { - case V4L2_PIX_FMT_MPEG4: - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_STD_MPEG4, - CODA_CMD_ENC_SEQ_COD_STD); - else - coda_write(dev, CODA_STD_MPEG4, - CODA_CMD_ENC_SEQ_COD_STD); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); - break; - case V4L2_PIX_FMT_H264: - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_STD_H264, - CODA_CMD_ENC_SEQ_COD_STD); - else - coda_write(dev, CODA_STD_H264, - CODA_CMD_ENC_SEQ_COD_STD); - value = ((ctx->params.h264_disable_deblocking_filter_idc & - CODA_264PARAM_DISABLEDEBLK_MASK) << - CODA_264PARAM_DISABLEDEBLK_OFFSET) | - ((ctx->params.h264_slice_alpha_c0_offset_div2 & - CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | - ((ctx->params.h264_slice_beta_offset_div2 & - CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | - (ctx->params.h264_constrained_intra_pred_flag << - CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) | - (ctx->params.h264_chroma_qp_index_offset & - CODA_264PARAM_CHROMAQPOFFSET_MASK); - coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); - break; - case V4L2_PIX_FMT_JPEG: - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_PARA); - coda_write(dev, ctx->params.jpeg_restart_interval, - CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_EN); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET); - - coda_jpeg_write_tables(ctx); - break; - default: - v4l2_err(v4l2_dev, - "dst format (0x%08x) invalid.\n", dst_fourcc); - ret = -EINVAL; - goto out; - } - - /* - * slice mode and GOP size registers are used for thumb size/offset - * in JPEG mode - */ - if (dst_fourcc != V4L2_PIX_FMT_JPEG) { - value = coda_slice_mode(ctx); - coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); - value = ctx->params.gop_size; - coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); - } - - if (ctx->params.bitrate && (ctx->params.frame_rc_enable || - ctx->params.mb_rc_enable)) { - ctx->params.bitrate_changed = false; - ctx->params.h264_intra_qp_changed = false; - - /* Rate control enabled */ - value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) - << CODA_RATECONTROL_BITRATE_OFFSET; - value |= 1 & CODA_RATECONTROL_ENABLE_MASK; - value |= (ctx->params.vbv_delay & - CODA_RATECONTROL_INITIALDELAY_MASK) - << CODA_RATECONTROL_INITIALDELAY_OFFSET; - if (dev->devtype->product == CODA_960) - value |= BIT(31); /* disable autoskip */ - } else { - value = 0; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); - - coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); - coda_write(dev, ctx->params.intra_refresh, - CODA_CMD_ENC_SEQ_INTRA_REFRESH); - - coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); - coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); - - - value = 0; - if (dev->devtype->product == CODA_960) - gamma = CODA9_DEFAULT_GAMMA; - else - gamma = CODA_DEFAULT_GAMMA; - if (gamma > 0) { - coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, - CODA_CMD_ENC_SEQ_RC_GAMMA); - } - - if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { - coda_write(dev, - ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | - ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, - CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); - } - if (dev->devtype->product == CODA_960) { - if (ctx->params.h264_max_qp) - value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; - if (CODA_DEFAULT_GAMMA > 0) - value |= 1 << CODA9_OPTION_GAMMA_OFFSET; - } else { - if (CODA_DEFAULT_GAMMA > 0) { - if (dev->devtype->product == CODA_DX6) - value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; - else - value |= 1 << CODA7_OPTION_GAMMA_OFFSET; - } - if (ctx->params.h264_min_qp) - value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; - if (ctx->params.h264_max_qp) - value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); - - if (ctx->params.frame_rc_enable && !ctx->params.mb_rc_enable) - value = 1; - else - value = 0; - coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); - - coda_setup_iram(ctx); - - if (dst_fourcc == V4L2_PIX_FMT_H264) { - switch (dev->devtype->product) { - case CODA_DX6: - value = FMO_SLICE_SAVE_BUF_SIZE << 7; - coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); - break; - case CODA_HX4: - case CODA_7541: - coda_write(dev, ctx->iram_info.search_ram_paddr, - CODA7_CMD_ENC_SEQ_SEARCH_BASE); - coda_write(dev, ctx->iram_info.search_ram_size, - CODA7_CMD_ENC_SEQ_SEARCH_SIZE); - break; - case CODA_960: - coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); - coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); - } - } - - ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); - if (ret < 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - goto out; - } - - if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); - ret = -EFAULT; - goto out; - } - ctx->initialized = 1; - - if (dst_fourcc != V4L2_PIX_FMT_JPEG) { - if (dev->devtype->product == CODA_960) - ctx->num_internal_frames = 4; - else - ctx->num_internal_frames = 2; - ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); - if (ret < 0) { - v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); - goto out; - } - num_fb = 2; - stride = q_data_src->bytesperline; - } else { - ctx->num_internal_frames = 0; - num_fb = 0; - stride = 0; - } - coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE); - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - coda_write(dev, q_data_src->bytesperline, - CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); - } - if (dev->devtype->product != CODA_DX6) { - coda_write(dev, ctx->iram_info.buf_bit_use, - CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, - CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_y_use, - CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_c_use, - CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, ctx->iram_info.buf_ovl_use, - CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) { - coda_write(dev, ctx->iram_info.buf_btp_use, - CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - - coda9_set_frame_cache(ctx, q_data_src->fourcc); - - /* FIXME */ - coda_write(dev, ctx->internal_frames[2].buf.paddr, - CODA9_CMD_SET_FRAME_SUBSAMP_A); - coda_write(dev, ctx->internal_frames[3].buf.paddr, - CODA9_CMD_SET_FRAME_SUBSAMP_B); - } - } - - ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); - if (ret < 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - goto out; - } - - coda_dbg(1, ctx, "start encoding %dx%d %4.4s->%4.4s @ %d/%d Hz\n", - q_data_src->rect.width, q_data_src->rect.height, - (char *)&ctx->codec->src_fourcc, (char *)&dst_fourcc, - ctx->params.framerate & 0xffff, - (ctx->params.framerate >> 16) + 1); - - /* Save stream headers */ - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - /* - * Get SPS in the first frame and copy it to an - * intermediate buffer. - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0]); - if (ret < 0) - goto out; - - /* - * If visible width or height are not aligned to macroblock - * size, the crop_right and crop_bottom SPS fields must be set - * to the difference between visible and coded size. This is - * only supported by CODA960 firmware. All others do not allow - * writing frame cropping parameters, so we have to manually - * fix up the SPS RBSP (Sequence Parameter Set Raw Byte - * Sequence Payload) ourselves. - */ - if (ctx->dev->devtype->product != CODA_960 && - ((q_data_src->rect.width % 16) || - (q_data_src->rect.height % 16))) { - ret = coda_h264_sps_fixup(ctx, q_data_src->rect.width, - q_data_src->rect.height, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0], - sizeof(ctx->vpu_header[0])); - if (ret < 0) - goto out; - } - - /* - * Get PPS in the first frame and copy it to an - * intermediate buffer. - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, - &ctx->vpu_header[1][0], - &ctx->vpu_header_size[1]); - if (ret < 0) - goto out; - - /* - * Length of H.264 headers is variable and thus it might not be - * aligned for the coda to append the encoded frame. In that is - * the case a filler NAL must be added to header 2. - */ - ctx->vpu_header_size[2] = coda_h264_padding( - (ctx->vpu_header_size[0] + - ctx->vpu_header_size[1]), - ctx->vpu_header[2]); - break; - case V4L2_PIX_FMT_MPEG4: - /* - * Get VOS in the first frame and copy it to an - * intermediate buffer - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0]); - if (ret < 0) - goto out; - - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, - &ctx->vpu_header[1][0], - &ctx->vpu_header_size[1]); - if (ret < 0) - goto out; - - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, - &ctx->vpu_header[2][0], - &ctx->vpu_header_size[2]); - if (ret < 0) - goto out; - break; - default: - /* No more formats need to save headers at the moment */ - break; - } - -out: - mutex_unlock(&dev->coda_mutex); - return ret; -} - -static int coda_prepare_encode(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - int force_ipicture; - int quant_param = 0; - u32 pic_stream_buffer_addr, pic_stream_buffer_size; - u32 rot_mode = 0; - u32 dst_fourcc; - u32 reg; - int ret; - - ret = coda_enc_param_change(ctx); - if (ret < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "parameter change failed: %d\n", - ret); - } - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - src_buf->sequence = ctx->osequence; - dst_buf->sequence = ctx->osequence; - ctx->osequence++; - - force_ipicture = ctx->params.force_ipicture; - if (force_ipicture) - ctx->params.force_ipicture = false; - else if (ctx->params.gop_size != 0 && - (src_buf->sequence % ctx->params.gop_size) == 0) - force_ipicture = 1; - - /* - * Workaround coda firmware BUG that only marks the first - * frame as IDR. This is a problem for some decoders that can't - * recover when a frame is lost. - */ - if (!force_ipicture) { - src_buf->flags |= V4L2_BUF_FLAG_PFRAME; - src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; - } else { - src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; - } - - if (dev->devtype->product == CODA_960) - coda_set_gdi_regs(ctx); - - /* - * Copy headers in front of the first frame and forced I frames for - * H.264 only. In MPEG4 they are already copied by the CODA. - */ - if (src_buf->sequence == 0 || force_ipicture) { - pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]; - pic_stream_buffer_size = q_data_dst->sizeimage - - ctx->vpu_header_size[0] - - ctx->vpu_header_size[1] - - ctx->vpu_header_size[2]; - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0), - &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) - + ctx->vpu_header_size[0], &ctx->vpu_header[1][0], - ctx->vpu_header_size[1]); - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) - + ctx->vpu_header_size[0] + ctx->vpu_header_size[1], - &ctx->vpu_header[2][0], ctx->vpu_header_size[2]); - } else { - pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - pic_stream_buffer_size = q_data_dst->sizeimage; - } - - if (force_ipicture) { - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - quant_param = ctx->params.h264_intra_qp; - break; - case V4L2_PIX_FMT_MPEG4: - quant_param = ctx->params.mpeg4_intra_qp; - break; - case V4L2_PIX_FMT_JPEG: - quant_param = 30; - break; - default: - v4l2_warn(&ctx->dev->v4l2_dev, - "cannot set intra qp, fmt not supported\n"); - break; - } - } else { - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - quant_param = ctx->params.h264_inter_qp; - break; - case V4L2_PIX_FMT_MPEG4: - quant_param = ctx->params.mpeg4_inter_qp; - break; - default: - v4l2_warn(&ctx->dev->v4l2_dev, - "cannot set inter qp, fmt not supported\n"); - break; - } - } - - /* submit */ - if (ctx->params.rot_mode) - rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; - coda_write(dev, rot_mode, CODA_CMD_ENC_PIC_ROT_MODE); - coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); - - if (dev->devtype->product == CODA_960) { - coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); - coda_write(dev, q_data_src->bytesperline, - CODA9_CMD_ENC_PIC_SRC_STRIDE); - coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); - - reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y; - } else { - reg = CODA_CMD_ENC_PIC_SRC_ADDR_Y; - } - coda_write_base(ctx, q_data_src, src_buf, reg); - - coda_write(dev, force_ipicture << 1 & 0x2, - CODA_CMD_ENC_PIC_OPTION); - - coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); - coda_write(dev, pic_stream_buffer_size / 1024, - CODA_CMD_ENC_PIC_BB_SIZE); - - if (!ctx->streamon_out) { - /* After streamoff on the output side, set stream end flag */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - } - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, ctx->iram_info.axi_sram_use, - CODA7_REG_BIT_AXI_SRAM_USE); - - trace_coda_enc_pic_run(ctx, src_buf); - - coda_command_async(ctx, CODA_COMMAND_PIC_RUN); - - return 0; -} - -static char coda_frame_type_char(u32 flags) -{ - return (flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : - (flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : - (flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '?'; -} - -static void coda_finish_encode(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr, start_ptr; - - if (ctx->aborting) - return; - - /* - * Lock to make sure that an encoder stop command running in parallel - * will either already have marked src_buf as last, or it will wake up - * the capture queue after the buffers are returned. - */ - mutex_lock(&ctx->wakeup_mutex); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - trace_coda_enc_pic_done(ctx, dst_buf); - - /* Get results from the coda */ - start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); - wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); - - /* Calculate bytesused field */ - if (dst_buf->sequence == 0 || - src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]); - } else { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); - } - - coda_dbg(1, ctx, "frame size = %u\n", wr_ptr - start_ptr); - - coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); - coda_read(dev, CODA_RET_ENC_PIC_FLAG); - - dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_LAST); - if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; - dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); - mutex_unlock(&ctx->wakeup_mutex); - - ctx->gopcounter--; - if (ctx->gopcounter < 0) - ctx->gopcounter = ctx->params.gop_size - 1; - - coda_dbg(1, ctx, "job finished: encoded %c frame (%d)%s\n", - coda_frame_type_char(dst_buf->flags), dst_buf->sequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); -} - -static void coda_seq_end_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); - struct coda_dev *dev = ctx->dev; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - if (ctx->initialized == 0) - goto out; - - coda_dbg(1, ctx, "%s: sent command 'SEQ_END' to coda\n", __func__); - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_END failed\n"); - } - - /* - * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing - * from the output stream after the h.264 decoder has run. Resetting the - * hardware after the decoder has finished seems to help. - */ - if (dev->devtype->product == CODA_960) - coda_hw_reset(ctx); - - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - - coda_free_framebuffers(ctx); - - ctx->initialized = 0; - -out: - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); -} - -static void coda_bit_release(struct coda_ctx *ctx) -{ - mutex_lock(&ctx->buffer_mutex); - coda_free_framebuffers(ctx); - coda_free_context_buffers(ctx); - coda_free_bitstream_buffer(ctx); - mutex_unlock(&ctx->buffer_mutex); -} - -const struct coda_context_ops coda_bit_encode_ops = { - .queue_init = coda_encoder_queue_init, - .reqbufs = coda_encoder_reqbufs, - .start_streaming = coda_start_encoding, - .prepare_run = coda_prepare_encode, - .finish_run = coda_finish_encode, - .seq_end_work = coda_seq_end_work, - .release = coda_bit_release, -}; - -/* - * Decoder context operations - */ - -static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, - struct coda_q_data *q_data) -{ - if (ctx->bitstream.vaddr) - return 0; - - ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); - ctx->bitstream.vaddr = dma_alloc_wc(ctx->dev->dev, ctx->bitstream.size, - &ctx->bitstream.paddr, GFP_KERNEL); - if (!ctx->bitstream.vaddr) { - v4l2_err(&ctx->dev->v4l2_dev, - "failed to allocate bitstream ringbuffer"); - return -ENOMEM; - } - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - - return 0; -} - -static void coda_free_bitstream_buffer(struct coda_ctx *ctx) -{ - if (ctx->bitstream.vaddr == NULL) - return; - - dma_free_wc(ctx->dev->dev, ctx->bitstream.size, ctx->bitstream.vaddr, - ctx->bitstream.paddr); - ctx->bitstream.vaddr = NULL; - kfifo_init(&ctx->bitstream_fifo, NULL, 0); -} - -static int coda_decoder_reqbufs(struct coda_ctx *ctx, - struct v4l2_requestbuffers *rb) -{ - struct coda_q_data *q_data_src; - int ret; - - if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return 0; - - if (rb->count) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - ret = coda_alloc_bitstream_buffer(ctx, q_data_src); - if (ret < 0) { - coda_free_context_buffers(ctx); - return ret; - } - } else { - coda_free_bitstream_buffer(ctx); - coda_free_context_buffers(ctx); - } - - return 0; -} - -static bool coda_reorder_enable(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int profile; - - if (dev->devtype->product != CODA_HX4 && - dev->devtype->product != CODA_7541 && - dev->devtype->product != CODA_960) - return false; - - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) - return false; - - if (ctx->codec->src_fourcc != V4L2_PIX_FMT_H264) - return true; - - profile = coda_h264_profile(ctx->params.h264_profile_idc); - if (profile < 0) - v4l2_warn(&dev->v4l2_dev, "Unknown H264 Profile: %u\n", - ctx->params.h264_profile_idc); - - /* Baseline profile does not support reordering */ - return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; -} - -static void coda_decoder_drop_used_metas(struct coda_ctx *ctx) -{ - struct coda_buffer_meta *meta, *tmp; - - /* - * All metas that end at or before the RD pointer (fifo out), - * are now consumed by the VPU and should be released. - */ - spin_lock(&ctx->buffer_meta_lock); - list_for_each_entry_safe(meta, tmp, &ctx->buffer_meta_list, list) { - if (ctx->bitstream_fifo.kfifo.out >= meta->end) { - coda_dbg(2, ctx, "releasing meta: seq=%d start=%d end=%d\n", - meta->sequence, meta->start, meta->end); - - list_del(&meta->list); - ctx->num_metas--; - ctx->first_frame_sequence++; - kfree(meta); - } - } - spin_unlock(&ctx->buffer_meta_lock); -} - -static int __coda_decoder_seq_init(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - u32 bitstream_buf, bitstream_size; - struct coda_dev *dev = ctx->dev; - int width, height; - u32 src_fourcc, dst_fourcc; - u32 val; - int ret; - - lockdep_assert_held(&dev->coda_mutex); - - coda_dbg(1, ctx, "Video Data Order Adapter: %s\n", - ctx->use_vdoa ? "Enabled" : "Disabled"); - - /* Start decoding */ - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - bitstream_buf = ctx->bitstream.paddr; - bitstream_size = ctx->bitstream.size; - src_fourcc = q_data_src->fourcc; - dst_fourcc = q_data_dst->fourcc; - - /* Update coda bitstream read and write pointers from kfifo */ - coda_kfifo_sync_to_device_full(ctx); - - ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | - CODA9_FRAME_TILED2LINEAR); - if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) - ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ctx->frame_mem_ctrl |= (0x3 << 9) | - ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); - coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); - - ctx->display_idx = -1; - ctx->frm_dis_flg = 0; - coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - - coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); - coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); - val = 0; - if (coda_reorder_enable(ctx)) - val |= CODA_REORDER_ENABLE; - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) - val |= CODA_NO_INT_ENABLE; - coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); - - ctx->params.codec_mode = ctx->codec->mode; - if (dev->devtype->product == CODA_960 && - src_fourcc == V4L2_PIX_FMT_MPEG4) - ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; - else - ctx->params.codec_mode_aux = 0; - if (src_fourcc == V4L2_PIX_FMT_MPEG4) { - coda_write(dev, CODA_MP4_CLASS_MPEG4, - CODA_CMD_DEC_SEQ_MP4_ASP_CLASS); - } - if (src_fourcc == V4L2_PIX_FMT_H264) { - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - coda_write(dev, ctx->psbuf.paddr, - CODA_CMD_DEC_SEQ_PS_BB_START); - coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), - CODA_CMD_DEC_SEQ_PS_BB_SIZE); - } - if (dev->devtype->product == CODA_960) { - coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); - coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); - } - } - if (src_fourcc == V4L2_PIX_FMT_JPEG) - coda_write(dev, 0, CODA_CMD_DEC_SEQ_JPG_THUMB_EN); - if (dev->devtype->product != CODA_960) - coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); - - ctx->bit_stream_param = CODA_BIT_DEC_SEQ_INIT_ESCAPE; - ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); - ctx->bit_stream_param = 0; - if (ret) { - v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - return ret; - } - ctx->sequence_offset = ~0U; - ctx->initialized = 1; - ctx->first_frame_sequence = 0; - - /* Update kfifo out pointer from coda bitstream read pointer */ - coda_kfifo_sync_from_device(ctx); - - /* - * After updating the read pointer, we need to check if - * any metas are consumed and should be released. - */ - coda_decoder_drop_used_metas(ctx); - - if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_INIT failed, error code = 0x%x\n", - coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); - return -EAGAIN; - } - - val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); - if (dev->devtype->product == CODA_DX6) { - width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; - height = val & CODADX6_PICHEIGHT_MASK; - } else { - width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; - height = val & CODA7_PICHEIGHT_MASK; - } - - if (width > q_data_dst->bytesperline || height > q_data_dst->height) { - v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", - width, height, q_data_dst->bytesperline, - q_data_dst->height); - return -EINVAL; - } - - width = round_up(width, 16); - height = round_up(height, 16); - - coda_dbg(1, ctx, "start decoding: %dx%d\n", width, height); - - ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); - /* - * If the VDOA is used, the decoder needs one additional frame, - * because the frames are freed when the next frame is decoded. - * Otherwise there are visible errors in the decoded frames (green - * regions in displayed frames) and a broken order of frames (earlier - * frames are sporadically displayed after later frames). - */ - if (ctx->use_vdoa) - ctx->num_internal_frames += 1; - if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { - v4l2_err(&dev->v4l2_dev, - "not enough framebuffers to decode (%d < %d)\n", - CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); - return -EINVAL; - } - - if (src_fourcc == V4L2_PIX_FMT_H264) { - u32 left_right; - u32 top_bottom; - - left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); - top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); - - q_data_dst->rect.left = (left_right >> 10) & 0x3ff; - q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; - q_data_dst->rect.width = width - q_data_dst->rect.left - - (left_right & 0x3ff); - q_data_dst->rect.height = height - q_data_dst->rect.top - - (top_bottom & 0x3ff); - } - - if (dev->devtype->product != CODA_DX6) { - u8 profile, level; - - val = coda_read(dev, CODA7_RET_DEC_SEQ_HEADER_REPORT); - profile = val & 0xff; - level = (val >> 8) & 0x7f; - - if (profile || level) - coda_update_profile_level_ctrls(ctx, profile, level); - } - - return 0; -} - -static void coda_dec_seq_init_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, - struct coda_ctx, seq_init_work); - struct coda_dev *dev = ctx->dev; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - if (!ctx->initialized) - __coda_decoder_seq_init(ctx); - - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); -} - -static int __coda_start_decoding(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - struct coda_dev *dev = ctx->dev; - u32 src_fourcc, dst_fourcc; - int ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - src_fourcc = q_data_src->fourcc; - dst_fourcc = q_data_dst->fourcc; - - if (!ctx->initialized) { - ret = __coda_decoder_seq_init(ctx); - if (ret < 0) - return ret; - } else { - ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | - CODA9_FRAME_TILED2LINEAR); - if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) - ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ctx->frame_mem_ctrl |= (0x3 << 9) | - ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); - } - - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - - ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); - return ret; - } - - /* Tell the decoder how many frame buffers we allocated. */ - coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, round_up(q_data_dst->rect.width, 16), - CODA_CMD_SET_FRAME_BUF_STRIDE); - - if (dev->devtype->product != CODA_DX6) { - /* Set secondary AXI IRAM */ - coda_setup_iram(ctx); - - coda_write(dev, ctx->iram_info.buf_bit_use, - CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, - CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_y_use, - CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_c_use, - CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, ctx->iram_info.buf_ovl_use, - CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) { - coda_write(dev, ctx->iram_info.buf_btp_use, - CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - - coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); - coda9_set_frame_cache(ctx, dst_fourcc); - } - } - - if (src_fourcc == V4L2_PIX_FMT_H264) { - coda_write(dev, ctx->slicebuf.paddr, - CODA_CMD_SET_FRAME_SLICE_BB_START); - coda_write(dev, ctx->slicebuf.size / 1024, - CODA_CMD_SET_FRAME_SLICE_BB_SIZE); - } - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - int max_mb_x = 1920 / 16; - int max_mb_y = 1088 / 16; - int max_mb_num = max_mb_x * max_mb_y; - - coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, - CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); - } else if (dev->devtype->product == CODA_960) { - int max_mb_x = 1920 / 16; - int max_mb_y = 1088 / 16; - int max_mb_num = max_mb_x * max_mb_y; - - coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, - CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); - } - - if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { - v4l2_err(&ctx->dev->v4l2_dev, - "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int coda_start_decoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int ret; - - mutex_lock(&dev->coda_mutex); - ret = __coda_start_decoding(ctx); - mutex_unlock(&dev->coda_mutex); - - return ret; -} - -static int coda_prepare_decode(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *dst_buf; - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_dst; - struct coda_buffer_meta *meta; - u32 rot_mode = 0; - u32 reg_addr, reg_stride; - - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - /* Try to copy source buffer contents into the bitstream ringbuffer */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); - - if (coda_get_bitstream_payload(ctx) < 512 && - (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { - coda_dbg(1, ctx, "bitstream payload: %d, skipping\n", - coda_get_bitstream_payload(ctx)); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return -EAGAIN; - } - - /* Run coda_start_decoding (again) if not yet initialized */ - if (!ctx->initialized) { - int ret = __coda_start_decoding(ctx); - - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return -EAGAIN; - } else { - ctx->initialized = 1; - } - } - - if (dev->devtype->product == CODA_960) - coda_set_gdi_regs(ctx); - - if (ctx->use_vdoa && - ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - vdoa_device_run(ctx->vdoa, - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0), - ctx->internal_frames[ctx->display_idx].buf.paddr); - } else { - if (dev->devtype->product == CODA_960) { - /* - * It was previously assumed that the CODA960 has an - * internal list of 64 buffer entries that contains - * both the registered internal frame buffers as well - * as the rotator buffer output, and that the ROT_INDEX - * register must be set to a value between the last - * internal frame buffers' index and 64. - * At least on firmware version 3.1.1 it turns out that - * setting ROT_INDEX to any value >= 32 causes CODA - * hangups that it can not recover from with the SRC VPU - * reset. - * It does appear to work however, to just set it to a - * fixed value in the [ctx->num_internal_frames, 31] - * range, for example CODA_MAX_FRAMEBUFFERS. - */ - coda_write(dev, CODA_MAX_FRAMEBUFFERS, - CODA9_CMD_DEC_PIC_ROT_INDEX); - - reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y; - reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE; - } else { - reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y; - reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE; - } - coda_write_base(ctx, q_data_dst, dst_buf, reg_addr); - coda_write(dev, q_data_dst->bytesperline, reg_stride); - - rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; - } - - coda_write(dev, rot_mode, CODA_CMD_DEC_PIC_ROT_MODE); - - switch (dev->devtype->product) { - case CODA_DX6: - /* TBD */ - case CODA_HX4: - case CODA_7541: - coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); - break; - case CODA_960: - /* 'hardcode to use interrupt disable mode'? */ - coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); - break; - } - - coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); - - coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); - coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, ctx->iram_info.axi_sram_use, - CODA7_REG_BIT_AXI_SRAM_USE); - - spin_lock(&ctx->buffer_meta_lock); - meta = list_first_entry_or_null(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - - if (meta && ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) { - - /* If this is the last buffer in the bitstream, add padding */ - if (meta->end == ctx->bitstream_fifo.kfifo.in) { - static unsigned char buf[512]; - unsigned int pad; - - /* Pad to multiple of 256 and then add 256 more */ - pad = ((0 - meta->end) & 0xff) + 256; - - memset(buf, 0xff, sizeof(buf)); - - kfifo_in(&ctx->bitstream_fifo, buf, pad); - } - } - spin_unlock(&ctx->buffer_meta_lock); - - coda_kfifo_sync_to_device_full(ctx); - - /* Clear decode success flag */ - coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); - - /* Clear error return value */ - coda_write(dev, 0, CODA_RET_DEC_PIC_ERR_MB); - - trace_coda_dec_pic_run(ctx, meta); - - coda_command_async(ctx, CODA_COMMAND_PIC_RUN); - - return 0; -} - -static void coda_finish_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_src; - struct coda_q_data *q_data_dst; - struct vb2_v4l2_buffer *dst_buf; - struct coda_buffer_meta *meta; - int width, height; - int decoded_idx; - int display_idx; - struct coda_internal_frame *decoded_frame = NULL; - u32 src_fourcc; - int success; - u32 err_mb; - int err_vdoa = 0; - u32 val; - - if (ctx->aborting) - return; - - /* Update kfifo out pointer from coda bitstream read pointer */ - coda_kfifo_sync_from_device(ctx); - - /* - * in stream-end mode, the read pointer can overshoot the write pointer - * by up to 512 bytes - */ - if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { - if (coda_get_bitstream_payload(ctx) >= ctx->bitstream.size - 512) - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - } - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - src_fourcc = q_data_src->fourcc; - - val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); - if (val != 1) - pr_err("DEC_PIC_SUCCESS = %d\n", val); - - success = val & 0x1; - if (!success) - v4l2_err(&dev->v4l2_dev, "decode failed\n"); - - if (src_fourcc == V4L2_PIX_FMT_H264) { - if (val & (1 << 3)) - v4l2_err(&dev->v4l2_dev, - "insufficient PS buffer space (%d bytes)\n", - ctx->psbuf.size); - if (val & (1 << 2)) - v4l2_err(&dev->v4l2_dev, - "insufficient slice buffer space (%d bytes)\n", - ctx->slicebuf.size); - } - - val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); - width = (val >> 16) & 0xffff; - height = val & 0xffff; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - /* frame crop information */ - if (src_fourcc == V4L2_PIX_FMT_H264) { - u32 left_right; - u32 top_bottom; - - left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); - top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); - - if (left_right == 0xffffffff && top_bottom == 0xffffffff) { - /* Keep current crop information */ - } else { - struct v4l2_rect *rect = &q_data_dst->rect; - - rect->left = left_right >> 16 & 0xffff; - rect->top = top_bottom >> 16 & 0xffff; - rect->width = width - rect->left - - (left_right & 0xffff); - rect->height = height - rect->top - - (top_bottom & 0xffff); - } - } else { - /* no cropping */ - } - - err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); - if (err_mb > 0) { - if (__ratelimit(&dev->mb_err_rs)) - coda_dbg(1, ctx, "errors in %d macroblocks\n", err_mb); - v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, - v4l2_ctrl_g_ctrl(ctx->mb_err_cnt_ctrl) + err_mb); - } - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); - if (val == 0) { - /* not enough bitstream data */ - coda_dbg(1, ctx, "prescan failed: %d\n", val); - ctx->hold = true; - return; - } - } - - /* Wait until the VDOA finished writing the previous display frame */ - if (ctx->use_vdoa && - ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - err_vdoa = vdoa_wait_for_completion(ctx->vdoa); - } - - ctx->frm_dis_flg = coda_read(dev, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - - /* The previous display frame was copied out and can be overwritten */ - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - ctx->frm_dis_flg &= ~(1 << ctx->display_idx); - coda_write(dev, ctx->frm_dis_flg, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - } - - /* - * The index of the last decoded frame, not necessarily in - * display order, and the index of the next display frame. - * The latter could have been decoded in a previous run. - */ - decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); - display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); - - if (decoded_idx == -1) { - /* no frame was decoded, but we might have a display frame */ - if (display_idx >= 0 && display_idx < ctx->num_internal_frames) - ctx->sequence_offset++; - else if (ctx->display_idx < 0) - ctx->hold = true; - } else if (decoded_idx == -2) { - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) - ctx->sequence_offset++; - /* no frame was decoded, we still return remaining buffers */ - } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { - v4l2_err(&dev->v4l2_dev, - "decoded frame index out of range: %d\n", decoded_idx); - } else { - int sequence; - - decoded_frame = &ctx->internal_frames[decoded_idx]; - - val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM); - if (ctx->sequence_offset == -1) - ctx->sequence_offset = val; - - sequence = val + ctx->first_frame_sequence - - ctx->sequence_offset; - spin_lock(&ctx->buffer_meta_lock); - if (!list_empty(&ctx->buffer_meta_list)) { - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - list_del(&meta->list); - ctx->num_metas--; - spin_unlock(&ctx->buffer_meta_lock); - /* - * Clamp counters to 16 bits for comparison, as the HW - * counter rolls over at this point for h.264. This - * may be different for other formats, but using 16 bits - * should be enough to detect most errors and saves us - * from doing different things based on the format. - */ - if ((sequence & 0xffff) != (meta->sequence & 0xffff)) { - v4l2_err(&dev->v4l2_dev, - "sequence number mismatch (%d(%d) != %d)\n", - sequence, ctx->sequence_offset, - meta->sequence); - } - decoded_frame->meta = *meta; - kfree(meta); - } else { - spin_unlock(&ctx->buffer_meta_lock); - v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); - memset(&decoded_frame->meta, 0, - sizeof(struct coda_buffer_meta)); - decoded_frame->meta.sequence = sequence; - decoded_frame->meta.last = false; - ctx->sequence_offset++; - } - - trace_coda_dec_pic_done(ctx, &decoded_frame->meta); - - val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; - decoded_frame->type = (val == 0) ? V4L2_BUF_FLAG_KEYFRAME : - (val == 1) ? V4L2_BUF_FLAG_PFRAME : - V4L2_BUF_FLAG_BFRAME; - - decoded_frame->error = err_mb; - } - - if (display_idx == -1) { - /* - * no more frames to be decoded, but there could still - * be rotator output to dequeue - */ - ctx->hold = true; - } else if (display_idx == -3) { - /* possibly prescan failure */ - } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { - v4l2_err(&dev->v4l2_dev, - "presentation frame index out of range: %d\n", - display_idx); - } - - /* If a frame was copied out, return it */ - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - struct coda_internal_frame *ready_frame; - - ready_frame = &ctx->internal_frames[ctx->display_idx]; - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->sequence = ctx->osequence++; - - dst_buf->field = V4L2_FIELD_NONE; - dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME); - dst_buf->flags |= ready_frame->type; - meta = &ready_frame->meta; - if (meta->last && !coda_reorder_enable(ctx)) { - /* - * If this was the last decoded frame, and reordering - * is disabled, this will be the last display frame. - */ - coda_dbg(1, ctx, "last meta, marking as last frame\n"); - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - } else if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG && - display_idx == -1) { - /* - * If there is no designated presentation frame anymore, - * this frame has to be the last one. - */ - coda_dbg(1, ctx, - "no more frames to return, marking as last frame\n"); - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - } - dst_buf->timecode = meta->timecode; - dst_buf->vb2_buf.timestamp = meta->timestamp; - - trace_coda_dec_rot_done(ctx, dst_buf, meta); - - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - q_data_dst->sizeimage); - - if (ready_frame->error || err_vdoa) - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); - else - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); - - if (decoded_frame) { - coda_dbg(1, ctx, "job finished: decoded %c frame %u, returned %c frame %u (%u/%u)%s\n", - coda_frame_type_char(decoded_frame->type), - decoded_frame->meta.sequence, - coda_frame_type_char(dst_buf->flags), - ready_frame->meta.sequence, - dst_buf->sequence, ctx->qsequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? - " (last)" : ""); - } else { - coda_dbg(1, ctx, "job finished: no frame decoded (%d), returned %c frame %u (%u/%u)%s\n", - decoded_idx, - coda_frame_type_char(dst_buf->flags), - ready_frame->meta.sequence, - dst_buf->sequence, ctx->qsequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? - " (last)" : ""); - } - } else { - if (decoded_frame) { - coda_dbg(1, ctx, "job finished: decoded %c frame %u, no frame returned (%d)\n", - coda_frame_type_char(decoded_frame->type), - decoded_frame->meta.sequence, - ctx->display_idx); - } else { - coda_dbg(1, ctx, "job finished: no frame decoded (%d) or returned (%d)\n", - decoded_idx, ctx->display_idx); - } - } - - /* The rotator will copy the current display frame next time */ - ctx->display_idx = display_idx; - - /* - * The current decode run might have brought the bitstream fill level - * below the size where we can start the next decode run. As userspace - * might have filled the output queue completely and might thus be - * blocked, we can't rely on the next qbuf to trigger the bitstream - * refill. Check if we have data to refill the bitstream now. - */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); -} - -static void coda_decode_timeout(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *dst_buf; - - /* - * For now this only handles the case where we would deadlock with - * userspace, i.e. userspace issued DEC_CMD_STOP and waits for EOS, - * but after a failed decode run we would hold the context and wait for - * userspace to queue more buffers. - */ - if (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG)) - return; - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->sequence = ctx->qsequence - 1; - - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); -} - -const struct coda_context_ops coda_bit_decode_ops = { - .queue_init = coda_decoder_queue_init, - .reqbufs = coda_decoder_reqbufs, - .start_streaming = coda_start_decoding, - .prepare_run = coda_prepare_decode, - .finish_run = coda_finish_decode, - .run_timeout = coda_decode_timeout, - .seq_init_work = coda_dec_seq_init_work, - .seq_end_work = coda_seq_end_work, - .release = coda_bit_release, -}; - -irqreturn_t coda_irq_handler(int irq, void *data) -{ - struct coda_dev *dev = data; - struct coda_ctx *ctx; - - /* read status register to attend the IRQ */ - coda_read(dev, CODA_REG_BIT_INT_STATUS); - coda_write(dev, 0, CODA_REG_BIT_INT_REASON); - coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, - CODA_REG_BIT_INT_CLEAR); - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (ctx == NULL) { - v4l2_err(&dev->v4l2_dev, - "Instance released before the end of transaction\n"); - return IRQ_HANDLED; - } - - trace_coda_bit_done(ctx); - - if (ctx->aborting) { - coda_dbg(1, ctx, "task has been aborted\n"); - } - - if (coda_isbusy(ctx->dev)) { - coda_dbg(1, ctx, "coda is still busy!!!!\n"); - return IRQ_NONE; - } - - complete(&ctx->completion); - - return IRQ_HANDLED; -} diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c deleted file mode 100644 index a57822b05070..000000000000 --- a/drivers/media/platform/coda/coda-common.c +++ /dev/null @@ -1,3341 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "coda.h" -#include "imx-vdoa.h" - -#define CODA_NAME "coda" - -#define CODADX6_MAX_INSTANCES 4 -#define CODA_MAX_FORMATS 5 - -#define CODA_ISRAM_SIZE (2048 * 2) - -#define MIN_W 48 -#define MIN_H 16 - -#define S_ALIGN 1 /* multiple of 2 */ -#define W_ALIGN 1 /* multiple of 2 */ -#define H_ALIGN 1 /* multiple of 2 */ - -#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) - -int coda_debug; -module_param(coda_debug, int, 0644); -MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); - -static int disable_tiling; -module_param(disable_tiling, int, 0644); -MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); - -static int disable_vdoa; -module_param(disable_vdoa, int, 0644); -MODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion"); - -static int enable_bwb = 0; -module_param(enable_bwb, int, 0644); -MODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain streams"); - -void coda_write(struct coda_dev *dev, u32 data, u32 reg) -{ - v4l2_dbg(3, coda_debug, &dev->v4l2_dev, - "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); - writel(data, dev->regs_base + reg); -} - -unsigned int coda_read(struct coda_dev *dev, u32 reg) -{ - u32 data; - - data = readl(dev->regs_base + reg); - v4l2_dbg(3, coda_debug, &dev->v4l2_dev, - "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); - return data; -} - -void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, - struct vb2_v4l2_buffer *buf, unsigned int reg_y) -{ - u32 base_y = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); - u32 base_cb, base_cr; - - switch (q_data->fourcc) { - case V4L2_PIX_FMT_YUYV: - /* Fallthrough: IN -H264-> CODA -NV12 MB-> VDOA -YUYV-> OUT */ - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_YUV420: - default: - base_cb = base_y + q_data->bytesperline * q_data->height; - base_cr = base_cb + q_data->bytesperline * q_data->height / 4; - break; - case V4L2_PIX_FMT_YVU420: - /* Switch Cb and Cr for YVU420 format */ - base_cr = base_y + q_data->bytesperline * q_data->height; - base_cb = base_cr + q_data->bytesperline * q_data->height / 4; - break; - case V4L2_PIX_FMT_YUV422P: - base_cb = base_y + q_data->bytesperline * q_data->height; - base_cr = base_cb + q_data->bytesperline * q_data->height / 2; - } - - coda_write(ctx->dev, base_y, reg_y); - coda_write(ctx->dev, base_cb, reg_y + 4); - coda_write(ctx->dev, base_cr, reg_y + 8); -} - -#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ - { mode, src_fourcc, dst_fourcc, max_w, max_h } - -/* - * Arrays of codecs supported by each given version of Coda: - * i.MX27 -> codadx6 - * i.MX51 -> codahx4 - * i.MX53 -> coda7 - * i.MX6 -> coda960 - * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants - */ -static const struct coda_codec codadx6_codecs[] = { - CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), - CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), -}; - -static const struct coda_codec codahx4_codecs[] = { - CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), - CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720), -}; - -static const struct coda_codec coda7_codecs[] = { - CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), - CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), - CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), - CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), -}; - -static const struct coda_codec coda9_codecs[] = { - CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088), - CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088), - CODA_CODEC(CODA9_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), - CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA9_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), -}; - -struct coda_video_device { - const char *name; - enum coda_inst_type type; - const struct coda_context_ops *ops; - bool direct; - u32 src_formats[CODA_MAX_FORMATS]; - u32 dst_formats[CODA_MAX_FORMATS]; -}; - -static const struct coda_video_device coda_bit_encoder = { - .name = "coda-video-encoder", - .type = CODA_INST_ENCODER, - .ops = &coda_bit_encode_ops, - .src_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - }, - .dst_formats = { - V4L2_PIX_FMT_H264, - V4L2_PIX_FMT_MPEG4, - }, -}; - -static const struct coda_video_device coda_bit_jpeg_encoder = { - .name = "coda-jpeg-encoder", - .type = CODA_INST_ENCODER, - .ops = &coda_bit_encode_ops, - .src_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - }, - .dst_formats = { - V4L2_PIX_FMT_JPEG, - }, -}; - -static const struct coda_video_device coda_bit_decoder = { - .name = "coda-video-decoder", - .type = CODA_INST_DECODER, - .ops = &coda_bit_decode_ops, - .src_formats = { - V4L2_PIX_FMT_H264, - V4L2_PIX_FMT_MPEG2, - V4L2_PIX_FMT_MPEG4, - }, - .dst_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - /* - * If V4L2_PIX_FMT_YUYV should be default, - * set_default_params() must be adjusted. - */ - V4L2_PIX_FMT_YUYV, - }, -}; - -static const struct coda_video_device coda_bit_jpeg_decoder = { - .name = "coda-jpeg-decoder", - .type = CODA_INST_DECODER, - .ops = &coda_bit_decode_ops, - .src_formats = { - V4L2_PIX_FMT_JPEG, - }, - .dst_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - }, -}; - -static const struct coda_video_device coda9_jpeg_encoder = { - .name = "coda-jpeg-encoder", - .type = CODA_INST_ENCODER, - .ops = &coda9_jpeg_encode_ops, - .direct = true, - .src_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - V4L2_PIX_FMT_GREY, - }, - .dst_formats = { - V4L2_PIX_FMT_JPEG, - }, -}; - -static const struct coda_video_device coda9_jpeg_decoder = { - .name = "coda-jpeg-decoder", - .type = CODA_INST_DECODER, - .ops = &coda9_jpeg_decode_ops, - .direct = true, - .src_formats = { - V4L2_PIX_FMT_JPEG, - }, - .dst_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - }, -}; - -static const struct coda_video_device *codadx6_video_devices[] = { - &coda_bit_encoder, -}; - -static const struct coda_video_device *codahx4_video_devices[] = { - &coda_bit_encoder, - &coda_bit_decoder, -}; - -static const struct coda_video_device *coda7_video_devices[] = { - &coda_bit_jpeg_encoder, - &coda_bit_jpeg_decoder, - &coda_bit_encoder, - &coda_bit_decoder, -}; - -static const struct coda_video_device *coda9_video_devices[] = { - &coda9_jpeg_encoder, - &coda9_jpeg_decoder, - &coda_bit_encoder, - &coda_bit_decoder, -}; - -/* - * Normalize all supported YUV 4:2:0 formats to the value used in the codec - * tables. - */ -static u32 coda_format_normalize_yuv(u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_YUYV: - return V4L2_PIX_FMT_YUV420; - default: - return fourcc; - } -} - -static const struct coda_codec *coda_find_codec(struct coda_dev *dev, - int src_fourcc, int dst_fourcc) -{ - const struct coda_codec *codecs = dev->devtype->codecs; - int num_codecs = dev->devtype->num_codecs; - int k; - - src_fourcc = coda_format_normalize_yuv(src_fourcc); - dst_fourcc = coda_format_normalize_yuv(dst_fourcc); - if (src_fourcc == dst_fourcc) - return NULL; - - for (k = 0; k < num_codecs; k++) { - if (codecs[k].src_fourcc == src_fourcc && - codecs[k].dst_fourcc == dst_fourcc) - break; - } - - if (k == num_codecs) - return NULL; - - return &codecs[k]; -} - -static void coda_get_max_dimensions(struct coda_dev *dev, - const struct coda_codec *codec, - int *max_w, int *max_h) -{ - const struct coda_codec *codecs = dev->devtype->codecs; - int num_codecs = dev->devtype->num_codecs; - unsigned int w, h; - int k; - - if (codec) { - w = codec->max_w; - h = codec->max_h; - } else { - for (k = 0, w = 0, h = 0; k < num_codecs; k++) { - w = max(w, codecs[k].max_w); - h = max(h, codecs[k].max_h); - } - } - - if (max_w) - *max_w = w; - if (max_h) - *max_h = h; -} - -static const struct coda_video_device *to_coda_video_device(struct video_device - *vdev) -{ - struct coda_dev *dev = video_get_drvdata(vdev); - unsigned int i = vdev - dev->vfd; - - if (i >= dev->devtype->num_vdevs) - return NULL; - - return dev->devtype->vdevs[i]; -} - -const char *coda_product_name(int product) -{ - static char buf[9]; - - switch (product) { - case CODA_DX6: - return "CodaDx6"; - case CODA_HX4: - return "CodaHx4"; - case CODA_7541: - return "CODA7541"; - case CODA_960: - return "CODA960"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", product); - return buf; - } -} - -static struct vdoa_data *coda_get_vdoa_data(void) -{ - struct device_node *vdoa_node; - struct platform_device *vdoa_pdev; - struct vdoa_data *vdoa_data = NULL; - - vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa"); - if (!vdoa_node) - return NULL; - - vdoa_pdev = of_find_device_by_node(vdoa_node); - if (!vdoa_pdev) - goto out; - - vdoa_data = platform_get_drvdata(vdoa_pdev); - if (!vdoa_data) - vdoa_data = ERR_PTR(-EPROBE_DEFER); - - put_device(&vdoa_pdev->dev); -out: - of_node_put(vdoa_node); - - return vdoa_data; -} - -/* - * V4L2 ioctl() operations. - */ -static int coda_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - strscpy(cap->driver, CODA_NAME, sizeof(cap->driver)); - strscpy(cap->card, coda_product_name(ctx->dev->devtype->product), - sizeof(cap->card)); - strscpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); - return 0; -} - -static const u32 coda_formats_420[CODA_MAX_FORMATS] = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, -}; - -static int coda_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct video_device *vdev = video_devdata(file); - const struct coda_video_device *cvd = to_coda_video_device(vdev); - struct coda_ctx *ctx = fh_to_ctx(priv); - const u32 *formats; - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - formats = cvd->src_formats; - else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - struct coda_q_data *q_data_src; - struct vb2_queue *src_vq; - - formats = cvd->dst_formats; - - /* - * If the source format is already fixed, only allow the same - * chroma subsampling. - */ - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && - vb2_is_streaming(src_vq)) { - if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_420) { - formats = coda_formats_420; - } else if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_422) { - f->pixelformat = V4L2_PIX_FMT_YUV422P; - return f->index ? -EINVAL : 0; - } - } - } else { - return -EINVAL; - } - - if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) - return -EINVAL; - - /* Skip YUYV if the vdoa is not available */ - if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - formats[f->index] == V4L2_PIX_FMT_YUYV) - return -EINVAL; - - f->pixelformat = formats[f->index]; - - return 0; -} - -static int coda_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_q_data *q_data; - struct coda_ctx *ctx = fh_to_ctx(priv); - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fourcc; - f->fmt.pix.width = q_data->width; - f->fmt.pix.height = q_data->height; - f->fmt.pix.bytesperline = q_data->bytesperline; - - f->fmt.pix.sizeimage = q_data->sizeimage; - f->fmt.pix.colorspace = ctx->colorspace; - f->fmt.pix.xfer_func = ctx->xfer_func; - f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; - f->fmt.pix.quantization = ctx->quantization; - - return 0; -} - -static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f) -{ - struct coda_q_data *q_data; - const u32 *formats; - int i; - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - formats = ctx->cvd->src_formats; - else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - formats = ctx->cvd->dst_formats; - else - return -EINVAL; - - for (i = 0; i < CODA_MAX_FORMATS; i++) { - /* Skip YUYV if the vdoa is not available */ - if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - formats[i] == V4L2_PIX_FMT_YUYV) - continue; - - if (formats[i] == f->fmt.pix.pixelformat) { - f->fmt.pix.pixelformat = formats[i]; - return 0; - } - } - - /* Fall back to currently set pixelformat */ - q_data = get_q_data(ctx, f->type); - f->fmt.pix.pixelformat = q_data->fourcc; - - return 0; -} - -static int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f, - bool *use_vdoa) -{ - int err; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (!use_vdoa) - return -EINVAL; - - if (!ctx->vdoa) { - *use_vdoa = false; - return 0; - } - - err = vdoa_context_configure(NULL, round_up(f->fmt.pix.width, 16), - f->fmt.pix.height, f->fmt.pix.pixelformat); - if (err) { - *use_vdoa = false; - return 0; - } - - *use_vdoa = true; - return 0; -} - -static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage, - u32 width, u32 height) -{ - /* - * This is a rough estimate for sensible compressed buffer - * sizes (between 1 and 16 bits per pixel). This could be - * improved by better format specific worst case estimates. - */ - return round_up(clamp(sizeimage, width * height / 8, - width * height * 2), PAGE_SIZE); -} - -static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, - struct v4l2_format *f) -{ - struct coda_dev *dev = ctx->dev; - unsigned int max_w, max_h; - enum v4l2_field field; - - field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - field = V4L2_FIELD_NONE; - else if (V4L2_FIELD_NONE != field) - return -EINVAL; - - /* V4L2 specification suggests the driver corrects the format struct - * if any of the dimensions is unsupported */ - f->fmt.pix.field = field; - - coda_get_max_dimensions(dev, codec, &max_w, &max_h); - v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, - &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, - S_ALIGN); - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - /* - * Frame stride must be at least multiple of 8, - * but multiple of 16 for h.264 or JPEG 4:2:x - */ - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; - break; - case V4L2_PIX_FMT_YUYV: - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height; - break; - case V4L2_PIX_FMT_YUV422P: - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 2; - break; - case V4L2_PIX_FMT_GREY: - /* keep 16 pixel alignment of 8-bit pixel data */ - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; - break; - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_H264: - case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_MPEG2: - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx, - f->fmt.pix.sizeimage, - f->fmt.pix.width, - f->fmt.pix.height); - break; - default: - BUG(); - } - - return 0; -} - -static int coda_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - const struct coda_q_data *q_data_src; - const struct coda_codec *codec; - struct vb2_queue *src_vq; - int ret; - bool use_vdoa; - - ret = coda_try_pixelformat(ctx, f); - if (ret < 0) - return ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - /* - * If the source format is already fixed, only allow the same output - * resolution. When decoding JPEG images, we also have to make sure to - * use the same chroma subsampling. - */ - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (vb2_is_streaming(src_vq)) { - f->fmt.pix.width = q_data_src->width; - f->fmt.pix.height = q_data_src->height; - - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { - if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_420 && - f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; - else if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_422) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; - } - } - - f->fmt.pix.colorspace = ctx->colorspace; - f->fmt.pix.xfer_func = ctx->xfer_func; - f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; - f->fmt.pix.quantization = ctx->quantization; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - codec = coda_find_codec(ctx->dev, q_data_src->fourcc, - f->fmt.pix.pixelformat); - if (!codec) - return -EINVAL; - - ret = coda_try_fmt(ctx, codec, f); - if (ret < 0) - return ret; - - /* The decoders always write complete macroblocks or MCUs */ - if (ctx->inst_type == CODA_INST_DECODER) { - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.height = round_up(f->fmt.pix.height, 16); - if (codec->src_fourcc == V4L2_PIX_FMT_JPEG && - f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 2; - } else { - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; - } - - ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa); - if (ret < 0) - return ret; - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { - if (!use_vdoa) - return -EINVAL; - - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height; - } - } - - return 0; -} - -static void coda_set_default_colorspace(struct v4l2_pix_format *fmt) -{ - enum v4l2_colorspace colorspace; - - if (fmt->pixelformat == V4L2_PIX_FMT_JPEG) - colorspace = V4L2_COLORSPACE_JPEG; - else if (fmt->width <= 720 && fmt->height <= 576) - colorspace = V4L2_COLORSPACE_SMPTE170M; - else - colorspace = V4L2_COLORSPACE_REC709; - - fmt->colorspace = colorspace; - fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - fmt->quantization = V4L2_QUANTIZATION_DEFAULT; -} - -static int coda_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_dev *dev = ctx->dev; - const struct coda_q_data *q_data_dst; - const struct coda_codec *codec; - int ret; - - ret = coda_try_pixelformat(ctx, f); - if (ret < 0) - return ret; - - if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) - coda_set_default_colorspace(&f->fmt.pix); - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc); - - return coda_try_fmt(ctx, codec, f); -} - -static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, - struct v4l2_rect *r) -{ - struct coda_q_data *q_data; - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - if (vb2_is_busy(vq)) { - v4l2_err(&ctx->dev->v4l2_dev, "%s: %s queue busy: %d\n", - __func__, v4l2_type_names[f->type], vq->num_buffers); - return -EBUSY; - } - - q_data->fourcc = f->fmt.pix.pixelformat; - q_data->width = f->fmt.pix.width; - q_data->height = f->fmt.pix.height; - q_data->bytesperline = f->fmt.pix.bytesperline; - q_data->sizeimage = f->fmt.pix.sizeimage; - if (r) { - q_data->rect = *r; - } else { - q_data->rect.left = 0; - q_data->rect.top = 0; - q_data->rect.width = f->fmt.pix.width; - q_data->rect.height = f->fmt.pix.height; - } - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: - ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; - break; - case V4L2_PIX_FMT_NV12: - if (!disable_tiling && ctx->use_bit && - ctx->dev->devtype->product == CODA_960) { - ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; - break; - } - fallthrough; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; - break; - default: - break; - } - - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP && - !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) && - ctx->use_vdoa) - vdoa_context_configure(ctx->vdoa, - round_up(f->fmt.pix.width, 16), - f->fmt.pix.height, - f->fmt.pix.pixelformat); - else - ctx->use_vdoa = false; - - coda_dbg(1, ctx, "Setting %s format, wxh: %dx%d, fmt: %4.4s %c\n", - v4l2_type_names[f->type], q_data->width, q_data->height, - (char *)&q_data->fourcc, - (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) ? 'L' : 'T'); - - return 0; -} - -static int coda_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_q_data *q_data_src; - const struct coda_codec *codec; - struct v4l2_rect r; - int ret; - - ret = coda_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - r.left = 0; - r.top = 0; - r.width = q_data_src->width; - r.height = q_data_src->height; - - ret = coda_s_fmt(ctx, f, &r); - if (ret) - return ret; - - if (ctx->inst_type != CODA_INST_ENCODER) - return 0; - - /* Setting the coded format determines the selected codec */ - codec = coda_find_codec(ctx->dev, q_data_src->fourcc, - f->fmt.pix.pixelformat); - if (!codec) { - v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); - return -EINVAL; - } - ctx->codec = codec; - - ctx->colorspace = f->fmt.pix.colorspace; - ctx->xfer_func = f->fmt.pix.xfer_func; - ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; - ctx->quantization = f->fmt.pix.quantization; - - return 0; -} - -static int coda_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - const struct coda_codec *codec; - struct v4l2_format f_cap; - struct vb2_queue *dst_vq; - int ret; - - ret = coda_try_fmt_vid_out(file, priv, f); - if (ret) - return ret; - - ret = coda_s_fmt(ctx, f, NULL); - if (ret) - return ret; - - ctx->colorspace = f->fmt.pix.colorspace; - ctx->xfer_func = f->fmt.pix.xfer_func; - ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; - ctx->quantization = f->fmt.pix.quantization; - - if (ctx->inst_type != CODA_INST_DECODER) - return 0; - - /* Setting the coded format determines the selected codec */ - codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, - V4L2_PIX_FMT_YUV420); - if (!codec) { - v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); - return -EINVAL; - } - ctx->codec = codec; - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (!dst_vq) - return -EINVAL; - - /* - * Setting the capture queue format is not possible while the capture - * queue is still busy. This is not an error, but the user will have to - * make sure themselves that the capture format is set correctly before - * starting the output queue again. - */ - if (vb2_is_busy(dst_vq)) - return 0; - - memset(&f_cap, 0, sizeof(f_cap)); - f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - coda_g_fmt(file, priv, &f_cap); - f_cap.fmt.pix.width = f->fmt.pix.width; - f_cap.fmt.pix.height = f->fmt.pix.height; - - return coda_s_fmt_vid_cap(file, priv, &f_cap); -} - -static int coda_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb); - if (ret) - return ret; - - /* - * Allow to allocate instance specific per-context buffers, such as - * bitstream ringbuffer, slice buffer, work buffer, etc. if needed. - */ - if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs) - return ctx->ops->reqbufs(ctx, rb); - - return 0; -} - -static int coda_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - if (ctx->inst_type == CODA_INST_DECODER && - buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - buf->flags &= ~V4L2_BUF_FLAG_LAST; - - return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); -} - -static int coda_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); - - if (ctx->inst_type == CODA_INST_DECODER && - buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - buf->flags &= ~V4L2_BUF_FLAG_LAST; - - return ret; -} - -void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state) -{ - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; - - if (buf->flags & V4L2_BUF_FLAG_LAST) - v4l2_event_queue_fh(&ctx->fh, &eos_event); - - v4l2_m2m_buf_done(buf, state); -} - -static int coda_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data; - struct v4l2_rect r, *rsel; - - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - r.left = 0; - r.top = 0; - r.width = q_data->width; - r.height = q_data->height; - rsel = &q_data->rect; - - switch (s->target) { - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - rsel = &r; - fallthrough; - case V4L2_SEL_TGT_CROP: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || - ctx->inst_type == CODA_INST_DECODER) - return -EINVAL; - break; - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: - rsel = &r; - fallthrough; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - ctx->inst_type == CODA_INST_ENCODER) - return -EINVAL; - break; - default: - return -EINVAL; - } - - s->r = *rsel; - - return 0; -} - -static int coda_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data; - - switch (s->target) { - case V4L2_SEL_TGT_CROP: - if (ctx->inst_type == CODA_INST_ENCODER && - s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - s->r.left = 0; - s->r.top = 0; - s->r.width = clamp(s->r.width, 2U, q_data->width); - s->r.height = clamp(s->r.height, 2U, q_data->height); - - if (s->flags & V4L2_SEL_FLAG_LE) { - s->r.width = round_up(s->r.width, 2); - s->r.height = round_up(s->r.height, 2); - } else { - s->r.width = round_down(s->r.width, 2); - s->r.height = round_down(s->r.height, 2); - } - - q_data->rect = s->r; - - coda_dbg(1, ctx, "Setting crop rectangle: %dx%d\n", - s->r.width, s->r.height); - - return 0; - } - fallthrough; - case V4L2_SEL_TGT_NATIVE_SIZE: - case V4L2_SEL_TGT_COMPOSE: - return coda_g_selection(file, fh, s); - default: - /* v4l2-compliance expects this to fail for read-only targets */ - return -EINVAL; - } -} - -static int coda_try_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *ec) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - - if (ctx->inst_type != CODA_INST_ENCODER) - return -ENOTTY; - - return v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); -} - -static void coda_wake_up_capture_queue(struct coda_ctx *ctx) -{ - struct vb2_queue *dst_vq; - - coda_dbg(1, ctx, "waking up capture queue\n"); - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_vq->last_buffer_dequeued = true; - wake_up(&dst_vq->done_wq); -} - -static int coda_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *ec) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct vb2_v4l2_buffer *buf; - int ret; - - ret = coda_try_encoder_cmd(file, fh, ec); - if (ret < 0) - return ret; - - mutex_lock(&ctx->wakeup_mutex); - buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - if (buf) { - /* - * If the last output buffer is still on the queue, make sure - * that decoder finish_run will see the last flag and report it - * to userspace. - */ - buf->flags |= V4L2_BUF_FLAG_LAST; - } else { - /* Set the stream-end flag on this context */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - - /* - * If the last output buffer has already been taken from the - * queue, wake up the capture queue and signal end of stream - * via the -EPIPE mechanism. - */ - coda_wake_up_capture_queue(ctx); - } - mutex_unlock(&ctx->wakeup_mutex); - - return 0; -} - -static int coda_try_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - - if (ctx->inst_type != CODA_INST_DECODER) - return -ENOTTY; - - return v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); -} - -static bool coda_mark_last_meta(struct coda_ctx *ctx) -{ - struct coda_buffer_meta *meta; - - coda_dbg(1, ctx, "marking last meta\n"); - - spin_lock(&ctx->buffer_meta_lock); - if (list_empty(&ctx->buffer_meta_list)) { - spin_unlock(&ctx->buffer_meta_lock); - return false; - } - - meta = list_last_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, - list); - meta->last = true; - - spin_unlock(&ctx->buffer_meta_lock); - return true; -} - -static bool coda_mark_last_dst_buf(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *buf; - struct vb2_buffer *dst_vb; - struct vb2_queue *dst_vq; - unsigned long flags; - - coda_dbg(1, ctx, "marking last capture buffer\n"); - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - spin_lock_irqsave(&dst_vq->done_lock, flags); - if (list_empty(&dst_vq->done_list)) { - spin_unlock_irqrestore(&dst_vq->done_lock, flags); - return false; - } - - dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer, - done_entry); - buf = to_vb2_v4l2_buffer(dst_vb); - buf->flags |= V4L2_BUF_FLAG_LAST; - - spin_unlock_irqrestore(&dst_vq->done_lock, flags); - return true; -} - -static int coda_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *buf; - struct vb2_queue *dst_vq; - bool stream_end; - bool wakeup; - int ret; - - ret = coda_try_decoder_cmd(file, fh, dc); - if (ret < 0) - return ret; - - switch (dc->cmd) { - case V4L2_DEC_CMD_START: - mutex_lock(&dev->coda_mutex); - mutex_lock(&ctx->bitstream_mutex); - coda_bitstream_flush(ctx); - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - vb2_clear_last_buffer_dequeued(dst_vq); - ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); - mutex_unlock(&dev->coda_mutex); - break; - case V4L2_DEC_CMD_STOP: - stream_end = false; - wakeup = false; - - mutex_lock(&ctx->wakeup_mutex); - - buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - if (buf) { - coda_dbg(1, ctx, "marking last pending buffer\n"); - - /* Mark last buffer */ - buf->flags |= V4L2_BUF_FLAG_LAST; - - if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0) { - coda_dbg(1, ctx, "all remaining buffers queued\n"); - stream_end = true; - } - } else { - if (ctx->use_bit) - if (coda_mark_last_meta(ctx)) - stream_end = true; - else - wakeup = true; - else - if (!coda_mark_last_dst_buf(ctx)) - wakeup = true; - } - - if (stream_end) { - coda_dbg(1, ctx, "all remaining buffers queued\n"); - - /* Set the stream-end flag on this context */ - coda_bit_stream_end_flag(ctx); - ctx->hold = false; - v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); - } - - if (wakeup) { - /* If there is no buffer in flight, wake up */ - coda_wake_up_capture_queue(ctx); - } - - mutex_unlock(&ctx->wakeup_mutex); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int coda_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data_dst; - const struct coda_codec *codec; - - if (ctx->inst_type != CODA_INST_ENCODER) - return -ENOTTY; - - if (fsize->index) - return -EINVAL; - - if (coda_format_normalize_yuv(fsize->pixel_format) == - V4L2_PIX_FMT_YUV420) { - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - codec = coda_find_codec(ctx->dev, fsize->pixel_format, - q_data_dst->fourcc); - } else { - codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, - fsize->pixel_format); - } - if (!codec) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = MIN_W; - fsize->stepwise.max_width = codec->max_w; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = MIN_H; - fsize->stepwise.max_height = codec->max_h; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int coda_enum_frameintervals(struct file *file, void *fh, - struct v4l2_frmivalenum *f) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - int i; - - if (f->index) - return -EINVAL; - - /* Disallow YUYV if the vdoa is not available */ - if (!ctx->vdoa && f->pixel_format == V4L2_PIX_FMT_YUYV) - return -EINVAL; - - for (i = 0; i < CODA_MAX_FORMATS; i++) { - if (f->pixel_format == ctx->cvd->src_formats[i] || - f->pixel_format == ctx->cvd->dst_formats[i]) - break; - } - if (i == CODA_MAX_FORMATS) - return -EINVAL; - - f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - f->stepwise.min.numerator = 1; - f->stepwise.min.denominator = 65535; - f->stepwise.max.numerator = 65536; - f->stepwise.max.denominator = 1; - f->stepwise.step.numerator = 1; - f->stepwise.step.denominator = 1; - - return 0; -} - -static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct v4l2_fract *tpf; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - tpf = &a->parm.output.timeperframe; - tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; - tpf->numerator = 1 + (ctx->params.framerate >> - CODA_FRATE_DIV_OFFSET); - - return 0; -} - -/* - * Approximate timeperframe v4l2_fract with values that can be written - * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. - */ -static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) -{ - struct v4l2_fract s = *timeperframe; - struct v4l2_fract f0; - struct v4l2_fract f1 = { 1, 0 }; - struct v4l2_fract f2 = { 0, 1 }; - unsigned int i, div, s_denominator; - - /* Lower bound is 1/65535 */ - if (s.numerator == 0 || s.denominator / s.numerator > 65535) { - timeperframe->numerator = 1; - timeperframe->denominator = 65535; - return; - } - - /* Upper bound is 65536/1 */ - if (s.denominator == 0 || s.numerator / s.denominator > 65536) { - timeperframe->numerator = 65536; - timeperframe->denominator = 1; - return; - } - - /* Reduce fraction to lowest terms */ - div = gcd(s.numerator, s.denominator); - if (div > 1) { - s.numerator /= div; - s.denominator /= div; - } - - if (s.numerator <= 65536 && s.denominator < 65536) { - *timeperframe = s; - return; - } - - /* Find successive convergents from continued fraction expansion */ - while (f2.numerator <= 65536 && f2.denominator < 65536) { - f0 = f1; - f1 = f2; - - /* Stop when f2 exactly equals timeperframe */ - if (s.numerator == 0) - break; - - i = s.denominator / s.numerator; - - f2.numerator = f0.numerator + i * f1.numerator; - f2.denominator = f0.denominator + i * f2.denominator; - - s_denominator = s.numerator; - s.numerator = s.denominator % s.numerator; - s.denominator = s_denominator; - } - - *timeperframe = f1; -} - -static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) -{ - return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | - timeperframe->denominator; -} - -static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct v4l2_fract *tpf; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - tpf = &a->parm.output.timeperframe; - coda_approximate_timeperframe(tpf); - ctx->params.framerate = coda_timeperframe_to_frate(tpf); - ctx->params.framerate_changed = true; - - return 0; -} - -static int coda_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - - switch (sub->type) { - case V4L2_EVENT_EOS: - return v4l2_event_subscribe(fh, sub, 0, NULL); - case V4L2_EVENT_SOURCE_CHANGE: - if (ctx->inst_type == CODA_INST_DECODER) - return v4l2_event_subscribe(fh, sub, 0, NULL); - else - return -EINVAL; - default: - return v4l2_ctrl_subscribe_event(fh, sub); - } -} - -static const struct v4l2_ioctl_ops coda_ioctl_ops = { - .vidioc_querycap = coda_querycap, - - .vidioc_enum_fmt_vid_cap = coda_enum_fmt, - .vidioc_g_fmt_vid_cap = coda_g_fmt, - .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, - - .vidioc_enum_fmt_vid_out = coda_enum_fmt, - .vidioc_g_fmt_vid_out = coda_g_fmt, - .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, - - .vidioc_reqbufs = coda_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - - .vidioc_qbuf = coda_qbuf, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_dqbuf = coda_dqbuf, - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_g_selection = coda_g_selection, - .vidioc_s_selection = coda_s_selection, - - .vidioc_try_encoder_cmd = coda_try_encoder_cmd, - .vidioc_encoder_cmd = coda_encoder_cmd, - .vidioc_try_decoder_cmd = coda_try_decoder_cmd, - .vidioc_decoder_cmd = coda_decoder_cmd, - - .vidioc_g_parm = coda_g_parm, - .vidioc_s_parm = coda_s_parm, - - .vidioc_enum_framesizes = coda_enum_framesizes, - .vidioc_enum_frameintervals = coda_enum_frameintervals, - - .vidioc_subscribe_event = coda_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* - * Mem-to-mem operations. - */ - -static void coda_device_run(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *dev = ctx->dev; - - queue_work(dev->workqueue, &ctx->pic_run_work); -} - -static void coda_pic_run_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); - struct coda_dev *dev = ctx->dev; - int ret; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - ret = ctx->ops->prepare_run(ctx); - if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) { - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); - /* job_finish scheduled by prepare_decode */ - return; - } - - if (!wait_for_completion_timeout(&ctx->completion, - msecs_to_jiffies(1000))) { - if (ctx->use_bit) { - dev_err(dev->dev, "CODA PIC_RUN timeout\n"); - - ctx->hold = true; - - coda_hw_reset(ctx); - } - - if (ctx->ops->run_timeout) - ctx->ops->run_timeout(ctx); - } else { - ctx->ops->finish_run(ctx); - } - - if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) && - ctx->ops->seq_end_work) - queue_work(dev->workqueue, &ctx->seq_end_work); - - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); - - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); -} - -static int coda_job_ready(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); - - /* - * For both 'P' and 'key' frame cases 1 picture - * and 1 frame are needed. In the decoder case, - * the compressed frame can be in the bitstream. - */ - if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { - coda_dbg(1, ctx, "not ready: not enough vid-out buffers.\n"); - return 0; - } - - if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { - coda_dbg(1, ctx, "not ready: not enough vid-cap buffers.\n"); - return 0; - } - - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { - bool stream_end = ctx->bit_stream_param & - CODA_BIT_STREAM_END_FLAG; - int num_metas = ctx->num_metas; - struct coda_buffer_meta *meta; - unsigned int count; - - count = hweight32(ctx->frm_dis_flg); - if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) { - coda_dbg(1, ctx, - "not ready: all internal buffers in use: %d/%d (0x%x)", - count, ctx->num_internal_frames, - ctx->frm_dis_flg); - return 0; - } - - if (ctx->hold && !src_bufs) { - coda_dbg(1, ctx, - "not ready: on hold for more buffers.\n"); - return 0; - } - - if (!stream_end && (num_metas + src_bufs) < 2) { - coda_dbg(1, ctx, - "not ready: need 2 buffers available (queue:%d + bitstream:%d)\n", - num_metas, src_bufs); - return 0; - } - - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - if (!coda_bitstream_can_fetch_past(ctx, meta->end) && - !stream_end) { - coda_dbg(1, ctx, - "not ready: not enough bitstream data to read past %u (%u)\n", - meta->end, ctx->bitstream_fifo.kfifo.in); - return 0; - } - } - - if (ctx->aborting) { - coda_dbg(1, ctx, "not ready: aborting\n"); - return 0; - } - - coda_dbg(2, ctx, "job ready\n"); - - return 1; -} - -static void coda_job_abort(void *priv) -{ - struct coda_ctx *ctx = priv; - - ctx->aborting = 1; - - coda_dbg(1, ctx, "job abort\n"); -} - -static const struct v4l2_m2m_ops coda_m2m_ops = { - .device_run = coda_device_run, - .job_ready = coda_job_ready, - .job_abort = coda_job_abort, -}; - -static void set_default_params(struct coda_ctx *ctx) -{ - unsigned int max_w, max_h, usize, csize; - - ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0], - ctx->cvd->dst_formats[0]); - max_w = min(ctx->codec->max_w, 1920U); - max_h = min(ctx->codec->max_h, 1088U); - usize = max_w * max_h * 3 / 2; - csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); - - ctx->params.codec_mode = ctx->codec->mode; - if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG) - ctx->colorspace = V4L2_COLORSPACE_JPEG; - else - ctx->colorspace = V4L2_COLORSPACE_REC709; - ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; - ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - ctx->quantization = V4L2_QUANTIZATION_DEFAULT; - ctx->params.framerate = 30; - - /* Default formats for output and input queues */ - ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; - ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; - ctx->q_data[V4L2_M2M_SRC].width = max_w; - ctx->q_data[V4L2_M2M_SRC].height = max_h; - ctx->q_data[V4L2_M2M_DST].width = max_w; - ctx->q_data[V4L2_M2M_DST].height = max_h; - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { - ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; - ctx->q_data[V4L2_M2M_SRC].sizeimage = usize; - ctx->q_data[V4L2_M2M_DST].bytesperline = 0; - ctx->q_data[V4L2_M2M_DST].sizeimage = csize; - } else { - ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; - ctx->q_data[V4L2_M2M_SRC].sizeimage = csize; - ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; - ctx->q_data[V4L2_M2M_DST].sizeimage = usize; - } - ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; - ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; - ctx->q_data[V4L2_M2M_DST].rect.width = max_w; - ctx->q_data[V4L2_M2M_DST].rect.height = max_h; - - /* - * Since the RBC2AXI logic only supports a single chroma plane, - * macroblock tiling only works for to NV12 pixel format. - */ - ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; -} - -/* - * Queue operations - */ -static int coda_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(vq); - struct coda_q_data *q_data; - unsigned int size; - - q_data = get_q_data(ctx, vq->type); - size = q_data->sizeimage; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; - - coda_dbg(1, ctx, "get %d buffer(s) of size %d each.\n", *nbuffers, - size); - - return 0; -} - -static int coda_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct coda_q_data *q_data; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - v4l2_warn(&ctx->dev->v4l2_dev, - "%s field isn't supported\n", __func__); - return -EINVAL; - } - } - - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { - v4l2_warn(&ctx->dev->v4l2_dev, - "%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); - return -EINVAL; - } - - return 0; -} - -static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) -{ - if (!ctrl) - return; - - v4l2_ctrl_lock(ctrl); - - /* - * Extend the control range if the parsed stream contains a known but - * unsupported value or level. - */ - if (value > ctrl->maximum) { - __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, - ctrl->menu_skip_mask & ~(1 << value), - ctrl->default_value); - } else if (value < ctrl->minimum) { - __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, - ctrl->menu_skip_mask & ~(1 << value), - ctrl->default_value); - } - - __v4l2_ctrl_s_ctrl(ctrl, value); - - v4l2_ctrl_unlock(ctrl); -} - -void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, - u8 level_idc) -{ - const char * const *profile_names; - const char * const *level_names; - struct v4l2_ctrl *profile_ctrl; - struct v4l2_ctrl *level_ctrl; - const char *codec_name; - u32 profile_cid; - u32 level_cid; - int profile; - int level; - - switch (ctx->codec->src_fourcc) { - case V4L2_PIX_FMT_H264: - codec_name = "H264"; - profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; - level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; - profile_ctrl = ctx->h264_profile_ctrl; - level_ctrl = ctx->h264_level_ctrl; - profile = coda_h264_profile(profile_idc); - level = coda_h264_level(level_idc); - break; - case V4L2_PIX_FMT_MPEG2: - codec_name = "MPEG-2"; - profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; - level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; - profile_ctrl = ctx->mpeg2_profile_ctrl; - level_ctrl = ctx->mpeg2_level_ctrl; - profile = coda_mpeg2_profile(profile_idc); - level = coda_mpeg2_level(level_idc); - break; - case V4L2_PIX_FMT_MPEG4: - codec_name = "MPEG-4"; - profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; - level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; - profile_ctrl = ctx->mpeg4_profile_ctrl; - level_ctrl = ctx->mpeg4_level_ctrl; - profile = coda_mpeg4_profile(profile_idc); - level = coda_mpeg4_level(level_idc); - break; - default: - return; - } - - profile_names = v4l2_ctrl_get_menu(profile_cid); - level_names = v4l2_ctrl_get_menu(level_cid); - - if (profile < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", - codec_name, profile_idc); - } else { - coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, - profile_names[profile]); - coda_update_menu_ctrl(profile_ctrl, profile); - } - - if (level < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", - codec_name, level_idc); - } else { - coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, - level_names[level]); - coda_update_menu_ctrl(level_ctrl, level); - } -} - -static void coda_queue_source_change_event(struct coda_ctx *ctx) -{ - static const struct v4l2_event source_change_event = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, - }; - - v4l2_event_queue_fh(&ctx->fh, &source_change_event); -} - -static void coda_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_queue *vq = vb->vb2_queue; - struct coda_q_data *q_data; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - - /* - * In the decoder case, immediately try to copy the buffer into the - * bitstream ringbuffer and mark it as ready to be dequeued. - */ - if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - /* - * For backwards compatibility, queuing an empty buffer marks - * the stream end - */ - if (vb2_get_plane_payload(vb, 0) == 0) - coda_bit_stream_end_flag(ctx); - - if (q_data->fourcc == V4L2_PIX_FMT_H264) { - /* - * Unless already done, try to obtain profile_idc and - * level_idc from the SPS header. This allows to decide - * whether to enable reordering during sequence - * initialization. - */ - if (!ctx->params.h264_profile_idc) { - coda_sps_parse_profile(ctx, vb); - coda_update_profile_level_ctrls(ctx, - ctx->params.h264_profile_idc, - ctx->params.h264_level_idc); - } - } - - mutex_lock(&ctx->bitstream_mutex); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); - if (vb2_is_streaming(vb->vb2_queue)) - /* This set buf->sequence = ctx->qsequence++ */ - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); - - if (!ctx->initialized) { - /* - * Run sequence initialization in case the queued - * buffer contained headers. - */ - if (vb2_is_streaming(vb->vb2_queue) && - ctx->ops->seq_init_work) { - queue_work(ctx->dev->workqueue, - &ctx->seq_init_work); - flush_work(&ctx->seq_init_work); - } - - if (ctx->initialized) - coda_queue_source_change_event(ctx); - } - } else { - if ((ctx->inst_type == CODA_INST_ENCODER || !ctx->use_bit) && - vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - vbuf->sequence = ctx->qsequence++; - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); - } -} - -int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, - size_t size, const char *name, struct dentry *parent) -{ - buf->vaddr = dma_alloc_coherent(dev->dev, size, &buf->paddr, - GFP_KERNEL); - if (!buf->vaddr) { - v4l2_err(&dev->v4l2_dev, - "Failed to allocate %s buffer of size %zu\n", - name, size); - return -ENOMEM; - } - - buf->size = size; - - if (name && parent) { - buf->blob.data = buf->vaddr; - buf->blob.size = size; - buf->dentry = debugfs_create_blob(name, 0444, parent, - &buf->blob); - } - - return 0; -} - -void coda_free_aux_buf(struct coda_dev *dev, - struct coda_aux_buf *buf) -{ - if (buf->vaddr) { - dma_free_coherent(dev->dev, buf->size, buf->vaddr, buf->paddr); - buf->vaddr = NULL; - buf->size = 0; - debugfs_remove(buf->dentry); - buf->dentry = NULL; - } -} - -static int coda_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; - struct coda_q_data *q_data_src, *q_data_dst; - struct v4l2_m2m_buffer *m2m_buf, *tmp; - struct vb2_v4l2_buffer *buf; - struct list_head list; - int ret = 0; - - if (count < 1) - return -EINVAL; - - coda_dbg(1, ctx, "start streaming %s\n", v4l2_type_names[q->type]); - - INIT_LIST_HEAD(&list); - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { - /* copy the buffers that were queued before streamon */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx, &list); - mutex_unlock(&ctx->bitstream_mutex); - - if (ctx->dev->devtype->product != CODA_960 && - coda_get_bitstream_payload(ctx) < 512) { - v4l2_err(v4l2_dev, "start payload < 512\n"); - ret = -EINVAL; - goto err; - } - - if (!ctx->initialized) { - /* Run sequence initialization */ - if (ctx->ops->seq_init_work) { - queue_work(ctx->dev->workqueue, - &ctx->seq_init_work); - flush_work(&ctx->seq_init_work); - } - } - } - - /* - * Check the first input JPEG buffer to determine chroma - * subsampling. - */ - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { - buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - ret = coda_jpeg_decode_header(ctx, &buf->vb2_buf); - if (ret < 0) { - v4l2_err(v4l2_dev, - "failed to decode JPEG header: %d\n", - ret); - goto err; - } - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - q_data_dst->width = round_up(q_data_src->width, 16); - q_data_dst->height = round_up(q_data_src->height, 16); - q_data_dst->bytesperline = q_data_dst->width; - if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_420) { - q_data_dst->sizeimage = - q_data_dst->bytesperline * - q_data_dst->height * 3 / 2; - if (q_data_dst->fourcc != V4L2_PIX_FMT_YUV420) - q_data_dst->fourcc = V4L2_PIX_FMT_NV12; - } else { - q_data_dst->sizeimage = - q_data_dst->bytesperline * - q_data_dst->height * 2; - q_data_dst->fourcc = V4L2_PIX_FMT_YUV422P; - } - q_data_dst->rect.left = 0; - q_data_dst->rect.top = 0; - q_data_dst->rect.width = q_data_src->width; - q_data_dst->rect.height = q_data_src->height; - } - ctx->streamon_out = 1; - } else { - ctx->streamon_cap = 1; - } - - /* Don't start the coda unless both queues are on */ - if (!(ctx->streamon_out && ctx->streamon_cap)) - goto out; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if ((q_data_src->rect.width != q_data_dst->width && - round_up(q_data_src->rect.width, 16) != q_data_dst->width) || - (q_data_src->rect.height != q_data_dst->height && - round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { - v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", - q_data_src->rect.width, q_data_src->rect.height, - q_data_dst->width, q_data_dst->height); - ret = -EINVAL; - goto err; - } - - /* Allow BIT decoder device_run with no new buffers queued */ - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) - v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); - - ctx->gopcounter = ctx->params.gop_size - 1; - - if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG) - ctx->params.gop_size = 1; - ctx->gopcounter = ctx->params.gop_size - 1; - /* Only decoders have this control */ - if (ctx->mb_err_cnt_ctrl) - v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0); - - ret = ctx->ops->start_streaming(ctx); - if (ctx->inst_type == CODA_INST_DECODER) { - if (ret == -EAGAIN) - goto out; - } - if (ret < 0) - goto err; - -out: - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - list_for_each_entry_safe(m2m_buf, tmp, &list, list) { - list_del(&m2m_buf->list); - v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_DONE); - } - } - return 0; - -err: - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - list_for_each_entry_safe(m2m_buf, tmp, &list, list) { - list_del(&m2m_buf->list); - v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_QUEUED); - } - while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); - } else { - while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); - } - return ret; -} - -static void coda_stop_streaming(struct vb2_queue *q) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - struct coda_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *buf; - bool stop; - - stop = ctx->streamon_out && ctx->streamon_cap; - - coda_dbg(1, ctx, "stop streaming %s\n", v4l2_type_names[q->type]); - - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - ctx->streamon_out = 0; - - coda_bit_stream_end_flag(ctx); - - ctx->qsequence = 0; - - while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); - } else { - ctx->streamon_cap = 0; - - ctx->osequence = 0; - ctx->sequence_offset = 0; - - while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); - } - - if (stop) { - struct coda_buffer_meta *meta; - - if (ctx->ops->seq_end_work) { - queue_work(dev->workqueue, &ctx->seq_end_work); - flush_work(&ctx->seq_end_work); - } - spin_lock(&ctx->buffer_meta_lock); - while (!list_empty(&ctx->buffer_meta_list)) { - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - list_del(&meta->list); - kfree(meta); - } - ctx->num_metas = 0; - spin_unlock(&ctx->buffer_meta_lock); - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - ctx->runcounter = 0; - ctx->aborting = 0; - ctx->hold = false; - } - - if (!ctx->streamon_out && !ctx->streamon_cap) - ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; -} - -static const struct vb2_ops coda_qops = { - .queue_setup = coda_queue_setup, - .buf_prepare = coda_buf_prepare, - .buf_queue = coda_buf_queue, - .start_streaming = coda_start_streaming, - .stop_streaming = coda_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int coda_s_ctrl(struct v4l2_ctrl *ctrl) -{ - const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); - struct coda_ctx *ctx = - container_of(ctrl->handler, struct coda_ctx, ctrls); - - if (val_names) - coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", - ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); - else - coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", - ctrl->id, ctrl->name, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - if (ctrl->val) - ctx->params.rot_mode |= CODA_MIR_HOR; - else - ctx->params.rot_mode &= ~CODA_MIR_HOR; - break; - case V4L2_CID_VFLIP: - if (ctrl->val) - ctx->params.rot_mode |= CODA_MIR_VER; - else - ctx->params.rot_mode &= ~CODA_MIR_VER; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctx->params.bitrate = ctrl->val / 1000; - ctx->params.bitrate_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctx->params.gop_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: - ctx->params.h264_intra_qp = ctrl->val; - ctx->params.h264_intra_qp_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: - ctx->params.h264_inter_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: - ctx->params.h264_min_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: - ctx->params.h264_max_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: - ctx->params.h264_slice_alpha_c0_offset_div2 = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: - ctx->params.h264_slice_beta_offset_div2 = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: - ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: - ctx->params.h264_constrained_intra_pred_flag = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - ctx->params.frame_rc_enable = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: - ctx->params.mb_rc_enable = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: - ctx->params.h264_chroma_qp_index_offset = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - /* TODO: switch between baseline and constrained baseline */ - if (ctx->inst_type == CODA_INST_ENCODER) - ctx->params.h264_profile_idc = 66; - break; - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - /* nothing to do, this is set by the encoder */ - break; - case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: - ctx->params.mpeg4_intra_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: - ctx->params.mpeg4_inter_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: - case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: - case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - /* nothing to do, these are fixed */ - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: - ctx->params.slice_mode = ctrl->val; - ctx->params.slice_mode_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: - ctx->params.slice_max_mb = ctrl->val; - ctx->params.slice_mode_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: - ctx->params.slice_max_bits = ctrl->val * 8; - ctx->params.slice_mode_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - break; - case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: - ctx->params.intra_refresh = ctrl->val; - ctx->params.intra_refresh_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: - ctx->params.force_ipicture = true; - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - coda_set_jpeg_compression_quality(ctx, ctrl->val); - break; - case V4L2_CID_JPEG_RESTART_INTERVAL: - ctx->params.jpeg_restart_interval = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_VBV_DELAY: - ctx->params.vbv_delay = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_VBV_SIZE: - ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); - break; - default: - coda_dbg(1, ctx, "Invalid control, id=%d, val=%d\n", - ctrl->id, ctrl->val); - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops coda_ctrl_ops = { - .s_ctrl = coda_s_ctrl, -}; - -static void coda_encode_ctrls(struct coda_ctx *ctx) -{ - int max_gop_size = (ctx->dev->devtype->product == CODA_DX6) ? 60 : 99; - - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, max_gop_size, 1, 16); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); - if (ctx->dev->devtype->product != CODA_960) { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); - } - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, - V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, - 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, - 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, - V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); - if (ctx->dev->devtype->product == CODA_HX4 || - ctx->dev->devtype->product == CODA_7541) { - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_MPEG_VIDEO_H264_LEVEL_3_1, - ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1)), - V4L2_MPEG_VIDEO_H264_LEVEL_3_1); - } - if (ctx->dev->devtype->product == CODA_960) { - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_MPEG_VIDEO_H264_LEVEL_4_0, - ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0)), - V4L2_MPEG_VIDEO_H264_LEVEL_4_0); - } - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); - if (ctx->dev->devtype->product == CODA_HX4 || - ctx->dev->devtype->product == CODA_7541 || - ctx->dev->devtype->product == CODA_960) { - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, - ~(1 << V4L2_MPEG_VIDEO_MPEG4_LEVEL_5), - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); - } - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, - V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, - 500); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_HEADER_MODE, - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, - 1920 * 1088 / 256, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); - /* - * The maximum VBV size value is 0x7fffffff bits, - * one bit less than 262144 KiB - */ - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); -} - -static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) -{ - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); -} - -static void coda_decode_ctrls(struct coda_ctx *ctx) -{ - u8 max; - - ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), - V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); - if (ctx->h264_profile_ctrl) - ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - if (ctx->dev->devtype->product == CODA_HX4 || - ctx->dev->devtype->product == CODA_7541) - max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - else if (ctx->dev->devtype->product == CODA_960) - max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; - else - return; - ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); - if (ctx->h264_level_ctrl) - ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, - V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, - V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); - if (ctx->mpeg2_profile_ctrl) - ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, - V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, - V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); - if (ctx->mpeg2_level_ctrl) - ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); - if (ctx->mpeg4_profile_ctrl) - ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); - if (ctx->mpeg4_level_ctrl) - ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; -} - -static const struct v4l2_ctrl_config coda_mb_err_cnt_ctrl_config = { - .id = V4L2_CID_CODA_MB_ERR_CNT, - .name = "Macroblocks Error Count", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0, - .max = 0x7fffffff, - .step = 1, -}; - -static int coda_ctrls_setup(struct coda_ctx *ctx) -{ - v4l2_ctrl_handler_init(&ctx->ctrls, 2); - - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - if (ctx->inst_type == CODA_INST_ENCODER) { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, - 1, 1, 1, 1); - if (ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) - coda_jpeg_encode_ctrls(ctx); - else - coda_encode_ctrls(ctx); - } else { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, - 1, 1, 1, 1); - if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) - coda_decode_ctrls(ctx); - - ctx->mb_err_cnt_ctrl = v4l2_ctrl_new_custom(&ctx->ctrls, - &coda_mb_err_cnt_ctrl_config, - NULL); - if (ctx->mb_err_cnt_ctrl) - ctx->mb_err_cnt_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - } - - if (ctx->ctrls.error) { - v4l2_err(&ctx->dev->v4l2_dev, - "control initialization error (%d)", - ctx->ctrls.error); - return -EINVAL; - } - - return v4l2_ctrl_handler_setup(&ctx->ctrls); -} - -static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) -{ - vq->drv_priv = ctx; - vq->ops = &coda_qops; - vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - vq->lock = &ctx->dev->dev_mutex; - /* One way to indicate end-of-stream for coda is to set the - * bytesused == 0. However by default videobuf2 handles bytesused - * equal to 0 as a special case and changes its value to the size - * of the buffer. Set the allow_zero_bytesused flag, so - * that videobuf2 will keep the value of bytesused intact. - */ - vq->allow_zero_bytesused = 1; - /* - * We might be fine with no buffers on some of the queues, but that - * would need to be reflected in job_ready(). Currently we expect all - * queues to have at least one buffer queued. - */ - vq->min_buffers_needed = 1; - vq->dev = ctx->dev->dev; - - return vb2_queue_init(vq); -} - -int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->mem_ops = &vb2_dma_contig_memops; - - ret = coda_queue_init(priv, src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->mem_ops = &vb2_dma_contig_memops; - - return coda_queue_init(priv, dst_vq); -} - -int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; - src_vq->mem_ops = &vb2_vmalloc_memops; - - ret = coda_queue_init(priv, src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; - dst_vq->mem_ops = &vb2_dma_contig_memops; - - return coda_queue_init(priv, dst_vq); -} - -/* - * File operations - */ - -static int coda_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct coda_dev *dev = video_get_drvdata(vdev); - struct coda_ctx *ctx; - unsigned int max = ~0; - char *name; - int ret; - int idx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - if (dev->devtype->product == CODA_DX6) - max = CODADX6_MAX_INSTANCES - 1; - idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL); - if (idx < 0) { - ret = idx; - goto err_coda_max; - } - - name = kasprintf(GFP_KERNEL, "context%d", idx); - if (!name) { - ret = -ENOMEM; - goto err_coda_name_init; - } - - ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); - kfree(name); - - ctx->cvd = to_coda_video_device(vdev); - ctx->inst_type = ctx->cvd->type; - ctx->ops = ctx->cvd->ops; - ctx->use_bit = !ctx->cvd->direct; - init_completion(&ctx->completion); - INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); - if (ctx->ops->seq_init_work) - INIT_WORK(&ctx->seq_init_work, ctx->ops->seq_init_work); - if (ctx->ops->seq_end_work) - INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - ctx->dev = dev; - ctx->idx = idx; - - coda_dbg(1, ctx, "open instance (%p)\n", ctx); - - switch (dev->devtype->product) { - case CODA_960: - /* - * Enabling the BWB when decoding can hang the firmware with - * certain streams. The issue was tracked as ENGR00293425 by - * Freescale. As a workaround, disable BWB for all decoders. - * The enable_bwb module parameter allows to override this. - */ - if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) - ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; - fallthrough; - case CODA_HX4: - case CODA_7541: - ctx->reg_idx = 0; - break; - default: - ctx->reg_idx = idx; - } - if (ctx->dev->vdoa && !disable_vdoa) { - ctx->vdoa = vdoa_context_create(dev->vdoa); - if (!ctx->vdoa) - v4l2_warn(&dev->v4l2_dev, - "Failed to create vdoa context: not using vdoa"); - } - ctx->use_vdoa = false; - - /* Power up and upload firmware if necessary */ - ret = pm_runtime_resume_and_get(dev->dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); - goto err_pm_get; - } - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_enable; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - set_default_params(ctx); - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, - ctx->ops->queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - - v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", - __func__, ret); - goto err_ctx_init; - } - - ret = coda_ctrls_setup(ctx); - if (ret) { - v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); - goto err_ctrls_setup; - } - - ctx->fh.ctrl_handler = &ctx->ctrls; - - mutex_init(&ctx->bitstream_mutex); - mutex_init(&ctx->buffer_mutex); - mutex_init(&ctx->wakeup_mutex); - INIT_LIST_HEAD(&ctx->buffer_meta_list); - spin_lock_init(&ctx->buffer_meta_lock); - - return 0; - -err_ctrls_setup: - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); -err_ctx_init: - clk_disable_unprepare(dev->clk_ahb); -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_enable: - pm_runtime_put_sync(dev->dev); -err_pm_get: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); -err_coda_name_init: - ida_free(&dev->ida, ctx->idx); -err_coda_max: - kfree(ctx); - return ret; -} - -static int coda_release(struct file *file) -{ - struct coda_dev *dev = video_drvdata(file); - struct coda_ctx *ctx = fh_to_ctx(file->private_data); - - coda_dbg(1, ctx, "release instance (%p)\n", ctx); - - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) - coda_bit_stream_end_flag(ctx); - - /* If this instance is running, call .job_abort and wait for it to end */ - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - - if (ctx->vdoa) - vdoa_context_destroy(ctx->vdoa); - - /* In case the instance was not running, we still need to call SEQ_END */ - if (ctx->ops->seq_end_work) { - queue_work(dev->workqueue, &ctx->seq_end_work); - flush_work(&ctx->seq_end_work); - } - - if (ctx->dev->devtype->product == CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - - v4l2_ctrl_handler_free(&ctx->ctrls); - clk_disable_unprepare(dev->clk_ahb); - clk_disable_unprepare(dev->clk_per); - pm_runtime_put_sync(dev->dev); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - ida_free(&dev->ida, ctx->idx); - if (ctx->ops->release) - ctx->ops->release(ctx); - debugfs_remove_recursive(ctx->debugfs_entry); - kfree(ctx); - - return 0; -} - -static const struct v4l2_file_operations coda_fops = { - .owner = THIS_MODULE, - .open = coda_open, - .release = coda_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int coda_hw_init(struct coda_dev *dev) -{ - u32 data; - u16 *p; - int i, ret; - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - reset_control_reset(dev->rstc); - - /* - * Copy the first CODA_ISRAM_SIZE in the internal SRAM. - * The 16-bit chars in the code buffer are in memory access - * order, re-sort them to CODA order for register download. - * Data in this SRAM survives a reboot. - */ - p = (u16 *)dev->codebuf.vaddr; - if (dev->devtype->product == CODA_DX6) { - for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { - data = CODA_DOWN_ADDRESS_SET(i) | - CODA_DOWN_DATA_SET(p[i ^ 1]); - coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); - } - } else { - for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { - data = CODA_DOWN_ADDRESS_SET(i) | - CODA_DOWN_DATA_SET(p[round_down(i, 4) + - 3 - (i % 4)]); - coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); - } - } - - /* Clear registers */ - for (i = 0; i < 64; i++) - coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); - - /* Tell the BIT where to find everything it needs */ - if (dev->devtype->product == CODA_960 || - dev->devtype->product == CODA_7541 || - dev->devtype->product == CODA_HX4) { - coda_write(dev, dev->tempbuf.paddr, - CODA_REG_BIT_TEMP_BUF_ADDR); - coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); - } else { - coda_write(dev, dev->workbuf.paddr, - CODA_REG_BIT_WORK_BUF_ADDR); - } - coda_write(dev, dev->codebuf.paddr, - CODA_REG_BIT_CODE_BUF_ADDR); - coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); - - /* Set default values */ - switch (dev->devtype->product) { - case CODA_DX6: - coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, - CODA_REG_BIT_STREAM_CTRL); - break; - default: - coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, - CODA_REG_BIT_STREAM_CTRL); - } - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_FRAME_ENABLE_BWB, - CODA_REG_BIT_FRAME_MEM_CTRL); - else - coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); - - coda_write(dev, CODA_INT_INTERRUPT_ENABLE, - CODA_REG_BIT_INT_ENABLE); - - /* Reset VPU and start processor */ - data = coda_read(dev, CODA_REG_BIT_CODE_RESET); - data |= CODA_REG_RESET_ENABLE; - coda_write(dev, data, CODA_REG_BIT_CODE_RESET); - udelay(10); - data &= ~CODA_REG_RESET_ENABLE; - coda_write(dev, data, CODA_REG_BIT_CODE_RESET); - coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - - clk_disable_unprepare(dev->clk_ahb); - clk_disable_unprepare(dev->clk_per); - - return 0; - -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - return ret; -} - -static int coda_register_device(struct coda_dev *dev, int i) -{ - struct video_device *vfd = &dev->vfd[i]; - const char *name; - int ret; - - if (i >= dev->devtype->num_vdevs) - return -EINVAL; - name = dev->devtype->vdevs[i]->name; - - strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); - vfd->fops = &coda_fops; - vfd->ioctl_ops = &coda_ioctl_ops; - vfd->release = video_device_release_empty; - vfd->lock = &dev->dev_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - video_set_drvdata(vfd, dev); - - /* Not applicable, use the selection API instead */ - v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); - v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); - v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (!ret) - v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", - name, video_device_node_name(vfd)); - return ret; -} - -static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, - size_t size) -{ - u32 *src = (u32 *)buf; - - /* Check if the firmware has a 16-byte Freescale header, skip it */ - if (buf[0] == 'M' && buf[1] == 'X') - src += 4; - /* - * Check whether the firmware is in native order or pre-reordered for - * memory access. The first instruction opcode always is 0xe40e. - */ - if (__le16_to_cpup((__le16 *)src) == 0xe40e) { - u32 *dst = dev->codebuf.vaddr; - int i; - - /* Firmware in native order, reorder while copying */ - if (dev->devtype->product == CODA_DX6) { - for (i = 0; i < (size - 16) / 4; i++) - dst[i] = (src[i] << 16) | (src[i] >> 16); - } else { - for (i = 0; i < (size - 16) / 4; i += 2) { - dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16); - dst[i + 1] = (src[i] << 16) | (src[i] >> 16); - } - } - } else { - /* Copy the already reordered firmware image */ - memcpy(dev->codebuf.vaddr, src, size); - } -} - -static void coda_fw_callback(const struct firmware *fw, void *context); - -static int coda_firmware_request(struct coda_dev *dev) -{ - char *fw; - - if (dev->firmware >= ARRAY_SIZE(dev->devtype->firmware)) - return -EINVAL; - - fw = dev->devtype->firmware[dev->firmware]; - - dev_dbg(dev->dev, "requesting firmware '%s' for %s\n", fw, - coda_product_name(dev->devtype->product)); - - return request_firmware_nowait(THIS_MODULE, true, fw, dev->dev, - GFP_KERNEL, dev, coda_fw_callback); -} - -static void coda_fw_callback(const struct firmware *fw, void *context) -{ - struct coda_dev *dev = context; - int i, ret; - - if (!fw) { - dev->firmware++; - ret = coda_firmware_request(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); - goto put_pm; - } - return; - } - if (dev->firmware > 0) { - /* - * Since we can't suppress warnings for failed asynchronous - * firmware requests, report that the fallback firmware was - * found. - */ - dev_info(dev->dev, "Using fallback firmware %s\n", - dev->devtype->firmware[dev->firmware]); - } - - /* allocate auxiliary per-device code buffer for the BIT processor */ - ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", - dev->debugfs_root); - if (ret < 0) - goto put_pm; - - coda_copy_firmware(dev, fw->data, fw->size); - release_firmware(fw); - - ret = coda_hw_init(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); - goto put_pm; - } - - ret = coda_check_firmware(dev); - if (ret < 0) - goto put_pm; - - dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); - goto put_pm; - } - - for (i = 0; i < dev->devtype->num_vdevs; i++) { - ret = coda_register_device(dev, i); - if (ret) { - v4l2_err(&dev->v4l2_dev, - "Failed to register %s video device: %d\n", - dev->devtype->vdevs[i]->name, ret); - goto rel_vfd; - } - } - - pm_runtime_put_sync(dev->dev); - return; - -rel_vfd: - while (--i >= 0) - video_unregister_device(&dev->vfd[i]); - v4l2_m2m_release(dev->m2m_dev); -put_pm: - pm_runtime_put_sync(dev->dev); -} - -enum coda_platform { - CODA_IMX27, - CODA_IMX51, - CODA_IMX53, - CODA_IMX6Q, - CODA_IMX6DL, -}; - -static const struct coda_devtype coda_devdata[] = { - [CODA_IMX27] = { - .firmware = { - "vpu_fw_imx27_TO2.bin", - "vpu/vpu_fw_imx27_TO2.bin", - "v4l-codadx6-imx27.bin" - }, - .product = CODA_DX6, - .codecs = codadx6_codecs, - .num_codecs = ARRAY_SIZE(codadx6_codecs), - .vdevs = codadx6_video_devices, - .num_vdevs = ARRAY_SIZE(codadx6_video_devices), - .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, - .iram_size = 0xb000, - }, - [CODA_IMX51] = { - .firmware = { - "vpu_fw_imx51.bin", - "vpu/vpu_fw_imx51.bin", - "v4l-codahx4-imx51.bin" - }, - .product = CODA_HX4, - .codecs = codahx4_codecs, - .num_codecs = ARRAY_SIZE(codahx4_codecs), - .vdevs = codahx4_video_devices, - .num_vdevs = ARRAY_SIZE(codahx4_video_devices), - .workbuf_size = 128 * 1024, - .tempbuf_size = 304 * 1024, - .iram_size = 0x14000, - }, - [CODA_IMX53] = { - .firmware = { - "vpu_fw_imx53.bin", - "vpu/vpu_fw_imx53.bin", - "v4l-coda7541-imx53.bin" - }, - .product = CODA_7541, - .codecs = coda7_codecs, - .num_codecs = ARRAY_SIZE(coda7_codecs), - .vdevs = coda7_video_devices, - .num_vdevs = ARRAY_SIZE(coda7_video_devices), - .workbuf_size = 128 * 1024, - .tempbuf_size = 304 * 1024, - .iram_size = 0x14000, - }, - [CODA_IMX6Q] = { - .firmware = { - "vpu_fw_imx6q.bin", - "vpu/vpu_fw_imx6q.bin", - "v4l-coda960-imx6q.bin" - }, - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), - .vdevs = coda9_video_devices, - .num_vdevs = ARRAY_SIZE(coda9_video_devices), - .workbuf_size = 80 * 1024, - .tempbuf_size = 204 * 1024, - .iram_size = 0x21000, - }, - [CODA_IMX6DL] = { - .firmware = { - "vpu_fw_imx6d.bin", - "vpu/vpu_fw_imx6d.bin", - "v4l-coda960-imx6dl.bin" - }, - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), - .vdevs = coda9_video_devices, - .num_vdevs = ARRAY_SIZE(coda9_video_devices), - .workbuf_size = 80 * 1024, - .tempbuf_size = 204 * 1024, - .iram_size = 0x1f000, /* leave 4k for suspend code */ - }, -}; - -static const struct of_device_id coda_dt_ids[] = { - { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, - { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, - { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, - { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, - { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, coda_dt_ids); - -static int coda_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct gen_pool *pool; - struct coda_dev *dev; - int ret, irq; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->devtype = of_device_get_match_data(&pdev->dev); - - dev->dev = &pdev->dev; - dev->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(dev->clk_per)) { - dev_err(&pdev->dev, "Could not get per clock\n"); - return PTR_ERR(dev->clk_per); - } - - dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(dev->clk_ahb)) { - dev_err(&pdev->dev, "Could not get ahb clock\n"); - return PTR_ERR(dev->clk_ahb); - } - - /* Get memory for physical registers */ - dev->regs_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dev->regs_base)) - return PTR_ERR(dev->regs_base); - - /* IRQ */ - irq = platform_get_irq_byname(pdev, "bit"); - if (irq < 0) - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, - CODA_NAME "-video", dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; - } - - /* JPEG IRQ */ - if (dev->devtype->product == CODA_960) { - irq = platform_get_irq_byname(pdev, "jpeg"); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - coda9_jpeg_irq_handler, - IRQF_ONESHOT, CODA_NAME "-jpeg", - dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request jpeg irq\n"); - return ret; - } - } - - dev->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, - NULL); - if (IS_ERR(dev->rstc)) { - ret = PTR_ERR(dev->rstc); - dev_err(&pdev->dev, "failed get reset control: %d\n", ret); - return ret; - } - - /* Get IRAM pool from device tree */ - pool = of_gen_pool_get(np, "iram", 0); - if (!pool) { - dev_err(&pdev->dev, "iram pool not available\n"); - return -ENOMEM; - } - dev->iram_pool = pool; - - /* Get vdoa_data if supported by the platform */ - dev->vdoa = coda_get_vdoa_data(); - if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - ratelimit_default_init(&dev->mb_err_rs); - mutex_init(&dev->dev_mutex); - mutex_init(&dev->coda_mutex); - ida_init(&dev->ida); - - dev->debugfs_root = debugfs_create_dir("coda", NULL); - - /* allocate auxiliary per-device buffers for the BIT processor */ - if (dev->devtype->product == CODA_DX6) { - ret = coda_alloc_aux_buf(dev, &dev->workbuf, - dev->devtype->workbuf_size, "workbuf", - dev->debugfs_root); - if (ret < 0) - goto err_v4l2_register; - } - - if (dev->devtype->tempbuf_size) { - ret = coda_alloc_aux_buf(dev, &dev->tempbuf, - dev->devtype->tempbuf_size, "tempbuf", - dev->debugfs_root); - if (ret < 0) - goto err_v4l2_register; - } - - dev->iram.size = dev->devtype->iram_size; - dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, - &dev->iram.paddr); - if (!dev->iram.vaddr) { - dev_warn(&pdev->dev, "unable to alloc iram\n"); - } else { - memset(dev->iram.vaddr, 0, dev->iram.size); - dev->iram.blob.data = dev->iram.vaddr; - dev->iram.blob.size = dev->iram.size; - dev->iram.dentry = debugfs_create_blob("iram", 0444, - dev->debugfs_root, - &dev->iram.blob); - } - - dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); - if (!dev->workqueue) { - dev_err(&pdev->dev, "unable to alloc workqueue\n"); - ret = -ENOMEM; - goto err_v4l2_register; - } - - platform_set_drvdata(pdev, dev); - - /* - * Start activated so we can directly call coda_hw_init in - * coda_fw_callback regardless of whether CONFIG_PM is - * enabled or whether the device is associated with a PM domain. - */ - pm_runtime_get_noresume(&pdev->dev); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - ret = coda_firmware_request(dev); - if (ret) - goto err_alloc_workqueue; - return 0; - -err_alloc_workqueue: - pm_runtime_disable(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - destroy_workqueue(dev->workqueue); -err_v4l2_register: - v4l2_device_unregister(&dev->v4l2_dev); - return ret; -} - -static int coda_remove(struct platform_device *pdev) -{ - struct coda_dev *dev = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) { - if (video_get_drvdata(&dev->vfd[i])) - video_unregister_device(&dev->vfd[i]); - } - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - pm_runtime_disable(&pdev->dev); - v4l2_device_unregister(&dev->v4l2_dev); - destroy_workqueue(dev->workqueue); - if (dev->iram.vaddr) - gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, - dev->iram.size); - coda_free_aux_buf(dev, &dev->codebuf); - coda_free_aux_buf(dev, &dev->tempbuf); - coda_free_aux_buf(dev, &dev->workbuf); - debugfs_remove_recursive(dev->debugfs_root); - ida_destroy(&dev->ida); - return 0; -} - -#ifdef CONFIG_PM -static int coda_runtime_resume(struct device *dev) -{ - struct coda_dev *cdev = dev_get_drvdata(dev); - int ret = 0; - - if (dev->pm_domain && cdev->codebuf.vaddr) { - ret = coda_hw_init(cdev); - if (ret) - v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); - } - - return ret; -} -#endif - -static const struct dev_pm_ops coda_pm_ops = { - SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) -}; - -static struct platform_driver coda_driver = { - .probe = coda_probe, - .remove = coda_remove, - .driver = { - .name = CODA_NAME, - .of_match_table = coda_dt_ids, - .pm = &coda_pm_ops, - }, -}; - -module_platform_driver(coda_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Javier Martin "); -MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); diff --git a/drivers/media/platform/coda/coda-gdi.c b/drivers/media/platform/coda/coda-gdi.c deleted file mode 100644 index 59d65daca153..000000000000 --- a/drivers/media/platform/coda/coda-gdi.c +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2014 Philipp Zabel, Pengutronix - */ - -#include -#include "coda.h" - -#define XY2_INVERT BIT(7) -#define XY2_ZERO BIT(6) -#define XY2_TB_XOR BIT(5) -#define XY2_XYSEL BIT(4) -#define XY2_Y (1 << 4) -#define XY2_X (0 << 4) - -#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \ - (((XY2_##luma_sel) | (luma_bit)) << 8 | \ - (XY2_##chroma_sel) | (chroma_bit)) - -static const u16 xy2ca_zero_map[16] = { - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), -}; - -static const u16 xy2ca_tiled_map[16] = { - XY2(Y, 0, Y, 0), - XY2(Y, 1, Y, 1), - XY2(Y, 2, Y, 2), - XY2(Y, 3, X, 3), - XY2(X, 3, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), -}; - -/* - * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock - * start offset (macroblock size is 16x16 for luma, 16x8 for chroma). - * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused. - */ - -#define RBC_CA (0 << 4) -#define RBC_BA (1 << 4) -#define RBC_RA (2 << 4) -#define RBC_ZERO (3 << 4) - -#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \ - (((RBC_##luma_sel) | (luma_bit)) << 6 | \ - (RBC_##chroma_sel) | (chroma_bit)) - -static const u16 rbc2axi_tiled_map[32] = { - RBC(ZERO, 0, ZERO, 0), - RBC(ZERO, 0, ZERO, 0), - RBC(ZERO, 0, ZERO, 0), - RBC(CA, 0, CA, 0), - RBC(CA, 1, CA, 1), - RBC(CA, 2, CA, 2), - RBC(CA, 3, CA, 3), - RBC(CA, 4, CA, 8), - RBC(CA, 8, CA, 9), - RBC(CA, 9, CA, 10), - RBC(CA, 10, CA, 11), - RBC(CA, 11, CA, 12), - RBC(CA, 12, CA, 13), - RBC(CA, 13, CA, 14), - RBC(CA, 14, CA, 15), - RBC(CA, 15, RA, 0), - RBC(RA, 0, RA, 1), - RBC(RA, 1, RA, 2), - RBC(RA, 2, RA, 3), - RBC(RA, 3, RA, 4), - RBC(RA, 4, RA, 5), - RBC(RA, 5, RA, 6), - RBC(RA, 6, RA, 7), - RBC(RA, 7, RA, 8), - RBC(RA, 8, RA, 9), - RBC(RA, 9, RA, 10), - RBC(RA, 10, RA, 11), - RBC(RA, 11, RA, 12), - RBC(RA, 12, RA, 13), - RBC(RA, 13, RA, 14), - RBC(RA, 14, RA, 15), - RBC(RA, 15, ZERO, 0), -}; - -void coda_set_gdi_regs(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - const u16 *xy2ca_map; - u32 xy2rbc_config; - int i; - - switch (ctx->tiled_map_type) { - case GDI_LINEAR_FRAME_MAP: - default: - xy2ca_map = xy2ca_zero_map; - xy2rbc_config = 0; - break; - case GDI_TILED_FRAME_MB_RASTER_MAP: - xy2ca_map = xy2ca_tiled_map; - xy2rbc_config = CODA9_XY2RBC_TILED_MAP | - CODA9_XY2RBC_CA_INC_HOR | - (16 - 1) << 12 | (8 - 1) << 4; - break; - } - - for (i = 0; i < 16; i++) - coda_write(dev, xy2ca_map[i], - CODA9_GDI_XY2_CAS_0 + 4 * i); - for (i = 0; i < 4; i++) - coda_write(dev, XY2(ZERO, 0, ZERO, 0), - CODA9_GDI_XY2_BA_0 + 4 * i); - for (i = 0; i < 16; i++) - coda_write(dev, XY2(ZERO, 0, ZERO, 0), - CODA9_GDI_XY2_RAS_0 + 4 * i); - coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); - if (xy2rbc_config) { - for (i = 0; i < 32; i++) - coda_write(dev, rbc2axi_tiled_map[i], - CODA9_GDI_RBC2_AXI_0 + 4 * i); - } -} diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c deleted file mode 100644 index 8bd0aa8af114..000000000000 --- a/drivers/media/platform/coda/coda-h264.c +++ /dev/null @@ -1,429 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - H.264 helper functions - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - */ - -#include -#include -#include - -#include "coda.h" - -static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; - -static const u8 *coda_find_nal_header(const u8 *buf, const u8 *end) -{ - u32 val = 0xffffffff; - - do { - val = val << 8 | *buf++; - if (buf >= end) - return NULL; - } while (val != 0x00000001); - - return buf; -} - -int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb) -{ - const u8 *buf = vb2_plane_vaddr(vb, 0); - const u8 *end = buf + vb2_get_plane_payload(vb, 0); - - /* Find SPS header */ - do { - buf = coda_find_nal_header(buf, end); - if (!buf) - return -EINVAL; - } while ((*buf++ & 0x1f) != 0x7); - - ctx->params.h264_profile_idc = buf[0]; - ctx->params.h264_level_idc = buf[2]; - - return 0; -} - -int coda_h264_filler_nal(int size, char *p) -{ - if (size < 6) - return -EINVAL; - - p[0] = 0x00; - p[1] = 0x00; - p[2] = 0x00; - p[3] = 0x01; - p[4] = 0x0c; - memset(p + 5, 0xff, size - 6); - /* Add rbsp stop bit and trailing at the end */ - p[size - 1] = 0x80; - - return 0; -} - -int coda_h264_padding(int size, char *p) -{ - int nal_size; - int diff; - - diff = size - (size & ~0x7); - if (diff == 0) - return 0; - - nal_size = coda_filler_size[diff]; - coda_h264_filler_nal(nal_size, p); - - return nal_size; -} - -int coda_h264_profile(int profile_idc) -{ - switch (profile_idc) { - case 66: return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - case 77: return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; - case 88: return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; - case 100: return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; - default: return -EINVAL; - } -} - -int coda_h264_level(int level_idc) -{ - switch (level_idc) { - case 10: return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; - case 9: return V4L2_MPEG_VIDEO_H264_LEVEL_1B; - case 11: return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; - case 12: return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; - case 13: return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; - case 20: return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; - case 21: return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; - case 22: return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; - case 30: return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; - case 31: return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; - case 32: return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; - case 40: return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - case 41: return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; - case 42: return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; - case 50: return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; - case 51: return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; - default: return -EINVAL; - } -} - -struct rbsp { - char *buf; - int size; - int pos; -}; - -static inline int rbsp_read_bit(struct rbsp *rbsp) -{ - int shift = 7 - (rbsp->pos % 8); - int ofs = rbsp->pos++ / 8; - - if (ofs >= rbsp->size) - return -EINVAL; - - return (rbsp->buf[ofs] >> shift) & 1; -} - -static inline int rbsp_write_bit(struct rbsp *rbsp, int bit) -{ - int shift = 7 - (rbsp->pos % 8); - int ofs = rbsp->pos++ / 8; - - if (ofs >= rbsp->size) - return -EINVAL; - - rbsp->buf[ofs] &= ~(1 << shift); - rbsp->buf[ofs] |= bit << shift; - - return 0; -} - -static inline int rbsp_read_bits(struct rbsp *rbsp, int num, int *val) -{ - int i, ret; - int tmp = 0; - - if (num > 32) - return -EINVAL; - - for (i = 0; i < num; i++) { - ret = rbsp_read_bit(rbsp); - if (ret < 0) - return ret; - tmp |= ret << (num - i - 1); - } - - if (val) - *val = tmp; - - return 0; -} - -static int rbsp_write_bits(struct rbsp *rbsp, int num, int value) -{ - int ret; - - while (num--) { - ret = rbsp_write_bit(rbsp, (value >> num) & 1); - if (ret) - return ret; - } - - return 0; -} - -static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *val) -{ - int leading_zero_bits = 0; - unsigned int tmp = 0; - int ret; - - while ((ret = rbsp_read_bit(rbsp)) == 0) - leading_zero_bits++; - if (ret < 0) - return ret; - - if (leading_zero_bits > 0) { - ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); - if (ret) - return ret; - } - - if (val) - *val = (1 << leading_zero_bits) - 1 + tmp; - - return 0; -} - -static int rbsp_write_uev(struct rbsp *rbsp, unsigned int value) -{ - int i; - int ret; - int tmp = value + 1; - int leading_zero_bits = fls(tmp) - 1; - - for (i = 0; i < leading_zero_bits; i++) { - ret = rbsp_write_bit(rbsp, 0); - if (ret) - return ret; - } - - return rbsp_write_bits(rbsp, leading_zero_bits + 1, tmp); -} - -static int rbsp_read_sev(struct rbsp *rbsp, int *val) -{ - unsigned int tmp; - int ret; - - ret = rbsp_read_uev(rbsp, &tmp); - if (ret) - return ret; - - if (val) { - if (tmp & 1) - *val = (tmp + 1) / 2; - else - *val = -(tmp / 2); - } - - return 0; -} - -/** - * coda_h264_sps_fixup - fixes frame cropping values in h.264 SPS - * @ctx: encoder context - * @width: visible width - * @height: visible height - * @buf: buffer containing h.264 SPS RBSP, starting with NAL header - * @size: modified RBSP size return value - * @max_size: available size in buf - * - * Rewrites the frame cropping values in an h.264 SPS RBSP correctly for the - * given visible width and height. - */ -int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, - int *size, int max_size) -{ - int profile_idc; - unsigned int pic_order_cnt_type; - int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1; - int frame_mbs_only_flag, frame_cropping_flag; - int vui_parameters_present_flag; - unsigned int crop_right, crop_bottom; - struct rbsp sps; - int pos; - int ret; - - if (*size < 8 || *size >= max_size) - return -EINVAL; - - sps.buf = buf + 5; /* Skip NAL header */ - sps.size = *size - 5; - - profile_idc = sps.buf[0]; - /* Skip constraint_set[0-5]_flag, reserved_zero_2bits */ - /* Skip level_idc */ - sps.pos = 24; - - /* seq_parameter_set_id */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - - if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || - profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || - profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || - profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || - profile_idc == 135) { - dev_err(ctx->fh.vdev->dev_parent, - "%s: Handling profile_idc %d not implemented\n", - __func__, profile_idc); - return -EINVAL; - } - - /* log2_max_frame_num_minus4 */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - - ret = rbsp_read_uev(&sps, &pic_order_cnt_type); - if (ret) - return ret; - - if (pic_order_cnt_type == 0) { - /* log2_max_pic_order_cnt_lsb_minus4 */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - } else if (pic_order_cnt_type == 1) { - unsigned int i, num_ref_frames_in_pic_order_cnt_cycle; - - /* delta_pic_order_always_zero_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - /* offset_for_non_ref_pic */ - ret = rbsp_read_sev(&sps, NULL); - if (ret) - return ret; - /* offset_for_top_to_bottom_field */ - ret = rbsp_read_sev(&sps, NULL); - if (ret) - return ret; - - ret = rbsp_read_uev(&sps, - &num_ref_frames_in_pic_order_cnt_cycle); - if (ret) - return ret; - for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { - /* offset_for_ref_frame */ - ret = rbsp_read_sev(&sps, NULL); - if (ret) - return ret; - } - } - - /* max_num_ref_frames */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - - /* gaps_in_frame_num_value_allowed_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - ret = rbsp_read_uev(&sps, &pic_width_in_mbs_minus1); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &pic_height_in_map_units_minus1); - if (ret) - return ret; - frame_mbs_only_flag = ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - if (!frame_mbs_only_flag) { - /* mb_adaptive_frame_field_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - } - /* direct_8x8_inference_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - - /* Mark position of the frame cropping flag */ - pos = sps.pos; - frame_cropping_flag = ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - if (frame_cropping_flag) { - unsigned int crop_left, crop_top; - - ret = rbsp_read_uev(&sps, &crop_left); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &crop_right); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &crop_top); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &crop_bottom); - if (ret) - return ret; - } - vui_parameters_present_flag = ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - if (vui_parameters_present_flag) { - dev_err(ctx->fh.vdev->dev_parent, - "%s: Handling vui_parameters not implemented\n", - __func__); - return -EINVAL; - } - - crop_right = round_up(width, 16) - width; - crop_bottom = round_up(height, 16) - height; - crop_right /= 2; - if (frame_mbs_only_flag) - crop_bottom /= 2; - else - crop_bottom /= 4; - - - sps.size = max_size - 5; - sps.pos = pos; - frame_cropping_flag = 1; - ret = rbsp_write_bit(&sps, frame_cropping_flag); - if (ret) - return ret; - ret = rbsp_write_uev(&sps, 0); /* crop_left */ - if (ret) - return ret; - ret = rbsp_write_uev(&sps, crop_right); - if (ret) - return ret; - ret = rbsp_write_uev(&sps, 0); /* crop_top */ - if (ret) - return ret; - ret = rbsp_write_uev(&sps, crop_bottom); - if (ret) - return ret; - ret = rbsp_write_bit(&sps, 0); /* vui_parameters_present_flag */ - if (ret) - return ret; - ret = rbsp_write_bit(&sps, 1); - if (ret) - return ret; - - *size = 5 + DIV_ROUND_UP(sps.pos, 8); - - return 0; -} diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c deleted file mode 100644 index a72f4655e5ad..000000000000 --- a/drivers/media/platform/coda/coda-jpeg.c +++ /dev/null @@ -1,1546 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - JPEG support functions - * - * Copyright (C) 2014 Philipp Zabel, Pengutronix - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "coda.h" -#include "trace.h" - -#define SOI_MARKER 0xffd8 -#define APP9_MARKER 0xffe9 -#define DRI_MARKER 0xffdd -#define DQT_MARKER 0xffdb -#define DHT_MARKER 0xffc4 -#define SOF_MARKER 0xffc0 -#define SOS_MARKER 0xffda -#define EOI_MARKER 0xffd9 - -enum { - CODA9_JPEG_FORMAT_420, - CODA9_JPEG_FORMAT_422, - CODA9_JPEG_FORMAT_224, - CODA9_JPEG_FORMAT_444, - CODA9_JPEG_FORMAT_400, -}; - -struct coda_huff_tab { - u8 luma_dc[16 + 12]; - u8 chroma_dc[16 + 12]; - u8 luma_ac[16 + 162]; - u8 chroma_ac[16 + 162]; - - /* DC Luma, DC Chroma, AC Luma, AC Chroma */ - s16 min[4 * 16]; - s16 max[4 * 16]; - s8 ptr[4 * 16]; -}; - -#define CODA9_JPEG_ENC_HUFF_DATA_SIZE (256 + 256 + 16 + 16) - -/* - * Typical Huffman tables for 8-bit precision luminance and - * chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3 - */ - -static const unsigned char luma_dc[16 + 12] = { - /* bits */ - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* values */ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc[16 + 12] = { - /* bits */ - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - /* values */ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac[16 + 162 + 2] = { - /* bits */ - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - /* values */ - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, /* padded to 32-bit */ -}; - -static const unsigned char chroma_ac[16 + 162 + 2] = { - /* bits */ - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - /* values */ - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, /* padded to 32-bit */ -}; - -/* - * Quantization tables for luminance and chrominance components in - * zig-zag scan order from the Freescale i.MX VPU libraries - */ - -static unsigned char luma_q[64] = { - 0x06, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x05, - 0x05, 0x06, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0b, - 0x08, 0x06, 0x06, 0x08, 0x0b, 0x0c, 0x0a, 0x0a, - 0x0b, 0x0a, 0x0a, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, -}; - -static unsigned char chroma_q[64] = { - 0x07, 0x07, 0x07, 0x0d, 0x0c, 0x0d, 0x18, 0x10, - 0x10, 0x18, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, - 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, -}; - -static const unsigned char width_align[] = { - [CODA9_JPEG_FORMAT_420] = 16, - [CODA9_JPEG_FORMAT_422] = 16, - [CODA9_JPEG_FORMAT_224] = 8, - [CODA9_JPEG_FORMAT_444] = 8, - [CODA9_JPEG_FORMAT_400] = 8, -}; - -static const unsigned char height_align[] = { - [CODA9_JPEG_FORMAT_420] = 16, - [CODA9_JPEG_FORMAT_422] = 8, - [CODA9_JPEG_FORMAT_224] = 16, - [CODA9_JPEG_FORMAT_444] = 8, - [CODA9_JPEG_FORMAT_400] = 8, -}; - -static int coda9_jpeg_chroma_format(u32 pixfmt) -{ - switch (pixfmt) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_NV12: - return CODA9_JPEG_FORMAT_420; - case V4L2_PIX_FMT_YUV422P: - return CODA9_JPEG_FORMAT_422; - case V4L2_PIX_FMT_YUV444: - return CODA9_JPEG_FORMAT_444; - case V4L2_PIX_FMT_GREY: - return CODA9_JPEG_FORMAT_400; - } - return -EINVAL; -} - -struct coda_memcpy_desc { - int offset; - const void *src; - size_t len; -}; - -static void coda_memcpy_parabuf(void *parabuf, - const struct coda_memcpy_desc *desc) -{ - u32 *dst = parabuf + desc->offset; - const u32 *src = desc->src; - int len = desc->len / 4; - int i; - - for (i = 0; i < len; i += 2) { - dst[i + 1] = swab32(src[i]); - dst[i] = swab32(src[i + 1]); - } -} - -int coda_jpeg_write_tables(struct coda_ctx *ctx) -{ - int i; - static const struct coda_memcpy_desc huff[8] = { - { 0, luma_dc, sizeof(luma_dc) }, - { 32, luma_ac, sizeof(luma_ac) }, - { 216, chroma_dc, sizeof(chroma_dc) }, - { 248, chroma_ac, sizeof(chroma_ac) }, - }; - struct coda_memcpy_desc qmat[3] = { - { 512, ctx->params.jpeg_qmat_tab[0], 64 }, - { 576, ctx->params.jpeg_qmat_tab[1], 64 }, - { 640, ctx->params.jpeg_qmat_tab[1], 64 }, - }; - - /* Write huffman tables to parameter memory */ - for (i = 0; i < ARRAY_SIZE(huff); i++) - coda_memcpy_parabuf(ctx->parabuf.vaddr, huff + i); - - /* Write Q-matrix to parameter memory */ - for (i = 0; i < ARRAY_SIZE(qmat); i++) - coda_memcpy_parabuf(ctx->parabuf.vaddr, qmat + i); - - return 0; -} - -bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) -{ - void *vaddr = vb2_plane_vaddr(vb, 0); - u16 soi, eoi; - int len, i; - - soi = be16_to_cpup((__be16 *)vaddr); - if (soi != SOI_MARKER) - return false; - - len = vb2_get_plane_payload(vb, 0); - vaddr += len - 2; - for (i = 0; i < 32; i++) { - eoi = be16_to_cpup((__be16 *)(vaddr - i)); - if (eoi == EOI_MARKER) { - if (i > 0) - vb2_set_plane_payload(vb, 0, len - i); - return true; - } - } - - return false; -} - -static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num); - -int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) -{ - struct coda_dev *dev = ctx->dev; - u8 *buf = vb2_plane_vaddr(vb, 0); - size_t len = vb2_get_plane_payload(vb, 0); - struct v4l2_jpeg_scan_header scan_header; - struct v4l2_jpeg_reference quantization_tables[4] = { }; - struct v4l2_jpeg_reference huffman_tables[4] = { }; - struct v4l2_jpeg_header header = { - .scan = &scan_header, - .quantization_tables = quantization_tables, - .huffman_tables = huffman_tables, - }; - struct coda_q_data *q_data_src; - struct coda_huff_tab *huff_tab; - int i, j, ret; - - ret = v4l2_jpeg_parse_header(buf, len, &header); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to parse header\n"); - return ret; - } - - ctx->params.jpeg_restart_interval = header.restart_interval; - - /* check frame header */ - if (header.frame.height > ctx->codec->max_h || - header.frame.width > ctx->codec->max_w) { - v4l2_err(&dev->v4l2_dev, "invalid dimensions: %dx%d\n", - header.frame.width, header.frame.height); - return -EINVAL; - } - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (header.frame.height != q_data_src->height || - header.frame.width != q_data_src->width) { - v4l2_err(&dev->v4l2_dev, - "dimensions don't match format: %dx%d\n", - header.frame.width, header.frame.height); - return -EINVAL; - } - - if (header.frame.num_components != 3) { - v4l2_err(&dev->v4l2_dev, - "unsupported number of components: %d\n", - header.frame.num_components); - return -EINVAL; - } - - /* install quantization tables */ - if (quantization_tables[3].start) { - v4l2_err(&dev->v4l2_dev, - "only 3 quantization tables supported\n"); - return -EINVAL; - } - for (i = 0; i < 3; i++) { - if (!quantization_tables[i].start) - continue; - if (quantization_tables[i].length != 64) { - v4l2_err(&dev->v4l2_dev, - "only 8-bit quantization tables supported\n"); - continue; - } - if (!ctx->params.jpeg_qmat_tab[i]) { - ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[i]) - return -ENOMEM; - } - memcpy(ctx->params.jpeg_qmat_tab[i], - quantization_tables[i].start, 64); - } - - /* install Huffman tables */ - for (i = 0; i < 4; i++) { - if (!huffman_tables[i].start) { - v4l2_err(&dev->v4l2_dev, "missing Huffman table\n"); - return -EINVAL; - } - /* AC tables should be between 17 -> 178, DC between 17 -> 28 */ - if (huffman_tables[i].length < 17 || - huffman_tables[i].length > 178 || - ((i & 2) == 0 && huffman_tables[i].length > 28)) { - v4l2_err(&dev->v4l2_dev, - "invalid Huffman table %d length: %zu\n", - i, huffman_tables[i].length); - return -EINVAL; - } - } - huff_tab = ctx->params.jpeg_huff_tab; - if (!huff_tab) { - huff_tab = kzalloc(sizeof(struct coda_huff_tab), GFP_KERNEL); - if (!huff_tab) - return -ENOMEM; - ctx->params.jpeg_huff_tab = huff_tab; - } - - memset(huff_tab, 0, sizeof(*huff_tab)); - memcpy(huff_tab->luma_dc, huffman_tables[0].start, huffman_tables[0].length); - memcpy(huff_tab->chroma_dc, huffman_tables[1].start, huffman_tables[1].length); - memcpy(huff_tab->luma_ac, huffman_tables[2].start, huffman_tables[2].length); - memcpy(huff_tab->chroma_ac, huffman_tables[3].start, huffman_tables[3].length); - - /* check scan header */ - for (i = 0; i < scan_header.num_components; i++) { - struct v4l2_jpeg_scan_component_spec *scan_component; - - scan_component = &scan_header.component[i]; - for (j = 0; j < header.frame.num_components; j++) { - if (header.frame.component[j].component_identifier == - scan_component->component_selector) - break; - } - if (j == header.frame.num_components) - continue; - - ctx->params.jpeg_huff_dc_index[j] = - scan_component->dc_entropy_coding_table_selector; - ctx->params.jpeg_huff_ac_index[j] = - scan_component->ac_entropy_coding_table_selector; - } - - /* Generate Huffman table information */ - for (i = 0; i < 4; i++) - coda9_jpeg_gen_dec_huff_tab(ctx, i); - - /* start of entropy coded segment */ - ctx->jpeg_ecs_offset = header.ecs_offset; - - switch (header.frame.subsampling) { - case V4L2_JPEG_CHROMA_SUBSAMPLING_420: - case V4L2_JPEG_CHROMA_SUBSAMPLING_422: - ctx->params.jpeg_chroma_subsampling = header.frame.subsampling; - break; - default: - v4l2_err(&dev->v4l2_dev, "chroma subsampling not supported: %d", - header.frame.subsampling); - return -EINVAL; - } - - return 0; -} - -static inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits, - int num_values) -{ - s8 *values = (s8 *)(bits + 16); - int huff_length, i; - - for (huff_length = 0, i = 0; i < 16; i++) - huff_length += bits[i]; - for (i = huff_length; i < num_values; i++) - values[i] = -1; - for (i = 0; i < num_values; i++) - coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA); -} - -static int coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx) -{ - struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; - struct coda_dev *dev = ctx->dev; - s16 *huff_min = huff_tab->min; - s16 *huff_max = huff_tab->max; - s8 *huff_ptr = huff_tab->ptr; - int i; - - /* MIN Tables */ - coda_write(dev, 0x003, CODA9_REG_JPEG_HUFF_CTRL); - coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_ADDR); - for (i = 0; i < 4 * 16; i++) - coda_write(dev, (s32)huff_min[i], CODA9_REG_JPEG_HUFF_DATA); - - /* MAX Tables */ - coda_write(dev, 0x403, CODA9_REG_JPEG_HUFF_CTRL); - coda_write(dev, 0x440, CODA9_REG_JPEG_HUFF_ADDR); - for (i = 0; i < 4 * 16; i++) - coda_write(dev, (s32)huff_max[i], CODA9_REG_JPEG_HUFF_DATA); - - /* PTR Tables */ - coda_write(dev, 0x803, CODA9_REG_JPEG_HUFF_CTRL); - coda_write(dev, 0x880, CODA9_REG_JPEG_HUFF_ADDR); - for (i = 0; i < 4 * 16; i++) - coda_write(dev, (s32)huff_ptr[i], CODA9_REG_JPEG_HUFF_DATA); - - /* VAL Tables: DC Luma, DC Chroma, AC Luma, AC Chroma */ - coda_write(dev, 0xc03, CODA9_REG_JPEG_HUFF_CTRL); - coda9_jpeg_write_huff_values(dev, huff_tab->luma_dc, 12); - coda9_jpeg_write_huff_values(dev, huff_tab->chroma_dc, 12); - coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162); - coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162); - coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL); - return 0; -} - -static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev, - u8 *qmat, int index) -{ - int i; - - coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); - for (i = 0; i < 64; i++) - coda_write(dev, qmat[i], CODA9_REG_JPEG_QMAT_DATA); - coda_write(dev, 0, CODA9_REG_JPEG_QMAT_CTRL); -} - -static void coda9_jpeg_qmat_setup(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int *qmat_index = ctx->params.jpeg_qmat_index; - u8 **qmat_tab = ctx->params.jpeg_qmat_tab; - - coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[0]], 0x00); - coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[1]], 0x40); - coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[2]], 0x80); -} - -static void coda9_jpeg_dec_bbc_gbu_setup(struct coda_ctx *ctx, - struct vb2_buffer *buf, u32 ecs_offset) -{ - struct coda_dev *dev = ctx->dev; - int page_ptr, word_ptr, bit_ptr; - u32 bbc_base_addr, end_addr; - int bbc_cur_pos; - int ret, val; - - bbc_base_addr = vb2_dma_contig_plane_dma_addr(buf, 0); - end_addr = bbc_base_addr + vb2_get_plane_payload(buf, 0); - - page_ptr = ecs_offset / 256; - word_ptr = (ecs_offset % 256) / 4; - if (page_ptr & 1) - word_ptr += 64; - bit_ptr = (ecs_offset % 4) * 8; - if (word_ptr & 1) - bit_ptr += 32; - word_ptr &= ~0x1; - - coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_WR_PTR); - coda_write(dev, bbc_base_addr, CODA9_REG_JPEG_BBC_BAS_ADDR); - - /* Leave 3 256-byte page margin to avoid a BBC interrupt */ - coda_write(dev, end_addr + 256 * 3 + 256, CODA9_REG_JPEG_BBC_END_ADDR); - val = DIV_ROUND_UP(vb2_plane_size(buf, 0), 256) + 3; - coda_write(dev, BIT(31) | val, CODA9_REG_JPEG_BBC_STRM_CTRL); - - bbc_cur_pos = page_ptr; - coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); - coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), - CODA9_REG_JPEG_BBC_EXT_ADDR); - coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); - coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); - do { - ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); - } while (ret == 1); - - bbc_cur_pos++; - coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); - coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), - CODA9_REG_JPEG_BBC_EXT_ADDR); - coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); - coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); - do { - ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); - } while (ret == 1); - - bbc_cur_pos++; - coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); - coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); - - coda_write(dev, 0, CODA9_REG_JPEG_GBU_TT_CNT); - coda_write(dev, word_ptr, CODA9_REG_JPEG_GBU_WD_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); - coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); - if (page_ptr & 1) { - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBIR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBHR); - } else { - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); - } - coda_write(dev, 4, CODA9_REG_JPEG_GBU_CTRL); - coda_write(dev, bit_ptr, CODA9_REG_JPEG_GBU_FF_RPTR); - coda_write(dev, 3, CODA9_REG_JPEG_GBU_CTRL); -} - -static const int bus_req_num[] = { - [CODA9_JPEG_FORMAT_420] = 2, - [CODA9_JPEG_FORMAT_422] = 3, - [CODA9_JPEG_FORMAT_224] = 3, - [CODA9_JPEG_FORMAT_444] = 4, - [CODA9_JPEG_FORMAT_400] = 4, -}; - -#define MCU_INFO(mcu_block_num, comp_num, comp0_info, comp1_info, comp2_info) \ - (((mcu_block_num) << CODA9_JPEG_MCU_BLOCK_NUM_OFFSET) | \ - ((comp_num) << CODA9_JPEG_COMP_NUM_OFFSET) | \ - ((comp0_info) << CODA9_JPEG_COMP0_INFO_OFFSET) | \ - ((comp1_info) << CODA9_JPEG_COMP1_INFO_OFFSET) | \ - ((comp2_info) << CODA9_JPEG_COMP2_INFO_OFFSET)) - -static const u32 mcu_info[] = { - [CODA9_JPEG_FORMAT_420] = MCU_INFO(6, 3, 10, 5, 5), - [CODA9_JPEG_FORMAT_422] = MCU_INFO(4, 3, 9, 5, 5), - [CODA9_JPEG_FORMAT_224] = MCU_INFO(4, 3, 6, 5, 5), - [CODA9_JPEG_FORMAT_444] = MCU_INFO(3, 3, 5, 5, 5), - [CODA9_JPEG_FORMAT_400] = MCU_INFO(1, 1, 5, 0, 0), -}; - -/* - * Convert Huffman table specifcations to tables of codes and code lengths. - * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] - * - * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf - */ -static int coda9_jpeg_gen_enc_huff_tab(struct coda_ctx *ctx, int tab_num, - int *ehufsi, int *ehufco) -{ - int i, j, k, lastk, si, code, maxsymbol; - const u8 *bits, *huffval; - struct { - int size[256]; - int code[256]; - } *huff; - static const unsigned char *huff_tabs[4] = { - luma_dc, luma_ac, chroma_dc, chroma_ac, - }; - int ret = -EINVAL; - - huff = kzalloc(sizeof(*huff), GFP_KERNEL); - if (!huff) - return -ENOMEM; - - bits = huff_tabs[tab_num]; - huffval = huff_tabs[tab_num] + 16; - - maxsymbol = tab_num & 1 ? 256 : 16; - - /* Figure C.1 - Generation of table of Huffman code sizes */ - k = 0; - for (i = 1; i <= 16; i++) { - j = bits[i - 1]; - if (k + j > maxsymbol) - goto out; - while (j--) - huff->size[k++] = i; - } - lastk = k; - - /* Figure C.2 - Generation of table of Huffman codes */ - k = 0; - code = 0; - si = huff->size[0]; - while (k < lastk) { - while (huff->size[k] == si) { - huff->code[k++] = code; - code++; - } - if (code >= (1 << si)) - goto out; - code <<= 1; - si++; - } - - /* Figure C.3 - Ordering procedure for encoding procedure code tables */ - for (k = 0; k < lastk; k++) { - i = huffval[k]; - if (i >= maxsymbol || ehufsi[i]) - goto out; - ehufco[i] = huff->code[k]; - ehufsi[i] = huff->size[k]; - } - - ret = 0; -out: - kfree(huff); - return ret; -} - -#define DC_TABLE_INDEX0 0 -#define AC_TABLE_INDEX0 1 -#define DC_TABLE_INDEX1 2 -#define AC_TABLE_INDEX1 3 - -static u8 *coda9_jpeg_get_huff_bits(struct coda_ctx *ctx, int tab_num) -{ - struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; - - if (!huff_tab) - return NULL; - - switch (tab_num) { - case DC_TABLE_INDEX0: return huff_tab->luma_dc; - case AC_TABLE_INDEX0: return huff_tab->luma_ac; - case DC_TABLE_INDEX1: return huff_tab->chroma_dc; - case AC_TABLE_INDEX1: return huff_tab->chroma_ac; - } - - return NULL; -} - -static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num) -{ - int ptr_cnt = 0, huff_code = 0, zero_flag = 0, data_flag = 0; - u8 *huff_bits; - s16 *huff_max; - s16 *huff_min; - s8 *huff_ptr; - int ofs; - int i; - - huff_bits = coda9_jpeg_get_huff_bits(ctx, tab_num); - if (!huff_bits) - return -EINVAL; - - /* DC/AC Luma, DC/AC Chroma -> DC Luma/Chroma, AC Luma/Chroma */ - ofs = ((tab_num & 1) << 1) | ((tab_num >> 1) & 1); - ofs *= 16; - - huff_ptr = ctx->params.jpeg_huff_tab->ptr + ofs; - huff_max = ctx->params.jpeg_huff_tab->max + ofs; - huff_min = ctx->params.jpeg_huff_tab->min + ofs; - - for (i = 0; i < 16; i++) { - if (huff_bits[i]) { - huff_ptr[i] = ptr_cnt; - ptr_cnt += huff_bits[i]; - huff_min[i] = huff_code; - huff_max[i] = huff_code + (huff_bits[i] - 1); - data_flag = 1; - zero_flag = 0; - } else { - huff_ptr[i] = -1; - huff_min[i] = -1; - huff_max[i] = -1; - zero_flag = 1; - } - - if (data_flag == 1) { - if (zero_flag == 1) - huff_code <<= 1; - else - huff_code = (huff_max[i] + 1) << 1; - } - } - - return 0; -} - -static int coda9_jpeg_load_huff_tab(struct coda_ctx *ctx) -{ - struct { - int size[4][256]; - int code[4][256]; - } *huff; - u32 *huff_data; - int i, j; - int ret; - - huff = kzalloc(sizeof(*huff), GFP_KERNEL); - if (!huff) - return -ENOMEM; - - /* Generate all four (luma/chroma DC/AC) code/size lookup tables */ - for (i = 0; i < 4; i++) { - ret = coda9_jpeg_gen_enc_huff_tab(ctx, i, huff->size[i], - huff->code[i]); - if (ret) - goto out; - } - - if (!ctx->params.jpeg_huff_data) { - ctx->params.jpeg_huff_data = - kzalloc(sizeof(u32) * CODA9_JPEG_ENC_HUFF_DATA_SIZE, - GFP_KERNEL); - if (!ctx->params.jpeg_huff_data) { - ret = -ENOMEM; - goto out; - } - } - huff_data = ctx->params.jpeg_huff_data; - - for (j = 0; j < 4; j++) { - /* Store Huffman lookup tables in AC0, AC1, DC0, DC1 order */ - int t = (j == 0) ? AC_TABLE_INDEX0 : - (j == 1) ? AC_TABLE_INDEX1 : - (j == 2) ? DC_TABLE_INDEX0 : - DC_TABLE_INDEX1; - /* DC tables only have 16 entries */ - int len = (j < 2) ? 256 : 16; - - for (i = 0; i < len; i++) { - if (huff->size[t][i] == 0 && huff->code[t][i] == 0) - *(huff_data++) = 0; - else - *(huff_data++) = - ((huff->size[t][i] - 1) << 16) | - huff->code[t][i]; - } - } - - ret = 0; -out: - kfree(huff); - return ret; -} - -static void coda9_jpeg_write_huff_tab(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u32 *huff_data = ctx->params.jpeg_huff_data; - int i; - - /* Write Huffman size/code lookup tables in AC0, AC1, DC0, DC1 order */ - coda_write(dev, 0x3, CODA9_REG_JPEG_HUFF_CTRL); - for (i = 0; i < CODA9_JPEG_ENC_HUFF_DATA_SIZE; i++) - coda_write(dev, *(huff_data++), CODA9_REG_JPEG_HUFF_DATA); - coda_write(dev, 0x0, CODA9_REG_JPEG_HUFF_CTRL); -} - -static inline void coda9_jpeg_write_qmat_quotients(struct coda_dev *dev, - u8 *qmat, int index) -{ - int i; - - coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); - for (i = 0; i < 64; i++) - coda_write(dev, 0x80000 / qmat[i], CODA9_REG_JPEG_QMAT_DATA); - coda_write(dev, index, CODA9_REG_JPEG_QMAT_CTRL); -} - -static void coda9_jpeg_load_qmat_tab(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u8 *luma_tab; - u8 *chroma_tab; - - luma_tab = ctx->params.jpeg_qmat_tab[0]; - if (!luma_tab) - luma_tab = luma_q; - - chroma_tab = ctx->params.jpeg_qmat_tab[1]; - if (!chroma_tab) - chroma_tab = chroma_q; - - coda9_jpeg_write_qmat_quotients(dev, luma_tab, 0x00); - coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x40); - coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x80); -} - -struct coda_jpeg_stream { - u8 *curr; - u8 *end; -}; - -static inline int coda_jpeg_put_byte(u8 byte, struct coda_jpeg_stream *stream) -{ - if (stream->curr >= stream->end) - return -EINVAL; - - *stream->curr++ = byte; - - return 0; -} - -static inline int coda_jpeg_put_word(u16 word, struct coda_jpeg_stream *stream) -{ - if (stream->curr + sizeof(__be16) > stream->end) - return -EINVAL; - - put_unaligned_be16(word, stream->curr); - stream->curr += sizeof(__be16); - - return 0; -} - -static int coda_jpeg_put_table(u16 marker, u8 index, const u8 *table, - size_t len, struct coda_jpeg_stream *stream) -{ - int i, ret; - - ret = coda_jpeg_put_word(marker, stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(3 + len, stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(index, stream); - for (i = 0; i < len && ret == 0; i++) - ret = coda_jpeg_put_byte(table[i], stream); - - return ret; -} - -static int coda_jpeg_define_quantization_table(struct coda_ctx *ctx, u8 index, - struct coda_jpeg_stream *stream) -{ - return coda_jpeg_put_table(DQT_MARKER, index, - ctx->params.jpeg_qmat_tab[index], 64, - stream); -} - -static int coda_jpeg_define_huffman_table(u8 index, const u8 *table, size_t len, - struct coda_jpeg_stream *stream) -{ - return coda_jpeg_put_table(DHT_MARKER, index, table, len, stream); -} - -static int coda9_jpeg_encode_header(struct coda_ctx *ctx, int len, u8 *buf) -{ - struct coda_jpeg_stream stream = { buf, buf + len }; - struct coda_q_data *q_data_src; - int chroma_format, comp_num; - int i, ret, pad; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); - if (chroma_format < 0) - return 0; - - /* Start Of Image */ - ret = coda_jpeg_put_word(SOI_MARKER, &stream); - if (ret < 0) - return ret; - - /* Define Restart Interval */ - if (ctx->params.jpeg_restart_interval) { - ret = coda_jpeg_put_word(DRI_MARKER, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(4, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(ctx->params.jpeg_restart_interval, - &stream); - if (ret < 0) - return ret; - } - - /* Define Quantization Tables */ - ret = coda_jpeg_define_quantization_table(ctx, 0x00, &stream); - if (ret < 0) - return ret; - if (chroma_format != CODA9_JPEG_FORMAT_400) { - ret = coda_jpeg_define_quantization_table(ctx, 0x01, &stream); - if (ret < 0) - return ret; - } - - /* Define Huffman Tables */ - ret = coda_jpeg_define_huffman_table(0x00, luma_dc, 16 + 12, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_define_huffman_table(0x10, luma_ac, 16 + 162, &stream); - if (ret < 0) - return ret; - if (chroma_format != CODA9_JPEG_FORMAT_400) { - ret = coda_jpeg_define_huffman_table(0x01, chroma_dc, 16 + 12, - &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_define_huffman_table(0x11, chroma_ac, 16 + 162, - &stream); - if (ret < 0) - return ret; - } - - /* Start Of Frame */ - ret = coda_jpeg_put_word(SOF_MARKER, &stream); - if (ret < 0) - return ret; - comp_num = (chroma_format == CODA9_JPEG_FORMAT_400) ? 1 : 3; - ret = coda_jpeg_put_word(8 + comp_num * 3, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(0x08, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(q_data_src->height, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(q_data_src->width, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(comp_num, &stream); - if (ret < 0) - return ret; - for (i = 0; i < comp_num; i++) { - static unsigned char subsampling[5][3] = { - [CODA9_JPEG_FORMAT_420] = { 0x22, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_422] = { 0x21, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_224] = { 0x12, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_444] = { 0x11, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_400] = { 0x11 }, - }; - - /* Component identifier, matches SOS */ - ret = coda_jpeg_put_byte(i + 1, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(subsampling[chroma_format][i], - &stream); - if (ret < 0) - return ret; - /* Chroma table index */ - ret = coda_jpeg_put_byte((i == 0) ? 0 : 1, &stream); - if (ret < 0) - return ret; - } - - /* Pad to multiple of 8 bytes */ - pad = (stream.curr - buf) % 8; - if (pad) { - pad = 8 - pad; - while (pad--) { - ret = coda_jpeg_put_byte(0x00, &stream); - if (ret < 0) - return ret; - } - } - - return stream.curr - buf; -} - -/* - * Scale quantization table using nonlinear scaling factor - * u8 qtab[64], scale [50,190] - */ -static void coda_scale_quant_table(u8 *q_tab, int scale) -{ - unsigned int temp; - int i; - - for (i = 0; i < 64; i++) { - temp = DIV_ROUND_CLOSEST((unsigned int)q_tab[i] * scale, 100); - if (temp <= 0) - temp = 1; - if (temp > 255) - temp = 255; - q_tab[i] = (unsigned char)temp; - } -} - -void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality) -{ - unsigned int scale; - - ctx->params.jpeg_quality = quality; - - /* Clip quality setting to [5,100] interval */ - if (quality > 100) - quality = 100; - if (quality < 5) - quality = 5; - - /* - * Non-linear scaling factor: - * [5,50] -> [1000..100], [51,100] -> [98..0] - */ - if (quality < 50) - scale = 5000 / quality; - else - scale = 200 - 2 * quality; - - if (ctx->params.jpeg_qmat_tab[0]) { - memcpy(ctx->params.jpeg_qmat_tab[0], luma_q, 64); - coda_scale_quant_table(ctx->params.jpeg_qmat_tab[0], scale); - } - if (ctx->params.jpeg_qmat_tab[1]) { - memcpy(ctx->params.jpeg_qmat_tab[1], chroma_q, 64); - coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale); - } -} - -/* - * Encoder context operations - */ - -static int coda9_jpeg_start_encoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int ret; - - ret = coda9_jpeg_load_huff_tab(ctx); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n"); - return ret; - } - if (!ctx->params.jpeg_qmat_tab[0]) - ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[1]) - ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); - coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); - - return 0; -} - -static int coda9_jpeg_prepare_encode(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 start_addr, end_addr; - u16 aligned_width, aligned_height; - bool chroma_interleave; - int chroma_format; - int header_len; - int ret; - ktime_t timeout; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) - vb2_set_plane_payload(&src_buf->vb2_buf, 0, - vb2_plane_size(&src_buf->vb2_buf, 0)); - - src_buf->sequence = ctx->osequence; - dst_buf->sequence = ctx->osequence; - ctx->osequence++; - - src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; - - coda_set_gdi_regs(ctx); - - start_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - end_addr = start_addr + vb2_plane_size(&dst_buf->vb2_buf, 0); - - chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); - if (chroma_format < 0) - return chroma_format; - - /* Round image dimensions to multiple of MCU size */ - aligned_width = round_up(q_data_src->width, width_align[chroma_format]); - aligned_height = round_up(q_data_src->height, - height_align[chroma_format]); - if (aligned_width != q_data_src->bytesperline) { - v4l2_err(&dev->v4l2_dev, "wrong stride: %d instead of %d\n", - aligned_width, q_data_src->bytesperline); - } - - header_len = - coda9_jpeg_encode_header(ctx, - vb2_plane_size(&dst_buf->vb2_buf, 0), - vb2_plane_vaddr(&dst_buf->vb2_buf, 0)); - if (header_len < 0) - return header_len; - - coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_BAS_ADDR); - coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_END_ADDR); - coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_WR_PTR); - coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_RD_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_CUR_POS); - /* 64 words per 256-byte page */ - coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); - coda_write(dev, start_addr, CODA9_REG_JPEG_BBC_EXT_ADDR); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_INT_ADDR); - - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BT_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_WD_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); - coda_write(dev, BIT(31) | ((end_addr - start_addr - header_len) / 256), - CODA9_REG_JPEG_BBC_STRM_CTRL); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_CTRL); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_FF_RPTR); - coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); - - chroma_interleave = (q_data_src->fourcc == V4L2_PIX_FMT_NV12); - coda_write(dev, CODA9_JPEG_PIC_CTRL_TC_DIRECTION | - CODA9_JPEG_PIC_CTRL_ENCODER_EN, CODA9_REG_JPEG_PIC_CTRL); - coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); - coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); - coda_write(dev, ctx->params.jpeg_restart_interval, - CODA9_REG_JPEG_RST_INTVAL); - coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); - - coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); - - coda9_jpeg_write_huff_tab(ctx); - coda9_jpeg_load_qmat_tab(ctx); - - if (ctx->params.rot_mode & CODA_ROT_90) { - aligned_width = aligned_height; - aligned_height = q_data_src->bytesperline; - if (chroma_format == CODA9_JPEG_FORMAT_422) - chroma_format = CODA9_JPEG_FORMAT_224; - else if (chroma_format == CODA9_JPEG_FORMAT_224) - chroma_format = CODA9_JPEG_FORMAT_422; - } - /* These need to be multiples of MCU size */ - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_REG_JPEG_PIC_SIZE); - coda_write(dev, ctx->params.rot_mode ? - (CODA_ROT_MIR_ENABLE | ctx->params.rot_mode) : 0, - CODA9_REG_JPEG_ROT_INFO); - - coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); - - coda_write(dev, 1, CODA9_GDI_CONTROL); - timeout = ktime_add_us(ktime_get(), 100000); - do { - ret = coda_read(dev, CODA9_GDI_STATUS); - if (ktime_compare(ktime_get(), timeout) > 0) { - v4l2_err(&dev->v4l2_dev, "timeout waiting for GDI\n"); - return -ETIMEDOUT; - } - } while (!ret); - - coda_write(dev, (chroma_format << 17) | (chroma_interleave << 16) | - q_data_src->bytesperline, CODA9_GDI_INFO_CONTROL); - /* The content of this register seems to be irrelevant: */ - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_GDI_INFO_PIC_SIZE); - - coda_write_base(ctx, q_data_src, src_buf, CODA9_GDI_INFO_BASE_Y); - - coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); - coda_write(dev, 0, CODA9_GDI_CONTROL); - coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); - - coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - - trace_coda_jpeg_run(ctx, src_buf); - - coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); - - return 0; -} - -static void coda9_jpeg_finish_encode(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr, start_ptr; - u32 err_mb; - - if (ctx->aborting) { - coda_write(ctx->dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); - return; - } - - /* - * Lock to make sure that an encoder stop command running in parallel - * will either already have marked src_buf as last, or it will wake up - * the capture queue after the buffers are returned. - */ - mutex_lock(&ctx->wakeup_mutex); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - - trace_coda_jpeg_done(ctx, dst_buf); - - /* - * Set plane payload to the number of bytes written out - * by the JPEG processing unit - */ - start_ptr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); - - err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); - if (err_mb) - coda_dbg(1, ctx, "ERRMB: 0x%x\n", err_mb); - - coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); - - dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : - VB2_BUF_STATE_DONE); - mutex_unlock(&ctx->wakeup_mutex); - - coda_dbg(1, ctx, "job finished: encoded frame (%u)%s\n", - dst_buf->sequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); - - /* - * Reset JPEG processing unit after each encode run to work - * around hangups when switching context between encoder and - * decoder. - */ - coda_hw_reset(ctx); -} - -static void coda9_jpeg_encode_timeout(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u32 end_addr, wr_ptr; - - /* Handle missing BBC overflow interrupt via timeout */ - end_addr = coda_read(dev, CODA9_REG_JPEG_BBC_END_ADDR); - wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); - if (wr_ptr >= end_addr - 256) { - v4l2_err(&dev->v4l2_dev, "JPEG too large for capture buffer\n"); - coda9_jpeg_finish_encode(ctx); - return; - } - - coda_hw_reset(ctx); -} - -static void coda9_jpeg_release(struct coda_ctx *ctx) -{ - int i; - - if (ctx->params.jpeg_qmat_tab[0] == luma_q) - ctx->params.jpeg_qmat_tab[0] = NULL; - if (ctx->params.jpeg_qmat_tab[1] == chroma_q) - ctx->params.jpeg_qmat_tab[1] = NULL; - for (i = 0; i < 3; i++) - kfree(ctx->params.jpeg_qmat_tab[i]); - kfree(ctx->params.jpeg_huff_data); - kfree(ctx->params.jpeg_huff_tab); -} - -const struct coda_context_ops coda9_jpeg_encode_ops = { - .queue_init = coda_encoder_queue_init, - .start_streaming = coda9_jpeg_start_encoding, - .prepare_run = coda9_jpeg_prepare_encode, - .finish_run = coda9_jpeg_finish_encode, - .run_timeout = coda9_jpeg_encode_timeout, - .release = coda9_jpeg_release, -}; - -/* - * Decoder context operations - */ - -static int coda9_jpeg_start_decoding(struct coda_ctx *ctx) -{ - ctx->params.jpeg_qmat_index[0] = 0; - ctx->params.jpeg_qmat_index[1] = 1; - ctx->params.jpeg_qmat_index[2] = 1; - ctx->params.jpeg_qmat_tab[0] = luma_q; - ctx->params.jpeg_qmat_tab[1] = chroma_q; - /* nothing more to do here */ - - /* TODO: we could already scan the first header to get the chroma - * format. - */ - - return 0; -} - -static int coda9_jpeg_prepare_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int aligned_width, aligned_height; - int chroma_format; - int ret; - u32 val, dst_fourcc; - struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - int chroma_interleave; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) - vb2_set_plane_payload(&src_buf->vb2_buf, 0, - vb2_plane_size(&src_buf->vb2_buf, 0)); - - chroma_format = coda9_jpeg_chroma_format(q_data_dst->fourcc); - if (chroma_format < 0) { - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return chroma_format; - } - - ret = coda_jpeg_decode_header(ctx, &src_buf->vb2_buf); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to decode JPEG header: %d\n", - ret); - - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return ret; - } - - /* Round image dimensions to multiple of MCU size */ - aligned_width = round_up(q_data_src->width, width_align[chroma_format]); - aligned_height = round_up(q_data_src->height, height_align[chroma_format]); - if (aligned_width != q_data_dst->bytesperline) { - v4l2_err(&dev->v4l2_dev, "stride mismatch: %d != %d\n", - aligned_width, q_data_dst->bytesperline); - } - - coda_set_gdi_regs(ctx); - - val = ctx->params.jpeg_huff_ac_index[0] << 12 | - ctx->params.jpeg_huff_ac_index[1] << 11 | - ctx->params.jpeg_huff_ac_index[2] << 10 | - ctx->params.jpeg_huff_dc_index[0] << 9 | - ctx->params.jpeg_huff_dc_index[1] << 8 | - ctx->params.jpeg_huff_dc_index[2] << 7; - if (ctx->params.jpeg_huff_tab) - val |= CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN; - coda_write(dev, val, CODA9_REG_JPEG_PIC_CTRL); - - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_REG_JPEG_PIC_SIZE); - - chroma_interleave = (dst_fourcc == V4L2_PIX_FMT_NV12); - coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); - coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); - coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); - coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); - coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); - coda_write(dev, ctx->params.jpeg_restart_interval, - CODA9_REG_JPEG_RST_INTVAL); - - if (ctx->params.jpeg_huff_tab) { - ret = coda9_jpeg_dec_huff_setup(ctx); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to set up Huffman tables: %d\n", ret); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return ret; - } - } - - coda9_jpeg_qmat_setup(ctx); - - coda9_jpeg_dec_bbc_gbu_setup(ctx, &src_buf->vb2_buf, - ctx->jpeg_ecs_offset); - - coda_write(dev, 0, CODA9_REG_JPEG_RST_INDEX); - coda_write(dev, 0, CODA9_REG_JPEG_RST_COUNT); - - coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_Y); - coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CB); - coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CR); - - coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); - - coda_write(dev, 1, CODA9_GDI_CONTROL); - do { - ret = coda_read(dev, CODA9_GDI_STATUS); - } while (!ret); - - val = (chroma_format << 17) | (chroma_interleave << 16) | - q_data_dst->bytesperline; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - val |= 3 << 20; - coda_write(dev, val, CODA9_GDI_INFO_CONTROL); - - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_GDI_INFO_PIC_SIZE); - - coda_write_base(ctx, q_data_dst, dst_buf, CODA9_GDI_INFO_BASE_Y); - - coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); - coda_write(dev, 0, CODA9_GDI_CONTROL); - coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); - - trace_coda_jpeg_run(ctx, src_buf); - - coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); - - return 0; -} - -static void coda9_jpeg_finish_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *dst_buf, *src_buf; - struct coda_q_data *q_data_dst; - u32 err_mb; - - err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); - if (err_mb) - v4l2_err(&dev->v4l2_dev, "ERRMB: 0x%x\n", err_mb); - - coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); - - /* - * Lock to make sure that a decoder stop command running in parallel - * will either already have marked src_buf as last, or it will wake up - * the capture queue after the buffers are returned. - */ - mutex_lock(&ctx->wakeup_mutex); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->sequence = ctx->osequence++; - - trace_coda_jpeg_done(ctx, dst_buf); - - dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : - VB2_BUF_STATE_DONE); - - mutex_unlock(&ctx->wakeup_mutex); - - coda_dbg(1, ctx, "job finished: decoded frame (%u)%s\n", - dst_buf->sequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); - - /* - * Reset JPEG processing unit after each decode run to work - * around hangups when switching context between encoder and - * decoder. - */ - coda_hw_reset(ctx); -} - -const struct coda_context_ops coda9_jpeg_decode_ops = { - .queue_init = coda_encoder_queue_init, /* non-bitstream operation */ - .start_streaming = coda9_jpeg_start_decoding, - .prepare_run = coda9_jpeg_prepare_decode, - .finish_run = coda9_jpeg_finish_decode, - .release = coda9_jpeg_release, -}; - -irqreturn_t coda9_jpeg_irq_handler(int irq, void *data) -{ - struct coda_dev *dev = data; - struct coda_ctx *ctx; - int status; - int err_mb; - - status = coda_read(dev, CODA9_REG_JPEG_PIC_STATUS); - if (status == 0) - return IRQ_HANDLED; - coda_write(dev, status, CODA9_REG_JPEG_PIC_STATUS); - - if (status & CODA9_JPEG_STATUS_OVERFLOW) - v4l2_err(&dev->v4l2_dev, "JPEG overflow\n"); - - if (status & CODA9_JPEG_STATUS_BBC_INT) - v4l2_err(&dev->v4l2_dev, "JPEG BBC interrupt\n"); - - if (status & CODA9_JPEG_STATUS_ERROR) { - v4l2_err(&dev->v4l2_dev, "JPEG error\n"); - - err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); - if (err_mb) { - v4l2_err(&dev->v4l2_dev, - "ERRMB: 0x%x: rst idx %d, mcu pos (%d,%d)\n", - err_mb, err_mb >> 24, (err_mb >> 12) & 0xfff, - err_mb & 0xfff); - } - } - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (!ctx) { - v4l2_err(&dev->v4l2_dev, - "Instance released before the end of transaction\n"); - mutex_unlock(&dev->coda_mutex); - return IRQ_HANDLED; - } - - complete(&ctx->completion); - - return IRQ_HANDLED; -} diff --git a/drivers/media/platform/coda/coda-mpeg2.c b/drivers/media/platform/coda/coda-mpeg2.c deleted file mode 100644 index 6f3f6721d286..000000000000 --- a/drivers/media/platform/coda/coda-mpeg2.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - MPEG-2 helper functions - * - * Copyright (C) 2019 Pengutronix, Philipp Zabel - */ - -#include -#include -#include "coda.h" - -int coda_mpeg2_profile(int profile_idc) -{ - switch (profile_idc) { - case 5: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE; - case 4: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN; - case 3: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE; - case 2: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE; - case 1: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH; - default: - return -EINVAL; - } -} - -int coda_mpeg2_level(int level_idc) -{ - switch (level_idc) { - case 10: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW; - case 8: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN; - case 6: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440; - case 4: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH; - default: - return -EINVAL; - } -} - -/* - * Check if the buffer starts with the MPEG-2 sequence header (with or without - * quantization matrix) and extension header, for example: - * - * 00 00 01 b3 2d 01 e0 34 08 8b a3 81 - * 10 11 11 12 12 12 13 13 13 13 14 14 14 14 14 15 - * 15 15 15 15 15 16 16 16 16 16 16 16 17 17 17 17 - * 17 17 17 17 18 18 18 19 18 18 18 19 1a 1a 1a 1a - * 19 1b 1b 1b 1b 1b 1c 1c 1c 1c 1e 1e 1e 1f 1f 21 - * 00 00 01 b5 14 8a 00 01 00 00 - * - * or: - * - * 00 00 01 b3 08 00 40 15 ff ff e0 28 - * 00 00 01 b5 14 8a 00 01 00 00 - * - * Returns the detected header size in bytes or 0. - */ -u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) -{ - static const u8 sequence_header_start[4] = { 0x00, 0x00, 0x01, 0xb3 }; - static const union { - u8 extension_start[4]; - u8 start_code_prefix[3]; - } u = { { 0x00, 0x00, 0x01, 0xb5 } }; - - if (size < 22 || - memcmp(buf, sequence_header_start, 4) != 0) - return 0; - - if ((size == 22 || - (size >= 25 && memcmp(buf + 22, u.start_code_prefix, 3) == 0)) && - memcmp(buf + 12, u.extension_start, 4) == 0) - return 22; - - if ((size == 86 || - (size > 89 && memcmp(buf + 86, u.start_code_prefix, 3) == 0)) && - memcmp(buf + 76, u.extension_start, 4) == 0) - return 86; - - return 0; -} diff --git a/drivers/media/platform/coda/coda-mpeg4.c b/drivers/media/platform/coda/coda-mpeg4.c deleted file mode 100644 index 483a4fba1b4f..000000000000 --- a/drivers/media/platform/coda/coda-mpeg4.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - MPEG-4 helper functions - * - * Copyright (C) 2019 Pengutronix, Philipp Zabel - */ - -#include -#include - -#include "coda.h" - -int coda_mpeg4_profile(int profile_idc) -{ - switch (profile_idc) { - case 0: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE; - case 15: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE; - case 2: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE; - case 1: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE; - case 11: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY; - default: - return -EINVAL; - } -} - -int coda_mpeg4_level(int level_idc) -{ - switch (level_idc) { - case 0: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_0; - case 1: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_1; - case 2: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_2; - case 3: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_3; - case 4: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_4; - case 5: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_5; - default: - return -EINVAL; - } -} - -/* - * Check if the buffer starts with the MPEG-4 visual object sequence and visual - * object headers, for example: - * - * 00 00 01 b0 f1 - * 00 00 01 b5 a9 13 00 00 01 00 00 00 01 20 08 - * d4 8d 88 00 f5 04 04 08 14 30 3f - * - * Returns the detected header size in bytes or 0. - */ -u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) -{ - static const u8 vos_start[4] = { 0x00, 0x00, 0x01, 0xb0 }; - static const union { - u8 vo_start[4]; - u8 start_code_prefix[3]; - } u = { { 0x00, 0x00, 0x01, 0xb5 } }; - - if (size < 30 || - memcmp(buf, vos_start, 4) != 0 || - memcmp(buf + 5, u.vo_start, 4) != 0) - return 0; - - if (size == 30 || - (size >= 33 && memcmp(buf + 30, u.start_code_prefix, 3) == 0)) - return 30; - - if (size == 31 || - (size >= 34 && memcmp(buf + 31, u.start_code_prefix, 3) == 0)) - return 31; - - if (size == 32 || - (size >= 35 && memcmp(buf + 32, u.start_code_prefix, 3) == 0)) - return 32; - - return 0; -} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h deleted file mode 100644 index dcf35641c603..000000000000 --- a/drivers/media/platform/coda/coda.h +++ /dev/null @@ -1,396 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix - */ - -#ifndef __CODA_H__ -#define __CODA_H__ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "coda_regs.h" - -#define CODA_MAX_FRAMEBUFFERS 19 -#define FMO_SLICE_SAVE_BUF_SIZE (32) - -/* - * This control allows applications to read the per-stream - * (i.e. per-context) Macroblocks Error Count. This value - * is CODA specific. - */ -#define V4L2_CID_CODA_MB_ERR_CNT (V4L2_CID_USER_CODA_BASE + 0) - -enum { - V4L2_M2M_SRC = 0, - V4L2_M2M_DST = 1, -}; - -enum coda_inst_type { - CODA_INST_ENCODER, - CODA_INST_DECODER, -}; - -enum coda_product { - CODA_DX6 = 0xf001, - CODA_HX4 = 0xf00a, - CODA_7541 = 0xf012, - CODA_960 = 0xf020, -}; - -struct coda_video_device; - -struct coda_devtype { - char *firmware[3]; - enum coda_product product; - const struct coda_codec *codecs; - unsigned int num_codecs; - const struct coda_video_device **vdevs; - unsigned int num_vdevs; - size_t workbuf_size; - size_t tempbuf_size; - size_t iram_size; -}; - -struct coda_aux_buf { - void *vaddr; - dma_addr_t paddr; - u32 size; - struct debugfs_blob_wrapper blob; - struct dentry *dentry; -}; - -struct coda_dev { - struct v4l2_device v4l2_dev; - struct video_device vfd[6]; - struct device *dev; - const struct coda_devtype *devtype; - int firmware; - struct vdoa_data *vdoa; - - void __iomem *regs_base; - struct clk *clk_per; - struct clk *clk_ahb; - struct reset_control *rstc; - - struct coda_aux_buf codebuf; - struct coda_aux_buf tempbuf; - struct coda_aux_buf workbuf; - struct gen_pool *iram_pool; - struct coda_aux_buf iram; - - struct mutex dev_mutex; - struct mutex coda_mutex; - struct workqueue_struct *workqueue; - struct v4l2_m2m_dev *m2m_dev; - struct ida ida; - struct dentry *debugfs_root; - struct ratelimit_state mb_err_rs; -}; - -struct coda_codec { - u32 mode; - u32 src_fourcc; - u32 dst_fourcc; - u32 max_w; - u32 max_h; -}; - -struct coda_huff_tab; - -struct coda_params { - u8 rot_mode; - u8 h264_intra_qp; - u8 h264_inter_qp; - u8 h264_min_qp; - u8 h264_max_qp; - u8 h264_disable_deblocking_filter_idc; - s8 h264_slice_alpha_c0_offset_div2; - s8 h264_slice_beta_offset_div2; - bool h264_constrained_intra_pred_flag; - s8 h264_chroma_qp_index_offset; - u8 h264_profile_idc; - u8 h264_level_idc; - u8 mpeg2_profile_idc; - u8 mpeg2_level_idc; - u8 mpeg4_intra_qp; - u8 mpeg4_inter_qp; - u8 gop_size; - int intra_refresh; - enum v4l2_jpeg_chroma_subsampling jpeg_chroma_subsampling; - u8 jpeg_quality; - u8 jpeg_restart_interval; - u8 *jpeg_qmat_tab[3]; - int jpeg_qmat_index[3]; - int jpeg_huff_dc_index[3]; - int jpeg_huff_ac_index[3]; - u32 *jpeg_huff_data; - struct coda_huff_tab *jpeg_huff_tab; - int codec_mode; - int codec_mode_aux; - enum v4l2_mpeg_video_multi_slice_mode slice_mode; - u32 framerate; - u16 bitrate; - u16 vbv_delay; - u32 vbv_size; - u32 slice_max_bits; - u32 slice_max_mb; - bool force_ipicture; - bool gop_size_changed; - bool bitrate_changed; - bool framerate_changed; - bool h264_intra_qp_changed; - bool intra_refresh_changed; - bool slice_mode_changed; - bool frame_rc_enable; - bool mb_rc_enable; -}; - -struct coda_buffer_meta { - struct list_head list; - u32 sequence; - struct v4l2_timecode timecode; - u64 timestamp; - unsigned int start; - unsigned int end; - bool last; -}; - -/* Per-queue, driver-specific private data */ -struct coda_q_data { - unsigned int width; - unsigned int height; - unsigned int bytesperline; - unsigned int sizeimage; - unsigned int fourcc; - struct v4l2_rect rect; -}; - -struct coda_iram_info { - u32 axi_sram_use; - phys_addr_t buf_bit_use; - phys_addr_t buf_ip_ac_dc_use; - phys_addr_t buf_dbk_y_use; - phys_addr_t buf_dbk_c_use; - phys_addr_t buf_ovl_use; - phys_addr_t buf_btp_use; - phys_addr_t search_ram_paddr; - int search_ram_size; - int remaining; - phys_addr_t next_paddr; -}; - -#define GDI_LINEAR_FRAME_MAP 0 -#define GDI_TILED_FRAME_MB_RASTER_MAP 1 - -struct coda_ctx; - -struct coda_context_ops { - int (*queue_init)(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - int (*reqbufs)(struct coda_ctx *ctx, struct v4l2_requestbuffers *rb); - int (*start_streaming)(struct coda_ctx *ctx); - int (*prepare_run)(struct coda_ctx *ctx); - void (*finish_run)(struct coda_ctx *ctx); - void (*run_timeout)(struct coda_ctx *ctx); - void (*seq_init_work)(struct work_struct *work); - void (*seq_end_work)(struct work_struct *work); - void (*release)(struct coda_ctx *ctx); -}; - -struct coda_internal_frame { - struct coda_aux_buf buf; - struct coda_buffer_meta meta; - u32 type; - u32 error; -}; - -struct coda_ctx { - struct coda_dev *dev; - struct mutex buffer_mutex; - struct work_struct pic_run_work; - struct work_struct seq_init_work; - struct work_struct seq_end_work; - struct completion completion; - const struct coda_video_device *cvd; - const struct coda_context_ops *ops; - int aborting; - int initialized; - int streamon_out; - int streamon_cap; - u32 qsequence; - u32 osequence; - u32 sequence_offset; - struct coda_q_data q_data[2]; - enum coda_inst_type inst_type; - const struct coda_codec *codec; - enum v4l2_colorspace colorspace; - enum v4l2_xfer_func xfer_func; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - struct coda_params params; - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *h264_profile_ctrl; - struct v4l2_ctrl *h264_level_ctrl; - struct v4l2_ctrl *mpeg2_profile_ctrl; - struct v4l2_ctrl *mpeg2_level_ctrl; - struct v4l2_ctrl *mpeg4_profile_ctrl; - struct v4l2_ctrl *mpeg4_level_ctrl; - struct v4l2_ctrl *mb_err_cnt_ctrl; - struct v4l2_fh fh; - int gopcounter; - int runcounter; - int jpeg_ecs_offset; - char vpu_header[3][64]; - int vpu_header_size[3]; - struct kfifo bitstream_fifo; - struct mutex bitstream_mutex; - struct coda_aux_buf bitstream; - bool hold; - struct coda_aux_buf parabuf; - struct coda_aux_buf psbuf; - struct coda_aux_buf slicebuf; - struct coda_internal_frame internal_frames[CODA_MAX_FRAMEBUFFERS]; - struct list_head buffer_meta_list; - spinlock_t buffer_meta_lock; - int num_metas; - unsigned int first_frame_sequence; - struct coda_aux_buf workbuf; - int num_internal_frames; - int idx; - int reg_idx; - struct coda_iram_info iram_info; - int tiled_map_type; - u32 bit_stream_param; - u32 frm_dis_flg; - u32 frame_mem_ctrl; - u32 para_change; - int display_idx; - struct dentry *debugfs_entry; - bool use_bit; - bool use_vdoa; - struct vdoa_ctx *vdoa; - /* - * wakeup mutex used to serialize encoder stop command and finish_run, - * ensures that finish_run always either flags the last returned buffer - * or wakes up the capture queue to signal EOS afterwards. - */ - struct mutex wakeup_mutex; -}; - -extern int coda_debug; - -#define coda_dbg(level, ctx, fmt, arg...) \ - do { \ - if (coda_debug >= (level)) \ - v4l2_dbg((level), coda_debug, &(ctx)->dev->v4l2_dev, \ - "%u: " fmt, (ctx)->idx, ##arg); \ - } while (0) - -void coda_write(struct coda_dev *dev, u32 data, u32 reg); -unsigned int coda_read(struct coda_dev *dev, u32 reg); -void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, - struct vb2_v4l2_buffer *buf, unsigned int reg_y); - -int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, - size_t size, const char *name, struct dentry *parent); -void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf); - -int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); -int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - -int coda_hw_reset(struct coda_ctx *ctx); - -void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list); - -void coda_set_gdi_regs(struct coda_ctx *ctx); - -static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx, - enum v4l2_buf_type type) -{ - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return &(ctx->q_data[V4L2_M2M_SRC]); - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &(ctx->q_data[V4L2_M2M_DST]); - default: - return NULL; - } -} - -const char *coda_product_name(int product); - -int coda_check_firmware(struct coda_dev *dev); - -static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx) -{ - return kfifo_len(&ctx->bitstream_fifo); -} - -/* - * The bitstream prefetcher needs to read at least 2 256 byte periods past - * the desired bitstream position for all data to reach the decoder. - */ -static inline bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, - unsigned int pos) -{ - return (int)(ctx->bitstream_fifo.kfifo.in - ALIGN(pos, 256)) > 512; -} - -bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, unsigned int pos); -int coda_bitstream_flush(struct coda_ctx *ctx); - -void coda_bit_stream_end_flag(struct coda_ctx *ctx); - -void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state); - -int coda_h264_filler_nal(int size, char *p); -int coda_h264_padding(int size, char *p); -int coda_h264_profile(int profile_idc); -int coda_h264_level(int level_idc); -int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); -int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, - int *size, int max_size); - -int coda_mpeg2_profile(int profile_idc); -int coda_mpeg2_level(int level_idc); -u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); -int coda_mpeg4_profile(int profile_idc); -int coda_mpeg4_level(int level_idc); -u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); - -void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, - u8 level_idc); - -bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); -int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb); -int coda_jpeg_write_tables(struct coda_ctx *ctx); -void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); - -extern const struct coda_context_ops coda_bit_encode_ops; -extern const struct coda_context_ops coda_bit_decode_ops; -extern const struct coda_context_ops coda9_jpeg_encode_ops; -extern const struct coda_context_ops coda9_jpeg_decode_ops; - -irqreturn_t coda_irq_handler(int irq, void *data); -irqreturn_t coda9_jpeg_irq_handler(int irq, void *data); - -#endif /* __CODA_H__ */ diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h deleted file mode 100644 index da5bb3212528..000000000000 --- a/drivers/media/platform/coda/coda_regs.h +++ /dev/null @@ -1,563 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * linux/drivers/media/platform/coda/coda_regs.h - * - * Copyright (C) 2012 Vista Silicon SL - * Javier Martin - * Xavier Duret - */ - -#ifndef _REGS_CODA_H_ -#define _REGS_CODA_H_ - -/* HW registers */ -#define CODA_REG_BIT_CODE_RUN 0x000 -#define CODA_REG_RUN_ENABLE (1 << 0) -#define CODA_REG_BIT_CODE_DOWN 0x004 -#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16) -#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff) -#define CODA_REG_BIT_HOST_IN_REQ 0x008 -#define CODA_REG_BIT_INT_CLEAR 0x00c -#define CODA_REG_BIT_INT_CLEAR_SET 0x1 -#define CODA_REG_BIT_INT_STATUS 0x010 -#define CODA_REG_BIT_CODE_RESET 0x014 -#define CODA_REG_RESET_ENABLE (1 << 0) -#define CODA_REG_BIT_CUR_PC 0x018 -#define CODA9_REG_BIT_SW_RESET 0x024 -#define CODA9_SW_RESET_BPU_CORE 0x008 -#define CODA9_SW_RESET_BPU_BUS 0x010 -#define CODA9_SW_RESET_VCE_CORE 0x020 -#define CODA9_SW_RESET_VCE_BUS 0x040 -#define CODA9_SW_RESET_GDI_CORE 0x080 -#define CODA9_SW_RESET_GDI_BUS 0x100 -#define CODA9_REG_BIT_SW_RESET_STATUS 0x034 - -/* Static SW registers */ -#define CODA_REG_BIT_CODE_BUF_ADDR 0x100 -#define CODA_REG_BIT_WORK_BUF_ADDR 0x104 -#define CODA_REG_BIT_PARA_BUF_ADDR 0x108 -#define CODA_REG_BIT_STREAM_CTRL 0x10c -#define CODA7_STREAM_BUF_PIC_RESET (1 << 4) -#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3) -#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3) -#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2) -#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5) -#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4) -#define CODADX6_STREAM_CHKDIS_OFFSET (1 << 1) -#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) -#define CODA_STREAM_ENDIAN_SELECT (1 << 0) -#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 -#define CODA9_FRAME_ENABLE_BWB (1 << 12) -#define CODA9_FRAME_TILED2LINEAR (1 << 11) -#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) -#define CODA_IMAGE_ENDIAN_SELECT (1 << 0) -#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 -#define CODA_BIT_STREAM_END_FLAG (1 << 2) -#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0) -#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 -#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) -#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) -#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x)) -#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 -#define CODA7_REG_BIT_AXI_SRAM_USE 0x140 -#define CODA9_USE_HOST_BTP_ENABLE (1 << 13) -#define CODA9_USE_HOST_OVL_ENABLE (1 << 12) -#define CODA7_USE_HOST_ME_ENABLE (1 << 11) -#define CODA9_USE_HOST_DBK_ENABLE (3 << 10) -#define CODA7_USE_HOST_OVL_ENABLE (1 << 10) -#define CODA7_USE_HOST_DBK_ENABLE (1 << 9) -#define CODA9_USE_HOST_IP_ENABLE (1 << 9) -#define CODA7_USE_HOST_IP_ENABLE (1 << 8) -#define CODA9_USE_HOST_BIT_ENABLE (1 << 8) -#define CODA7_USE_HOST_BIT_ENABLE (1 << 7) -#define CODA9_USE_BTP_ENABLE (1 << 5) -#define CODA7_USE_ME_ENABLE (1 << 4) -#define CODA9_USE_OVL_ENABLE (1 << 4) -#define CODA7_USE_OVL_ENABLE (1 << 3) -#define CODA9_USE_DBK_ENABLE (3 << 2) -#define CODA7_USE_DBK_ENABLE (1 << 2) -#define CODA7_USE_IP_ENABLE (1 << 1) -#define CODA7_USE_BIT_ENABLE (1 << 0) - -#define CODA_REG_BIT_BUSY 0x160 -#define CODA_REG_BIT_BUSY_FLAG 1 -#define CODA_REG_BIT_RUN_COMMAND 0x164 -#define CODA_COMMAND_SEQ_INIT 1 -#define CODA_COMMAND_SEQ_END 2 -#define CODA_COMMAND_PIC_RUN 3 -#define CODA_COMMAND_SET_FRAME_BUF 4 -#define CODA_COMMAND_ENCODE_HEADER 5 -#define CODA_COMMAND_ENC_PARA_SET 6 -#define CODA_COMMAND_DEC_PARA_SET 7 -#define CODA_COMMAND_DEC_BUF_FLUSH 8 -#define CODA_COMMAND_RC_CHANGE_PARAMETER 9 -#define CODA_COMMAND_FIRMWARE_GET 0xf -#define CODA_REG_BIT_RUN_INDEX 0x168 -#define CODA_INDEX_SET(x) ((x) & 0x3) -#define CODA_REG_BIT_RUN_COD_STD 0x16c -#define CODADX6_MODE_DECODE_MP4 0 -#define CODADX6_MODE_ENCODE_MP4 1 -#define CODADX6_MODE_DECODE_H264 2 -#define CODADX6_MODE_ENCODE_H264 3 -#define CODA7_MODE_DECODE_H264 0 -#define CODA7_MODE_DECODE_VC1 1 -#define CODA7_MODE_DECODE_MP2 2 -#define CODA7_MODE_DECODE_MP4 3 -#define CODA7_MODE_DECODE_DV3 3 -#define CODA7_MODE_DECODE_RV 4 -#define CODA7_MODE_DECODE_MJPG 5 -#define CODA7_MODE_ENCODE_H264 8 -#define CODA7_MODE_ENCODE_MP4 11 -#define CODA7_MODE_ENCODE_MJPG 13 -#define CODA9_MODE_DECODE_H264 0 -#define CODA9_MODE_DECODE_VC1 1 -#define CODA9_MODE_DECODE_MP2 2 -#define CODA9_MODE_DECODE_MP4 3 -#define CODA9_MODE_DECODE_DV3 3 -#define CODA9_MODE_DECODE_RV 4 -#define CODA9_MODE_DECODE_AVS 5 -#define CODA9_MODE_DECODE_MJPG 6 -#define CODA9_MODE_DECODE_VPX 7 -#define CODA9_MODE_ENCODE_H264 8 -#define CODA9_MODE_ENCODE_MP4 11 -#define CODA9_MODE_ENCODE_MJPG 13 -#define CODA_MODE_INVALID 0xffff -#define CODA_REG_BIT_INT_ENABLE 0x170 -#define CODA_INT_INTERRUPT_ENABLE (1 << 3) -#define CODA_REG_BIT_INT_REASON 0x174 -#define CODA7_REG_BIT_RUN_AUX_STD 0x178 -#define CODA_MP4_AUX_MPEG4 0 -#define CODA_MP4_AUX_DIVX3 1 -#define CODA_VPX_AUX_THO 0 -#define CODA_VPX_AUX_VP6 1 -#define CODA_VPX_AUX_VP8 2 -#define CODA_H264_AUX_AVC 0 -#define CODA_H264_AUX_MVC 1 - -/* - * Commands' mailbox: - * registers with offsets in the range 0x180-0x1d0 - * have different meaning depending on the command being - * issued. - */ - -/* Decoder Sequence Initialization */ -#define CODA_CMD_DEC_SEQ_BB_START 0x180 -#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184 -#define CODA_CMD_DEC_SEQ_OPTION 0x188 -#define CODA_NO_INT_ENABLE (1 << 10) -#define CODA_REORDER_ENABLE (1 << 1) -#define CODADX6_QP_REPORT (1 << 0) -#define CODA7_MP4_DEBLK_ENABLE (1 << 0) -#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c -#define CODA_CMD_DEC_SEQ_START_BYTE 0x190 -#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 -#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 -#define CODA_CMD_DEC_SEQ_JPG_THUMB_EN 0x19c -#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c -#define CODA_MP4_CLASS_MPEG4 0 -#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c -#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0 - -#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0 -#define CODA9_RET_DEC_SEQ_BITRATE 0x1b4 -#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0 -#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */ -#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4 -#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8 -#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8 -#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc -#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0 -#define CODA_RET_DEC_SEQ_INFO 0x1d4 -#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8 -#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc -#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0 -#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0 -#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4 -#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 -#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 -#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 -#define CODA7_RET_DEC_SEQ_HEADER_REPORT 0x1ec - -/* Decoder Picture Run */ -#define CODA_CMD_DEC_PIC_ROT_MODE 0x180 -#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184 -#define CODA9_CMD_DEC_PIC_ROT_INDEX 0x184 -#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188 -#define CODA9_CMD_DEC_PIC_ROT_ADDR_Y 0x188 -#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c -#define CODA9_CMD_DEC_PIC_ROT_ADDR_CB 0x18c -#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190 -#define CODA9_CMD_DEC_PIC_ROT_ADDR_CR 0x190 -#define CODA9_CMD_DEC_PIC_ROT_STRIDE 0x1b8 - -#define CODA_CMD_DEC_PIC_OPTION 0x194 -#define CODA_PRE_SCAN_EN (1 << 0) -#define CODA_PRE_SCAN_MODE_DECODE (0 << 1) -#define CODA_PRE_SCAN_MODE_RETURN (1 << 1) -#define CODA_IFRAME_SEARCH_EN (1 << 2) -#define CODA_SKIP_FRAME_MODE (0x3 << 3) -#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198 -#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c -#define CODA_CMD_DEC_PIC_BB_START 0x1a0 -#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4 -#define CODA_RET_DEC_PIC_SIZE 0x1bc -#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0 -#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4 -#define CODA_RET_DEC_PIC_ERR_MB 0x1c8 -#define CODA_RET_DEC_PIC_TYPE 0x1cc -#define CODA_PIC_TYPE_MASK 0x7 -#define CODA_PIC_TYPE_MASK_VC1 0x3f -#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3) -#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6) -#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16) -#define CODA7_PIC_TYPE_INTERLACED (1 << 18) -#define CODA_RET_DEC_PIC_POST 0x1d0 -#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0 -#define CODA_RET_DEC_PIC_OPTION 0x1d4 -#define CODA_RET_DEC_PIC_SUCCESS 0x1d8 -#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc -#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0 -#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4 -#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec - -#define CODA9_RET_DEC_PIC_VP8_PIC_REPORT 0x1e8 -#define CODA9_RET_DEC_PIC_ASPECT 0x1f0 -#define CODA9_RET_DEC_PIC_VP8_SCALE_INFO 0x1f0 -#define CODA9_RET_DEC_PIC_FRATE_NR 0x1f4 -#define CODA9_RET_DEC_PIC_FRATE_DR 0x1f8 - -/* Encoder Sequence Initialization */ -#define CODA_CMD_ENC_SEQ_BB_START 0x180 -#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 -#define CODA_CMD_ENC_SEQ_OPTION 0x188 -#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9 -#define CODA9_OPTION_MVC_PREFIX_NAL_OFFSET 9 -#define CODA7_OPTION_GAMMA_OFFSET 8 -#define CODA9_OPTION_MVC_PARASET_REFRESH_OFFSET 8 -#define CODA7_OPTION_RCQPMAX_OFFSET 7 -#define CODA9_OPTION_GAMMA_OFFSET 7 -#define CODADX6_OPTION_GAMMA_OFFSET 7 -#define CODA7_OPTION_RCQPMIN_OFFSET 6 -#define CODA9_OPTION_RCQPMAX_OFFSET 6 -#define CODA_OPTION_LIMITQP_OFFSET 6 -#define CODA_OPTION_RCINTRAQP_OFFSET 5 -#define CODA_OPTION_FMO_OFFSET 4 -#define CODA9_OPTION_MVC_INTERVIEW_OFFSET 4 -#define CODA_OPTION_AVC_AUD_OFFSET 2 -#define CODA_OPTION_SLICEREPORT_OFFSET 1 -#define CODA_CMD_ENC_SEQ_COD_STD 0x18c -#define CODA_STD_MPEG4 0 -#define CODA9_STD_H264 0 -#define CODA_STD_H263 1 -#define CODA_STD_H264 2 -#define CODA9_STD_MPEG4 3 - -#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190 -#define CODA7_PICWIDTH_OFFSET 16 -#define CODA7_PICWIDTH_MASK 0xffff -#define CODADX6_PICWIDTH_OFFSET 10 -#define CODADX6_PICWIDTH_MASK 0x3ff -#define CODA_PICHEIGHT_OFFSET 0 -#define CODADX6_PICHEIGHT_MASK 0x3ff -#define CODA7_PICHEIGHT_MASK 0xffff -#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 -#define CODA_FRATE_RES_OFFSET 0 -#define CODA_FRATE_RES_MASK 0xffff -#define CODA_FRATE_DIV_OFFSET 16 -#define CODA_FRATE_DIV_MASK 0xffff -#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 -#define CODA_MP4PARAM_VERID_OFFSET 6 -#define CODA_MP4PARAM_VERID_MASK 0x01 -#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2 -#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07 -#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1 -#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01 -#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0 -#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_263_PARA 0x19c -#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2 -#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01 -#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1 -#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01 -#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0 -#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0 -#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12 -#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f -#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8 -#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f -#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6 -#define CODA_264PARAM_DISABLEDEBLK_MASK 0x03 -#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5 -#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01 -#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0 -#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f -#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4 -#define CODA_SLICING_SIZE_OFFSET 2 -#define CODA_SLICING_SIZE_MASK 0x3fffffff -#define CODA_SLICING_UNIT_OFFSET 1 -#define CODA_SLICING_UNIT_MASK 0x01 -#define CODA_SLICING_MODE_OFFSET 0 -#define CODA_SLICING_MODE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8 -#define CODA_GOP_SIZE_OFFSET 0 -#define CODA_GOP_SIZE_MASK 0x3f -#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac -#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31 -#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01 -#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16 -#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7fff -#define CODA_RATECONTROL_BITRATE_OFFSET 1 -#define CODA_RATECONTROL_BITRATE_MASK 0x7fff -#define CODA_RATECONTROL_ENABLE_OFFSET 0 -#define CODA_RATECONTROL_ENABLE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0 -#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4 -#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8 -#define CODA_FMOPARAM_TYPE_OFFSET 4 -#define CODA_FMOPARAM_TYPE_MASK 1 -#define CODA_FMOPARAM_SLICENUM_OFFSET 0 -#define CODA_FMOPARAM_SLICENUM_MASK 0x0f -#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc -#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8 -#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc -#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4 -#define CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX 0x1c8 -#define CODA_QPMIN_OFFSET 8 -#define CODA_QPMIN_MASK 0x3f -#define CODA_QPMAX_OFFSET 0 -#define CODA_QPMAX_MASK 0x3f -#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc -#define CODA_GAMMA_OFFSET 0 -#define CODA_GAMMA_MASK 0xffff -#define CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE 0x1d0 -#define CODA9_CMD_ENC_SEQ_INTRA_WEIGHT 0x1d4 -#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8 -#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0 - -#define CODA_CMD_ENC_SEQ_JPG_PARA 0x198 -#define CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL 0x19C -#define CODA_CMD_ENC_SEQ_JPG_THUMB_EN 0x1a0 -#define CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE 0x1a4 -#define CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET 0x1a8 - -/* Encoder Parameter Change */ -#define CODA_CMD_ENC_PARAM_CHANGE_ENABLE 0x180 -#define CODA_PARAM_CHANGE_RC_GOP BIT(0) -#define CODA_PARAM_CHANGE_RC_INTRA_QP BIT(1) -#define CODA_PARAM_CHANGE_RC_BITRATE BIT(2) -#define CODA_PARAM_CHANGE_RC_FRAME_RATE BIT(3) -#define CODA_PARAM_CHANGE_INTRA_MB_NUM BIT(4) -#define CODA_PARAM_CHANGE_SLICE_MODE BIT(5) -#define CODA_PARAM_CHANGE_HEC_MODE BIT(6) -#define CODA_CMD_ENC_PARAM_RC_GOP 0x184 -#define CODA_CMD_ENC_PARAM_RC_INTRA_QP 0x188 -#define CODA_CMD_ENC_PARAM_RC_BITRATE 0x18c -#define CODA_CMD_ENC_PARAM_RC_FRAME_RATE 0x190 -#define CODA_CMD_ENC_PARAM_INTRA_MB_NUM 0x194 -#define CODA_CMD_ENC_PARAM_SLICE_MODE 0x198 -#define CODA_CMD_ENC_PARAM_HEC_MODE 0x19c -#define CODA_RET_ENC_PARAM_CHANGE_SUCCESS 0x1c0 - -/* Encoder Picture Run */ -#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180 -#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184 -#define CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC 0x1a4 -#define CODA9_CMD_ENC_PIC_SRC_ADDR_Y 0x1a8 -#define CODA9_CMD_ENC_PIC_SRC_ADDR_CB 0x1ac -#define CODA9_CMD_ENC_PIC_SRC_ADDR_CR 0x1b0 -#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180 -#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184 -#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188 -#define CODA_CMD_ENC_PIC_QS 0x18c -#define CODA_CMD_ENC_PIC_ROT_MODE 0x190 -#define CODA_ROT_MIR_ENABLE (1 << 4) -#define CODA_ROT_0 (0x0 << 0) -#define CODA_ROT_90 (0x1 << 0) -#define CODA_ROT_180 (0x2 << 0) -#define CODA_ROT_270 (0x3 << 0) -#define CODA_MIR_NONE (0x0 << 2) -#define CODA_MIR_VER (0x1 << 2) -#define CODA_MIR_HOR (0x2 << 2) -#define CODA_MIR_VER_HOR (0x3 << 2) -#define CODA_CMD_ENC_PIC_OPTION 0x194 -#define CODA_FORCE_IPICTURE BIT(1) -#define CODA_REPORT_MB_INFO BIT(3) -#define CODA_REPORT_MV_INFO BIT(4) -#define CODA_REPORT_SLICE_INFO BIT(5) -#define CODA_CMD_ENC_PIC_BB_START 0x198 -#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c -#define CODA_RET_ENC_FRAME_NUM 0x1c0 -#define CODA_RET_ENC_PIC_TYPE 0x1c4 -#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8 -#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc -#define CODA_RET_ENC_PIC_FLAG 0x1d0 -#define CODA_RET_ENC_PIC_SUCCESS 0x1d8 - -/* Set Frame Buffer */ -#define CODA_CMD_SET_FRAME_BUF_NUM 0x180 -#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184 -#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188 -#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c -#define CODA9_CMD_SET_FRAME_SUBSAMP_A 0x188 -#define CODA9_CMD_SET_FRAME_SUBSAMP_B 0x18c -#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190 -#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194 -#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198 -#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c -#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0 -#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4 -#define CODA9_CMD_SET_FRAME_AXI_BTP_ADDR 0x1a4 -#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 -#define CODA9_CMD_SET_FRAME_CACHE_SIZE 0x1a8 -#define CODA9_CMD_SET_FRAME_CACHE_CONFIG 0x1ac -#define CODA9_CACHE_BYPASS_OFFSET 28 -#define CODA9_CACHE_DUALCONF_OFFSET 26 -#define CODA9_CACHE_PAGEMERGE_OFFSET 24 -#define CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET 16 -#define CODA9_CACHE_CB_BUFFER_SIZE_OFFSET 8 -#define CODA9_CACHE_CR_BUFFER_SIZE_OFFSET 0 -#define CODA9_CMD_SET_FRAME_SUBSAMP_A_MVC 0x1b0 -#define CODA9_CMD_SET_FRAME_SUBSAMP_B_MVC 0x1b4 -#define CODA9_CMD_SET_FRAME_DP_BUF_BASE 0x1b0 -#define CODA9_CMD_SET_FRAME_DP_BUF_SIZE 0x1b4 -#define CODA9_CMD_SET_FRAME_MAX_DEC_SIZE 0x1b8 -#define CODA9_CMD_SET_FRAME_DELAY 0x1bc - -/* Encoder Header */ -#define CODA_CMD_ENC_HEADER_CODE 0x180 -#define CODA_GAMMA_OFFSET 0 -#define CODA_HEADER_H264_SPS 0 -#define CODA_HEADER_H264_PPS 1 -#define CODA_HEADER_MP4V_VOL 0 -#define CODA_HEADER_MP4V_VOS 1 -#define CODA_HEADER_MP4V_VIS 2 -#define CODA9_HEADER_FRAME_CROP (1 << 3) -#define CODA_CMD_ENC_HEADER_BB_START 0x184 -#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188 -#define CODA9_CMD_ENC_HEADER_FRAME_CROP_H 0x18c -#define CODA9_CMD_ENC_HEADER_FRAME_CROP_V 0x190 - -/* Get Version */ -#define CODA_CMD_FIRMWARE_VERNUM 0x1c0 -#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff) -#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f) -#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f) -#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff) -#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \ - ((product) << 16 | ((major) << 12) | \ - ((minor) << 8) | (release)) -#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4 - -#define CODA9_GDMA_BASE 0x1000 -#define CODA9_GDI_CONTROL (CODA9_GDMA_BASE + 0x034) -#define CODA9_GDI_PIC_INIT_HOST (CODA9_GDMA_BASE + 0x038) -#define CODA9_GDI_STATUS (CODA9_GDMA_BASE + 0x080) -#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0) -#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac) - -#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0) -#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4) - -#define CODA9_GDI_INFO_CONTROL (CODA9_GDMA_BASE + 0x400) -#define CODA9_GDI_INFO_PIC_SIZE (CODA9_GDMA_BASE + 0x404) -#define CODA9_GDI_INFO_BASE_Y (CODA9_GDMA_BASE + 0x408) -#define CODA9_GDI_INFO_BASE_CB (CODA9_GDMA_BASE + 0x40c) -#define CODA9_GDI_INFO_BASE_CR (CODA9_GDMA_BASE + 0x410) - -#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800) -#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c) - -#define CODA9_GDI_XY2_BA_0 (CODA9_GDMA_BASE + 0x840) -#define CODA9_GDI_XY2_BA_1 (CODA9_GDMA_BASE + 0x844) -#define CODA9_GDI_XY2_BA_2 (CODA9_GDMA_BASE + 0x848) -#define CODA9_GDI_XY2_BA_3 (CODA9_GDMA_BASE + 0x84c) - -#define CODA9_GDI_XY2_RAS_0 (CODA9_GDMA_BASE + 0x850) -#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) - -#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) -#define CODA9_XY2RBC_SEPARATE_MAP BIT(19) -#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18) -#define CODA9_XY2RBC_TILED_MAP BIT(17) -#define CODA9_XY2RBC_CA_INC_HOR BIT(16) -#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) -#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) -#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920) - -#define CODA9_JPEG_BASE 0x3000 -#define CODA9_REG_JPEG_PIC_START (CODA9_JPEG_BASE + 0x000) -#define CODA9_REG_JPEG_PIC_STATUS (CODA9_JPEG_BASE + 0x004) -#define CODA9_JPEG_STATUS_OVERFLOW BIT(3) -#define CODA9_JPEG_STATUS_BBC_INT BIT(2) -#define CODA9_JPEG_STATUS_ERROR BIT(1) -#define CODA9_JPEG_STATUS_DONE BIT(0) -#define CODA9_REG_JPEG_PIC_ERRMB (CODA9_JPEG_BASE + 0x008) -#define CODA9_JPEG_ERRMB_RESTART_IDX_MASK (0xf << 24) -#define CODA9_JPEG_ERRMB_MCU_POS_X_MASK (0xfff << 12) -#define CODA9_JPEG_ERRMB_MCU_POS_Y_MASK 0xfff -#define CODA9_REG_JPEG_PIC_CTRL (CODA9_JPEG_BASE + 0x010) -#define CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN BIT(6) -#define CODA9_JPEG_PIC_CTRL_TC_DIRECTION BIT(4) -#define CODA9_JPEG_PIC_CTRL_ENCODER_EN BIT(3) -#define CODA9_REG_JPEG_PIC_SIZE (CODA9_JPEG_BASE + 0x014) -#define CODA9_REG_JPEG_MCU_INFO (CODA9_JPEG_BASE + 0x018) -#define CODA9_JPEG_MCU_BLOCK_NUM_OFFSET 16 -#define CODA9_JPEG_COMP_NUM_OFFSET 12 -#define CODA9_JPEG_COMP0_INFO_OFFSET 8 -#define CODA9_JPEG_COMP1_INFO_OFFSET 4 -#define CODA9_JPEG_COMP2_INFO_OFFSET 0 -#define CODA9_REG_JPEG_ROT_INFO (CODA9_JPEG_BASE + 0x01c) -#define CODA9_JPEG_ROT_MIR_ENABLE BIT(4) -#define CODA9_JPEG_ROT_MIR_MODE_MASK 0xf -#define CODA9_REG_JPEG_SCL_INFO (CODA9_JPEG_BASE + 0x020) -#define CODA9_JPEG_SCL_ENABLE BIT(4) -#define CODA9_JPEG_SCL_HOR_MODE_MASK (0x3 << 2) -#define CODA9_JPEG_SCL_VER_MODE_MASK (0x3 << 0) -#define CODA9_REG_JPEG_IF_INFO (CODA9_JPEG_BASE + 0x024) -#define CODA9_JPEG_SENS_IF_CLR BIT(1) -#define CODA9_JPEG_DISP_IF_CLR BIT(0) -#define CODA9_REG_JPEG_OP_INFO (CODA9_JPEG_BASE + 0x02c) -#define CODA9_JPEG_BUS_REQ_NUM_OFFSET 0 -#define CODA9_JPEG_BUS_REQ_NUM_MASK 0x7 -#define CODA9_REG_JPEG_DPB_CONFIG (CODA9_JPEG_BASE + 0x030) -#define CODA9_REG_JPEG_DPB_BASE00 (CODA9_JPEG_BASE + 0x040) -#define CODA9_REG_JPEG_HUFF_CTRL (CODA9_JPEG_BASE + 0x080) -#define CODA9_REG_JPEG_HUFF_ADDR (CODA9_JPEG_BASE + 0x084) -#define CODA9_REG_JPEG_HUFF_DATA (CODA9_JPEG_BASE + 0x088) -#define CODA9_REG_JPEG_QMAT_CTRL (CODA9_JPEG_BASE + 0x090) -#define CODA9_REG_JPEG_QMAT_ADDR (CODA9_JPEG_BASE + 0x094) -#define CODA9_REG_JPEG_QMAT_DATA (CODA9_JPEG_BASE + 0x098) -#define CODA9_REG_JPEG_RST_INTVAL (CODA9_JPEG_BASE + 0x0b0) -#define CODA9_REG_JPEG_RST_INDEX (CODA9_JPEG_BASE + 0x0b4) -#define CODA9_REG_JPEG_RST_COUNT (CODA9_JPEG_BASE + 0x0b8) -#define CODA9_REG_JPEG_DPCM_DIFF_Y (CODA9_JPEG_BASE + 0x0f0) -#define CODA9_REG_JPEG_DPCM_DIFF_CB (CODA9_JPEG_BASE + 0x0f4) -#define CODA9_REG_JPEG_DPCM_DIFF_CR (CODA9_JPEG_BASE + 0x0f8) -#define CODA9_REG_JPEG_GBU_CTRL (CODA9_JPEG_BASE + 0x100) -#define CODA9_REG_JPEG_GBU_BT_PTR (CODA9_JPEG_BASE + 0x110) -#define CODA9_REG_JPEG_GBU_WD_PTR (CODA9_JPEG_BASE + 0x114) -#define CODA9_REG_JPEG_GBU_TT_CNT (CODA9_JPEG_BASE + 0x118) -#define CODA9_REG_JPEG_GBU_BBSR (CODA9_JPEG_BASE + 0x140) -#define CODA9_REG_JPEG_GBU_BBER (CODA9_JPEG_BASE + 0x144) -#define CODA9_REG_JPEG_GBU_BBIR (CODA9_JPEG_BASE + 0x148) -#define CODA9_REG_JPEG_GBU_BBHR (CODA9_JPEG_BASE + 0x14c) -#define CODA9_REG_JPEG_GBU_BCNT (CODA9_JPEG_BASE + 0x158) -#define CODA9_REG_JPEG_GBU_FF_RPTR (CODA9_JPEG_BASE + 0x160) -#define CODA9_REG_JPEG_GBU_FF_WPTR (CODA9_JPEG_BASE + 0x164) -#define CODA9_REG_JPEG_BBC_END_ADDR (CODA9_JPEG_BASE + 0x208) -#define CODA9_REG_JPEG_BBC_WR_PTR (CODA9_JPEG_BASE + 0x20c) -#define CODA9_REG_JPEG_BBC_RD_PTR (CODA9_JPEG_BASE + 0x210) -#define CODA9_REG_JPEG_BBC_EXT_ADDR (CODA9_JPEG_BASE + 0x214) -#define CODA9_REG_JPEG_BBC_INT_ADDR (CODA9_JPEG_BASE + 0x218) -#define CODA9_REG_JPEG_BBC_DATA_CNT (CODA9_JPEG_BASE + 0x21c) -#define CODA9_REG_JPEG_BBC_COMMAND (CODA9_JPEG_BASE + 0x220) -#define CODA9_REG_JPEG_BBC_BUSY (CODA9_JPEG_BASE + 0x224) -#define CODA9_REG_JPEG_BBC_CTRL (CODA9_JPEG_BASE + 0x228) -#define CODA9_REG_JPEG_BBC_CUR_POS (CODA9_JPEG_BASE + 0x22c) -#define CODA9_REG_JPEG_BBC_BAS_ADDR (CODA9_JPEG_BASE + 0x230) -#define CODA9_REG_JPEG_BBC_STRM_CTRL (CODA9_JPEG_BASE + 0x234) -#define CODA9_REG_JPEG_BBC_FLUSH_CMD (CODA9_JPEG_BASE + 0x238) - -#endif diff --git a/drivers/media/platform/coda/imx-vdoa.c b/drivers/media/platform/coda/imx-vdoa.c deleted file mode 100644 index c70871bae193..000000000000 --- a/drivers/media/platform/coda/imx-vdoa.c +++ /dev/null @@ -1,352 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * i.MX6 Video Data Order Adapter (VDOA) - * - * Copyright (C) 2014 Philipp Zabel - * Copyright (C) 2016 Pengutronix, Michael Tretter - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-vdoa.h" - -#define VDOA_NAME "imx-vdoa" - -#define VDOAC 0x00 -#define VDOASRR 0x04 -#define VDOAIE 0x08 -#define VDOAIST 0x0c -#define VDOAFP 0x10 -#define VDOAIEBA00 0x14 -#define VDOAIEBA01 0x18 -#define VDOAIEBA02 0x1c -#define VDOAIEBA10 0x20 -#define VDOAIEBA11 0x24 -#define VDOAIEBA12 0x28 -#define VDOASL 0x2c -#define VDOAIUBO 0x30 -#define VDOAVEBA0 0x34 -#define VDOAVEBA1 0x38 -#define VDOAVEBA2 0x3c -#define VDOAVUBO 0x40 -#define VDOASR 0x44 - -#define VDOAC_ISEL BIT(6) -#define VDOAC_PFS BIT(5) -#define VDOAC_SO BIT(4) -#define VDOAC_SYNC BIT(3) -#define VDOAC_NF BIT(2) -#define VDOAC_BNDM_MASK 0x3 -#define VDOAC_BAND_HEIGHT_8 0x0 -#define VDOAC_BAND_HEIGHT_16 0x1 -#define VDOAC_BAND_HEIGHT_32 0x2 - -#define VDOASRR_START BIT(1) -#define VDOASRR_SWRST BIT(0) - -#define VDOAIE_EITERR BIT(1) -#define VDOAIE_EIEOT BIT(0) - -#define VDOAIST_TERR BIT(1) -#define VDOAIST_EOT BIT(0) - -#define VDOAFP_FH_MASK (0x1fff << 16) -#define VDOAFP_FW_MASK (0x3fff) - -#define VDOASL_VSLY_MASK (0x3fff << 16) -#define VDOASL_ISLY_MASK (0x7fff) - -#define VDOASR_ERRW BIT(4) -#define VDOASR_EOB BIT(3) -#define VDOASR_CURRENT_FRAME (0x3 << 1) -#define VDOASR_CURRENT_BUFFER BIT(1) - -enum { - V4L2_M2M_SRC = 0, - V4L2_M2M_DST = 1, -}; - -struct vdoa_data { - struct vdoa_ctx *curr_ctx; - struct device *dev; - struct clk *vdoa_clk; - void __iomem *regs; -}; - -struct vdoa_q_data { - unsigned int width; - unsigned int height; - unsigned int bytesperline; - unsigned int sizeimage; - u32 pixelformat; -}; - -struct vdoa_ctx { - struct vdoa_data *vdoa; - struct completion completion; - struct vdoa_q_data q_data[2]; - unsigned int submitted_job; - unsigned int completed_job; -}; - -static irqreturn_t vdoa_irq_handler(int irq, void *data) -{ - struct vdoa_data *vdoa = data; - struct vdoa_ctx *curr_ctx; - u32 val; - - /* Disable interrupts */ - writel(0, vdoa->regs + VDOAIE); - - curr_ctx = vdoa->curr_ctx; - if (!curr_ctx) { - dev_warn(vdoa->dev, - "Instance released before the end of transaction\n"); - return IRQ_HANDLED; - } - - val = readl(vdoa->regs + VDOAIST); - writel(val, vdoa->regs + VDOAIST); - if (val & VDOAIST_TERR) { - val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW; - dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read"); - } else if (!(val & VDOAIST_EOT)) { - dev_warn(vdoa->dev, "Spurious interrupt\n"); - } - curr_ctx->completed_job++; - complete(&curr_ctx->completion); - - return IRQ_HANDLED; -} - -int vdoa_wait_for_completion(struct vdoa_ctx *ctx) -{ - struct vdoa_data *vdoa = ctx->vdoa; - - if (ctx->submitted_job == ctx->completed_job) - return 0; - - if (!wait_for_completion_timeout(&ctx->completion, - msecs_to_jiffies(300))) { - dev_err(vdoa->dev, - "Timeout waiting for transfer result\n"); - return -ETIMEDOUT; - } - - return 0; -} -EXPORT_SYMBOL(vdoa_wait_for_completion); - -void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src) -{ - struct vdoa_q_data *src_q_data, *dst_q_data; - struct vdoa_data *vdoa = ctx->vdoa; - u32 val; - - if (vdoa->curr_ctx) - vdoa_wait_for_completion(vdoa->curr_ctx); - - vdoa->curr_ctx = ctx; - - reinit_completion(&ctx->completion); - ctx->submitted_job++; - - src_q_data = &ctx->q_data[V4L2_M2M_SRC]; - dst_q_data = &ctx->q_data[V4L2_M2M_DST]; - - /* Progressive, no sync, 1 frame per run */ - if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV) - val = VDOAC_PFS; - else - val = 0; - writel(val, vdoa->regs + VDOAC); - - writel(dst_q_data->height << 16 | dst_q_data->width, - vdoa->regs + VDOAFP); - - val = dst; - writel(val, vdoa->regs + VDOAIEBA00); - - writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline, - vdoa->regs + VDOASL); - - if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 || - dst_q_data->pixelformat == V4L2_PIX_FMT_NV21) - val = dst_q_data->bytesperline * dst_q_data->height; - else - val = 0; - writel(val, vdoa->regs + VDOAIUBO); - - val = src; - writel(val, vdoa->regs + VDOAVEBA0); - val = round_up(src_q_data->bytesperline * src_q_data->height, 4096); - writel(val, vdoa->regs + VDOAVUBO); - - /* Enable interrupts and start transfer */ - writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE); - writel(VDOASRR_START, vdoa->regs + VDOASRR); -} -EXPORT_SYMBOL(vdoa_device_run); - -struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) -{ - struct vdoa_ctx *ctx; - int err; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return NULL; - - err = clk_prepare_enable(vdoa->vdoa_clk); - if (err) { - kfree(ctx); - return NULL; - } - - init_completion(&ctx->completion); - ctx->vdoa = vdoa; - - return ctx; -} -EXPORT_SYMBOL(vdoa_context_create); - -void vdoa_context_destroy(struct vdoa_ctx *ctx) -{ - struct vdoa_data *vdoa = ctx->vdoa; - - if (vdoa->curr_ctx == ctx) { - vdoa_wait_for_completion(vdoa->curr_ctx); - vdoa->curr_ctx = NULL; - } - - clk_disable_unprepare(vdoa->vdoa_clk); - kfree(ctx); -} -EXPORT_SYMBOL(vdoa_context_destroy); - -int vdoa_context_configure(struct vdoa_ctx *ctx, - unsigned int width, unsigned int height, - u32 pixelformat) -{ - struct vdoa_q_data *src_q_data; - struct vdoa_q_data *dst_q_data; - - if (width < 16 || width > 8192 || width % 16 != 0 || - height < 16 || height > 4096 || height % 16 != 0) - return -EINVAL; - - if (pixelformat != V4L2_PIX_FMT_YUYV && - pixelformat != V4L2_PIX_FMT_NV12) - return -EINVAL; - - /* If no context is passed, only check if the format is valid */ - if (!ctx) - return 0; - - src_q_data = &ctx->q_data[V4L2_M2M_SRC]; - dst_q_data = &ctx->q_data[V4L2_M2M_DST]; - - src_q_data->width = width; - src_q_data->height = height; - src_q_data->bytesperline = width; - src_q_data->sizeimage = - round_up(src_q_data->bytesperline * height, 4096) + - src_q_data->bytesperline * height / 2; - - dst_q_data->width = width; - dst_q_data->height = height; - dst_q_data->pixelformat = pixelformat; - switch (pixelformat) { - case V4L2_PIX_FMT_YUYV: - dst_q_data->bytesperline = width * 2; - dst_q_data->sizeimage = dst_q_data->bytesperline * height; - break; - case V4L2_PIX_FMT_NV12: - default: - dst_q_data->bytesperline = width; - dst_q_data->sizeimage = - dst_q_data->bytesperline * height * 3 / 2; - break; - } - - return 0; -} -EXPORT_SYMBOL(vdoa_context_configure); - -static int vdoa_probe(struct platform_device *pdev) -{ - struct vdoa_data *vdoa; - int ret; - - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(&pdev->dev, "DMA enable failed\n"); - return ret; - } - - vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL); - if (!vdoa) - return -ENOMEM; - - vdoa->dev = &pdev->dev; - - vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL); - if (IS_ERR(vdoa->vdoa_clk)) { - dev_err(vdoa->dev, "Failed to get clock\n"); - return PTR_ERR(vdoa->vdoa_clk); - } - - vdoa->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(vdoa->regs)) - return PTR_ERR(vdoa->regs); - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, - vdoa_irq_handler, IRQF_ONESHOT, - "vdoa", vdoa); - if (ret < 0) { - dev_err(vdoa->dev, "Failed to get irq\n"); - return ret; - } - - platform_set_drvdata(pdev, vdoa); - - return 0; -} - -static int vdoa_remove(struct platform_device *pdev) -{ - return 0; -} - -static const struct of_device_id vdoa_dt_ids[] = { - { .compatible = "fsl,imx6q-vdoa" }, - {} -}; -MODULE_DEVICE_TABLE(of, vdoa_dt_ids); - -static struct platform_driver vdoa_driver = { - .probe = vdoa_probe, - .remove = vdoa_remove, - .driver = { - .name = VDOA_NAME, - .of_match_table = vdoa_dt_ids, - }, -}; - -module_platform_driver(vdoa_driver); - -MODULE_DESCRIPTION("Video Data Order Adapter"); -MODULE_AUTHOR("Philipp Zabel "); -MODULE_ALIAS("platform:imx-vdoa"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/coda/imx-vdoa.h b/drivers/media/platform/coda/imx-vdoa.h deleted file mode 100644 index a62eab476d58..000000000000 --- a/drivers/media/platform/coda/imx-vdoa.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Pengutronix - */ - -#ifndef IMX_VDOA_H -#define IMX_VDOA_H - -struct vdoa_data; -struct vdoa_ctx; - -#if (defined CONFIG_VIDEO_IMX_VDOA || defined CONFIG_VIDEO_IMX_VDOA_MODULE) - -struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa); -int vdoa_context_configure(struct vdoa_ctx *ctx, - unsigned int width, unsigned int height, - u32 pixelformat); -void vdoa_context_destroy(struct vdoa_ctx *ctx); - -void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src); -int vdoa_wait_for_completion(struct vdoa_ctx *ctx); - -#else - -static inline struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) -{ - return NULL; -} - -static inline int vdoa_context_configure(struct vdoa_ctx *ctx, - unsigned int width, - unsigned int height, - u32 pixelformat) -{ - return 0; -} - -static inline void vdoa_context_destroy(struct vdoa_ctx *ctx) { }; - -static inline void vdoa_device_run(struct vdoa_ctx *ctx, - dma_addr_t dst, dma_addr_t src) { }; - -static inline int vdoa_wait_for_completion(struct vdoa_ctx *ctx) -{ - return 0; -}; - -#endif - -#endif /* IMX_VDOA_H */ diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h deleted file mode 100644 index c0791c847f7c..000000000000 --- a/drivers/media/platform/coda/trace.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM coda - -#if !defined(__CODA_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) -#define __CODA_TRACE_H__ - -#include -#include - -#include "coda.h" - -TRACE_EVENT(coda_bit_run, - TP_PROTO(struct coda_ctx *ctx, int cmd), - - TP_ARGS(ctx, cmd), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, ctx) - __field(int, cmd) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->ctx = ctx->idx; - __entry->cmd = cmd; - ), - - TP_printk("minor = %d, ctx = %d, cmd = %d", - __entry->minor, __entry->ctx, __entry->cmd) -); - -TRACE_EVENT(coda_bit_done, - TP_PROTO(struct coda_ctx *ctx), - - TP_ARGS(ctx), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) -); - -DECLARE_EVENT_CLASS(coda_buf_class, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - - TP_ARGS(ctx, buf), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, index) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->vb2_buf.index; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, index = %d, ctx = %d", - __entry->minor, __entry->index, __entry->ctx) -); - -DEFINE_EVENT(coda_buf_class, coda_enc_pic_run, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -DEFINE_EVENT(coda_buf_class, coda_enc_pic_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -DECLARE_EVENT_CLASS(coda_buf_meta_class, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - struct coda_buffer_meta *meta), - - TP_ARGS(ctx, buf, meta), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, index) - __field(int, start) - __field(int, end) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->vb2_buf.index; - __entry->start = meta->start & ctx->bitstream_fifo.kfifo.mask; - __entry->end = meta->end & ctx->bitstream_fifo.kfifo.mask; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, index = %d, start = 0x%x, end = 0x%x, ctx = %d", - __entry->minor, __entry->index, __entry->start, __entry->end, - __entry->ctx) -); - -DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - struct coda_buffer_meta *meta), - TP_ARGS(ctx, buf, meta) -); - -DECLARE_EVENT_CLASS(coda_meta_class, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - - TP_ARGS(ctx, meta), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, start) - __field(int, end) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->start = meta ? (meta->start & - ctx->bitstream_fifo.kfifo.mask) : 0; - __entry->end = meta ? (meta->end & - ctx->bitstream_fifo.kfifo.mask) : 0; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", - __entry->minor, __entry->start, __entry->end, __entry->ctx) -); - -DEFINE_EVENT(coda_meta_class, coda_dec_pic_run, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - TP_ARGS(ctx, meta) -); - -DEFINE_EVENT(coda_meta_class, coda_dec_pic_done, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - TP_ARGS(ctx, meta) -); - -DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - struct coda_buffer_meta *meta), - TP_ARGS(ctx, buf, meta) -); - -DEFINE_EVENT(coda_buf_class, coda_jpeg_run, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -DEFINE_EVENT(coda_buf_class, coda_jpeg_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -#endif /* __CODA_TRACE_H__ */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../drivers/media/platform/coda -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE trace - -/* This part must be outside protection */ -#include -- cgit