diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-13 01:29:48 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-13 01:29:48 +0300 |
commit | 61da593f4458f25c59f65cfd9ba1bda570db5db7 (patch) | |
tree | 42196179375dfda07901577e080f979b2897d308 /drivers/media/platform | |
parent | a3cc31e75185f9b1ad8dc45eac77f8de788dc410 (diff) | |
parent | 60a031b64984ad4a219a13b0fe912746b586bb9b (diff) | |
download | linux-61da593f4458f25c59f65cfd9ba1bda570db5db7.tar.xz |
Merge tag 'media/v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- v4l core: subdev frame interval now supports which field
- v4l kapi: moves and renames the init_cfg pad op to init_state as an
internal op.
- new sensor drivers: gc0308, gc2145, Avnet Alvium, ov64a40, tw9900
- new camera driver: STM32 DCMIPP
- s5p-mfc has gained MFC v12 support
- new ISP driver added to staging: Starfive
- new stateful encoder/decoded: Wave5 codec It is found on the J721S2
SoC, JH7100 SoC, ssd202d SoC. Etc.
- fwnode gained support for MIPI "DisCo for Imaging"
(https://www.mipi.org/specifications/mipi-disco-imaging)
- as usual, lots of cleanups, fixups and driver improvements.
* tag 'media/v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (309 commits)
media: i2c: thp7312: select CONFIG_FW_LOADER
media: i2c: mt9m114: use fsleep() in place of udelay()
media: videobuf2: core: Rename min_buffers_needed field in vb2_queue
media: i2c: thp7312: Store frame interval in subdev state
media: docs: uAPI: Fix documentation of 'which' field for routing ioctls
media: docs: uAPI: Expand error documentation for invalid 'which' value
media: docs: uAPI: Clarify error documentation for invalid 'which' value
media: v4l2-subdev: Store frame interval in subdev state
media: v4l2-subdev: Add which field to struct v4l2_subdev_frame_interval
media: v4l2-subdev: Turn .[gs]_frame_interval into pad operations
media: v4l: subdev: Move out subdev state lock macros outside CONFIG_MEDIA_CONTROLLER
media: s5p-mfc: DPB Count Independent of VIDIOC_REQBUF
media: s5p-mfc: Load firmware for each run in MFCv12.
media: s5p-mfc: Set context for valid case before calling try_run
media: s5p-mfc: Add support for DMABUF for encoder
media: s5p-mfc: Add support for UHD encoding.
media: s5p-mfc: Add support for rate controls in MFCv12
media: s5p-mfc: Add YV12 and I420 multiplanar format support
media: s5p-mfc: Add initial support for MFCv12
media: s5p-mfc: Rename IS_MFCV10 macro
...
Diffstat (limited to 'drivers/media/platform')
185 files changed, 15490 insertions, 1615 deletions
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 5a701f64289e..0246cf0ac3a8 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -154,7 +154,6 @@ struct vpu_core { struct vpu_mbox tx_type; struct vpu_mbox tx_data; struct vpu_mbox rx; - unsigned long cmd_seq; wait_queue_head_t ack_wq; struct completion cmp; @@ -253,6 +252,8 @@ struct vpu_inst { struct list_head cmd_q; void *pending; + unsigned long cmd_seq; + atomic_long_t last_response_cmd; struct vpu_inst_ops *ops; const struct vpu_format *formats; diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c index c2337812573e..5695f5c1cb3e 100644 --- a/drivers/media/platform/amphion/vpu_cmds.c +++ b/drivers/media/platform/amphion/vpu_cmds.c @@ -32,6 +32,7 @@ struct vpu_cmd_t { struct vpu_cmd_request *request; struct vpu_rpc_event *pkt; unsigned long key; + atomic_long_t *last_response_cmd; }; static struct vpu_cmd_request vpu_cmd_requests[] = { @@ -115,6 +116,8 @@ static void vpu_free_cmd(struct vpu_cmd_t *cmd) { if (!cmd) return; + if (cmd->last_response_cmd) + atomic_long_set(cmd->last_response_cmd, cmd->key); vfree(cmd->pkt); vfree(cmd); } @@ -172,7 +175,8 @@ static int vpu_request_cmd(struct vpu_inst *inst, u32 id, void *data, return -ENOMEM; mutex_lock(&core->cmd_lock); - cmd->key = core->cmd_seq++; + cmd->key = ++inst->cmd_seq; + cmd->last_response_cmd = &inst->last_response_cmd; if (key) *key = cmd->key; if (sync) @@ -246,26 +250,12 @@ void vpu_clear_request(struct vpu_inst *inst) static bool check_is_responsed(struct vpu_inst *inst, unsigned long key) { - struct vpu_core *core = inst->core; - struct vpu_cmd_t *cmd; - bool flag = true; + unsigned long last_response = atomic_long_read(&inst->last_response_cmd); - mutex_lock(&core->cmd_lock); - cmd = inst->pending; - if (cmd && key == cmd->key) { - flag = false; - goto exit; - } - list_for_each_entry(cmd, &inst->cmd_q, list) { - if (key == cmd->key) { - flag = false; - break; - } - } -exit: - mutex_unlock(&core->cmd_lock); + if (key <= last_response && (last_response - key) < (ULONG_MAX >> 1)) + return true; - return flag; + return false; } static int sync_session_response(struct vpu_inst *inst, unsigned long key, long timeout, int try) diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 1af6fc9460d4..3a2030d02e45 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -642,7 +642,7 @@ static int vpu_core_probe(struct platform_device *pdev) return -ENODEV; core->type = core->res->type; - core->id = of_alias_get_id(dev->of_node, "vpu_core"); + core->id = of_alias_get_id(dev->of_node, "vpu-core"); if (core->id < 0) { dev_err(dev, "can't get vpu core id\n"); return core->id; diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c index 982c2c777484..940e5bda5fa3 100644 --- a/drivers/media/platform/amphion/vpu_dbg.c +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -87,7 +87,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;", vb2_is_streaming(vq), - vq->num_buffers, + vb2_get_num_buffers(vq), inst->out_format.pixfmt, inst->out_format.pixfmt >> 8, inst->out_format.pixfmt >> 16, @@ -111,7 +111,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;", vb2_is_streaming(vq), - vq->num_buffers, + vb2_get_num_buffers(vq), inst->cap_format.pixfmt, inst->cap_format.pixfmt >> 8, inst->cap_format.pixfmt >> 16, @@ -139,12 +139,19 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) return 0; vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); - for (i = 0; i < vq->num_buffers; i++) { - struct vb2_buffer *vb = vq->bufs[i]; - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + for (i = 0; i < vb2_get_num_buffers(vq); i++) { + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + + vb = vb2_get_buffer(vq, i); + if (!vb) + continue; if (vb->state == VB2_BUF_STATE_DEQUEUED) continue; + + vbuf = to_vb2_v4l2_buffer(vb); + num = scnprintf(str, sizeof(str), "output [%2d] state = %10s, %8s\n", i, vb2_stat_name[vb->state], @@ -154,12 +161,19 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) } vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); - for (i = 0; i < vq->num_buffers; i++) { - struct vb2_buffer *vb = vq->bufs[i]; - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + for (i = 0; i < vb2_get_num_buffers(vq); i++) { + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + + vb = vb2_get_buffer(vq, i); + if (!vb) + continue; if (vb->state == VB2_BUF_STATE_DEQUEUED) continue; + + vbuf = to_vb2_v4l2_buffer(vb); + num = scnprintf(str, sizeof(str), "capture[%2d] state = %10s, %8s\n", i, vb2_stat_name[vb->state], diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 0f6e4c666440..c88738e8fff7 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -439,7 +439,7 @@ int vpu_get_num_buffers(struct vpu_inst *inst, u32 type) else q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); - return q->num_buffers; + return vb2_get_num_buffers(q); } static void vpu_m2m_device_run(void *priv) @@ -587,7 +587,7 @@ static int vpu_vb2_start_streaming(struct vb2_queue *q, unsigned int count) fmt->sizeimage[0], fmt->bytesperline[0], fmt->sizeimage[1], fmt->bytesperline[1], fmt->sizeimage[2], fmt->bytesperline[2], - q->num_buffers); + vb2_get_num_buffers(q)); vb2_clear_last_buffer_dequeued(q); ret = call_vop(inst, start, q->type); if (ret) @@ -649,7 +649,7 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->dev = inst->vpu->dev; src_vq->lock = &inst->lock; ret = vb2_queue_init(src_vq); @@ -666,7 +666,7 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); - dst_vq->min_buffers_needed = 1; + dst_vq->min_queued_buffers = 1; dst_vq->dev = inst->vpu->dev; dst_vq->lock = &inst->lock; ret = vb2_queue_init(dst_vq); @@ -716,6 +716,7 @@ int vpu_v4l2_open(struct file *file, struct vpu_inst *inst) func = &vpu->decoder; atomic_set(&inst->ref_count, 0); + atomic_long_set(&inst->last_response_cmd, 0); vpu_inst_get(inst); inst->vpu = vpu; inst->core = vpu_request_core(vpu, inst->type); diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index d08aa7f73d4f..fc6050e3be0d 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2034,7 +2034,7 @@ static int aspeed_video_setup_video(struct aspeed_video *video) vbq->drv_priv = video; vbq->buf_struct_size = sizeof(struct aspeed_video_buffer); vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; + vbq->min_queued_buffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; rc = vb2_queue_init(vbq); if (rc) { diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 4046212d48b4..f8450a8ccda6 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -559,11 +559,13 @@ static const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, struct v4l2_subdev_state *sd_state) { - int ret; + struct v4l2_rect *try_crop = + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .code = isi_fmt->mbus_code, .which = V4L2_SUBDEV_FORMAT_TRY, }; + int ret; ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, sd_state, &fse); @@ -572,11 +574,11 @@ static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, * just use the maximum ISI can receive. */ if (ret) { - sd_state->pads->try_crop.width = MAX_SUPPORT_WIDTH; - sd_state->pads->try_crop.height = MAX_SUPPORT_HEIGHT; + try_crop->width = MAX_SUPPORT_WIDTH; + try_crop->height = MAX_SUPPORT_HEIGHT; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } @@ -587,6 +589,7 @@ static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, struct v4l2_pix_format *pixfmt = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg = {}; struct v4l2_subdev_state pad_state = { + .sd = isi->entity.subdev, .pads = &pad_cfg, }; struct v4l2_subdev_format format = { @@ -1242,7 +1245,7 @@ static int atmel_isi_probe(struct platform_device *pdev) q->ops = &isi_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->dev = &pdev->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 889f4fbbafb3..fead5426830e 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -406,20 +406,20 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field = V4L2_FIELD_NONE; /* Set sink format */ - fmt = v4l2_subdev_get_pad_format(subdev, state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad); *fmt = format->format; /* Propagate to source formats */ for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt = v4l2_subdev_get_pad_format(subdev, state, i); + fmt = v4l2_subdev_state_get_format(state, i); *fmt = format->format; } return 0; } -static int csi2rx_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int csi2rx_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .pad = CSI2RX_PAD_SINK, @@ -441,7 +441,6 @@ static int csi2rx_init_cfg(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2rx_set_fmt, - .init_cfg = csi2rx_init_cfg, }; static const struct v4l2_subdev_video_ops csi2rx_video_ops = { @@ -453,6 +452,10 @@ static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .pad = &csi2rx_pad_ops, }; +static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { + .init_state = csi2rx_init_state, +}; + static const struct media_entity_operations csi2rx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -663,6 +666,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->subdev.owner = THIS_MODULE; csi2rx->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); + csi2rx->subdev.internal_ops = &csi2rx_internal_ops; v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); snprintf(csi2rx->subdev.name, sizeof(csi2rx->subdev.name), "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index c115742f347f..3d98f91f1bee 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -176,8 +176,7 @@ __csi2tx_get_pad_format(struct v4l2_subdev *subdev, struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + return v4l2_subdev_state_get_format(sd_state, fmt->pad); return &csi2tx->pad_fmts[fmt->pad]; } diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig index 57f8f8a22df8..ad350eb6b1fc 100644 --- a/drivers/media/platform/chips-media/Kconfig +++ b/drivers/media/platform/chips-media/Kconfig @@ -2,19 +2,5 @@ comment "Chips&Media media platform drivers" -config VIDEO_CODA - tristate "Chips&Media Coda multi-standard codec IP" - depends on V4L_MEM2MEM_DRIVERS - depends on VIDEO_DEV && 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 +source "drivers/media/platform/chips-media/coda/Kconfig" +source "drivers/media/platform/chips-media/wave5/Kconfig" diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile index bbb16425a875..6b5d99de8b54 100644 --- a/drivers/media/platform/chips-media/Makefile +++ b/drivers/media/platform/chips-media/Makefile @@ -1,6 +1,4 @@ # 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 +obj-y += coda/ +obj-y += wave5/ diff --git a/drivers/media/platform/chips-media/coda/Kconfig b/drivers/media/platform/chips-media/coda/Kconfig new file mode 100644 index 000000000000..cb7b66c71380 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/Kconfig @@ -0,0 +1,18 @@ +# 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 && 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/coda/Makefile b/drivers/media/platform/chips-media/coda/Makefile new file mode 100644 index 000000000000..bbb16425a875 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/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/coda-bit.c index ed47d5bd8d61..ed47d5bd8d61 100644 --- a/drivers/media/platform/chips-media/coda-bit.c +++ b/drivers/media/platform/chips-media/coda/coda-bit.c diff --git a/drivers/media/platform/chips-media/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c index cc4892129aaf..7da0194ec850 100644 --- a/drivers/media/platform/chips-media/coda-common.c +++ b/drivers/media/platform/chips-media/coda/coda-common.c @@ -794,7 +794,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, 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); + __func__, v4l2_type_names[f->type], vb2_get_num_buffers(vq)); return -EBUSY; } @@ -2546,7 +2546,7 @@ static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) * 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->min_queued_buffers = 1; vq->dev = ctx->dev->dev; return vb2_queue_init(vq); diff --git a/drivers/media/platform/chips-media/coda-gdi.c b/drivers/media/platform/chips-media/coda/coda-gdi.c index 59d65daca153..59d65daca153 100644 --- a/drivers/media/platform/chips-media/coda-gdi.c +++ b/drivers/media/platform/chips-media/coda/coda-gdi.c diff --git a/drivers/media/platform/chips-media/coda-h264.c b/drivers/media/platform/chips-media/coda/coda-h264.c index 8bd0aa8af114..8bd0aa8af114 100644 --- a/drivers/media/platform/chips-media/coda-h264.c +++ b/drivers/media/platform/chips-media/coda/coda-h264.c diff --git a/drivers/media/platform/chips-media/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c index ba8f41002917..ba8f41002917 100644 --- a/drivers/media/platform/chips-media/coda-jpeg.c +++ b/drivers/media/platform/chips-media/coda/coda-jpeg.c diff --git a/drivers/media/platform/chips-media/coda-mpeg2.c b/drivers/media/platform/chips-media/coda/coda-mpeg2.c index 6f3f6721d286..6f3f6721d286 100644 --- a/drivers/media/platform/chips-media/coda-mpeg2.c +++ b/drivers/media/platform/chips-media/coda/coda-mpeg2.c diff --git a/drivers/media/platform/chips-media/coda-mpeg4.c b/drivers/media/platform/chips-media/coda/coda-mpeg4.c index 483a4fba1b4f..483a4fba1b4f 100644 --- a/drivers/media/platform/chips-media/coda-mpeg4.c +++ b/drivers/media/platform/chips-media/coda/coda-mpeg4.c diff --git a/drivers/media/platform/chips-media/coda.h b/drivers/media/platform/chips-media/coda/coda.h index ddfd0a32c653..ddfd0a32c653 100644 --- a/drivers/media/platform/chips-media/coda.h +++ b/drivers/media/platform/chips-media/coda/coda.h diff --git a/drivers/media/platform/chips-media/coda_regs.h b/drivers/media/platform/chips-media/coda/coda_regs.h index db81a904cf3f..db81a904cf3f 100644 --- a/drivers/media/platform/chips-media/coda_regs.h +++ b/drivers/media/platform/chips-media/coda/coda_regs.h diff --git a/drivers/media/platform/chips-media/imx-vdoa.c b/drivers/media/platform/chips-media/coda/imx-vdoa.c index c3561fcecb98..c3561fcecb98 100644 --- a/drivers/media/platform/chips-media/imx-vdoa.c +++ b/drivers/media/platform/chips-media/coda/imx-vdoa.c diff --git a/drivers/media/platform/chips-media/imx-vdoa.h b/drivers/media/platform/chips-media/coda/imx-vdoa.h index a62eab476d58..a62eab476d58 100644 --- a/drivers/media/platform/chips-media/imx-vdoa.h +++ b/drivers/media/platform/chips-media/coda/imx-vdoa.h diff --git a/drivers/media/platform/chips-media/trace.h b/drivers/media/platform/chips-media/coda/trace.h index 19f98e6dafb9..abc6a01a74e9 100644 --- a/drivers/media/platform/chips-media/trace.h +++ b/drivers/media/platform/chips-media/coda/trace.h @@ -167,7 +167,7 @@ DEFINE_EVENT(coda_buf_class, coda_jpeg_done, #endif /* __CODA_TRACE_H__ */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media/coda #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace diff --git a/drivers/media/platform/chips-media/wave5/Kconfig b/drivers/media/platform/chips-media/wave5/Kconfig new file mode 100644 index 000000000000..f1bcef5177bd --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_WAVE_VPU + tristate "Chips&Media Wave Codec Driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_K3 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select GENERIC_ALLOCATOR + help + Chips&Media stateful encoder and decoder driver. + The driver supports HEVC and H264 formats. + To compile this driver as modules, choose M here: the + modules will be called wave5. diff --git a/drivers/media/platform/chips-media/wave5/Makefile b/drivers/media/platform/chips-media/wave5/Makefile new file mode 100644 index 000000000000..3d738a03bd8e --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_WAVE_VPU) += wave5.o +wave5-objs += wave5-hw.o \ + wave5-vpuapi.o \ + wave5-vdi.o \ + wave5-vpu-dec.o \ + wave5-vpu.o \ + wave5-vpu-enc.o \ + wave5-helper.o diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c new file mode 100644 index 000000000000..8433ecab230c --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - decoder interface + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include "wave5-helper.h" + +const char *state_to_str(enum vpu_instance_state state) +{ + switch (state) { + case VPU_INST_STATE_NONE: + return "NONE"; + case VPU_INST_STATE_OPEN: + return "OPEN"; + case VPU_INST_STATE_INIT_SEQ: + return "INIT_SEQ"; + case VPU_INST_STATE_PIC_RUN: + return "PIC_RUN"; + case VPU_INST_STATE_STOP: + return "STOP"; + default: + return "UNKNOWN"; + } +} + +void wave5_cleanup_instance(struct vpu_instance *inst) +{ + int i; + + if (list_is_singular(&inst->list)) + wave5_vdi_free_sram(inst->dev); + + for (i = 0; i < inst->fbc_buf_count; i++) + wave5_vpu_dec_reset_framebuffer(inst, i); + + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf); + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl); + if (inst->v4l2_fh.vdev) { + v4l2_fh_del(&inst->v4l2_fh); + v4l2_fh_exit(&inst->v4l2_fh); + } + list_del_init(&inst->list); + ida_free(&inst->dev->inst_ida, inst->id); + kfree(inst->codec_info); + kfree(inst); +} + +int wave5_vpu_release_device(struct file *filp, + int (*close_func)(struct vpu_instance *inst, u32 *fail_res), + char *name) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + if (inst->state != VPU_INST_STATE_NONE) { + u32 fail_res; + int ret; + + ret = close_func(inst, &fail_res); + if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_err(inst->dev->dev, "%s close failed, device is still running\n", + name); + return -EBUSY; + } + if (ret && ret != -EIO) { + dev_err(inst->dev->dev, "%s close, fail: %d\n", name, ret); + return ret; + } + } + + wave5_cleanup_instance(inst); + + return 0; +} + +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq, + const struct vb2_ops *ops) +{ + struct vpu_instance *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->ops = ops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->buf_struct_size = sizeof(struct vpu_src_buffer); + src_vq->drv_priv = inst; + src_vq->lock = &inst->dev->dev_lock; + src_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->ops = ops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->buf_struct_size = sizeof(struct vpu_src_buffer); + dst_vq->drv_priv = inst; + dst_vq->lock = &inst->dev->dev_lock; + dst_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + bool is_decoder = inst->type == VPU_INST_TYPE_DEC; + + dev_dbg(inst->dev->dev, "%s: [%s] type: %u id: %u | flags: %u\n", __func__, + is_decoder ? "decoder" : "encoder", sub->type, sub->id, sub->flags); + + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (is_decoder) + return v4l2_src_change_event_subscribe(fh, sub); + return -EINVAL; + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i; + + f->fmt.pix_mp.width = inst->src_fmt.width; + f->fmt.pix_mp.height = inst->src_fmt.height; + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat; + f->fmt.pix_mp.field = inst->src_fmt.field; + f->fmt.pix_mp.flags = inst->src_fmt.flags; + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage; + } + + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt, + const struct vpu_format fmt_list[MAX_FMTS]) +{ + unsigned int index; + + for (index = 0; index < MAX_FMTS; index++) { + if (fmt_list[index].v4l2_pix_fmt == v4l2_pix_fmt) + return &fmt_list[index]; + } + + return NULL; +} + +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx, + const struct vpu_format fmt_list[MAX_FMTS]) +{ + if (idx >= MAX_FMTS) + return NULL; + + if (!fmt_list[idx].v4l2_pix_fmt) + return NULL; + + return &fmt_list[idx]; +} + +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type) +{ + switch (v4l2_pix_fmt) { + case V4L2_PIX_FMT_H264: + return type == VPU_INST_TYPE_DEC ? W_AVC_DEC : W_AVC_ENC; + case V4L2_PIX_FMT_HEVC: + return type == VPU_INST_TYPE_DEC ? W_HEVC_DEC : W_HEVC_ENC; + default: + return STD_UNKNOWN; + } +} + +void wave5_return_bufs(struct vb2_queue *q, u32 state) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_ctrl_handler v4l2_ctrl_hdl = inst->v4l2_ctrl_hdl; + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf = v4l2_m2m_src_buf_remove(m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!vbuf) + return; + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &v4l2_ctrl_hdl); + v4l2_m2m_buf_done(vbuf, state); + } +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h new file mode 100644 index 000000000000..6cee1c14d3ce --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - basic types + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef __WAVE_HELPER_H__ +#define __WAVE_HELPER_H__ + +#include "wave5-vpu.h" + +#define FMT_TYPES 2 +#define MAX_FMTS 12 + +const char *state_to_str(enum vpu_instance_state state); +void wave5_cleanup_instance(struct vpu_instance *inst); +int wave5_vpu_release_device(struct file *filp, + int (*close_func)(struct vpu_instance *inst, u32 *fail_res), + char *name); +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq, + const struct vb2_ops *ops); +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub); +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f); +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt, + const struct vpu_format fmt_list[MAX_FMTS]); +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx, + const struct vpu_format fmt_list[MAX_FMTS]); +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type); +void wave5_return_bufs(struct vb2_queue *q, u32 state); +#endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c new file mode 100644 index 000000000000..f1e022fb148e --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -0,0 +1,2551 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - wave5 backend logic + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include <linux/iopoll.h> +#include <linux/bitfield.h> +#include "wave5-vpu.h" +#include "wave5.h" +#include "wave5-regdefine.h" + +#define FIO_TIMEOUT 10000000 +#define FIO_CTRL_READY BIT(31) +#define FIO_CTRL_WRITE BIT(16) +#define VPU_BUSY_CHECK_TIMEOUT 10000000 +#define QUEUE_REPORT_MASK 0xffff + +/* Encoder support fields */ +#define FEATURE_HEVC10BIT_ENC BIT(3) +#define FEATURE_AVC10BIT_ENC BIT(11) +#define FEATURE_AVC_ENCODER BIT(1) +#define FEATURE_HEVC_ENCODER BIT(0) + +/* Decoder support fields */ +#define FEATURE_AVC_DECODER BIT(3) +#define FEATURE_HEVC_DECODER BIT(2) + +#define FEATURE_BACKBONE BIT(16) +#define FEATURE_VCORE_BACKBONE BIT(22) +#define FEATURE_VCPU_BACKBONE BIT(28) + +#define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff) +#define REMAP_CTRL_REGISTER_VALUE(index) ( \ + (BIT(31) | (index << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS) \ +) + +#define FASTIO_ADDRESS_MASK GENMASK(15, 0) +#define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24) + +static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, + const char *func); +#define PRINT_REG_ERR(dev, reason) _wave5_print_reg_err((dev), (reason), __func__) + +static inline const char *cmd_to_str(int cmd, bool is_dec) +{ + switch (cmd) { + case W5_INIT_VPU: + return "W5_INIT_VPU"; + case W5_WAKEUP_VPU: + return "W5_WAKEUP_VPU"; + case W5_SLEEP_VPU: + return "W5_SLEEP_VPU"; + case W5_CREATE_INSTANCE: + return "W5_CREATE_INSTANCE"; + case W5_FLUSH_INSTANCE: + return "W5_FLUSH_INSTANCE"; + case W5_DESTROY_INSTANCE: + return "W5_DESTROY_INSTANCE"; + case W5_INIT_SEQ: + return "W5_INIT_SEQ"; + case W5_SET_FB: + return "W5_SET_FB"; + case W5_DEC_ENC_PIC: + if (is_dec) + return "W5_DEC_PIC"; + return "W5_ENC_PIC"; + case W5_ENC_SET_PARAM: + return "W5_ENC_SET_PARAM"; + case W5_QUERY: + return "W5_QUERY"; + case W5_UPDATE_BS: + return "W5_UPDATE_BS"; + case W5_MAX_VPU_COMD: + return "W5_MAX_VPU_COMD"; + default: + return "UNKNOWN"; + } +} + +static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, + const char *func) +{ + struct device *dev = vpu_dev->dev; + u32 reg_val; + + switch (reg_fail_reason) { + case WAVE5_SYSERR_QUEUEING_FAIL: + reg_val = vpu_read_reg(vpu_dev, W5_RET_QUEUE_FAIL_REASON); + dev_dbg(dev, "%s: queueing failure: 0x%x\n", func, reg_val); + break; + case WAVE5_SYSERR_RESULT_NOT_READY: + dev_err(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_ACCESS_VIOLATION_HW: + dev_err(dev, "%s: access violation: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_WATCHDOG_TIMEOUT: + dev_err(dev, "%s: watchdog timeout: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_BUS_ERROR: + dev_err(dev, "%s: bus error: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_DOUBLE_FAULT: + dev_err(dev, "%s: double fault: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_VPU_STILL_RUNNING: + dev_err(dev, "%s: still running: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_VLC_BUF_FULL: + dev_err(dev, "%s: vlc buf full: 0x%x\n", func, reg_fail_reason); + break; + default: + dev_err(dev, "%s: failure:: 0x%x\n", func, reg_fail_reason); + break; + } +} + +static int wave5_wait_fio_readl(struct vpu_device *vpu_dev, u32 addr, u32 val) +{ + u32 ctrl; + int ret; + + ctrl = addr & 0xffff; + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl); + ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, + 0, FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR); + if (ret) + return ret; + + if (wave5_vdi_read_register(vpu_dev, W5_VPU_FIO_DATA) != val) + return -ETIMEDOUT; + + return 0; +} + +static void wave5_fio_writel(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data) +{ + int ret; + unsigned int ctrl; + + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_DATA, data); + ctrl = FIELD_GET(FASTIO_ADDRESS_MASK, addr); + ctrl |= FIO_CTRL_WRITE; + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl); + ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, 0, + FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR); + if (ret) + dev_dbg_ratelimited(vpu_dev->dev, "FIO write timeout: addr=0x%x data=%x\n", + ctrl, data); +} + +static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr) +{ + u32 gdi_status_check_value = 0x3f; + + if (vpu_dev->product_code == WAVE521C_CODE || + vpu_dev->product_code == WAVE521_CODE || + vpu_dev->product_code == WAVE521E1_CODE) + gdi_status_check_value = 0x00ff1f3f; + + return wave5_wait_fio_readl(vpu_dev, addr, gdi_status_check_value); +} + +static int wave5_wait_vpu_busy(struct vpu_device *vpu_dev, unsigned int addr) +{ + u32 data; + + return read_poll_timeout(wave5_vdi_read_register, data, data == 0, + 0, VPU_BUSY_CHECK_TIMEOUT, false, vpu_dev, addr); +} + +static int wave5_wait_vcpu_bus_busy(struct vpu_device *vpu_dev, unsigned int addr) +{ + return wave5_wait_fio_readl(vpu_dev, addr, 0); +} + +bool wave5_vpu_is_init(struct vpu_device *vpu_dev) +{ + return vpu_read_reg(vpu_dev, W5_VCPU_CUR_PC) != 0; +} + +unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev) +{ + u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER); + + switch (val) { + case WAVE521C_CODE: + return PRODUCT_ID_521; + case WAVE521_CODE: + case WAVE521C_DUAL_CODE: + case WAVE521E1_CODE: + case WAVE511_CODE: + case WAVE517_CODE: + case WAVE537_CODE: + dev_err(vpu_dev->dev, "Unsupported product id (%x)\n", val); + break; + default: + dev_err(vpu_dev->dev, "Invalid product id (%x)\n", val); + break; + } + + return PRODUCT_ID_NONE; +} + +static void wave5_bit_issue_command(struct vpu_device *vpu_dev, struct vpu_instance *inst, u32 cmd) +{ + u32 instance_index; + u32 codec_mode; + + if (inst) { + instance_index = inst->id; + codec_mode = inst->std; + + vpu_write_reg(vpu_dev, W5_CMD_INSTANCE_INFO, (codec_mode << 16) | + (instance_index & 0xffff)); + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + } + + vpu_write_reg(vpu_dev, W5_COMMAND, cmd); + + if (inst) { + dev_dbg(vpu_dev->dev, "%s: cmd=0x%x (%s)\n", __func__, cmd, + cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC)); + } else { + dev_dbg(vpu_dev->dev, "%s: cmd=0x%x\n", __func__, cmd); + } + + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1); +} + +static int wave5_vpu_firmware_command_queue_error_check(struct vpu_device *dev, u32 *fail_res) +{ + u32 reason = 0; + + /* Check if we were able to add a command into the VCPU QUEUE */ + if (!vpu_read_reg(dev, W5_RET_SUCCESS)) { + reason = vpu_read_reg(dev, W5_RET_FAIL_REASON); + PRINT_REG_ERR(dev, reason); + + /* + * The fail_res argument will be either NULL or 0. + * If the fail_res argument is NULL, then just return -EIO. + * Otherwise, assign the reason to fail_res, so that the + * calling function can use it. + */ + if (fail_res) + *fail_res = reason; + else + return -EIO; + + if (reason == WAVE5_SYSERR_VPU_STILL_RUNNING) + return -EBUSY; + } + return 0; +} + +static int send_firmware_command(struct vpu_instance *inst, u32 cmd, bool check_success, + u32 *queue_status, u32 *fail_result) +{ + int ret; + + wave5_bit_issue_command(inst->dev, inst, cmd); + ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_warn(inst->dev->dev, "%s: command: '%s', timed out\n", __func__, + cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC)); + return -ETIMEDOUT; + } + + if (queue_status) + *queue_status = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + /* In some cases we want to send multiple commands before checking + * whether they are queued properly + */ + if (!check_success) + return 0; + + return wave5_vpu_firmware_command_queue_error_check(inst->dev, fail_result); +} + +static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *inst, + enum query_opt query_opt) +{ + int ret; + + vpu_write_reg(vpu_dev, W5_QUERY_OPTION, query_opt); + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + wave5_bit_issue_command(vpu_dev, inst, W5_QUERY); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_warn(vpu_dev->dev, "command: 'W5_QUERY', timed out opt=0x%x\n", query_opt); + return ret; + } + + return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); +} + +static int setup_wave5_properties(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + struct vpu_attr *p_attr = &vpu_dev->attr; + u32 reg_val; + u8 *str; + int ret; + u32 hw_config_def0, hw_config_def1, hw_config_feature; + + ret = wave5_send_query(vpu_dev, NULL, GET_VPU_INFO); + if (ret) + return ret; + + reg_val = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_NAME); + str = (u8 *)®_val; + p_attr->product_name[0] = str[3]; + p_attr->product_name[1] = str[2]; + p_attr->product_name[2] = str[1]; + p_attr->product_name[3] = str[0]; + p_attr->product_name[4] = 0; + + p_attr->product_id = wave5_vpu_get_product_id(vpu_dev); + p_attr->product_version = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_VERSION); + p_attr->fw_version = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION); + p_attr->customer_id = vpu_read_reg(vpu_dev, W5_RET_CUSTOMER_ID); + hw_config_def0 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF0); + hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1); + hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE); + + p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature); + p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature); + + p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC; + p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC; + p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC; + p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC; + + p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0); + p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0); + p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0); + + return 0; +} + +int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision) +{ + u32 reg_val; + int ret; + + ret = wave5_send_query(vpu_dev, NULL, GET_VPU_INFO); + if (ret) + return ret; + + reg_val = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION); + if (revision) { + *revision = reg_val; + return 0; + } + + return -EINVAL; +} + +static void remap_page(struct vpu_device *vpu_dev, dma_addr_t code_base, u32 index) +{ + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CTRL, REMAP_CTRL_REGISTER_VALUE(index)); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_VADDR, index * W5_REMAP_MAX_SIZE); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_PADDR, code_base + index * W5_REMAP_MAX_SIZE); +} + +int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) +{ + struct vpu_buf *common_vb; + dma_addr_t code_base, temp_base; + u32 code_size, temp_size; + u32 i, reg_val, reason_code; + int ret; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + common_vb = &vpu_dev->common_mem; + + code_base = common_vb->daddr; + /* ALIGN TO 4KB */ + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + if (code_size < size * 2) + return -EINVAL; + + temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_size = WAVE5_TEMPBUF_SIZE; + + ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); + if (ret < 0) { + dev_err(vpu_dev->dev, "VPU init, Writing firmware to common buffer, fail: %d\n", + ret); + return ret; + } + + vpu_write_reg(vpu_dev, W5_PO_CONF, 0); + + /* clear registers */ + + for (i = W5_CMD_REG_BASE; i < W5_CMD_REG_END; i += 4) + vpu_write_reg(vpu_dev, i, 0x00); + + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0); + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1); + + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base); + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size); + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0); + vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base); + vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size); + + /* These register must be reset explicitly */ + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + /* Encoder interrupt */ + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + reg_val = ((WAVE5_PROC_AXI_ID << 28) | + (WAVE5_PRP_AXI_ID << 24) | + (WAVE5_FBD_Y_AXI_ID << 20) | + (WAVE5_FBC_Y_AXI_ID << 16) | + (WAVE5_FBD_C_AXI_ID << 12) | + (WAVE5_FBC_C_AXI_ID << 8) | + (WAVE5_PRI_AXI_ID << 4) | + WAVE5_SEC_AXI_ID); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); + } + + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_err(vpu_dev->dev, "VPU init(W5_VPU_REMAP_CORE_START) timeout\n"); + return ret; + } + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + if (ret) + return ret; + + return setup_wave5_properties(dev); +} + +int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, + struct dec_open_param *param) +{ + int ret; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct vpu_device *vpu_dev = inst->dev; + + p_dec_info->cycle_per_tick = 256; + if (vpu_dev->sram_buf.size) { + p_dec_info->sec_axi_info.use_bit_enable = 1; + p_dec_info->sec_axi_info.use_ip_enable = 1; + p_dec_info->sec_axi_info.use_lf_row_enable = 1; + } + switch (inst->std) { + case W_HEVC_DEC: + p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_HEVC; + break; + case W_AVC_DEC: + p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_AVC; + break; + default: + return -EINVAL; + } + + p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work); + if (ret) + return ret; + + vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1); + + wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work); + + vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr); + vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size); + + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + + vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr); + vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size); + + /* NOTE: SDMA reads MSB first */ + vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN); + /* This register must be reset explicitly */ + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + + ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); + if (ret) { + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work); + return ret; + } + + p_dec_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER); + + return 0; +} + +int wave5_vpu_hw_flush_instance(struct vpu_instance *inst) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 instance_queue_count, report_queue_count; + u32 reg_val = 0; + u32 fail_res = 0; + int ret; + + ret = send_firmware_command(inst, W5_FLUSH_INSTANCE, true, ®_val, &fail_res); + if (ret) + return ret; + + instance_queue_count = (reg_val >> 16) & 0xff; + report_queue_count = (reg_val & QUEUE_REPORT_MASK); + if (instance_queue_count != 0 || report_queue_count != 0) { + dev_warn(inst->dev->dev, + "FLUSH_INSTANCE cmd didn't reset the amount of queued commands & reports"); + } + + /* reset our local copy of the counts */ + p_dec_info->instance_queue_count = 0; + p_dec_info->report_queue_count = 0; + + return 0; +} + +static u32 get_bitstream_options(struct dec_info *info) +{ + u32 bs_option = BSOPTION_ENABLE_EXPLICIT_END; + + if (info->stream_endflag) + bs_option |= BSOPTION_HIGHLIGHT_STREAM_END; + return bs_option; +} + +int wave5_vpu_dec_init_seq(struct vpu_instance *inst) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 cmd_option = INIT_SEQ_NORMAL; + u32 reg_val, fail_res; + int ret; + + if (!inst->codec_info) + return -EINVAL; + + vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr); + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); + + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + + vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option); + vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable); + + ret = send_firmware_command(inst, W5_INIT_SEQ, true, ®_val, &fail_res); + if (ret) + return ret; + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: init seq sent (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + return 0; +} + +static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initial_info *info) +{ + u32 reg_val; + u32 profile_compatibility_flag; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst); + info->rd_ptr = p_dec_info->stream_rd_ptr; + + p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC); + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE); + info->pic_width = ((reg_val >> 16) & 0xffff); + info->pic_height = (reg_val & 0xffff); + info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_DEC_NUM_REQUIRED_FB); + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_LEFT_RIGHT); + info->pic_crop_rect.left = (reg_val >> 16) & 0xffff; + info->pic_crop_rect.right = reg_val & 0xffff; + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_TOP_BOTTOM); + info->pic_crop_rect.top = (reg_val >> 16) & 0xffff; + info->pic_crop_rect.bottom = reg_val & 0xffff; + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_COLOR_SAMPLE_INFO); + info->luma_bitdepth = reg_val & 0xf; + info->chroma_bitdepth = (reg_val >> 4) & 0xf; + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_SEQ_PARAM); + profile_compatibility_flag = (reg_val >> 12) & 0xff; + info->profile = (reg_val >> 24) & 0x1f; + + if (inst->std == W_HEVC_DEC) { + /* guessing profile */ + if (!info->profile) { + if ((profile_compatibility_flag & 0x06) == 0x06) + info->profile = HEVC_PROFILE_MAIN; /* main profile */ + else if (profile_compatibility_flag & 0x04) + info->profile = HEVC_PROFILE_MAIN10; /* main10 profile */ + else if (profile_compatibility_flag & 0x08) + /* main still picture profile */ + info->profile = HEVC_PROFILE_STILLPICTURE; + else + info->profile = HEVC_PROFILE_MAIN; /* for old version HM */ + } + } else if (inst->std == W_AVC_DEC) { + info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val); + } + + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); + p_dec_info->vlc_buf_size = info->vlc_buf_size; + p_dec_info->param_buf_size = info->param_buf_size; +} + +int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info) +{ + int ret; + u32 reg_val; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN); + + /* send QUERY cmd */ + ret = wave5_send_query(inst->dev, inst, GET_RESULT); + if (ret) + return ret; + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: init seq complete (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + /* this is not a fatal error, set ret to -EIO but don't return immediately */ + if (vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_SUCCESS) != 1) { + info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_DEC_ERR_INFO); + ret = -EIO; + } + + wave5_get_dec_seq_result(inst, info); + + return ret; +} + +int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_buffer *fb_arr, + enum tiled_map_type map_type, unsigned int count) +{ + int ret; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct dec_initial_info *init_info = &p_dec_info->initial_info; + size_t remain, idx, j, i, cnt_8_chunk, size; + u32 start_no, end_no; + u32 reg_val, cbcr_interleave, nv21, pic_size; + u32 addr_y, addr_cb, addr_cr; + u32 mv_col_size, frame_width, frame_height, fbc_y_tbl_size, fbc_c_tbl_size; + struct vpu_buf vb_buf; + bool justified = WTL_RIGHT_JUSTIFIED; + u32 format_no = WTL_PIXEL_8BIT; + u32 color_format = 0; + u32 pixel_order = 1; + u32 bwb_flag = (map_type == LINEAR_FRAME_MAP) ? 1 : 0; + + cbcr_interleave = inst->cbcr_interleave; + nv21 = inst->nv21; + mv_col_size = 0; + fbc_y_tbl_size = 0; + fbc_c_tbl_size = 0; + + if (map_type >= COMPRESSED_FRAME_MAP) { + cbcr_interleave = 0; + nv21 = 0; + + switch (inst->std) { + case W_HEVC_DEC: + mv_col_size = WAVE5_DEC_HEVC_BUF_SIZE(init_info->pic_width, + init_info->pic_height); + break; + case W_AVC_DEC: + mv_col_size = WAVE5_DEC_AVC_BUF_SIZE(init_info->pic_width, + init_info->pic_height); + break; + default: + return -EINVAL; + } + + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) { + size = ALIGN(ALIGN(mv_col_size, 16), BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_mv, count, size); + if (ret) + goto free_mv_buffers; + } + + frame_width = init_info->pic_width; + frame_height = init_info->pic_height; + fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(frame_width, frame_height), 16); + fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(frame_width, frame_height), 16); + + size = ALIGN(fbc_y_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_y_tbl, count, size); + if (ret) + goto free_fbc_y_tbl_buffers; + + size = ALIGN(fbc_c_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_c_tbl, count, size); + if (ret) + goto free_fbc_c_tbl_buffers; + + pic_size = (init_info->pic_width << 16) | (init_info->pic_height); + + vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + + (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH); + vb_buf.daddr = 0; + + if (vb_buf.size != p_dec_info->vb_task.size) { + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); + ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf); + if (ret) + goto free_fbc_c_tbl_buffers; + + p_dec_info->vb_task = vb_buf; + } + + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, + p_dec_info->vb_task.daddr); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size); + } else { + pic_size = (init_info->pic_width << 16) | (init_info->pic_height); + + if (inst->output_format == FORMAT_422) + color_format = 1; + } + vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size); + + reg_val = (bwb_flag << 28) | + (pixel_order << 23) | + (justified << 22) | + (format_no << 20) | + (color_format << 19) | + (nv21 << 17) | + (cbcr_interleave << 16) | + (fb_arr[0].stride); + vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, reg_val); + + remain = count; + cnt_8_chunk = DIV_ROUND_UP(count, 8); + idx = 0; + for (j = 0; j < cnt_8_chunk; j++) { + reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3); + vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val); + start_no = j * 8; + end_no = start_no + ((remain >= 8) ? 8 : remain) - 1; + + vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no); + + for (i = 0; i < 8 && i < remain; i++) { + addr_y = fb_arr[i + start_no].buf_y; + addr_cb = fb_arr[i + start_no].buf_cb; + addr_cr = fb_arr[i + start_no].buf_cr; + vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), addr_y); + vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), addr_cb); + if (map_type >= COMPRESSED_FRAME_MAP) { + /* luma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4), + p_dec_info->vb_fbc_y_tbl[idx].daddr); + /* chroma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), + p_dec_info->vb_fbc_c_tbl[idx].daddr); + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), + p_dec_info->vb_mv[idx].daddr); + } else { + vpu_write_reg(inst->dev, W5_ADDR_CR_BASE0 + (i << 4), addr_cr); + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), 0); + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), 0); + } + idx++; + } + remain -= i; + + ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL); + if (ret) + goto free_buffers; + } + + reg_val = vpu_read_reg(inst->dev, W5_RET_SUCCESS); + if (!reg_val) { + ret = -EIO; + goto free_buffers; + } + + return 0; + +free_buffers: + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); +free_fbc_c_tbl_buffers: + for (i = 0; i < count; i++) + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[i]); +free_fbc_y_tbl_buffers: + for (i = 0; i < count; i++) + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[i]); +free_mv_buffers: + for (i = 0; i < count; i++) + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[i]); + return ret; +} + +int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) +{ + u32 reg_val; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + + vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr); + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); + + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + + /* secondary AXI */ + reg_val = p_dec_info->sec_axi_info.use_bit_enable | + (p_dec_info->sec_axi_info.use_ip_enable << 9) | + (p_dec_info->sec_axi_info.use_lf_row_enable << 15); + vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val); + + /* set attributes of user buffer */ + vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable); + + vpu_write_reg(inst->dev, W5_COMMAND_OPTION, DEC_PIC_NORMAL); + vpu_write_reg(inst->dev, W5_CMD_DEC_TEMPORAL_ID_PLUS1, + (p_dec_info->target_spatial_id << 9) | + (p_dec_info->temp_id_select_mode << 8) | p_dec_info->target_temp_id); + vpu_write_reg(inst->dev, W5_CMD_SEQ_CHANGE_ENABLE_FLAG, p_dec_info->seq_change_mask); + /* When reordering is disabled we force the latency of the framebuffers */ + vpu_write_reg(inst->dev, W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1, !p_dec_info->reorder_enable); + + ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, ®_val, fail_res); + if (ret == -ETIMEDOUT) + return ret; + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: dec pic sent (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result) +{ + int ret; + u32 index, nal_unit_type, reg_val, sub_layer_info; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct vpu_device *vpu_dev = inst->dev; + + vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN); + + /* send QUERY cmd */ + ret = wave5_send_query(vpu_dev, inst, GET_RESULT); + if (ret) + return ret; + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: dec pic complete (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_TYPE); + + nal_unit_type = (reg_val >> 4) & 0x3f; + + if (inst->std == W_HEVC_DEC) { + if (reg_val & 0x04) + result->pic_type = PIC_TYPE_B; + else if (reg_val & 0x02) + result->pic_type = PIC_TYPE_P; + else if (reg_val & 0x01) + result->pic_type = PIC_TYPE_I; + else + result->pic_type = PIC_TYPE_MAX; + if ((nal_unit_type == 19 || nal_unit_type == 20) && result->pic_type == PIC_TYPE_I) + /* IDR_W_RADL, IDR_N_LP */ + result->pic_type = PIC_TYPE_IDR; + } else if (inst->std == W_AVC_DEC) { + if (reg_val & 0x04) + result->pic_type = PIC_TYPE_B; + else if (reg_val & 0x02) + result->pic_type = PIC_TYPE_P; + else if (reg_val & 0x01) + result->pic_type = PIC_TYPE_I; + else + result->pic_type = PIC_TYPE_MAX; + if (nal_unit_type == 5 && result->pic_type == PIC_TYPE_I) + result->pic_type = PIC_TYPE_IDR; + } + index = vpu_read_reg(inst->dev, W5_RET_DEC_DISPLAY_INDEX); + result->index_frame_display = index; + index = vpu_read_reg(inst->dev, W5_RET_DEC_DECODED_INDEX); + result->index_frame_decoded = index; + result->index_frame_decoded_for_tiled = index; + + sub_layer_info = vpu_read_reg(inst->dev, W5_RET_DEC_SUB_LAYER_INFO); + result->temporal_id = sub_layer_info & 0x7; + + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) { + result->decoded_poc = -1; + if (result->index_frame_decoded >= 0 || + result->index_frame_decoded == DECODED_IDX_FLAG_SKIP) + result->decoded_poc = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_POC); + } + + result->sequence_changed = vpu_read_reg(inst->dev, W5_RET_DEC_NOTIFICATION); + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE); + result->dec_pic_width = reg_val >> 16; + result->dec_pic_height = reg_val & 0xffff; + + if (result->sequence_changed) { + memcpy((void *)&p_dec_info->new_seq_info, (void *)&p_dec_info->initial_info, + sizeof(struct dec_initial_info)); + wave5_get_dec_seq_result(inst, &p_dec_info->new_seq_info); + } + + result->dec_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_DEC_HOST_CMD_TICK); + result->dec_decode_end_tick = vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_ENC_TICK); + + if (!p_dec_info->first_cycle_check) { + result->frame_cycle = + (result->dec_decode_end_tick - result->dec_host_cmd_tick) * + p_dec_info->cycle_per_tick; + vpu_dev->last_performance_cycles = result->dec_decode_end_tick; + p_dec_info->first_cycle_check = true; + } else if (result->index_frame_decoded_for_tiled != -1) { + result->frame_cycle = + (result->dec_decode_end_tick - vpu_dev->last_performance_cycles) * + p_dec_info->cycle_per_tick; + vpu_dev->last_performance_cycles = result->dec_decode_end_tick; + if (vpu_dev->last_performance_cycles < result->dec_host_cmd_tick) + result->frame_cycle = + (result->dec_decode_end_tick - result->dec_host_cmd_tick) * + p_dec_info->cycle_per_tick; + } + + /* no remaining command. reset frame cycle. */ + if (p_dec_info->instance_queue_count == 0 && p_dec_info->report_queue_count == 0) + p_dec_info->first_cycle_check = false; + + return 0; +} + +int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) +{ + struct vpu_buf *common_vb; + dma_addr_t code_base, temp_base; + dma_addr_t old_code_base, temp_size; + u32 code_size, reason_code; + u32 reg_val; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + common_vb = &vpu_dev->common_mem; + + code_base = common_vb->daddr; + /* ALIGN TO 4KB */ + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + if (code_size < size * 2) + return -EINVAL; + temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_size = WAVE5_TEMPBUF_SIZE; + + old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR); + + if (old_code_base != code_base + W5_REMAP_INDEX1 * W5_REMAP_MAX_SIZE) { + int ret; + + ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); + if (ret < 0) { + dev_err(vpu_dev->dev, + "VPU init, Writing firmware to common buffer, fail: %d\n", ret); + return ret; + } + + vpu_write_reg(vpu_dev, W5_PO_CONF, 0); + + ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT); + if (ret < 0) { + dev_err(vpu_dev->dev, "VPU init, Resetting the VPU, fail: %d\n", ret); + return ret; + } + + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0); + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1); + + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base); + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size); + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0); + vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base); + vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size); + + /* These register must be reset explicitly */ + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + /* Encoder interrupt */ + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + reg_val = ((WAVE5_PROC_AXI_ID << 28) | + (WAVE5_PRP_AXI_ID << 24) | + (WAVE5_FBD_Y_AXI_ID << 20) | + (WAVE5_FBC_Y_AXI_ID << 16) | + (WAVE5_FBD_C_AXI_ID << 12) | + (WAVE5_FBC_C_AXI_ID << 8) | + (WAVE5_PRI_AXI_ID << 4) | + WAVE5_SEC_AXI_ID); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); + } + + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_err(vpu_dev->dev, "VPU reinit(W5_VPU_REMAP_CORE_START) timeout\n"); + return ret; + } + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + if (ret) + return ret; + } + + return setup_wave5_properties(dev); +} + +static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code, + size_t size) +{ + u32 reg_val; + struct vpu_buf *common_vb; + dma_addr_t code_base; + u32 code_size, reason_code; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + int ret; + + if (i_sleep_wake) { + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) + return ret; + + /* + * Declare who has ownership for the host interface access + * 1 = VPU + * 0 = Host processor + */ + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_SLEEP_VPU); + /* Send an interrupt named HOST to the VPU */ + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) + return ret; + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + if (ret) + return ret; + } else { /* restore */ + common_vb = &vpu_dev->common_mem; + + code_base = common_vb->daddr; + /* ALIGN TO 4KB */ + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + if (code_size < size * 2) { + dev_err(dev, "size too small\n"); + return -EINVAL; + } + + /* Power on without DEBUG mode */ + vpu_write_reg(vpu_dev, W5_PO_CONF, 0); + + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0); + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1); + + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base); + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size); + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0); + + /* These register must be reset explicitly */ + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + /* Encoder interrupt */ + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + reg_val = ((WAVE5_PROC_AXI_ID << 28) | + (WAVE5_PRP_AXI_ID << 24) | + (WAVE5_FBD_Y_AXI_ID << 20) | + (WAVE5_FBC_Y_AXI_ID << 16) | + (WAVE5_FBD_C_AXI_ID << 12) | + (WAVE5_FBC_C_AXI_ID << 8) | + (WAVE5_PRI_AXI_ID << 4) | + WAVE5_SEC_AXI_ID); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); + } + + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU); + /* Start VPU after settings */ + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_err(vpu_dev->dev, "VPU wakeup(W5_VPU_REMAP_CORE_START) timeout\n"); + return ret; + } + + return wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + } + + return 0; +} + +int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode) +{ + u32 val = 0; + int ret = 0; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + struct vpu_attr *p_attr = &vpu_dev->attr; + /* VPU doesn't send response. force to set BUSY flag to 0. */ + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 0); + + if (reset_mode == SW_RESET_SAFETY) { + ret = wave5_vpu_sleep_wake(dev, true, NULL, 0); + if (ret) + return ret; + } + + val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if ((val >> 16) & 0x1) + p_attr->support_backbone = true; + if ((val >> 22) & 0x1) + p_attr->support_vcore_backbone = true; + if ((val >> 28) & 0x1) + p_attr->support_vcpu_backbone = true; + + /* waiting for completion of bus transaction */ + if (p_attr->support_backbone) { + dev_dbg(dev, "%s: backbone supported\n", __func__); + + if (p_attr->support_vcore_backbone) { + if (p_attr->support_vcpu_backbone) { + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0xFF); + + /* step2 : waiting for completion of bus transaction */ + ret = wave5_wait_vcpu_bus_busy(vpu_dev, + W5_BACKBONE_BUS_STATUS_VCPU); + if (ret) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00); + return ret; + } + } + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x7); + + /* step2 : waiting for completion of bus transaction */ + if (wave5_wait_bus_busy(vpu_dev, W5_BACKBONE_BUS_STATUS_VCORE0)) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00); + return -EBUSY; + } + } else { + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x7); + + /* step2 : waiting for completion of bus transaction */ + if (wave5_wait_bus_busy(vpu_dev, W5_COMBINED_BACKBONE_BUS_STATUS)) { + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00); + return -EBUSY; + } + } + } else { + dev_dbg(dev, "%s: backbone NOT supported\n", __func__); + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x100); + + /* step2 : waiting for completion of bus transaction */ + ret = wave5_wait_bus_busy(vpu_dev, W5_GDI_BUS_STATUS); + if (ret) { + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00); + return ret; + } + } + + switch (reset_mode) { + case SW_RESET_ON_BOOT: + case SW_RESET_FORCE: + case SW_RESET_SAFETY: + val = W5_RST_BLOCK_ALL; + break; + default: + return -EINVAL; + } + + if (val) { + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, val); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_RESET_STATUS); + if (ret) { + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0); + return ret; + } + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0); + } + /* step3 : must clear GDI_BUS_CTRL after done SW_RESET */ + if (p_attr->support_backbone) { + if (p_attr->support_vcore_backbone) { + if (p_attr->support_vcpu_backbone) + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00); + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00); + } else { + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00); + } + } else { + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00); + } + if (reset_mode == SW_RESET_SAFETY || reset_mode == SW_RESET_FORCE) + ret = wave5_vpu_sleep_wake(dev, false, NULL, 0); + + return ret; +} + +int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res) +{ + return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res); +} + +int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + p_dec_info->stream_endflag = eos ? 1 : 0; + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); + + return send_firmware_command(inst, W5_UPDATE_BS, true, NULL, NULL); +} + +int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + + vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, BIT(index)); + vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, 0); + + ret = wave5_send_query(inst->dev, inst, UPDATE_DISP_FLAG); + if (ret) + return ret; + + p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC); + + return 0; +} + +int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index) +{ + int ret; + + vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, 0); + vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, BIT(index)); + + ret = wave5_send_query(inst->dev, inst, UPDATE_DISP_FLAG); + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags) +{ + u32 interrupt_reason; + + interrupt_reason = vpu_read_reg(inst->dev, W5_VPU_VINT_REASON_USR); + interrupt_reason &= ~flags; + vpu_write_reg(inst->dev, W5_VPU_VINT_REASON_USR, interrupt_reason); + + return 0; +} + +dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst) +{ + int ret; + + ret = wave5_send_query(inst->dev, inst, GET_BS_RD_PTR); + if (ret) + return inst->codec_info->dec_info.stream_rd_ptr; + + return vpu_read_reg(inst->dev, W5_RET_QUERY_DEC_BS_RD_PTR); +} + +int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr) +{ + int ret; + + vpu_write_reg(inst->dev, W5_RET_QUERY_DEC_SET_BS_RD_PTR, addr); + + ret = wave5_send_query(inst->dev, inst, SET_BS_RD_PTR); + + return ret; +} + +/************************************************************************/ +/* ENCODER functions */ +/************************************************************************/ + +int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, + struct enc_open_param *open_param) +{ + int ret; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + u32 reg_val; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + dma_addr_t buffer_addr; + size_t buffer_size; + + p_enc_info->cycle_per_tick = 256; + if (vpu_dev->sram_buf.size) { + p_enc_info->sec_axi_info.use_enc_rdo_enable = 1; + p_enc_info->sec_axi_info.use_enc_lf_enable = 1; + } + + p_enc_info->vb_work.size = WAVE521ENC_WORKBUF_SIZE; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &p_enc_info->vb_work); + if (ret) { + memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work)); + return ret; + } + + wave5_vdi_clear_memory(vpu_dev, &p_enc_info->vb_work); + + vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_enc_info->vb_work.daddr); + vpu_write_reg(inst->dev, W5_WORK_SIZE, p_enc_info->vb_work.size); + + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + + reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN; + vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val); + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + + /* This register must be reset explicitly */ + vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_VCORE_INFO, 1); + + ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); + if (ret) + goto free_vb_work; + + buffer_addr = open_param->bitstream_buffer; + buffer_size = open_param->bitstream_buffer_size; + p_enc_info->stream_rd_ptr = buffer_addr; + p_enc_info->stream_wr_ptr = buffer_addr; + p_enc_info->line_buf_int_en = open_param->line_buf_int_en; + p_enc_info->stream_buf_start_addr = buffer_addr; + p_enc_info->stream_buf_size = buffer_size; + p_enc_info->stream_buf_end_addr = buffer_addr + buffer_size; + p_enc_info->stride = 0; + p_enc_info->initial_info_obtained = false; + p_enc_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER); + + return 0; +free_vb_work: + if (wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work)) + memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work)); + return ret; +} + +static void wave5_set_enc_crop_info(u32 codec, struct enc_wave_param *param, int rot_mode, + int src_width, int src_height) +{ + int aligned_width = (codec == W_HEVC_ENC) ? ALIGN(src_width, 32) : ALIGN(src_width, 16); + int aligned_height = (codec == W_HEVC_ENC) ? ALIGN(src_height, 32) : ALIGN(src_height, 16); + int pad_right, pad_bot; + int crop_right, crop_left, crop_top, crop_bot; + int prp_mode = rot_mode >> 1; /* remove prp_enable bit */ + + if (codec == W_HEVC_ENC && + (!rot_mode || prp_mode == 14)) /* prp_mode 14 : hor_mir && ver_mir && rot_180 */ + return; + + pad_right = aligned_width - src_width; + pad_bot = aligned_height - src_height; + + if (param->conf_win_right > 0) + crop_right = param->conf_win_right + pad_right; + else + crop_right = pad_right; + + if (param->conf_win_bot > 0) + crop_bot = param->conf_win_bot + pad_bot; + else + crop_bot = pad_bot; + + crop_top = param->conf_win_top; + crop_left = param->conf_win_left; + + param->conf_win_top = crop_top; + param->conf_win_left = crop_left; + param->conf_win_bot = crop_bot; + param->conf_win_right = crop_right; + + switch (prp_mode) { + case 0: + return; + case 1: + case 15: + param->conf_win_top = crop_right; + param->conf_win_left = crop_top; + param->conf_win_bot = crop_left; + param->conf_win_right = crop_bot; + break; + case 2: + case 12: + param->conf_win_top = crop_bot; + param->conf_win_left = crop_right; + param->conf_win_bot = crop_top; + param->conf_win_right = crop_left; + break; + case 3: + case 13: + param->conf_win_top = crop_left; + param->conf_win_left = crop_bot; + param->conf_win_bot = crop_right; + param->conf_win_right = crop_top; + break; + case 4: + case 10: + param->conf_win_top = crop_bot; + param->conf_win_bot = crop_top; + break; + case 8: + case 6: + param->conf_win_left = crop_right; + param->conf_win_right = crop_left; + break; + case 5: + case 11: + param->conf_win_top = crop_left; + param->conf_win_left = crop_top; + param->conf_win_bot = crop_right; + param->conf_win_right = crop_bot; + break; + case 7: + case 9: + param->conf_win_top = crop_right; + param->conf_win_left = crop_bot; + param->conf_win_bot = crop_left; + param->conf_win_right = crop_top; + break; + default: + WARN(1, "Invalid prp_mode: %d, must be in range of 1 - 15\n", prp_mode); + } +} + +int wave5_vpu_enc_init_seq(struct vpu_instance *inst) +{ + u32 reg_val = 0, rot_mir_mode, fixed_cu_size_mode = 0x7; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct enc_open_param *p_open_param = &p_enc_info->open_param; + struct enc_wave_param *p_param = &p_open_param->wave_param; + + /* + * OPT_COMMON: + * the last SET_PARAM command should be called with OPT_COMMON + */ + rot_mir_mode = 0; + if (p_enc_info->rotation_enable) { + switch (p_enc_info->rotation_angle) { + case 0: + rot_mir_mode |= NONE_ROTATE; + break; + case 90: + rot_mir_mode |= ROT_CLOCKWISE_90; + break; + case 180: + rot_mir_mode |= ROT_CLOCKWISE_180; + break; + case 270: + rot_mir_mode |= ROT_CLOCKWISE_270; + break; + } + } + + if (p_enc_info->mirror_enable) { + switch (p_enc_info->mirror_direction) { + case MIRDIR_NONE: + rot_mir_mode |= NONE_ROTATE; + break; + case MIRDIR_VER: + rot_mir_mode |= MIR_VER_FLIP; + break; + case MIRDIR_HOR: + rot_mir_mode |= MIR_HOR_FLIP; + break; + case MIRDIR_HOR_VER: + rot_mir_mode |= MIR_HOR_VER_FLIP; + break; + } + } + + wave5_set_enc_crop_info(inst->std, p_param, rot_mir_mode, p_open_param->pic_width, + p_open_param->pic_height); + + /* SET_PARAM + COMMON */ + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SET_PARAM_OPTION, OPT_COMMON); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SRC_SIZE, p_open_param->pic_height << 16 + | p_open_param->pic_width); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN, VDI_LITTLE_ENDIAN); + + reg_val = p_param->profile | + (p_param->level << 3) | + (p_param->internal_bit_depth << 14); + if (inst->std == W_HEVC_ENC) + reg_val |= (p_param->tier << 12) | + (p_param->tmvp_enable << 23) | + (p_param->sao_enable << 24) | + (p_param->skip_intra_trans << 25) | + (p_param->strong_intra_smooth_enable << 27) | + (p_param->en_still_picture << 30); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); + + reg_val = (p_param->lossless_enable) | + (p_param->const_intra_pred_flag << 1) | + (p_param->lf_cross_slice_boundary_enable << 2) | + (p_param->wpp_enable << 4) | + (p_param->disable_deblk << 5) | + ((p_param->beta_offset_div2 & 0xF) << 6) | + ((p_param->tc_offset_div2 & 0xF) << 10) | + ((p_param->chroma_cb_qp_offset & 0x1F) << 14) | + ((p_param->chroma_cr_qp_offset & 0x1F) << 19) | + (p_param->transform8x8_enable << 29) | + (p_param->entropy_coding_mode << 30); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_PPS_PARAM, reg_val); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_GOP_PARAM, p_param->gop_preset_idx); + + if (inst->std == W_AVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, p_param->intra_qp | + ((p_param->intra_period & 0x7ff) << 6) | + ((p_param->avc_idr_period & 0x7ff) << 17)); + else if (inst->std == W_HEVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, + p_param->decoding_refresh_type | (p_param->intra_qp << 3) | + (p_param->intra_period << 16)); + + reg_val = (p_param->rdo_skip << 2) | + (p_param->lambda_scaling_enable << 3) | + (fixed_cu_size_mode << 5) | + (p_param->intra_nx_n_enable << 8) | + (p_param->max_num_merge << 18); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RDO_PARAM, reg_val); + + if (inst->std == W_AVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH, + p_param->intra_mb_refresh_arg << 16 | p_param->intra_mb_refresh_mode); + else if (inst->std == W_HEVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH, + p_param->intra_refresh_arg << 16 | p_param->intra_refresh_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_FRAME_RATE, p_open_param->frame_rate_info); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_TARGET_RATE, p_open_param->bit_rate); + + reg_val = p_open_param->rc_enable | + (p_param->hvs_qp_enable << 2) | + (p_param->hvs_qp_scale << 4) | + ((p_param->initial_rc_qp & 0x3F) << 14) | + (p_open_param->vbv_buffer_size << 20); + if (inst->std == W_AVC_ENC) + reg_val |= (p_param->mb_level_rc_enable << 1); + else if (inst->std == W_HEVC_ENC) + reg_val |= (p_param->cu_level_rc_enable << 1); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_PARAM, reg_val); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM, + p_param->rc_weight_buf << 8 | p_param->rc_weight_param); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_MIN_MAX_QP, p_param->min_qp_i | + (p_param->max_qp_i << 6) | (p_param->hvs_max_delta_qp << 12)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP, p_param->min_qp_p | + (p_param->max_qp_p << 6) | (p_param->min_qp_b << 12) | + (p_param->max_qp_b << 18)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT, + p_param->conf_win_bot << 16 | p_param->conf_win_top); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT, + p_param->conf_win_right << 16 | p_param->conf_win_left); + + if (inst->std == W_AVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE, + p_param->avc_slice_arg << 16 | p_param->avc_slice_mode); + else if (inst->std == W_HEVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE, + p_param->independ_slice_mode_arg << 16 | + p_param->independ_slice_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_TIME_SCALE, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE, 0); + + if (inst->std == W_HEVC_ENC) { + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU04, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU08, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU16, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU32, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU08, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU16, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU32, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_DEPENDENT_SLICE, + p_param->depend_slice_mode_arg << 16 | p_param->depend_slice_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_PARAM, 0); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_WEIGHT, + p_param->nr_intra_weight_y | + (p_param->nr_intra_weight_cb << 5) | + (p_param->nr_intra_weight_cr << 10) | + (p_param->nr_inter_weight_y << 15) | + (p_param->nr_inter_weight_cb << 20) | + (p_param->nr_inter_weight_cr << 25)); + } + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_VUI_HRD_PARAM, 0); + + return send_firmware_command(inst, W5_ENC_SET_PARAM, true, NULL, NULL); +} + +int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info) +{ + int ret; + u32 reg_val; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + /* send QUERY cmd */ + ret = wave5_send_query(inst->dev, inst, GET_RESULT); + if (ret) + return ret; + + dev_dbg(inst->dev->dev, "%s: init seq\n", __func__); + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + if (vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS) != 1) { + info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO); + ret = -EIO; + } else { + info->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO); + } + + info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_ENC_NUM_REQUIRED_FB); + info->min_src_frame_count = vpu_read_reg(inst->dev, W5_RET_ENC_MIN_SRC_BUF_NUM); + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); + p_enc_info->vlc_buf_size = info->vlc_buf_size; + p_enc_info->param_buf_size = info->param_buf_size; + + return ret; +} + +static u32 calculate_luma_stride(u32 width, u32 bit_depth) +{ + return ALIGN(ALIGN(width, 16) * ((bit_depth > 8) ? 5 : 4), 32); +} + +static u32 calculate_chroma_stride(u32 width, u32 bit_depth) +{ + return ALIGN(ALIGN(width / 2, 16) * ((bit_depth > 8) ? 5 : 4), 32); +} + +int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst, + struct frame_buffer *fb_arr, enum tiled_map_type map_type, + unsigned int count) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + int ret = 0; + u32 stride; + u32 start_no, end_no; + size_t remain, idx, j, i, cnt_8_chunk; + u32 reg_val = 0, pic_size = 0, mv_col_size, fbc_y_tbl_size, fbc_c_tbl_size; + u32 sub_sampled_size = 0; + u32 luma_stride, chroma_stride; + u32 buf_height = 0, buf_width = 0; + u32 bit_depth; + bool avc_encoding = (inst->std == W_AVC_ENC); + struct vpu_buf vb_mv = {0}; + struct vpu_buf vb_fbc_y_tbl = {0}; + struct vpu_buf vb_fbc_c_tbl = {0}; + struct vpu_buf vb_sub_sam_buf = {0}; + struct vpu_buf vb_task = {0}; + struct enc_open_param *p_open_param; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + p_open_param = &p_enc_info->open_param; + mv_col_size = 0; + fbc_y_tbl_size = 0; + fbc_c_tbl_size = 0; + stride = p_enc_info->stride; + bit_depth = p_open_param->wave_param.internal_bit_depth; + + if (avc_encoding) { + buf_width = ALIGN(p_open_param->pic_width, 16); + buf_height = ALIGN(p_open_param->pic_height, 16); + + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) && + !(p_enc_info->rotation_angle == 180 && + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) { + buf_width = ALIGN(p_open_param->pic_width, 16); + buf_height = ALIGN(p_open_param->pic_height, 16); + } + + if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) { + buf_width = ALIGN(p_open_param->pic_height, 16); + buf_height = ALIGN(p_open_param->pic_width, 16); + } + } else { + buf_width = ALIGN(p_open_param->pic_width, 8); + buf_height = ALIGN(p_open_param->pic_height, 8); + + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) && + !(p_enc_info->rotation_angle == 180 && + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) { + buf_width = ALIGN(p_open_param->pic_width, 32); + buf_height = ALIGN(p_open_param->pic_height, 32); + } + + if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) { + buf_width = ALIGN(p_open_param->pic_height, 32); + buf_height = ALIGN(p_open_param->pic_width, 32); + } + } + + pic_size = (buf_width << 16) | buf_height; + + if (avc_encoding) { + mv_col_size = WAVE5_ENC_AVC_BUF_SIZE(buf_width, buf_height); + vb_mv.daddr = 0; + vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + } else { + mv_col_size = WAVE5_ENC_HEVC_BUF_SIZE(buf_width, buf_height); + mv_col_size = ALIGN(mv_col_size, 16); + vb_mv.daddr = 0; + vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + } + + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_mv); + if (ret) + return ret; + + p_enc_info->vb_mv = vb_mv; + + fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(buf_width, buf_height), 16); + fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(buf_width, buf_height), 16); + + vb_fbc_y_tbl.daddr = 0; + vb_fbc_y_tbl.size = ALIGN(fbc_y_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_y_tbl); + if (ret) + goto free_vb_fbc_y_tbl; + + p_enc_info->vb_fbc_y_tbl = vb_fbc_y_tbl; + + vb_fbc_c_tbl.daddr = 0; + vb_fbc_c_tbl.size = ALIGN(fbc_c_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_c_tbl); + if (ret) + goto free_vb_fbc_c_tbl; + + p_enc_info->vb_fbc_c_tbl = vb_fbc_c_tbl; + + if (avc_encoding) + sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE_AVC(buf_width, buf_height); + else + sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE(buf_width, buf_height); + vb_sub_sam_buf.size = ALIGN(sub_sampled_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + vb_sub_sam_buf.daddr = 0; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_sub_sam_buf); + if (ret) + goto free_vb_sam_buf; + + p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf; + + vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) + + (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH); + vb_task.daddr = 0; + if (p_enc_info->vb_task.size == 0) { + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task); + if (ret) + goto free_vb_task; + + p_enc_info->vb_task = vb_task; + + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, + p_enc_info->vb_task.daddr); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_task.size); + } + + /* set sub-sampled buffer base addr */ + vpu_write_reg(inst->dev, W5_ADDR_SUB_SAMPLED_FB_BASE, vb_sub_sam_buf.daddr); + /* set sub-sampled buffer size for one frame */ + vpu_write_reg(inst->dev, W5_SUB_SAMPLED_ONE_FB_SIZE, sub_sampled_size); + + vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size); + + /* set stride of luma/chroma for compressed buffer */ + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) && + !(p_enc_info->rotation_angle == 180 && + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) { + luma_stride = calculate_luma_stride(buf_width, bit_depth); + chroma_stride = calculate_chroma_stride(buf_width / 2, bit_depth); + } else { + luma_stride = calculate_luma_stride(p_open_param->pic_width, bit_depth); + chroma_stride = calculate_chroma_stride(p_open_param->pic_width / 2, bit_depth); + } + + vpu_write_reg(inst->dev, W5_FBC_STRIDE, luma_stride << 16 | chroma_stride); + vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, stride); + + remain = count; + cnt_8_chunk = DIV_ROUND_UP(count, 8); + idx = 0; + for (j = 0; j < cnt_8_chunk; j++) { + reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3); + vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val); + start_no = j * 8; + end_no = start_no + ((remain >= 8) ? 8 : remain) - 1; + + vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no); + + for (i = 0; i < 8 && i < remain; i++) { + vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), fb_arr[i + + start_no].buf_y); + vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), + fb_arr[i + start_no].buf_cb); + /* luma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4), + vb_fbc_y_tbl.daddr + idx * fbc_y_tbl_size); + /* chroma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), + vb_fbc_c_tbl.daddr + idx * fbc_c_tbl_size); + + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), + vb_mv.daddr + idx * mv_col_size); + idx++; + } + remain -= i; + + ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL); + if (ret) + goto free_vb_mem; + } + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); + if (ret) + goto free_vb_mem; + + return ret; + +free_vb_mem: + wave5_vdi_free_dma_memory(vpu_dev, &vb_task); +free_vb_task: + wave5_vdi_free_dma_memory(vpu_dev, &vb_sub_sam_buf); +free_vb_sam_buf: + wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_c_tbl); +free_vb_fbc_c_tbl: + wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_y_tbl); +free_vb_fbc_y_tbl: + wave5_vdi_free_dma_memory(vpu_dev, &vb_mv); + return ret; +} + +int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res) +{ + u32 src_frame_format; + u32 reg_val = 0; + u32 src_stride_c = 0; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct frame_buffer *p_src_frame = option->source_frame; + struct enc_open_param *p_open_param = &p_enc_info->open_param; + bool justified = WTL_RIGHT_JUSTIFIED; + u32 format_no = WTL_PIXEL_8BIT; + int ret; + + vpu_write_reg(inst->dev, W5_CMD_ENC_BS_START_ADDR, option->pic_stream_buffer_addr); + vpu_write_reg(inst->dev, W5_CMD_ENC_BS_SIZE, option->pic_stream_buffer_size); + p_enc_info->stream_buf_start_addr = option->pic_stream_buffer_addr; + p_enc_info->stream_buf_size = option->pic_stream_buffer_size; + p_enc_info->stream_buf_end_addr = + option->pic_stream_buffer_addr + option->pic_stream_buffer_size; + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI); + /* secondary AXI */ + reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) | + (p_enc_info->sec_axi_info.use_enc_lf_enable << 15); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0); + + /* + * CODEOPT_ENC_VCL is used to implicitly encode header/headers to generate bitstream. + * (use ENC_PUT_VIDEO_HEADER for give_command to encode only a header) + */ + if (option->code_option.implicit_header_encode) + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION, + CODEOPT_ENC_HEADER_IMPLICIT | CODEOPT_ENC_VCL | + (option->code_option.encode_aud << 5) | + (option->code_option.encode_eos << 6) | + (option->code_option.encode_eob << 7)); + else + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION, + option->code_option.implicit_header_encode | + (option->code_option.encode_vcl << 1) | + (option->code_option.encode_vps << 2) | + (option->code_option.encode_sps << 3) | + (option->code_option.encode_pps << 4) | + (option->code_option.encode_aud << 5) | + (option->code_option.encode_eos << 6) | + (option->code_option.encode_eob << 7)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_PIC_PARAM, 0); + + if (option->src_end_flag) + /* no more source images. */ + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, 0xFFFFFFFF); + else + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, option->src_idx); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_Y, p_src_frame->buf_y); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_U, p_src_frame->buf_cb); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_V, p_src_frame->buf_cr); + + switch (p_open_param->src_format) { + case FORMAT_420: + case FORMAT_422: + case FORMAT_YUYV: + case FORMAT_YVYU: + case FORMAT_UYVY: + case FORMAT_VYUY: + justified = WTL_LEFT_JUSTIFIED; + format_no = WTL_PIXEL_8BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + (p_src_frame->stride / 2); + src_stride_c = (p_open_param->src_format == FORMAT_422) ? src_stride_c * 2 : + src_stride_c; + break; + case FORMAT_420_P10_16BIT_MSB: + case FORMAT_422_P10_16BIT_MSB: + case FORMAT_YUYV_P10_16BIT_MSB: + case FORMAT_YVYU_P10_16BIT_MSB: + case FORMAT_UYVY_P10_16BIT_MSB: + case FORMAT_VYUY_P10_16BIT_MSB: + justified = WTL_RIGHT_JUSTIFIED; + format_no = WTL_PIXEL_16BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + (p_src_frame->stride / 2); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_16BIT_MSB) ? src_stride_c * 2 : src_stride_c; + break; + case FORMAT_420_P10_16BIT_LSB: + case FORMAT_422_P10_16BIT_LSB: + case FORMAT_YUYV_P10_16BIT_LSB: + case FORMAT_YVYU_P10_16BIT_LSB: + case FORMAT_UYVY_P10_16BIT_LSB: + case FORMAT_VYUY_P10_16BIT_LSB: + justified = WTL_LEFT_JUSTIFIED; + format_no = WTL_PIXEL_16BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + (p_src_frame->stride / 2); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_16BIT_LSB) ? src_stride_c * 2 : src_stride_c; + break; + case FORMAT_420_P10_32BIT_MSB: + case FORMAT_422_P10_32BIT_MSB: + case FORMAT_YUYV_P10_32BIT_MSB: + case FORMAT_YVYU_P10_32BIT_MSB: + case FORMAT_UYVY_P10_32BIT_MSB: + case FORMAT_VYUY_P10_32BIT_MSB: + justified = WTL_RIGHT_JUSTIFIED; + format_no = WTL_PIXEL_32BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_32BIT_MSB) ? src_stride_c * 2 : src_stride_c; + break; + case FORMAT_420_P10_32BIT_LSB: + case FORMAT_422_P10_32BIT_LSB: + case FORMAT_YUYV_P10_32BIT_LSB: + case FORMAT_YVYU_P10_32BIT_LSB: + case FORMAT_UYVY_P10_32BIT_LSB: + case FORMAT_VYUY_P10_32BIT_LSB: + justified = WTL_LEFT_JUSTIFIED; + format_no = WTL_PIXEL_32BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_32BIT_LSB) ? src_stride_c * 2 : src_stride_c; + break; + default: + return -EINVAL; + } + + src_frame_format = (inst->cbcr_interleave << 1) | (inst->nv21); + switch (p_open_param->packed_format) { + case PACKED_YUYV: + src_frame_format = 4; + break; + case PACKED_YVYU: + src_frame_format = 5; + break; + case PACKED_UYVY: + src_frame_format = 6; + break; + case PACKED_VYUY: + src_frame_format = 7; + break; + default: + break; + } + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_STRIDE, + (p_src_frame->stride << 16) | src_stride_c); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_FORMAT, src_frame_format | + (format_no << 3) | (justified << 5) | (PIC_SRC_ENDIANNESS_BIG_ENDIAN << 6)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_LONGTERM_PIC, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_Y, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_C, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_MEAN_Y, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_MEAN_C, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_PREFIX_SEI_INFO, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_PREFIX_SEI_NAL_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SUFFIX_SEI_INFO, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SUFFIX_SEI_NAL_ADDR, 0); + + ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, ®_val, fail_res); + if (ret == -ETIMEDOUT) + return ret; + + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result) +{ + int ret; + u32 encoding_success; + u32 reg_val; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct vpu_device *vpu_dev = inst->dev; + + ret = wave5_send_query(inst->dev, inst, GET_RESULT); + if (ret) + return ret; + + dev_dbg(inst->dev->dev, "%s: enc pic complete\n", __func__); + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + encoding_success = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS); + if (!encoding_success) { + result->error_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO); + return -EIO; + } + + result->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO); + + reg_val = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_TYPE); + result->pic_type = reg_val & 0xFFFF; + + result->enc_vcl_nut = vpu_read_reg(inst->dev, W5_RET_ENC_VCL_NUT); + /* + * To get the reconstructed frame use the following index on + * inst->frame_buf + */ + result->recon_frame_index = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_IDX); + result->enc_pic_byte = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_BYTE); + result->enc_src_idx = vpu_read_reg(inst->dev, W5_RET_ENC_USED_SRC_IDX); + p_enc_info->stream_wr_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_WR_PTR); + p_enc_info->stream_rd_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR); + + result->bitstream_buffer = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR); + result->rd_ptr = p_enc_info->stream_rd_ptr; + result->wr_ptr = p_enc_info->stream_wr_ptr; + + /*result for header only(no vcl) encoding */ + if (result->recon_frame_index == RECON_IDX_FLAG_HEADER_ONLY) + result->bitstream_size = result->enc_pic_byte; + else if (result->recon_frame_index < 0) + result->bitstream_size = 0; + else + result->bitstream_size = result->enc_pic_byte; + + result->enc_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_ENC_HOST_CMD_TICK); + result->enc_encode_end_tick = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_END_TICK); + + if (!p_enc_info->first_cycle_check) { + result->frame_cycle = (result->enc_encode_end_tick - result->enc_host_cmd_tick) * + p_enc_info->cycle_per_tick; + p_enc_info->first_cycle_check = true; + } else { + result->frame_cycle = + (result->enc_encode_end_tick - vpu_dev->last_performance_cycles) * + p_enc_info->cycle_per_tick; + if (vpu_dev->last_performance_cycles < result->enc_host_cmd_tick) + result->frame_cycle = (result->enc_encode_end_tick - + result->enc_host_cmd_tick) * p_enc_info->cycle_per_tick; + } + vpu_dev->last_performance_cycles = result->enc_encode_end_tick; + + return 0; +} + +int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res) +{ + return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res); +} + +static bool wave5_vpu_enc_check_common_param_valid(struct vpu_instance *inst, + struct enc_open_param *open_param) +{ + bool low_delay = true; + struct enc_wave_param *param = &open_param->wave_param; + struct vpu_device *vpu_dev = inst->dev; + struct device *dev = vpu_dev->dev; + u32 num_ctu_row = (open_param->pic_height + 64 - 1) / 64; + u32 num_ctu_col = (open_param->pic_width + 64 - 1) / 64; + u32 ctu_sz = num_ctu_col * num_ctu_row; + + if (inst->std == W_HEVC_ENC && low_delay && + param->decoding_refresh_type == DEC_REFRESH_TYPE_CRA) { + dev_warn(dev, + "dec_refresh_type(CRA) shouldn't be used together with low delay GOP\n"); + dev_warn(dev, "Suggested configuration parameter: decoding refresh type (IDR)\n"); + param->decoding_refresh_type = 2; + } + + if (param->wpp_enable && param->independ_slice_mode) { + unsigned int num_ctb_in_width = ALIGN(open_param->pic_width, 64) >> 6; + + if (param->independ_slice_mode_arg % num_ctb_in_width) { + dev_err(dev, "independ_slice_mode_arg %u must be a multiple of %u\n", + param->independ_slice_mode_arg, num_ctb_in_width); + return false; + } + } + + /* multi-slice & wpp */ + if (param->wpp_enable && param->depend_slice_mode) { + dev_err(dev, "wpp_enable && depend_slice_mode cannot be used simultaneously\n"); + return false; + } + + if (!param->independ_slice_mode && param->depend_slice_mode) { + dev_err(dev, "depend_slice_mode requires independ_slice_mode\n"); + return false; + } else if (param->independ_slice_mode && + param->depend_slice_mode == DEPEND_SLICE_MODE_RECOMMENDED && + param->independ_slice_mode_arg < param->depend_slice_mode_arg) { + dev_err(dev, "independ_slice_mode_arg: %u must be smaller than %u\n", + param->independ_slice_mode_arg, param->depend_slice_mode_arg); + return false; + } + + if (param->independ_slice_mode && param->independ_slice_mode_arg > 65535) { + dev_err(dev, "independ_slice_mode_arg: %u must be smaller than 65535\n", + param->independ_slice_mode_arg); + return false; + } + + if (param->depend_slice_mode && param->depend_slice_mode_arg > 65535) { + dev_err(dev, "depend_slice_mode_arg: %u must be smaller than 65535\n", + param->depend_slice_mode_arg); + return false; + } + + if (param->conf_win_top % 2) { + dev_err(dev, "conf_win_top: %u, must be a multiple of 2\n", param->conf_win_top); + return false; + } + + if (param->conf_win_bot % 2) { + dev_err(dev, "conf_win_bot: %u, must be a multiple of 2\n", param->conf_win_bot); + return false; + } + + if (param->conf_win_left % 2) { + dev_err(dev, "conf_win_left: %u, must be a multiple of 2\n", param->conf_win_left); + return false; + } + + if (param->conf_win_right % 2) { + dev_err(dev, "conf_win_right: %u, Must be a multiple of 2\n", + param->conf_win_right); + return false; + } + + if (param->lossless_enable && open_param->rc_enable) { + dev_err(dev, "option rate_control cannot be used with lossless_coding\n"); + return false; + } + + if (param->lossless_enable && !param->skip_intra_trans) { + dev_err(dev, "option intra_trans_skip must be enabled with lossless_coding\n"); + return false; + } + + /* intra refresh */ + if (param->intra_refresh_mode && param->intra_refresh_arg == 0) { + dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u must be > 0\n", + param->intra_refresh_mode, param->intra_refresh_arg); + return false; + } + switch (param->intra_refresh_mode) { + case REFRESH_MODE_CTU_ROWS: + if (param->intra_mb_refresh_arg > num_ctu_row) + goto invalid_refresh_argument; + break; + case REFRESH_MODE_CTU_COLUMNS: + if (param->intra_refresh_arg > num_ctu_col) + goto invalid_refresh_argument; + break; + case REFRESH_MODE_CTU_STEP_SIZE: + if (param->intra_refresh_arg > ctu_sz) + goto invalid_refresh_argument; + break; + case REFRESH_MODE_CTUS: + if (param->intra_refresh_arg > ctu_sz) + goto invalid_refresh_argument; + if (param->lossless_enable) { + dev_err(dev, "mode: %u cannot be used lossless_enable", + param->intra_refresh_mode); + return false; + } + }; + return true; + +invalid_refresh_argument: + dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u > W(%u)xH(%u)\n", + param->intra_refresh_mode, param->intra_refresh_arg, + num_ctu_row, num_ctu_col); + return false; +} + +static bool wave5_vpu_enc_check_param_valid(struct vpu_device *vpu_dev, + struct enc_open_param *open_param) +{ + struct enc_wave_param *param = &open_param->wave_param; + + if (open_param->rc_enable) { + if (param->min_qp_i > param->max_qp_i || param->min_qp_p > param->max_qp_p || + param->min_qp_b > param->max_qp_b) { + dev_err(vpu_dev->dev, "Configuration failed because min_qp is greater than max_qp\n"); + dev_err(vpu_dev->dev, "Suggested configuration parameters: min_qp = max_qp\n"); + return false; + } + + if (open_param->bit_rate <= (int)open_param->frame_rate_info) { + dev_err(vpu_dev->dev, + "enc_bit_rate: %u must be greater than the frame_rate: %u\n", + open_param->bit_rate, (int)open_param->frame_rate_info); + return false; + } + } + + return true; +} + +int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param) +{ + u32 pic_width; + u32 pic_height; + s32 product_id = inst->dev->product; + struct vpu_attr *p_attr = &inst->dev->attr; + struct enc_wave_param *param; + + if (!open_param) + return -EINVAL; + + param = &open_param->wave_param; + pic_width = open_param->pic_width; + pic_height = open_param->pic_height; + + if (inst->id >= MAX_NUM_INSTANCE) { + dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n", + inst->id, MAX_NUM_INSTANCE); + return -EOPNOTSUPP; + } + + if (inst->std != W_HEVC_ENC && + !(inst->std == W_AVC_ENC && product_id == PRODUCT_ID_521)) { + dev_err(inst->dev->dev, "Unsupported encoder-codec & product combination\n"); + return -EOPNOTSUPP; + } + + if (param->internal_bit_depth == 10) { + if (inst->std == W_HEVC_ENC && !p_attr->support_hevc10bit_enc) { + dev_err(inst->dev->dev, + "Flag support_hevc10bit_enc must be set to encode 10bit HEVC\n"); + return -EOPNOTSUPP; + } else if (inst->std == W_AVC_ENC && !p_attr->support_avc10bit_enc) { + dev_err(inst->dev->dev, + "Flag support_avc10bit_enc must be set to encode 10bit AVC\n"); + return -EOPNOTSUPP; + } + } + + if (!open_param->frame_rate_info) { + dev_err(inst->dev->dev, "No frame rate information.\n"); + return -EINVAL; + } + + if (open_param->bit_rate > MAX_BIT_RATE) { + dev_err(inst->dev->dev, "Invalid encoding bit-rate: %u (valid: 0-%u)\n", + open_param->bit_rate, MAX_BIT_RATE); + return -EINVAL; + } + + if (pic_width < W5_MIN_ENC_PIC_WIDTH || pic_width > W5_MAX_ENC_PIC_WIDTH || + pic_height < W5_MIN_ENC_PIC_HEIGHT || pic_height > W5_MAX_ENC_PIC_HEIGHT) { + dev_err(inst->dev->dev, "Invalid encoding dimension: %ux%u\n", + pic_width, pic_height); + return -EINVAL; + } + + if (param->profile) { + if (inst->std == W_HEVC_ENC) { + if ((param->profile != HEVC_PROFILE_MAIN || + (param->profile == HEVC_PROFILE_MAIN && + param->internal_bit_depth > 8)) && + (param->profile != HEVC_PROFILE_MAIN10 || + (param->profile == HEVC_PROFILE_MAIN10 && + param->internal_bit_depth < 10)) && + param->profile != HEVC_PROFILE_STILLPICTURE) { + dev_err(inst->dev->dev, + "Invalid HEVC encoding profile: %u (bit-depth: %u)\n", + param->profile, param->internal_bit_depth); + return -EINVAL; + } + } else if (inst->std == W_AVC_ENC) { + if ((param->internal_bit_depth > 8 && + param->profile != H264_PROFILE_HIGH10)) { + dev_err(inst->dev->dev, + "Invalid AVC encoding profile: %u (bit-depth: %u)\n", + param->profile, param->internal_bit_depth); + return -EINVAL; + } + } + } + + if (param->decoding_refresh_type > DEC_REFRESH_TYPE_IDR) { + dev_err(inst->dev->dev, "Invalid decoding refresh type: %u (valid: 0-2)\n", + param->decoding_refresh_type); + return -EINVAL; + } + + if (param->intra_refresh_mode > REFRESH_MODE_CTUS) { + dev_err(inst->dev->dev, "Invalid intra refresh mode: %d (valid: 0-4)\n", + param->intra_refresh_mode); + return -EINVAL; + } + + if (inst->std == W_HEVC_ENC && param->independ_slice_mode && + param->depend_slice_mode > DEPEND_SLICE_MODE_BOOST) { + dev_err(inst->dev->dev, + "Can't combine slice modes: independent and fast dependent for HEVC\n"); + return -EINVAL; + } + + if (!param->disable_deblk) { + if (param->beta_offset_div2 < -6 || param->beta_offset_div2 > 6) { + dev_err(inst->dev->dev, "Invalid beta offset: %d (valid: -6-6)\n", + param->beta_offset_div2); + return -EINVAL; + } + + if (param->tc_offset_div2 < -6 || param->tc_offset_div2 > 6) { + dev_err(inst->dev->dev, "Invalid tc offset: %d (valid: -6-6)\n", + param->tc_offset_div2); + return -EINVAL; + } + } + + if (param->intra_qp > MAX_INTRA_QP) { + dev_err(inst->dev->dev, + "Invalid intra quantization parameter: %u (valid: 0-%u)\n", + param->intra_qp, MAX_INTRA_QP); + return -EINVAL; + } + + if (open_param->rc_enable) { + if (param->min_qp_i > MAX_INTRA_QP || param->max_qp_i > MAX_INTRA_QP || + param->min_qp_p > MAX_INTRA_QP || param->max_qp_p > MAX_INTRA_QP || + param->min_qp_b > MAX_INTRA_QP || param->max_qp_b > MAX_INTRA_QP) { + dev_err(inst->dev->dev, + "Invalid quantization parameter min/max values: " + "I: %u-%u, P: %u-%u, B: %u-%u (valid for each: 0-%u)\n", + param->min_qp_i, param->max_qp_i, param->min_qp_p, param->max_qp_p, + param->min_qp_b, param->max_qp_b, MAX_INTRA_QP); + return -EINVAL; + } + + if (param->hvs_qp_enable && param->hvs_max_delta_qp > MAX_HVS_MAX_DELTA_QP) { + dev_err(inst->dev->dev, + "Invalid HVS max delta quantization parameter: %u (valid: 0-%u)\n", + param->hvs_max_delta_qp, MAX_HVS_MAX_DELTA_QP); + return -EINVAL; + } + + if (open_param->vbv_buffer_size < MIN_VBV_BUFFER_SIZE || + open_param->vbv_buffer_size > MAX_VBV_BUFFER_SIZE) { + dev_err(inst->dev->dev, "VBV buffer size: %u (valid: %u-%u)\n", + open_param->vbv_buffer_size, MIN_VBV_BUFFER_SIZE, + MAX_VBV_BUFFER_SIZE); + return -EINVAL; + } + } + + if (!wave5_vpu_enc_check_common_param_valid(inst, open_param)) + return -EINVAL; + + if (!wave5_vpu_enc_check_param_valid(inst->dev, open_param)) + return -EINVAL; + + if (param->chroma_cb_qp_offset < -12 || param->chroma_cb_qp_offset > 12) { + dev_err(inst->dev->dev, + "Invalid chroma Cb quantization parameter offset: %d (valid: -12-12)\n", + param->chroma_cb_qp_offset); + return -EINVAL; + } + + if (param->chroma_cr_qp_offset < -12 || param->chroma_cr_qp_offset > 12) { + dev_err(inst->dev->dev, + "Invalid chroma Cr quantization parameter offset: %d (valid: -12-12)\n", + param->chroma_cr_qp_offset); + return -EINVAL; + } + + if (param->intra_refresh_mode == REFRESH_MODE_CTU_STEP_SIZE && !param->intra_refresh_arg) { + dev_err(inst->dev->dev, + "Intra refresh mode CTU step-size requires an argument\n"); + return -EINVAL; + } + + if (inst->std == W_HEVC_ENC) { + if (param->nr_intra_weight_y > MAX_INTRA_WEIGHT || + param->nr_intra_weight_cb > MAX_INTRA_WEIGHT || + param->nr_intra_weight_cr > MAX_INTRA_WEIGHT) { + dev_err(inst->dev->dev, + "Invalid intra weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n", + param->nr_intra_weight_y, param->nr_intra_weight_cb, + param->nr_intra_weight_cr, MAX_INTRA_WEIGHT); + return -EINVAL; + } + + if (param->nr_inter_weight_y > MAX_INTER_WEIGHT || + param->nr_inter_weight_cb > MAX_INTER_WEIGHT || + param->nr_inter_weight_cr > MAX_INTER_WEIGHT) { + dev_err(inst->dev->dev, + "Invalid inter weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n", + param->nr_inter_weight_y, param->nr_inter_weight_cb, + param->nr_inter_weight_cr, MAX_INTER_WEIGHT); + return -EINVAL; + } + } + + return 0; +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h new file mode 100644 index 000000000000..a15c6b2c3d8b --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h @@ -0,0 +1,732 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - wave5 register definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef __WAVE5_REGISTER_DEFINE_H__ +#define __WAVE5_REGISTER_DEFINE_H__ + +enum W5_VPU_COMMAND { + W5_INIT_VPU = 0x0001, + W5_WAKEUP_VPU = 0x0002, + W5_SLEEP_VPU = 0x0004, + W5_CREATE_INSTANCE = 0x0008, /* queuing command */ + W5_FLUSH_INSTANCE = 0x0010, + W5_DESTROY_INSTANCE = 0x0020, /* queuing command */ + W5_INIT_SEQ = 0x0040, /* queuing command */ + W5_SET_FB = 0x0080, + W5_DEC_ENC_PIC = 0x0100, /* queuing command */ + W5_ENC_SET_PARAM = 0x0200, /* queuing command */ + W5_QUERY = 0x4000, + W5_UPDATE_BS = 0x8000, + W5_MAX_VPU_COMD = 0x10000, +}; + +enum query_opt { + GET_VPU_INFO = 0, + SET_WRITE_PROT = 1, + GET_RESULT = 2, + UPDATE_DISP_FLAG = 3, + GET_BW_REPORT = 4, + GET_BS_RD_PTR = 5, /* for decoder */ + GET_BS_WR_PTR = 6, /* for encoder */ + GET_SRC_BUF_FLAG = 7, /* for encoder */ + SET_BS_RD_PTR = 8, /* for decoder */ + GET_DEBUG_INFO = 0x61, +}; + +#define W5_REG_BASE 0x00000000 +#define W5_CMD_REG_BASE 0x00000100 +#define W5_CMD_REG_END 0x00000200 + +/* + * COMMON + * + * ---- + * + * Power on configuration + * PO_DEBUG_MODE [0] 1 - power on with debug mode + * USE_PO_CONF [3] 1 - use power-on-configuration + */ +#define W5_PO_CONF (W5_REG_BASE + 0x0000) +#define W5_VCPU_CUR_PC (W5_REG_BASE + 0x0004) +#define W5_VCPU_CUR_LR (W5_REG_BASE + 0x0008) +#define W5_VPU_PDBG_STEP_MASK_V (W5_REG_BASE + 0x000C) +#define W5_VPU_PDBG_CTRL (W5_REG_BASE + 0x0010) /* v_cpu debugger ctrl register */ +#define W5_VPU_PDBG_IDX_REG (W5_REG_BASE + 0x0014) /* v_cpu debugger index register */ +#define W5_VPU_PDBG_WDATA_REG (W5_REG_BASE + 0x0018) /* v_cpu debugger write data reg */ +#define W5_VPU_PDBG_RDATA_REG (W5_REG_BASE + 0x001C) /* v_cpu debugger read data reg */ + +#define W5_VPU_FIO_CTRL_ADDR (W5_REG_BASE + 0x0020) +#define W5_VPU_FIO_DATA (W5_REG_BASE + 0x0024) +#define W5_VPU_VINT_REASON_USR (W5_REG_BASE + 0x0030) +#define W5_VPU_VINT_REASON_CLR (W5_REG_BASE + 0x0034) +#define W5_VPU_HOST_INT_REQ (W5_REG_BASE + 0x0038) +#define W5_VPU_VINT_CLEAR (W5_REG_BASE + 0x003C) +#define W5_VPU_HINT_CLEAR (W5_REG_BASE + 0x0040) +#define W5_VPU_VPU_INT_STS (W5_REG_BASE + 0x0044) +#define W5_VPU_VINT_ENABLE (W5_REG_BASE + 0x0048) +#define W5_VPU_VINT_REASON (W5_REG_BASE + 0x004C) +#define W5_VPU_RESET_REQ (W5_REG_BASE + 0x0050) +#define W5_RST_BLOCK_CCLK(_core) BIT((_core)) +#define W5_RST_BLOCK_CCLK_ALL (0xff) +#define W5_RST_BLOCK_BCLK(_core) (0x100 << (_core)) +#define W5_RST_BLOCK_BCLK_ALL (0xff00) +#define W5_RST_BLOCK_ACLK(_core) (0x10000 << (_core)) +#define W5_RST_BLOCK_ACLK_ALL (0xff0000) +#define W5_RST_BLOCK_VCPU_ALL (0x3f000000) +#define W5_RST_BLOCK_ALL (0x3fffffff) +#define W5_VPU_RESET_STATUS (W5_REG_BASE + 0x0054) + +#define W5_VCPU_RESTART (W5_REG_BASE + 0x0058) +#define W5_VPU_CLK_MASK (W5_REG_BASE + 0x005C) + +/* REMAP_CTRL + * PAGE SIZE: [8:0] 0x001 - 4K + * 0x002 - 8K + * 0x004 - 16K + * ... + * 0x100 - 1M + * REGION ATTR1 [10] 0 - normal + * 1 - make bus error for the region + * REGION ATTR2 [11] 0 - normal + * 1 - bypass region + * REMAP INDEX [15:12] - 0 ~ 3 + * ENDIAN [19:16] - NOTE: Currently not supported in this driver + * AXI-ID [23:20] - upper AXI-ID + * BUS_ERROR [29] 0 - bypass + * 1 - make BUS_ERROR for unmapped region + * BYPASS_ALL [30] 1 - bypass all + * ENABLE [31] 1 - update control register[30:16] + */ +#define W5_VPU_REMAP_CTRL (W5_REG_BASE + 0x0060) +#define W5_VPU_REMAP_VADDR (W5_REG_BASE + 0x0064) +#define W5_VPU_REMAP_PADDR (W5_REG_BASE + 0x0068) +#define W5_VPU_REMAP_CORE_START (W5_REG_BASE + 0x006C) +#define W5_VPU_BUSY_STATUS (W5_REG_BASE + 0x0070) +#define W5_VPU_HALT_STATUS (W5_REG_BASE + 0x0074) +#define W5_VPU_VCPU_STATUS (W5_REG_BASE + 0x0078) +#define W5_VPU_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0094) +/* + * assign vpu_config0 = {conf_map_converter_reg, // [31] + * conf_map_converter_sig, // [30] + * 8'd0, // [29:22] + * conf_std_switch_en, // [21] + * conf_bg_detect, // [20] + * conf_3dnr_en, // [19] + * conf_one_axi_en, // [18] + * conf_sec_axi_en, // [17] + * conf_bus_info, // [16] + * conf_afbc_en, // [15] + * conf_afbc_version_id, // [14:12] + * conf_fbc_en, // [11] + * conf_fbc_version_id, // [10:08] + * conf_scaler_en, // [07] + * conf_scaler_version_id, // [06:04] + * conf_bwb_en, // [03] + * 3'd0}; // [02:00] + */ +#define W5_VPU_RET_VPU_CONFIG0 (W5_REG_BASE + 0x0098) +/* + * assign vpu_config1 = {4'd0, // [31:28] + * conf_perf_timer_en, // [27] + * conf_multi_core_en, // [26] + * conf_gcu_en, // [25] + * conf_cu_report, // [24] + * 4'd0, // [23:20] + * conf_vcore_id_3, // [19] + * conf_vcore_id_2, // [18] + * conf_vcore_id_1, // [17] + * conf_vcore_id_0, // [16] + * conf_bwb_opt, // [15] + * 7'd0, // [14:08] + * conf_cod_std_en_reserved_7, // [7] + * conf_cod_std_en_reserved_6, // [6] + * conf_cod_std_en_reserved_5, // [5] + * conf_cod_std_en_reserved_4, // [4] + * conf_cod_std_en_reserved_3, // [3] + * conf_cod_std_en_reserved_2, // [2] + * conf_cod_std_en_vp9, // [1] + * conf_cod_std_en_hevc}; // [0] + * } + */ +#define W5_VPU_RET_VPU_CONFIG1 (W5_REG_BASE + 0x009C) + +#define W5_VPU_DBG_REG0 (W5_REG_BASE + 0x00f0) +#define W5_VPU_DBG_REG1 (W5_REG_BASE + 0x00f4) +#define W5_VPU_DBG_REG2 (W5_REG_BASE + 0x00f8) +#define W5_VPU_DBG_REG3 (W5_REG_BASE + 0x00fc) + +/************************************************************************/ +/* PRODUCT INFORMATION */ +/************************************************************************/ +#define W5_PRODUCT_NAME (W5_REG_BASE + 0x1040) +#define W5_PRODUCT_NUMBER (W5_REG_BASE + 0x1044) + +/************************************************************************/ +/* DECODER/ENCODER COMMON */ +/************************************************************************/ +#define W5_COMMAND (W5_REG_BASE + 0x0100) +#define W5_COMMAND_OPTION (W5_REG_BASE + 0x0104) +#define W5_QUERY_OPTION (W5_REG_BASE + 0x0104) +#define W5_RET_SUCCESS (W5_REG_BASE + 0x0108) +#define W5_RET_FAIL_REASON (W5_REG_BASE + 0x010C) +#define W5_RET_QUEUE_FAIL_REASON (W5_REG_BASE + 0x0110) +#define W5_CMD_INSTANCE_INFO (W5_REG_BASE + 0x0110) + +#define W5_RET_QUEUE_STATUS (W5_REG_BASE + 0x01E0) +#define W5_RET_BS_EMPTY_INST (W5_REG_BASE + 0x01E4) +#define W5_RET_QUEUE_CMD_DONE_INST (W5_REG_BASE + 0x01E8) +#define W5_RET_STAGE0_INSTANCE_INFO (W5_REG_BASE + 0x01EC) +#define W5_RET_STAGE1_INSTANCE_INFO (W5_REG_BASE + 0x01F0) +#define W5_RET_STAGE2_INSTANCE_INFO (W5_REG_BASE + 0x01F4) + +#define W5_RET_SEQ_DONE_INSTANCE_INFO (W5_REG_BASE + 0x01FC) + +#define W5_BS_OPTION (W5_REG_BASE + 0x0120) + +/* return info when QUERY (GET_RESULT) for en/decoder */ +#define W5_RET_VLC_BUF_SIZE (W5_REG_BASE + 0x01B0) +/* return info when QUERY (GET_RESULT) for en/decoder */ +#define W5_RET_PARAM_BUF_SIZE (W5_REG_BASE + 0x01B4) + +/* set when SET_FB for en/decoder */ +#define W5_CMD_SET_FB_ADDR_TASK_BUF (W5_REG_BASE + 0x01D4) +#define W5_CMD_SET_FB_TASK_BUF_SIZE (W5_REG_BASE + 0x01D8) +/************************************************************************/ +/* INIT_VPU - COMMON */ +/************************************************************************/ +/* note: W5_ADDR_CODE_BASE should be aligned to 4KB */ +#define W5_ADDR_CODE_BASE (W5_REG_BASE + 0x0110) +#define W5_CODE_SIZE (W5_REG_BASE + 0x0114) +#define W5_CODE_PARAM (W5_REG_BASE + 0x0118) +#define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C) +#define W5_TEMP_SIZE (W5_REG_BASE + 0x0120) +#define W5_HW_OPTION (W5_REG_BASE + 0x012C) +#define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180) + +/************************************************************************/ +/* CREATE_INSTANCE - COMMON */ +/************************************************************************/ +#define W5_ADDR_WORK_BASE (W5_REG_BASE + 0x0114) +#define W5_WORK_SIZE (W5_REG_BASE + 0x0118) +#define W5_CMD_DEC_BS_START_ADDR (W5_REG_BASE + 0x011C) +#define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120) +#define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124) +#define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130) +#define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134) +#define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138) +#define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C) +#define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140) + +/************************************************************************/ +/* DECODER - INIT_SEQ */ +/************************************************************************/ +#define W5_BS_RD_PTR (W5_REG_BASE + 0x0118) +#define W5_BS_WR_PTR (W5_REG_BASE + 0x011C) +/************************************************************************/ +/* SET_FRAME_BUF */ +/************************************************************************/ +/* SET_FB_OPTION 0x00 REGISTER FRAMEBUFFERS + * 0x01 UPDATE FRAMEBUFFER, just one framebuffer(linear, fbc and mvcol) + */ +#define W5_SFB_OPTION (W5_REG_BASE + 0x0104) +#define W5_COMMON_PIC_INFO (W5_REG_BASE + 0x0118) +#define W5_PIC_SIZE (W5_REG_BASE + 0x011C) +#define W5_SET_FB_NUM (W5_REG_BASE + 0x0120) +#define W5_EXTRA_PIC_INFO (W5_REG_BASE + 0x0124) + +#define W5_ADDR_LUMA_BASE0 (W5_REG_BASE + 0x0134) +#define W5_ADDR_CB_BASE0 (W5_REG_BASE + 0x0138) +#define W5_ADDR_CR_BASE0 (W5_REG_BASE + 0x013C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET0 (W5_REG_BASE + 0x013C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET0 (W5_REG_BASE + 0x0140) +#define W5_ADDR_LUMA_BASE1 (W5_REG_BASE + 0x0144) +#define W5_ADDR_CB_ADDR1 (W5_REG_BASE + 0x0148) +#define W5_ADDR_CR_ADDR1 (W5_REG_BASE + 0x014C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET1 (W5_REG_BASE + 0x014C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET1 (W5_REG_BASE + 0x0150) +#define W5_ADDR_LUMA_BASE2 (W5_REG_BASE + 0x0154) +#define W5_ADDR_CB_ADDR2 (W5_REG_BASE + 0x0158) +#define W5_ADDR_CR_ADDR2 (W5_REG_BASE + 0x015C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET2 (W5_REG_BASE + 0x015C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET2 (W5_REG_BASE + 0x0160) +#define W5_ADDR_LUMA_BASE3 (W5_REG_BASE + 0x0164) +#define W5_ADDR_CB_ADDR3 (W5_REG_BASE + 0x0168) +#define W5_ADDR_CR_ADDR3 (W5_REG_BASE + 0x016C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET3 (W5_REG_BASE + 0x016C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET3 (W5_REG_BASE + 0x0170) +#define W5_ADDR_LUMA_BASE4 (W5_REG_BASE + 0x0174) +#define W5_ADDR_CB_ADDR4 (W5_REG_BASE + 0x0178) +#define W5_ADDR_CR_ADDR4 (W5_REG_BASE + 0x017C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET4 (W5_REG_BASE + 0x017C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET4 (W5_REG_BASE + 0x0180) +#define W5_ADDR_LUMA_BASE5 (W5_REG_BASE + 0x0184) +#define W5_ADDR_CB_ADDR5 (W5_REG_BASE + 0x0188) +#define W5_ADDR_CR_ADDR5 (W5_REG_BASE + 0x018C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET5 (W5_REG_BASE + 0x018C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET5 (W5_REG_BASE + 0x0190) +#define W5_ADDR_LUMA_BASE6 (W5_REG_BASE + 0x0194) +#define W5_ADDR_CB_ADDR6 (W5_REG_BASE + 0x0198) +#define W5_ADDR_CR_ADDR6 (W5_REG_BASE + 0x019C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET6 (W5_REG_BASE + 0x019C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET6 (W5_REG_BASE + 0x01A0) +#define W5_ADDR_LUMA_BASE7 (W5_REG_BASE + 0x01A4) +#define W5_ADDR_CB_ADDR7 (W5_REG_BASE + 0x01A8) +#define W5_ADDR_CR_ADDR7 (W5_REG_BASE + 0x01AC) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET7 (W5_REG_BASE + 0x01AC) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET7 (W5_REG_BASE + 0x01B0) +#define W5_ADDR_MV_COL0 (W5_REG_BASE + 0x01B4) +#define W5_ADDR_MV_COL1 (W5_REG_BASE + 0x01B8) +#define W5_ADDR_MV_COL2 (W5_REG_BASE + 0x01BC) +#define W5_ADDR_MV_COL3 (W5_REG_BASE + 0x01C0) +#define W5_ADDR_MV_COL4 (W5_REG_BASE + 0x01C4) +#define W5_ADDR_MV_COL5 (W5_REG_BASE + 0x01C8) +#define W5_ADDR_MV_COL6 (W5_REG_BASE + 0x01CC) +#define W5_ADDR_MV_COL7 (W5_REG_BASE + 0x01D0) + +/* UPDATE_FB */ +/* CMD_SET_FB_STRIDE [15:0] - FBC framebuffer stride + * [31:15] - linear framebuffer stride + */ +#define W5_CMD_SET_FB_STRIDE (W5_REG_BASE + 0x0118) +#define W5_CMD_SET_FB_INDEX (W5_REG_BASE + 0x0120) +#define W5_ADDR_LUMA_BASE (W5_REG_BASE + 0x0134) +#define W5_ADDR_CB_BASE (W5_REG_BASE + 0x0138) +#define W5_ADDR_CR_BASE (W5_REG_BASE + 0x013C) +#define W5_ADDR_MV_COL (W5_REG_BASE + 0x0140) +#define W5_ADDR_FBC_Y_BASE (W5_REG_BASE + 0x0144) +#define W5_ADDR_FBC_C_BASE (W5_REG_BASE + 0x0148) +#define W5_ADDR_FBC_Y_OFFSET (W5_REG_BASE + 0x014C) +#define W5_ADDR_FBC_C_OFFSET (W5_REG_BASE + 0x0150) + +/************************************************************************/ +/* DECODER - DEC_PIC */ +/************************************************************************/ +#define W5_CMD_DEC_VCORE_INFO (W5_REG_BASE + 0x0194) +/* sequence change enable mask register + * CMD_SEQ_CHANGE_ENABLE_FLAG [5] profile_idc + * [16] pic_width/height_in_luma_sample + * [19] sps_max_dec_pic_buffering, max_num_reorder, max_latency_increase + */ +#define W5_CMD_SEQ_CHANGE_ENABLE_FLAG (W5_REG_BASE + 0x0128) +#define W5_CMD_DEC_USER_MASK (W5_REG_BASE + 0x012C) +#define W5_CMD_DEC_TEMPORAL_ID_PLUS1 (W5_REG_BASE + 0x0130) +#define W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1 (W5_REG_BASE + 0x0134) +#define W5_USE_SEC_AXI (W5_REG_BASE + 0x0150) + +/************************************************************************/ +/* DECODER - QUERY : GET_VPU_INFO */ +/************************************************************************/ +#define W5_RET_FW_VERSION (W5_REG_BASE + 0x0118) +#define W5_RET_PRODUCT_NAME (W5_REG_BASE + 0x011C) +#define W5_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0120) +#define W5_RET_STD_DEF0 (W5_REG_BASE + 0x0124) +#define W5_RET_STD_DEF1 (W5_REG_BASE + 0x0128) +#define W5_RET_CONF_FEATURE (W5_REG_BASE + 0x012C) +#define W5_RET_CONF_DATE (W5_REG_BASE + 0x0130) +#define W5_RET_CONF_REVISION (W5_REG_BASE + 0x0134) +#define W5_RET_CONF_TYPE (W5_REG_BASE + 0x0138) +#define W5_RET_PRODUCT_ID (W5_REG_BASE + 0x013C) +#define W5_RET_CUSTOMER_ID (W5_REG_BASE + 0x0140) + +/************************************************************************/ +/* DECODER - QUERY : GET_RESULT */ +/************************************************************************/ +#define W5_CMD_DEC_ADDR_REPORT_BASE (W5_REG_BASE + 0x0114) +#define W5_CMD_DEC_REPORT_SIZE (W5_REG_BASE + 0x0118) +#define W5_CMD_DEC_REPORT_PARAM (W5_REG_BASE + 0x011C) + +#define W5_RET_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C) +#define W5_RET_DEC_SEQ_PARAM (W5_REG_BASE + 0x0120) +#define W5_RET_DEC_COLOR_SAMPLE_INFO (W5_REG_BASE + 0x0124) +#define W5_RET_DEC_ASPECT_RATIO (W5_REG_BASE + 0x0128) +#define W5_RET_DEC_BIT_RATE (W5_REG_BASE + 0x012C) +#define W5_RET_DEC_FRAME_RATE_NR (W5_REG_BASE + 0x0130) +#define W5_RET_DEC_FRAME_RATE_DR (W5_REG_BASE + 0x0134) +#define W5_RET_DEC_NUM_REQUIRED_FB (W5_REG_BASE + 0x0138) +#define W5_RET_DEC_NUM_REORDER_DELAY (W5_REG_BASE + 0x013C) +#define W5_RET_DEC_SUB_LAYER_INFO (W5_REG_BASE + 0x0140) +#define W5_RET_DEC_NOTIFICATION (W5_REG_BASE + 0x0144) +/* + * USER_DATA_FLAGS for HEVC/H264 only. + * Bits: + * [1] - User data buffer full boolean + * [2] - VUI parameter flag + * [4] - Pic_timing SEI flag + * [5] - 1st user_data_registed_itu_t_t35 prefix SEI flag + * [6] - user_data_unregistered prefix SEI flag + * [7] - 1st user_data_registed_itu_t_t35 suffix SEI flag + * [8] - user_data_unregistered suffix SEI flag + * [10]- mastering_display_color_volume prefix SEI flag + * [11]- chroma_resampling_display_color_volume prefix SEI flag + * [12]- knee_function_info SEI flag + * [13]- tone_mapping_info prefix SEI flag + * [14]- film_grain_characteristics_info prefix SEI flag + * [15]- content_light_level_info prefix SEI flag + * [16]- color_remapping_info prefix SEI flag + * [28]- 2nd user_data_registed_itu_t_t35 prefix SEI flag + * [29]- 3rd user_data_registed_itu_t_t35 prefix SEI flag + * [30]- 2nd user_data_registed_itu_t_t35 suffix SEI flag + * [31]- 3rd user_data_registed_itu_t_t35 suffix SEI flag + */ +#define W5_RET_DEC_USERDATA_IDC (W5_REG_BASE + 0x0148) +#define W5_RET_DEC_PIC_SIZE (W5_REG_BASE + 0x014C) +#define W5_RET_DEC_CROP_TOP_BOTTOM (W5_REG_BASE + 0x0150) +#define W5_RET_DEC_CROP_LEFT_RIGHT (W5_REG_BASE + 0x0154) +/* + * #define W5_RET_DEC_AU_START_POS (W5_REG_BASE + 0x0158) + * => Access unit (AU) Bitstream start position + * #define W5_RET_DEC_AU_END_POS (W5_REG_BASE + 0x015C) + * => Access unit (AU) Bitstream end position + */ + +/* + * Decoded picture type: + * reg_val & 0x7 => picture type + * (reg_val >> 4) & 0x3f => VCL NAL unit type + * (reg_val >> 31) & 0x1 => output_flag + * 16 << ((reg_val >> 10) & 0x3) => ctu_size + */ +#define W5_RET_DEC_PIC_TYPE (W5_REG_BASE + 0x0160) +#define W5_RET_DEC_PIC_POC (W5_REG_BASE + 0x0164) +/* + * #define W5_RET_DEC_RECOVERY_POINT (W5_REG_BASE + 0x0168) + * => HEVC recovery point + * reg_val & 0xff => number of signed recovery picture order counts + * (reg_val >> 16) & 0x1 => exact match flag + * (reg_val >> 17) & 0x1 => broken link flag + * (reg_val >> 18) & 0x1 => exist flag + */ +#define W5_RET_DEC_DEBUG_INDEX (W5_REG_BASE + 0x016C) +#define W5_RET_DEC_DECODED_INDEX (W5_REG_BASE + 0x0170) +#define W5_RET_DEC_DISPLAY_INDEX (W5_REG_BASE + 0x0174) +/* + * #define W5_RET_DEC_REALLOC_INDEX (W5_REG_BASE + 0x0178) + * => display picture index in decoded picture buffer + * reg_val & 0xf => display picture index for FBC buffer (by reordering) + */ +#define W5_RET_DEC_DISP_IDC (W5_REG_BASE + 0x017C) +/* + * #define W5_RET_DEC_ERR_CTB_NUM (W5_REG_BASE + 0x0180) + * => Number of error CTUs + * reg_val >> 16 => erroneous CTUs in bitstream + * reg_val & 0xffff => total CTUs in bitstream + * + * #define W5_RET_DEC_PIC_PARAM (W5_REG_BASE + 0x01A0) + * => Bitstream sequence/picture parameter information (AV1 only) + * reg_val & 0x1 => intrabc tool enable + * (reg_val >> 1) & 0x1 => screen content tools enable + */ +#define W5_RET_DEC_HOST_CMD_TICK (W5_REG_BASE + 0x01B8) +/* + * #define W5_RET_DEC_SEEK_START_TICK (W5_REG_BASE + 0x01BC) + * #define W5_RET_DEC_SEEK_END_TICK (W5_REG_BASE + 0x01C0) + * => Start and end ticks for seeking slices of the picture + * #define W5_RET_DEC_PARSING_START_TICK (W5_REG_BASE + 0x01C4) + * #define W5_RET_DEC_PARSING_END_TICK (W5_REG_BASE + 0x01C8) + * => Start and end ticks for parsing slices of the picture + * #define W5_RET_DEC_DECODING_START_TICK (W5_REG_BASE + 0x01CC) + * => Start tick for decoding slices of the picture + */ +#define W5_RET_DEC_DECODING_ENC_TICK (W5_REG_BASE + 0x01D0) +#define W5_RET_DEC_WARN_INFO (W5_REG_BASE + 0x01D4) +#define W5_RET_DEC_ERR_INFO (W5_REG_BASE + 0x01D8) +#define W5_RET_DEC_DECODING_SUCCESS (W5_REG_BASE + 0x01DC) + +/************************************************************************/ +/* DECODER - FLUSH_INSTANCE */ +/************************************************************************/ +#define W5_CMD_FLUSH_INST_OPT (W5_REG_BASE + 0x104) + +/************************************************************************/ +/* DECODER - QUERY : UPDATE_DISP_FLAG */ +/************************************************************************/ +#define W5_CMD_DEC_SET_DISP_IDC (W5_REG_BASE + 0x0118) +#define W5_CMD_DEC_CLR_DISP_IDC (W5_REG_BASE + 0x011C) + +/************************************************************************/ +/* DECODER - QUERY : SET_BS_RD_PTR */ +/************************************************************************/ +#define W5_RET_QUERY_DEC_SET_BS_RD_PTR (W5_REG_BASE + 0x011C) + +/************************************************************************/ +/* DECODER - QUERY : GET_BS_RD_PTR */ +/************************************************************************/ +#define W5_RET_QUERY_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C) + +/************************************************************************/ +/* QUERY : GET_DEBUG_INFO */ +/************************************************************************/ +#define W5_RET_QUERY_DEBUG_PRI_REASON (W5_REG_BASE + 0x114) + +/************************************************************************/ +/* GDI register for debugging */ +/************************************************************************/ +#define W5_GDI_BASE 0x8800 +#define W5_GDI_BUS_CTRL (W5_GDI_BASE + 0x0F0) +#define W5_GDI_BUS_STATUS (W5_GDI_BASE + 0x0F4) + +#define W5_BACKBONE_BASE_VCPU 0xFE00 +#define W5_BACKBONE_BUS_CTRL_VCPU (W5_BACKBONE_BASE_VCPU + 0x010) +#define W5_BACKBONE_BUS_STATUS_VCPU (W5_BACKBONE_BASE_VCPU + 0x014) +#define W5_BACKBONE_PROG_AXI_ID (W5_BACKBONE_BASE_VCPU + 0x00C) + +#define W5_BACKBONE_PROC_EXT_ADDR (W5_BACKBONE_BASE_VCPU + 0x0C0) +#define W5_BACKBONE_AXI_PARAM (W5_BACKBONE_BASE_VCPU + 0x0E0) + +#define W5_BACKBONE_BASE_VCORE0 0x8E00 +#define W5_BACKBONE_BUS_CTRL_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x010) +#define W5_BACKBONE_BUS_STATUS_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x014) + +#define W5_BACKBONE_BASE_VCORE1 0x9E00 /* for dual-core product */ +#define W5_BACKBONE_BUS_CTRL_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x010) +#define W5_BACKBONE_BUS_STATUS_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x014) + +#define W5_COMBINED_BACKBONE_BASE 0xFE00 +#define W5_COMBINED_BACKBONE_BUS_CTRL (W5_COMBINED_BACKBONE_BASE + 0x010) +#define W5_COMBINED_BACKBONE_BUS_STATUS (W5_COMBINED_BACKBONE_BASE + 0x014) + +/************************************************************************/ +/* */ +/* for ENCODER */ +/* */ +/************************************************************************/ +#define W5_RET_STAGE3_INSTANCE_INFO (W5_REG_BASE + 0x1F8) +/************************************************************************/ +/* ENCODER - CREATE_INSTANCE */ +/************************************************************************/ +/* 0x114 ~ 0x124 : defined above (CREATE_INSTANCE COMMON) */ +#define W5_CMD_ENC_VCORE_INFO (W5_REG_BASE + 0x0194) +#define W5_CMD_ENC_SRC_OPTIONS (W5_REG_BASE + 0x0128) + +/************************************************************************/ +/* ENCODER - SET_FB */ +/************************************************************************/ +#define W5_FBC_STRIDE (W5_REG_BASE + 0x128) +#define W5_ADDR_SUB_SAMPLED_FB_BASE (W5_REG_BASE + 0x12C) +#define W5_SUB_SAMPLED_ONE_FB_SIZE (W5_REG_BASE + 0x130) + +/************************************************************************/ +/* ENCODER - ENC_SET_PARAM (COMMON & CHANGE_PARAM) */ +/************************************************************************/ +#define W5_CMD_ENC_SEQ_SET_PARAM_OPTION (W5_REG_BASE + 0x104) +#define W5_CMD_ENC_SEQ_SET_PARAM_ENABLE (W5_REG_BASE + 0x118) +#define W5_CMD_ENC_SEQ_SRC_SIZE (W5_REG_BASE + 0x11C) +#define W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN (W5_REG_BASE + 0x120) +#define W5_CMD_ENC_SEQ_SPS_PARAM (W5_REG_BASE + 0x124) +#define W5_CMD_ENC_SEQ_PPS_PARAM (W5_REG_BASE + 0x128) +#define W5_CMD_ENC_SEQ_GOP_PARAM (W5_REG_BASE + 0x12C) +#define W5_CMD_ENC_SEQ_INTRA_PARAM (W5_REG_BASE + 0x130) +#define W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT (W5_REG_BASE + 0x134) +#define W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT (W5_REG_BASE + 0x138) +#define W5_CMD_ENC_SEQ_RDO_PARAM (W5_REG_BASE + 0x13C) +#define W5_CMD_ENC_SEQ_INDEPENDENT_SLICE (W5_REG_BASE + 0x140) +#define W5_CMD_ENC_SEQ_DEPENDENT_SLICE (W5_REG_BASE + 0x144) +#define W5_CMD_ENC_SEQ_INTRA_REFRESH (W5_REG_BASE + 0x148) +#define W5_CMD_ENC_SEQ_INPUT_SRC_PARAM (W5_REG_BASE + 0x14C) + +#define W5_CMD_ENC_SEQ_RC_FRAME_RATE (W5_REG_BASE + 0x150) +#define W5_CMD_ENC_SEQ_RC_TARGET_RATE (W5_REG_BASE + 0x154) +#define W5_CMD_ENC_SEQ_RC_PARAM (W5_REG_BASE + 0x158) +#define W5_CMD_ENC_SEQ_RC_MIN_MAX_QP (W5_REG_BASE + 0x15C) +#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3 (W5_REG_BASE + 0x160) +#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7 (W5_REG_BASE + 0x164) +#define W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP (W5_REG_BASE + 0x168) +#define W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM (W5_REG_BASE + 0x16C) + +#define W5_CMD_ENC_SEQ_ROT_PARAM (W5_REG_BASE + 0x170) +#define W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK (W5_REG_BASE + 0x174) +#define W5_CMD_ENC_SEQ_TIME_SCALE (W5_REG_BASE + 0x178) +#define W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE (W5_REG_BASE + 0x17C) + +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU04 (W5_REG_BASE + 0x184) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU08 (W5_REG_BASE + 0x188) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU16 (W5_REG_BASE + 0x18C) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU32 (W5_REG_BASE + 0x190) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU08 (W5_REG_BASE + 0x194) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU16 (W5_REG_BASE + 0x198) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU32 (W5_REG_BASE + 0x19C) +#define W5_CMD_ENC_SEQ_NR_PARAM (W5_REG_BASE + 0x1A0) +#define W5_CMD_ENC_SEQ_NR_WEIGHT (W5_REG_BASE + 0x1A4) +#define W5_CMD_ENC_SEQ_BG_PARAM (W5_REG_BASE + 0x1A8) +#define W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR (W5_REG_BASE + 0x1AC) +#define W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR (W5_REG_BASE + 0x1B0) +#define W5_CMD_ENC_SEQ_VUI_HRD_PARAM (W5_REG_BASE + 0x180) +#define W5_CMD_ENC_SEQ_VUI_RBSP_ADDR (W5_REG_BASE + 0x1B8) +#define W5_CMD_ENC_SEQ_HRD_RBSP_ADDR (W5_REG_BASE + 0x1BC) + +/************************************************************************/ +/* ENCODER - ENC_SET_PARAM (CUSTOM_GOP) */ +/************************************************************************/ +#define W5_CMD_ENC_CUSTOM_GOP_PARAM (W5_REG_BASE + 0x11C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_0 (W5_REG_BASE + 0x120) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_1 (W5_REG_BASE + 0x124) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_2 (W5_REG_BASE + 0x128) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_3 (W5_REG_BASE + 0x12C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_4 (W5_REG_BASE + 0x130) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_5 (W5_REG_BASE + 0x134) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_6 (W5_REG_BASE + 0x138) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_7 (W5_REG_BASE + 0x13C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_8 (W5_REG_BASE + 0x140) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_9 (W5_REG_BASE + 0x144) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_10 (W5_REG_BASE + 0x148) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_11 (W5_REG_BASE + 0x14C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_12 (W5_REG_BASE + 0x150) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_13 (W5_REG_BASE + 0x154) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_14 (W5_REG_BASE + 0x158) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_15 (W5_REG_BASE + 0x15C) + +/************************************************************************/ +/* ENCODER - ENC_PIC */ +/************************************************************************/ +#define W5_CMD_ENC_BS_START_ADDR (W5_REG_BASE + 0x118) +#define W5_CMD_ENC_BS_SIZE (W5_REG_BASE + 0x11C) +#define W5_CMD_ENC_PIC_USE_SEC_AXI (W5_REG_BASE + 0x124) +#define W5_CMD_ENC_PIC_REPORT_PARAM (W5_REG_BASE + 0x128) + +#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_PARAM (W5_REG_BASE + 0x138) +#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_ADDR (W5_REG_BASE + 0x13C) +#define W5_CMD_ENC_PIC_SRC_PIC_IDX (W5_REG_BASE + 0x144) +#define W5_CMD_ENC_PIC_SRC_ADDR_Y (W5_REG_BASE + 0x148) +#define W5_CMD_ENC_PIC_SRC_ADDR_U (W5_REG_BASE + 0x14C) +#define W5_CMD_ENC_PIC_SRC_ADDR_V (W5_REG_BASE + 0x150) +#define W5_CMD_ENC_PIC_SRC_STRIDE (W5_REG_BASE + 0x154) +#define W5_CMD_ENC_PIC_SRC_FORMAT (W5_REG_BASE + 0x158) +#define W5_CMD_ENC_PIC_SRC_AXI_SEL (W5_REG_BASE + 0x160) +#define W5_CMD_ENC_PIC_CODE_OPTION (W5_REG_BASE + 0x164) +#define W5_CMD_ENC_PIC_PIC_PARAM (W5_REG_BASE + 0x168) +#define W5_CMD_ENC_PIC_LONGTERM_PIC (W5_REG_BASE + 0x16C) +#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_Y (W5_REG_BASE + 0x170) +#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_C (W5_REG_BASE + 0x174) +#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_Y (W5_REG_BASE + 0x178) +#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_C (W5_REG_BASE + 0x17C) +#define W5_CMD_ENC_PIC_CF50_Y_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x190) +#define W5_CMD_ENC_PIC_CF50_CB_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x194) +#define W5_CMD_ENC_PIC_CF50_CR_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x198) +#define W5_CMD_ENC_PIC_PREFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x180) +#define W5_CMD_ENC_PIC_PREFIX_SEI_INFO (W5_REG_BASE + 0x184) +#define W5_CMD_ENC_PIC_SUFFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x188) +#define W5_CMD_ENC_PIC_SUFFIX_SEI_INFO (W5_REG_BASE + 0x18c) + +/************************************************************************/ +/* ENCODER - QUERY (GET_RESULT) */ +/************************************************************************/ +#define W5_RET_ENC_NUM_REQUIRED_FB (W5_REG_BASE + 0x11C) +#define W5_RET_ENC_MIN_SRC_BUF_NUM (W5_REG_BASE + 0x120) +#define W5_RET_ENC_PIC_TYPE (W5_REG_BASE + 0x124) +/* + * #define W5_RET_ENC_PIC_POC (W5_REG_BASE + 0x128) + * => picture order count value of current encoded picture + */ +#define W5_RET_ENC_PIC_IDX (W5_REG_BASE + 0x12C) +/* + * #define W5_RET_ENC_PIC_SLICE_NUM (W5_REG_BASE + 0x130) + * reg_val & 0xffff = total independent slice segment number (16 bits) + * (reg_val >> 16) & 0xffff = total dependent slice segment number (16 bits) + * + * #define W5_RET_ENC_PIC_SKIP (W5_REG_BASE + 0x134) + * reg_val & 0xfe = picture skip flag (7 bits) + * + * #define W5_RET_ENC_PIC_NUM_INTRA (W5_REG_BASE + 0x138) + * => number of intra blocks in 8x8 (32 bits) + * + * #define W5_RET_ENC_PIC_NUM_MERGE (W5_REG_BASE + 0x13C) + * => number of merge blocks in 8x8 (32 bits) + * + * #define W5_RET_ENC_PIC_NUM_SKIP (W5_REG_BASE + 0x144) + * => number of skip blocks in 8x8 (32 bits) + * + * #define W5_RET_ENC_PIC_AVG_CTU_QP (W5_REG_BASE + 0x148) + * => Average CTU QP value (32 bits) + */ +#define W5_RET_ENC_PIC_BYTE (W5_REG_BASE + 0x14C) +/* + * #define W5_RET_ENC_GOP_PIC_IDX (W5_REG_BASE + 0x150) + * => picture index in group of pictures + */ +#define W5_RET_ENC_USED_SRC_IDX (W5_REG_BASE + 0x154) +/* + * #define W5_RET_ENC_PIC_NUM (W5_REG_BASE + 0x158) + * => encoded picture number + */ +#define W5_RET_ENC_VCL_NUT (W5_REG_BASE + 0x15C) +/* + * Only for H264: + * #define W5_RET_ENC_PIC_DIST_LOW (W5_REG_BASE + 0x164) + * => lower 32 bits of the sum of squared difference between source Y picture + * and reconstructed Y picture + * #define W5_RET_ENC_PIC_DIST_HIGH (W5_REG_BASE + 0x168) + * => upper 32 bits of the sum of squared difference between source Y picture + * and reconstructed Y picture + */ +#define W5_RET_ENC_PIC_MAX_LATENCY_PICS (W5_REG_BASE + 0x16C) + +#define W5_RET_ENC_HOST_CMD_TICK (W5_REG_BASE + 0x1B8) +/* + * #define W5_RET_ENC_PREPARE_START_TICK (W5_REG_BASE + 0x1BC) + * #define W5_RET_ENC_PREPARE_END_TICK (W5_REG_BASE + 0x1C0) + * => Start and end ticks for preparing slices of the picture + * #define W5_RET_ENC_PROCESSING_START_TICK (W5_REG_BASE + 0x1C4) + * #define W5_RET_ENC_PROCESSING_END_TICK (W5_REG_BASE + 0x1C8) + * => Start and end ticks for processing slices of the picture + * #define W5_RET_ENC_ENCODING_START_TICK (W5_REG_BASE + 0x1CC) + * => Start tick for encoding slices of the picture + */ +#define W5_RET_ENC_ENCODING_END_TICK (W5_REG_BASE + 0x1D0) + +#define W5_RET_ENC_WARN_INFO (W5_REG_BASE + 0x1D4) +#define W5_RET_ENC_ERR_INFO (W5_REG_BASE + 0x1D8) +#define W5_RET_ENC_ENCODING_SUCCESS (W5_REG_BASE + 0x1DC) + +/************************************************************************/ +/* ENCODER - QUERY (GET_BS_WR_PTR) */ +/************************************************************************/ +#define W5_RET_ENC_RD_PTR (W5_REG_BASE + 0x114) +#define W5_RET_ENC_WR_PTR (W5_REG_BASE + 0x118) +#define W5_CMD_ENC_REASON_SEL (W5_REG_BASE + 0x11C) + +/************************************************************************/ +/* ENCODER - QUERY (GET_BW_REPORT) */ +/************************************************************************/ +#define RET_QUERY_BW_PRP_AXI_READ (W5_REG_BASE + 0x118) +#define RET_QUERY_BW_PRP_AXI_WRITE (W5_REG_BASE + 0x11C) +#define RET_QUERY_BW_FBD_Y_AXI_READ (W5_REG_BASE + 0x120) +#define RET_QUERY_BW_FBC_Y_AXI_WRITE (W5_REG_BASE + 0x124) +#define RET_QUERY_BW_FBD_C_AXI_READ (W5_REG_BASE + 0x128) +#define RET_QUERY_BW_FBC_C_AXI_WRITE (W5_REG_BASE + 0x12C) +#define RET_QUERY_BW_PRI_AXI_READ (W5_REG_BASE + 0x130) +#define RET_QUERY_BW_PRI_AXI_WRITE (W5_REG_BASE + 0x134) +#define RET_QUERY_BW_SEC_AXI_READ (W5_REG_BASE + 0x138) +#define RET_QUERY_BW_SEC_AXI_WRITE (W5_REG_BASE + 0x13C) +#define RET_QUERY_BW_PROC_AXI_READ (W5_REG_BASE + 0x140) +#define RET_QUERY_BW_PROC_AXI_WRITE (W5_REG_BASE + 0x144) +#define RET_QUERY_BW_BWB_AXI_WRITE (W5_REG_BASE + 0x148) +#define W5_CMD_BW_OPTION (W5_REG_BASE + 0x14C) + +/************************************************************************/ +/* ENCODER - QUERY (GET_SRC_FLAG) */ +/************************************************************************/ +#define W5_RET_RELEASED_SRC_INSTANCE (W5_REG_BASE + 0x1EC) + +#define W5_ENC_PIC_SUB_FRAME_SYNC_IF (W5_REG_BASE + 0x0300) + +#endif /* __WAVE5_REGISTER_DEFINE_H__ */ diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c new file mode 100644 index 000000000000..3809f70bc0b4 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - low level access functions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include <linux/bug.h> +#include "wave5-vdi.h" +#include "wave5-vpu.h" +#include "wave5-regdefine.h" +#include <linux/delay.h> + +static int wave5_vdi_allocate_common_memory(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + if (!vpu_dev->common_mem.vaddr) { + int ret; + + vpu_dev->common_mem.size = SIZE_COMMON; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem); + if (ret) { + dev_err(dev, "unable to allocate common buffer\n"); + return ret; + } + } + + dev_dbg(dev, "[VDI] common_mem: daddr=%pad size=%zu vaddr=0x%p\n", + &vpu_dev->common_mem.daddr, vpu_dev->common_mem.size, vpu_dev->common_mem.vaddr); + + return 0; +} + +int wave5_vdi_init(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + int ret; + + ret = wave5_vdi_allocate_common_memory(dev); + if (ret < 0) { + dev_err(dev, "[VDI] failed to get vpu common buffer from driver\n"); + return ret; + } + + if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) { + WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code); + return -EOPNOTSUPP; + } + + /* if BIT processor is not running. */ + if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) { + int i; + + for (i = 0; i < 64; i++) + wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0); + } + + dev_dbg(dev, "[VDI] driver initialized successfully\n"); + + return 0; +} + +int wave5_vdi_release(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + vpu_dev->vdb_register = NULL; + wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem); + + return 0; +} + +void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data) +{ + writel(data, vpu_dev->vdb_register + addr); +} + +unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr) +{ + return readl(vpu_dev->vdb_register + addr); +} + +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb) +{ + if (!vb || !vb->vaddr) { + dev_err(vpu_dev->dev, "%s: unable to clear unmapped buffer\n", __func__); + return -EINVAL; + } + + memset(vb->vaddr, 0, vb->size); + return vb->size; +} + +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset, + u8 *data, size_t len) +{ + if (!vb || !vb->vaddr) { + dev_err(vpu_dev->dev, "%s: unable to write to unmapped buffer\n", __func__); + return -EINVAL; + } + + if (offset > vb->size || len > vb->size || offset + len > vb->size) { + dev_err(vpu_dev->dev, "%s: buffer too small\n", __func__); + return -ENOSPC; + } + + memcpy(vb->vaddr + offset, data, len); + + return len; +} + +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb) +{ + void *vaddr; + dma_addr_t daddr; + + if (!vb->size) { + dev_err(vpu_dev->dev, "%s: requested size==0\n", __func__); + return -EINVAL; + } + + vaddr = dma_alloc_coherent(vpu_dev->dev, vb->size, &daddr, GFP_KERNEL); + if (!vaddr) + return -ENOMEM; + vb->vaddr = vaddr; + vb->daddr = daddr; + + return 0; +} + +int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb) +{ + if (vb->size == 0) + return -EINVAL; + + if (!vb->vaddr) + dev_err(vpu_dev->dev, "%s: requested free of unmapped buffer\n", __func__); + else + dma_free_coherent(vpu_dev->dev, vb->size, vb->vaddr, vb->daddr); + + memset(vb, 0, sizeof(*vb)); + + return 0; +} + +int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count, + size_t size) +{ + struct vpu_buf vb_buf; + int i, ret = 0; + + vb_buf.size = size; + + for (i = 0; i < count; i++) { + if (array[i].size == size) + continue; + + if (array[i].size != 0) + wave5_vdi_free_dma_memory(vpu_dev, &array[i]); + + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_buf); + if (ret) + return -ENOMEM; + array[i] = vb_buf; + } + + for (i = count; i < MAX_REG_FRAME; i++) + wave5_vdi_free_dma_memory(vpu_dev, &array[i]); + + return 0; +} + +void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) +{ + struct vpu_buf *vb = &vpu_dev->sram_buf; + + if (!vpu_dev->sram_pool || !vpu_dev->sram_size) + return; + + if (!vb->vaddr) { + vb->size = vpu_dev->sram_size; + vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size, + &vb->daddr); + if (!vb->vaddr) + vb->size = 0; + } + + dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n", + __func__, &vb->daddr, vb->size, vb->vaddr); +} + +void wave5_vdi_free_sram(struct vpu_device *vpu_dev) +{ + struct vpu_buf *vb = &vpu_dev->sram_buf; + + if (!vb->size || !vb->vaddr) + return; + + if (vb->vaddr) + gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, + vb->size); + + memset(vb, 0, sizeof(*vb)); +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.h b/drivers/media/platform/chips-media/wave5/wave5-vdi.h new file mode 100644 index 000000000000..3984ef3f1f96 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - low level access functions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef _VDI_H_ +#define _VDI_H_ + +#include "wave5-vpuconfig.h" +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/device.h> + +/************************************************************************/ +/* COMMON REGISTERS */ +/************************************************************************/ +#define VPU_PRODUCT_CODE_REGISTER 0x1044 + +/* system register write */ +#define vpu_write_reg(VPU_INST, ADDR, DATA) wave5_vdi_write_register(VPU_INST, ADDR, DATA) +/* system register read */ +#define vpu_read_reg(CORE, ADDR) wave5_vdi_read_register(CORE, ADDR) + +struct vpu_buf { + size_t size; + dma_addr_t daddr; + void *vaddr; +}; + +int wave5_vdi_init(struct device *dev); +int wave5_vdi_release(struct device *dev); //this function may be called only at system off. + +#endif //#ifndef _VDI_H_ diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c new file mode 100644 index 000000000000..ef227af72348 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -0,0 +1,1932 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - decoder interface + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include "wave5-helper.h" + +#define VPU_DEC_DEV_NAME "C&M Wave5 VPU decoder" +#define VPU_DEC_DRV_NAME "wave5-dec" + +#define DEFAULT_SRC_SIZE(width, height) ({ \ + (width) * (height) / 8 * 3; \ +}) + +static const struct vpu_format dec_fmt_list[FMT_TYPES][MAX_FMTS] = { + [VPU_FMT_TYPE_CODEC] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, + .max_width = 8192, + .min_width = 32, + .max_height = 4320, + .min_height = 32, + }, + }, + [VPU_FMT_TYPE_RAW] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + } +}; + +/* + * Make sure that the state switch is allowed and add logging for debugging + * purposes + */ +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state) +{ + switch (state) { + case VPU_INST_STATE_NONE: + break; + case VPU_INST_STATE_OPEN: + if (inst->state != VPU_INST_STATE_NONE) + goto invalid_state_switch; + goto valid_state_switch; + case VPU_INST_STATE_INIT_SEQ: + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP) + goto invalid_state_switch; + goto valid_state_switch; + case VPU_INST_STATE_PIC_RUN: + if (inst->state != VPU_INST_STATE_INIT_SEQ) + goto invalid_state_switch; + goto valid_state_switch; + case VPU_INST_STATE_STOP: + goto valid_state_switch; + } +invalid_state_switch: + WARN(1, "Invalid state switch from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + return -EINVAL; +valid_state_switch: + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + inst->state = state; + return 0; +} + +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst) +{ + int ret; + + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0); + if (ret) { + /* + * To set the EOS flag, a command is sent to the firmware. + * That command may never return (timeout) or may report an error. + */ + dev_err(inst->dev->dev, + "Setting EOS for the bitstream, fail: %d\n", ret); + return ret; + } + return 0; +} + +static bool wave5_last_src_buffer_consumed(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct vpu_src_buffer *vpu_buf; + + if (!m2m_ctx->last_src_buf) + return false; + + vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf); + return vpu_buf->consumed; +} + +static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + size_t consumed_bytes = 0; + + if (rd_ptr >= inst->last_rd_ptr) { + consumed_bytes = rd_ptr - inst->last_rd_ptr; + } else { + size_t rd_offs = rd_ptr - inst->bitstream_vbuf.daddr; + size_t last_rd_offs = inst->last_rd_ptr - inst->bitstream_vbuf.daddr; + + consumed_bytes = rd_offs + (inst->bitstream_vbuf.size - last_rd_offs); + } + + inst->last_rd_ptr = rd_ptr; + consumed_bytes += inst->remaining_consumed_bytes; + + dev_dbg(inst->dev->dev, "%s: %zu bytes of bitstream was consumed", __func__, + consumed_bytes); + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + struct vb2_v4l2_buffer *src_buf = &buf->vb; + size_t src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + if (src_size > consumed_bytes) + break; + + dev_dbg(inst->dev->dev, "%s: removing src buffer %i", + __func__, src_buf->vb2_buf.index); + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + inst->timestamp = src_buf->vb2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + consumed_bytes -= src_size; + + /* Handle the case the last bitstream buffer has been picked */ + if (src_buf == m2m_ctx->last_src_buf) { + int ret; + + m2m_ctx->last_src_buf = NULL; + ret = wave5_vpu_dec_set_eos_on_firmware(inst); + if (ret) + dev_warn(inst->dev->dev, + "Setting EOS for the bitstream, fail: %d\n", ret); + break; + } + } + + inst->remaining_consumed_bytes = consumed_bytes; +} + +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width, + unsigned int height) +{ + switch (pix_mp->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height * 3 / 2; + break; + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height * 2; + break; + case V4L2_PIX_FMT_YUV420M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[1].sizeimage = width * height / 4; + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[2].sizeimage = width * height / 4; + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[1].sizeimage = width * height / 2; + break; + case V4L2_PIX_FMT_YUV422M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[1].sizeimage = width * height / 2; + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[2].sizeimage = width * height / 2; + break; + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[1].sizeimage = width * height; + break; + default: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_SRC_SIZE(width, height), + pix_mp->plane_fmt[0].sizeimage); + break; + } +} + +static int start_decode(struct vpu_instance *inst, u32 *fail_res) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret = 0; + + ret = wave5_vpu_dec_start_one_frame(inst, fail_res); + if (ret) { + struct vb2_v4l2_buffer *src_buf; + + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + if (src_buf) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + switch_state(inst, VPU_INST_STATE_STOP); + + dev_dbg(inst->dev->dev, "%s: pic run failed / finish job", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } + + return ret; +} + +static void flag_last_buffer_done(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *vb; + int i; + + lockdep_assert_held(&inst->state_spinlock); + + vb = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!vb) { + m2m_ctx->is_draining = true; + m2m_ctx->next_buf_last = true; + return; + } + + for (i = 0; i < vb->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&vb->vb2_buf, i, 0); + vb->field = V4L2_FIELD_NONE; + + v4l2_m2m_last_buffer_done(m2m_ctx, vb); +} + +static void send_eos_event(struct vpu_instance *inst) +{ + static const struct v4l2_event vpu_event_eos = { + .type = V4L2_EVENT_EOS + }; + + lockdep_assert_held(&inst->state_spinlock); + + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos); + inst->eos = false; +} + +static int handle_dynamic_resolution_change(struct vpu_instance *inst) +{ + struct v4l2_fh *fh = &inst->v4l2_fh; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + static const struct v4l2_event vpu_event_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info; + + lockdep_assert_held(&inst->state_spinlock); + + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad", __func__, &initial_info->rd_ptr); + + dev_dbg(inst->dev->dev, "%s: width: %u height: %u profile: %u | minbuffer: %u\n", + __func__, initial_info->pic_width, initial_info->pic_height, + initial_info->profile, initial_info->min_frame_buffer_count); + + inst->needs_reallocation = true; + inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1; + if (inst->fbc_buf_count != v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) { + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count); + } + + if (p_dec_info->initial_info_obtained) { + inst->conf_win.left = initial_info->pic_crop_rect.left; + inst->conf_win.top = initial_info->pic_crop_rect.top; + inst->conf_win.width = initial_info->pic_width - + initial_info->pic_crop_rect.left - initial_info->pic_crop_rect.right; + inst->conf_win.height = initial_info->pic_height - + initial_info->pic_crop_rect.top - initial_info->pic_crop_rect.bottom; + + wave5_update_pix_fmt(&inst->src_fmt, initial_info->pic_width, + initial_info->pic_height); + wave5_update_pix_fmt(&inst->dst_fmt, initial_info->pic_width, + initial_info->pic_height); + } + + v4l2_event_queue_fh(fh, &vpu_event_src_ch); + + return 0; +} + +static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct dec_output_info dec_info; + int ret; + struct vb2_v4l2_buffer *dec_buf = NULL; + struct vb2_v4l2_buffer *disp_buf = NULL; + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + struct queue_status_info q_status; + + dev_dbg(inst->dev->dev, "%s: Fetch output info from firmware.", __func__); + + ret = wave5_vpu_dec_get_output_info(inst, &dec_info); + if (ret) { + dev_warn(inst->dev->dev, "%s: could not get output info.", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + return; + } + + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &dec_info.rd_ptr, + &dec_info.wr_ptr); + wave5_handle_src_buffer(inst, dec_info.rd_ptr); + + dev_dbg(inst->dev->dev, "%s: dec_info dec_idx %i disp_idx %i", __func__, + dec_info.index_frame_decoded, dec_info.index_frame_display); + + if (!vb2_is_streaming(dst_vq)) { + dev_dbg(inst->dev->dev, "%s: capture is not streaming..", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + return; + } + + /* Remove decoded buffer from the ready queue now that it has been + * decoded. + */ + if (dec_info.index_frame_decoded >= 0) { + struct vb2_buffer *vb = vb2_get_buffer(dst_vq, + dec_info.index_frame_decoded); + if (vb) { + dec_buf = to_vb2_v4l2_buffer(vb); + dec_buf->vb2_buf.timestamp = inst->timestamp; + } else { + dev_warn(inst->dev->dev, "%s: invalid decoded frame index %i", + __func__, dec_info.index_frame_decoded); + } + } + + if (dec_info.index_frame_display >= 0) { + disp_buf = v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, dec_info.index_frame_display); + if (!disp_buf) + dev_warn(inst->dev->dev, "%s: invalid display frame index %i", + __func__, dec_info.index_frame_display); + } + + /* If there is anything to display, do that now */ + if (disp_buf) { + struct vpu_dst_buffer *dst_vpu_buf = wave5_to_vpu_dst_buf(disp_buf); + + if (inst->dst_fmt.num_planes == 1) { + vb2_set_plane_payload(&disp_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + } else if (inst->dst_fmt.num_planes == 2) { + vb2_set_plane_payload(&disp_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + vb2_set_plane_payload(&disp_buf->vb2_buf, 1, + inst->dst_fmt.plane_fmt[1].sizeimage); + } else if (inst->dst_fmt.num_planes == 3) { + vb2_set_plane_payload(&disp_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + vb2_set_plane_payload(&disp_buf->vb2_buf, 1, + inst->dst_fmt.plane_fmt[1].sizeimage); + vb2_set_plane_payload(&disp_buf->vb2_buf, 2, + inst->dst_fmt.plane_fmt[2].sizeimage); + } + + /* TODO implement interlace support */ + disp_buf->field = V4L2_FIELD_NONE; + dst_vpu_buf->display = true; + v4l2_m2m_buf_done(disp_buf, VB2_BUF_STATE_DONE); + + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u (payload %lu)\n", + __func__, dec_info.frame_cycle, + vb2_get_plane_payload(&disp_buf->vb2_buf, 0)); + } + + if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END || + dec_info.sequence_changed)) { + unsigned long flags; + + spin_lock_irqsave(&inst->state_spinlock, flags); + if (!v4l2_m2m_has_stopped(m2m_ctx)) { + switch_state(inst, VPU_INST_STATE_STOP); + + if (dec_info.sequence_changed) + handle_dynamic_resolution_change(inst); + else + send_eos_event(inst); + + flag_last_buffer_done(inst); + } + spin_unlock_irqrestore(&inst->state_spinlock, flags); + } + + /* + * During a resolution change and while draining, the firmware may flush + * the reorder queue regardless of having a matching decoding operation + * pending. Only terminate the job if there are no more IRQ coming. + */ + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + if (q_status.report_queue_count == 0 && + (q_status.instance_queue_count == 0 || dec_info.sequence_changed)) { + dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } +} + +static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card)); + + return 0; +} + +static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize) +{ + const struct vpu_format *vpu_fmt; + + if (fsize->index) + return -EINVAL; + + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = vpu_fmt->min_width; + fsize->stepwise.max_width = vpu_fmt->max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = vpu_fmt->min_height; + fsize->stepwise.max_height = vpu_fmt->max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int wave5_vpu_dec_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + const struct vpu_format *vpu_fmt; + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + const struct vpu_format *vpu_fmt; + int width, height; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u nm planes: %u colorspace: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + } else { + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt); + + width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = info->mem_planes; + } + + if (p_dec_info->initial_info_obtained) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + } + + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_dec_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field); + + ret = wave5_vpu_dec_try_fmt_cap(file, fh, f); + if (ret) + return ret; + + inst->dst_fmt.width = f->fmt.pix_mp.width; + inst->dst_fmt.height = f->fmt.pix_mp.height; + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->dst_fmt.field = f->fmt.pix_mp.field; + inst->dst_fmt.flags = f->fmt.pix_mp.flags; + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + inst->output_format = FORMAT_420; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + inst->output_format = FORMAT_420; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + inst->output_format = FORMAT_422; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + inst->output_format = FORMAT_422; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422P || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) { + inst->cbcr_interleave = false; + inst->nv21 = false; + inst->output_format = FORMAT_422; + } else { + inst->cbcr_interleave = false; + inst->nv21 = false; + inst->output_format = FORMAT_420; + } + + return 0; +} + +static int wave5_vpu_dec_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i; + + f->fmt.pix_mp.width = inst->dst_fmt.width; + f->fmt.pix_mp.height = inst->dst_fmt.height; + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.field = inst->dst_fmt.field; + f->fmt.pix_mp.flags = inst->dst_fmt.flags; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage; + } + + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index); + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height); + } else { + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = 1; + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + } + + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + + return 0; +} + +static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + ret = wave5_vpu_dec_try_fmt_out(file, fh, f); + if (ret) + return ret; + + inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type); + if (inst->std == STD_UNKNOWN) { + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n", + (char *)&f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + + inst->src_fmt.width = f->fmt.pix_mp.width; + inst->src_fmt.height = f->fmt.pix_mp.height; + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->src_fmt.field = f->fmt.pix_mp.field; + inst->src_fmt.flags = f->fmt.pix_mp.flags; + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->src_fmt.num_planes; i++) { + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + inst->colorspace = f->fmt.pix_mp.colorspace; + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + inst->quantization = f->fmt.pix_mp.quantization; + inst->xfer_func = f->fmt.pix_mp.xfer_func; + + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height); + + return 0; +} + +static int wave5_vpu_dec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = 0; + s->r.top = 0; + if (inst->state > VPU_INST_STATE_OPEN) { + s->r = inst->conf_win; + } else { + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave5_vpu_dec_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_COMPOSE w: %u h: %u\n", + s->r.width, s->r.height); + + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + + return 0; +} + +static int wave5_vpu_dec_stop(struct vpu_instance *inst) +{ + int ret = 0; + unsigned long flags; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + spin_lock_irqsave(&inst->state_spinlock, flags); + + if (m2m_ctx->is_draining) { + ret = -EBUSY; + goto unlock_and_return; + } + + if (inst->state != VPU_INST_STATE_NONE) { + /* + * Temporarily release the state_spinlock so that subsequent + * calls do not block on a mutex while inside this spinlock. + */ + spin_unlock_irqrestore(&inst->state_spinlock, flags); + ret = wave5_vpu_dec_set_eos_on_firmware(inst); + if (ret) + return ret; + + spin_lock_irqsave(&inst->state_spinlock, flags); + /* + * TODO eliminate this check by using a separate check for + * draining triggered by a resolution change. + */ + if (m2m_ctx->is_draining) { + ret = -EBUSY; + goto unlock_and_return; + } + } + + /* + * Used to remember the EOS state after the streamoff/on transition on + * the capture queue. + */ + inst->eos = true; + + if (m2m_ctx->has_stopped) + goto unlock_and_return; + + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); + m2m_ctx->is_draining = true; + + /* + * Deferred to device run in case it wasn't in the ring buffer + * yet. In other case, we have to send the EOS signal to the + * firmware so that any pending PIC_RUN ends without new + * bitstream buffer. + */ + if (m2m_ctx->last_src_buf) + goto unlock_and_return; + + if (inst->state == VPU_INST_STATE_NONE) { + send_eos_event(inst); + flag_last_buffer_done(inst); + } + +unlock_and_return: + spin_unlock_irqrestore(&inst->state_spinlock, flags); + return ret; +} + +static int wave5_vpu_dec_start(struct vpu_instance *inst) +{ + int ret = 0; + unsigned long flags; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + + spin_lock_irqsave(&inst->state_spinlock, flags); + + if (m2m_ctx->is_draining) { + ret = -EBUSY; + goto unlock_and_return; + } + + if (m2m_ctx->has_stopped) + m2m_ctx->has_stopped = false; + + vb2_clear_last_buffer_dequeued(dst_vq); + inst->eos = false; + +unlock_and_return: + spin_unlock_irqrestore(&inst->state_spinlock, flags); + return ret; +} + +static int wave5_vpu_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + + dev_dbg(inst->dev->dev, "decoder command: %u\n", dc->cmd); + + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); + if (ret) + return ret; + + switch (dc->cmd) { + case V4L2_DEC_CMD_STOP: + ret = wave5_vpu_dec_stop(inst); + /* Just in case we don't have anything to decode anymore */ + v4l2_m2m_try_schedule(m2m_ctx); + break; + case V4L2_DEC_CMD_START: + ret = wave5_vpu_dec_start(inst); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops wave5_vpu_dec_ioctl_ops = { + .vidioc_querycap = wave5_vpu_dec_querycap, + .vidioc_enum_framesizes = wave5_vpu_dec_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = wave5_vpu_dec_enum_fmt_cap, + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_dec_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_dec_g_fmt_cap, + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_dec_try_fmt_cap, + + .vidioc_enum_fmt_vid_out = wave5_vpu_dec_enum_fmt_out, + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_dec_s_fmt_out, + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out, + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_dec_try_fmt_out, + + .vidioc_g_selection = wave5_vpu_dec_g_selection, + .vidioc_s_selection = wave5_vpu_dec_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + /* + * Firmware does not support CREATE_BUFS for CAPTURE queue. Since + * there is no immediate use-case for supporting CREATE_BUFS on + * just the OUTPUT queue, disable CREATE_BUFS altogether. + */ + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, + .vidioc_decoder_cmd = wave5_vpu_dec_decoder_cmd, + + .vidioc_subscribe_event = wave5_vpu_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane inst_format = + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt; + + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__, + *num_buffers, *num_planes, q->type); + + *num_planes = inst_format.num_planes; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + sizes[0] = inst_format.plane_fmt[0].sizeimage; + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (*num_buffers < inst->fbc_buf_count) + *num_buffers = inst->fbc_buf_count; + + if (*num_planes == 1) { + if (inst->output_format == FORMAT_422) + sizes[0] = inst_format.width * inst_format.height * 2; + else + sizes[0] = inst_format.width * inst_format.height * 3 / 2; + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]); + } else if (*num_planes == 2) { + sizes[0] = inst_format.width * inst_format.height; + if (inst->output_format == FORMAT_422) + sizes[1] = inst_format.width * inst_format.height; + else + sizes[1] = inst_format.width * inst_format.height / 2; + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n", + __func__, sizes[0], sizes[1]); + } else if (*num_planes == 3) { + sizes[0] = inst_format.width * inst_format.height; + if (inst->output_format == FORMAT_422) { + sizes[1] = inst_format.width * inst_format.height / 2; + sizes[2] = inst_format.width * inst_format.height / 2; + } else { + sizes[1] = inst_format.width * inst_format.height / 4; + sizes[2] = inst_format.width * inst_format.height / 4; + } + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n", + __func__, sizes[0], sizes[1], sizes[2]); + } + } + + return 0; +} + +static int wave5_prepare_fb(struct vpu_instance *inst) +{ + int linear_num; + int non_linear_num; + int fb_stride = 0, fb_height = 0; + int luma_size, chroma_size; + int ret, i; + struct v4l2_m2m_buffer *buf, *n; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx); + non_linear_num = inst->fbc_buf_count; + + for (i = 0; i < non_linear_num; i++) { + struct frame_buffer *frame = &inst->frame_buf[i]; + struct vpu_buf *vframe = &inst->frame_vbuf[i]; + + fb_stride = inst->dst_fmt.width; + fb_height = ALIGN(inst->dst_fmt.height, 32); + luma_size = fb_stride * fb_height; + + chroma_size = ALIGN(fb_stride / 2, 16) * fb_height; + + if (vframe->size == (luma_size + chroma_size)) + continue; + + if (vframe->size) + wave5_vpu_dec_reset_framebuffer(inst, i); + + vframe->size = luma_size + chroma_size; + ret = wave5_vdi_allocate_dma_memory(inst->dev, vframe); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: Allocating FBC buf of size %zu, fail: %d\n", + __func__, vframe->size, ret); + return ret; + } + + frame->buf_y = vframe->daddr; + frame->buf_cb = vframe->daddr + luma_size; + frame->buf_cr = (dma_addr_t)-1; + frame->size = vframe->size; + frame->width = inst->src_fmt.width; + frame->stride = fb_stride; + frame->map_type = COMPRESSED_FRAME_MAP; + frame->update_fb_info = true; + } + /* In case the count has reduced, clean up leftover framebuffer memory */ + for (i = non_linear_num; i < MAX_REG_FRAME; i++) { + ret = wave5_vpu_dec_reset_framebuffer(inst, i); + if (ret) + break; + } + + for (i = 0; i < linear_num; i++) { + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + struct vb2_buffer *vb = vb2_get_buffer(dst_vq, i); + struct frame_buffer *frame = &inst->frame_buf[non_linear_num + i]; + dma_addr_t buf_addr_y = 0, buf_addr_cb = 0, buf_addr_cr = 0; + u32 buf_size = 0; + u32 fb_stride = inst->dst_fmt.width; + u32 luma_size = fb_stride * inst->dst_fmt.height; + u32 chroma_size; + + if (inst->output_format == FORMAT_422) + chroma_size = fb_stride * inst->dst_fmt.height / 2; + else + chroma_size = fb_stride * inst->dst_fmt.height / 4; + + if (inst->dst_fmt.num_planes == 1) { + buf_size = vb2_plane_size(vb, 0); + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0); + buf_addr_cb = buf_addr_y + luma_size; + buf_addr_cr = buf_addr_cb + chroma_size; + } else if (inst->dst_fmt.num_planes == 2) { + buf_size = vb2_plane_size(vb, 0) + + vb2_plane_size(vb, 1); + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0); + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1); + buf_addr_cr = buf_addr_cb + chroma_size; + } else if (inst->dst_fmt.num_planes == 3) { + buf_size = vb2_plane_size(vb, 0) + + vb2_plane_size(vb, 1) + + vb2_plane_size(vb, 2); + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0); + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1); + buf_addr_cr = vb2_dma_contig_plane_dma_addr(vb, 2); + } + + frame->buf_y = buf_addr_y; + frame->buf_cb = buf_addr_cb; + frame->buf_cr = buf_addr_cr; + frame->size = buf_size; + frame->width = inst->src_fmt.width; + frame->stride = fb_stride; + frame->map_type = LINEAR_FRAME_MAP; + frame->update_fb_info = true; + } + + ret = wave5_vpu_dec_register_frame_buffer_ex(inst, non_linear_num, linear_num, + fb_stride, inst->dst_fmt.height); + if (ret) { + dev_dbg(inst->dev->dev, "%s: vpu_dec_register_frame_buffer_ex fail: %d", + __func__, ret); + return ret; + } + + /* + * Mark all frame buffers as out of display, to avoid using them before + * the application have them queued. + */ + for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) { + ret = wave5_vpu_dec_set_disp_flag(inst, i); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: Setting display flag of buf index: %u, fail: %d\n", + __func__, i, ret); + } + } + + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) { + struct vb2_v4l2_buffer *vbuf = &buf->vb; + + ret = wave5_vpu_dec_clr_disp_flag(inst, vbuf->vb2_buf.index); + if (ret) + dev_dbg(inst->dev->dev, + "%s: Clearing display flag of buf index: %u, fail: %d\n", + __func__, i, ret); + } + + return 0; +} + +static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t buffer_size, + struct vpu_buf *ring_buffer, dma_addr_t wr_ptr) +{ + size_t size; + size_t offset = wr_ptr - ring_buffer->daddr; + int ret; + + if (wr_ptr + buffer_size > ring_buffer->daddr + ring_buffer->size) { + size = ring_buffer->daddr + ring_buffer->size - wr_ptr; + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, size); + if (ret < 0) + return ret; + + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, 0, (u8 *)buffer + size, + buffer_size - size); + if (ret < 0) + return ret; + } else { + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, + buffer_size); + if (ret < 0) + return ret; + } + + return 0; +} + +static int fill_ringbuffer(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + int ret; + + if (m2m_ctx->last_src_buf) { + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf); + + if (vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "last src buffer already written\n"); + return 0; + } + } + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + struct vb2_v4l2_buffer *vbuf = &buf->vb; + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf); + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf; + size_t src_size = vb2_get_plane_payload(&vbuf->vb2_buf, 0); + void *src_buf = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + dma_addr_t rd_ptr = 0; + dma_addr_t wr_ptr = 0; + size_t remain_size = 0; + + if (vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "already copied src buf (%u) to the ring buffer\n", + vbuf->vb2_buf.index); + continue; + } + + if (!src_buf) { + dev_dbg(inst->dev->dev, + "%s: Acquiring kernel pointer to src buf (%u), fail\n", + __func__, vbuf->vb2_buf.index); + break; + } + + ret = wave5_vpu_dec_get_bitstream_buffer(inst, &rd_ptr, &wr_ptr, &remain_size); + if (ret) { + /* Unable to acquire the mutex */ + dev_err(inst->dev->dev, "Getting the bitstream buffer, fail: %d\n", + ret); + return ret; + } + + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &rd_ptr, &wr_ptr); + + if (remain_size < src_size) { + dev_dbg(inst->dev->dev, + "%s: remaining size: %zu < source size: %zu for src buf (%u)\n", + __func__, remain_size, src_size, vbuf->vb2_buf.index); + break; + } + + ret = write_to_ringbuffer(inst, src_buf, src_size, ring_buffer, wr_ptr); + if (ret) { + dev_err(inst->dev->dev, "Write src buf (%u) to ring buffer, fail: %d\n", + vbuf->vb2_buf.index, ret); + return ret; + } + + ret = wave5_vpu_dec_update_bitstream_buffer(inst, src_size); + if (ret) { + dev_dbg(inst->dev->dev, + "update_bitstream_buffer fail: %d for src buf (%u)\n", + ret, vbuf->vb2_buf.index); + break; + } + + vpu_buf->consumed = true; + + /* Don't write buffers passed the last one while draining. */ + if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx, vbuf)) { + dev_dbg(inst->dev->dev, "last src buffer written to the ring buffer\n"); + break; + } + } + + return 0; +} + +static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb) +{ + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf); + + vpu_buf->consumed = false; + vbuf->sequence = inst->queued_src_buf_num++; + + v4l2_m2m_buf_queue(m2m_ctx, vbuf); +} + +static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + vbuf->sequence = inst->queued_dst_buf_num++; + + if (inst->state == VPU_INST_STATE_PIC_RUN) { + struct vpu_dst_buffer *vpu_buf = wave5_to_vpu_dst_buf(vbuf); + int ret; + + /* + * The buffer is already registered just clear the display flag + * to let the firmware know it can be used. + */ + vpu_buf->display = false; + ret = wave5_vpu_dec_clr_disp_flag(inst, vb->index); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: Clearing the display flag of buffer index: %u, fail: %d\n", + __func__, vb->index, ret); + } + } + + if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb2_set_plane_payload(vb, i, 0); + + vbuf->field = V4L2_FIELD_NONE; + + send_eos_event(inst); + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf); + } else { + v4l2_m2m_buf_queue(m2m_ctx, vbuf); + } +} + +static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n", + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + wave5_vpu_dec_buf_queue_src(vb); + else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + wave5_vpu_dec_buf_queue_dst(vb); +} + +static int wave5_vpu_dec_allocate_ring_buffer(struct vpu_instance *inst) +{ + int ret; + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf; + + ring_buffer->size = ALIGN(inst->src_fmt.plane_fmt[0].sizeimage, 1024) * 4; + ret = wave5_vdi_allocate_dma_memory(inst->dev, ring_buffer); + if (ret) { + dev_dbg(inst->dev->dev, "%s: allocate ring buffer of size %zu fail: %d\n", + __func__, ring_buffer->size, ret); + return ret; + } + + inst->last_rd_ptr = ring_buffer->daddr; + + return 0; +} + +static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret = 0; + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + + v4l2_m2m_update_start_streaming_state(m2m_ctx, q); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && inst->state == VPU_INST_STATE_NONE) { + struct dec_open_param open_param; + + memset(&open_param, 0, sizeof(struct dec_open_param)); + + ret = wave5_vpu_dec_allocate_ring_buffer(inst); + if (ret) + goto return_buffers; + + open_param.bitstream_buffer = inst->bitstream_vbuf.daddr; + open_param.bitstream_buffer_size = inst->bitstream_vbuf.size; + + ret = wave5_vpu_dec_open(inst, &open_param); + if (ret) { + dev_dbg(inst->dev->dev, "%s: decoder opening, fail: %d\n", + __func__, ret); + goto free_bitstream_vbuf; + } + + ret = switch_state(inst, VPU_INST_STATE_OPEN); + if (ret) + goto free_bitstream_vbuf; + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + struct dec_initial_info *initial_info = + &inst->codec_info->dec_info.initial_info; + + if (inst->state == VPU_INST_STATE_STOP) + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); + if (ret) + goto return_buffers; + + if (inst->state == VPU_INST_STATE_INIT_SEQ) { + if (initial_info->luma_bitdepth != 8) { + dev_info(inst->dev->dev, "%s: no support for %d bit depth", + __func__, initial_info->luma_bitdepth); + ret = -EINVAL; + goto return_buffers; + } + } + } + + return ret; + +free_bitstream_vbuf: + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf); +return_buffers: + wave5_return_bufs(q, VB2_BUF_STATE_QUEUED); + return ret; +} + +static int streamoff_output(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + int ret; + dma_addr_t new_rd_ptr; + + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) { + dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + ret = wave5_vpu_flush_instance(inst); + if (ret) + return ret; + + /* Reset the ring buffer information */ + new_rd_ptr = wave5_vpu_dec_get_rd_ptr(inst); + inst->last_rd_ptr = new_rd_ptr; + inst->codec_info->dec_info.stream_rd_ptr = new_rd_ptr; + inst->codec_info->dec_info.stream_wr_ptr = new_rd_ptr; + + if (v4l2_m2m_has_stopped(m2m_ctx)) + send_eos_event(inst); + + /* streamoff on output cancels any draining operation */ + inst->eos = false; + + return 0; +} + +static int streamoff_capture(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + unsigned int i; + int ret = 0; + + for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) { + ret = wave5_vpu_dec_set_disp_flag(inst, i); + if (ret) + dev_dbg(inst->dev->dev, + "%s: Setting display flag of buf index: %u, fail: %d\n", + __func__, i, ret); + } + + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) { + u32 plane; + + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + + for (plane = 0; plane < inst->dst_fmt.num_planes; plane++) + vb2_set_plane_payload(&buf->vb2_buf, plane, 0); + + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + if (inst->needs_reallocation) { + wave5_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, NULL); + inst->needs_reallocation = false; + } + + if (v4l2_m2m_has_stopped(m2m_ctx)) { + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); + if (ret) + return ret; + } + + return 0; +} + +static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + bool check_cmd = TRUE; + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + + while (check_cmd) { + struct queue_status_info q_status; + struct dec_output_info dec_output_info; + + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + + if (q_status.report_queue_count == 0) + break; + + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0) + break; + + if (wave5_vpu_dec_get_output_info(inst, &dec_output_info)) + dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n"); + } + + v4l2_m2m_update_stop_streaming_state(m2m_ctx, q); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + streamoff_output(q); + else + streamoff_capture(q); +} + +static const struct vb2_ops wave5_vpu_dec_vb2_ops = { + .queue_setup = wave5_vpu_dec_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = wave5_vpu_dec_buf_queue, + .start_streaming = wave5_vpu_dec_start_streaming, + .stop_streaming = wave5_vpu_dec_stop_streaming, +}; + +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, + struct v4l2_pix_format_mplane *dst_fmt) +{ + unsigned int dst_pix_fmt = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; + const struct v4l2_format_info *dst_fmt_info = v4l2_format_info(dst_pix_fmt); + + src_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->flags = 0; + src_fmt->num_planes = 1; + wave5_update_pix_fmt(src_fmt, 720, 480); + + dst_fmt->pixelformat = dst_pix_fmt; + dst_fmt->field = V4L2_FIELD_NONE; + dst_fmt->flags = 0; + dst_fmt->num_planes = dst_fmt_info->mem_planes; + wave5_update_pix_fmt(dst_fmt, 736, 480); +} + +static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_dec_vb2_ops); +} + +static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = { + .finish_process = wave5_vpu_dec_finish_decode, +}; + +static int initialize_sequence(struct vpu_instance *inst) +{ + struct dec_initial_info initial_info; + int ret = 0; + + memset(&initial_info, 0, sizeof(struct dec_initial_info)); + + ret = wave5_vpu_dec_issue_seq_init(inst); + if (ret) { + dev_dbg(inst->dev->dev, "%s: wave5_vpu_dec_issue_seq_init, fail: %d\n", + __func__, ret); + return ret; + } + + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0) + dev_dbg(inst->dev->dev, "%s: failed to call vpu_wait_interrupt()\n", __func__); + + ret = wave5_vpu_dec_complete_seq_init(inst, &initial_info); + if (ret) { + dev_dbg(inst->dev->dev, "%s: vpu_dec_complete_seq_init, fail: %d, reason: %u\n", + __func__, ret, initial_info.seq_init_err_reason); + wave5_handle_src_buffer(inst, initial_info.rd_ptr); + return ret; + } + + handle_dynamic_resolution_change(inst); + + return 0; +} + +static bool wave5_is_draining_or_eos(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + lockdep_assert_held(&inst->state_spinlock); + return m2m_ctx->is_draining || inst->eos; +} + +static void wave5_vpu_dec_device_run(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct queue_status_info q_status; + u32 fail_res = 0; + int ret = 0; + + dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__); + + ret = fill_ringbuffer(inst); + if (ret) { + dev_warn(inst->dev->dev, "Filling ring buffer failed\n"); + goto finish_job_and_return; + } + + switch (inst->state) { + case VPU_INST_STATE_OPEN: + ret = initialize_sequence(inst); + if (ret) { + unsigned long flags; + + spin_lock_irqsave(&inst->state_spinlock, flags); + if (wave5_is_draining_or_eos(inst) && + wave5_last_src_buffer_consumed(m2m_ctx)) { + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + + switch_state(inst, VPU_INST_STATE_STOP); + + if (vb2_is_streaming(dst_vq)) + send_eos_event(inst); + else + handle_dynamic_resolution_change(inst); + + flag_last_buffer_done(inst); + } + spin_unlock_irqrestore(&inst->state_spinlock, flags); + } else { + switch_state(inst, VPU_INST_STATE_INIT_SEQ); + } + + break; + + case VPU_INST_STATE_INIT_SEQ: + /* + * Do this early, preparing the fb can trigger an IRQ before + * we had a chance to switch, which leads to an invalid state + * change. + */ + switch_state(inst, VPU_INST_STATE_PIC_RUN); + + /* + * During DRC, the picture decoding remains pending, so just leave the job + * active until this decode operation completes. + */ + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + + /* + * The sequence must be analyzed first to calculate the proper + * size of the auxiliary buffers. + */ + ret = wave5_prepare_fb(inst); + if (ret) { + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret); + switch_state(inst, VPU_INST_STATE_STOP); + break; + } + + if (q_status.instance_queue_count) { + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + return; + } + + fallthrough; + case VPU_INST_STATE_PIC_RUN: + ret = start_decode(inst, &fail_res); + if (ret) { + dev_err(inst->dev->dev, + "Frame decoding on m2m context (%p), fail: %d (result: %d)\n", + m2m_ctx, ret, fail_res); + break; + } + /* Return so that we leave this job active */ + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + return; + default: + WARN(1, "Execution of a job in state %s illegal.\n", state_to_str(inst->state)); + break; + } + +finish_job_and_return: + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); +} + +static void wave5_vpu_dec_job_abort(void *priv) +{ + struct vpu_instance *inst = priv; + int ret; + + ret = switch_state(inst, VPU_INST_STATE_STOP); + if (ret) + return; + + ret = wave5_vpu_dec_set_eos_on_firmware(inst); + if (ret) + dev_warn(inst->dev->dev, + "Setting EOS for the bitstream, fail: %d\n", ret); +} + +static int wave5_vpu_dec_job_ready(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&inst->state_spinlock, flags); + + switch (inst->state) { + case VPU_INST_STATE_NONE: + dev_dbg(inst->dev->dev, "Decoder must be open to start queueing M2M jobs!\n"); + break; + case VPU_INST_STATE_OPEN: + if (wave5_is_draining_or_eos(inst) || !v4l2_m2m_has_stopped(m2m_ctx) || + v4l2_m2m_num_src_bufs_ready(m2m_ctx) > 0) { + ret = 1; + break; + } + + dev_dbg(inst->dev->dev, + "Decoder must be draining or >= 1 OUTPUT queue buffer must be queued!\n"); + break; + case VPU_INST_STATE_INIT_SEQ: + case VPU_INST_STATE_PIC_RUN: + if (!m2m_ctx->cap_q_ctx.q.streaming) { + dev_dbg(inst->dev->dev, "CAPTURE queue must be streaming to queue jobs!\n"); + break; + } else if (v4l2_m2m_num_dst_bufs_ready(m2m_ctx) < (inst->fbc_buf_count - 1)) { + dev_dbg(inst->dev->dev, + "No capture buffer ready to decode!\n"); + break; + } else if (!wave5_is_draining_or_eos(inst) && + !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + dev_dbg(inst->dev->dev, + "No bitstream data to decode!\n"); + break; + } + ret = 1; + break; + case VPU_INST_STATE_STOP: + dev_dbg(inst->dev->dev, "Decoder is stopped, not running.\n"); + break; + } + + spin_unlock_irqrestore(&inst->state_spinlock, flags); + + return ret; +} + +static const struct v4l2_m2m_ops wave5_vpu_dec_m2m_ops = { + .device_run = wave5_vpu_dec_device_run, + .job_abort = wave5_vpu_dec_job_abort, + .job_ready = wave5_vpu_dec_job_ready, +}; + +static int wave5_vpu_open_dec(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct vpu_device *dev = video_drvdata(filp); + struct vpu_instance *inst = NULL; + struct v4l2_m2m_ctx *m2m_ctx; + int ret = 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->dev = dev; + inst->type = VPU_INST_TYPE_DEC; + inst->ops = &wave5_vpu_dec_inst_ops; + + spin_lock_init(&inst->state_spinlock); + + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL); + if (!inst->codec_info) + return -ENOMEM; + + v4l2_fh_init(&inst->v4l2_fh, vdev); + filp->private_data = &inst->v4l2_fh; + v4l2_fh_add(&inst->v4l2_fh); + + INIT_LIST_HEAD(&inst->list); + list_add_tail(&inst->list, &dev->instances); + + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev; + inst->v4l2_fh.m2m_ctx = + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_dec_queue_init); + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); + goto cleanup_inst; + } + m2m_ctx = inst->v4l2_fh.m2m_ctx; + + v4l2_m2m_set_src_buffered(m2m_ctx, true); + v4l2_m2m_set_dst_buffered(m2m_ctx, true); + /* + * We use the M2M job queue to ensure synchronization of steps where + * needed, as IOCTLs can occur at anytime and we need to run commands on + * the firmware in a specified order. + * In order to initialize the sequence on the firmware within an M2M + * job, the M2M framework needs to be able to queue jobs before + * the CAPTURE queue has been started, because we need the results of the + * initialization to properly prepare the CAPTURE queue with the correct + * amount of buffers. + * By setting ignore_cap_streaming to true the m2m framework will call + * job_ready as soon as the OUTPUT queue is streaming, instead of + * waiting until both the CAPTURE and OUTPUT queues are streaming. + */ + m2m_ctx->ignore_cap_streaming = true; + + v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10); + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, NULL, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1); + + if (inst->v4l2_ctrl_hdl.error) { + ret = -ENODEV; + goto cleanup_inst; + } + + inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl; + v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl); + + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + inst->colorspace = V4L2_COLORSPACE_REC709; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->quantization = V4L2_QUANTIZATION_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + init_completion(&inst->irq_done); + + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL); + if (inst->id < 0) { + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id); + ret = inst->id; + goto cleanup_inst; + } + + wave5_vdi_allocate_sram(inst->dev); + + return 0; + +cleanup_inst: + wave5_cleanup_instance(inst); + return ret; +} + +static int wave5_vpu_dec_release(struct file *filp) +{ + return wave5_vpu_release_device(filp, wave5_vpu_dec_close, "decoder"); +} + +static const struct v4l2_file_operations wave5_vpu_dec_fops = { + .owner = THIS_MODULE, + .open = wave5_vpu_open_dec, + .release = wave5_vpu_dec_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +int wave5_vpu_dec_register_device(struct vpu_device *dev) +{ + struct video_device *vdev_dec; + int ret; + + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL); + if (!vdev_dec) + return -ENOMEM; + + dev->v4l2_m2m_dec_dev = v4l2_m2m_init(&wave5_vpu_dec_m2m_ops); + if (IS_ERR(dev->v4l2_m2m_dec_dev)) { + ret = PTR_ERR(dev->v4l2_m2m_dec_dev); + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret); + return -EINVAL; + } + + dev->video_dev_dec = vdev_dec; + + strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec->name)); + vdev_dec->fops = &wave5_vpu_dec_fops; + vdev_dec->ioctl_ops = &wave5_vpu_dec_ioctl_ops; + vdev_dec->release = video_device_release_empty; + vdev_dec->v4l2_dev = &dev->v4l2_dev; + vdev_dec->vfl_dir = VFL_DIR_M2M; + vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vdev_dec->lock = &dev->dev_lock; + + ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + video_set_drvdata(vdev_dec, dev); + + return 0; +} + +void wave5_vpu_dec_unregister_device(struct vpu_device *dev) +{ + video_unregister_device(dev->video_dev_dec); + if (dev->v4l2_m2m_dec_dev) + v4l2_m2m_release(dev->v4l2_m2m_dec_dev); +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c new file mode 100644 index 000000000000..f29cfa3af94a --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -0,0 +1,1794 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - encoder interface + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include "wave5-helper.h" + +#define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder" +#define VPU_ENC_DRV_NAME "wave5-enc" + +static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = { + [VPU_FMT_TYPE_CODEC] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + }, + [VPU_FMT_TYPE_RAW] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + } +}; + +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state) +{ + switch (state) { + case VPU_INST_STATE_NONE: + goto invalid_state_switch; + case VPU_INST_STATE_OPEN: + if (inst->state != VPU_INST_STATE_NONE) + goto invalid_state_switch; + break; + case VPU_INST_STATE_INIT_SEQ: + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP) + goto invalid_state_switch; + break; + case VPU_INST_STATE_PIC_RUN: + if (inst->state != VPU_INST_STATE_INIT_SEQ) + goto invalid_state_switch; + break; + case VPU_INST_STATE_STOP: + break; + }; + + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + inst->state = state; + return 0; + +invalid_state_switch: + WARN(1, "Invalid state switch from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + return -EINVAL; +} + +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width, + unsigned int height) +{ + switch (pix_mp->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height * 3 / 2; + break; + case V4L2_PIX_FMT_YUV420M: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 4; + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[2].sizeimage = round_up(width, 32) * height / 4; + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 2; + break; + default: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage = width * height / 8 * 3; + break; + } +} + +static int start_encode(struct vpu_instance *inst, u32 *fail_res) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct frame_buffer frame_buf; + struct enc_param pic_param; + u32 stride = ALIGN(inst->dst_fmt.width, 32); + u32 luma_size = (stride * inst->dst_fmt.height); + u32 chroma_size = ((stride / 2) * (inst->dst_fmt.height / 2)); + + memset(&pic_param, 0, sizeof(struct enc_param)); + memset(&frame_buf, 0, sizeof(struct frame_buffer)); + + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx); + if (!dst_buf) { + dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__); + return -EAGAIN; + } + + pic_param.pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + pic_param.pic_stream_buffer_size = + vb2_plane_size(&dst_buf->vb2_buf, 0); + + src_buf = v4l2_m2m_next_src_buf(m2m_ctx); + if (!src_buf) { + dev_dbg(inst->dev->dev, "%s: No source buffer found\n", __func__); + if (m2m_ctx->is_draining) + pic_param.src_end_flag = 1; + else + return -EAGAIN; + } else { + if (inst->src_fmt.num_planes == 1) { + frame_buf.buf_y = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + frame_buf.buf_cb = frame_buf.buf_y + luma_size; + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } else if (inst->src_fmt.num_planes == 2) { + frame_buf.buf_y = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + frame_buf.buf_cb = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1); + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } else if (inst->src_fmt.num_planes == 3) { + frame_buf.buf_y = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + frame_buf.buf_cb = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1); + frame_buf.buf_cr = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 2); + } + frame_buf.stride = stride; + pic_param.src_idx = src_buf->vb2_buf.index; + } + + pic_param.source_frame = &frame_buf; + pic_param.code_option.implicit_header_encode = 1; + pic_param.code_option.encode_aud = inst->encode_aud; + ret = wave5_vpu_enc_start_one_frame(inst, &pic_param, fail_res); + if (ret) { + if (*fail_res == WAVE5_SYSERR_QUEUEING_FAIL) + return -EINVAL; + + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame fail: %d\n", + __func__, ret); + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + if (!src_buf) { + dev_dbg(inst->dev->dev, + "%s: Removing src buf failed, the queue is empty\n", + __func__); + return -EINVAL; + } + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!dst_buf) { + dev_dbg(inst->dev->dev, + "%s: Removing dst buf failed, the queue is empty\n", + __func__); + return -EINVAL; + } + switch_state(inst, VPU_INST_STATE_STOP); + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } else { + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n", + __func__); + /* + * Remove the source buffer from the ready-queue now and finish + * it in the videobuf2 framework once the index is returned by the + * firmware in finish_encode + */ + if (src_buf) + v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index); + } + + return 0; +} + +static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + struct enc_output_info enc_output_info; + struct vb2_v4l2_buffer *src_buf = NULL; + struct vb2_v4l2_buffer *dst_buf = NULL; + + ret = wave5_vpu_enc_get_output_info(inst, &enc_output_info); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: vpu_enc_get_output_info fail: %d reason: %u | info: %u\n", + __func__, ret, enc_output_info.error_reason, enc_output_info.warn_info); + return; + } + + dev_dbg(inst->dev->dev, + "%s: pic_type %i recon_idx %i src_idx %i pic_byte %u pts %llu\n", + __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index, + enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts); + + /* + * The source buffer will not be found in the ready-queue as it has been + * dropped after sending of the encode firmware command, locate it in + * the videobuf2 queue directly + */ + if (enc_output_info.enc_src_idx >= 0) { + struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx), + enc_output_info.enc_src_idx); + if (vb->state != VB2_BUF_STATE_ACTIVE) + dev_warn(inst->dev->dev, + "%s: encoded buffer (%d) was not in ready queue %i.", + __func__, enc_output_info.enc_src_idx, vb->state); + else + src_buf = to_vb2_v4l2_buffer(vb); + + if (src_buf) { + inst->timestamp = src_buf->vb2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } else { + dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n", + __func__, enc_output_info.enc_src_idx); + } + } + + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (enc_output_info.recon_frame_index == RECON_IDX_FLAG_ENC_END) { + static const struct v4l2_event vpu_event_eos = { + .type = V4L2_EVENT_EOS + }; + + if (!WARN_ON(!dst_buf)) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + dst_buf->field = V4L2_FIELD_NONE; + v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf); + } + + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos); + + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } else { + if (!dst_buf) { + dev_warn(inst->dev->dev, "No bitstream buffer."); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + return; + } + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_output_info.bitstream_size); + + dst_buf->vb2_buf.timestamp = inst->timestamp; + dst_buf->field = V4L2_FIELD_NONE; + if (enc_output_info.pic_type == PIC_TYPE_I) { + if (enc_output_info.enc_vcl_nut == 19 || + enc_output_info.enc_vcl_nut == 20) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + } else if (enc_output_info.pic_type == PIC_TYPE_P) { + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + } else if (enc_output_info.pic_type == PIC_TYPE_B) { + dst_buf->flags |= V4L2_BUF_FLAG_BFRAME; + } + + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u\n", + __func__, enc_output_info.frame_cycle); + + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } +} + +static int wave5_vpu_enc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card)); + + return 0; +} + +static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize) +{ + const struct vpu_format *vpu_fmt; + + if (fsize->index) + return -EINVAL; + + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = vpu_fmt->min_width; + fsize->stepwise.max_width = vpu_fmt->max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = vpu_fmt->min_height; + fsize->stepwise.max_height = vpu_fmt->max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index); + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height); + } else { + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = 1; + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + } + + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_enc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + ret = wave5_vpu_enc_try_fmt_cap(file, fh, f); + if (ret) + return ret; + + inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type); + if (inst->std == STD_UNKNOWN) { + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n", + (char *)&f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + + inst->dst_fmt.width = f->fmt.pix_mp.width; + inst->dst_fmt.height = f->fmt.pix_mp.height; + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->dst_fmt.field = f->fmt.pix_mp.field; + inst->dst_fmt.flags = f->fmt.pix_mp.flags; + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + return 0; +} + +static int wave5_vpu_enc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i; + + f->fmt.pix_mp.width = inst->dst_fmt.width; + f->fmt.pix_mp.height = inst->dst_fmt.height; + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.field = inst->dst_fmt.field; + f->fmt.pix_mp.flags = inst->dst_fmt.flags; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage; + } + + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index); + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) { + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height); + } else { + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt); + + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = info->mem_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + } + + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + + return 0; +} + +static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + ret = wave5_vpu_enc_try_fmt_out(file, fh, f); + if (ret) + return ret; + + inst->src_fmt.width = f->fmt.pix_mp.width; + inst->src_fmt.height = f->fmt.pix_mp.height; + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->src_fmt.field = f->fmt.pix_mp.field; + inst->src_fmt.flags = f->fmt.pix_mp.flags; + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->src_fmt.num_planes; i++) { + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + } else { + inst->cbcr_interleave = false; + inst->nv21 = false; + } + + inst->colorspace = f->fmt.pix_mp.colorspace; + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + inst->quantization = f->fmt.pix_mp.quantization; + inst->xfer_func = f->fmt.pix_mp.xfer_func; + + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height); + + return 0; +} + +static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n", + __func__, s->r.width, s->r.height); + + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + + return 0; +} + +static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); + if (ret) + return ret; + + if (!wave5_vpu_both_queues_are_streaming(inst)) + return 0; + + switch (ec->cmd) { + case V4L2_ENC_CMD_STOP: + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + return 0; + + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); + m2m_ctx->is_draining = true; + break; + case V4L2_ENC_CMD_START: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave5_vpu_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe.numerator = 1; + a->parm.output.timeperframe.denominator = inst->frame_rate; + + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n", + __func__, a->parm.output.timeperframe.numerator, + a->parm.output.timeperframe.denominator); + + return 0; +} + +static int wave5_vpu_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + if (a->parm.output.timeperframe.denominator && a->parm.output.timeperframe.numerator) { + inst->frame_rate = a->parm.output.timeperframe.denominator / + a->parm.output.timeperframe.numerator; + } else { + a->parm.output.timeperframe.numerator = 1; + a->parm.output.timeperframe.denominator = inst->frame_rate; + } + + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n", + __func__, a->parm.output.timeperframe.numerator, + a->parm.output.timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops wave5_vpu_enc_ioctl_ops = { + .vidioc_querycap = wave5_vpu_enc_querycap, + .vidioc_enum_framesizes = wave5_vpu_enc_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = wave5_vpu_enc_enum_fmt_cap, + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_enc_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_enc_g_fmt_cap, + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_enc_try_fmt_cap, + + .vidioc_enum_fmt_vid_out = wave5_vpu_enc_enum_fmt_out, + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_enc_s_fmt_out, + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out, + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_enc_try_fmt_out, + + .vidioc_g_selection = wave5_vpu_enc_g_selection, + .vidioc_s_selection = wave5_vpu_enc_s_selection, + + .vidioc_g_parm = wave5_vpu_enc_g_parm, + .vidioc_s_parm = wave5_vpu_enc_s_parm, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = wave5_vpu_enc_encoder_cmd, + + .vidioc_subscribe_event = wave5_vpu_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_instance *inst = wave5_ctrl_to_vpu_inst(ctrl); + + dev_dbg(inst->dev->dev, "%s: name: %s | value: %d\n", __func__, ctrl->name, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: + inst->encode_aud = ctrl->val; + break; + case V4L2_CID_HFLIP: + inst->mirror_direction |= (ctrl->val << 1); + break; + case V4L2_CID_VFLIP: + inst->mirror_direction |= ctrl->val; + break; + case V4L2_CID_ROTATE: + inst->rot_angle = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + inst->vbv_buf_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: + inst->rc_mode = 0; + break; + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: + inst->rc_mode = 1; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + inst->bit_rate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + inst->enc_param.avc_idr_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + inst->enc_param.independ_slice_mode = ctrl->val; + inst->enc_param.avc_slice_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + inst->enc_param.independ_slice_mode_arg = ctrl->val; + inst->enc_param.avc_slice_arg = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + inst->rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + inst->enc_param.mb_level_rc_enable = ctrl->val; + inst->enc_param.cu_level_rc_enable = ctrl->val; + inst->enc_param.hvs_qp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + inst->enc_param.profile = HEVC_PROFILE_MAIN; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + inst->enc_param.profile = HEVC_PROFILE_STILLPICTURE; + inst->enc_param.en_still_picture = 1; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + inst->enc_param.profile = HEVC_PROFILE_MAIN10; + inst->bit_depth = 10; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + inst->enc_param.level = 10 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + inst->enc_param.level = 20 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + inst->enc_param.level = 21 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + inst->enc_param.level = 30 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + inst->enc_param.level = 31 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + inst->enc_param.level = 40 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + inst->enc_param.level = 41 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + inst->enc_param.level = 50 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + inst->enc_param.level = 51 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + inst->enc_param.level = 52 * 3; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + inst->enc_param.min_qp_i = ctrl->val; + inst->enc_param.min_qp_p = ctrl->val; + inst->enc_param.min_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + inst->enc_param.max_qp_i = ctrl->val; + inst->enc_param.max_qp_p = ctrl->val; + inst->enc_param.max_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + inst->enc_param.intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED: + inst->enc_param.disable_deblk = 1; + inst->enc_param.sao_enable = 0; + inst->enc_param.lf_cross_slice_boundary_enable = 0; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED: + inst->enc_param.disable_deblk = 0; + inst->enc_param.sao_enable = 1; + inst->enc_param.lf_cross_slice_boundary_enable = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + inst->enc_param.disable_deblk = 0; + inst->enc_param.sao_enable = 1; + inst->enc_param.lf_cross_slice_boundary_enable = 0; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: + inst->enc_param.beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: + inst->enc_param.tc_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE: + inst->enc_param.decoding_refresh_type = 0; + break; + case V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA: + inst->enc_param.decoding_refresh_type = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR: + inst->enc_param.decoding_refresh_type = 2; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: + inst->enc_param.intra_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU: + inst->enc_param.lossless_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: + inst->enc_param.const_intra_pred_flag = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT: + inst->enc_param.wpp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: + inst->enc_param.strong_intra_smooth_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: + inst->enc_param.max_num_merge = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: + inst->enc_param.tmvp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + inst->enc_param.profile = H264_PROFILE_BP; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + inst->enc_param.profile = H264_PROFILE_MP; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + inst->enc_param.profile = H264_PROFILE_EXTENDED; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + inst->enc_param.profile = H264_PROFILE_HP; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10: + inst->enc_param.profile = H264_PROFILE_HIGH10; + inst->bit_depth = 10; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422: + inst->enc_param.profile = H264_PROFILE_HIGH422; + inst->bit_depth = 10; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE: + inst->enc_param.profile = H264_PROFILE_HIGH444; + inst->bit_depth = 10; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + inst->enc_param.level = 10; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + inst->enc_param.level = 9; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + inst->enc_param.level = 11; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + inst->enc_param.level = 12; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + inst->enc_param.level = 13; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + inst->enc_param.level = 20; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + inst->enc_param.level = 21; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + inst->enc_param.level = 22; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + inst->enc_param.level = 30; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + inst->enc_param.level = 31; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + inst->enc_param.level = 32; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + inst->enc_param.level = 40; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + inst->enc_param.level = 41; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + inst->enc_param.level = 42; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + inst->enc_param.level = 50; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + inst->enc_param.level = 51; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + inst->enc_param.min_qp_i = ctrl->val; + inst->enc_param.min_qp_p = ctrl->val; + inst->enc_param.min_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + inst->enc_param.max_qp_i = ctrl->val; + inst->enc_param.max_qp_p = ctrl->val; + inst->enc_param.max_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + inst->enc_param.intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED: + inst->enc_param.disable_deblk = 1; + inst->enc_param.lf_cross_slice_boundary_enable = 1; + break; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: + inst->enc_param.disable_deblk = 0; + inst->enc_param.lf_cross_slice_boundary_enable = 1; + break; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + inst->enc_param.disable_deblk = 0; + inst->enc_param.lf_cross_slice_boundary_enable = 0; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + inst->enc_param.beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + inst->enc_param.tc_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + inst->enc_param.transform8x8_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + inst->enc_param.const_intra_pred_flag = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + inst->enc_param.chroma_cb_qp_offset = ctrl->val; + inst->enc_param.chroma_cr_qp_offset = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + inst->enc_param.intra_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + inst->enc_param.entropy_coding_mode = ctrl->val; + break; + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops wave5_vpu_enc_ctrl_ops = { + .s_ctrl = wave5_vpu_enc_s_ctrl, +}; + +static int wave5_vpu_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane inst_format = + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt; + unsigned int i; + + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__, + *num_buffers, *num_planes, q->type); + + if (*num_planes) { + if (inst_format.num_planes != *num_planes) + return -EINVAL; + + for (i = 0; i < *num_planes; i++) { + if (sizes[i] < inst_format.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = inst_format.num_planes; + for (i = 0; i < *num_planes; i++) { + sizes[i] = inst_format.plane_fmt[i].sizeimage; + dev_dbg(inst->dev->dev, "%s: size[%u]: %u\n", __func__, i, sizes[i]); + } + } + + dev_dbg(inst->dev->dev, "%s: size: %u\n", __func__, sizes[0]); + + return 0; +} + +static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n", + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf->sequence = inst->queued_src_buf_num++; + else + vbuf->sequence = inst->queued_dst_buf_num++; + + v4l2_m2m_buf_queue(m2m_ctx, vbuf); +} + +static void wave5_set_enc_openparam(struct enc_open_param *open_param, + struct vpu_instance *inst) +{ + struct enc_wave_param input = inst->enc_param; + u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64; + u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16; + + open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE; + open_param->wave_param.hvs_qp_scale = 2; + open_param->wave_param.hvs_max_delta_qp = 10; + open_param->wave_param.skip_intra_trans = 1; + open_param->wave_param.intra_nx_n_enable = 1; + open_param->wave_param.nr_intra_weight_y = 7; + open_param->wave_param.nr_intra_weight_cb = 7; + open_param->wave_param.nr_intra_weight_cr = 7; + open_param->wave_param.nr_inter_weight_y = 4; + open_param->wave_param.nr_inter_weight_cb = 4; + open_param->wave_param.nr_inter_weight_cr = 4; + open_param->wave_param.rdo_skip = 1; + open_param->wave_param.lambda_scaling_enable = 1; + + open_param->line_buf_int_en = true; + open_param->pic_width = inst->dst_fmt.width; + open_param->pic_height = inst->dst_fmt.height; + open_param->frame_rate_info = inst->frame_rate; + open_param->rc_enable = inst->rc_enable; + if (inst->rc_enable) { + open_param->wave_param.initial_rc_qp = -1; + open_param->wave_param.rc_weight_param = 16; + open_param->wave_param.rc_weight_buf = 128; + } + open_param->wave_param.mb_level_rc_enable = input.mb_level_rc_enable; + open_param->wave_param.cu_level_rc_enable = input.cu_level_rc_enable; + open_param->wave_param.hvs_qp_enable = input.hvs_qp_enable; + open_param->bit_rate = inst->bit_rate; + open_param->vbv_buffer_size = inst->vbv_buf_size; + if (inst->rc_mode == 0) + open_param->vbv_buffer_size = 3000; + open_param->wave_param.profile = input.profile; + open_param->wave_param.en_still_picture = input.en_still_picture; + open_param->wave_param.level = input.level; + open_param->wave_param.internal_bit_depth = inst->bit_depth; + open_param->wave_param.intra_qp = input.intra_qp; + open_param->wave_param.min_qp_i = input.min_qp_i; + open_param->wave_param.max_qp_i = input.max_qp_i; + open_param->wave_param.min_qp_p = input.min_qp_p; + open_param->wave_param.max_qp_p = input.max_qp_p; + open_param->wave_param.min_qp_b = input.min_qp_b; + open_param->wave_param.max_qp_b = input.max_qp_b; + open_param->wave_param.disable_deblk = input.disable_deblk; + open_param->wave_param.lf_cross_slice_boundary_enable = + input.lf_cross_slice_boundary_enable; + open_param->wave_param.tc_offset_div2 = input.tc_offset_div2; + open_param->wave_param.beta_offset_div2 = input.beta_offset_div2; + open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type; + open_param->wave_param.intra_period = input.intra_period; + if (inst->std == W_HEVC_ENC) { + if (input.intra_period == 0) { + open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR; + open_param->wave_param.intra_period = input.avc_idr_period; + } + } else { + open_param->wave_param.avc_idr_period = input.avc_idr_period; + } + open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; + open_param->wave_param.lossless_enable = input.lossless_enable; + open_param->wave_param.const_intra_pred_flag = input.const_intra_pred_flag; + open_param->wave_param.wpp_enable = input.wpp_enable; + open_param->wave_param.strong_intra_smooth_enable = input.strong_intra_smooth_enable; + open_param->wave_param.max_num_merge = input.max_num_merge; + open_param->wave_param.tmvp_enable = input.tmvp_enable; + open_param->wave_param.transform8x8_enable = input.transform8x8_enable; + open_param->wave_param.chroma_cb_qp_offset = input.chroma_cb_qp_offset; + open_param->wave_param.chroma_cr_qp_offset = input.chroma_cr_qp_offset; + open_param->wave_param.independ_slice_mode = input.independ_slice_mode; + open_param->wave_param.independ_slice_mode_arg = input.independ_slice_mode_arg; + open_param->wave_param.avc_slice_mode = input.avc_slice_mode; + open_param->wave_param.avc_slice_arg = input.avc_slice_arg; + open_param->wave_param.intra_mb_refresh_mode = input.intra_mb_refresh_mode; + if (input.intra_mb_refresh_mode != REFRESH_MB_MODE_NONE) { + if (num_mb_row >= input.intra_mb_refresh_arg) + open_param->wave_param.intra_mb_refresh_arg = + num_mb_row / input.intra_mb_refresh_arg; + else + open_param->wave_param.intra_mb_refresh_arg = num_mb_row; + } + open_param->wave_param.intra_refresh_mode = input.intra_refresh_mode; + if (input.intra_refresh_mode != 0) { + if (num_ctu_row >= input.intra_refresh_arg) + open_param->wave_param.intra_refresh_arg = + num_ctu_row / input.intra_refresh_arg; + else + open_param->wave_param.intra_refresh_arg = num_ctu_row; + } +} + +static int initialize_sequence(struct vpu_instance *inst) +{ + struct enc_initial_info initial_info; + struct v4l2_ctrl *ctrl; + int ret; + + ret = wave5_vpu_enc_issue_seq_init(inst); + if (ret) { + dev_err(inst->dev->dev, "%s: wave5_vpu_enc_issue_seq_init, fail: %d\n", + __func__, ret); + return ret; + } + + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) { + dev_err(inst->dev->dev, "%s: wave5_vpu_wait_interrupt failed\n", __func__); + return -EINVAL; + } + + ret = wave5_vpu_enc_complete_seq_init(inst, &initial_info); + if (ret) + return ret; + + dev_dbg(inst->dev->dev, "%s: min_frame_buffer: %u | min_source_buffer: %u\n", + __func__, initial_info.min_frame_buffer_count, + initial_info.min_src_frame_count); + inst->min_src_buf_count = initial_info.min_src_frame_count + + COMMAND_QUEUE_DEPTH; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, inst->min_src_buf_count); + + inst->fbc_buf_count = initial_info.min_frame_buffer_count; + + return 0; +} + +static int prepare_fb(struct vpu_instance *inst) +{ + u32 fb_stride = ALIGN(inst->dst_fmt.width, 32); + u32 fb_height = ALIGN(inst->dst_fmt.height, 32); + int i, ret = 0; + + for (i = 0; i < inst->fbc_buf_count; i++) { + u32 luma_size = fb_stride * fb_height; + u32 chroma_size = ALIGN(fb_stride / 2, 16) * fb_height; + + inst->frame_vbuf[i].size = luma_size + chroma_size; + ret = wave5_vdi_allocate_dma_memory(inst->dev, &inst->frame_vbuf[i]); + if (ret < 0) { + dev_err(inst->dev->dev, "%s: failed to allocate FBC buffer %zu\n", + __func__, inst->frame_vbuf[i].size); + goto free_buffers; + } + + inst->frame_buf[i].buf_y = inst->frame_vbuf[i].daddr; + inst->frame_buf[i].buf_cb = (dma_addr_t)-1; + inst->frame_buf[i].buf_cr = (dma_addr_t)-1; + inst->frame_buf[i].update_fb_info = true; + inst->frame_buf[i].size = inst->frame_vbuf[i].size; + } + + ret = wave5_vpu_enc_register_frame_buffer(inst, inst->fbc_buf_count, fb_stride, + fb_height, COMPRESSED_FRAME_MAP); + if (ret) { + dev_err(inst->dev->dev, + "%s: wave5_vpu_enc_register_frame_buffer, fail: %d\n", + __func__, ret); + goto free_buffers; + } + + return 0; +free_buffers: + for (i = 0; i < inst->fbc_buf_count; i++) + wave5_vpu_dec_reset_framebuffer(inst, i); + return ret; +} + +static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret = 0; + + v4l2_m2m_update_start_streaming_state(m2m_ctx, q); + + if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct enc_open_param open_param; + + memset(&open_param, 0, sizeof(struct enc_open_param)); + + wave5_set_enc_openparam(&open_param, inst); + + ret = wave5_vpu_enc_open(inst, &open_param); + if (ret) { + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_open, fail: %d\n", + __func__, ret); + goto return_buffers; + } + + if (inst->mirror_direction) { + wave5_vpu_enc_give_command(inst, ENABLE_MIRRORING, NULL); + wave5_vpu_enc_give_command(inst, SET_MIRROR_DIRECTION, + &inst->mirror_direction); + } + if (inst->rot_angle) { + wave5_vpu_enc_give_command(inst, ENABLE_ROTATION, NULL); + wave5_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, &inst->rot_angle); + } + + ret = switch_state(inst, VPU_INST_STATE_OPEN); + if (ret) + goto return_buffers; + } + if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) { + ret = initialize_sequence(inst); + if (ret) { + dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret); + goto return_buffers; + } + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); + if (ret) + goto return_buffers; + /* + * The sequence must be analyzed first to calculate the proper + * size of the auxiliary buffers. + */ + ret = prepare_fb(inst); + if (ret) { + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret); + goto return_buffers; + } + + ret = switch_state(inst, VPU_INST_STATE_PIC_RUN); + } + if (ret) + goto return_buffers; + + return 0; +return_buffers: + wave5_return_bufs(q, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void streamoff_output(struct vpu_instance *inst, struct vb2_queue *q) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) { + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } +} + +static void streamoff_capture(struct vpu_instance *inst, struct vb2_queue *q) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) { + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + vb2_set_plane_payload(&buf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + v4l2_m2m_clear_state(m2m_ctx); +} + +static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + bool check_cmd = true; + + /* + * Note that we don't need m2m_ctx->next_buf_last for this driver, so we + * don't call v4l2_m2m_update_stop_streaming_state(). + */ + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + + if (wave5_vpu_both_queues_are_streaming(inst)) + switch_state(inst, VPU_INST_STATE_STOP); + + while (check_cmd) { + struct queue_status_info q_status; + struct enc_output_info enc_output_info; + + wave5_vpu_enc_give_command(inst, ENC_GET_QUEUE_STATUS, &q_status); + + if (q_status.report_queue_count == 0) + break; + + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) + break; + + if (wave5_vpu_enc_get_output_info(inst, &enc_output_info)) + dev_dbg(inst->dev->dev, "Getting encoding results from fw, fail\n"); + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + streamoff_output(inst, q); + else + streamoff_capture(inst, q); +} + +static const struct vb2_ops wave5_vpu_enc_vb2_ops = { + .queue_setup = wave5_vpu_enc_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = wave5_vpu_enc_buf_queue, + .start_streaming = wave5_vpu_enc_start_streaming, + .stop_streaming = wave5_vpu_enc_stop_streaming, +}; + +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, + struct v4l2_pix_format_mplane *dst_fmt) +{ + unsigned int src_pix_fmt = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; + const struct v4l2_format_info *src_fmt_info = v4l2_format_info(src_pix_fmt); + + src_fmt->pixelformat = src_pix_fmt; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->flags = 0; + src_fmt->num_planes = src_fmt_info->mem_planes; + wave5_update_pix_fmt(src_fmt, 416, 240); + + dst_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt; + dst_fmt->field = V4L2_FIELD_NONE; + dst_fmt->flags = 0; + dst_fmt->num_planes = 1; + wave5_update_pix_fmt(dst_fmt, 416, 240); +} + +static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_enc_vb2_ops); +} + +static const struct vpu_instance_ops wave5_vpu_enc_inst_ops = { + .finish_process = wave5_vpu_enc_finish_encode, +}; + +static void wave5_vpu_enc_device_run(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + u32 fail_res = 0; + int ret = 0; + + switch (inst->state) { + case VPU_INST_STATE_PIC_RUN: + ret = start_encode(inst, &fail_res); + if (ret) { + if (ret == -EINVAL) + dev_err(inst->dev->dev, + "Frame encoding on m2m context (%p), fail: %d (res: %d)\n", + m2m_ctx, ret, fail_res); + else if (ret == -EAGAIN) + dev_dbg(inst->dev->dev, "Missing buffers for encode, try again\n"); + break; + } + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + return; + default: + WARN(1, "Execution of a job in state %s is invalid.\n", + state_to_str(inst->state)); + break; + } + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); +} + +static int wave5_vpu_enc_job_ready(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + switch (inst->state) { + case VPU_INST_STATE_NONE: + dev_dbg(inst->dev->dev, "Encoder must be open to start queueing M2M jobs!\n"); + return false; + case VPU_INST_STATE_PIC_RUN: + if (m2m_ctx->is_draining || v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + dev_dbg(inst->dev->dev, "Encoder ready for a job, state: %s\n", + state_to_str(inst->state)); + return true; + } + fallthrough; + default: + dev_dbg(inst->dev->dev, + "Encoder not ready for a job, state: %s, %s draining, %d src bufs ready\n", + state_to_str(inst->state), m2m_ctx->is_draining ? "is" : "is not", + v4l2_m2m_num_src_bufs_ready(m2m_ctx)); + break; + } + return false; +} + +static const struct v4l2_m2m_ops wave5_vpu_enc_m2m_ops = { + .device_run = wave5_vpu_enc_device_run, + .job_ready = wave5_vpu_enc_job_ready, +}; + +static int wave5_vpu_open_enc(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct vpu_device *dev = video_drvdata(filp); + struct vpu_instance *inst = NULL; + struct v4l2_ctrl_handler *v4l2_ctrl_hdl; + int ret = 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl; + + inst->dev = dev; + inst->type = VPU_INST_TYPE_ENC; + inst->ops = &wave5_vpu_enc_inst_ops; + + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL); + if (!inst->codec_info) + return -ENOMEM; + + v4l2_fh_init(&inst->v4l2_fh, vdev); + filp->private_data = &inst->v4l2_fh; + v4l2_fh_add(&inst->v4l2_fh); + + INIT_LIST_HEAD(&inst->list); + list_add_tail(&inst->list, &dev->instances); + + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev; + inst->v4l2_fh.m2m_ctx = + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_enc_queue_init); + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); + goto cleanup_inst; + } + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true); + + v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + 0, 63, 1, 8); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + 0, 63, 1, 51); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + 0, 63, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2, + -6, 6, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE, + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, 0, + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD, + 0, 2047, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1, + 1, 2, 1, 2); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION, + 0, 1, 1, 1); + + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE, 0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0, + V4L2_MPEG_VIDEO_H264_LEVEL_1_0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 63, 1, 8); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 63, 1, 51); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 63, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, + -12, 12, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + 0, 2047, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_AU_DELIMITER, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_ROTATE, + 0, 270, 90, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_SIZE, + 10, 3000, 1, 1000); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, 700000000, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, 2047, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + 0, 0xFFFF, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1); + + if (v4l2_ctrl_hdl->error) { + ret = -ENODEV; + goto cleanup_inst; + } + + inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl; + v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); + + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + inst->colorspace = V4L2_COLORSPACE_REC709; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->quantization = V4L2_QUANTIZATION_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; + inst->frame_rate = 30; + + init_completion(&inst->irq_done); + + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL); + if (inst->id < 0) { + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id); + ret = inst->id; + goto cleanup_inst; + } + + wave5_vdi_allocate_sram(inst->dev); + + return 0; + +cleanup_inst: + wave5_cleanup_instance(inst); + return ret; +} + +static int wave5_vpu_enc_release(struct file *filp) +{ + return wave5_vpu_release_device(filp, wave5_vpu_enc_close, "encoder"); +} + +static const struct v4l2_file_operations wave5_vpu_enc_fops = { + .owner = THIS_MODULE, + .open = wave5_vpu_open_enc, + .release = wave5_vpu_enc_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +int wave5_vpu_enc_register_device(struct vpu_device *dev) +{ + struct video_device *vdev_enc; + int ret; + + vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_enc), GFP_KERNEL); + if (!vdev_enc) + return -ENOMEM; + + dev->v4l2_m2m_enc_dev = v4l2_m2m_init(&wave5_vpu_enc_m2m_ops); + if (IS_ERR(dev->v4l2_m2m_enc_dev)) { + ret = PTR_ERR(dev->v4l2_m2m_enc_dev); + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret); + return -EINVAL; + } + + dev->video_dev_enc = vdev_enc; + + strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc->name)); + vdev_enc->fops = &wave5_vpu_enc_fops; + vdev_enc->ioctl_ops = &wave5_vpu_enc_ioctl_ops; + vdev_enc->release = video_device_release_empty; + vdev_enc->v4l2_dev = &dev->v4l2_dev; + vdev_enc->vfl_dir = VFL_DIR_M2M; + vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vdev_enc->lock = &dev->dev_lock; + + ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + video_set_drvdata(vdev_enc, dev); + + return 0; +} + +void wave5_vpu_enc_unregister_device(struct vpu_device *dev) +{ + video_unregister_device(dev->video_dev_enc); + if (dev->v4l2_m2m_enc_dev) + v4l2_m2m_release(dev->v4l2_m2m_enc_dev); +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c new file mode 100644 index 000000000000..bfe4caa79cc9 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - platform driver + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include "wave5-vpu.h" +#include "wave5-regdefine.h" +#include "wave5-vpuconfig.h" +#include "wave5.h" + +#define VPU_PLATFORM_DEVICE_NAME "vdec" +#define VPU_CLK_NAME "vcodec" + +#define WAVE5_IS_ENC BIT(0) +#define WAVE5_IS_DEC BIT(1) + +struct wave5_match_data { + int flags; + const char *fw_name; +}; + +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout) +{ + int ret; + + ret = wait_for_completion_timeout(&inst->irq_done, + msecs_to_jiffies(timeout)); + if (!ret) + return -ETIMEDOUT; + + reinit_completion(&inst->irq_done); + + return 0; +} + +static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) +{ + u32 seq_done; + u32 cmd_done; + u32 irq_reason; + struct vpu_instance *inst; + struct vpu_device *dev = dev_id; + + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) { + irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON); + wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason); + wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1); + + list_for_each_entry(inst, &dev->instances, list) { + seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO); + cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST); + + if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) || + irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) { + if (seq_done & BIT(inst->id)) { + seq_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, + seq_done); + complete(&inst->irq_done); + } + } + + if (irq_reason & BIT(INT_WAVE5_DEC_PIC) || + irq_reason & BIT(INT_WAVE5_ENC_PIC)) { + if (cmd_done & BIT(inst->id)) { + cmd_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, + cmd_done); + inst->ops->finish_process(inst); + } + } + + wave5_vpu_clear_interrupt(inst, irq_reason); + } + } + + return IRQ_HANDLED; +} + +static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name, + u32 *revision) +{ + const struct firmware *fw; + int ret; + unsigned int product_id; + + ret = request_firmware(&fw, fw_name, dev); + if (ret) { + dev_err(dev, "request_firmware, fail: %d\n", ret); + return ret; + } + + ret = wave5_vpu_init_with_bitcode(dev, (u8 *)fw->data, fw->size); + if (ret) { + dev_err(dev, "vpu_init_with_bitcode, fail: %d\n", ret); + release_firmware(fw); + return ret; + } + release_firmware(fw); + + ret = wave5_vpu_get_version_info(dev, revision, &product_id); + if (ret) { + dev_err(dev, "vpu_get_version_info fail: %d\n", ret); + return ret; + } + + dev_dbg(dev, "%s: enum product_id: %08x, fw revision: %u\n", + __func__, product_id, *revision); + + return 0; +} + +static int wave5_vpu_probe(struct platform_device *pdev) +{ + int ret; + struct vpu_device *dev; + const struct wave5_match_data *match_data; + u32 fw_revision; + + match_data = device_get_match_data(&pdev->dev); + if (!match_data) { + dev_err(&pdev->dev, "missing device match data\n"); + return -EINVAL; + } + + /* physical addresses limited to 32 bits */ + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set DMA mask: %d\n", ret); + return ret; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->vdb_register = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->vdb_register)) + return PTR_ERR(dev->vdb_register); + ida_init(&dev->inst_ida); + + mutex_init(&dev->dev_lock); + mutex_init(&dev->hw_lock); + dev_set_drvdata(&pdev->dev, dev); + dev->dev = &pdev->dev; + + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks); + + /* continue without clock, assume externally managed */ + if (ret < 0) { + dev_warn(&pdev->dev, "Getting clocks, fail: %d\n", ret); + ret = 0; + } + dev->num_clks = ret; + + ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks); + if (ret) { + dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, "sram-size", + &dev->sram_size); + if (ret) { + dev_warn(&pdev->dev, "sram-size not found\n"); + dev->sram_size = 0; + } + + dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + if (!dev->sram_pool) + dev_warn(&pdev->dev, "sram node not found\n"); + + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER); + ret = wave5_vdi_init(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "wave5_vdi_init, fail: %d\n", ret); + goto err_clk_dis; + } + dev->product = wave5_vpu_get_product_id(dev); + + INIT_LIST_HEAD(&dev->instances); + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret); + goto err_vdi_release; + } + + if (match_data->flags & WAVE5_IS_DEC) { + ret = wave5_vpu_dec_register_device(dev); + if (ret) { + dev_err(&pdev->dev, "wave5_vpu_dec_register_device, fail: %d\n", ret); + goto err_v4l2_unregister; + } + } + if (match_data->flags & WAVE5_IS_ENC) { + ret = wave5_vpu_enc_register_device(dev); + if (ret) { + dev_err(&pdev->dev, "wave5_vpu_enc_register_device, fail: %d\n", ret); + goto err_dec_unreg; + } + } + + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + ret = -ENXIO; + goto err_enc_unreg; + } + + ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL, + wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev); + if (ret) { + dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret); + goto err_enc_unreg; + } + + ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision); + if (ret) { + dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret); + goto err_enc_unreg; + } + + dev_info(&pdev->dev, "Added wave5 driver with caps: %s %s\n", + (match_data->flags & WAVE5_IS_ENC) ? "'ENCODE'" : "", + (match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : ""); + dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code); + dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision); + return 0; + +err_enc_unreg: + if (match_data->flags & WAVE5_IS_ENC) + wave5_vpu_enc_unregister_device(dev); +err_dec_unreg: + if (match_data->flags & WAVE5_IS_DEC) + wave5_vpu_dec_unregister_device(dev); +err_v4l2_unregister: + v4l2_device_unregister(&dev->v4l2_dev); +err_vdi_release: + wave5_vdi_release(&pdev->dev); +err_clk_dis: + clk_bulk_disable_unprepare(dev->num_clks, dev->clks); + + return ret; +} + +static int wave5_vpu_remove(struct platform_device *pdev) +{ + struct vpu_device *dev = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&dev->dev_lock); + mutex_destroy(&dev->hw_lock); + clk_bulk_disable_unprepare(dev->num_clks, dev->clks); + wave5_vpu_enc_unregister_device(dev); + wave5_vpu_dec_unregister_device(dev); + v4l2_device_unregister(&dev->v4l2_dev); + wave5_vdi_release(&pdev->dev); + ida_destroy(&dev->inst_ida); + + return 0; +} + +static const struct wave5_match_data ti_wave521c_data = { + .flags = WAVE5_IS_ENC | WAVE5_IS_DEC, + .fw_name = "cnm/wave521c_k3_codec_fw.bin", +}; + +static const struct of_device_id wave5_dt_ids[] = { + { .compatible = "ti,k3-j721s2-wave521c", .data = &ti_wave521c_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, wave5_dt_ids); + +static struct platform_driver wave5_vpu_driver = { + .driver = { + .name = VPU_PLATFORM_DEVICE_NAME, + .of_match_table = of_match_ptr(wave5_dt_ids), + }, + .probe = wave5_vpu_probe, + .remove = wave5_vpu_remove, +}; + +module_platform_driver(wave5_vpu_driver); +MODULE_DESCRIPTION("chips&media VPU V4L2 driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h new file mode 100644 index 000000000000..32b7fd3730b5 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - basic types + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ +#ifndef __VPU_DRV_H__ +#define __VPU_DRV_H__ + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> +#include "wave5-vpuconfig.h" +#include "wave5-vpuapi.h" + +#define VPU_BUF_SYNC_TO_DEVICE 0 +#define VPU_BUF_SYNC_FROM_DEVICE 1 + +struct vpu_src_buffer { + struct v4l2_m2m_buffer v4l2_m2m_buf; + struct list_head list; + bool consumed; +}; + +struct vpu_dst_buffer { + struct v4l2_m2m_buffer v4l2_m2m_buf; + bool display; +}; + +enum vpu_fmt_type { + VPU_FMT_TYPE_CODEC = 0, + VPU_FMT_TYPE_RAW = 1 +}; + +struct vpu_format { + unsigned int v4l2_pix_fmt; + unsigned int max_width; + unsigned int min_width; + unsigned int max_height; + unsigned int min_height; +}; + +static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh) +{ + return container_of(vfh, struct vpu_instance, v4l2_fh); +} + +static inline struct vpu_instance *wave5_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl) +{ + return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl); +} + +static inline struct vpu_src_buffer *wave5_to_vpu_src_buf(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct vpu_src_buffer, v4l2_m2m_buf.vb); +} + +static inline struct vpu_dst_buffer *wave5_to_vpu_dst_buf(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct vpu_dst_buffer, v4l2_m2m_buf.vb); +} + +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout); + +int wave5_vpu_dec_register_device(struct vpu_device *dev); +void wave5_vpu_dec_unregister_device(struct vpu_device *dev); +int wave5_vpu_enc_register_device(struct vpu_device *dev); +void wave5_vpu_enc_unregister_device(struct vpu_device *dev); +static inline bool wave5_vpu_both_queues_are_streaming(struct vpu_instance *inst) +{ + struct vb2_queue *vq_cap = + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + struct vb2_queue *vq_out = + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out); +} + +#endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c new file mode 100644 index 000000000000..1a3efb638dde --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - helper functions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include <linux/bug.h> +#include "wave5-vpuapi.h" +#include "wave5-regdefine.h" +#include "wave5.h" + +#define DECODE_ALL_TEMPORAL_LAYERS 0 +#define DECODE_ALL_SPATIAL_LAYERS 0 + +static int wave5_initialize_vpu(struct device *dev, u8 *code, size_t size) +{ + int ret; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (wave5_vpu_is_init(vpu_dev)) { + wave5_vpu_re_init(dev, (void *)code, size); + ret = -EBUSY; + goto err_out; + } + + ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT); + if (ret) + goto err_out; + + ret = wave5_vpu_init(dev, (void *)code, size); + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + return ret; +} + +int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size) +{ + if (!bitcode || size == 0) + return -EINVAL; + + return wave5_initialize_vpu(dev, bitcode, size); +} + +int wave5_vpu_flush_instance(struct vpu_instance *inst) +{ + int ret = 0; + int retry = 0; + + ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (ret) + return ret; + do { + /* + * Repeat the FLUSH command until the firmware reports that the + * VPU isn't running anymore + */ + ret = wave5_vpu_hw_flush_instance(inst); + if (ret < 0 && ret != -EBUSY) { + dev_warn(inst->dev->dev, "Flush of %s instance with id: %d fail: %d\n", + inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id, + ret); + mutex_unlock(&inst->dev->hw_lock); + return ret; + } + if (ret == -EBUSY && retry++ >= MAX_FIRMWARE_CALL_RETRY) { + dev_warn(inst->dev->dev, "Flush of %s instance with id: %d timed out!\n", + inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id); + mutex_unlock(&inst->dev->hw_lock); + return -ETIMEDOUT; + } + } while (ret != 0); + mutex_unlock(&inst->dev->hw_lock); + + return ret; +} + +int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id) +{ + int ret; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (!wave5_vpu_is_init(vpu_dev)) { + ret = -EINVAL; + goto err_out; + } + + if (product_id) + *product_id = vpu_dev->product; + ret = wave5_vpu_get_version(vpu_dev, revision); + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + return ret; +} + +static int wave5_check_dec_open_param(struct vpu_instance *inst, struct dec_open_param *param) +{ + if (inst->id >= MAX_NUM_INSTANCE) { + dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n", + inst->id, MAX_NUM_INSTANCE); + return -EOPNOTSUPP; + } + + if (param->bitstream_buffer % 8) { + dev_err(inst->dev->dev, + "Bitstream buffer must be aligned to a multiple of 8\n"); + return -EINVAL; + } + + if (param->bitstream_buffer_size % 1024 || + param->bitstream_buffer_size < MIN_BITSTREAM_BUFFER_SIZE) { + dev_err(inst->dev->dev, + "Bitstream buffer size must be aligned to a multiple of 1024 and have a minimum size of %d\n", + MIN_BITSTREAM_BUFFER_SIZE); + return -EINVAL; + } + + return 0; +} + +int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param) +{ + struct dec_info *p_dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + dma_addr_t buffer_addr; + size_t buffer_size; + + ret = wave5_check_dec_open_param(inst, open_param); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (!wave5_vpu_is_init(vpu_dev)) { + mutex_unlock(&vpu_dev->hw_lock); + return -ENODEV; + } + + p_dec_info = &inst->codec_info->dec_info; + memcpy(&p_dec_info->open_param, open_param, sizeof(struct dec_open_param)); + + buffer_addr = open_param->bitstream_buffer; + buffer_size = open_param->bitstream_buffer_size; + p_dec_info->stream_wr_ptr = buffer_addr; + p_dec_info->stream_rd_ptr = buffer_addr; + p_dec_info->stream_buf_start_addr = buffer_addr; + p_dec_info->stream_buf_size = buffer_size; + p_dec_info->stream_buf_end_addr = buffer_addr + buffer_size; + p_dec_info->reorder_enable = TRUE; + p_dec_info->temp_id_select_mode = TEMPORAL_ID_MODE_ABSOLUTE; + p_dec_info->target_temp_id = DECODE_ALL_TEMPORAL_LAYERS; + p_dec_info->target_spatial_id = DECODE_ALL_SPATIAL_LAYERS; + + ret = wave5_vpu_build_up_dec_param(inst, open_param); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +static int reset_auxiliary_buffers(struct vpu_instance *inst, unsigned int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + if (index >= MAX_REG_FRAME) + return 1; + + if (p_dec_info->vb_mv[index].size == 0 && p_dec_info->vb_fbc_y_tbl[index].size == 0 && + p_dec_info->vb_fbc_c_tbl[index].size == 0) + return 1; + + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[index]); + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[index]); + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[index]); + + return 0; +} + +int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + int retry = 0; + struct vpu_device *vpu_dev = inst->dev; + int i; + + *fail_res = 0; + if (!inst->codec_info) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + do { + ret = wave5_vpu_dec_finish_seq(inst, fail_res); + if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_warn(inst->dev->dev, "dec_finish_seq timed out\n"); + goto unlock_and_return; + } + + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING && + retry++ >= MAX_FIRMWARE_CALL_RETRY) { + ret = -ETIMEDOUT; + goto unlock_and_return; + } + } while (ret != 0); + + dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__); + + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work); + + for (i = 0 ; i < MAX_REG_FRAME; i++) { + ret = reset_auxiliary_buffers(inst, i); + if (ret) { + ret = 0; + break; + } + } + + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task); + +unlock_and_return: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst) +{ + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_dec_init_seq(inst); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_dec_get_seq_info(inst, info); + if (!ret) + p_dec_info->initial_info_obtained = true; + + info->rd_ptr = wave5_dec_get_rd_ptr(inst); + info->wr_ptr = p_dec_info->stream_wr_ptr; + + p_dec_info->initial_info = *info; + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs, + int num_of_display_fbs, int stride, int height) +{ + struct dec_info *p_dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + struct frame_buffer *fb; + + if (num_of_decoding_fbs >= WAVE5_MAX_FBS || num_of_display_fbs >= WAVE5_MAX_FBS) + return -EINVAL; + + p_dec_info = &inst->codec_info->dec_info; + p_dec_info->num_of_decoding_fbs = num_of_decoding_fbs; + p_dec_info->num_of_display_fbs = num_of_display_fbs; + p_dec_info->stride = stride; + + if (!p_dec_info->initial_info_obtained) + return -EINVAL; + + if (stride < p_dec_info->initial_info.pic_width || (stride % 8 != 0) || + height < p_dec_info->initial_info.pic_height) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + fb = inst->frame_buf; + ret = wave5_vpu_dec_register_framebuffer(inst, &fb[p_dec_info->num_of_decoding_fbs], + LINEAR_FRAME_MAP, p_dec_info->num_of_display_fbs); + if (ret) + goto err_out; + + ret = wave5_vpu_dec_register_framebuffer(inst, &fb[0], COMPRESSED_FRAME_MAP, + p_dec_info->num_of_decoding_fbs); + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr, + dma_addr_t *pwr_ptr, size_t *size) +{ + struct dec_info *p_dec_info; + dma_addr_t rd_ptr; + dma_addr_t wr_ptr; + int room; + struct vpu_device *vpu_dev = inst->dev; + int ret; + + p_dec_info = &inst->codec_info->dec_info; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + rd_ptr = wave5_dec_get_rd_ptr(inst); + mutex_unlock(&vpu_dev->hw_lock); + + wr_ptr = p_dec_info->stream_wr_ptr; + + if (wr_ptr < rd_ptr) + room = rd_ptr - wr_ptr; + else + room = (p_dec_info->stream_buf_end_addr - wr_ptr) + + (rd_ptr - p_dec_info->stream_buf_start_addr); + room--; + + if (prd_ptr) + *prd_ptr = rd_ptr; + if (pwr_ptr) + *pwr_ptr = wr_ptr; + if (size) + *size = room; + + return 0; +} + +int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size) +{ + struct dec_info *p_dec_info; + dma_addr_t wr_ptr; + dma_addr_t rd_ptr; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (!inst->codec_info) + return -EINVAL; + + p_dec_info = &inst->codec_info->dec_info; + wr_ptr = p_dec_info->stream_wr_ptr; + rd_ptr = p_dec_info->stream_rd_ptr; + + if (size > 0) { + if (wr_ptr < rd_ptr && rd_ptr <= wr_ptr + size) + return -EINVAL; + + wr_ptr += size; + + if (wr_ptr > p_dec_info->stream_buf_end_addr) { + u32 room = wr_ptr - p_dec_info->stream_buf_end_addr; + + wr_ptr = p_dec_info->stream_buf_start_addr; + wr_ptr += room; + } else if (wr_ptr == p_dec_info->stream_buf_end_addr) { + wr_ptr = p_dec_info->stream_buf_start_addr; + } + + p_dec_info->stream_wr_ptr = wr_ptr; + p_dec_info->stream_rd_ptr = rd_ptr; + } + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + ret = wave5_vpu_dec_set_bitstream_flag(inst, (size == 0)); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (p_dec_info->stride == 0) /* this means frame buffers have not been registered. */ + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_decode(inst, res_fail); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_dec_set_rd_ptr(inst, addr); + + p_dec_info->stream_rd_ptr = addr; + if (update_wr_ptr) + p_dec_info->stream_wr_ptr = addr; + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst) +{ + int ret; + dma_addr_t rd_ptr; + + ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (ret) + return ret; + + rd_ptr = wave5_dec_get_rd_ptr(inst); + + mutex_unlock(&inst->dev->hw_lock); + + return rd_ptr; +} + +int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info) +{ + struct dec_info *p_dec_info; + int ret; + struct vpu_rect rect_info; + u32 val; + u32 decoded_index; + u32 disp_idx; + u32 max_dec_index; + struct vpu_device *vpu_dev = inst->dev; + struct dec_output_info *disp_info; + + if (!info) + return -EINVAL; + + p_dec_info = &inst->codec_info->dec_info; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + memset(info, 0, sizeof(*info)); + + ret = wave5_vpu_dec_get_result(inst, info); + if (ret) { + info->rd_ptr = p_dec_info->stream_rd_ptr; + info->wr_ptr = p_dec_info->stream_wr_ptr; + goto err_out; + } + + decoded_index = info->index_frame_decoded; + + /* calculate display frame region */ + val = 0; + rect_info.left = 0; + rect_info.right = 0; + rect_info.top = 0; + rect_info.bottom = 0; + + if (decoded_index < WAVE5_MAX_FBS) { + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) + rect_info = p_dec_info->initial_info.pic_crop_rect; + + if (inst->std == W_HEVC_DEC) + p_dec_info->dec_out_info[decoded_index].decoded_poc = info->decoded_poc; + + p_dec_info->dec_out_info[decoded_index].rc_decoded = rect_info; + } + info->rc_decoded = rect_info; + + disp_idx = info->index_frame_display; + if (info->index_frame_display >= 0 && info->index_frame_display < WAVE5_MAX_FBS) { + disp_info = &p_dec_info->dec_out_info[disp_idx]; + if (info->index_frame_display != info->index_frame_decoded) { + /* + * when index_frame_decoded < 0, and index_frame_display >= 0 + * info->dec_pic_width and info->dec_pic_height are still valid + * but those of p_dec_info->dec_out_info[disp_idx] are invalid in VP9 + */ + info->disp_pic_width = disp_info->dec_pic_width; + info->disp_pic_height = disp_info->dec_pic_height; + } else { + info->disp_pic_width = info->dec_pic_width; + info->disp_pic_height = info->dec_pic_height; + } + + info->rc_display = disp_info->rc_decoded; + + } else { + info->rc_display.left = 0; + info->rc_display.right = 0; + info->rc_display.top = 0; + info->rc_display.bottom = 0; + info->disp_pic_width = 0; + info->disp_pic_height = 0; + } + + p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst); + p_dec_info->frame_display_flag = vpu_read_reg(vpu_dev, W5_RET_DEC_DISP_IDC); + + val = p_dec_info->num_of_decoding_fbs; //fb_offset + + max_dec_index = (p_dec_info->num_of_decoding_fbs > p_dec_info->num_of_display_fbs) ? + p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs; + + if (info->index_frame_display >= 0 && + info->index_frame_display < (int)max_dec_index) + info->disp_frame = inst->frame_buf[val + info->index_frame_display]; + + info->rd_ptr = p_dec_info->stream_rd_ptr; + info->wr_ptr = p_dec_info->stream_wr_ptr; + info->frame_display_flag = p_dec_info->frame_display_flag; + + info->sequence_no = p_dec_info->initial_info.sequence_no; + if (decoded_index < WAVE5_MAX_FBS) + p_dec_info->dec_out_info[decoded_index] = *info; + + if (disp_idx < WAVE5_MAX_FBS) + info->disp_frame.sequence_no = info->sequence_no; + + if (info->sequence_changed) { + memcpy((void *)&p_dec_info->initial_info, (void *)&p_dec_info->new_seq_info, + sizeof(struct dec_initial_info)); + p_dec_info->initial_info.sequence_no++; + } + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (index >= p_dec_info->num_of_display_fbs) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + ret = wave5_dec_clr_disp_flag(inst, index); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret = 0; + struct vpu_device *vpu_dev = inst->dev; + + if (index >= p_dec_info->num_of_display_fbs) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + ret = wave5_dec_set_disp_flag(inst, index); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index) +{ + if (index >= MAX_REG_FRAME) + return -EINVAL; + + if (inst->frame_vbuf[index].size == 0) + return -EINVAL; + + wave5_vdi_free_dma_memory(inst->dev, &inst->frame_vbuf[index]); + + return 0; +} + +int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret = 0; + + switch (cmd) { + case DEC_GET_QUEUE_STATUS: { + struct queue_status_info *queue_info = parameter; + + queue_info->instance_queue_count = p_dec_info->instance_queue_count; + queue_info->report_queue_count = p_dec_info->report_queue_count; + break; + } + case DEC_RESET_FRAMEBUF_INFO: { + int i; + + for (i = 0; i < MAX_REG_FRAME; i++) { + ret = wave5_vpu_dec_reset_framebuffer(inst, i); + if (ret) + break; + } + + for (i = 0; i < MAX_REG_FRAME; i++) { + ret = reset_auxiliary_buffers(inst, i); + if (ret) + break; + } + + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); + break; + } + case DEC_GET_SEQ_INFO: { + struct dec_initial_info *seq_info = parameter; + + *seq_info = p_dec_info->initial_info; + break; + } + + default: + return -EINVAL; + } + + return ret; +} + +int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param) +{ + struct enc_info *p_enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = wave5_vpu_enc_check_open_param(inst, open_param); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (!wave5_vpu_is_init(vpu_dev)) { + mutex_unlock(&vpu_dev->hw_lock); + return -ENODEV; + } + + p_enc_info = &inst->codec_info->enc_info; + p_enc_info->open_param = *open_param; + + ret = wave5_vpu_build_up_enc_param(vpu_dev->dev, inst, open_param); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + int retry = 0; + struct vpu_device *vpu_dev = inst->dev; + + *fail_res = 0; + if (!inst->codec_info) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + do { + ret = wave5_vpu_enc_finish_seq(inst, fail_res); + if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_warn(inst->dev->dev, "enc_finish_seq timed out\n"); + mutex_unlock(&vpu_dev->hw_lock); + return ret; + } + + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING && + retry++ >= MAX_FIRMWARE_CALL_RETRY) { + mutex_unlock(&vpu_dev->hw_lock); + return -ETIMEDOUT; + } + } while (ret != 0); + + dev_dbg(inst->dev->dev, "%s: enc_finish_seq complete\n", __func__); + + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work); + + if (inst->std == W_HEVC_ENC || inst->std == W_AVC_ENC) { + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_sub_sam_buf); + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_mv); + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_y_tbl); + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_c_tbl); + } + + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task); + + mutex_unlock(&vpu_dev->hw_lock); + + return 0; +} + +int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num, + unsigned int stride, int height, + enum tiled_map_type map_type) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + unsigned int size_luma, size_chroma; + int i; + + if (p_enc_info->stride) + return -EINVAL; + + if (!p_enc_info->initial_info_obtained) + return -EINVAL; + + if (num < p_enc_info->initial_info.min_frame_buffer_count) + return -EINVAL; + + if (stride == 0 || stride % 8 != 0) + return -EINVAL; + + if (height <= 0) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + p_enc_info->num_frame_buffers = num; + p_enc_info->stride = stride; + + size_luma = stride * height; + size_chroma = ALIGN(stride / 2, 16) * height; + + for (i = 0; i < num; i++) { + if (!inst->frame_buf[i].update_fb_info) + continue; + + inst->frame_buf[i].update_fb_info = false; + inst->frame_buf[i].stride = stride; + inst->frame_buf[i].height = height; + inst->frame_buf[i].map_type = COMPRESSED_FRAME_MAP; + inst->frame_buf[i].buf_y_size = size_luma; + inst->frame_buf[i].buf_cb = inst->frame_buf[i].buf_y + size_luma; + inst->frame_buf[i].buf_cb_size = size_chroma; + inst->frame_buf[i].buf_cr_size = 0; + } + + ret = wave5_vpu_enc_register_framebuffer(inst->dev->dev, inst, &inst->frame_buf[0], + COMPRESSED_FRAME_MAP, + p_enc_info->num_frame_buffers); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +static int wave5_check_enc_param(struct vpu_instance *inst, struct enc_param *param) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + if (!param) + return -EINVAL; + + if (!param->source_frame) + return -EINVAL; + + if (p_enc_info->open_param.bit_rate == 0 && inst->std == W_HEVC_ENC) { + if (param->pic_stream_buffer_addr % 16 || param->pic_stream_buffer_size == 0) + return -EINVAL; + } + if (param->pic_stream_buffer_addr % 8 || param->pic_stream_buffer_size == 0) + return -EINVAL; + + return 0; +} + +int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, u32 *fail_res) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + *fail_res = 0; + + if (p_enc_info->stride == 0) /* this means frame buffers have not been registered. */ + return -EINVAL; + + ret = wave5_check_enc_param(inst, param); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + p_enc_info->pts_map[param->src_idx] = param->pts; + + ret = wave5_vpu_encode(inst, param, fail_res); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_enc_get_result(inst, info); + if (ret) { + info->pts = 0; + goto unlock; + } + + if (info->recon_frame_index >= 0) + info->pts = p_enc_info->pts_map[info->enc_src_idx]; + +unlock: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + switch (cmd) { + case ENABLE_ROTATION: + p_enc_info->rotation_enable = true; + break; + case ENABLE_MIRRORING: + p_enc_info->mirror_enable = true; + break; + case SET_MIRROR_DIRECTION: { + enum mirror_direction mir_dir; + + mir_dir = *(enum mirror_direction *)parameter; + if (mir_dir != MIRDIR_NONE && mir_dir != MIRDIR_HOR && + mir_dir != MIRDIR_VER && mir_dir != MIRDIR_HOR_VER) + return -EINVAL; + p_enc_info->mirror_direction = mir_dir; + break; + } + case SET_ROTATION_ANGLE: { + int angle; + + angle = *(int *)parameter; + if (angle && angle != 90 && angle != 180 && angle != 270) + return -EINVAL; + if (p_enc_info->initial_info_obtained && (angle == 90 || angle == 270)) + return -EINVAL; + p_enc_info->rotation_angle = angle; + break; + } + case ENC_GET_QUEUE_STATUS: { + struct queue_status_info *queue_info = parameter; + + queue_info->instance_queue_count = p_enc_info->instance_queue_count; + queue_info->report_queue_count = p_enc_info->report_queue_count; + break; + } + default: + return -EINVAL; + } + return 0; +} + +int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst) +{ + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_enc_init_seq(inst); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (!info) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_enc_get_seq_info(inst, info); + if (ret) { + p_enc_info->initial_info_obtained = false; + mutex_unlock(&vpu_dev->hw_lock); + return ret; + } + + p_enc_info->initial_info_obtained = true; + p_enc_info->initial_info = *info; + + mutex_unlock(&vpu_dev->hw_lock); + + return 0; +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h new file mode 100644 index 000000000000..352f6e904e50 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -0,0 +1,870 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - helper definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef VPUAPI_H_INCLUDED +#define VPUAPI_H_INCLUDED + +#include <linux/idr.h> +#include <linux/genalloc.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-ctrls.h> +#include "wave5-vpuerror.h" +#include "wave5-vpuconfig.h" +#include "wave5-vdi.h" + +enum product_id { + PRODUCT_ID_521, + PRODUCT_ID_511, + PRODUCT_ID_517, + PRODUCT_ID_NONE, +}; + +struct vpu_attr; + +enum vpu_instance_type { + VPU_INST_TYPE_DEC = 0, + VPU_INST_TYPE_ENC = 1 +}; + +enum vpu_instance_state { + VPU_INST_STATE_NONE = 0, + VPU_INST_STATE_OPEN = 1, + VPU_INST_STATE_INIT_SEQ = 2, + VPU_INST_STATE_PIC_RUN = 3, + VPU_INST_STATE_STOP = 4 +}; + +/* Maximum available on hardware. */ +#define WAVE5_MAX_FBS 32 + +#define MAX_REG_FRAME (WAVE5_MAX_FBS * 2) + +#define WAVE5_DEC_HEVC_BUF_SIZE(_w, _h) (DIV_ROUND_UP(_w, 64) * DIV_ROUND_UP(_h, 64) * 256 + 64) +#define WAVE5_DEC_AVC_BUF_SIZE(_w, _h) ((((ALIGN(_w, 256) / 16) * (ALIGN(_h, 16) / 16)) + 16) * 80) + +#define WAVE5_FBC_LUMA_TABLE_SIZE(_w, _h) (ALIGN(_h, 64) * ALIGN(_w, 256) / 32) +#define WAVE5_FBC_CHROMA_TABLE_SIZE(_w, _h) (ALIGN((_h), 64) * ALIGN((_w) / 2, 256) / 32) +#define WAVE5_ENC_AVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) * ALIGN(_h, 64) / 32) +#define WAVE5_ENC_HEVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) / 64 * ALIGN(_h, 64) / 64 * 128) + +/* + * common struct and definition + */ +enum cod_std { + STD_AVC = 0, + STD_HEVC = 12, + STD_MAX +}; + +enum wave_std { + W_HEVC_DEC = 0x00, + W_HEVC_ENC = 0x01, + W_AVC_DEC = 0x02, + W_AVC_ENC = 0x03, + STD_UNKNOWN = 0xFF +}; + +enum set_param_option { + OPT_COMMON = 0, /* SET_PARAM command option for encoding sequence */ + OPT_CUSTOM_GOP = 1, /* SET_PARAM command option for setting custom GOP */ + OPT_CUSTOM_HEADER = 2, /* SET_PARAM command option for setting custom VPS/SPS/PPS */ + OPT_VUI = 3, /* SET_PARAM command option for encoding VUI */ + OPT_CHANGE_PARAM = 0x10, +}; + +/************************************************************************/ +/* PROFILE & LEVEL */ +/************************************************************************/ +/* HEVC */ +#define HEVC_PROFILE_MAIN 1 +#define HEVC_PROFILE_MAIN10 2 +#define HEVC_PROFILE_STILLPICTURE 3 +#define HEVC_PROFILE_MAIN10_STILLPICTURE 2 + +/* H.264 profile for encoder*/ +#define H264_PROFILE_BP 1 +#define H264_PROFILE_MP 2 +#define H264_PROFILE_EXTENDED 3 +#define H264_PROFILE_HP 4 +#define H264_PROFILE_HIGH10 5 +#define H264_PROFILE_HIGH422 6 +#define H264_PROFILE_HIGH444 7 + +/************************************************************************/ +/* error codes */ +/************************************************************************/ + +/************************************************************************/ +/* utility macros */ +/************************************************************************/ + +/* Initialize sequence firmware command mode */ +#define INIT_SEQ_NORMAL 1 + +/* Decode firmware command mode */ +#define DEC_PIC_NORMAL 0 + +/* bit_alloc_mode */ +#define BIT_ALLOC_MODE_FIXED_RATIO 2 + +/* bit_rate */ +#define MAX_BIT_RATE 700000000 + +/* decoding_refresh_type */ +#define DEC_REFRESH_TYPE_NON_IRAP 0 +#define DEC_REFRESH_TYPE_CRA 1 +#define DEC_REFRESH_TYPE_IDR 2 + +/* depend_slice_mode */ +#define DEPEND_SLICE_MODE_RECOMMENDED 1 +#define DEPEND_SLICE_MODE_BOOST 2 +#define DEPEND_SLICE_MODE_FAST 3 + +/* hvs_max_delta_qp */ +#define MAX_HVS_MAX_DELTA_QP 51 + +/* intra_refresh_mode */ +#define REFRESH_MODE_CTU_ROWS 1 +#define REFRESH_MODE_CTU_COLUMNS 2 +#define REFRESH_MODE_CTU_STEP_SIZE 3 +#define REFRESH_MODE_CTUS 4 + +/* intra_mb_refresh_mode */ +#define REFRESH_MB_MODE_NONE 0 +#define REFRESH_MB_MODE_CTU_ROWS 1 +#define REFRESH_MB_MODE_CTU_COLUMNS 2 +#define REFRESH_MB_MODE_CTU_STEP_SIZE 3 + +/* intra_qp */ +#define MAX_INTRA_QP 63 + +/* nr_inter_weight_* */ +#define MAX_INTER_WEIGHT 31 + +/* nr_intra_weight_* */ +#define MAX_INTRA_WEIGHT 31 + +/* nr_noise_sigma_* */ +#define MAX_NOISE_SIGMA 255 + +/* bitstream_buffer_size */ +#define MIN_BITSTREAM_BUFFER_SIZE 1024 +#define MIN_BITSTREAM_BUFFER_SIZE_WAVE521 (1024 * 64) + +/* vbv_buffer_size */ +#define MIN_VBV_BUFFER_SIZE 10 +#define MAX_VBV_BUFFER_SIZE 3000 + +#define BUFFER_MARGIN 4096 + +#define MAX_FIRMWARE_CALL_RETRY 10 + +#define VDI_LITTLE_ENDIAN 0x0 + +/* + * Parameters of DEC_SET_SEQ_CHANGE_MASK + */ +#define SEQ_CHANGE_ENABLE_PROFILE BIT(5) +#define SEQ_CHANGE_ENABLE_SIZE BIT(16) +#define SEQ_CHANGE_ENABLE_BITDEPTH BIT(18) +#define SEQ_CHANGE_ENABLE_DPB_COUNT BIT(19) +#define SEQ_CHANGE_ENABLE_ASPECT_RATIO BIT(21) +#define SEQ_CHANGE_ENABLE_VIDEO_SIGNAL BIT(23) +#define SEQ_CHANGE_ENABLE_VUI_TIMING_INFO BIT(29) + +#define SEQ_CHANGE_ENABLE_ALL_HEVC (SEQ_CHANGE_ENABLE_PROFILE | \ + SEQ_CHANGE_ENABLE_SIZE | \ + SEQ_CHANGE_ENABLE_BITDEPTH | \ + SEQ_CHANGE_ENABLE_DPB_COUNT) + +#define SEQ_CHANGE_ENABLE_ALL_AVC (SEQ_CHANGE_ENABLE_SIZE | \ + SEQ_CHANGE_ENABLE_BITDEPTH | \ + SEQ_CHANGE_ENABLE_DPB_COUNT | \ + SEQ_CHANGE_ENABLE_ASPECT_RATIO | \ + SEQ_CHANGE_ENABLE_VIDEO_SIGNAL | \ + SEQ_CHANGE_ENABLE_VUI_TIMING_INFO) + +#define DISPLAY_IDX_FLAG_SEQ_END -1 +#define DISPLAY_IDX_FLAG_NO_FB -3 +#define DECODED_IDX_FLAG_NO_FB -1 +#define DECODED_IDX_FLAG_SKIP -2 + +#define RECON_IDX_FLAG_ENC_END -1 +#define RECON_IDX_FLAG_ENC_DELAY -2 +#define RECON_IDX_FLAG_HEADER_ONLY -3 +#define RECON_IDX_FLAG_CHANGE_PARAM -4 + +enum codec_command { + ENABLE_ROTATION, + ENABLE_MIRRORING, + SET_MIRROR_DIRECTION, + SET_ROTATION_ANGLE, + DEC_GET_QUEUE_STATUS, + ENC_GET_QUEUE_STATUS, + DEC_RESET_FRAMEBUF_INFO, + DEC_GET_SEQ_INFO, +}; + +enum mirror_direction { + MIRDIR_NONE, /* no mirroring */ + MIRDIR_VER, /* vertical mirroring */ + MIRDIR_HOR, /* horizontal mirroring */ + MIRDIR_HOR_VER /* horizontal and vertical mirroring */ +}; + +enum frame_buffer_format { + FORMAT_ERR = -1, + FORMAT_420 = 0, /* 8bit */ + FORMAT_422, /* 8bit */ + FORMAT_224, /* 8bit */ + FORMAT_444, /* 8bit */ + FORMAT_400, /* 8bit */ + + /* little endian perspective */ + /* | addr 0 | addr 1 | */ + FORMAT_420_P10_16BIT_MSB = 5, /* lsb |000000xx|xxxxxxxx | msb */ + FORMAT_420_P10_16BIT_LSB, /* lsb |xxxxxxx |xx000000 | msb */ + FORMAT_420_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */ + FORMAT_420_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */ + + /* 4:2:2 packed format */ + /* little endian perspective */ + /* | addr 0 | addr 1 | */ + FORMAT_422_P10_16BIT_MSB, /* lsb |000000xx |xxxxxxxx | msb */ + FORMAT_422_P10_16BIT_LSB, /* lsb |xxxxxxxx |xx000000 | msb */ + FORMAT_422_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */ + FORMAT_422_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */ + + FORMAT_YUYV, /* 8bit packed format : Y0U0Y1V0 Y2U1Y3V1 ... */ + FORMAT_YUYV_P10_16BIT_MSB, + FORMAT_YUYV_P10_16BIT_LSB, + FORMAT_YUYV_P10_32BIT_MSB, + FORMAT_YUYV_P10_32BIT_LSB, + + FORMAT_YVYU, /* 8bit packed format : Y0V0Y1U0 Y2V1Y3U1 ... */ + FORMAT_YVYU_P10_16BIT_MSB, + FORMAT_YVYU_P10_16BIT_LSB, + FORMAT_YVYU_P10_32BIT_MSB, + FORMAT_YVYU_P10_32BIT_LSB, + + FORMAT_UYVY, /* 8bit packed format : U0Y0V0Y1 U1Y2V1Y3 ... */ + FORMAT_UYVY_P10_16BIT_MSB, + FORMAT_UYVY_P10_16BIT_LSB, + FORMAT_UYVY_P10_32BIT_MSB, + FORMAT_UYVY_P10_32BIT_LSB, + + FORMAT_VYUY, /* 8bit packed format : V0Y0U0Y1 V1Y2U1Y3 ... */ + FORMAT_VYUY_P10_16BIT_MSB, + FORMAT_VYUY_P10_16BIT_LSB, + FORMAT_VYUY_P10_32BIT_MSB, + FORMAT_VYUY_P10_32BIT_LSB, + + FORMAT_MAX, +}; + +enum packed_format_num { + NOT_PACKED = 0, + PACKED_YUYV, + PACKED_YVYU, + PACKED_UYVY, + PACKED_VYUY, +}; + +enum wave5_interrupt_bit { + INT_WAVE5_INIT_VPU = 0, + INT_WAVE5_WAKEUP_VPU = 1, + INT_WAVE5_SLEEP_VPU = 2, + INT_WAVE5_CREATE_INSTANCE = 3, + INT_WAVE5_FLUSH_INSTANCE = 4, + INT_WAVE5_DESTROY_INSTANCE = 5, + INT_WAVE5_INIT_SEQ = 6, + INT_WAVE5_SET_FRAMEBUF = 7, + INT_WAVE5_DEC_PIC = 8, + INT_WAVE5_ENC_PIC = 8, + INT_WAVE5_ENC_SET_PARAM = 9, + INT_WAVE5_DEC_QUERY = 14, + INT_WAVE5_BSBUF_EMPTY = 15, + INT_WAVE5_BSBUF_FULL = 15, +}; + +enum pic_type { + PIC_TYPE_I = 0, + PIC_TYPE_P = 1, + PIC_TYPE_B = 2, + PIC_TYPE_IDR = 5, /* H.264/H.265 IDR (Instantaneous Decoder Refresh) picture */ + PIC_TYPE_MAX /* no meaning */ +}; + +enum sw_reset_mode { + SW_RESET_SAFETY, + SW_RESET_FORCE, + SW_RESET_ON_BOOT +}; + +enum tiled_map_type { + LINEAR_FRAME_MAP = 0, /* linear frame map type */ + COMPRESSED_FRAME_MAP = 17, /* compressed frame map type*/ +}; + +enum temporal_id_mode { + TEMPORAL_ID_MODE_ABSOLUTE, + TEMPORAL_ID_MODE_RELATIVE, +}; + +struct vpu_attr { + u32 product_id; + char product_name[8]; /* product name in ascii code */ + u32 product_version; + u32 fw_version; + u32 customer_id; + u32 support_decoders; /* bitmask */ + u32 support_encoders; /* bitmask */ + u32 support_backbone: 1; + u32 support_avc10bit_enc: 1; + u32 support_hevc10bit_enc: 1; + u32 support_vcore_backbone: 1; + u32 support_vcpu_backbone: 1; +}; + +struct frame_buffer { + dma_addr_t buf_y; + dma_addr_t buf_cb; + dma_addr_t buf_cr; + unsigned int buf_y_size; + unsigned int buf_cb_size; + unsigned int buf_cr_size; + enum tiled_map_type map_type; + unsigned int stride; /* horizontal stride for the given frame buffer */ + unsigned int width; /* width of the given frame buffer */ + unsigned int height; /* height of the given frame buffer */ + size_t size; /* size of the given frame buffer */ + unsigned int sequence_no; + bool update_fb_info; +}; + +struct vpu_rect { + unsigned int left; /* horizontal pixel offset from left edge */ + unsigned int top; /* vertical pixel offset from top edge */ + unsigned int right; /* horizontal pixel offset from right edge */ + unsigned int bottom; /* vertical pixel offset from bottom edge */ +}; + +/* + * decode struct and definition + */ + +struct dec_open_param { + dma_addr_t bitstream_buffer; + size_t bitstream_buffer_size; +}; + +struct dec_initial_info { + u32 pic_width; + u32 pic_height; + struct vpu_rect pic_crop_rect; + u32 min_frame_buffer_count; /* between 1 to 16 */ + + u32 profile; + u32 luma_bitdepth; /* bit-depth of the luma sample */ + u32 chroma_bitdepth; /* bit-depth of the chroma sample */ + u32 seq_init_err_reason; + dma_addr_t rd_ptr; /* read pointer of bitstream buffer */ + dma_addr_t wr_ptr; /* write pointer of bitstream buffer */ + u32 sequence_no; + u32 vlc_buf_size; + u32 param_buf_size; +}; + +struct dec_output_info { + /** + * This is a frame buffer index for the picture to be displayed at the moment + * among frame buffers which are registered using vpu_dec_register_frame_buffer(). + * Frame data that will be displayed is stored in the frame buffer with this index + * When there is no display delay, this index is always the equal to + * index_frame_decoded, however, if displaying is delayed (for display + * reordering in AVC or B-frames in VC1), this index might be different to + * index_frame_decoded. By checking this index, HOST applications can easily figure + * out whether sequence decoding has been finished or not. + * + * -3(0xFFFD) or -2(0xFFFE) : when a display output cannot be given due to picture + * reordering or skip option + * -1(0xFFFF) : when there is no more output for display at the end of sequence + * decoding + */ + s32 index_frame_display; + /** + * This is the frame buffer index of the decoded picture among the frame buffers which were + * registered using vpu_dec_register_frame_buffer(). The currently decoded frame is stored + * into the frame buffer specified by this index. + * + * -2 : indicates that no decoded output is generated because decoder meets EOS + * (end of sequence) or skip + * -1 : indicates that the decoder fails to decode a picture because there is no available + * frame buffer + */ + s32 index_frame_decoded; + s32 index_frame_decoded_for_tiled; + u32 nal_type; + unsigned int pic_type; + struct vpu_rect rc_display; + unsigned int disp_pic_width; + unsigned int disp_pic_height; + struct vpu_rect rc_decoded; + u32 dec_pic_width; + u32 dec_pic_height; + s32 decoded_poc; + int temporal_id; /* temporal ID of the picture */ + dma_addr_t rd_ptr; /* stream buffer read pointer for the current decoder instance */ + dma_addr_t wr_ptr; /* stream buffer write pointer for the current decoder instance */ + struct frame_buffer disp_frame; + u32 frame_display_flag; /* it reports a frame buffer flag to be displayed */ + /** + * this variable reports that sequence has been changed while H.264/AVC stream decoding. + * if it is 1, HOST application can get the new sequence information by calling + * vpu_dec_get_initial_info() or wave5_vpu_dec_issue_seq_init(). + * + * for H.265/HEVC decoder, each bit has a different meaning as follows. + * + * sequence_changed[5] : it indicates that the profile_idc has been changed + * sequence_changed[16] : it indicates that the resolution has been changed + * sequence_changed[19] : it indicates that the required number of frame buffer has + * been changed. + */ + unsigned int frame_cycle; /* reports the number of cycles for processing a frame */ + u32 sequence_no; + + u32 dec_host_cmd_tick; /* tick of DEC_PIC command for the picture */ + u32 dec_decode_end_tick; /* end tick of decoding slices of the picture */ + + u32 sequence_changed; +}; + +struct queue_status_info { + u32 instance_queue_count; + u32 report_queue_count; +}; + +/* + * encode struct and definition + */ + +#define MAX_NUM_TEMPORAL_LAYER 7 +#define MAX_NUM_SPATIAL_LAYER 3 +#define MAX_GOP_NUM 8 + +struct custom_gop_pic_param { + u32 pic_type; /* picture type of nth picture in the custom GOP */ + u32 poc_offset; /* POC of nth picture in the custom GOP */ + u32 pic_qp; /* quantization parameter of nth picture in the custom GOP */ + u32 use_multi_ref_p; /* use multiref pic for P picture. valid only if PIC_TYPE is P */ + u32 ref_poc_l0; /* POC of reference L0 of nth picture in the custom GOP */ + u32 ref_poc_l1; /* POC of reference L1 of nth picture in the custom GOP */ + s32 temporal_id; /* temporal ID of nth picture in the custom GOP */ +}; + +struct enc_wave_param { + /* + * profile indicator (HEVC only) + * + * 0 : the firmware determines a profile according to the internal_bit_depth + * 1 : main profile + * 2 : main10 profile + * 3 : main still picture profile + * In the AVC encoder, a profile cannot be set by the host application. + * The firmware decides it based on internal_bit_depth. + * profile = HIGH (bitdepth 8) profile = HIGH10 (bitdepth 10) + */ + u32 profile; + u32 level; /* level indicator (level * 10) */ + u32 internal_bit_depth: 4; /* 8/10 */ + u32 gop_preset_idx: 4; /* 0 - 9 */ + u32 decoding_refresh_type: 2; /* 0=non-IRAP, 1=CRA, 2=IDR */ + u32 intra_qp; /* quantization parameter of intra picture */ + u32 intra_period; /* period of intra picture in GOP size */ + u32 conf_win_top; /* top offset of conformance window */ + u32 conf_win_bot; /* bottom offset of conformance window */ + u32 conf_win_left; /* left offset of conformance window */ + u32 conf_win_right; /* right offset of conformance window */ + u32 intra_refresh_mode: 3; + /* + * Argument for intra_ctu_refresh_mode. + * + * Depending on intra_refresh_mode, it can mean one of the following: + * - intra_ctu_refresh_mode (1) -> number of consecutive CTU rows + * - intra_ctu_refresh_mode (2) -> the number of consecutive CTU columns + * - intra_ctu_refresh_mode (3) -> step size in CTU + * - intra_ctu_refresh_mode (4) -> number of intra ct_us to be encoded in a picture + */ + u32 intra_refresh_arg; + /* + * 0 : custom setting + * 1 : recommended encoder parameters (slow encoding speed, highest picture quality) + * 2 : boost mode (normal encoding speed, moderate picture quality) + * 3 : fast mode (fast encoding speed, low picture quality) + */ + u32 depend_slice_mode : 2; + u32 depend_slice_mode_arg; + u32 independ_slice_mode : 1; /* 0=no-multi-slice, 1=slice-in-ctu-number*/ + u32 independ_slice_mode_arg; + u32 max_num_merge: 2; + s32 beta_offset_div2: 4; /* sets beta_offset_div2 for deblocking filter */ + s32 tc_offset_div2: 4; /* sets tc_offset_div3 for deblocking filter */ + u32 hvs_qp_scale: 4; /* QP scaling factor for CU QP adjust if hvs_qp_scale_enable is 1 */ + u32 hvs_max_delta_qp; /* maximum delta QP for HVS */ + s32 chroma_cb_qp_offset; /* the value of chroma(cb) QP offset */ + s32 chroma_cr_qp_offset; /* the value of chroma(cr) QP offset */ + s32 initial_rc_qp; + u32 nr_intra_weight_y; + u32 nr_intra_weight_cb; /* weight to cb noise level for intra picture (0 ~ 31) */ + u32 nr_intra_weight_cr; /* weight to cr noise level for intra picture (0 ~ 31) */ + u32 nr_inter_weight_y; + u32 nr_inter_weight_cb; /* weight to cb noise level for inter picture (0 ~ 31) */ + u32 nr_inter_weight_cr; /* weight to cr noise level for inter picture (0 ~ 31) */ + u32 min_qp_i; /* minimum QP of I picture for rate control */ + u32 max_qp_i; /* maximum QP of I picture for rate control */ + u32 min_qp_p; /* minimum QP of P picture for rate control */ + u32 max_qp_p; /* maximum QP of P picture for rate control */ + u32 min_qp_b; /* minimum QP of B picture for rate control */ + u32 max_qp_b; /* maximum QP of B picture for rate control */ + u32 avc_idr_period; /* period of IDR picture (0 ~ 1024). 0 - implies an infinite period */ + u32 avc_slice_arg; /* the number of MB for a slice when avc_slice_mode is set with 1 */ + u32 intra_mb_refresh_mode: 2; /* 0=none, 1=row, 2=column, 3=step-size-in-mb */ + /** + * Argument for intra_mb_refresh_mode. + * + * intra_mb_refresh_mode (1) -> number of consecutive MB rows + * intra_mb_refresh_mode (2) ->the number of consecutive MB columns + * intra_mb_refresh_mode (3) -> step size in MB + */ + u32 intra_mb_refresh_arg; + u32 rc_weight_param; + u32 rc_weight_buf; + + /* flags */ + u32 en_still_picture: 1; /* still picture profile */ + u32 tier: 1; /* 0=main, 1=high */ + u32 avc_slice_mode: 1; /* 0=none, 1=slice-in-mb-number */ + u32 entropy_coding_mode: 1; /* 0=CAVLC, 1=CABAC */ + u32 lossless_enable: 1; /* enable lossless encoding */ + u32 const_intra_pred_flag: 1; /* enable constrained intra prediction */ + u32 tmvp_enable: 1; /* enable temporal motion vector prediction */ + u32 wpp_enable: 1; + u32 disable_deblk: 1; /* disable in-loop deblocking filtering */ + u32 lf_cross_slice_boundary_enable: 1; + u32 skip_intra_trans: 1; + u32 sao_enable: 1; /* enable SAO (sample adaptive offset) */ + u32 intra_nx_n_enable: 1; /* enables intra nx_n p_us */ + u32 cu_level_rc_enable: 1; /* enable CU level rate control */ + u32 hvs_qp_enable: 1; /* enable CU QP adjustment for subjective quality enhancement */ + u32 strong_intra_smooth_enable: 1; /* enable strong intra smoothing */ + u32 rdo_skip: 1; /* skip RDO (rate distortion optimization) */ + u32 lambda_scaling_enable: 1; /* enable lambda scaling using custom GOP */ + u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ + u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ +}; + +struct enc_open_param { + dma_addr_t bitstream_buffer; + unsigned int bitstream_buffer_size; + u32 pic_width; /* width of a picture to be encoded in unit of sample */ + u32 pic_height; /* height of a picture to be encoded in unit of sample */ + u32 frame_rate_info;/* desired fps */ + u32 vbv_buffer_size; + u32 bit_rate; /* target bitrate in bps */ + struct enc_wave_param wave_param; + enum packed_format_num packed_format; /* <<vpuapi_h_packed_format_num>> */ + enum frame_buffer_format src_format; + bool line_buf_int_en; + u32 rc_enable : 1; /* rate control */ +}; + +struct enc_initial_info { + u32 min_frame_buffer_count; /* minimum number of frame buffers */ + u32 min_src_frame_count; /* minimum number of source buffers */ + u32 seq_init_err_reason; + u32 warn_info; + u32 vlc_buf_size; /* size of task buffer */ + u32 param_buf_size; /* size of task buffer */ +}; + +/* + * Flags to encode NAL units explicitly + */ +struct enc_code_opt { + u32 implicit_header_encode: 1; + u32 encode_vcl: 1; + u32 encode_vps: 1; + u32 encode_sps: 1; + u32 encode_pps: 1; + u32 encode_aud: 1; + u32 encode_eos: 1; + u32 encode_eob: 1; + u32 encode_vui: 1; +}; + +struct enc_param { + struct frame_buffer *source_frame; + u32 pic_stream_buffer_addr; + u64 pic_stream_buffer_size; + u32 src_idx; /* source frame buffer index */ + struct enc_code_opt code_option; + u64 pts; /* presentation timestamp (PTS) of the input source */ + bool src_end_flag; +}; + +struct enc_output_info { + u32 bitstream_buffer; + u32 bitstream_size; /* byte size of encoded bitstream */ + u32 pic_type: 2; /* <<vpuapi_h_pic_type>> */ + s32 recon_frame_index; + dma_addr_t rd_ptr; + dma_addr_t wr_ptr; + u32 enc_pic_byte; /* number of encoded picture bytes */ + s32 enc_src_idx; /* source buffer index of the currently encoded picture */ + u32 enc_vcl_nut; + u32 error_reason; /* error reason of the currently encoded picture */ + u32 warn_info; /* warning information on the currently encoded picture */ + unsigned int frame_cycle; /* param for reporting the cycle number of encoding one frame*/ + u64 pts; + u32 enc_host_cmd_tick; /* tick of ENC_PIC command for the picture */ + u32 enc_encode_end_tick; /* end tick of encoding slices of the picture */ +}; + +enum enc_pic_code_option { + CODEOPT_ENC_HEADER_IMPLICIT = BIT(0), + CODEOPT_ENC_VCL = BIT(1), /* flag to encode VCL nal unit explicitly */ +}; + +enum gop_preset_idx { + PRESET_IDX_CUSTOM_GOP = 0, /* user defined GOP structure */ + PRESET_IDX_ALL_I = 1, /* all intra, gopsize = 1 */ + PRESET_IDX_IPP = 2, /* consecutive P, cyclic gopsize = 1 */ + PRESET_IDX_IBBB = 3, /* consecutive B, cyclic gopsize = 1 */ + PRESET_IDX_IBPBP = 4, /* gopsize = 2 */ + PRESET_IDX_IBBBP = 5, /* gopsize = 4 */ + PRESET_IDX_IPPPP = 6, /* consecutive P, cyclic gopsize = 4 */ + PRESET_IDX_IBBBB = 7, /* consecutive B, cyclic gopsize = 4 */ + PRESET_IDX_RA_IB = 8, /* random access, cyclic gopsize = 8 */ + PRESET_IDX_IPP_SINGLE = 9, /* consecutive P, cyclic gopsize = 1, with single ref */ +}; + +struct sec_axi_info { + u32 use_ip_enable; + u32 use_bit_enable; + u32 use_lf_row_enable: 1; + u32 use_enc_rdo_enable: 1; + u32 use_enc_lf_enable: 1; +}; + +struct dec_info { + struct dec_open_param open_param; + struct dec_initial_info initial_info; + struct dec_initial_info new_seq_info; /* temporal new sequence information */ + u32 stream_wr_ptr; + u32 stream_rd_ptr; + u32 frame_display_flag; + dma_addr_t stream_buf_start_addr; + dma_addr_t stream_buf_end_addr; + u32 stream_buf_size; + struct vpu_buf vb_mv[MAX_REG_FRAME]; + struct vpu_buf vb_fbc_y_tbl[MAX_REG_FRAME]; + struct vpu_buf vb_fbc_c_tbl[MAX_REG_FRAME]; + unsigned int num_of_decoding_fbs: 7; + unsigned int num_of_display_fbs: 7; + unsigned int stride; + struct sec_axi_info sec_axi_info; + dma_addr_t user_data_buf_addr; + u32 user_data_enable; + u32 user_data_buf_size; + struct vpu_buf vb_work; + struct vpu_buf vb_task; + struct dec_output_info dec_out_info[WAVE5_MAX_FBS]; + u32 seq_change_mask; + enum temporal_id_mode temp_id_select_mode; + u32 target_temp_id; + u32 target_spatial_id; + u32 instance_queue_count; + u32 report_queue_count; + u32 cycle_per_tick; + u32 product_code; + u32 vlc_buf_size; + u32 param_buf_size; + bool initial_info_obtained; + bool reorder_enable; + bool first_cycle_check; + u32 stream_endflag: 1; +}; + +struct enc_info { + struct enc_open_param open_param; + struct enc_initial_info initial_info; + u32 stream_rd_ptr; + u32 stream_wr_ptr; + dma_addr_t stream_buf_start_addr; + dma_addr_t stream_buf_end_addr; + u32 stream_buf_size; + unsigned int num_frame_buffers; + unsigned int stride; + bool rotation_enable; + bool mirror_enable; + enum mirror_direction mirror_direction; + unsigned int rotation_angle; + bool initial_info_obtained; + struct sec_axi_info sec_axi_info; + bool line_buf_int_en; + struct vpu_buf vb_work; + struct vpu_buf vb_mv; /* col_mv buffer */ + struct vpu_buf vb_fbc_y_tbl; /* FBC luma table buffer */ + struct vpu_buf vb_fbc_c_tbl; /* FBC chroma table buffer */ + struct vpu_buf vb_sub_sam_buf; /* sub-sampled buffer for ME */ + struct vpu_buf vb_task; + u64 cur_pts; /* current timestamp in 90_k_hz */ + u64 pts_map[32]; /* PTS mapped with source frame index */ + u32 instance_queue_count; + u32 report_queue_count; + bool first_cycle_check; + u32 cycle_per_tick; + u32 product_code; + u32 vlc_buf_size; + u32 param_buf_size; +}; + +struct vpu_device { + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *v4l2_m2m_dec_dev; + struct v4l2_m2m_dev *v4l2_m2m_enc_dev; + struct list_head instances; + struct video_device *video_dev_dec; + struct video_device *video_dev_enc; + struct mutex dev_lock; /* lock for the src, dst v4l2 queues */ + struct mutex hw_lock; /* lock hw configurations */ + int irq; + enum product_id product; + struct vpu_attr attr; + struct vpu_buf common_mem; + u32 last_performance_cycles; + u32 sram_size; + struct gen_pool *sram_pool; + struct vpu_buf sram_buf; + void __iomem *vdb_register; + u32 product_code; + struct ida inst_ida; + struct clk_bulk_data *clks; + int num_clks; +}; + +struct vpu_instance; + +struct vpu_instance_ops { + void (*finish_process)(struct vpu_instance *inst); +}; + +struct vpu_instance { + struct list_head list; + struct v4l2_fh v4l2_fh; + struct v4l2_m2m_dev *v4l2_m2m_dev; + struct v4l2_ctrl_handler v4l2_ctrl_hdl; + struct vpu_device *dev; + struct completion irq_done; + + struct v4l2_pix_format_mplane src_fmt; + struct v4l2_pix_format_mplane dst_fmt; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + + enum vpu_instance_state state; + enum vpu_instance_type type; + const struct vpu_instance_ops *ops; + spinlock_t state_spinlock; /* This protects the instance state */ + + enum wave_std std; + s32 id; + union { + struct enc_info enc_info; + struct dec_info dec_info; + } *codec_info; + struct frame_buffer frame_buf[MAX_REG_FRAME]; + struct vpu_buf frame_vbuf[MAX_REG_FRAME]; + u32 fbc_buf_count; + u32 queued_src_buf_num; + u32 queued_dst_buf_num; + struct list_head avail_src_bufs; + struct list_head avail_dst_bufs; + struct v4l2_rect conf_win; + u64 timestamp; + enum frame_buffer_format output_format; + bool cbcr_interleave; + bool nv21; + bool eos; + struct vpu_buf bitstream_vbuf; + dma_addr_t last_rd_ptr; + size_t remaining_consumed_bytes; + bool needs_reallocation; + + unsigned int min_src_buf_count; + unsigned int rot_angle; + unsigned int mirror_direction; + unsigned int bit_depth; + unsigned int frame_rate; + unsigned int vbv_buf_size; + unsigned int rc_mode; + unsigned int rc_enable; + unsigned int bit_rate; + unsigned int encode_aud; + struct enc_wave_param enc_param; +}; + +void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data); +u32 wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr); +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb); +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb); +int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count, + size_t size); +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset, + u8 *data, size_t len); +int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb); +void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev); +void wave5_vdi_free_sram(struct vpu_device *vpu_dev); + +int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size); +int wave5_vpu_flush_instance(struct vpu_instance *inst); +int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id); +int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param); +int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res); +int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst); +int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info); +int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs, + int num_of_display_fbs, int stride, int height); +int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail); +int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info); +int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr); +dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst); +int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index); +int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter); +int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr, + dma_addr_t *pwr_ptr, size_t *size); +int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size); +int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index); +int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index); + +int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param); +int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res); +int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst); +int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info); +int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num, + unsigned int stride, int height, + enum tiled_map_type map_type); +int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, + u32 *fail_res); +int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info); +int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter); + +#endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h new file mode 100644 index 000000000000..d9751eedb0f9 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - product config definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef _VPU_CONFIG_H_ +#define _VPU_CONFIG_H_ + +#define WAVE517_CODE 0x5170 +#define WAVE537_CODE 0x5370 +#define WAVE511_CODE 0x5110 +#define WAVE521_CODE 0x5210 +#define WAVE521C_CODE 0x521c +#define WAVE521C_DUAL_CODE 0x521d // wave521 dual core +#define WAVE521E1_CODE 0x5211 + +#define PRODUCT_CODE_W_SERIES(x) ({ \ + int c = x; \ + ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \ + (c) == WAVE511_CODE || (c) == WAVE521_CODE || \ + (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \ + (c) == WAVE521C_DUAL_CODE); \ +}) + +#define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024) +#define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K +#define WAVE521DEC_WORKBUF_SIZE (1784 * 1024) + +#define MAX_NUM_INSTANCE 32 + +#define W5_MIN_ENC_PIC_WIDTH 256 +#define W5_MIN_ENC_PIC_HEIGHT 128 +#define W5_MAX_ENC_PIC_WIDTH 8192 +#define W5_MAX_ENC_PIC_HEIGHT 8192 + +// application specific configuration +#define VPU_ENC_TIMEOUT 60000 +#define VPU_DEC_TIMEOUT 60000 + +// for WAVE encoder +#define USE_SRC_PRP_AXI 0 +#define USE_SRC_PRI_AXI 1 +#define DEFAULT_SRC_AXI USE_SRC_PRP_AXI + +/************************************************************************/ +/* VPU COMMON MEMORY */ +/************************************************************************/ +#define VLC_BUF_NUM (2) + +#define COMMAND_QUEUE_DEPTH (2) + +#define W5_REMAP_INDEX0 0 +#define W5_REMAP_INDEX1 1 +#define W5_REMAP_MAX_SIZE (1024 * 1024) + +#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) +#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE +#define WAVE5_TEMPBUF_SIZE (1024 * 1024) + +#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) + +//=====4. VPU REPORT MEMORY ======================// + +#define WAVE5_UPPER_PROC_AXI_ID 0x0 + +#define WAVE5_PROC_AXI_ID 0x0 +#define WAVE5_PRP_AXI_ID 0x0 +#define WAVE5_FBD_Y_AXI_ID 0x0 +#define WAVE5_FBC_Y_AXI_ID 0x0 +#define WAVE5_FBD_C_AXI_ID 0x0 +#define WAVE5_FBC_C_AXI_ID 0x0 +#define WAVE5_SEC_AXI_ID 0x0 +#define WAVE5_PRI_AXI_ID 0x0 + +#endif /* _VPU_CONFIG_H_ */ diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h new file mode 100644 index 000000000000..905d5c34fd4e --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - error values + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef ERROR_CODE_H_INCLUDED +#define ERROR_CODE_H_INCLUDED + +/* + * WAVE5 + */ + +/************************************************************************/ +/* WAVE5 COMMON SYSTEM ERROR (FAIL_REASON) */ +/************************************************************************/ +#define WAVE5_SYSERR_QUEUEING_FAIL 0x00000001 +#define WAVE5_SYSERR_ACCESS_VIOLATION_HW 0x00000040 +#define WAVE5_SYSERR_BUS_ERROR 0x00000200 +#define WAVE5_SYSERR_DOUBLE_FAULT 0x00000400 +#define WAVE5_SYSERR_RESULT_NOT_READY 0x00000800 +#define WAVE5_SYSERR_VPU_STILL_RUNNING 0x00001000 +#define WAVE5_SYSERR_UNKNOWN_CMD 0x00002000 +#define WAVE5_SYSERR_UNKNOWN_CODEC_STD 0x00004000 +#define WAVE5_SYSERR_UNKNOWN_QUERY_OPTION 0x00008000 +#define WAVE5_SYSERR_VLC_BUF_FULL 0x00010000 +#define WAVE5_SYSERR_WATCHDOG_TIMEOUT 0x00020000 +#define WAVE5_SYSERR_VCPU_TIMEOUT 0x00080000 +#define WAVE5_SYSERR_TEMP_SEC_BUF_OVERFLOW 0x00200000 +#define WAVE5_SYSERR_NEED_MORE_TASK_BUF 0x00400000 +#define WAVE5_SYSERR_PRESCAN_ERR 0x00800000 +#define WAVE5_SYSERR_ENC_GBIN_OVERCONSUME 0x01000000 +#define WAVE5_SYSERR_ENC_MAX_ZERO_DETECT 0x02000000 +#define WAVE5_SYSERR_ENC_LVL_FIRST_ERROR 0x04000000 +#define WAVE5_SYSERR_ENC_EG_RANGE_OVER 0x08000000 +#define WAVE5_SYSERR_ENC_IRB_FRAME_DROP 0x10000000 +#define WAVE5_SYSERR_INPLACE_V 0x20000000 +#define WAVE5_SYSERR_FATAL_VPU_HANGUP 0xf0000000 + +/************************************************************************/ +/* WAVE5 COMMAND QUEUE ERROR (FAIL_REASON) */ +/************************************************************************/ +#define WAVE5_CMDQ_ERR_NOT_QUEABLE_CMD 0x00000001 +#define WAVE5_CMDQ_ERR_SKIP_MODE_ENABLE 0x00000002 +#define WAVE5_CMDQ_ERR_INST_FLUSHING 0x00000003 +#define WAVE5_CMDQ_ERR_INST_INACTIVE 0x00000004 +#define WAVE5_CMDQ_ERR_QUEUE_FAIL 0x00000005 +#define WAVE5_CMDQ_ERR_CMD_BUF_FULL 0x00000006 + +/************************************************************************/ +/* WAVE5 ERROR ON DECODER (ERR_INFO) */ +/************************************************************************/ +// HEVC +#define HEVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000 +#define HEVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001 +#define HEVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002 +#define HEVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003 +#define HEVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004 +#define HEVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005 +#define HEVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006 +#define HEVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007 +#define HEVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008 +#define HEVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009 +#define HEVC_SPSERR_LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 0x0000100A +#define HEVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B +#define HEVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C +#define HEVC_SPSERR_SPS_MAX_LATENCY_INCREASE 0x0000100D +#define HEVC_SPSERR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3 0x0000100E +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE 0x0000100F +#define HEVC_SPSERR_LOG2_MIN_TRANSFORM_BLOCK_SIZE_MINUS2 0x00001010 +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_TRANSFORM_BLOCK_SIZE 0x00001011 +#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTER 0x00001012 +#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA 0x00001013 +#define HEVC_SPSERR_SCALING_LIST 0x00001014 +#define HEVC_SPSERR_LOG2_DIFF_MIN_PCM_LUMA_CODING_BLOCK_SIZE_MINUS3 0x00001015 +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE 0x00001016 +#define HEVC_SPSERR_NUM_SHORT_TERM_REF_PIC_SETS 0x00001017 +#define HEVC_SPSERR_NUM_LONG_TERM_REF_PICS_SPS 0x00001018 +#define HEVC_SPSERR_GBU_PARSING_ERROR 0x00001019 +#define HEVC_SPSERR_EXTENSION_FLAG 0x0000101A +#define HEVC_SPSERR_VUI_ERROR 0x0000101B +#define HEVC_SPSERR_ACTIVATE_SPS 0x0000101C +#define HEVC_SPSERR_PROFILE_SPACE 0x0000101D +#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000 +#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001 +#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002 +#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003 +#define HEVC_PPSERR_INIT_QP_MINUS26 0x00002004 +#define HEVC_PPSERR_DIFF_CU_QP_DELTA_DEPTH 0x00002005 +#define HEVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006 +#define HEVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007 +#define HEVC_PPSERR_NUM_TILE_COLUMNS_MINUS1 0x00002008 +#define HEVC_PPSERR_NUM_TILE_ROWS_MINUS1 0x00002009 +#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1 0x0000200A +#define HEVC_PPSERR_ROW_HEIGHT_MINUS1 0x0000200B +#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2 0x0000200C +#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2 0x0000200D +#define HEVC_PPSERR_SCALING_LIST 0x0000200E +#define HEVC_PPSERR_LOG2_PARALLEL_MERGE_LEVEL_MINUS2 0x0000200F +#define HEVC_PPSERR_NUM_TILE_COLUMNS_RANGE_OUT 0x00002010 +#define HEVC_PPSERR_NUM_TILE_ROWS_RANGE_OUT 0x00002011 +#define HEVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012 +#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013 +#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014 +#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015 +#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016 +#define HEVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017 +#define HEVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018 +#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1_RANGE_OUT 0x00002019 +#define HEVC_PPSERR_ROW_HEIGHT_MINUS1_RANGE_OUT 0x00002020 +#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2_RANGE_OUT 0x00002021 +#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2_RANGE_OUT 0x00002022 +#define HEVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000 +#define HEVC_SHERR_ACTIVATE_PPS 0x00003001 +#define HEVC_SHERR_ACTIVATE_SPS 0x00003002 +#define HEVC_SHERR_SLICE_TYPE 0x00003003 +#define HEVC_SHERR_FIRST_SLICE_IS_DEPENDENT_SLICE 0x00003004 +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_SPS_FLAG 0x00003005 +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET 0x00003006 +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_IDX 0x00003007 +#define HEVC_SHERR_NUM_LONG_TERM_SPS 0x00003008 +#define HEVC_SHERR_NUM_LONG_TERM_PICS 0x00003009 +#define HEVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A +#define HEVC_SHERR_DELTA_POC_MSB_CYCLE_LT 0x0000300B +#define HEVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C +#define HEVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D +#define HEVC_SHERR_COLLOCATED_REF_IDX 0x0000300E +#define HEVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F +#define HEVC_SHERR_FIVE_MINUS_MAX_NUM_MERGE_CAND 0x00003010 +#define HEVC_SHERR_SLICE_QP_DELTA 0x00003011 +#define HEVC_SHERR_SLICE_QP_DELTA_IS_OUT_OF_RANGE 0x00003012 +#define HEVC_SHERR_SLICE_CB_QP_OFFSET 0x00003013 +#define HEVC_SHERR_SLICE_CR_QP_OFFSET 0x00003014 +#define HEVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015 +#define HEVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016 +#define HEVC_SHERR_NUM_ENTRY_POINT_OFFSETS 0x00003017 +#define HEVC_SHERR_OFFSET_LEN_MINUS1 0x00003018 +#define HEVC_SHERR_SLICE_SEGMENT_HEADER_EXTENSION_LENGTH 0x00003019 +#define HEVC_SHERR_WRONG_POC_IN_STILL_PICTURE_PROFILE 0x0000301A +#define HEVC_SHERR_SLICE_TYPE_ERROR_IN_STILL_PICTURE_PROFILE 0x0000301B +#define HEVC_SHERR_PPS_ID_NOT_EQUAL_PREV_VALUE 0x0000301C +#define HEVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000 +#define HEVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001 +#define HEVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002 +#define HEVC_SPECERR_OVER_BIT_DEPTH 0x00004003 +#define HEVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004 +#define HEVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005 +#define HEVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000 +#define HEVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001 +#define HEVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002 +#define HEVC_ETCERR_INPLACE_V 0x0000500F + +// AVC +#define AVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000 +#define AVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001 +#define AVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002 +#define AVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003 +#define AVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004 +#define AVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005 +#define AVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006 +#define AVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007 +#define AVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008 +#define AVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009 +#define AVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B +#define AVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C +#define AVC_SPSERR_SCALING_LIST 0x00001014 +#define AVC_SPSERR_GBU_PARSING_ERROR 0x00001019 +#define AVC_SPSERR_VUI_ERROR 0x0000101B +#define AVC_SPSERR_ACTIVATE_SPS 0x0000101C +#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000 +#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001 +#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002 +#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003 +#define AVC_PPSERR_INIT_QP_MINUS26 0x00002004 +#define AVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006 +#define AVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007 +#define AVC_PPSERR_SCALING_LIST 0x0000200E +#define AVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012 +#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013 +#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014 +#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015 +#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016 +#define AVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017 +#define AVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018 +#define AVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000 +#define AVC_SHERR_ACTIVATE_PPS 0x00003001 +#define AVC_SHERR_ACTIVATE_SPS 0x00003002 +#define AVC_SHERR_SLICE_TYPE 0x00003003 +#define AVC_SHERR_FIRST_MB_IN_SLICE 0x00003004 +#define AVC_SHERR_RPLM 0x00003006 +#define AVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A +#define AVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C +#define AVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D +#define AVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F +#define AVC_SHERR_SLICE_QP_DELTA 0x00003011 +#define AVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015 +#define AVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016 +#define AVC_SHERR_DISABLE_DEBLOCK_FILTER_IDC 0x00003017 +#define AVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000 +#define AVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001 +#define AVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002 +#define AVC_SPECERR_OVER_BIT_DEPTH 0x00004003 +#define AVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004 +#define AVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005 +#define AVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000 +#define AVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001 +#define AVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002 +#define AVC_ETCERR_ASO 0x00005004 +#define AVC_ETCERR_FMO 0x00005005 +#define AVC_ETCERR_INPLACE_V 0x0000500F + +/************************************************************************/ +/* WAVE5 WARNING ON DECODER (WARN_INFO) */ +/************************************************************************/ +// HEVC +#define HEVC_SPSWARN_MAX_SUB_LAYERS_MINUS1 0x00000001 +#define HEVC_SPSWARN_GENERAL_RESERVED_ZERO_44BITS 0x00000002 +#define HEVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004 +#define HEVC_SPSWARN_SUB_LAYER_RESERVED_ZERO_44BITS 0x00000008 +#define HEVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010 +#define HEVC_SPSWARN_SPS_MAX_DEC_PIC_BUFFERING_VALUE_OVER 0x00000020 +#define HEVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040 +#define HEVC_SPSWARN_ST_RPS_UE_ERROR 0x00000080 +#define HEVC_SPSWARN_EXTENSION_FLAG 0x01000000 +#define HEVC_SPSWARN_REPLACED_WITH_PREV_SPS 0x02000000 +#define HEVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100 +#define HEVC_PPSWARN_REPLACED_WITH_PREV_PPS 0x00000200 +#define HEVC_SHWARN_FIRST_SLICE_SEGMENT_IN_PIC_FLAG 0x00001000 +#define HEVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000 +#define HEVC_SHWARN_PIC_OUTPUT_FLAG 0x00004000 +#define HEVC_SHWARN_DUPLICATED_SLICE_SEGMENT 0x00008000 +#define HEVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000 +#define HEVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000 +#define HEVC_ETCWARN_WRONG_TEMPORAL_ID 0x00040000 +#define HEVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000 +#define HEVC_SPECWARN_OVER_PROFILE 0x00100000 +#define HEVC_SPECWARN_OVER_LEVEL 0x00200000 +#define HEVC_PRESWARN_PARSING_ERR 0x04000000 +#define HEVC_PRESWARN_MVD_OUT_OF_RANGE 0x08000000 +#define HEVC_PRESWARN_CU_QP_DELTA_VAL_OUT_OF_RANGE 0x09000000 +#define HEVC_PRESWARN_COEFF_LEVEL_REMAINING_OUT_OF_RANGE 0x0A000000 +#define HEVC_PRESWARN_PCM_ERR 0x0B000000 +#define HEVC_PRESWARN_OVERCONSUME 0x0C000000 +#define HEVC_PRESWARN_END_OF_SUBSET_ONE_BIT_ERR 0x10000000 +#define HEVC_PRESWARN_END_OF_SLICE_SEGMENT_FLAG 0x20000000 + +// AVC +#define AVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004 +#define AVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010 +#define AVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040 +#define AVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100 +#define AVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000 +#define AVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000 +#define AVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000 +#define AVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000 +#define AVC_SPECWARN_OVER_PROFILE 0x00100000 +#define AVC_SPECWARN_OVER_LEVEL 0x00200000 +#define AVC_PRESWARN_MVD_RANGE_OUT 0x00400000 +#define AVC_PRESWARN_MB_QPD_RANGE_OUT 0x00500000 +#define AVC_PRESWARN_COEFF_RANGE_OUT 0x00600000 +#define AVC_PRESWARN_MV_RANGE_OUT 0x00700000 +#define AVC_PRESWARN_MB_SKIP_RUN_RANGE_OUT 0x00800000 +#define AVC_PRESWARN_MB_TYPE_RANGE_OUT 0x00900000 +#define AVC_PRESWARN_SUB_MB_TYPE_RANGE_OUT 0x00A00000 +#define AVC_PRESWARN_CBP_RANGE_OUT 0x00B00000 +#define AVC_PRESWARN_INTRA_CHROMA_PRED_MODE_RANGE_OUT 0x00C00000 +#define AVC_PRESWARN_REF_IDX_RANGE_OUT 0x00D00000 +#define AVC_PRESWARN_COEFF_TOKEN_RANGE_OUT 0x00E00000 +#define AVC_PRESWARN_TOTAL_ZERO_RANGE_OUT 0x00F00000 +#define AVC_PRESWARN_RUN_BEFORE_RANGE_OUT 0x01000000 +#define AVC_PRESWARN_OVERCONSUME 0x01100000 +#define AVC_PRESWARN_MISSING_SLICE 0x01200000 + +/************************************************************************/ +/* WAVE5 ERROR ON ENCODER (ERR_INFO) */ +/************************************************************************/ + +/************************************************************************/ +/* WAVE5 WARNING ON ENCODER (WARN_INFO) */ +/************************************************************************/ +#define WAVE5_ETCWARN_FORCED_SPLIT_BY_CU8X8 0x000000001 + +/************************************************************************/ +/* WAVE5 debug info (PRI_REASON) */ +/************************************************************************/ +#define WAVE5_DEC_VCORE_VCE_HANGUP 0x0001 +#define WAVE5_DEC_VCORE_UNDETECTED_SYNTAX_ERR 0x0002 +#define WAVE5_DEC_VCORE_MIB_BUSY 0x0003 +#define WAVE5_DEC_VCORE_VLC_BUSY 0x0004 + +#endif /* ERROR_CODE_H_INCLUDED */ diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h new file mode 100644 index 000000000000..063028eccd3b --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - wave5 backend definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef __WAVE5_FUNCTION_H__ +#define __WAVE5_FUNCTION_H__ + +#define WAVE5_SUBSAMPLED_ONE_SIZE(_w, _h) (ALIGN((_w) / 4, 16) * ALIGN((_h) / 4, 8)) +#define WAVE5_SUBSAMPLED_ONE_SIZE_AVC(_w, _h) (ALIGN((_w) / 4, 32) * ALIGN((_h) / 4, 4)) + +/* + * Bitstream buffer option: Explicit End + * When set to 1 the VPU assumes that the bitstream has at least one frame and + * will read until the end of the bitstream buffer. + * When set to 0 the VPU will not read the last few bytes. + * This option can be set anytime but cannot be cleared during processing. + * It can be set to force finish decoding even though there is not enough + * bitstream data for a full frame. + */ +#define BSOPTION_ENABLE_EXPLICIT_END BIT(0) +#define BSOPTION_HIGHLIGHT_STREAM_END BIT(1) + +/* + * Currently the driver only supports hardware with little endian but for source + * picture format, the bitstream and the report parameter the hardware works + * with the opposite endianness, thus hard-code big endian for the register + * writes + */ +#define PIC_SRC_ENDIANNESS_BIG_ENDIAN 0xf +#define BITSTREAM_ENDIANNESS_BIG_ENDIAN 0xf +#define REPORT_PARAM_ENDIANNESS_BIG_ENDIAN 0xf + +#define WTL_RIGHT_JUSTIFIED 0 +#define WTL_LEFT_JUSTIFIED 1 +#define WTL_PIXEL_8BIT 0 +#define WTL_PIXEL_16BIT 1 +#define WTL_PIXEL_32BIT 2 + +/* Mirror & rotation modes of the PRP (pre-processing) module */ +#define NONE_ROTATE 0x0 +#define ROT_CLOCKWISE_90 0x3 +#define ROT_CLOCKWISE_180 0x5 +#define ROT_CLOCKWISE_270 0x7 +#define MIR_HOR_FLIP 0x11 +#define MIR_VER_FLIP 0x9 +#define MIR_HOR_VER_FLIP (MIR_HOR_FLIP | MIR_VER_FLIP) + +bool wave5_vpu_is_init(struct vpu_device *vpu_dev); + +unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev); + +int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision); + +int wave5_vpu_init(struct device *dev, u8 *fw, size_t size); + +int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode); + +int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, struct dec_open_param *param); + +int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos); + +int wave5_vpu_hw_flush_instance(struct vpu_instance *inst); + +int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, + struct frame_buffer *fb_arr, enum tiled_map_type map_type, + unsigned int count); + +int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size); + +int wave5_vpu_dec_init_seq(struct vpu_instance *inst); + +int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info); + +int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res); + +int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result); + +int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res); + +int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index); + +int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index); + +int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags); + +dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst); + +int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr); + +/***< WAVE5 encoder >******/ + +int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, + struct enc_open_param *open_param); + +int wave5_vpu_enc_init_seq(struct vpu_instance *inst); + +int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info); + +int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst, + struct frame_buffer *fb_arr, enum tiled_map_type map_type, + unsigned int count); + +int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res); + +int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result); + +int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res); + +int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param); + +#endif /* __WAVE5_FUNCTION_H__ */ diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 7194f88edc0f..ac48658e2de4 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -598,12 +598,11 @@ static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) goto end; vq = v4l2_m2m_get_vq(fh->m2m_ctx, buf->type); - if (buf->index >= vq->num_buffers) { - dev_err(ctx->jpeg->dev, "buffer index out of range\n"); + vb = vb2_get_buffer(vq, buf->index); + if (!vb) { + dev_err(ctx->jpeg->dev, "buffer not found\n"); return -EINVAL; } - - vb = vq->bufs[buf->index]; jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb); jpeg_src_buf->bs_size = buf->m.planes[0].bytesused; @@ -1021,13 +1020,13 @@ static void mtk_jpeg_dec_device_run(void *priv) if (ret < 0) goto dec_end; - schedule_delayed_work(&jpeg->job_timeout_work, - msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); - mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs); if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb)) goto dec_end; + schedule_delayed_work(&jpeg->job_timeout_work, + msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); + spin_lock_irqsave(&jpeg->hw_lock, flags); mtk_jpeg_dec_reset(jpeg->reg_base); mtk_jpeg_dec_set_config(jpeg->reg_base, @@ -1403,7 +1402,6 @@ static void mtk_jpeg_remove(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev); - cancel_delayed_work_sync(&jpeg->job_timeout_work); pm_runtime_disable(&pdev->dev); video_unregister_device(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); @@ -1750,9 +1748,6 @@ retry_select: v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - schedule_delayed_work(&comp_jpeg[hw_id]->job_timeout_work, - msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); - mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs); if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, @@ -1762,6 +1757,9 @@ retry_select: goto setdst_end; } + schedule_delayed_work(&comp_jpeg[hw_id]->job_timeout_work, + msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); + spin_lock_irqsave(&comp_jpeg[hw_id]->hw_lock, flags); ctx->total_frame_num++; mtk_jpeg_dec_reset(comp_jpeg[hw_id]->reg_base); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index cc44be10fdb7..94f4ed78523b 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -208,13 +208,17 @@ static int mdp_probe(struct platform_device *pdev) goto err_destroy_job_wq; } - mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_SCP); - if (WARN_ON(!mm_pdev)) { - dev_err(&pdev->dev, "Could not get scp device\n"); - ret = -ENODEV; - goto err_destroy_clock_wq; + mdp->scp = scp_get(pdev); + if (!mdp->scp) { + mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_SCP); + if (WARN_ON(!mm_pdev)) { + dev_err(&pdev->dev, "Could not get scp device\n"); + ret = -ENODEV; + goto err_destroy_clock_wq; + } + mdp->scp = platform_get_drvdata(mm_pdev); } - mdp->scp = platform_get_drvdata(mm_pdev); + mdp->rproc_handle = scp_get_rproc(mdp->scp); dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp->rproc_handle); diff --git a/drivers/media/platform/mediatek/vcodec/Kconfig b/drivers/media/platform/mediatek/vcodec/Kconfig index 74b00eb1bc97..bc8292232530 100644 --- a/drivers/media/platform/mediatek/vcodec/Kconfig +++ b/drivers/media/platform/mediatek/vcodec/Kconfig @@ -24,7 +24,6 @@ config VIDEO_MEDIATEK_VCODEC select V4L2_H264 select V4L2_VP9 select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API help Mediatek video codec driver provides HW capability to encode and decode in a range of video formats on MT8173 diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index 91ed576d6821..ba742f0e391d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -208,36 +208,14 @@ static int vidioc_vdec_dqbuf(struct file *file, void *priv, return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } -static int mtk_vcodec_dec_get_chip_name(void *priv) -{ - struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); - struct device *dev = &ctx->dev->plat_dev->dev; - - if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-dec")) - return 8173; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-dec")) - return 8183; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-dec")) - return 8192; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-dec")) - return 8195; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8186-vcodec-dec")) - return 8186; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-dec")) - return 8188; - else - return 8173; -} - static int vidioc_vdec_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct device *dev = &ctx->dev->plat_dev->dev; - int platform_name = mtk_vcodec_dec_get_chip_name(priv); strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); - snprintf(cap->card, sizeof(cap->card), "MT%d video decoder", platform_name); + snprintf(cap->card, sizeof(cap->card), "MT%d video decoder", ctx->dev->chip_name); return 0; } diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c index 0a89ce452ac3..f47c98faf068 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c @@ -326,6 +326,26 @@ static const struct v4l2_file_operations mtk_vcodec_fops = { .mmap = v4l2_m2m_fop_mmap, }; +static void mtk_vcodec_dec_get_chip_name(struct mtk_vcodec_dec_dev *vdec_dev) +{ + struct device *dev = &vdec_dev->plat_dev->dev; + + if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8173; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8183; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8192; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8195; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8186-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8186; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8188; + else + vdec_dev->chip_name = MTK_VDEC_INVAL; +} + static int mtk_vcodec_probe(struct platform_device *pdev) { struct mtk_vcodec_dec_dev *dev; @@ -341,6 +361,12 @@ static int mtk_vcodec_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dev->ctx_list); dev->plat_dev = pdev; + mtk_vcodec_dec_get_chip_name(dev); + if (dev->chip_name == MTK_VDEC_INVAL) { + dev_err(&pdev->dev, "Failed to get decoder chip name"); + return -EINVAL; + } + dev->vdec_pdata = of_device_get_match_data(&pdev->dev); if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", &rproc_phandle)) { diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h index 7e36b2c69b7d..849b89dd205c 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h @@ -18,6 +18,16 @@ #define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) #define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING) +enum mtk_vcodec_dec_chip_name { + MTK_VDEC_INVAL = 0, + MTK_VDEC_MT8173 = 8173, + MTK_VDEC_MT8183 = 8183, + MTK_VDEC_MT8186 = 8186, + MTK_VDEC_MT8188 = 8188, + MTK_VDEC_MT8192 = 8192, + MTK_VDEC_MT8195 = 8195, +}; + /* * enum mtk_vdec_format_types - Structure used to get supported * format types according to decoder capability @@ -249,6 +259,8 @@ struct mtk_vcodec_dec_ctx { * @vdec_racing_info: record register value * @dec_racing_info_mutex: mutex lock used for inner racing mode * @dbgfs: debug log related information + * + * @chip_name: used to distinguish platforms and select the correct codec configuration values */ struct mtk_vcodec_dec_dev { struct v4l2_device v4l2_dev; @@ -289,6 +301,8 @@ struct mtk_vcodec_dec_dev { /* Protects access to vdec_racing_info data */ struct mutex dec_racing_info_mutex; struct mtk_vcodec_dbgfs dbgfs; + + enum mtk_vcodec_dec_chip_name chip_name; }; static inline struct mtk_vcodec_dec_ctx *fh_to_dec_ctx(struct v4l2_fh *fh) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c index e29c9c58f3da..d54b3833790d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c @@ -58,6 +58,15 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { }, { .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .def = V4L2_MPEG_VIDEO_H264_LEVEL_4_1, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { .id = V4L2_CID_STATELESS_H264_DECODE_MODE, .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, @@ -100,7 +109,17 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { .id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE, .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, .def = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - .max = V4L2_MPEG_VIDEO_VP9_PROFILE_3, + .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, + .menu_skip_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_1), + }, + .codec_type = V4L2_PIX_FMT_VP9_FRAME, + }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_VP9_LEVEL, + .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0, + .def = V4L2_MPEG_VIDEO_VP9_LEVEL_4_0, + .max = V4L2_MPEG_VIDEO_VP9_LEVEL_4_1, }, .codec_type = V4L2_PIX_FMT_VP9_FRAME, }, @@ -140,6 +159,16 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { }, { .cfg = { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .def = V4L2_MPEG_VIDEO_HEVC_LEVEL_4, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + + { + .cfg = { .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, .min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, .def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, @@ -519,6 +548,141 @@ static const struct v4l2_ctrl_ops mtk_vcodec_dec_ctrl_ops = { .s_ctrl = mtk_vdec_s_ctrl, }; +static void mtk_vcodec_dec_fill_h264_level(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8192: + case MTK_VDEC_MT8188: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_5_2; + break; + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0; + break; + case MTK_VDEC_MT8183: + case MTK_VDEC_MT8186: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + break; + }; +} + +static void mtk_vcodec_dec_fill_h264_profile(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + break; + }; +} + +static void mtk_vcodec_dec_fill_h265_level(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1; + break; + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_4; + break; + }; +} + +static void mtk_vcodec_dec_fill_h265_profile(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE; + break; + }; +} + +static void mtk_vcodec_dec_fill_vp9_level(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8192: + case MTK_VDEC_MT8188: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_1; + break; + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_2; + break; + case MTK_VDEC_MT8186: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_4_1; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_4_0; + break; + }; +} + +static void mtk_vcodec_dec_fill_vp9_profile(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_VP9_PROFILE_2; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_VP9_PROFILE_1; + break; + }; +} + +static void mtk_vcodec_dec_reset_controls(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (cfg->id) { + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + mtk_vcodec_dec_fill_h264_level(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h264 supported level: %lld %lld", cfg->max, cfg->def); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + mtk_vcodec_dec_fill_h265_level(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h265 supported level: %lld %lld", cfg->max, cfg->def); + break; + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: + mtk_vcodec_dec_fill_vp9_level(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "vp9 supported level: %lld %lld", cfg->max, cfg->def); + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + mtk_vcodec_dec_fill_h264_profile(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h264 supported profile: %lld %lld", cfg->max, + cfg->menu_skip_mask); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + mtk_vcodec_dec_fill_h265_profile(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h265 supported profile: %lld %lld", cfg->max, + cfg->menu_skip_mask); + break; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + mtk_vcodec_dec_fill_vp9_profile(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "vp9 supported profile: %lld %lld", cfg->max, + cfg->menu_skip_mask); + break; + default: + break; + }; +} + static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) { unsigned int i; @@ -532,6 +696,8 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) for (i = 0; i < NUM_CTRLS; i++) { struct v4l2_ctrl_config cfg = mtk_stateless_controls[i].cfg; cfg.ops = &mtk_vcodec_dec_ctrl_ops; + + mtk_vcodec_dec_reset_controls(&cfg, ctx); v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &cfg, NULL); if (ctx->ctrl_hdl.error) { mtk_v4l2_vdec_err(ctx, "Adding control %d failed %d", i, diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c index e393e3e668f8..69d37b93bd35 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c @@ -1695,13 +1695,8 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst return -EINVAL; /* update internal buffer's width/height */ - for (i = 0; i < vq->num_buffers; i++) { - if (vb == vq->bufs[i]) { - instance->dpb[i].width = w; - instance->dpb[i].height = h; - break; - } - } + instance->dpb[vb->index].width = w; + instance->dpb[vb->index].height = h; /* * get buffer's width/height from instance diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c index eb381fa6e7d1..181884e798fd 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c @@ -912,7 +912,7 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) return 0; err_start_stream: - for (i = 0; i < q->num_buffers; ++i) { + for (i = 0; i < vb2_get_num_buffers(q); ++i) { struct vb2_buffer *buf = vb2_get_buffer(q, i); /* diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index 988c1cc1d8b6..fee73260bb1e 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -232,8 +232,8 @@ static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -281,13 +281,12 @@ static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd, req_fmt->format.field = V4L2_FIELD_NONE; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, - sd_state, - CSI2DC_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + CSI2DC_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; /* if we are just trying, we are done */ @@ -436,11 +435,11 @@ static int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable) return ret; } -static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, - struct v4l2_subdev_state *sd_state) +static int csi2dc_init_state(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); v4l2_try_fmt->height = 480; v4l2_try_fmt->width = 640; @@ -462,7 +461,6 @@ static const struct v4l2_subdev_pad_ops csi2dc_pad_ops = { .enum_mbus_code = csi2dc_enum_mbus_code, .set_fmt = csi2dc_set_fmt, .get_fmt = csi2dc_get_fmt, - .init_cfg = csi2dc_init_cfg, }; static const struct v4l2_subdev_video_ops csi2dc_video_ops = { @@ -474,6 +472,10 @@ static const struct v4l2_subdev_ops csi2dc_subdev_ops = { .video = &csi2dc_video_ops, }; +static const struct v4l2_subdev_internal_ops csi2dc_internal_ops = { + .init_state = csi2dc_init_state, +}; + static int csi2dc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) @@ -678,6 +680,7 @@ static int csi2dc_probe(struct platform_device *pdev) } v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops); + csi2dc->csi2dc_sd.internal_ops = &csi2dc_internal_ops; csi2dc->csi2dc_sd.owner = THIS_MODULE; csi2dc->csi2dc_sd.dev = dev; diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index 1f8528844497..f3a5cbacadbe 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -851,38 +851,6 @@ static int isc_try_configure_pipeline(struct isc_device *isc) return 0; } -static void isc_try_fse(struct isc_device *isc, - struct v4l2_subdev_state *sd_state) -{ - struct v4l2_subdev_frame_size_enum fse = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - int ret; - - /* - * If we do not know yet which format the subdev is using, we cannot - * do anything. - */ - if (!isc->config.sd_format) - return; - - fse.code = isc->try_config.sd_format->mbus_code; - - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - sd_state, &fse); - /* - * Attempt to obtain format size from subdev. If not available, - * just use the maximum ISC can receive. - */ - if (ret) { - sd_state->pads->try_crop.width = isc->max_width; - sd_state->pads->try_crop.height = isc->max_height; - } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; - } -} - static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f) { struct v4l2_pix_format *pixfmt = &f->fmt.pix; @@ -944,10 +912,6 @@ static int isc_validate(struct isc_device *isc) .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = isc->remote_pad, }; - struct v4l2_subdev_pad_config pad_cfg = {}; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, - }; /* Get current format from subdev */ ret = v4l2_subdev_call(isc->current_subdev->sd, pad, get_fmt, NULL, @@ -1008,9 +972,6 @@ static int isc_validate(struct isc_device *isc) if (ret) return ret; - /* Obtain frame sizes if possible to have crop requirements ready */ - isc_try_fse(isc, &pad_state); - /* Configure ISC pipeline for the config */ ret = isc_try_configure_pipeline(isc); if (ret) @@ -1819,7 +1780,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &isc->lock; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->dev = isc->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/microchip/microchip-isc-scaler.c b/drivers/media/platform/microchip/microchip-isc-scaler.c index 0f29a32d15ce..e83463543e21 100644 --- a/drivers/media/platform/microchip/microchip-isc-scaler.c +++ b/drivers/media/platform/microchip/microchip-isc-scaler.c @@ -33,8 +33,8 @@ static int isc_scaler_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -74,12 +74,12 @@ static int isc_scaler_set_fmt(struct v4l2_subdev *sd, req_fmt->format.code = fmt->mbus_code; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - ISC_SCALER_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + ISC_SCALER_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; v4l_bound_align_image(&v4l2_try_fmt->width, @@ -145,17 +145,17 @@ static int isc_scaler_g_sel(struct v4l2_subdev *sd, return 0; } -static int isc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int isc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); struct v4l2_rect *try_crop; struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); *v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE]; - try_crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); + try_crop = v4l2_subdev_state_get_crop(sd_state, 0); try_crop->top = 0; try_crop->left = 0; @@ -170,7 +170,6 @@ static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = { .set_fmt = isc_scaler_set_fmt, .get_fmt = isc_scaler_get_fmt, .get_selection = isc_scaler_g_sel, - .init_cfg = isc_scaler_init_cfg, }; static const struct media_entity_operations isc_scaler_entity_ops = { @@ -181,11 +180,16 @@ static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = { .pad = &isc_scaler_pad_ops, }; +static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = { + .init_state = isc_scaler_init_state, +}; + int isc_scaler_init(struct isc_device *isc) { int ret; v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops); + isc->scaler_sd.internal_ops = &isc_scaler_internal_ops; isc->scaler_sd.owner = THIS_MODULE; isc->scaler_sd.dev = isc->dev; diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index b9e6782f59b4..a1fcb616b256 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -26,7 +26,6 @@ #include <linux/regmap.h> #include <linux/reset.h> #include <linux/sched.h> -#include <linux/spinlock.h> #include <linux/string.h> #include <linux/v4l2-controls.h> #include <linux/videodev2.h> @@ -120,7 +119,7 @@ struct npcm_video { struct mutex video_lock; /* v4l2 and videobuf2 lock */ struct list_head buffers; - spinlock_t lock; /* buffer list lock */ + struct mutex buffer_lock; /* buffer list lock */ unsigned long flags; unsigned int sequence; @@ -393,7 +392,7 @@ static void npcm_video_free_diff_table(struct npcm_video *video) struct rect_list *tmp; unsigned int i; - for (i = 0; i < video->queue.num_buffers; i++) { + for (i = 0; i < vb2_get_num_buffers(&video->queue); i++) { head = &video->list[i]; list_for_each_safe(pos, nx, head) { tmp = list_entry(pos, struct rect_list, list); @@ -782,7 +781,6 @@ static int npcm_video_start_frame(struct npcm_video *video) { struct npcm_video_buffer *buf; struct regmap *vcd = video->vcd_regmap; - unsigned long flags; unsigned int val; int ret; @@ -798,17 +796,17 @@ static int npcm_video_start_frame(struct npcm_video *video) return -EBUSY; } - spin_lock_irqsave(&video->lock, flags); + mutex_lock(&video->buffer_lock); buf = list_first_entry_or_null(&video->buffers, struct npcm_video_buffer, link); if (!buf) { - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); dev_dbg(video->dev, "No empty buffers; skip capture frame\n"); return 0; } set_bit(VIDEO_CAPTURING, &video->flags); - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); npcm_video_vcd_state_machine_reset(video); @@ -834,14 +832,13 @@ static void npcm_video_bufs_done(struct npcm_video *video, enum vb2_buffer_state state) { struct npcm_video_buffer *buf; - unsigned long flags; - spin_lock_irqsave(&video->lock, flags); + mutex_lock(&video->buffer_lock); list_for_each_entry(buf, &video->buffers, link) vb2_buffer_done(&buf->vb.vb2_buf, state); INIT_LIST_HEAD(&video->buffers); - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); } static void npcm_video_get_diff_rect(struct npcm_video *video, unsigned int index) @@ -1071,12 +1068,12 @@ static irqreturn_t npcm_video_irq(int irq, void *arg) if (status & VCD_STAT_DONE) { regmap_write(vcd, VCD_INTE, 0); - spin_lock(&video->lock); + mutex_lock(&video->buffer_lock); clear_bit(VIDEO_CAPTURING, &video->flags); buf = list_first_entry_or_null(&video->buffers, struct npcm_video_buffer, link); if (!buf) { - spin_unlock(&video->lock); + mutex_unlock(&video->buffer_lock); return IRQ_NONE; } @@ -1093,7 +1090,7 @@ static irqreturn_t npcm_video_irq(int irq, void *arg) size = npcm_video_hextile(video, index, dma_addr, addr); break; default: - spin_unlock(&video->lock); + mutex_unlock(&video->buffer_lock); return IRQ_NONE; } @@ -1104,7 +1101,7 @@ static irqreturn_t npcm_video_irq(int irq, void *arg) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); list_del(&buf->link); - spin_unlock(&video->lock); + mutex_unlock(&video->buffer_lock); if (npcm_video_start_frame(video)) dev_err(video->dev, "Failed to capture next frame\n"); @@ -1508,13 +1505,12 @@ static void npcm_video_buf_queue(struct vb2_buffer *vb) struct npcm_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct npcm_video_buffer *nvb = to_npcm_video_buffer(vbuf); - unsigned long flags; bool empty; - spin_lock_irqsave(&video->lock, flags); + mutex_lock(&video->buffer_lock); empty = list_empty(&video->buffers); list_add_tail(&nvb->link, &video->buffers); - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); if (test_bit(VIDEO_STREAMING, &video->flags) && !test_bit(VIDEO_CAPTURING, &video->flags) && empty) { @@ -1616,7 +1612,7 @@ static int npcm_video_setup_video(struct npcm_video *video) vbq->drv_priv = video; vbq->buf_struct_size = sizeof(struct npcm_video_buffer); vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = 3; + vbq->min_queued_buffers = 3; rc = vb2_queue_init(vbq); if (rc) { @@ -1744,8 +1740,8 @@ static int npcm_video_probe(struct platform_device *pdev) return -ENOMEM; video->dev = &pdev->dev; - spin_lock_init(&video->lock); mutex_init(&video->video_lock); + mutex_init(&video->buffer_lock); INIT_LIST_HEAD(&video->buffers); regs = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig index f7454823bbbb..2fe13f39c95b 100644 --- a/drivers/media/platform/nvidia/tegra-vde/Kconfig +++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig @@ -6,7 +6,6 @@ config VIDEO_TEGRA_VDE select DMA_SHARED_BUFFER select IOMMU_IOVA select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API select SRAM select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/nvidia/tegra-vde/v4l2.c b/drivers/media/platform/nvidia/tegra-vde/v4l2.c index bd8c207d5b54..0f48ce6f243e 100644 --- a/drivers/media/platform/nvidia/tegra-vde/v4l2.c +++ b/drivers/media/platform/nvidia/tegra-vde/v4l2.c @@ -813,7 +813,7 @@ static int tegra_open(struct file *file) struct tegra_ctx *ctx; int err; - ctx = kzalloc(offsetof(struct tegra_ctx, ctrls[ARRAY_SIZE(ctrl_cfgs)]), + ctx = kzalloc(struct_size(ctx, ctrls, ARRAY_SIZE(ctrl_cfgs)), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 6cb20b45e0a1..db8ff5f5c4d3 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -954,7 +954,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) state = v4l2_subdev_lock_and_get_active_state(sd); - format = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CSIS_PAD_SINK); csis_fmt = find_csis_format(format->code); ret = mipi_csis_calculate_params(csis, csis_fmt); @@ -1002,7 +1002,7 @@ static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -1069,7 +1069,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, &sdformat->format.height, 1, CSIS_MAX_PIX_HEIGHT, 0, 0); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csis_fmt->code; fmt->width = sdformat->format.width; @@ -1083,7 +1083,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, CSIS_PAD_SOURCE); *fmt = sdformat->format; /* The format on the source pad might change due to unpacking. */ @@ -1104,7 +1104,7 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(state, CSIS_PAD_SOURCE); csis_fmt = find_csis_format(fmt->code); v4l2_subdev_unlock_state(state); @@ -1122,8 +1122,8 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return 0; } -static int mipi_csis_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mipi_csis_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = CSIS_PAD_SINK, @@ -1163,7 +1163,6 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { }; static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { - .init_cfg = mipi_csis_init_cfg, .enum_mbus_code = mipi_csis_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mipi_csis_set_fmt, @@ -1176,6 +1175,10 @@ static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { .pad = &mipi_csis_pad_ops, }; +static const struct v4l2_subdev_internal_ops mipi_csis_internal_ops = { + .init_state = mipi_csis_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -1350,6 +1353,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) int ret; v4l2_subdev_init(sd, &mipi_csis_subdev_ops); + sd->internal_ops = &mipi_csis_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "csis-%s", dev_name(csis->dev)); @@ -1435,24 +1439,18 @@ static int mipi_csis_probe(struct platform_device *pdev) /* Reset PHY and enable the clocks. */ mipi_csis_phy_reset(csis); - ret = mipi_csis_clk_enable(csis); - if (ret < 0) { - dev_err(csis->dev, "failed to enable clocks: %d\n", ret); - return ret; - } - /* Now that the hardware is initialized, request the interrupt. */ ret = devm_request_irq(dev, irq, mipi_csis_irq_handler, 0, dev_name(dev), csis); if (ret) { dev_err(dev, "Interrupt request failed\n"); - goto err_disable_clock; + return ret; } /* Initialize and register the subdev. */ ret = mipi_csis_subdev_init(csis); if (ret < 0) - goto err_disable_clock; + return ret; platform_set_drvdata(pdev, &csis->sd); @@ -1486,8 +1484,6 @@ err_cleanup: v4l2_async_nf_unregister(&csis->notifier); v4l2_async_nf_cleanup(&csis->notifier); v4l2_async_unregister_subdev(&csis->sd); -err_disable_clock: - mipi_csis_clk_disable(csis); return ret; } @@ -1502,9 +1498,10 @@ static void mipi_csis_remove(struct platform_device *pdev) v4l2_async_nf_cleanup(&csis->notifier); v4l2_async_unregister_subdev(&csis->sd); + if (!pm_runtime_enabled(&pdev->dev)) + mipi_csis_runtime_suspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); - mipi_csis_runtime_suspend(&pdev->dev); - mipi_csis_clk_disable(csis); v4l2_subdev_cleanup(&csis->sd); media_entity_cleanup(&csis->sd.entity); pm_runtime_set_suspended(&pdev->dev); diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 15049c6aab37..9566ff738818 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -3,31 +3,46 @@ * V4L2 Capture CSI Subdev for Freescale i.MX6UL/L / i.MX7 SOC * * Copyright (c) 2019 Linaro Ltd - * */ #include <linux/clk.h> +#include <linux/completion.h> +#include <linux/container_of.h> #include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/list.h> #include <linux/math.h> -#include <linux/mfd/syscon.h> #include <linux/minmax.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of.h> -#include <linux/of_graph.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/regmap.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/timekeeping.h> #include <linux/types.h> +#include <media/media-device.h> +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> #include <media/v4l2-device.h> -#include <media/v4l2-fwnode.h> +#include <media/v4l2-fh.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> +#include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> #define IMX7_CSI_PAD_SINK 0 #define IMX7_CSI_PAD_SRC 1 @@ -542,8 +557,8 @@ static void imx7_csi_configure(struct imx7_csi *csi, } else { const struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&csi->sd, sd_state, - IMX7_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SINK); cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN; @@ -1245,6 +1260,7 @@ static int imx7_csi_video_queue_setup(struct vb2_queue *vq, struct device *alloc_devs[]) { struct imx7_csi *csi = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); struct v4l2_pix_format *pix = &csi->vdev_fmt; unsigned int count = *nbuffers; @@ -1254,14 +1270,14 @@ static int imx7_csi_video_queue_setup(struct vb2_queue *vq, if (*nplanes) { if (*nplanes != 1 || sizes[0] < pix->sizeimage) return -EINVAL; - count += vq->num_buffers; + count += q_num_bufs; } count = min_t(__u32, IMX7_CSI_VIDEO_MEM_LIMIT / pix->sizeimage, count); if (*nplanes) - *nbuffers = (count < vq->num_buffers) ? 0 : - count - vq->num_buffers; + *nbuffers = (count < q_num_bufs) ? 0 : + count - q_num_bufs; else *nbuffers = count; @@ -1675,7 +1691,7 @@ static int imx7_csi_video_init(struct imx7_csi *csi) vq->mem_ops = &vb2_dma_contig_memops; vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vq->lock = &csi->vdev_mutex; - vq->min_buffers_needed = 2; + vq->min_queued_buffers = 2; vq->dev = csi->dev; ret = vb2_queue_init(vq); @@ -1728,8 +1744,8 @@ out_unlock: return ret; } -static int imx7_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx7_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct imx7_csi_pixfmt *cc; int i; @@ -1738,7 +1754,7 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd, for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_pad_format(sd, sd_state, i); + v4l2_subdev_state_get_format(sd_state, i); mf->code = IMX7_CSI_DEF_MBUS_CODE; mf->width = IMX7_CSI_DEF_PIX_WIDTH; @@ -1762,7 +1778,7 @@ static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; int ret = 0; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (code->pad) { case IMX7_CSI_PAD_SINK: @@ -1841,7 +1857,7 @@ static void imx7_csi_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; u32 code; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (sdformat->pad) { case IMX7_CSI_PAD_SRC: @@ -1891,7 +1907,7 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, imx7_csi_try_fmt(sd, sd_state, sdformat, &cc); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *fmt = sdformat->format; @@ -1902,8 +1918,8 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, format.format = sdformat->format; imx7_csi_try_fmt(sd, sd_state, &format, &outcc); - outfmt = v4l2_subdev_get_pad_format(sd, sd_state, - IMX7_CSI_PAD_SRC); + outfmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SRC); *outfmt = format.format; } @@ -2005,7 +2021,6 @@ static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { - .init_cfg = imx7_csi_init_cfg, .enum_mbus_code = imx7_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx7_csi_set_fmt, @@ -2018,6 +2033,7 @@ static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { + .init_state = imx7_csi_init_state, .registered = imx7_csi_registered, .unregistered = imx7_csi_unregistered, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 792f031e032a..575f17337388 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -58,7 +58,7 @@ static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, return -EINVAL; } - fmt = v4l2_subdev_state_get_stream_format(state, port, 0); + fmt = v4l2_subdev_state_get_format(state, port, 0); if (!fmt) return -EINVAL; @@ -176,8 +176,8 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, return sd; } -static int mxc_isi_crossbar_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); struct v4l2_subdev_krouting routing = { }; @@ -281,8 +281,7 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, * Set the format on the sink stream and propagate it to the source * streams. */ - sink_fmt = v4l2_subdev_state_get_stream_format(state, fmt->pad, - fmt->stream); + sink_fmt = v4l2_subdev_state_get_format(state, fmt->pad, fmt->stream); if (!sink_fmt) return -EINVAL; @@ -296,8 +295,9 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, route->sink_stream != fmt->stream) continue; - source_fmt = v4l2_subdev_state_get_stream_format(state, route->source_pad, - route->source_stream); + source_fmt = v4l2_subdev_state_get_format(state, + route->source_pad, + route->source_stream); if (!source_fmt) return -EINVAL; @@ -404,7 +404,6 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = { - .init_cfg = mxc_isi_crossbar_init_cfg, .enum_mbus_code = mxc_isi_crossbar_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_crossbar_set_fmt, @@ -417,6 +416,10 @@ static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = { .pad = &mxc_isi_crossbar_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_crossbar_internal_ops = { + .init_state = mxc_isi_crossbar_init_state, +}; + static const struct media_entity_operations mxc_isi_cross_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -438,6 +441,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) xbar->isi = isi; v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops); + sd->internal_ops = &mxc_isi_crossbar_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; strscpy(sd->name, "crossbar", sizeof(sd->name)); sd->dev = isi->dev; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c index 6709ab7ea1f3..5e8a177da054 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c @@ -22,10 +22,11 @@ static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg) static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) { #define MXC_ISI_DEBUG_REG(name) { name, #name } - static const struct { + struct debug_regs { u32 offset; const char * const name; - } registers[] = { + }; + static const struct debug_regs registers[] = { MXC_ISI_DEBUG_REG(CHNL_CTRL), MXC_ISI_DEBUG_REG(CHNL_IMG_CTRL), MXC_ISI_DEBUG_REG(CHNL_OUT_BUF_CTRL), @@ -67,6 +68,16 @@ static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) MXC_ISI_DEBUG_REG(CHNL_SCL_IMG_CFG), MXC_ISI_DEBUG_REG(CHNL_FLOW_CTRL), }; + /* These registers contain the upper 4 bits of 36-bit DMA addresses. */ + static const struct debug_regs registers_36bit_dma[] = { + MXC_ISI_DEBUG_REG(CHNL_Y_BUF1_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_U_BUF1_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_V_BUF1_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_Y_BUF2_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_U_BUF2_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_V_BUF2_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_IN_BUF_XTND_ADDR), + }; struct mxc_isi_pipe *pipe = m->private; unsigned int i; @@ -77,10 +88,20 @@ static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) seq_printf(m, "--- ISI pipe %u registers ---\n", pipe->id); for (i = 0; i < ARRAY_SIZE(registers); ++i) - seq_printf(m, "%20s[0x%02x]: 0x%08x\n", + seq_printf(m, "%21s[0x%02x]: 0x%08x\n", registers[i].name, registers[i].offset, mxc_isi_read(pipe, registers[i].offset)); + if (pipe->isi->pdata->has_36bit_dma) { + for (i = 0; i < ARRAY_SIZE(registers_36bit_dma); ++i) { + const struct debug_regs *reg = ®isters_36bit_dma[i]; + + seq_printf(m, "%21s[0x%02x]: 0x%08x\n", + reg->name, reg->offset, + mxc_isi_read(pipe, reg->offset)); + } + } + pm_runtime_put(pipe->isi->dev); return 0; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 65d20e9bae69..d76eb58deb09 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -263,10 +263,10 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) /* Configure the pipeline. */ state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); - compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); - crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); + compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK); + crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, MXC_ISI_PIPE_PAD_SINK); @@ -322,7 +322,7 @@ mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_format(&pipe->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); } static struct v4l2_rect * @@ -330,7 +330,7 @@ mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); } static struct v4l2_rect * @@ -338,11 +338,11 @@ mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); + return v4l2_subdev_state_get_compose(state, pad); } -static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_pipe *pipe = to_isi_pipe(sd); struct v4l2_mbus_framefmt *fmt_source; @@ -682,7 +682,6 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { - .init_cfg = mxc_isi_pipe_init_cfg, .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_pipe_set_fmt, @@ -694,6 +693,10 @@ static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { .pad = &mxc_isi_pipe_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = { + .init_state = mxc_isi_pipe_init_state, +}; + /* ----------------------------------------------------------------------------- * IRQ handling */ @@ -767,6 +770,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) sd = &pipe->sd; v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); + sd->internal_ops = &mxc_isi_pipe_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); sd->dev = isi->dev; @@ -832,8 +836,8 @@ int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, int ret; state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); v4l2_subdev_unlock_state(state); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 10840c9a0912..4091f1c0e78b 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -713,7 +713,7 @@ static int mxc_isi_video_validate_format(struct mxc_isi_video *video) info = mxc_isi_format_by_fourcc(video->pix.pixelformat, MXC_ISI_VIDEO_CAP); - format = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); if (format->code != info->mbus_code || format->width != video->pix.width || @@ -1453,7 +1453,7 @@ int mxc_isi_video_register(struct mxc_isi_pipe *pipe, q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mxc_isi_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &video->lock; q->dev = pipe->isi->dev; diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index ed048f73c982..ba2e81f24965 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -296,7 +296,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, /* Calculate the line rate from the pixel rate. */ - fmt = v4l2_subdev_get_pad_format(&state->sd, sd_state, MIPI_CSI2_PAD_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); csi2_fmt = find_csi2_format(fmt->code); link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler, @@ -437,14 +437,15 @@ unlock: return ret; } -static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx8mq_mipi_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt_sink; struct v4l2_mbus_framefmt *fmt_source; - fmt_sink = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SINK); - fmt_source = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt_sink = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); + fmt_source = v4l2_subdev_state_get_format(sd_state, + MIPI_CSI2_PAD_SOURCE); fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; @@ -477,7 +478,7 @@ static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -514,7 +515,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, if (!csi2_fmt) csi2_fmt = &imx8mq_mipi_csi_formats[0]; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csi2_fmt->code; fmt->width = sdformat->format.width; @@ -523,7 +524,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SOURCE); *fmt = sdformat->format; return 0; @@ -534,7 +535,6 @@ static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = { - .init_cfg = imx8mq_mipi_csi_init_cfg, .enum_mbus_code = imx8mq_mipi_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx8mq_mipi_csi_set_fmt, @@ -545,6 +545,10 @@ static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = { .pad = &imx8mq_mipi_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx8mq_mipi_csi_internal_ops = { + .init_state = imx8mq_mipi_csi_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -759,6 +763,7 @@ static int imx8mq_mipi_csi_subdev_init(struct csi_state *state) int ret; v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops); + sd->internal_ops = &imx8mq_mipi_csi_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "%s %s", MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 05ff5fa8095a..b11de4797cca 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -21,7 +21,6 @@ * interface support. As a result of that it has an * alternate register layout. */ -#define IS_LITE (csid->id >= 2 ? 1 : 0) #define CSID_HW_VERSION 0x0 #define HW_VERSION_STEPPING 0 @@ -35,13 +34,13 @@ #define CSID_CSI2_RX_IRQ_MASK 0x24 #define CSID_CSI2_RX_IRQ_CLEAR 0x28 -#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) ((IS_LITE ? 0x30 : 0x40) \ +#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) ((csid_is_lite(csid) ? 0x30 : 0x40) \ + 0x10 * (rdi)) -#define CSID_CSI2_RDIN_IRQ_MASK(rdi) ((IS_LITE ? 0x34 : 0x44) \ +#define CSID_CSI2_RDIN_IRQ_MASK(rdi) ((csid_is_lite(csid) ? 0x34 : 0x44) \ + 0x10 * (rdi)) -#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) ((IS_LITE ? 0x38 : 0x48) \ +#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) ((csid_is_lite(csid) ? 0x38 : 0x48) \ + 0x10 * (rdi)) -#define CSID_CSI2_RDIN_IRQ_SET(rdi) ((IS_LITE ? 0x3C : 0x4C) \ +#define CSID_CSI2_RDIN_IRQ_SET(rdi) ((csid_is_lite(csid) ? 0x3C : 0x4C) \ + 0x10 * (rdi)) #define CSID_TOP_IRQ_STATUS 0x70 @@ -73,7 +72,7 @@ #define CGC_MODE_DYNAMIC_GATING 0 #define CGC_MODE_ALWAYS_ON 1 -#define CSID_RDI_CFG0(rdi) ((IS_LITE ? 0x200 : 0x300) \ +#define CSID_RDI_CFG0(rdi) ((csid_is_lite(csid) ? 0x200 : 0x300) \ + 0x100 * (rdi)) #define RDI_CFG0_BYTE_CNTR_EN 0 #define RDI_CFG0_FORMAT_MEASURE_EN 1 @@ -98,32 +97,32 @@ #define RDI_CFG0_PACKING_FORMAT 30 #define RDI_CFG0_ENABLE 31 -#define CSID_RDI_CFG1(rdi) ((IS_LITE ? 0x204 : 0x304)\ +#define CSID_RDI_CFG1(rdi) ((csid_is_lite(csid) ? 0x204 : 0x304)\ + 0x100 * (rdi)) #define RDI_CFG1_TIMESTAMP_STB_SEL 0 -#define CSID_RDI_CTRL(rdi) ((IS_LITE ? 0x208 : 0x308)\ +#define CSID_RDI_CTRL(rdi) ((csid_is_lite(csid) ? 0x208 : 0x308)\ + 0x100 * (rdi)) #define RDI_CTRL_HALT_CMD 0 #define HALT_CMD_HALT_AT_FRAME_BOUNDARY 0 #define HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1 #define RDI_CTRL_HALT_MODE 2 -#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((IS_LITE ? 0x20C : 0x30C)\ +#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((csid_is_lite(csid) ? 0x20C : 0x30C)\ + 0x100 * (rdi)) -#define CSID_RDI_FRM_DROP_PERIOD(rdi) ((IS_LITE ? 0x210 : 0x310)\ +#define CSID_RDI_FRM_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x210 : 0x310)\ + 0x100 * (rdi)) -#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) ((IS_LITE ? 0x214 : 0x314)\ +#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) ((csid_is_lite(csid) ? 0x214 : 0x314)\ + 0x100 * (rdi)) -#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) ((IS_LITE ? 0x218 : 0x318)\ +#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) ((csid_is_lite(csid) ? 0x218 : 0x318)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_PIX_DROP_PATTERN(rdi) ((IS_LITE ? 0x224 : 0x324)\ +#define CSID_RDI_RPP_PIX_DROP_PATTERN(rdi) ((csid_is_lite(csid) ? 0x224 : 0x324)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_PIX_DROP_PERIOD(rdi) ((IS_LITE ? 0x228 : 0x328)\ +#define CSID_RDI_RPP_PIX_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x228 : 0x328)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_LINE_DROP_PATTERN(rdi) ((IS_LITE ? 0x22C : 0x32C)\ +#define CSID_RDI_RPP_LINE_DROP_PATTERN(rdi) ((csid_is_lite(csid) ? 0x22C : 0x32C)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((IS_LITE ? 0x230 : 0x330)\ +#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x230 : 0x330)\ + 0x100 * (rdi)) #define CSID_TPG_CTRL 0x600 diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 95873f988f7e..eb27d69e89a1 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -263,7 +263,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) /* * __csid_get_format - Get pointer to format structure * @csid: CSID device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -276,8 +276,7 @@ __csid_get_format(struct csid_device *csid, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csid->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csid->fmt[pad]; } @@ -285,7 +284,7 @@ __csid_get_format(struct csid_device *csid, /* * csid_try_format - Handle try format by pad subdev method * @csid: CSID device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -353,7 +352,7 @@ static void csid_try_format(struct csid_device *csid, /* * csid_enum_mbus_code - Handle pixel format enumeration * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -394,7 +393,7 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, /* * csid_enum_frame_size - Handle frame size enumeration * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * return -EINVAL or zero on success */ @@ -431,7 +430,7 @@ static int csid_enum_frame_size(struct v4l2_subdev *sd, /* * csid_get_format - Handle get format by pads subdev method * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -455,7 +454,7 @@ static int csid_get_format(struct v4l2_subdev *sd, /* * csid_set_format - Handle set format by pads subdev method * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -897,3 +896,8 @@ void msm_csid_unregister_entity(struct csid_device *csid) media_entity_cleanup(&csid->subdev.entity); v4l2_ctrl_handler_free(&csid->ctrls); } + +inline bool csid_is_lite(struct csid_device *csid) +{ + return csid->camss->res->csid_res[csid->id].is_lite; +} diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 30d94eb2eb04..fddccb69da13 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -215,5 +215,12 @@ extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; extern const struct csid_hw_ops csid_ops_gen2; +/* + * csid_is_lite - Check if CSID is CSID lite. + * @csid: CSID Device + * + * Return whether CSID is CSID lite + */ +bool csid_is_lite(struct csid_device *csid); #endif /* QC_MSM_CAMSS_CSID_H */ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index edd573606a6a..264c99efeae8 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -312,7 +312,7 @@ static int csiphy_set_stream(struct v4l2_subdev *sd, int enable) /* * __csiphy_get_format - Get pointer to format structure * @csiphy: CSIPHY device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -325,8 +325,7 @@ __csiphy_get_format(struct csiphy_device *csiphy, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csiphy->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csiphy->fmt[pad]; } @@ -334,7 +333,7 @@ __csiphy_get_format(struct csiphy_device *csiphy, /* * csiphy_try_format - Handle try format by pad subdev method * @csiphy: CSIPHY device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -381,7 +380,7 @@ static void csiphy_try_format(struct csiphy_device *csiphy, /* * csiphy_enum_mbus_code - Handle pixel format enumeration * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -414,7 +413,7 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, /* * csiphy_enum_frame_size - Handle frame size enumeration * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * return -EINVAL or zero on success */ @@ -451,7 +450,7 @@ static int csiphy_enum_frame_size(struct v4l2_subdev *sd, /* * csiphy_get_format - Handle get format by pads subdev method * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -475,7 +474,7 @@ static int csiphy_get_format(struct v4l2_subdev *sd, /* * csiphy_set_format - Handle set format by pads subdev method * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index be9d2f0a10c1..a12dcc7ff438 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -270,7 +270,7 @@ static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id) unsigned long time; u32 val; - if (vfe_id > camss->res->vfe_num - 1) { + if (vfe_id >= camss->res->vfe_num) { dev_err(camss->dev, "Error: asked reset for invalid VFE%d\n", vfe_id); return -ENOENT; @@ -866,7 +866,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) /* * __ispif_get_format - Get pointer to format structure * @ispif: ISPIF line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -879,8 +879,7 @@ __ispif_get_format(struct ispif_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } @@ -888,7 +887,7 @@ __ispif_get_format(struct ispif_line *line, /* * ispif_try_format - Handle try format by pad subdev method * @ispif: ISPIF line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -936,7 +935,7 @@ static void ispif_try_format(struct ispif_line *line, /* * ispif_enum_mbus_code - Handle pixel format enumeration * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -969,7 +968,7 @@ static int ispif_enum_mbus_code(struct v4l2_subdev *sd, /* * ispif_enum_frame_size - Handle frame size enumeration * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * return -EINVAL or zero on success */ @@ -1006,7 +1005,7 @@ static int ispif_enum_frame_size(struct v4l2_subdev *sd, /* * ispif_get_format - Handle get format by pads subdev method * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -1030,7 +1029,7 @@ static int ispif_get_format(struct v4l2_subdev *sd, /* * ispif_set_format - Handle set format by pads subdev method * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c index 0b211fed1276..795ac3815339 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c @@ -628,42 +628,6 @@ out_unlock: } /* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - - if (vfe->id >= camss->res->vfe_num) - return; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - if (id >= camss->res->vfe_num) - return 0; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!camss->genpd_link[id]) - return -EINVAL; - - return 0; -} - -/* * vfe_queue_buffer - Add empty buffer * @vid: Video device structure * @buf: Buffer to be enqueued diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index 2911e4126e7a..ef6b34c915df 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -936,7 +936,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) * vfe_pm_domain_off - Disable power domains specific to this VFE. * @vfe: VFE Device */ -static void vfe_pm_domain_off(struct vfe_device *vfe) +static void vfe_4_1_pm_domain_off(struct vfe_device *vfe) { /* nop */ } @@ -945,7 +945,7 @@ static void vfe_pm_domain_off(struct vfe_device *vfe) * vfe_pm_domain_on - Enable power domains specific to this VFE. * @vfe: VFE Device */ -static int vfe_pm_domain_on(struct vfe_device *vfe) +static int vfe_4_1_pm_domain_on(struct vfe_device *vfe) { return 0; } @@ -999,8 +999,8 @@ const struct vfe_hw_ops vfe_ops_4_1 = { .hw_version = vfe_hw_version, .isr_read = vfe_isr_read, .isr = vfe_isr, - .pm_domain_off = vfe_pm_domain_off, - .pm_domain_on = vfe_pm_domain_on, + .pm_domain_off = vfe_4_1_pm_domain_off, + .pm_domain_on = vfe_4_1_pm_domain_on, .reg_update_clear = vfe_reg_update_clear, .reg_update = vfe_reg_update, .subdev_init = vfe_subdev_init, diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index b65ed0fef595..7655d22a9fda 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -1103,42 +1103,6 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); } -/* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss; - - if (!vfe) - return; - - camss = vfe->camss; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); - - if (!camss->genpd_link[id]) { - dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id); - return -EINVAL; - } - - return 0; -} - static void vfe_violation_read(struct vfe_device *vfe) { u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c index 7b3805177f03..f52fa30f3853 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c @@ -1093,37 +1093,6 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); } -/* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); - - if (!camss->genpd_link[id]) { - dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id); - return -EINVAL; - } - - return 0; -} - static void vfe_violation_read(struct vfe_device *vfe) { u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c index f2368b77fc6d..dc2735476c82 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-480.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c @@ -15,31 +15,28 @@ #include "camss.h" #include "camss-vfe.h" -/* VFE 2/3 are lite and have a different register layout */ -#define IS_LITE (vfe->id >= 2 ? 1 : 0) - #define VFE_HW_VERSION (0x00) -#define VFE_GLOBAL_RESET_CMD (IS_LITE ? 0x0c : 0x1c) -#define GLOBAL_RESET_HW_AND_REG (IS_LITE ? BIT(1) : BIT(0)) +#define VFE_GLOBAL_RESET_CMD (vfe_is_lite(vfe) ? 0x0c : 0x1c) +#define GLOBAL_RESET_HW_AND_REG (vfe_is_lite(vfe) ? BIT(1) : BIT(0)) -#define VFE_REG_UPDATE_CMD (IS_LITE ? 0x20 : 0x34) +#define VFE_REG_UPDATE_CMD (vfe_is_lite(vfe) ? 0x20 : 0x34) static inline int reg_update_rdi(struct vfe_device *vfe, int n) { - return IS_LITE ? BIT(n) : BIT(1 + (n)); + return vfe_is_lite(vfe) ? BIT(n) : BIT(1 + (n)); } #define REG_UPDATE_RDI reg_update_rdi -#define VFE_IRQ_CMD (IS_LITE ? 0x24 : 0x38) +#define VFE_IRQ_CMD (vfe_is_lite(vfe) ? 0x24 : 0x38) #define IRQ_CMD_GLOBAL_CLEAR BIT(0) -#define VFE_IRQ_MASK(n) ((IS_LITE ? 0x28 : 0x3c) + (n) * 4) -#define IRQ_MASK_0_RESET_ACK (IS_LITE ? BIT(17) : BIT(0)) -#define IRQ_MASK_0_BUS_TOP_IRQ (IS_LITE ? BIT(4) : BIT(7)) -#define VFE_IRQ_CLEAR(n) ((IS_LITE ? 0x34 : 0x48) + (n) * 4) -#define VFE_IRQ_STATUS(n) ((IS_LITE ? 0x40 : 0x54) + (n) * 4) +#define VFE_IRQ_MASK(n) ((vfe_is_lite(vfe) ? 0x28 : 0x3c) + (n) * 4) +#define IRQ_MASK_0_RESET_ACK (vfe_is_lite(vfe) ? BIT(17) : BIT(0)) +#define IRQ_MASK_0_BUS_TOP_IRQ (vfe_is_lite(vfe) ? BIT(4) : BIT(7)) +#define VFE_IRQ_CLEAR(n) ((vfe_is_lite(vfe) ? 0x34 : 0x48) + (n) * 4) +#define VFE_IRQ_STATUS(n) ((vfe_is_lite(vfe) ? 0x40 : 0x54) + (n) * 4) -#define BUS_REG_BASE (IS_LITE ? 0x1a00 : 0xaa00) +#define BUS_REG_BASE (vfe_is_lite(vfe) ? 0x1a00 : 0xaa00) #define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08) #define WM_CGC_OVERRIDE_ALL (0x3FFFFFF) @@ -49,13 +46,13 @@ static inline int reg_update_rdi(struct vfe_device *vfe, int n) #define VFE_BUS_IRQ_MASK(n) (BUS_REG_BASE + 0x18 + (n) * 4) static inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n) { - return IS_LITE ? BIT(n) : BIT(3 + (n)); + return vfe_is_lite(vfe) ? BIT(n) : BIT(3 + (n)); } #define BUS_IRQ_MASK_0_RDI_RUP bus_irq_mask_0_rdi_rup static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) { - return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n)); + return vfe_is_lite(vfe) ? BIT(4 + (n)) : BIT(6 + (n)); } #define BUS_IRQ_MASK_0_COMP_DONE bus_irq_mask_0_comp_done @@ -90,8 +87,8 @@ static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) /* for titan 480, each bus client is hardcoded to a specific path * and each bus client is part of a hardcoded "comp group" */ -#define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n)) -#define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n)) +#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 23) + (n)) +#define RDI_COMP_GROUP(n) ((vfe_is_lite(vfe) ? 0 : 11) + (n)) #define MAX_VFE_OUTPUT_LINES 4 @@ -453,42 +450,6 @@ out_unlock: } /* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - - if (vfe->id >= camss->res->vfe_num) - return; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - if (id >= camss->res->vfe_num) - return 0; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!camss->genpd_link[id]) - return -EINVAL; - - return 0; -} - -/* * vfe_queue_buffer - Add empty buffer * @vid: Video device structure * @buf: Buffer to be enqueued diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 4839e2cedfe5..2062be668f49 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -14,6 +14,7 @@ #include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/spinlock_types.h> #include <linux/spinlock.h> @@ -474,6 +475,40 @@ void vfe_isr_reset_ack(struct vfe_device *vfe) complete(&vfe->reset_complete); } +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +void vfe_pm_domain_off(struct vfe_device *vfe) +{ + if (!vfe->genpd) + return; + + device_link_del(vfe->genpd_link); + vfe->genpd_link = NULL; +} + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +int vfe_pm_domain_on(struct vfe_device *vfe) +{ + struct camss *camss = vfe->camss; + + if (!vfe->genpd) + return 0; + + vfe->genpd_link = device_link_add(camss->dev, vfe->genpd, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!vfe->genpd_link) + return -EINVAL; + + return 0; +} + static int vfe_match_clock_names(struct vfe_device *vfe, struct camss_clock *clock) { @@ -815,7 +850,7 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) /* * __vfe_get_format - Get pointer to format structure * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -828,8 +863,7 @@ __vfe_get_format(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } @@ -837,7 +871,7 @@ __vfe_get_format(struct vfe_line *line, /* * __vfe_get_compose - Get pointer to compose selection structure * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which: TRY or ACTIVE format * * Return pointer to TRY or ACTIVE compose rectangle structure @@ -848,8 +882,8 @@ __vfe_get_compose(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&line->subdev, sd_state, - MSM_VFE_PAD_SINK); + return v4l2_subdev_state_get_compose(sd_state, + MSM_VFE_PAD_SINK); return &line->compose; } @@ -857,7 +891,7 @@ __vfe_get_compose(struct vfe_line *line, /* * __vfe_get_crop - Get pointer to crop selection structure * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which: TRY or ACTIVE format * * Return pointer to TRY or ACTIVE crop rectangle structure @@ -868,8 +902,7 @@ __vfe_get_crop(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&line->subdev, sd_state, - MSM_VFE_PAD_SRC); + return v4l2_subdev_state_get_crop(sd_state, MSM_VFE_PAD_SRC); return &line->crop; } @@ -877,7 +910,7 @@ __vfe_get_crop(struct vfe_line *line, /* * vfe_try_format - Handle try format by pad subdev method * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -938,7 +971,7 @@ static void vfe_try_format(struct vfe_line *line, /* * vfe_try_compose - Handle try compose selection by pad subdev method * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @rect: pointer to v4l2 rect structure * @which: wanted subdev format */ @@ -977,7 +1010,7 @@ static void vfe_try_compose(struct vfe_line *line, /* * vfe_try_crop - Handle try crop selection by pad subdev method * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @rect: pointer to v4l2 rect structure * @which: wanted subdev format */ @@ -1020,7 +1053,7 @@ static void vfe_try_crop(struct vfe_line *line, /* * vfe_enum_mbus_code - Handle pixel format enumeration * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * * return -EINVAL or zero on success @@ -1054,7 +1087,7 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, /* * vfe_enum_frame_size - Handle frame size enumeration * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * * Return -EINVAL or zero on success @@ -1092,7 +1125,7 @@ static int vfe_enum_frame_size(struct v4l2_subdev *sd, /* * vfe_get_format - Handle get format by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -1120,7 +1153,7 @@ static int vfe_set_selection(struct v4l2_subdev *sd, /* * vfe_set_format - Handle set format by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -1171,7 +1204,7 @@ static int vfe_set_format(struct v4l2_subdev *sd, /* * vfe_get_selection - Handle get selection by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: pointer to v4l2 subdev selection structure * * Return -EINVAL or zero on success @@ -1241,7 +1274,7 @@ static int vfe_get_selection(struct v4l2_subdev *sd, /* * vfe_set_selection - Handle set selection by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: pointer to v4l2 subdev selection structure * * Return -EINVAL or zero on success @@ -1347,6 +1380,34 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, if (!res->line_num) return -EINVAL; + /* Power domain */ + + if (res->pd_name) { + vfe->genpd = dev_pm_domain_attach_by_name(camss->dev, + res->pd_name); + if (IS_ERR(vfe->genpd)) { + ret = PTR_ERR(vfe->genpd); + return ret; + } + } + + if (!vfe->genpd && res->has_pd) { + /* + * Legacy magic index. + * Requires + * power-domain = <VFE_X>, + * <VFE_Y>, + * <TITAN_TOP> + * id must correspondng to the index of the VFE which must + * come before the TOP GDSC. VFE Lite has no individually + * collapasible domain which is why id < vfe_num is a valid + * check. + */ + vfe->genpd = dev_pm_domain_attach_by_id(camss->dev, id); + if (IS_ERR(vfe->genpd)) + return PTR_ERR(vfe->genpd); + } + vfe->line_num = res->line_num; vfe->ops->subdev_init(dev, vfe); @@ -1470,6 +1531,19 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, } /* + * msm_vfe_genpd_cleanup - Cleanup VFE genpd linkages + * @vfe: VFE device + */ +void msm_vfe_genpd_cleanup(struct vfe_device *vfe) +{ + if (vfe->genpd_link) + device_link_del(vfe->genpd_link); + + if (vfe->genpd) + dev_pm_domain_detach(vfe->genpd, true); +} + +/* * vfe_link_setup - Setup VFE connections * @entity: Pointer to media entity structure * @local: Pointer to local pad @@ -1663,3 +1737,8 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) media_entity_cleanup(&sd->entity); } } + +bool vfe_is_lite(struct vfe_device *vfe) +{ + return vfe->camss->res->vfe_res[vfe->id].is_lite; +} diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 09baded0dcdd..0572c9b08e11 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -150,6 +150,8 @@ struct vfe_device { const struct vfe_hw_ops_gen1 *ops_gen1; struct vfe_isr_ops isr_ops; struct camss_video_ops video_ops; + struct device *genpd; + struct device_link *genpd_link; }; struct camss_subdev_resources; @@ -157,6 +159,8 @@ struct camss_subdev_resources; int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, const struct camss_subdev_resources *res, u8 id); +void msm_vfe_genpd_cleanup(struct vfe_device *vfe); + int msm_vfe_register_entities(struct vfe_device *vfe, struct v4l2_device *v4l2_dev); @@ -201,6 +205,18 @@ int vfe_reset(struct vfe_device *vfe); */ int vfe_disable(struct vfe_line *line); +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +void vfe_pm_domain_off(struct vfe_device *vfe); + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +int vfe_pm_domain_on(struct vfe_device *vfe); + extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; @@ -210,4 +226,14 @@ extern const struct vfe_hw_ops vfe_ops_480; int vfe_get(struct vfe_device *vfe); void vfe_put(struct vfe_device *vfe); +/* + * vfe_is_lite - Return if VFE is VFE lite. + * @vfe: VFE Device + * + * Some VFE lites have a different register layout. + * + * Return whether VFE is VFE lite + */ +bool vfe_is_lite(struct vfe_device *vfe); + #endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8e78dd8d5961..58f4be660290 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -278,6 +278,7 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { .reg = { "vfe0" }, .interrupt = { "vfe0" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_7 }, @@ -298,6 +299,7 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { .reg = { "vfe1" }, .interrupt = { "vfe1" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_7 } }; @@ -468,6 +470,7 @@ static const struct camss_subdev_resources vfe_res_660[] = { .reg = { "vfe0" }, .interrupt = { "vfe0" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_8 }, @@ -491,6 +494,7 @@ static const struct camss_subdev_resources vfe_res_660[] = { .reg = { "vfe1" }, .interrupt = { "vfe1" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_8 } }; @@ -634,6 +638,7 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, + .is_lite = true, .ops = &csid_ops_gen2 } }; @@ -658,6 +663,7 @@ static const struct camss_subdev_resources vfe_res_845[] = { .reg = { "vfe0" }, .interrupt = { "vfe0" }, .line_num = 4, + .has_pd = true, .ops = &vfe_ops_170 }, @@ -680,6 +686,7 @@ static const struct camss_subdev_resources vfe_res_845[] = { .reg = { "vfe1" }, .interrupt = { "vfe1" }, .line_num = 4, + .has_pd = true, .ops = &vfe_ops_170 }, @@ -700,6 +707,7 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe_lite" }, .interrupt = { "vfe_lite" }, + .is_lite = true, .line_num = 4, .ops = &vfe_ops_170 } @@ -805,6 +813,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, + .is_lite = true, .ops = &csid_ops_gen2 }, /* CSID3 */ @@ -817,6 +826,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, + .is_lite = true, .ops = &csid_ops_gen2 } }; @@ -839,7 +849,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, + .pd_name = "ife0", .line_num = 3, + .has_pd = true, .ops = &vfe_ops_480 }, /* VFE1 */ @@ -859,7 +871,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, + .pd_name = "ife1", .line_num = 3, + .has_pd = true, .ops = &vfe_ops_480 }, /* VFE2 (lite) */ @@ -878,6 +892,7 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, + .is_lite = true, .line_num = 4, .ops = &vfe_ops_480 }, @@ -897,6 +912,7 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, + .is_lite = true, .line_num = 4, .ops = &vfe_ops_480 }, @@ -1196,7 +1212,7 @@ static int camss_init_subdevices(struct camss *camss) } /* note: SM8250 requires VFE to be initialized before CSID */ - for (i = 0; i < camss->vfe_total_num; i++) { + for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_subdev_init(camss, &camss->vfe[i], &res->vfe_res[i], i); if (ret < 0) { @@ -1268,7 +1284,7 @@ static int camss_register_entities(struct camss *camss) goto err_reg_ispif; } - for (i = 0; i < camss->vfe_total_num; i++) { + for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_register_entities(&camss->vfe[i], &camss->v4l2_dev); if (ret < 0) { @@ -1340,7 +1356,7 @@ static int camss_register_entities(struct camss *camss) } } else { for (i = 0; i < camss->res->csid_num; i++) - for (k = 0; k < camss->vfe_total_num; k++) + for (k = 0; k < camss->res->vfe_num; k++) for (j = 0; j < camss->vfe[k].line_num; j++) { struct v4l2_subdev *csid = &camss->csid[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1364,7 +1380,7 @@ static int camss_register_entities(struct camss *camss) return 0; err_link: - i = camss->vfe_total_num; + i = camss->res->vfe_num; err_reg_vfe: for (i--; i >= 0; i--) msm_vfe_unregister_entities(&camss->vfe[i]); @@ -1403,7 +1419,7 @@ static void camss_unregister_entities(struct camss *camss) msm_ispif_unregister_entities(camss->ispif); - for (i = 0; i < camss->vfe_total_num; i++) + for (i = 0; i < camss->res->vfe_num; i++) msm_vfe_unregister_entities(&camss->vfe[i]); } @@ -1478,7 +1494,9 @@ static const struct media_device_ops camss_media_ops = { static int camss_configure_pd(struct camss *camss) { + const struct camss_resources *res = camss->res; struct device *dev = camss->dev; + int vfepd_num; int i; int ret; @@ -1498,45 +1516,60 @@ static int camss_configure_pd(struct camss *camss) if (camss->genpd_num == 1) return 0; - camss->genpd = devm_kmalloc_array(dev, camss->genpd_num, - sizeof(*camss->genpd), GFP_KERNEL); - if (!camss->genpd) - return -ENOMEM; + /* count the # of VFEs which have flagged power-domain */ + for (vfepd_num = i = 0; i < camss->res->vfe_num; i++) { + if (res->vfe_res[i].has_pd) + vfepd_num++; + } - camss->genpd_link = devm_kmalloc_array(dev, camss->genpd_num, - sizeof(*camss->genpd_link), - GFP_KERNEL); - if (!camss->genpd_link) - return -ENOMEM; + /* + * If the number of power-domains is greater than the number of VFEs + * then the additional power-domain is for the entire CAMSS block. + */ + if (!(camss->genpd_num > vfepd_num)) + return 0; /* - * VFE power domains are in the beginning of the list, and while all - * power domains should be attached, only if TITAN_TOP power domain is - * found in the list, it should be linked over here. + * If a power-domain name is defined try to use it. + * It is possible we are running a new kernel with an old dtb so + * fallback to indexes even if a pd_name is defined but not found. */ - for (i = 0; i < camss->genpd_num; i++) { - camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i); - if (IS_ERR(camss->genpd[i])) { - ret = PTR_ERR(camss->genpd[i]); + if (camss->res->pd_name) { + camss->genpd = dev_pm_domain_attach_by_name(camss->dev, + camss->res->pd_name); + if (IS_ERR(camss->genpd)) { + ret = PTR_ERR(camss->genpd); goto fail_pm; } } - if (i > camss->res->vfe_num) { - camss->genpd_link[i - 1] = device_link_add(camss->dev, camss->genpd[i - 1], - DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!camss->genpd_link[i - 1]) { - ret = -EINVAL; - goto fail_pm; - } + if (!camss->genpd) { + /* + * Legacy magic index. TITAN_TOP GDSC must be the last + * item in the power-domain list. + */ + camss->genpd = dev_pm_domain_attach_by_id(camss->dev, + camss->genpd_num - 1); + } + if (IS_ERR_OR_NULL(camss->genpd)) { + if (!camss->genpd) + ret = -ENODEV; + else + ret = PTR_ERR(camss->genpd); + goto fail_pm; + } + camss->genpd_link = device_link_add(camss->dev, camss->genpd, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!camss->genpd_link) { + ret = -EINVAL; + goto fail_pm; } return 0; fail_pm: - for (--i ; i >= 0; i--) - dev_pm_domain_detach(camss->genpd[i], true); + dev_pm_domain_detach(camss->genpd, true); return ret; } @@ -1558,18 +1591,25 @@ static int camss_icc_get(struct camss *camss) return 0; } -static void camss_genpd_cleanup(struct camss *camss) +static void camss_genpd_subdevice_cleanup(struct camss *camss) { int i; + for (i = 0; i < camss->res->vfe_num; i++) + msm_vfe_genpd_cleanup(&camss->vfe[i]); +} + +static void camss_genpd_cleanup(struct camss *camss) +{ if (camss->genpd_num == 1) return; - if (camss->genpd_num > camss->res->vfe_num) - device_link_del(camss->genpd_link[camss->genpd_num - 1]); + camss_genpd_subdevice_cleanup(camss); + + if (camss->genpd_link) + device_link_del(camss->genpd_link); - for (i = 0; i < camss->genpd_num; i++) - dev_pm_domain_detach(camss->genpd[i], true); + dev_pm_domain_detach(camss->genpd, true); } /* @@ -1612,8 +1652,7 @@ static int camss_probe(struct platform_device *pdev) return -ENOMEM; } - camss->vfe_total_num = camss->res->vfe_num + camss->res->vfe_lite_num; - camss->vfe = devm_kcalloc(dev, camss->vfe_total_num, + camss->vfe = devm_kcalloc(dev, camss->res->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); if (!camss->vfe) return -ENOMEM; @@ -1771,12 +1810,12 @@ static const struct camss_resources sdm845_resources = { .vfe_res = vfe_res_845, .csiphy_num = ARRAY_SIZE(csiphy_res_845), .csid_num = ARRAY_SIZE(csid_res_845), - .vfe_num = 2, - .vfe_lite_num = 1, + .vfe_num = ARRAY_SIZE(vfe_res_845), }; static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, + .pd_name = "top", .csiphy_res = csiphy_res_8250, .csid_res = csid_res_8250, .vfe_res = vfe_res_8250, @@ -1784,8 +1823,7 @@ static const struct camss_resources sm8250_resources = { .icc_path_num = ARRAY_SIZE(icc_res_sm8250), .csiphy_num = ARRAY_SIZE(csiphy_res_8250), .csid_num = ARRAY_SIZE(csid_res_8250), - .vfe_num = 2, - .vfe_lite_num = 2, + .vfe_num = ARRAY_SIZE(vfe_res_8250), }; static const struct of_device_id camss_dt_match[] = { diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 8acad7321c09..a0c2dcc779f0 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -48,7 +48,10 @@ struct camss_subdev_resources { u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; char *reg[CAMSS_RES_MAX]; char *interrupt[CAMSS_RES_MAX]; + char *pd_name; u8 line_num; + bool has_pd; + bool is_lite; const void *ops; }; @@ -83,6 +86,7 @@ enum icc_count { struct camss_resources { enum camss_version version; + const char *pd_name; const struct camss_subdev_resources *csiphy_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; @@ -92,7 +96,6 @@ struct camss_resources { const unsigned int csiphy_num; const unsigned int csid_num; const unsigned int vfe_num; - const unsigned int vfe_lite_num; }; struct camss { @@ -106,11 +109,10 @@ struct camss { struct vfe_device *vfe; atomic_t ref_count; int genpd_num; - struct device **genpd; - struct device_link **genpd_link; + struct device *genpd; + struct device_link *genpd_link; struct icc_path *icc_path[ICC_SM8250_COUNT]; const struct camss_resources *res; - unsigned int vfe_total_num; }; struct camss_camera_interface { diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 9cffe975581b..a712dd4f02a5 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -881,6 +881,10 @@ static const struct venus_resources sc7280_res = { .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, .fwname = "qcom/vpu-2.0/venus.mbn", }; diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index dbf305cec120..29130a9441e7 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1641,7 +1641,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; - src_vq->min_buffers_needed = 0; + src_vq->min_queued_buffers = 0; src_vq->dev = inst->core->dev; src_vq->lock = &inst->ctx_q_lock; ret = vb2_queue_init(src_vq); @@ -1656,7 +1656,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; - dst_vq->min_buffers_needed = 0; + dst_vq->min_queued_buffers = 0; dst_vq->dev = inst->core->dev; dst_vq->lock = &inst->ctx_q_lock; return vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 44b13696cf82..3ec2fb8d9fab 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1398,7 +1398,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->dev = inst->core->dev; src_vq->lock = &inst->ctx_q_lock; if (inst->core->res->hfi_version == HFI_VERSION_1XX) @@ -1415,7 +1415,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; - dst_vq->min_buffers_needed = 1; + dst_vq->min_queued_buffers = 1; dst_vq->dev = inst->core->dev; dst_vq->lock = &inst->ctx_q_lock; return vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 19a005d83733..530d65fc546b 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -282,7 +282,7 @@ static int risp_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { isp->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -302,7 +302,7 @@ static int risp_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = isp->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&isp->lock); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 66fe553a00e7..582d5e35db0e 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -1185,7 +1185,7 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { priv->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -1205,7 +1205,7 @@ static int rcsi2_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = priv->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&priv->lock); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 2a77353f10b5..e2c40abc6d3d 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1559,7 +1559,7 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 4; + q->min_queued_buffers = 4; q->dev = vin->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 292c5bf9e50c..f21d05054341 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -424,10 +424,11 @@ static int rcar_drif_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); /* Need at least 16 buffers */ - if (vq->num_buffers + *num_buffers < 16) - *num_buffers = 16 - vq->num_buffers; + if (q_num_bufs + *num_buffers < 16) + *num_buffers = 16 - q_num_bufs; *num_planes = 1; sizes[0] = PAGE_ALIGN(sdr->fmt->buffersize); diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c index 2562b30acfb9..167760276796 100644 --- a/drivers/media/platform/renesas/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c @@ -1399,7 +1399,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct ceu_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &ceudev->mlock; q->dev = ceudev->v4l2_dev.dev; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index ad2bd71037ab..d20f4eff93a4 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -250,7 +250,7 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) } state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev); - fmt = v4l2_subdev_get_pad_format(&csi2->subdev, state, RZG2L_CSI2_SINK); + fmt = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); format = rzg2l_csi2_code_to_fmt(fmt->code); v4l2_subdev_unlock_state(state); @@ -500,13 +500,13 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SOURCE); if (fmt->pad == RZG2L_CSI2_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SINK); + sink_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); if (!rzg2l_csi2_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_csi2_formats[0].code; @@ -530,8 +530,8 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, return 0; } -static int rzg2l_csi2_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CSI2_SINK, }; @@ -582,7 +582,6 @@ static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_mbus_code = rzg2l_csi2_enum_mbus_code, - .init_cfg = rzg2l_csi2_init_config, .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, @@ -593,6 +592,10 @@ static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { .pad = &rzg2l_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_csi2_internal_ops = { + .init_state = rzg2l_csi2_init_state, +}; + /* ----------------------------------------------------------------------------- * Async handling and registration of subdevices and links. */ @@ -777,6 +780,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) csi2->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); + csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), "csi-%s", dev_name(&pdev->dev)); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 4dcd2faff5bb..9f351a05893e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -39,7 +39,7 @@ struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) struct v4l2_mbus_framefmt *fmt; state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); - fmt = v4l2_subdev_get_pad_format(&cru->ip.subdev, state, 1); + fmt = v4l2_subdev_state_get_format(state, 1); v4l2_subdev_unlock_state(state); return fmt; @@ -108,13 +108,13 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CRU_IP_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); if (fmt->pad == RZG2L_CRU_IP_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + sink_format = v4l2_subdev_state_get_format(state, fmt->pad); if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_cru_ip_formats[0].code; @@ -168,8 +168,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rzg2l_cru_ip_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; @@ -192,7 +192,6 @@ static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, .enum_frame_size = rzg2l_cru_ip_enum_frame_size, - .init_cfg = rzg2l_cru_ip_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rzg2l_cru_ip_set_format, }; @@ -202,6 +201,10 @@ static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { .pad = &rzg2l_cru_ip_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { + .init_state = rzg2l_cru_ip_init_state, +}; + static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -213,6 +216,7 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) ip->subdev.dev = cru->dev; v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); + ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; v4l2_set_subdevdata(&ip->subdev, cru); snprintf(ip->subdev.name, sizeof(ip->subdev.name), "cru-ip-%s", dev_name(cru->dev)); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index e6eedd65b71d..d0ffa90bc656 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -767,7 +767,7 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) q->ops = &rzg2l_cru_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 4; + q->min_queued_buffers = 4; q->dev = cru->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index f792aedc9d82..1e74dd601c2b 100644 --- a/drivers/media/platform/renesas/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c @@ -1297,7 +1297,7 @@ static int sh_vou_probe(struct platform_device *pdev) q->ops = &sh_vou_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &vou_dev->fop_lock; q->dev = &pdev->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 89385b4cabe5..a8535c6e2c46 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -100,7 +100,7 @@ static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&brx->entity.subdev, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); } static void brx_try_format(struct vsp1_brx *brx, @@ -136,29 +136,28 @@ static int brx_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&brx->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - brx_try_format(brx, config, fmt->pad, &fmt->format); + brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); *format = fmt->format; /* Reset the compose rectangle. */ if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = brx_get_compose(brx, config, fmt->pad); + compose = brx_get_compose(brx, state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -171,7 +170,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, for (i = 0; i <= brx->entity.source_pad; ++i) { format = vsp1_entity_get_pad_format(&brx->entity, - config, i); + state, i); format->code = fmt->format.code; } } @@ -186,7 +185,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; if (sel->pad == brx->entity.source_pad) return -EINVAL; @@ -200,13 +199,13 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return 0; case V4L2_SEL_TGT_COMPOSE: - config = vsp1_entity_get_pad_config(&brx->entity, sd_state, - sel->which); - if (!config) + state = vsp1_entity_get_state(&brx->entity, sd_state, + sel->which); + if (!state) return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, config, sel->pad); + sel->r = *brx_get_compose(brx, state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -220,7 +219,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; int ret = 0; @@ -233,9 +232,8 @@ static int brx_set_selection(struct v4l2_subdev *subdev, mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&brx->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } @@ -244,7 +242,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&brx->entity, config, + format = vsp1_entity_get_pad_format(&brx->entity, state, brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -253,11 +251,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad); + format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, config, sel->pad); + compose = brx_get_compose(brx, state, sel->pad); *compose = sel->r; done: @@ -266,7 +264,6 @@ done: } static const struct v4l2_subdev_pad_ops brx_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = brx_enum_mbus_code, .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -293,7 +290,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config, + format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, brx->entity.source_pad); /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index c5217fee24f1..625776a9bda4 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -155,7 +155,6 @@ static int clu_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops clu_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = clu_enum_mbus_code, .enum_frame_size = clu_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -182,8 +181,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = vsp1_entity_get_pad_format(&clu->entity, - clu->entity.config, + format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index c31f05a80bb5..0a5a7f9cc870 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -101,27 +101,26 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity, */ /** - * vsp1_entity_get_pad_config - Get the pad configuration for an entity + * vsp1_entity_get_state - Get the subdev state for an entity * @entity: the entity * @sd_state: the TRY state - * @which: configuration selector (ACTIVE or TRY) + * @which: state selector (ACTIVE or TRY) * * When called with which set to V4L2_SUBDEV_FORMAT_ACTIVE the caller must hold * the entity lock to access the returned configuration. * - * Return the pad configuration requested by the which argument. The TRY - * configuration is passed explicitly to the function through the cfg argument - * and simply returned when requested. The ACTIVE configuration comes from the - * entity structure. + * Return the subdev state requested by the which argument. The TRY state is + * passed explicitly to the function through the sd_state argument and simply + * returned when requested. The ACTIVE state comes from the entity structure. */ struct v4l2_subdev_state * -vsp1_entity_get_pad_config(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) +vsp1_entity_get_state(struct vsp1_entity *entity, + struct v4l2_subdev_state *sd_state, + enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_ACTIVE: - return entity->config; + return entity->state; case V4L2_SUBDEV_FORMAT_TRY: default: return sd_state; @@ -142,7 +141,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_format(&entity->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } /** @@ -163,46 +162,18 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, { switch (target) { case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_compose(sd_state, pad); case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); default: return NULL; } } /* - * vsp1_entity_init_cfg - Initialize formats on all pads - * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * - * Initialize all pad formats with default values in the given pad config. This - * function can be used as a handler for the subdev pad::init_cfg operation. - */ -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) -{ - unsigned int pad; - - for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { - struct v4l2_subdev_format format = { - .pad = pad, - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); - } - - return 0; -} - -/* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: V4L2 subdev format * * This function implements the subdev get_fmt pad operation. It can be used as @@ -213,14 +184,14 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; - config = vsp1_entity_get_pad_config(entity, sd_state, fmt->which); - if (!config) + state = vsp1_entity_get_state(entity, sd_state, fmt->which); + if (!state) return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad); + fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -229,7 +200,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, /* * vsp1_subdev_enum_mbus_code - Subdev pad enum_mbus_code handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: Media bus code enumeration * @codes: Array of supported media bus codes * @ncodes: Number of supported media bus codes @@ -252,7 +223,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; /* @@ -262,13 +233,12 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - config = vsp1_entity_get_pad_config(entity, sd_state, - code->which); - if (!config) + state = vsp1_entity_get_state(entity, sd_state, code->which); + if (!state) return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, config, 0); + format = vsp1_entity_get_pad_format(entity, state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -279,7 +249,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, /* * vsp1_subdev_enum_frame_size - Subdev pad enum_frame_size handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: Frame size enumeration * @min_width: Minimum image width * @min_height: Minimum image height @@ -298,15 +268,15 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, unsigned int max_width, unsigned int max_height) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(entity, sd_state, fse->which); - if (!config) + state = vsp1_entity_get_state(entity, sd_state, fse->which); + if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, config, fse->pad); + format = vsp1_entity_get_pad_format(entity, state, fse->pad); mutex_lock(&entity->lock); @@ -339,7 +309,7 @@ done: /* * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: V4L2 subdev format * @codes: Array of supported media bus codes * @ncodes: Number of supported media bus codes @@ -362,7 +332,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, unsigned int max_width, unsigned int max_height) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; unsigned int i; @@ -370,13 +340,13 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, mutex_lock(&entity->lock); - config = vsp1_entity_get_pad_config(entity, sd_state, fmt->which); - if (!config) { + state = vsp1_entity_get_state(entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - format = vsp1_entity_get_pad_format(entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(entity, state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -404,18 +374,18 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(entity, config, entity->source_pad); + format = vsp1_entity_get_pad_format(entity, state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, V4L2_SEL_TGT_CROP); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, V4L2_SEL_TGT_COMPOSE); selection->left = 0; selection->top = 0; @@ -427,6 +397,29 @@ done: return ret; } +static int vsp1_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + unsigned int pad; + + /* Initialize all pad formats with default values. */ + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + struct v4l2_subdev_format format = { + .pad = pad, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); + } + + return 0; +} + +static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = { + .init_state = vsp1_entity_init_state, +}; + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -661,6 +654,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, /* Initialize the V4L2 subdev. */ subdev = &entity->subdev; v4l2_subdev_init(subdev, ops); + subdev->internal_ops = &vsp1_entity_internal_ops; subdev->entity.function = function; subdev->entity.ops = &vsp1->media_ops; @@ -669,21 +663,21 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), name); - vsp1_entity_init_cfg(subdev, NULL); + vsp1_entity_init_state(subdev, NULL); /* - * Allocate the pad configuration to store formats and selection + * Allocate the subdev state to store formats and selection * rectangles. */ /* * FIXME: Drop this call, drivers are not supposed to use * __v4l2_subdev_state_alloc(). */ - entity->config = __v4l2_subdev_state_alloc(&entity->subdev, - "vsp1:config->lock", &key); - if (IS_ERR(entity->config)) { + entity->state = __v4l2_subdev_state_alloc(&entity->subdev, + "vsp1:state->lock", &key); + if (IS_ERR(entity->state)) { media_entity_cleanup(&entity->subdev.entity); - return PTR_ERR(entity->config); + return PTR_ERR(entity->state); } return 0; @@ -695,6 +689,6 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); - __v4l2_subdev_state_free(entity->config); + __v4l2_subdev_state_free(entity->state); media_entity_cleanup(&entity->subdev.entity); } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 17f98a6a972e..735f32dde4b5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -115,9 +115,9 @@ struct vsp1_entity { unsigned int sink_pad; struct v4l2_subdev subdev; - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; - struct mutex lock; /* Protects the pad config */ + struct mutex lock; /* Protects the state */ }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) @@ -135,9 +135,9 @@ int vsp1_entity_link_setup(struct media_entity *entity, const struct media_pad *remote, u32 flags); struct v4l2_subdev_state * -vsp1_entity_get_pad_config(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which); +vsp1_entity_get_state(struct vsp1_entity *entity, + struct v4l2_subdev_state *sd_state, + enum v4l2_subdev_format_whence which); struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, @@ -146,8 +146,6 @@ struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad, unsigned int target); -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index e6492deb0a64..40c571a987ef 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -140,9 +140,9 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->config, + crop = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->config, + compose = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index aa1c718e0453..8281b86874ab 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -139,9 +139,9 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->config, + crop = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->config, + compose = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index f22449dd654c..71155282ca11 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -203,7 +203,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; int ret = 0; @@ -213,9 +213,8 @@ static int histo_get_selection(struct v4l2_subdev *subdev, mutex_lock(&histo->entity.lock); - config = vsp1_entity_get_pad_config(&histo->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } @@ -223,7 +222,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - crop = vsp1_entity_get_pad_selection(&histo->entity, config, + crop = vsp1_entity_get_pad_selection(&histo->entity, state, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); sel->r.left = 0; @@ -234,7 +233,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&histo->entity, config, + format = vsp1_entity_get_pad_format(&histo->entity, state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; @@ -244,7 +243,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, config, + sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, sel->pad, sel->target); break; @@ -346,7 +345,7 @@ static int histo_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; int ret; if (sel->pad != HISTO_PAD_SINK) @@ -354,17 +353,16 @@ static int histo_set_selection(struct v4l2_subdev *subdev, mutex_lock(&histo->entity.lock); - config = vsp1_entity_get_pad_config(&histo->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } if (sel->target == V4L2_SEL_TGT_CROP) - ret = histo_set_crop(subdev, config, sel); + ret = histo_set_crop(subdev, state, sel); else if (sel->target == V4L2_SEL_TGT_COMPOSE) - ret = histo_set_compose(subdev, config, sel); + ret = histo_set_compose(subdev, state, sel); else ret = -EINVAL; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 361a870380c2..bc1299c29ac9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -66,20 +66,19 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&hsit->entity.lock); - config = vsp1_entity_get_pad_config(&hsit->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -102,7 +101,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, config, + format = vsp1_entity_get_pad_format(&hsit->entity, state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 @@ -114,7 +113,6 @@ done: } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 0ab2e0c70474..b1d21a54837b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -68,7 +68,6 @@ static int lif_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops lif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -94,7 +93,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, + format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index ac6802a325f5..451d24ab0b56 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -131,7 +131,6 @@ static int lut_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops lut_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index ea12c3f12c92..c47579efc65f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -81,10 +81,10 @@ static void rpf_configure_stream(struct vsp1_entity *entity, /* Format */ sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.config, + rpf->entity.state, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.config, + rpf->entity.state, RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM @@ -158,7 +158,7 @@ static void rpf_configure_stream(struct vsp1_entity *entity, const struct v4l2_rect *compose; compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->config, + pipe->brx->state, rpf->brx_input, V4L2_SEL_TGT_COMPOSE); left = compose->left; @@ -302,7 +302,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); + crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state); /* * Partition Algorithm Control diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index e0f87c8103ca..09fb6ffa14e2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -19,8 +19,7 @@ struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_state *sd_state) { - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state, - RWPF_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); } /* ----------------------------------------------------------------------------- @@ -62,15 +61,14 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } @@ -81,7 +79,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* @@ -107,7 +105,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_rect *crop; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, config); + crop = vsp1_rwpf_get_crop(rwpf, state); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -115,7 +113,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, } /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SOURCE); *format = fmt->format; @@ -134,7 +132,7 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; @@ -147,20 +145,19 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, config); + sel->r = *vsp1_rwpf_get_crop(rwpf, state); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; @@ -183,7 +180,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; int ret = 0; @@ -200,15 +197,14 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SINK); /* @@ -229,11 +225,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, config); + crop = vsp1_rwpf_get_crop(rwpf, state); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; @@ -244,7 +240,6 @@ done: } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index b614a2aea461..11e008aa9f20 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -123,16 +123,15 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(&sru->entity, sd_state, - fse->which); - if (!config) + state = vsp1_entity_get_state(&sru->entity, sd_state, fse->which); + if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK); + format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -221,31 +220,30 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&sru->entity.lock); - config = vsp1_entity_get_pad_config(&sru->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - sru_try_format(sru, config, fmt->pad, &fmt->format); + sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, config, + format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SOURCE); *format = fmt->format; - sru_try_format(sru, config, SRU_PAD_SOURCE, format); + sru_try_format(sru, state, SRU_PAD_SOURCE, format); } done: @@ -254,7 +252,6 @@ done: } static const struct v4l2_subdev_pad_ops sru_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -280,9 +277,9 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) @@ -310,9 +307,9 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SOURCE); /* @@ -336,9 +333,9 @@ static void sru_partition(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 1c290cda005a..d89f1197b86c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -128,17 +128,15 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(&uds->entity, sd_state, - fse->which); - if (!config) + state = vsp1_entity_get_state(&uds->entity, sd_state, fse->which); + if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, config, - UDS_PAD_SINK); + format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -205,31 +203,30 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&uds->entity.lock); - config = vsp1_entity_get_pad_config(&uds->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - uds_try_format(uds, config, fmt->pad, &fmt->format); + uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, config, + format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SOURCE); *format = fmt->format; - uds_try_format(uds, config, UDS_PAD_SOURCE, format); + uds_try_format(uds, state, UDS_PAD_SOURCE, format); } done: @@ -242,7 +239,6 @@ done: */ static const struct v4l2_subdev_pad_ops uds_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -269,9 +265,9 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); @@ -314,7 +310,7 @@ static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_partition *partition = pipe->partition; const struct v4l2_mbus_framefmt *output; - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); /* Input size clipping. */ @@ -339,9 +335,9 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); hscale = output->width / input->width; @@ -381,9 +377,9 @@ static void uds_partition(struct vsp1_entity *entity, partition->uds_sink = *window; partition->uds_source = *window; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 83d7f17df80e..f66936a28a2a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -86,7 +86,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_uif *uif = to_uif(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; @@ -95,9 +95,8 @@ static int uif_get_selection(struct v4l2_subdev *subdev, mutex_lock(&uif->entity.lock); - config = vsp1_entity_get_pad_config(&uif->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } @@ -105,7 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&uif->entity, config, + format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; @@ -114,7 +113,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&uif->entity, config, + sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state, sel->pad, sel->target); break; @@ -133,7 +132,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_uif *uif = to_uif(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; int ret = 0; @@ -144,15 +143,14 @@ static int uif_set_selection(struct v4l2_subdev *subdev, mutex_lock(&uif->entity.lock); - config = vsp1_entity_get_pad_config(&uif->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, config, UIF_PAD_SINK); + format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -162,7 +160,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Store the crop rectangle. */ - selection = vsp1_entity_get_pad_selection(&uif->entity, config, + selection = vsp1_entity_get_pad_selection(&uif->entity, state, sel->pad, V4L2_SEL_TGT_CROP); *selection = sel->r; @@ -176,7 +174,6 @@ done: */ static const struct v4l2_subdev_pad_ops uif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uif_enum_mbus_code, .enum_frame_size = uif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -206,7 +203,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = vsp1_entity_get_pad_selection(entity, entity->config, + crop = vsp1_entity_get_pad_selection(entity, entity->state, UIF_PAD_SINK, V4L2_SEL_TGT_CROP); left = crop->left; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index e9d5027761bb..5a9cb0e5640e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -198,7 +198,7 @@ static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, * at the WPF sink. */ format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.config, + pipe->output->entity.state, RWPF_PAD_SINK); /* A single partition simply processes the output size in full. */ @@ -263,7 +263,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) * at the WPF sink. */ format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.config, + pipe->output->entity.state, RWPF_PAD_SINK); div_size = format->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index cab4445eca69..9693aeab1cac 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -66,10 +66,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) } sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -246,10 +246,10 @@ static void wpf_configure_stream(struct vsp1_entity *entity, int ret; sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SOURCE); /* Format */ @@ -384,7 +384,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int i; sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SINK); width = sink_format->width; height = sink_format->height; diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 81508ed5abf3..662c81b6d0b5 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -5,7 +5,9 @@ */ #include <linux/pm_runtime.h> +#include <linux/scatterlist.h> +#include <media/v4l2-common.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> @@ -15,6 +17,26 @@ #include "rga-hw.h" #include "rga.h" +static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc, + struct sg_table *sgt) +{ + struct sg_dma_page_iter iter; + struct rga_dma_desc *tmp = desc; + size_t n_desc = 0; + dma_addr_t addr; + + for_each_sgtable_dma_page(sgt, &iter, 0) { + if (n_desc > max_desc) + return -EINVAL; + addr = sg_page_iter_dma_address(&iter); + tmp->addr = lower_32_bits(addr); + tmp++; + n_desc++; + } + + return n_desc; +} + static int rga_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, @@ -22,28 +44,105 @@ rga_queue_setup(struct vb2_queue *vq, { struct rga_ctx *ctx = vb2_get_drv_priv(vq); struct rga_frame *f = rga_get_frame(ctx, vq->type); + const struct v4l2_pix_format_mplane *pix_fmt; + int i; if (IS_ERR(f)) return PTR_ERR(f); - if (*nplanes) - return sizes[0] < f->size ? -EINVAL : 0; + pix_fmt = &f->pix; + + if (*nplanes) { + if (*nplanes != pix_fmt->num_planes) + return -EINVAL; + + for (i = 0; i < pix_fmt->num_planes; i++) + if (sizes[i] < pix_fmt->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + *nplanes = pix_fmt->num_planes; + + for (i = 0; i < pix_fmt->num_planes; i++) + sizes[i] = pix_fmt->plane_fmt[i].sizeimage; + + return 0; +} + +static int rga_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rga_vb_buffer *rbuf = vb_to_rga(vbuf); + struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct rockchip_rga *rga = ctx->rga; + struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); + size_t n_desc = 0; - sizes[0] = f->size; - *nplanes = 1; + n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); + + rbuf->n_desc = n_desc; + rbuf->dma_desc = dma_alloc_coherent(rga->dev, + rbuf->n_desc * sizeof(*rbuf->dma_desc), + &rbuf->dma_desc_pa, GFP_KERNEL); + if (!rbuf->dma_desc) + return -ENOMEM; return 0; } +static int get_plane_offset(struct rga_frame *f, int plane) +{ + if (plane == 0) + return 0; + if (plane == 1) + return f->width * f->height; + if (plane == 2) + return f->width * f->height + (f->width * f->height / f->fmt->uv_factor); + + return -EINVAL; +} + static int rga_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rga_vb_buffer *rbuf = vb_to_rga(vbuf); struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); + ssize_t n_desc = 0; + size_t curr_desc = 0; + int i; + const struct v4l2_format_info *info; + unsigned int offsets[VIDEO_MAX_PLANES]; if (IS_ERR(f)) return PTR_ERR(f); - vb2_set_plane_payload(vb, 0, f->size); + for (i = 0; i < vb->num_planes; i++) { + vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage); + + /* Create local MMU table for RGA */ + n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], + rbuf->n_desc - curr_desc, + vb2_dma_sg_plane_desc(vb, i)); + if (n_desc < 0) { + v4l2_err(&ctx->rga->v4l2_dev, + "Failed to map video buffer to RGA\n"); + return n_desc; + } + offsets[i] = curr_desc << PAGE_SHIFT; + curr_desc += n_desc; + } + + /* Fill the remaining planes */ + info = v4l2_format_info(f->fmt->fourcc); + for (i = info->mem_planes; i < info->comp_planes; i++) + offsets[i] = get_plane_offset(f, i); + + rbuf->offset.y_off = offsets[0]; + rbuf->offset.u_off = offsets[1]; + rbuf->offset.v_off = offsets[2]; return 0; } @@ -56,6 +155,17 @@ static void rga_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static void rga_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rga_vb_buffer *rbuf = vb_to_rga(vbuf); + struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct rockchip_rga *rga = ctx->rga; + + dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc), + rbuf->dma_desc, rbuf->dma_desc_pa); +} + static void rga_buf_return_buffers(struct vb2_queue *q, enum vb2_buffer_state state) { @@ -99,50 +209,12 @@ static void rga_buf_stop_streaming(struct vb2_queue *q) const struct vb2_ops rga_qops = { .queue_setup = rga_queue_setup, + .buf_init = rga_buf_init, .buf_prepare = rga_buf_prepare, .buf_queue = rga_buf_queue, + .buf_cleanup = rga_buf_cleanup, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .start_streaming = rga_buf_start_streaming, .stop_streaming = rga_buf_stop_streaming, }; - -/* RGA MMU is a 1-Level MMU, so it can't be used through the IOMMU API. - * We use it more like a scatter-gather list. - */ -void rga_buf_map(struct vb2_buffer *vb) -{ - struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct rockchip_rga *rga = ctx->rga; - struct sg_table *sgt; - struct scatterlist *sgl; - unsigned int *pages; - unsigned int address, len, i, p; - unsigned int mapped_size = 0; - - if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - pages = rga->src_mmu_pages; - else - pages = rga->dst_mmu_pages; - - /* Create local MMU table for RGA */ - sgt = vb2_plane_cookie(vb, 0); - - for_each_sg(sgt->sgl, sgl, sgt->nents, i) { - len = sg_dma_len(sgl) >> PAGE_SHIFT; - address = sg_phys(sgl); - - for (p = 0; p < len; p++) { - dma_addr_t phys = address + - ((dma_addr_t)p << PAGE_SHIFT); - - pages[mapped_size + p] = phys; - } - - mapped_size += len; - } - - /* sync local MMU table for RGA */ - dma_sync_single_for_device(rga->dev, virt_to_phys(pages), - 8 * PAGE_SIZE, DMA_BIDIRECTIONAL); -} diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index aaa96f256356..11c3d7234757 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -16,12 +16,6 @@ enum e_rga_start_pos { RB = 3, }; -struct rga_addr_offset { - unsigned int y_off; - unsigned int u_off; - unsigned int v_off; -}; - struct rga_corners_addr_offset { struct rga_addr_offset left_top; struct rga_addr_offset right_top; @@ -43,13 +37,13 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) } static struct rga_corners_addr_offset -rga_get_addr_offset(struct rga_frame *frm, unsigned int x, unsigned int y, - unsigned int w, unsigned int h) +rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, + unsigned int x, unsigned int y, unsigned int w, unsigned int h) { struct rga_corners_addr_offset offsets; struct rga_addr_offset *lt, *lb, *rt, *rb; unsigned int x_div = 0, - y_div = 0, uv_stride = 0, pixel_width = 0, uv_factor = 0; + y_div = 0, uv_stride = 0, pixel_width = 0; lt = &offsets.left_top; lb = &offsets.left_bottom; @@ -58,14 +52,12 @@ rga_get_addr_offset(struct rga_frame *frm, unsigned int x, unsigned int y, x_div = frm->fmt->x_div; y_div = frm->fmt->y_div; - uv_factor = frm->fmt->uv_factor; uv_stride = frm->stride / x_div; pixel_width = frm->stride / frm->width; - lt->y_off = y * frm->stride + x * pixel_width; - lt->u_off = - frm->width * frm->height + (y / y_div) * uv_stride + x / x_div; - lt->v_off = lt->u_off + frm->width * frm->height / uv_factor; + lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; + lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; + lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div; lb->y_off = lt->y_off + (h - 1) * frm->stride; lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride; @@ -119,40 +111,40 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct return NULL; } -static void rga_cmd_set_src_addr(struct rga_ctx *ctx, void *mmu_pages) +static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG; - dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4; + dest[reg >> 2] = dma_addr >> 4; reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG; dest[reg >> 2] |= 0x7; } -static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, void *mmu_pages) +static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG; - dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4; + dest[reg >> 2] = dma_addr >> 4; reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG; dest[reg >> 2] |= 0x7 << 4; } -static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, void *mmu_pages) +static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG; - dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4; + dest[reg >> 2] = dma_addr >> 4; reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG; dest[reg >> 2] |= 0x7 << 8; @@ -163,7 +155,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int scale_dst_w, scale_dst_h; - unsigned int src_h, src_w, src_x, src_y, dst_h, dst_w, dst_x, dst_y; + unsigned int src_h, src_w, dst_h, dst_w; union rga_src_info src_info; union rga_dst_info dst_info; union rga_src_x_factor x_factor; @@ -173,18 +165,10 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; - struct rga_addr_offset *dst_offset; - struct rga_corners_addr_offset offsets; - struct rga_corners_addr_offset src_offsets; - src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; - src_x = ctx->in.crop.left; - src_y = ctx->in.crop.top; dst_h = ctx->out.crop.height; dst_w = ctx->out.crop.width; - dst_x = ctx->out.crop.left; - dst_y = ctx->out.crop.top; src_info.val = dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2]; dst_info.val = dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2]; @@ -312,18 +296,37 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_act_info.data.act_height = dst_h - 1; dst_act_info.data.act_width = dst_w - 1; - /* - * Calculate the source framebuffer base address with offset pixel. - */ - src_offsets = rga_get_addr_offset(&ctx->in, src_x, src_y, - src_w, src_h); + dest[(RGA_SRC_X_FACTOR - RGA_MODE_BASE_REG) >> 2] = x_factor.val; + dest[(RGA_SRC_Y_FACTOR - RGA_MODE_BASE_REG) >> 2] = y_factor.val; + dest[(RGA_SRC_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = src_vir_info.val; + dest[(RGA_SRC_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = src_act_info.val; + + dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2] = src_info.val; + + dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = dst_vir_info.val; + dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = dst_act_info.val; + + dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2] = dst_info.val; +} + +static void rga_cmd_set_src_info(struct rga_ctx *ctx, + struct rga_addr_offset *offset) +{ + struct rga_corners_addr_offset src_offsets; + struct rockchip_rga *rga = ctx->rga; + u32 *dest = rga->cmdbuf_virt; + unsigned int src_h, src_w, src_x, src_y; + + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + src_x = ctx->in.crop.left; + src_y = ctx->in.crop.top; /* - * Configure the dest framebuffer base address with pixel offset. + * Calculate the source framebuffer base address with offset pixel. */ - offsets = rga_get_addr_offset(&ctx->out, dst_x, dst_y, dst_w, dst_h); - dst_offset = rga_lookup_draw_pos(&offsets, src_info.data.rot_mode, - src_info.data.mir_mode); + src_offsets = rga_get_addr_offset(&ctx->in, offset, + src_x, src_y, src_w, src_h); dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = src_offsets.left_top.y_off; @@ -331,13 +334,49 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) src_offsets.left_top.u_off; dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = src_offsets.left_top.v_off; +} - dest[(RGA_SRC_X_FACTOR - RGA_MODE_BASE_REG) >> 2] = x_factor.val; - dest[(RGA_SRC_Y_FACTOR - RGA_MODE_BASE_REG) >> 2] = y_factor.val; - dest[(RGA_SRC_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = src_vir_info.val; - dest[(RGA_SRC_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = src_act_info.val; +static void rga_cmd_set_dst_info(struct rga_ctx *ctx, + struct rga_addr_offset *offset) +{ + struct rga_addr_offset *dst_offset; + struct rga_corners_addr_offset offsets; + struct rockchip_rga *rga = ctx->rga; + u32 *dest = rga->cmdbuf_virt; + unsigned int dst_h, dst_w, dst_x, dst_y; + unsigned int mir_mode = 0; + unsigned int rot_mode = 0; - dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2] = src_info.val; + dst_h = ctx->out.crop.height; + dst_w = ctx->out.crop.width; + dst_x = ctx->out.crop.left; + dst_y = ctx->out.crop.top; + + if (ctx->vflip) + mir_mode |= RGA_SRC_MIRR_MODE_X; + if (ctx->hflip) + mir_mode |= RGA_SRC_MIRR_MODE_Y; + + switch (ctx->rotate) { + case 90: + rot_mode = RGA_SRC_ROT_MODE_90_DEGREE; + break; + case 180: + rot_mode = RGA_SRC_ROT_MODE_180_DEGREE; + break; + case 270: + rot_mode = RGA_SRC_ROT_MODE_270_DEGREE; + break; + default: + rot_mode = RGA_SRC_ROT_MODE_0_DEGREE; + break; + } + + /* + * Configure the dest framebuffer base address with pixel offset. + */ + offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h); + dst_offset = rga_lookup_draw_pos(&offsets, mir_mode, rot_mode); dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = dst_offset->y_off; @@ -345,11 +384,6 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_offset->u_off; dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = dst_offset->v_off; - - dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = dst_vir_info.val; - dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = dst_act_info.val; - - dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2] = dst_info.val; } static void rga_cmd_set_mode(struct rga_ctx *ctx) @@ -375,22 +409,25 @@ static void rga_cmd_set_mode(struct rga_ctx *ctx) dest[(RGA_MODE_CTRL - RGA_MODE_BASE_REG) >> 2] = mode.val; } -static void rga_cmd_set(struct rga_ctx *ctx) +static void rga_cmd_set(struct rga_ctx *ctx, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rockchip_rga *rga = ctx->rga; memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); - rga_cmd_set_src_addr(ctx, rga->src_mmu_pages); + rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* * Due to hardware bug, * src1 mmu also should be configured when using alpha blending. */ - rga_cmd_set_src1_addr(ctx, rga->dst_mmu_pages); + rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_dst_addr(ctx, rga->dst_mmu_pages); + rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); rga_cmd_set_mode(ctx); + rga_cmd_set_src_info(ctx, &src->offset); + rga_cmd_set_dst_info(ctx, &dst->offset); rga_cmd_set_trans_info(ctx); rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy); @@ -400,11 +437,12 @@ static void rga_cmd_set(struct rga_ctx *ctx) PAGE_SIZE, DMA_BIDIRECTIONAL); } -void rga_hw_start(struct rockchip_rga *rga) +void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rga_ctx *ctx = rga->curr; - rga_cmd_set(ctx); + rga_cmd_set(ctx, src, dst); rga_write(rga, RGA_SYS_CTRL, 0x00); diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index f1c532a5802a..00fdfa9e10bc 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -45,10 +45,7 @@ static void device_run(void *prv) src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_buf_map(&src->vb2_buf); - rga_buf_map(&dst->vb2_buf); - - rga_hw_start(rga); + rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); spin_unlock_irqrestore(&rga->ctrl_lock, flags); } @@ -96,12 +93,13 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) struct rga_ctx *ctx = priv; int ret; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &rga_qops; src_vq->mem_ops = &vb2_dma_sg_memops; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->gfp_flags = __GFP_DMA32; + src_vq->buf_struct_size = sizeof(struct rga_vb_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->rga->mutex; src_vq->dev = ctx->rga->v4l2_dev.dev; @@ -110,12 +108,13 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) if (ret) return ret; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &rga_qops; dst_vq->mem_ops = &vb2_dma_sg_memops; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->gfp_flags = __GFP_DMA32; + dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->rga->mutex; dst_vq->dev = ctx->rga->v4l2_dev.dev; @@ -184,7 +183,7 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) static struct rga_fmt formats[] = { { .fourcc = V4L2_PIX_FMT_ARGB32, - .color_swap = RGA_COLOR_RB_SWAP, + .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, .uv_factor = 1, @@ -192,17 +191,8 @@ static struct rga_fmt formats[] = { .x_div = 1, }, { - .fourcc = V4L2_PIX_FMT_XRGB32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_XBGR8888, - .depth = 32, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { .fourcc = V4L2_PIX_FMT_ABGR32, - .color_swap = RGA_COLOR_ALPHA_SWAP, + .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, .uv_factor = 1, @@ -211,7 +201,7 @@ static struct rga_fmt formats[] = { }, { .fourcc = V4L2_PIX_FMT_XBGR32, - .color_swap = RGA_COLOR_ALPHA_SWAP, + .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, .depth = 32, .uv_factor = 1, @@ -291,6 +281,15 @@ static struct rga_fmt formats[] = { .x_div = 1, }, { + .fourcc = V4L2_PIX_FMT_NV12M, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .uv_factor = 4, + .y_div = 2, + .x_div = 1, + }, + { .fourcc = V4L2_PIX_FMT_NV16, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, @@ -330,12 +329,12 @@ static struct rga_fmt formats[] = { #define NUM_FORMATS ARRAY_SIZE(formats) -static struct rga_fmt *rga_fmt_find(struct v4l2_format *f) +static struct rga_fmt *rga_fmt_find(u32 pixelformat) { unsigned int i; for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].fourcc == f->fmt.pix.pixelformat) + if (formats[i].fourcc == pixelformat) return &formats[i]; } return NULL; @@ -354,14 +353,11 @@ static struct rga_frame def_frame = { struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (V4L2_TYPE_IS_OUTPUT(type)) return &ctx->in; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (V4L2_TYPE_IS_CAPTURE(type)) return &ctx->out; - default: - return ERR_PTR(-EINVAL); - } + return ERR_PTR(-EINVAL); } static int rga_open(struct file *file) @@ -378,6 +374,11 @@ static int rga_open(struct file *file) ctx->in = def_frame; ctx->out = def_frame; + v4l2_fill_pixfmt_mp(&ctx->in.pix, + ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height); + v4l2_fill_pixfmt_mp(&ctx->out.pix, + ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height); + if (mutex_lock_interruptible(&rga->mutex)) { kfree(ctx); return -ERESTARTSYS; @@ -458,6 +459,7 @@ static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) { + struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = prv; struct vb2_queue *vq; struct rga_frame *frm; @@ -469,58 +471,43 @@ static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); - f->fmt.pix.width = frm->width; - f->fmt.pix.height = frm->height; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = frm->fmt->fourcc; - f->fmt.pix.bytesperline = frm->stride; - f->fmt.pix.sizeimage = frm->size; - f->fmt.pix.colorspace = frm->colorspace; + v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height); + + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = frm->colorspace; return 0; } static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) { + struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_fmt *fmt; - fmt = rga_fmt_find(f); - if (!fmt) { + fmt = rga_fmt_find(pix_fmt->pixelformat); + if (!fmt) fmt = &formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - f->fmt.pix.field = V4L2_FIELD_NONE; - if (f->fmt.pix.width > MAX_WIDTH) - f->fmt.pix.width = MAX_WIDTH; - if (f->fmt.pix.height > MAX_HEIGHT) - f->fmt.pix.height = MAX_HEIGHT; + pix_fmt->width = clamp(pix_fmt->width, + (u32)MIN_WIDTH, (u32)MAX_WIDTH); + pix_fmt->height = clamp(pix_fmt->height, + (u32)MIN_HEIGHT, (u32)MAX_HEIGHT); - if (f->fmt.pix.width < MIN_WIDTH) - f->fmt.pix.width = MIN_WIDTH; - if (f->fmt.pix.height < MIN_HEIGHT) - f->fmt.pix.height = MIN_HEIGHT; - - if (fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) - f->fmt.pix.bytesperline = f->fmt.pix.width; - else - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - - f->fmt.pix.sizeimage = - f->fmt.pix.height * (f->fmt.pix.width * fmt->depth) >> 3; + v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); + pix_fmt->field = V4L2_FIELD_NONE; return 0; } static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) { + struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = prv; struct rockchip_rga *rga = ctx->rga; struct vb2_queue *vq; struct rga_frame *frm; - struct rga_fmt *fmt; int ret = 0; + int i; /* Adjust all values accordingly to the hardware capabilities * and chosen format. @@ -536,15 +523,14 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - fmt = rga_fmt_find(f); - if (!fmt) - return -EINVAL; - frm->width = f->fmt.pix.width; - frm->height = f->fmt.pix.height; - frm->size = f->fmt.pix.sizeimage; - frm->fmt = fmt; - frm->stride = f->fmt.pix.bytesperline; - frm->colorspace = f->fmt.pix.colorspace; + frm->width = pix_fmt->width; + frm->height = pix_fmt->height; + frm->size = 0; + for (i = 0; i < pix_fmt->num_planes; i++) + frm->size += pix_fmt->plane_fmt[i].sizeimage; + frm->fmt = rga_fmt_find(pix_fmt->pixelformat); + frm->stride = pix_fmt->plane_fmt[0].bytesperline; + frm->colorspace = pix_fmt->colorspace; /* Reset crop settings */ frm->crop.left = 0; @@ -552,6 +538,21 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) frm->crop.width = frm->width; frm->crop.height = frm->height; + frm->pix = *pix_fmt; + + v4l2_dbg(debug, 1, &rga->v4l2_dev, + "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", + V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", + &frm->fmt->fourcc, frm->width, frm->height, + frm->stride, frm->size); + + for (i = 0; i < pix_fmt->num_planes; i++) { + v4l2_dbg(debug, 1, &rga->v4l2_dev, + "plane[%d]: size %d, bytesperline %d\n", + i, pix_fmt->plane_fmt[i].sizeimage, + pix_fmt->plane_fmt[i].bytesperline); + } + return 0; } @@ -569,21 +570,21 @@ static int vidioc_g_selection(struct file *file, void *prv, switch (s->target) { case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!V4L2_TYPE_IS_CAPTURE(s->type)) return -EINVAL; break; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (!V4L2_TYPE_IS_OUTPUT(s->type)) return -EINVAL; break; case V4L2_SEL_TGT_COMPOSE: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!V4L2_TYPE_IS_CAPTURE(s->type)) return -EINVAL; use_frame = true; break; case V4L2_SEL_TGT_CROP: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (!V4L2_TYPE_IS_OUTPUT(s->type)) return -EINVAL; use_frame = true; break; @@ -621,7 +622,7 @@ static int vidioc_s_selection(struct file *file, void *prv, * COMPOSE target is only valid for capture buffer type, return * error for output buffer type */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!V4L2_TYPE_IS_CAPTURE(s->type)) return -EINVAL; break; case V4L2_SEL_TGT_CROP: @@ -629,7 +630,7 @@ static int vidioc_s_selection(struct file *file, void *prv, * CROP target is only valid for output buffer type, return * error for capture buffer type */ - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (!V4L2_TYPE_IS_OUTPUT(s->type)) return -EINVAL; break; /* @@ -662,14 +663,14 @@ static const struct v4l2_ioctl_ops rga_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt, .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, - .vidioc_g_fmt_vid_out = vidioc_g_fmt, - .vidioc_try_fmt_vid_out = vidioc_try_fmt, - .vidioc_s_fmt_vid_out = vidioc_s_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt, .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, @@ -696,7 +697,7 @@ static const struct video_device rga_videodev = { .minor = -1, .release = video_device_release, .vfl_dir = VFL_DIR_M2M, - .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; static int rga_enable_clocks(struct rockchip_rga *rga) @@ -836,6 +837,12 @@ static int rga_probe(struct platform_device *pdev) goto err_put_clk; } + ret = dma_set_mask_and_coherent(rga->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(rga->dev, "32-bit DMA not supported"); + goto err_put_clk; + } + ret = v4l2_device_register(&pdev->dev, &rga->v4l2_dev); if (ret) goto err_put_clk; @@ -881,26 +888,13 @@ static int rga_probe(struct platform_device *pdev) goto rel_m2m; } - rga->src_mmu_pages = - (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); - if (!rga->src_mmu_pages) { - ret = -ENOMEM; - goto free_dma; - } - rga->dst_mmu_pages = - (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); - if (!rga->dst_mmu_pages) { - ret = -ENOMEM; - goto free_src_pages; - } - def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; def_frame.size = def_frame.stride * def_frame.height; ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); - goto free_dst_pages; + goto free_dma; } v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n", @@ -908,10 +902,6 @@ static int rga_probe(struct platform_device *pdev) return 0; -free_dst_pages: - free_pages((unsigned long)rga->dst_mmu_pages, 3); -free_src_pages: - free_pages((unsigned long)rga->src_mmu_pages, 3); free_dma: dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); @@ -934,9 +924,6 @@ static void rga_remove(struct platform_device *pdev) dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); - free_pages((unsigned long)rga->src_mmu_pages, 3); - free_pages((unsigned long)rga->dst_mmu_pages, 3); - v4l2_info(&rga->v4l2_dev, "Removing\n"); v4l2_m2m_release(rga->m2m_dev); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 5fa9d2f366dc..3502dff6055c 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -34,12 +34,17 @@ struct rga_frame { /* Image format */ struct rga_fmt *fmt; + struct v4l2_pix_format_mplane pix; /* Variables that can calculated once and reused */ u32 stride; u32 size; }; +struct rga_dma_desc { + u32 addr; +}; + struct rockchip_rga_version { u32 major; u32 minor; @@ -81,15 +86,36 @@ struct rockchip_rga { struct rga_ctx *curr; dma_addr_t cmdbuf_phy; void *cmdbuf_virt; - unsigned int *src_mmu_pages; - unsigned int *dst_mmu_pages; }; +struct rga_addr_offset { + unsigned int y_off; + unsigned int u_off; + unsigned int v_off; +}; + +struct rga_vb_buffer { + struct vb2_v4l2_buffer vb_buf; + struct list_head queue; + + /* RGA MMU mapping for this buffer */ + struct rga_dma_desc *dma_desc; + dma_addr_t dma_desc_pa; + size_t n_desc; + + /* Plane offsets of this buffer into the mapping */ + struct rga_addr_offset offset; +}; + +static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) +{ + return container_of(vb, struct rga_vb_buffer, vb_buf); +} + struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type); /* RGA Buffers Manage */ extern const struct vb2_ops rga_qops; -void rga_buf_map(struct vb2_buffer *vb); /* RGA Hardware */ static inline void rga_write(struct rockchip_rga *rga, u32 reg, u32 value) @@ -110,6 +136,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; -void rga_hw_start(struct rockchip_rga *rga); +void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst); #endif diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index c6d7e01c8949..aebd3c12020b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -1431,7 +1431,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) q->ops = &rkisp1_vb2_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct rkisp1_buffer); - q->min_buffers_needed = RKISP1_MIN_BUFFERS_NEEDED; + q->min_queued_buffers = RKISP1_MIN_BUFFERS_NEEDED; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->vlock; q->dev = cap->rkisp1->dev; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 1e7cea1bea5e..4b6b28c05b89 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -61,6 +61,14 @@ struct dentry; RKISP1_CIF_ISP_EXP_END | \ RKISP1_CIF_ISP_HIST_MEASURE_RDY) +/* IRQ lines */ +enum rkisp1_irq_line { + RKISP1_IRQ_ISP = 0, + RKISP1_IRQ_MI, + RKISP1_IRQ_MIPI, + RKISP1_NUM_IRQS, +}; + /* enum for the resizer pads */ enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_SINK, @@ -417,13 +425,13 @@ struct rkisp1_debug { unsigned long stats_error; unsigned long stop_timeout[2]; unsigned long frame_drop[2]; + unsigned long complete_frames; }; /* * struct rkisp1_device - ISP platform device * * @base_addr: base register address - * @irq: the irq number * @dev: a pointer to the struct device * @clk_size: number of clocks * @clks: array of clocks @@ -441,6 +449,7 @@ struct rkisp1_debug { * @stream_lock: serializes {start/stop}_streaming callbacks between the capture devices. * @debug: debug params to be exposed on debugfs * @info: version-specific ISP information + * @irqs: IRQ line numbers */ struct rkisp1_device { void __iomem *base_addr; @@ -461,6 +470,7 @@ struct rkisp1_device { struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */ struct rkisp1_debug debug; const struct rkisp1_info *info; + int irqs[RKISP1_NUM_IRQS]; }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 6e17b2817e61..b6e47e2f1b94 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -125,8 +125,20 @@ static void rkisp1_csi_disable(struct rkisp1_csi *csi) struct rkisp1_device *rkisp1 = csi->rkisp1; u32 val; - /* Mask and clear interrupts. */ + /* Mask MIPI interrupts. */ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, 0); + + /* Flush posted writes */ + rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC); + + /* + * Wait until the IRQ handler has ended. The IRQ handler may get called + * even after this, but it will return immediately as the MIPI + * interrupts have been masked. + */ + synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MIPI]); + + /* Clear MIPI interrupt status */ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0); val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL); @@ -242,8 +254,8 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index) return -EINVAL; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_CSI_PAD_SINK); code->code = sink_fmt->code; return 0; @@ -270,15 +282,13 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_csi_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -301,7 +311,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, if (fmt->pad == RKISP1_CSI_PAD_SRC) return v4l2_subdev_get_fmt(sd, sd_state, fmt); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); sink_fmt->code = fmt->format.code; @@ -321,7 +331,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, fmt->format = *sink_fmt; /* Propagate the format to the source pad. */ - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SRC); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); *src_fmt = *sink_fmt; return 0; @@ -374,7 +384,7 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) return -EINVAL; sd_state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); format = rkisp1_mbus_info_get_by_code(sink_fmt->code); v4l2_subdev_unlock_state(sd_state); @@ -407,7 +417,6 @@ static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = { static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = { .enum_mbus_code = rkisp1_csi_enum_mbus_code, - .init_cfg = rkisp1_csi_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_csi_set_fmt, }; @@ -417,6 +426,10 @@ static const struct v4l2_subdev_ops rkisp1_csi_ops = { .pad = &rkisp1_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_csi_internal_ops = { + .init_state = rkisp1_csi_init_state, +}; + int rkisp1_csi_register(struct rkisp1_device *rkisp1) { struct rkisp1_csi *csi = &rkisp1->csi; @@ -428,6 +441,7 @@ int rkisp1_csi_register(struct rkisp1_device *rkisp1) sd = &csi->sd; v4l2_subdev_init(sd, &rkisp1_csi_ops); + sd->internal_ops = &rkisp1_csi_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_csi_media_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c index 71df3dc95e6f..79cda589d935 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c @@ -92,6 +92,10 @@ static int rkisp1_debug_dump_isp_regs_show(struct seq_file *m, void *p) RKISP1_DEBUG_REG(ISP_FLAGS_SHD), RKISP1_DEBUG_REG(ISP_RIS), RKISP1_DEBUG_REG(ISP_ERR), + RKISP1_DEBUG_SHD_REG(ISP_IS_H_OFFS), + RKISP1_DEBUG_SHD_REG(ISP_IS_V_OFFS), + RKISP1_DEBUG_SHD_REG(ISP_IS_H_SIZE), + RKISP1_DEBUG_SHD_REG(ISP_IS_V_SIZE), { /* Sentinel */ }, }; struct rkisp1_device *rkisp1 = m->private; @@ -217,6 +221,8 @@ void rkisp1_debug_init(struct rkisp1_device *rkisp1) &debug->frame_drop[RKISP1_MAINPATH]); debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir, &debug->frame_drop[RKISP1_SELFPATH]); + debugfs_create_ulong("complete_frames", 0444, debug->debugfs_dir, + &debug->complete_frames); debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp1, &rkisp1_debug_input_status_fops); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index c41abd2833f1..f96f821a7b50 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -114,6 +114,7 @@ struct rkisp1_isr_data { const char *name; irqreturn_t (*isr)(int irq, void *ctx); + u32 line_mask; }; /* ---------------------------------------------------------------------------- @@ -442,17 +443,25 @@ error: static irqreturn_t rkisp1_isr(int irq, void *ctx) { + irqreturn_t ret = IRQ_NONE; + /* * Call rkisp1_capture_isr() first to handle the frame that * potentially completed using the current frame_sequence number before * it is potentially incremented by rkisp1_isp_isr() in the vertical * sync. */ - rkisp1_capture_isr(irq, ctx); - rkisp1_isp_isr(irq, ctx); - rkisp1_csi_isr(irq, ctx); - return IRQ_HANDLED; + if (rkisp1_capture_isr(irq, ctx) == IRQ_HANDLED) + ret = IRQ_HANDLED; + + if (rkisp1_isp_isr(irq, ctx) == IRQ_HANDLED) + ret = IRQ_HANDLED; + + if (rkisp1_csi_isr(irq, ctx) == IRQ_HANDLED) + ret = IRQ_HANDLED; + + return ret; } static const char * const px30_isp_clks[] = { @@ -463,9 +472,9 @@ static const char * const px30_isp_clks[] = { }; static const struct rkisp1_isr_data px30_isp_isrs[] = { - { "isp", rkisp1_isp_isr }, - { "mi", rkisp1_capture_isr }, - { "mipi", rkisp1_csi_isr }, + { "isp", rkisp1_isp_isr, BIT(RKISP1_IRQ_ISP) }, + { "mi", rkisp1_capture_isr, BIT(RKISP1_IRQ_MI) }, + { "mipi", rkisp1_csi_isr, BIT(RKISP1_IRQ_MIPI) }, }; static const struct rkisp1_info px30_isp_info = { @@ -484,7 +493,7 @@ static const char * const rk3399_isp_clks[] = { }; static const struct rkisp1_isr_data rk3399_isp_isrs[] = { - { NULL, rkisp1_isr }, + { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) | BIT(RKISP1_IRQ_MIPI) }, }; static const struct rkisp1_info rk3399_isp_info = { @@ -535,6 +544,9 @@ static int rkisp1_probe(struct platform_device *pdev) if (IS_ERR(rkisp1->base_addr)) return PTR_ERR(rkisp1->base_addr); + for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) + rkisp1->irqs[il] = -1; + for (i = 0; i < info->isr_size; i++) { irq = info->isrs[i].name ? platform_get_irq_byname(pdev, info->isrs[i].name) @@ -542,7 +554,12 @@ static int rkisp1_probe(struct platform_device *pdev) if (irq < 0) return irq; - ret = devm_request_irq(dev, irq, info->isrs[i].isr, IRQF_SHARED, + for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) { + if (info->isrs[i].line_mask & BIT(il)) + rkisp1->irqs[il] = irq; + } + + ret = devm_request_irq(dev, irq, info->isrs[i].isr, 0, dev_driver_string(dev), dev); if (ret) { dev_err(dev, "request irq failed: %d\n", ret); @@ -582,7 +599,7 @@ static int rkisp1_probe(struct platform_device *pdev) ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev); if (ret) - goto err_pm_runtime_disable; + goto err_media_dev_cleanup; ret = media_device_register(&rkisp1->media_dev); if (ret) { @@ -617,6 +634,8 @@ err_unreg_media_dev: media_device_unregister(&rkisp1->media_dev); err_unreg_v4l2_dev: v4l2_device_unregister(&rkisp1->v4l2_dev); +err_media_dev_cleanup: + media_device_cleanup(&rkisp1->media_dev); err_pm_runtime_disable: pm_runtime_disable(&pdev->dev); return ret; @@ -637,6 +656,8 @@ static void rkisp1_remove(struct platform_device *pdev) media_device_unregister(&rkisp1->media_dev); v4l2_device_unregister(&rkisp1->v4l2_dev); + media_device_cleanup(&rkisp1->media_dev); + pm_runtime_disable(&pdev->dev); } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 88ca8b2283b7..f00873d31c42 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -66,8 +66,8 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = - v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; @@ -102,12 +102,12 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, const struct v4l2_mbus_framefmt *sink_frm; const struct v4l2_rect *sink_crop; - sink_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code); src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code); @@ -201,8 +201,8 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, } else { struct v4l2_mbus_framefmt *src_frm; - src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat, src_frm->quantization, src_frm->ycbcr_enc); @@ -254,11 +254,25 @@ static void rkisp1_isp_stop(struct rkisp1_isp *isp) * ISP(mi) stop in mi frame end -> Stop ISP(mipi) -> * Stop ISP(isp) ->wait for ISP isp off */ - /* stop and clear MI and ISP interrupts */ - rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0); - rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0); + /* Mask MI and ISP interrupts */ + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0); rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0); + + /* Flush posted writes */ + rkisp1_read(rkisp1, RKISP1_CIF_MI_IMSC); + + /* + * Wait until the IRQ handler has ended. The IRQ handler may get called + * even after this, but it will return immediately as the MI and ISP + * interrupts have been masked. + */ + synchronize_irq(rkisp1->irqs[RKISP1_IRQ_ISP]); + if (rkisp1->irqs[RKISP1_IRQ_ISP] != rkisp1->irqs[RKISP1_IRQ_MI]) + synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MI]); + + /* Clear MI and ISP interrupt status */ + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0); rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0); /* stop ISP */ @@ -318,8 +332,8 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp, RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE; rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); src_info = rkisp1_mbus_info_get_by_code(src_fmt->code); if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER) @@ -409,15 +423,15 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rkisp1_isp_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_isp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop, *src_crop; /* Video. */ - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -427,15 +441,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - sink_crop = v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_fmt = *sink_fmt; src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; src_fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -443,15 +457,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - src_crop = v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_crop = *sink_crop; /* Parameters and statistics. */ - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_PARAMS); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_STATS); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_PARAMS); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_STATS); sink_fmt->width = 0; sink_fmt->height = 0; sink_fmt->field = V4L2_FIELD_NONE; @@ -472,12 +486,12 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, const struct v4l2_rect *src_crop; bool set_csc; - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); /* * Media bus code. The ISP can operate in pass-through mode (Bayer in, @@ -570,10 +584,10 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, const struct v4l2_rect *sink_crop; struct v4l2_rect *src_crop; - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); src_crop->left = ALIGN(r->left, 2); src_crop->width = ALIGN(r->width, 2); @@ -584,8 +598,8 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, *r = *src_crop; /* Propagate to out format */ - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt); } @@ -596,10 +610,10 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, struct v4l2_rect *sink_crop, *src_crop; const struct v4l2_mbus_framefmt *sink_fmt; - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->left = ALIGN(r->left, 2); sink_crop->width = ALIGN(r->width, 2); @@ -610,8 +624,8 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, *r = *sink_crop; /* Propagate to out crop */ - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_isp_set_src_crop(isp, sd_state, src_crop); } @@ -624,8 +638,8 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->code = format->code; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { @@ -673,8 +687,8 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, *format = *sink_fmt; /* Propagate to in crop */ - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop); } @@ -689,7 +703,8 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format); else - fmt->format = *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -709,19 +724,19 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sel->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sel->pad); sel->r.height = fmt->height; sel->r.width = fmt->width; sel->r.left = 0; sel->r.top = 0; } else { - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); } break; case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); break; default: @@ -768,7 +783,6 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .enum_frame_size = rkisp1_isp_enum_frame_size, .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, - .init_cfg = rkisp1_isp_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_isp_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -879,6 +893,10 @@ static const struct v4l2_subdev_ops rkisp1_isp_ops = { .pad = &rkisp1_isp_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_isp_internal_ops = { + .init_state = rkisp1_isp_init_state, +}; + int rkisp1_isp_register(struct rkisp1_device *rkisp1) { struct rkisp1_isp *isp = &rkisp1->isp; @@ -889,6 +907,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) isp->rkisp1 = rkisp1; v4l2_subdev_init(sd, &rkisp1_isp_ops); + sd->internal_ops = &rkisp1_isp_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkisp1_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; @@ -933,6 +952,7 @@ void rkisp1_isp_unregister(struct rkisp1_device *rkisp1) return; v4l2_device_unregister_subdev(&isp->sd); + v4l2_subdev_cleanup(&isp->sd); media_entity_cleanup(&isp->sd.entity); } @@ -989,6 +1009,8 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) if (status & RKISP1_CIF_ISP_FRAME) { u32 isp_ris; + rkisp1->debug.complete_frames++; + /* New frame from the sensor received */ isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS); if (isp_ris & RKISP1_STATS_MEAS_MASK) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index 350f452e676f..bea69a0d766a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -172,12 +172,9 @@ #define RKISP1_CIF_MI_FRAME(stream) BIT((stream)->id) #define RKISP1_CIF_MI_MBLK_LINE BIT(2) #define RKISP1_CIF_MI_FILL_MP_Y BIT(3) -#define RKISP1_CIF_MI_WRAP_MP_Y BIT(4) -#define RKISP1_CIF_MI_WRAP_MP_CB BIT(5) -#define RKISP1_CIF_MI_WRAP_MP_CR BIT(6) -#define RKISP1_CIF_MI_WRAP_SP_Y BIT(7) -#define RKISP1_CIF_MI_WRAP_SP_CB BIT(8) -#define RKISP1_CIF_MI_WRAP_SP_CR BIT(9) +#define RKISP1_CIF_MI_WRAP_Y(stream) BIT(4 + (stream)->id * 3) +#define RKISP1_CIF_MI_WRAP_CB(stream) BIT(5 + (stream)->id * 3) +#define RKISP1_CIF_MI_WRAP_CR(stream) BIT(6 + (stream)->id * 3) #define RKISP1_CIF_MI_DMA_READY BIT(11) /* MI_STATUS */ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 28ecc7347d54..a8e377701302 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -142,10 +142,8 @@ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, struct v4l2_rect *sink_crop; u32 dc_ctrl; - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); if (sink_crop->width == sink_fmt->width && sink_crop->height == sink_fmt->height && @@ -275,10 +273,8 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, struct v4l2_area src_y, src_c; struct v4l2_rect sink_c; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code); src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); @@ -292,8 +288,7 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, return; } - sink_y = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_y = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_c.width = sink_y->width / sink_yuv_info->hdiv; sink_c.height = sink_y->height / sink_yuv_info->vdiv; @@ -335,12 +330,8 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, { struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); - struct v4l2_subdev_pad_config dummy_cfg; - struct v4l2_subdev_state pad_state = { - .pads = &dummy_cfg - }; - u32 pad = code->pad; - int ret; + unsigned int index = code->index; + unsigned int i; if (code->pad == RKISP1_RSZ_PAD_SRC) { /* supported mbus codes on the src are the same as in the capture */ @@ -360,25 +351,38 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, return 0; } - /* supported mbus codes on the sink pad are the same as isp src pad */ - code->pad = RKISP1_ISP_PAD_SOURCE_VIDEO; - ret = v4l2_subdev_call(&rsz->rkisp1->isp.sd, pad, enum_mbus_code, - &pad_state, code); + /* + * Supported mbus codes on the sink pad are the same as on the ISP + * source pad. + */ + for (i = 0; ; i++) { + const struct rkisp1_mbus_info *fmt = + rkisp1_mbus_info_get_by_index(i); + + if (!fmt) + break; - /* restore pad */ - code->pad = pad; - code->flags = 0; - return ret; + if (!(fmt->direction & RKISP1_ISP_SD_SRC)) + continue; + + if (!index) { + code->code = fmt->mbus_code; + return 0; + } + + index--; + } + + return -EINVAL; } -static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_rsz_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -388,15 +392,13 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); *src_fmt = *sink_fmt; /* NOTE: there is no crop in the source pad, only in the sink */ @@ -411,10 +413,8 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, const struct rkisp1_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -441,10 +441,8 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); /* Not crop for MP bayer raw data */ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -478,12 +476,9 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); if (rsz->id == RKISP1_SELFPATH) sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; @@ -573,8 +568,8 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - mf_sink = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + mf_sink = v4l2_subdev_state_get_format(sd_state, + RKISP1_RSZ_PAD_SINK); sel->r.height = mf_sink->height; sel->r.width = mf_sink->width; sel->r.left = 0; @@ -582,8 +577,8 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, break; case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_RSZ_PAD_SINK); break; default: @@ -620,7 +615,6 @@ static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = { .enum_mbus_code = rkisp1_rsz_enum_mbus_code, .get_selection = rkisp1_rsz_get_selection, .set_selection = rkisp1_rsz_set_selection, - .init_cfg = rkisp1_rsz_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_rsz_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -667,6 +661,10 @@ static const struct v4l2_subdev_ops rkisp1_rsz_ops = { .pad = &rkisp1_rsz_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_rsz_internal_ops = { + .init_state = rkisp1_rsz_init_state, +}; + static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) { if (!rsz->rkisp1) @@ -696,6 +694,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) } v4l2_subdev_init(sd, &rkisp1_rsz_ops); + sd->internal_ops = &rkisp1_rsz_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_rsz_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h index 1ea5fa1bf3c8..b9777e07fb6d 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h @@ -26,7 +26,6 @@ #include "gsc-regs.h" -#define CONFIG_VB2_GSC_DMA_CONTIG 1 #define GSC_MODULE_NAME "exynos-gsc" #define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index a0d43bf892e6..05cafba1c728 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -1479,7 +1479,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1534,7 +1534,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; return 0; } @@ -1604,10 +1604,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: @@ -1651,10 +1651,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index 97908778e1c8..0be687b01ce5 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -814,7 +814,7 @@ err: fimc_clk_put(fimc); dev_err(&fimc->pdev->dev, "failed to get clock: %s\n", fimc_clocks[i]); - return -ENXIO; + return ret; } #ifdef CONFIG_PM diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c index bef6e9b4a25e..44363c4241d5 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c @@ -57,7 +57,6 @@ static int fimc_is_i2c_probe(struct platform_device *pdev) strscpy(i2c_adap->name, "exynos4x12-isp-i2c", sizeof(i2c_adap->name)); i2c_adap->owner = THIS_MODULE; i2c_adap->algo = &fimc_is_i2c_algorithm; - i2c_adap->class = I2C_CLASS_SPD; platform_set_drvdata(pdev, isp_i2c); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c index b85986e50f46..3c5d7bee2655 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c @@ -126,7 +126,7 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *mf = *v4l2_subdev_state_get_format(sd_state, fmt->pad); return 0; } @@ -172,9 +172,8 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(&isp->subdev, - sd_state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, + FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -207,7 +206,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, __isp_subdev_try_format(isp, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -220,8 +219,8 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, format.pad = pad; __isp_subdev_try_format(isp, sd_state, &format); - mf = v4l2_subdev_get_try_format(sd, sd_state, - pad); + mf = v4l2_subdev_state_get_format(sd_state, + pad); *mf = format.format; } } @@ -374,18 +373,17 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, .field = V4L2_FIELD_NONE, }; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, FIMC_ISP_SD_PAD_SINK); *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index 9396b10b5b1c..7898c9bebb04 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -574,16 +574,14 @@ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + rect = v4l2_subdev_state_get_crop(sd_state, + FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; mf->colorspace = sink->fmt->colorspace; @@ -1021,7 +1019,7 @@ static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, @@ -1129,7 +1127,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1166,7 +1164,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 686ca8753ba2..aae8a8b2c0f4 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -569,8 +569,7 @@ static struct v4l2_mbus_framefmt *__s5pcsis_get_format( enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&state->sd, - sd_state, 0) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, 0) : NULL; return &state->format; } diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index 0f5b3845d7b9..be58260ea67e 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1357,7 +1357,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1445,7 +1445,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h new file mode 100644 index 000000000000..24e669d8ea29 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Register definition file for Samsung MFC V12.x Interface (FIMV) driver + * + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + */ + +#ifndef _REGS_MFC_V12_H +#define _REGS_MFC_V12_H + +#include <linux/sizes.h> +#include "regs-mfc-v10.h" + +/* MFCv12 Context buffer sizes */ +#define MFC_CTX_BUF_SIZE_V12 (30 * SZ_1K) +#define MFC_H264_DEC_CTX_BUF_SIZE_V12 (2 * SZ_1M) +#define MFC_OTHER_DEC_CTX_BUF_SIZE_V12 (30 * SZ_1K) +#define MFC_H264_ENC_CTX_BUF_SIZE_V12 (100 * SZ_1K) +#define MFC_HEVC_ENC_CTX_BUF_SIZE_V12 (40 * SZ_1K) +#define MFC_OTHER_ENC_CTX_BUF_SIZE_V12 (25 * SZ_1K) + +/* MFCv12 variant defines */ +#define MAX_FW_SIZE_V12 (SZ_1M) +#define MAX_CPB_SIZE_V12 (7 * SZ_1M) +#define MFC_VERSION_V12 0xC0 +#define MFC_NUM_PORTS_V12 1 +#define S5P_FIMV_CODEC_VP9_ENC 27 +#define MFC_CHROMA_PAD_BYTES_V12 256 +#define S5P_FIMV_D_ALIGN_PLANE_SIZE_V12 256 + +/* Encoder buffer size for MFCv12 */ +#define ENC_V120_BASE_SIZE(x, y) \ + ((((x) + 3) * ((y) + 3) * 8) \ + + ((((y) * 64) + 2304) * ((x) + 7) / 8)) + +#define ENC_V120_H264_ME_SIZE(x, y) \ + ALIGN((ENC_V120_BASE_SIZE(x, y) \ + + (DIV_ROUND_UP((x) * (y), 64) * 32)), 256) + +#define ENC_V120_MPEG4_ME_SIZE(x, y) \ + ALIGN((ENC_V120_BASE_SIZE(x, y) \ + + (DIV_ROUND_UP((x) * (y), 128) * 16)), 256) + +#define ENC_V120_VP8_ME_SIZE(x, y) \ + ALIGN(ENC_V120_BASE_SIZE((x), (y)), 256) + +#define ENC_V120_HEVC_ME_SIZE(x, y) \ + ALIGN(((((x) + 3) * ((y) + 3) * 32) \ + + ((((y) * 128) + 2304) * ((x) + 3) / 4)), 256) + +#endif /*_REGS_MFC_V12_H*/ diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h index 4a7adfdaa359..50f9bf0603c1 100644 --- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h @@ -24,6 +24,7 @@ #define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7 0xfa70 #define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7 0xfa74 +#define S5P_FIMV_E_ENCODED_SOURCE_THIRD_ADDR_V7 0xfa78 #define S5P_FIMV_E_VP8_OPTIONS_V7 0xfdb0 #define S5P_FIMV_E_VP8_FILTER_OPTIONS_V7 0xfdb4 diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h index 162e3c7e920f..0ef9eb2dff22 100644 --- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h @@ -17,13 +17,16 @@ #define S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE_V8 0xf108 #define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8 0xf144 #define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8 0xf148 +#define S5P_FIMV_D_THIRD_PLANE_DPB_SIZE_V8 0xf14C #define S5P_FIMV_D_MV_BUFFER_SIZE_V8 0xf150 #define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8 0xf138 #define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8 0xf13c +#define S5P_FIMV_D_THIRD_PLANE_DPB_STRIDE_SIZE_V8 0xf140 #define S5P_FIMV_D_FIRST_PLANE_DPB_V8 0xf160 #define S5P_FIMV_D_SECOND_PLANE_DPB_V8 0xf260 +#define S5P_FIMV_D_THIRD_PLANE_DPB_V8 0xf360 #define S5P_FIMV_D_MV_BUFFER_V8 0xf460 #define S5P_FIMV_D_NUM_MV_V8 0xf134 diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index e30e54935d79..fbb047eadf5a 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -604,6 +604,8 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, s5p_mfc_clock_off(); wake_up(&ctx->queue); + if (ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1) + set_work_bit_irqsave(ctx); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); } else { WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); @@ -790,6 +792,8 @@ static int s5p_mfc_open(struct file *file) INIT_LIST_HEAD(&ctx->dst_queue); ctx->src_queue_cnt = 0; ctx->dst_queue_cnt = 0; + ctx->is_422 = 0; + ctx->is_10bit = 0; /* Get context number */ ctx->num = 0; while (dev->ctx[ctx->num]) { @@ -863,7 +867,7 @@ static int s5p_mfc_open(struct file *file) q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); } else if (vdev == dev->vfd_enc) { - q->io_modes = VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->ops = get_enc_queue_ops(); } else { ret = -ENOENT; @@ -890,7 +894,7 @@ static int s5p_mfc_open(struct file *file) q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); } else if (vdev == dev->vfd_enc) { - q->io_modes = VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->ops = get_enc_queue_ops(); } else { ret = -ENOENT; @@ -1660,6 +1664,31 @@ static struct s5p_mfc_variant mfc_drvdata_v10 = { .fw_name[0] = "s5p-mfc-v10.fw", }; +static struct s5p_mfc_buf_size_v6 mfc_buf_size_v12 = { + .dev_ctx = MFC_CTX_BUF_SIZE_V12, + .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V12, + .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V12, + .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V12, + .hevc_enc_ctx = MFC_HEVC_ENC_CTX_BUF_SIZE_V12, + .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V12, +}; + +static struct s5p_mfc_buf_size buf_size_v12 = { + .fw = MAX_FW_SIZE_V12, + .cpb = MAX_CPB_SIZE_V12, + .priv = &mfc_buf_size_v12, +}; + +static struct s5p_mfc_variant mfc_drvdata_v12 = { + .version = MFC_VERSION_V12, + .version_bit = MFC_V12_BIT, + .port_num = MFC_NUM_PORTS_V12, + .buf_size = &buf_size_v12, + .fw_name[0] = "s5p-mfc-v12.fw", + .clk_names = {"mfc"}, + .num_clocks = 1, +}; + static const struct of_device_id exynos_mfc_match[] = { { .compatible = "samsung,mfc-v5", @@ -1682,6 +1711,9 @@ static const struct of_device_id exynos_mfc_match[] = { }, { .compatible = "samsung,mfc-v10", .data = &mfc_drvdata_v10, + }, { + .compatible = "tesla,fsd-mfc", + .data = &mfc_drvdata_v12, }, {}, }; diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h index 5304f42c8c72..59450b324f7d 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h @@ -19,7 +19,7 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-v4l2.h> #include "regs-mfc.h" -#include "regs-mfc-v10.h" +#include "regs-mfc-v12.h" #define S5P_MFC_NAME "s5p-mfc" @@ -56,6 +56,7 @@ #define MFC_NO_INSTANCE_SET -1 #define MFC_ENC_CAP_PLANE_COUNT 1 #define MFC_ENC_OUT_PLANE_COUNT 2 +#define VB2_MAX_PLANE_COUNT 3 #define STUFF_BYTE 4 #define MFC_MAX_CTRLS 128 @@ -181,6 +182,7 @@ struct s5p_mfc_buf { struct { size_t luma; size_t chroma; + size_t chroma_1; } raw; size_t stream; } cookie; @@ -621,6 +623,10 @@ struct s5p_mfc_codec_ops { * v4l2 control framework * @ctrl_handler: handler for v4l2 framework * @scratch_buf_size: scratch buffer size + * @is_10bit: state to check 10bit support + * @is_422: state to check YUV422 10bit format + * @chroma_size_1: size of a chroma third plane + * @stride: size of stride for all planes */ struct s5p_mfc_ctx { struct s5p_mfc_dev *dev; @@ -657,6 +663,7 @@ struct s5p_mfc_ctx { int luma_size; int chroma_size; + int chroma_size_1; int mv_size; unsigned long consumed_stream; @@ -720,6 +727,9 @@ struct s5p_mfc_ctx { struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS]; struct v4l2_ctrl_handler ctrl_handler; size_t scratch_buf_size; + int is_10bit; + int is_422; + int stride[VB2_MAX_PLANE_COUNT]; }; /* @@ -771,22 +781,27 @@ void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq); #define HAS_PORTNUM(dev) (dev ? (dev->variant ? \ (dev->variant->port_num ? 1 : 0) : 0) : 0) #define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0) -#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0) -#define IS_MFCV7_PLUS(dev) (dev->variant->version >= 0x70 ? 1 : 0) -#define IS_MFCV8_PLUS(dev) (dev->variant->version >= 0x80 ? 1 : 0) -#define IS_MFCV10(dev) (dev->variant->version >= 0xA0 ? 1 : 0) -#define FW_HAS_E_MIN_SCRATCH_BUF(dev) (IS_MFCV10(dev)) +#define IS_MFCV6_PLUS(dev) ((dev)->variant->version >= 0x60) +#define IS_MFCV7_PLUS(dev) ((dev)->variant->version >= 0x70) +#define IS_MFCV8_PLUS(dev) ((dev)->variant->version >= 0x80) +#define IS_MFCV10_PLUS(dev) ((dev)->variant->version >= 0xA0) +#define IS_MFCV12(dev) ((dev)->variant->version >= 0xC0) +#define FW_HAS_E_MIN_SCRATCH_BUF(dev) (IS_MFCV10_PLUS(dev)) #define MFC_V5_BIT BIT(0) #define MFC_V6_BIT BIT(1) #define MFC_V7_BIT BIT(2) #define MFC_V8_BIT BIT(3) #define MFC_V10_BIT BIT(5) +#define MFC_V12_BIT BIT(7) #define MFC_V5PLUS_BITS (MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | \ - MFC_V8_BIT | MFC_V10_BIT) + MFC_V8_BIT | MFC_V10_BIT | MFC_V12_BIT) #define MFC_V6PLUS_BITS (MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT | \ - MFC_V10_BIT) -#define MFC_V7PLUS_BITS (MFC_V7_BIT | MFC_V8_BIT | MFC_V10_BIT) + MFC_V10_BIT | MFC_V12_BIT) +#define MFC_V7PLUS_BITS (MFC_V7_BIT | MFC_V8_BIT | MFC_V10_BIT | \ + MFC_V12_BIT) + +#define MFC_V10PLUS_BITS (MFC_V10_BIT | MFC_V12_BIT) #endif /* S5P_MFC_COMMON_H_ */ diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c index 6d3c92045c05..503487f34a80 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c @@ -51,8 +51,14 @@ int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) * into kernel. */ mfc_debug_enter(); - if (dev->fw_get_done) - return 0; + /* In case of MFC v12, RET_SYS_INIT response from hardware fails due to + * incorrect firmware transfer and therefore it is not able to initialize + * the hardware. This causes failed response for SYS_INIT command when + * MFC runs for second time. So, load the MFC v12 firmware for each run. + */ + if (!IS_MFCV12(dev)) + if (dev->fw_get_done) + return 0; for (i = MFC_FW_MAX_VERSIONS - 1; i >= 0; i--) { if (!dev->variant->fw_name[i]) @@ -130,7 +136,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev) mfc_write(dev, 0, S5P_FIMV_REG_CLEAR_BEGIN_V6 + (i*4)); /* check bus reset control before reset */ - if (dev->risc_on) + if (dev->risc_on && !IS_MFCV12(dev)) if (s5p_mfc_bus_reset(dev)) return -EIO; /* Reset @@ -236,7 +242,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) else mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_write(dev, 0x0, S5P_FIMV_MFC_CLOCK_OFF_V10); mfc_debug(2, "Will now wait for completion of firmware transfer\n"); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c index 268ffe4da53c..3957f28d4547 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c @@ -57,6 +57,20 @@ static struct s5p_mfc_fmt formats[] = { .versions = MFC_V6PLUS_BITS, }, { + .fourcc = V4L2_PIX_FMT_YUV420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT + }, + { .fourcc = V4L2_PIX_FMT_H264, .codec_mode = S5P_MFC_CODEC_H264_DEC, .type = MFC_FMT_DEC, @@ -146,7 +160,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_FIMV_CODEC_HEVC_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V10_BIT, + .versions = MFC_V10PLUS_BITS, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, @@ -155,7 +169,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_FIMV_CODEC_VP9_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V10_BIT, + .versions = MFC_V10PLUS_BITS, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, }; @@ -355,14 +369,19 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_mp->width = ctx->buf_width; pix_mp->height = ctx->buf_height; pix_mp->field = V4L2_FIELD_NONE; - pix_mp->num_planes = 2; + pix_mp->num_planes = ctx->dst_fmt->num_planes; /* Set pixelformat to the format in which MFC outputs the decoded frame */ pix_mp->pixelformat = ctx->dst_fmt->fourcc; - pix_mp->plane_fmt[0].bytesperline = ctx->buf_width; + pix_mp->plane_fmt[0].bytesperline = ctx->stride[0]; pix_mp->plane_fmt[0].sizeimage = ctx->luma_size; - pix_mp->plane_fmt[1].bytesperline = ctx->buf_width; + pix_mp->plane_fmt[1].bytesperline = ctx->stride[1]; pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size; + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + pix_mp->plane_fmt[2].bytesperline = ctx->stride[2]; + pix_mp->plane_fmt[2].sizeimage = ctx->chroma_size_1; + } } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { /* This is run on OUTPUT The buffer contains compressed image @@ -920,6 +939,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; + const struct v4l2_format_info *format; /* Video output for decoding (source) * this can be set after getting an instance */ @@ -936,7 +956,13 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, } else if (ctx->state == MFCINST_HEAD_PARSED && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { /* Output plane count is 2 - one for Y and one for CbCr */ - *plane_count = 2; + format = v4l2_format_info(ctx->dst_fmt->fourcc); + if (!format) { + mfc_err("invalid format\n"); + return -EINVAL; + } + *plane_count = format->comp_planes; + /* Setup buffer count */ if (*buf_count < ctx->pb_count) *buf_count = ctx->pb_count; @@ -955,14 +981,18 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; - + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + psize[2] = ctx->chroma_size_1; if (IS_MFCV6_PLUS(dev)) alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX]; else alloc_devs[0] = ctx->dev->mem_dev[BANK_R_CTX]; alloc_devs[1] = ctx->dev->mem_dev[BANK_L_CTX]; - } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - ctx->state == MFCINST_INIT) { + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + alloc_devs[2] = ctx->dev->mem_dev[BANK_L_CTX]; + } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && ctx->state == MFCINST_INIT) { psize[0] = ctx->dec_src_buf_size; alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX]; } else { @@ -994,12 +1024,24 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) mfc_err("Plane buffer (CAPTURE) is too small\n"); return -EINVAL; } + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + if (vb2_plane_size(vb, 2) < ctx->chroma_size_1) { + mfc_err("Plane buffer (CAPTURE) is too small\n"); + return -EINVAL; + } + } i = vb->index; ctx->dst_bufs[i].b = vbuf; ctx->dst_bufs[i].cookie.raw.luma = vb2_dma_contig_plane_dma_addr(vb, 0); ctx->dst_bufs[i].cookie.raw.chroma = vb2_dma_contig_plane_dma_addr(vb, 1); + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + ctx->dst_bufs[i].cookie.raw.chroma_1 = + vb2_dma_contig_plane_dma_addr(vb, 2); + } ctx->dst_bufs_cnt++; } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (IS_ERR_OR_NULL(ERR_PTR( diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c index 4b4c129c09e7..ef8bb40b9712 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c @@ -60,6 +60,20 @@ static struct s5p_mfc_fmt formats[] = { .versions = MFC_V6PLUS_BITS, }, { + .fourcc = V4L2_PIX_FMT_YUV420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT, + }, + { .fourcc = V4L2_PIX_FMT_H264, .codec_mode = S5P_MFC_CODEC_H264_ENC, .type = MFC_FMT_ENC, @@ -92,7 +106,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_FIMV_CODEC_HEVC_ENC, .type = MFC_FMT_ENC, .num_planes = 1, - .versions = MFC_V10_BIT, + .versions = MFC_V10PLUS_BITS, }, }; @@ -1150,7 +1164,6 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_buf *dst_mb; - unsigned int enc_pb_count; if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) { if (!list_empty(&ctx->dst_queue)) { @@ -1172,14 +1185,12 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) set_work_bit_irqsave(ctx); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); } else { - enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops, - get_enc_dpb_count, dev); - if (ctx->pb_count < enc_pb_count) - ctx->pb_count = enc_pb_count; + ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_enc_dpb_count, dev); if (FW_HAS_E_MIN_SCRATCH_BUF(dev)) { ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, get_e_min_scratch_buf_size, dev); - ctx->bank1.size += ctx->scratch_buf_size; + if (!IS_MFCV12(dev)) + ctx->bank1.size += ctx->scratch_buf_size; } ctx->state = MFCINST_HEAD_PRODUCED; } @@ -1192,14 +1203,20 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *dst_mb; struct s5p_mfc_buf *src_mb; - unsigned long src_y_addr, src_c_addr, dst_addr; + unsigned long src_y_addr, src_c_addr, src_c_1_addr, dst_addr; unsigned int dst_size; src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0); src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + src_c_1_addr = + vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 2); + else + src_c_1_addr = 0; s5p_mfc_hw_call(dev->mfc_ops, set_enc_frame_buffer, ctx, - src_y_addr, src_c_addr); + src_y_addr, src_c_addr, src_c_1_addr); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); @@ -1214,8 +1231,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *mb_entry; - unsigned long enc_y_addr = 0, enc_c_addr = 0; - unsigned long mb_y_addr, mb_c_addr; + unsigned long enc_y_addr = 0, enc_c_addr = 0, enc_c_1_addr = 0; + unsigned long mb_y_addr, mb_c_addr, mb_c_1_addr; int slice_type; unsigned int strm_size; bool src_ready; @@ -1228,18 +1245,26 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT)); if (slice_type >= 0) { s5p_mfc_hw_call(dev->mfc_ops, get_enc_frame_buffer, ctx, - &enc_y_addr, &enc_c_addr); + &enc_y_addr, &enc_c_addr, &enc_c_1_addr); list_for_each_entry(mb_entry, &ctx->src_queue, list) { mb_y_addr = vb2_dma_contig_plane_dma_addr( &mb_entry->b->vb2_buf, 0); mb_c_addr = vb2_dma_contig_plane_dma_addr( &mb_entry->b->vb2_buf, 1); - if ((enc_y_addr == mb_y_addr) && - (enc_c_addr == mb_c_addr)) { + if (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || + ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + mb_c_1_addr = vb2_dma_contig_plane_dma_addr + (&mb_entry->b->vb2_buf, 2); + else + mb_c_1_addr = 0; + if (enc_y_addr == mb_y_addr && enc_c_addr == mb_c_addr && enc_c_1_addr + == mb_c_1_addr) { list_del(&mb_entry->list); ctx->src_queue_cnt--; vb2_buffer_done(&mb_entry->b->vb2_buf, - VB2_BUF_STATE_DONE); + VB2_BUF_STATE_DONE); break; } } @@ -1248,20 +1273,27 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) &mb_entry->b->vb2_buf, 0); mb_c_addr = vb2_dma_contig_plane_dma_addr( &mb_entry->b->vb2_buf, 1); - if ((enc_y_addr == mb_y_addr) && - (enc_c_addr == mb_c_addr)) { + if (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || + ctx->src_fmt->fourcc == V4L2_PIX_FMT_YVU420M) + mb_c_1_addr = vb2_dma_contig_plane_dma_addr(& + mb_entry->b->vb2_buf, 2); + else + mb_c_1_addr = 0; + if (enc_y_addr == mb_y_addr && enc_c_addr == mb_c_addr && enc_c_1_addr + == mb_c_1_addr) { list_del(&mb_entry->list); ctx->ref_queue_cnt--; vb2_buffer_done(&mb_entry->b->vb2_buf, - VB2_BUF_STATE_DONE); + VB2_BUF_STATE_DONE); break; } } } if (ctx->src_queue_cnt > 0 && (ctx->state == MFCINST_RUNNING || - ctx->state == MFCINST_FINISHING)) { + ctx->state == MFCINST_FINISHING)) { mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, - list); + list); if (mb_entry->flags & MFC_BUF_FLAG_USED) { list_del(&mb_entry->list); ctx->src_queue_cnt--; @@ -1380,10 +1412,15 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc; pix_fmt_mp->num_planes = ctx->src_fmt->num_planes; - pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[0].bytesperline = ctx->stride[0]; pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size; - pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[1].bytesperline = ctx->stride[1]; pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size; + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + pix_fmt_mp->plane_fmt[2].bytesperline = ctx->stride[2]; + pix_fmt_mp->plane_fmt[2].sizeimage = ctx->chroma_size_1; + } } else { mfc_err("invalid buf type\n"); return -EINVAL; @@ -1420,9 +1457,12 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; } - - v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1, - &pix_fmt_mp->height, 4, 1080, 1, 0); + if (IS_MFCV12(dev)) + v4l_bound_align_image(&pix_fmt_mp->width, 8, 3840, 1, &pix_fmt_mp + ->height, 4, 2160, 1, 0); + else + v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1, &pix_fmt_mp + ->height, 4, 1080, 1, 0); } else { mfc_err("invalid buf type\n"); return -EINVAL; @@ -1467,9 +1507,14 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) s5p_mfc_hw_call(dev->mfc_ops, enc_calc_src_size, ctx); pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size; - pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[0].bytesperline = ctx->stride[0]; pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size; - pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[1].bytesperline = ctx->stride[1]; + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + pix_fmt_mp->plane_fmt[2].bytesperline = ctx->stride[2]; + pix_fmt_mp->plane_fmt[2].sizeimage = ctx->chroma_size_1; + } ctx->src_bufs_cnt = 0; ctx->output_state = QUEUE_FREE; @@ -1489,9 +1534,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; - /* if memory is not mmp or userptr return error */ + /* if memory is not mmp or userptr or dmabuf return error */ if ((reqbufs->memory != V4L2_MEMORY_MMAP) && - (reqbufs->memory != V4L2_MEMORY_USERPTR)) + (reqbufs->memory != V4L2_MEMORY_USERPTR) && + (reqbufs->memory != V4L2_MEMORY_DMABUF)) return -EINVAL; if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (reqbufs->count == 0) { @@ -1514,14 +1560,6 @@ static int vidioc_reqbufs(struct file *file, void *priv, } ctx->capture_state = QUEUE_BUFS_REQUESTED; - ret = s5p_mfc_hw_call(ctx->dev->mfc_ops, - alloc_codec_buffers, ctx); - if (ret) { - mfc_err("Failed to allocate encoding buffers\n"); - reqbufs->count = 0; - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - return -ENOMEM; - } } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (reqbufs->count == 0) { mfc_debug(2, "Freeing buffers\n"); @@ -1537,15 +1575,13 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; } - if (IS_MFCV6_PLUS(dev)) { + if (IS_MFCV6_PLUS(dev) && (!IS_MFCV12(dev))) { /* Check for min encoder buffers */ if (ctx->pb_count && (reqbufs->count < ctx->pb_count)) { reqbufs->count = ctx->pb_count; mfc_debug(2, "Minimum %d output buffers needed\n", ctx->pb_count); - } else { - ctx->pb_count = reqbufs->count; } } @@ -1568,9 +1604,10 @@ static int vidioc_querybuf(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; - /* if memory is not mmp or userptr return error */ + /* if memory is not mmp or userptr or dmabuf return error */ if ((buf->memory != V4L2_MEMORY_MMAP) && - (buf->memory != V4L2_MEMORY_USERPTR)) + (buf->memory != V4L2_MEMORY_USERPTR) && + (buf->memory != V4L2_MEMORY_DMABUF)) return -EINVAL; if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (ctx->state != MFCINST_GOT_INST) { @@ -2413,10 +2450,18 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; + if (ctx->src_fmt && (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M)) + psize[2] = ctx->chroma_size_1; if (IS_MFCV6_PLUS(dev)) { alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX]; alloc_devs[1] = ctx->dev->mem_dev[BANK_L_CTX]; + if (ctx->src_fmt && (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M)) + alloc_devs[2] = ctx->dev->mem_dev[BANK_L_CTX]; } else { alloc_devs[0] = ctx->dev->mem_dev[BANK_R_CTX]; alloc_devs[1] = ctx->dev->mem_dev[BANK_R_CTX]; @@ -2455,6 +2500,11 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) vb2_dma_contig_plane_dma_addr(vb, 0); ctx->src_bufs[i].cookie.raw.chroma = vb2_dma_contig_plane_dma_addr(vb, 1); + if (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + ctx->src_bufs[i].cookie.raw.chroma_1 = + vb2_dma_contig_plane_dma_addr(vb, 2); ctx->src_bufs_cnt++; } else { mfc_err("invalid queue type: %d\n", vq->type); @@ -2492,6 +2542,12 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb) mfc_err("plane size is too small for output\n"); return -EINVAL; } + if ((ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || + ctx->src_fmt->fourcc == V4L2_PIX_FMT_YVU420M) && + (vb2_plane_size(vb, 2) < ctx->chroma_size_1)) { + mfc_err("plane size is too small for output\n"); + return -EINVAL; + } } else { mfc_err("invalid queue type: %d\n", vq->type); return -EINVAL; @@ -2513,11 +2569,11 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) S5P_MFC_R2H_CMD_SEQ_DONE_RET, 0); } - - if (ctx->src_bufs_cnt < ctx->pb_count) { - mfc_err("Need minimum %d OUTPUT buffers\n", - ctx->pb_count); - return -ENOBUFS; + if (q->memory != V4L2_MEMORY_DMABUF) { + if (ctx->src_bufs_cnt < ctx->pb_count) { + mfc_err("Need minimum %d OUTPUT buffers\n", ctx->pb_count); + return -ENOBUFS; + } } } diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h index b9831275f3ab..7c5e851c8191 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h @@ -166,9 +166,9 @@ struct s5p_mfc_regs { void __iomem *d_decoded_third_addr;/* only v7 */ void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */ void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */ - void __iomem *d_min_scratch_buffer_size; /* v10 */ - void __iomem *d_static_buffer_addr; /* v10 */ - void __iomem *d_static_buffer_size; /* v10 */ + void __iomem *d_min_scratch_buffer_size; /* v10 and v12 */ + void __iomem *d_static_buffer_addr; /* v10 and v12 */ + void __iomem *d_static_buffer_size; /* v10 and v12 */ /* encoder registers */ void __iomem *e_frame_width; @@ -268,7 +268,7 @@ struct s5p_mfc_regs { void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ - void __iomem *e_min_scratch_buffer_size; /* v10 */ + void __iomem *e_min_scratch_buffer_size; /* v10 and v12 */ void __iomem *e_num_t_layer; /* v10 */ void __iomem *e_hier_qp_layer0; /* v10 */ void __iomem *e_hier_bit_rate_layer0; /* v10 */ @@ -293,9 +293,11 @@ struct s5p_mfc_hw_ops { int (*set_enc_stream_buffer)(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size); void (*set_enc_frame_buffer)(struct s5p_mfc_ctx *ctx, - unsigned long y_addr, unsigned long c_addr); + unsigned long y_addr, unsigned long c_addr, + unsigned long c_1_addr); void (*get_enc_frame_buffer)(struct s5p_mfc_ctx *ctx, - unsigned long *y_addr, unsigned long *c_addr); + unsigned long *y_addr, unsigned long *c_addr, + unsigned long *c_1_addr); void (*try_run)(struct s5p_mfc_dev *dev); void (*clear_int_flags)(struct s5p_mfc_dev *dev); int (*get_dspl_y_adr)(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c index 28a06dc343fd..fcfaf125a5a1 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c @@ -516,7 +516,8 @@ static int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, } static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, - unsigned long y_addr, unsigned long c_addr) + unsigned long y_addr, unsigned long c_addr, + unsigned long c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -525,7 +526,8 @@ static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, } static void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, - unsigned long *y_addr, unsigned long *c_addr) + unsigned long *y_addr, unsigned long *c_addr, + unsigned long *c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1210,7 +1212,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) if (list_empty(&ctx->src_queue)) { /* send null frame */ s5p_mfc_set_enc_frame_buffer_v5(ctx, dev->dma_base[BANK_R_CTX], - dev->dma_base[BANK_R_CTX]); + dev->dma_base[BANK_R_CTX], 0); src_mb = NULL; } else { src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, @@ -1220,7 +1222,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) /* send null frame */ s5p_mfc_set_enc_frame_buffer_v5(ctx, dev->dma_base[BANK_R_CTX], - dev->dma_base[BANK_R_CTX]); + dev->dma_base[BANK_R_CTX], 0); ctx->state = MFCINST_FINISHING; } else { src_y_addr = vb2_dma_contig_plane_dma_addr( @@ -1228,7 +1230,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) src_c_addr = vb2_dma_contig_plane_dma_addr( &src_mb->b->vb2_buf, 1); s5p_mfc_set_enc_frame_buffer_v5(ctx, src_y_addr, - src_c_addr); + src_c_addr, 0); if (src_mb->flags & MFC_BUF_FLAG_EOS) ctx->state = MFCINST_FINISHING; } diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c index c0df5ac9fcff..fd945211d28e 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c @@ -60,21 +60,23 @@ static void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx) static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - unsigned int mb_width, mb_height; + unsigned int mb_width, mb_height, width64, height32; unsigned int lcu_width = 0, lcu_height = 0; int ret; mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); + width64 = ALIGN(ctx->img_width, 64); + height32 = ALIGN(ctx->img_height, 32); if (ctx->type == MFCINST_DECODER) { mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n", ctx->luma_size, ctx->chroma_size, ctx->mv_size); mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count); } else if (ctx->type == MFCINST_ENCODER) { - if (IS_MFCV10(dev)) { + if (IS_MFCV10_PLUS(dev)) ctx->tmv_buffer_size = 0; - } else if (IS_MFCV8_PLUS(dev)) + else if (IS_MFCV8_PLUS(dev)) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); @@ -82,7 +84,39 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + lcu_width = S5P_MFC_LCU_WIDTH(ctx->img_width); + lcu_height = S5P_MFC_LCU_HEIGHT(ctx->img_height); + if (ctx->codec_mode == S5P_FIMV_CODEC_HEVC_ENC && ctx->is_10bit) { + ctx->luma_dpb_size = + width64 * height32 + + ALIGN(DIV_ROUND_UP(lcu_width * 32, 4), 16) * height32 + 128; + if (ctx->is_422) + ctx->chroma_dpb_size = + ctx->luma_dpb_size; + else + ctx->chroma_dpb_size = + width64 * height32 / 2 + + ALIGN(DIV_ROUND_UP(lcu_width * + 32, 4), 16) * height32 / 2 + 128; + } else if (ctx->codec_mode == S5P_FIMV_CODEC_VP9_ENC && ctx->is_10bit) { + ctx->luma_dpb_size = + ALIGN(ctx->img_width * 2, 128) * height32 + 64; + ctx->chroma_dpb_size = + ALIGN(ctx->img_width * 2, 128) * height32 / 2 + 64; + } else { + ctx->luma_dpb_size = + width64 * height32 + 64; + if (ctx->is_422) + ctx->chroma_dpb_size = + ctx->luma_dpb_size; + else + ctx->chroma_dpb_size = + width64 * height32 / 2 + 64; + } + ctx->luma_dpb_size = ALIGN(ctx->luma_dpb_size + 256, SZ_2K); + ctx->chroma_dpb_size = ALIGN(ctx->chroma_dpb_size + 256, SZ_2K); + } else if (IS_MFCV10_PLUS(dev)) { lcu_width = S5P_MFC_LCU_WIDTH(ctx->img_width); lcu_height = S5P_MFC_LCU_HEIGHT(ctx->img_height); if (ctx->codec_mode != S5P_FIMV_CODEC_HEVC_ENC) { @@ -133,7 +167,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: case S5P_MFC_CODEC_H264_MVC_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = @@ -152,7 +186,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) (ctx->mv_count * ctx->mv_size); break; case S5P_MFC_CODEC_MPEG4_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else if (IS_MFCV7_PLUS(dev)) { ctx->scratch_buf_size = @@ -172,7 +206,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else ctx->scratch_buf_size = @@ -189,7 +223,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else ctx->scratch_buf_size = @@ -201,7 +235,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = @@ -230,7 +264,11 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) DEC_VP9_STATIC_BUFFER_SIZE; break; case S5P_MFC_CODEC_H264_ENC: - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ENC_V120_H264_ME_SIZE(mb_width, mb_height); + } else if (IS_MFCV10_PLUS(dev)) { mfc_debug(2, "Use min scratch buffer size\n"); ctx->me_buffer_size = ALIGN(ENC_V100_H264_ME_SIZE(mb_width, mb_height), 16); @@ -254,7 +292,11 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ENC_V120_MPEG4_ME_SIZE(mb_width, mb_height); + } else if (IS_MFCV10_PLUS(dev)) { mfc_debug(2, "Use min scratch buffer size\n"); ctx->me_buffer_size = ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width, @@ -265,7 +307,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_width, mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, - S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); + S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->pb_count * (ctx->luma_dpb_size + @@ -273,7 +315,11 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VP8_ENC: - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ENC_V120_VP8_ME_SIZE(mb_width, mb_height); + } else if (IS_MFCV10_PLUS(dev)) { mfc_debug(2, "Use min scratch buffer size\n"); ctx->me_buffer_size = ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height), @@ -297,9 +343,13 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_HEVC_ENC: + if (IS_MFCV12(dev)) + ctx->me_buffer_size = + ENC_V120_HEVC_ME_SIZE(lcu_width, lcu_height); + else + ctx->me_buffer_size = + ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 16); mfc_debug(2, "Use min scratch buffer size\n"); - ctx->me_buffer_size = - ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 16); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256); ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + @@ -438,30 +488,48 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6); + ctx->chroma_size_1 = 0; mfc_debug(2, "SEQ Done: Movie dimensions %dx%d,\n" "buffer dimensions: %dx%d\n", ctx->img_width, ctx->img_height, ctx->buf_width, ctx->buf_height); - ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height); - ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1)); + switch (ctx->dst_fmt->fourcc) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->luma_size = calc_plane(ctx->stride[0], ctx->img_height); + ctx->chroma_size = calc_plane(ctx->stride[1], (ctx->img_height / 2)); + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->stride[2] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->luma_size = calc_plane(ctx->stride[0], ctx->img_height); + ctx->chroma_size = calc_plane(ctx->stride[1], (ctx->img_height / 2)); + ctx->chroma_size_1 = calc_plane(ctx->stride[2], (ctx->img_height / 2)); + break; + } + if (IS_MFCV8_PLUS(ctx->dev)) { /* MFCv8 needs additional 64 bytes for luma,chroma dpb*/ ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; + ctx->chroma_size_1 += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { - if (IS_MFCV10(dev)) { - ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V10(ctx->img_width, - ctx->img_height); - } else { - ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width, - ctx->img_height); - } + if (IS_MFCV12(dev)) + ctx->mv_size = S5P_MFC_DEC_MV_SIZE(ctx->img_width, ctx->img_height, 1024); + else if (IS_MFCV10_PLUS(dev)) + ctx->mv_size = S5P_MFC_DEC_MV_SIZE(ctx->img_width, ctx->img_height, 512); + else + ctx->mv_size = S5P_MFC_DEC_MV_SIZE(ctx->img_width, ctx->img_height, 128); + } else if (ctx->codec_mode == S5P_MFC_CODEC_HEVC_DEC) { - ctx->mv_size = s5p_mfc_dec_hevc_mv_size(ctx->img_width, - ctx->img_height); + ctx->mv_size = s5p_mfc_dec_hevc_mv_size(ctx->img_width, ctx->img_height); ctx->mv_size = ALIGN(ctx->mv_size, 32); } else { ctx->mv_size = 0; @@ -475,14 +543,40 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); - ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); - ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256); - ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256); - - /* MFCv7 needs pad bytes for Luma and Chroma */ - if (IS_MFCV7_PLUS(ctx->dev)) { + if (IS_MFCV12(ctx->dev)) { + switch (ctx->src_fmt->fourcc) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->luma_size = ctx->stride[0] * ALIGN(ctx->img_height, 16); + ctx->chroma_size = ctx->stride[0] * ALIGN(ctx->img_height / 2, 16); + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[2] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12M_HALIGN_V6); + ctx->luma_size = ctx->stride[0] * ALIGN(ctx->img_height, 16); + ctx->chroma_size = ctx->stride[1] * ALIGN(ctx->img_height / 2, 16); + ctx->chroma_size_1 = ctx->stride[2] * ALIGN(ctx->img_height / 2, 16); + break; + } ctx->luma_size += MFC_LUMA_PAD_BYTES_V7; - ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7; + ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V12; + ctx->chroma_size_1 += MFC_CHROMA_PAD_BYTES_V12; + } else { + ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[0] = ctx->buf_width; + ctx->stride[1] = ctx->buf_width; + ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256); + ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256); + ctx->chroma_size_1 = 0; + /* MFCv7 needs pad bytes for Luma and Chroma */ + if (IS_MFCV7_PLUS(ctx->dev)) { + ctx->luma_size += MFC_LUMA_PAD_BYTES_V7; + ctx->chroma_size += MFC_LUMA_PAD_BYTES_V7; + } } } @@ -529,15 +623,18 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) writel(ctx->total_dpb_count, mfc_regs->d_num_dpb); writel(ctx->luma_size, mfc_regs->d_first_plane_dpb_size); writel(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size); - + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + writel(ctx->chroma_size_1, mfc_regs->d_third_plane_dpb_size); writel(buf_addr1, mfc_regs->d_scratch_buffer_addr); writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); if (IS_MFCV8_PLUS(dev)) { - writel(ctx->img_width, - mfc_regs->d_first_plane_dpb_stride_size); - writel(ctx->img_width, - mfc_regs->d_second_plane_dpb_stride_size); + writel(ctx->stride[0], mfc_regs->d_first_plane_dpb_stride_size); + writel(ctx->stride[1], mfc_regs->d_second_plane_dpb_stride_size); + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + writel(ctx->stride[2], mfc_regs->d_third_plane_dpb_stride_size); } buf_addr1 += ctx->scratch_buf_size; @@ -566,6 +663,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) ctx->dst_bufs[i].cookie.raw.chroma); writel(ctx->dst_bufs[i].cookie.raw.chroma, mfc_regs->d_second_plane_dpb + i * 4); + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + mfc_debug(2, "\tChroma_1 %d: %zx\n", i, ctx + ->dst_bufs[i].cookie.raw.chroma_1); + writel(ctx->dst_bufs[i].cookie.raw.chroma_1, mfc_regs->d_third_plane_dpb + + i * 4); + } } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC || @@ -624,20 +728,24 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, } static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, - unsigned long y_addr, unsigned long c_addr) + unsigned long y_addr, unsigned long c_addr, + unsigned long c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; writel(y_addr, mfc_regs->e_source_first_plane_addr); writel(c_addr, mfc_regs->e_source_second_plane_addr); + writel(c_1_addr, mfc_regs->e_source_third_plane_addr); mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr); mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr); + mfc_debug(2, "enc src cr buf addr: 0x%08lx\n", c_1_addr); } static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, - unsigned long *y_addr, unsigned long *c_addr) + unsigned long *y_addr, unsigned long *c_addr, + unsigned long *c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; @@ -645,12 +753,17 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, *y_addr = readl(mfc_regs->e_encoded_source_first_plane_addr); *c_addr = readl(mfc_regs->e_encoded_source_second_plane_addr); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + *c_1_addr = readl(mfc_regs->e_encoded_source_third_plane_addr); + else + *c_1_addr = 0; enc_recon_y_addr = readl(mfc_regs->e_recon_luma_dpb_addr); enc_recon_c_addr = readl(mfc_regs->e_recon_chroma_dpb_addr); mfc_debug(2, "recon y addr: 0x%08lx y_addr: 0x%08lx\n", enc_recon_y_addr, *y_addr); - mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr); + mfc_debug(2, "recon c addr: 0x%08lx c_addr: 0x%08lx\n", enc_recon_c_addr, *c_addr); } /* Set encoding ref & codec buffer */ @@ -668,7 +781,7 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); - if (IS_MFCV10(dev)) { + if (IS_MFCV10_PLUS(dev)) { /* start address of per buffer is aligned */ for (i = 0; i < ctx->pb_count; i++) { writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); @@ -827,6 +940,20 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) writel(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ writel(0x0, mfc_regs->pixel_format); + } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YVU420M) { + /* 0: Linear, 1: 2D tiled*/ + reg = readl(mfc_regs->e_enc_options); + reg &= ~(0x1 << 7); + writel(reg, mfc_regs->e_enc_options); + /* 2: YV12(CrCb), 3: I420(CrCb) */ + writel(0x2, mfc_regs->pixel_format); + } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M) { + /* 0: Linear, 1: 2D tiled*/ + reg = readl(mfc_regs->e_enc_options); + reg &= ~(0x1 << 7); + writel(reg, mfc_regs->e_enc_options); + /* 2: YV12(CrCb), 3: I420(CrCb) */ + writel(0x3, mfc_regs->pixel_format); } /* memory structure recon. frame */ @@ -865,10 +992,24 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) /* reaction coefficient */ if (p->rc_frame) { - if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */ - writel(1, mfc_regs->e_rc_mode); - else /* loose CBR */ - writel(2, mfc_regs->e_rc_mode); + if (IS_MFCV12(dev)) { + /* loose CBR */ + if (p->rc_reaction_coeff < LOOSE_CBR_MAX) + writel(1, mfc_regs->e_rc_mode); + /* tight CBR */ + else if (p->rc_reaction_coeff < TIGHT_CBR_MAX) + writel(0, mfc_regs->e_rc_mode); + /* VBR */ + else + writel(2, mfc_regs->e_rc_mode); + } else { + /* tight CBR */ + if (p->rc_reaction_coeff < TIGHT_CBR_MAX) + writel(1, mfc_regs->e_rc_mode); + /* loose CBR */ + else + writel(2, mfc_regs->e_rc_mode); + } } /* seq header ctrl */ @@ -930,6 +1071,18 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p->num_b_frame & 0x3) << 16); writel(reg, mfc_regs->e_gop_config); + /* UHD encoding case */ + if (ctx->img_width == 3840 && ctx->img_height == 2160) { + if (p_h264->level < 51) { + mfc_debug(2, "Set Level 5.1 for UHD\n"); + p_h264->level = 51; + } + if (p_h264->profile != 0x2) { + mfc_debug(2, "Set High profile for UHD\n"); + p_h264->profile = 0x2; + } + } + /* profile & level */ reg = 0; /** level */ @@ -1637,8 +1790,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) else writel(reg, mfc_regs->d_dec_options); - /* 0: NV12(CbCr), 1: NV21(CrCb) */ - if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) + /* 0: NV12(CbCr), 1: NV21(CrCb), 2: YV12(CrCb), 3: I420(CbCr) */ + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M) + writel(0x3, mfc_regs->pixel_format); + else if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YVU420M) + writel(0x2, mfc_regs->pixel_format); + else if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) writel(0x1, mfc_regs->pixel_format); else writel(0x0, mfc_regs->pixel_format); @@ -1722,8 +1879,11 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) /* Set stride lengths for v7 & above */ if (IS_MFCV7_PLUS(dev)) { - writel(ctx->img_width, mfc_regs->e_source_first_plane_stride); - writel(ctx->img_width, mfc_regs->e_source_second_plane_stride); + writel(ctx->stride[0], mfc_regs->e_source_first_plane_stride); + writel(ctx->stride[1], mfc_regs->e_source_second_plane_stride); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + writel(ctx->stride[2], mfc_regs->e_source_third_plane_stride); } writel(ctx->inst_no, mfc_regs->instance_id); @@ -1832,7 +1992,7 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *dst_mb; struct s5p_mfc_buf *src_mb; - unsigned long src_y_addr, src_c_addr, dst_addr; + unsigned long src_y_addr, src_c_addr, src_c_1_addr, dst_addr; /* unsigned int src_y_size, src_c_size; */ @@ -1850,22 +2010,28 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) if (list_empty(&ctx->src_queue)) { /* send null frame */ - s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0); + s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0, 0); src_mb = NULL; } else { src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_mb->flags |= MFC_BUF_FLAG_USED; if (src_mb->b->vb2_buf.planes[0].bytesused == 0) { - s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0); + s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0, 0); ctx->state = MFCINST_FINISHING; } else { src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0); src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + src_c_1_addr = vb2_dma_contig_plane_dma_addr + (&src_mb->b->vb2_buf, 2); + else + src_c_1_addr = 0; mfc_debug(2, "enc src y addr: 0x%08lx\n", src_y_addr); mfc_debug(2, "enc src c addr: 0x%08lx\n", src_c_addr); - s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr); + s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr, src_c_1_addr); if (src_mb->flags & MFC_BUF_FLAG_EOS) ctx->state = MFCINST_FINISHING; } @@ -1944,6 +2110,13 @@ static inline int s5p_mfc_run_init_enc_buffers(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; int ret; + ret = s5p_mfc_hw_call(ctx->dev->mfc_ops, alloc_codec_buffers, ctx); + if (ret) { + mfc_err("Failed to allocate encoding buffers\n"); + return -ENOMEM; + } + mfc_debug(2, "Allocated Internal Encoding Buffers\n"); + dev->curr_ctx = ctx->num; ret = s5p_mfc_set_enc_ref_buffer_v6(ctx); if (ret) { @@ -2387,10 +2560,9 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(e_source_first_plane_stride, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); R(e_source_second_plane_stride, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); R(e_source_third_plane_stride, S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7); - R(e_encoded_source_first_plane_addr, - S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); - R(e_encoded_source_second_plane_addr, - S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); + R(e_encoded_source_first_plane_addr, S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); + R(e_encoded_source_second_plane_addr, S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); + R(e_encoded_source_third_plane_addr, S5P_FIMV_E_ENCODED_SOURCE_THIRD_ADDR_V7); R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7); if (!IS_MFCV8_PLUS(dev)) @@ -2405,16 +2577,17 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V8); R(d_first_plane_dpb_size, S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8); R(d_second_plane_dpb_size, S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8); + R(d_third_plane_dpb_size, S5P_FIMV_D_THIRD_PLANE_DPB_SIZE_V8); R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8); R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8); - R(d_first_plane_dpb_stride_size, - S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8); - R(d_second_plane_dpb_stride_size, - S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8); + R(d_first_plane_dpb_stride_size, S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8); + R(d_second_plane_dpb_stride_size, S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8); + R(d_third_plane_dpb_stride_size, S5P_FIMV_D_THIRD_PLANE_DPB_STRIDE_SIZE_V8); R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V8); R(d_num_mv, S5P_FIMV_D_NUM_MV_V8); R(d_first_plane_dpb, S5P_FIMV_D_FIRST_PLANE_DPB_V8); R(d_second_plane_dpb, S5P_FIMV_D_SECOND_PLANE_DPB_V8); + R(d_third_plane_dpb, S5P_FIMV_D_THIRD_PLANE_DPB_V8); R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V8); R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8); R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8); @@ -2455,7 +2628,7 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8); R(e_min_scratch_buffer_size, S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE_V8); - if (!IS_MFCV10(dev)) + if (!IS_MFCV10_PLUS(dev)) goto done; /* Initialize registers used in MFC v10 only. diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h index e4dd03c5454c..94ecb0e6e7c7 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h @@ -19,10 +19,8 @@ #define MB_WIDTH(x_size) DIV_ROUND_UP(x_size, 16) #define MB_HEIGHT(y_size) DIV_ROUND_UP(y_size, 16) -#define S5P_MFC_DEC_MV_SIZE_V6(x, y) (MB_WIDTH(x) * \ - (((MB_HEIGHT(y)+1)/2)*2) * 64 + 128) -#define S5P_MFC_DEC_MV_SIZE_V10(x, y) (MB_WIDTH(x) * \ - (((MB_HEIGHT(y)+1)/2)*2) * 64 + 512) +#define S5P_MFC_DEC_MV_SIZE(x, y, offset) (MB_WIDTH(x) * \ + (((MB_HEIGHT(y) + 1) / 2) * 2) * 64 + (offset)) #define S5P_MFC_LCU_WIDTH(x_size) DIV_ROUND_UP(x_size, 32) #define S5P_MFC_LCU_HEIGHT(y_size) DIV_ROUND_UP(y_size, 32) @@ -42,6 +40,7 @@ #define ENC_H264_LEVEL_MAX 42 #define ENC_MPEG4_VOP_TIME_RES_MAX ((1 << 16) - 1) #define FRAME_DELTA_H264_H263 1 +#define LOOSE_CBR_MAX 5 #define TIGHT_CBR_MAX 10 #define ENC_HEVC_RC_FRAME_RATE_MAX ((1 << 16) - 1) #define ENC_HEVC_QP_INDEX_MIN -12 diff --git a/drivers/media/platform/st/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c index 3a848ca32a0e..161a5c0fbc4e 100644 --- a/drivers/media/platform/st/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c @@ -569,14 +569,11 @@ static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) struct vb2_buffer *vb2_buf; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type); - - if (buf->index >= vq->num_buffers) { - dev_dbg(dev, "%s buffer index %d out of range (%d)\n", - ctx->name, buf->index, vq->num_buffers); + vb2_buf = vb2_get_buffer(vq, buf->index); + if (!vb2_buf) { + dev_dbg(dev, "%s buffer index %d not found\n", ctx->name, buf->index); return -EINVAL; } - - vb2_buf = vb2_get_buffer(vq, buf->index); stream = to_hva_stream(to_vb2_v4l2_buffer(vb2_buf)); stream->bytesused = buf->bytesused; } @@ -1145,7 +1142,7 @@ static int hva_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->buf_struct_size = sizeof(struct hva_frame); - src_vq->min_buffers_needed = MIN_FRAMES; + src_vq->min_queued_buffers = MIN_FRAMES; src_vq->dev = ctx->hva_dev->dev; ret = queue_init(ctx, src_vq); @@ -1154,7 +1151,7 @@ static int hva_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->buf_struct_size = sizeof(struct hva_stream); - dst_vq->min_buffers_needed = MIN_STREAMS; + dst_vq->min_queued_buffers = MIN_STREAMS; dst_vq->dev = ctx->hva_dev->dev; return queue_init(ctx, dst_vq); diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig index b22dd4753496..9df9a2a17728 100644 --- a/drivers/media/platform/st/stm32/Kconfig +++ b/drivers/media/platform/st/stm32/Kconfig @@ -16,6 +16,22 @@ config VIDEO_STM32_DCMI To compile this driver as a module, choose M here: the module will be called stm32-dcmi. +config VIDEO_STM32_DCMIPP + tristate "STM32 Digital Camera Memory Interface Pixel Processor (DCMIPP) support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_STM32 || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This module makes the STM32 Digital Camera Memory Interface + Pixel Processor (DCMIPP) available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-dcmipp. + # Mem2mem drivers config VIDEO_STM32_DMA2D tristate "STM32 Chrom-Art Accelerator (DMA2D)" diff --git a/drivers/media/platform/st/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile index 896ef98a73ab..7ed8297b9b19 100644 --- a/drivers/media/platform/st/stm32/Makefile +++ b/drivers/media/platform/st/stm32/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o +obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp/ stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32-dma2d.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 8cb4fdcae137..c4610e305546 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> @@ -1890,7 +1889,6 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) static int dcmi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *match = NULL; struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; struct stm32_dcmi *dcmi; struct vb2_queue *q; @@ -1899,12 +1897,6 @@ static int dcmi_probe(struct platform_device *pdev) struct clk *mclk; int ret = 0; - match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Could not find a match in devicetree\n"); - return -ENODEV; - } - dcmi = devm_kzalloc(&pdev->dev, sizeof(struct stm32_dcmi), GFP_KERNEL); if (!dcmi) return -ENOMEM; @@ -2039,7 +2031,7 @@ static int dcmi_probe(struct platform_device *pdev) q->ops = &dcmi_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->allow_cache_hints = 1; q->dev = &pdev->dev; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile new file mode 100644 index 000000000000..8920d9388a21 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-parallel.o dcmipp-byteproc.o dcmipp-bytecap.o + +obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c new file mode 100644 index 000000000000..9f768f011fa2 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/iopoll.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "dcmipp-common.h" + +#define DCMIPP_PRSR 0x1f8 +#define DCMIPP_CMIER 0x3f0 +#define DCMIPP_CMIER_P0FRAMEIE BIT(9) +#define DCMIPP_CMIER_P0VSYNCIE BIT(10) +#define DCMIPP_CMIER_P0OVRIE BIT(15) +#define DCMIPP_CMIER_P0ALL (DCMIPP_CMIER_P0VSYNCIE |\ + DCMIPP_CMIER_P0FRAMEIE |\ + DCMIPP_CMIER_P0OVRIE) +#define DCMIPP_CMSR1 0x3f4 +#define DCMIPP_CMSR2 0x3f8 +#define DCMIPP_CMSR2_P0FRAMEF BIT(9) +#define DCMIPP_CMSR2_P0VSYNCF BIT(10) +#define DCMIPP_CMSR2_P0OVRF BIT(15) +#define DCMIPP_CMFCR 0x3fc +#define DCMIPP_P0FSCR 0x404 +#define DCMIPP_P0FSCR_PIPEN BIT(31) +#define DCMIPP_P0FCTCR 0x500 +#define DCMIPP_P0FCTCR_CPTREQ BIT(3) +#define DCMIPP_P0DCCNTR 0x5b0 +#define DCMIPP_P0DCLMTR 0x5b4 +#define DCMIPP_P0DCLMTR_ENABLE BIT(31) +#define DCMIPP_P0DCLMTR_LIMIT_MASK GENMASK(23, 0) +#define DCMIPP_P0PPM0AR1 0x5c4 +#define DCMIPP_P0SR 0x5f8 +#define DCMIPP_P0SR_CPTACT BIT(23) + +struct dcmipp_bytecap_pix_map { + unsigned int code; + u32 pixelformat; +}; + +#define PIXMAP_MBUS_PFMT(mbus, fmt) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .pixelformat = V4L2_PIX_FMT_##fmt \ + } + +static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { + PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565), + PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV), + PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU), + PIXMAP_MBUS_PFMT(UYVY8_2X8, UYVY), + PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY), + PIXMAP_MBUS_PFMT(Y8_1X8, GREY), + PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8), + PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8), + PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8), + PIXMAP_MBUS_PFMT(SRGGB8_1X8, SRGGB8), + PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG), +}; + +static const struct dcmipp_bytecap_pix_map * +dcmipp_bytecap_pix_map_by_pixelformat(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + if (dcmipp_bytecap_pix_map_list[i].pixelformat == pixelformat) + return &dcmipp_bytecap_pix_map_list[i]; + } + + return NULL; +} + +struct dcmipp_buf { + struct vb2_v4l2_buffer vb; + bool prepared; + dma_addr_t addr; + size_t size; + struct list_head list; +}; + +enum dcmipp_state { + DCMIPP_STOPPED = 0, + DCMIPP_WAIT_FOR_BUFFER, + DCMIPP_RUNNING, +}; + +struct dcmipp_bytecap_device { + struct dcmipp_ent_device ved; + struct video_device vdev; + struct device *dev; + struct v4l2_pix_format format; + struct vb2_queue queue; + struct list_head buffers; + /* + * Protects concurrent calls of buf queue / irq handler + * and buffer handling related variables / lists + */ + spinlock_t irqlock; + /* mutex used as vdev and queue lock */ + struct mutex lock; + u32 sequence; + struct media_pipeline pipe; + struct v4l2_subdev *s_subdev; + + enum dcmipp_state state; + + /* + * DCMIPP driver is handling 2 buffers + * active: buffer into which DCMIPP is currently writing into + * next: buffer given to the DCMIPP and which will become + * automatically active on next VSYNC + */ + struct dcmipp_buf *active, *next; + + void __iomem *regs; + + u32 cmier; + u32 cmsr2; + + struct { + u32 errors; + u32 limit; + u32 overrun; + u32 buffers; + u32 vsync; + u32 frame; + u32 it; + u32 underrun; + u32 nactive; + } count; +}; + +static const struct v4l2_pix_format fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .pixelformat = V4L2_PIX_FMT_RGB565, + .field = V4L2_FIELD_NONE, + .bytesperline = DCMIPP_FMT_WIDTH_DEFAULT * 2, + .sizeimage = DCMIPP_FMT_WIDTH_DEFAULT * DCMIPP_FMT_HEIGHT_DEFAULT * 2, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_bytecap_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, DCMIPP_PDEV_NAME, sizeof(cap->driver)); + strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); + + return 0; +} + +static int dcmipp_bytecap_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dcmipp_bytecap_device *vcap = video_drvdata(file); + + f->fmt.pix = vcap->format; + + return 0; +} + +static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dcmipp_bytecap_device *vcap = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + const struct dcmipp_bytecap_pix_map *vpix; + u32 in_w, in_h; + + /* Don't accept a pixelformat that is not on the table */ + vpix = dcmipp_bytecap_pix_map_by_pixelformat(format->pixelformat); + if (!vpix) + format->pixelformat = fmt_default.pixelformat; + + /* Adjust width & height */ + in_w = format->width; + in_h = format->height; + v4l_bound_align_image(&format->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH, 0, &format->height, + DCMIPP_FRAME_MIN_HEIGHT, DCMIPP_FRAME_MAX_HEIGHT, + 0, 0); + if (format->width != in_w || format->height != in_h) + dev_dbg(vcap->dev, "resolution updated: %dx%d -> %dx%d\n", + in_w, in_h, format->width, format->height); + + if (format->pixelformat == V4L2_PIX_FMT_JPEG) { + format->bytesperline = format->width; + format->sizeimage = format->bytesperline * format->height; + } else { + v4l2_fill_pixfmt(format, format->pixelformat, + format->width, format->height); + } + + if (format->field == V4L2_FIELD_ANY) + format->field = fmt_default.field; + + dcmipp_colorimetry_clamp(format); + + return 0; +} + +static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dcmipp_bytecap_device *vcap = video_drvdata(file); + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&vcap->queue)) + return -EBUSY; + + ret = dcmipp_bytecap_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(vcap->dev, "%s: format update: old:%ux%u (0x%p4cc, %u, %u, %u, %u) new:%ux%d (0x%p4cc, %u, %u, %u, %u)\n", + vcap->vdev.name, + /* old */ + vcap->format.width, vcap->format.height, + &vcap->format.pixelformat, vcap->format.colorspace, + vcap->format.quantization, vcap->format.xfer_func, + vcap->format.ycbcr_enc, + /* new */ + f->fmt.pix.width, f->fmt.pix.height, + &f->fmt.pix.pixelformat, f->fmt.pix.colorspace, + f->fmt.pix.quantization, f->fmt.pix.xfer_func, + f->fmt.pix.ycbcr_enc); + + vcap->format = f->fmt.pix; + + return 0; +} + +static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct dcmipp_bytecap_pix_map *vpix; + unsigned int index = f->index; + unsigned int i; + + if (f->mbus_code) { + /* + * If a media bus code is specified, only enumerate formats + * compatible with it. + */ + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + vpix = &dcmipp_bytecap_pix_map_list[i]; + if (vpix->code != f->mbus_code) + continue; + + if (index == 0) + break; + + index--; + } + + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + } else { + /* Otherwise, enumerate all formats. */ + if (f->index >= ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + + vpix = &dcmipp_bytecap_pix_map_list[f->index]; + } + + f->pixelformat = vpix->pixelformat; + + return 0; +} + +static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct dcmipp_bytecap_pix_map *vpix; + + if (fsize->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_bytecap_pix_map_by_pixelformat(fsize->pixel_format); + if (!vpix) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = DCMIPP_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = DCMIPP_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = DCMIPP_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = DCMIPP_FRAME_MAX_HEIGHT; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + +static const struct v4l2_file_operations dcmipp_bytecap_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = { + .vidioc_querycap = dcmipp_bytecap_querycap, + + .vidioc_g_fmt_vid_cap = dcmipp_bytecap_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = dcmipp_bytecap_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = dcmipp_bytecap_try_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = dcmipp_bytecap_enum_fmt_vid_cap, + .vidioc_enum_framesizes = dcmipp_bytecap_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static int dcmipp_pipeline_s_stream(struct dcmipp_bytecap_device *vcap, + int state) +{ + struct media_pad *pad; + int ret; + + /* + * Get source subdev - since link is IMMUTABLE, pointer is cached + * within the dcmipp_bytecap_device structure + */ + if (!vcap->s_subdev) { + pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); + } + + ret = v4l2_subdev_call(vcap->s_subdev, video, s_stream, state); + if (ret < 0) { + dev_err(vcap->dev, "failed to %s streaming (%d)\n", + state ? "start" : "stop", ret); + return ret; + } + + return 0; +} + +static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap, + struct dcmipp_buf *buf) +{ + /* Set buffer address */ + reg_write(vcap, DCMIPP_P0PPM0AR1, buf->addr); + + /* Set buffer size */ + reg_write(vcap, DCMIPP_P0DCLMTR, DCMIPP_P0DCLMTR_ENABLE | + ((buf->size / 4) & DCMIPP_P0DCLMTR_LIMIT_MASK)); + + /* Capture request */ + reg_set(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); +} + +static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap, + enum vb2_buffer_state state) +{ + struct dcmipp_buf *buf, *node; + + list_for_each_entry_safe(buf, node, &vcap->buffers, list) { + list_del_init(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); + struct media_entity *entity = &vcap->vdev.entity; + struct dcmipp_buf *buf; + int ret; + + vcap->sequence = 0; + memset(&vcap->count, 0, sizeof(vcap->count)); + + ret = pm_runtime_resume_and_get(vcap->dev); + if (ret < 0) { + dev_err(vcap->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", + __func__, ret); + goto err_buffer_done; + } + + ret = media_pipeline_start(entity->pads, &vcap->pipe); + if (ret) { + dev_dbg(vcap->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n", + __func__, ret); + goto err_pm_put; + } + + ret = dcmipp_pipeline_s_stream(vcap, 1); + if (ret) + goto err_media_pipeline_stop; + + spin_lock_irq(&vcap->irqlock); + + /* Enable pipe at the end of programming */ + reg_set(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN); + + /* + * vb2 framework guarantee that we have at least 'min_queued_buffers' + * buffers in the list at this moment + */ + vcap->next = list_first_entry(&vcap->buffers, typeof(*buf), list); + dev_dbg(vcap->dev, "Start with next [%d] %p phy=%pad\n", + vcap->next->vb.vb2_buf.index, vcap->next, &vcap->next->addr); + + dcmipp_start_capture(vcap, vcap->next); + + /* Enable interruptions */ + vcap->cmier |= DCMIPP_CMIER_P0ALL; + reg_set(vcap, DCMIPP_CMIER, vcap->cmier); + + vcap->state = DCMIPP_RUNNING; + + spin_unlock_irq(&vcap->irqlock); + + return 0; + +err_media_pipeline_stop: + media_pipeline_stop(entity->pads); +err_pm_put: + pm_runtime_put(vcap->dev); +err_buffer_done: + spin_lock_irq(&vcap->irqlock); + /* + * Return all buffers to vb2 in QUEUED state. + * This will give ownership back to userspace + */ + dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_QUEUED); + vcap->active = NULL; + spin_unlock_irq(&vcap->irqlock); + + return ret; +} + +static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap) +{ + struct device *dev = vcap->dev; + + dev_dbg(dev, "[DCMIPP_PRSR] =%#10.8x\n", reg_read(vcap, DCMIPP_PRSR)); + dev_dbg(dev, "[DCMIPP_P0SR] =%#10.8x\n", reg_read(vcap, DCMIPP_P0SR)); + dev_dbg(dev, "[DCMIPP_P0DCCNTR]=%#10.8x\n", + reg_read(vcap, DCMIPP_P0DCCNTR)); + dev_dbg(dev, "[DCMIPP_CMSR1] =%#10.8x\n", reg_read(vcap, DCMIPP_CMSR1)); + dev_dbg(dev, "[DCMIPP_CMSR2] =%#10.8x\n", reg_read(vcap, DCMIPP_CMSR2)); +} + +/* + * Stop the stream engine. Any remaining buffers in the stream queue are + * dequeued and passed on to the vb2 framework marked as STATE_ERROR. + */ +static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); + int ret; + u32 status; + + dcmipp_pipeline_s_stream(vcap, 0); + + /* Stop the media pipeline */ + media_pipeline_stop(vcap->vdev.entity.pads); + + /* Disable interruptions */ + reg_clear(vcap, DCMIPP_CMIER, vcap->cmier); + + /* Stop capture */ + reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); + + /* Wait until CPTACT become 0 */ + ret = readl_relaxed_poll_timeout(vcap->regs + DCMIPP_P0SR, status, + !(status & DCMIPP_P0SR_CPTACT), + 20 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + dev_warn(vcap->dev, "Timeout when stopping\n"); + + /* Disable pipe */ + reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN); + + spin_lock_irq(&vcap->irqlock); + + /* Return all queued buffers to vb2 in ERROR state */ + dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_ERROR); + INIT_LIST_HEAD(&vcap->buffers); + + vcap->active = NULL; + vcap->state = DCMIPP_STOPPED; + + spin_unlock_irq(&vcap->irqlock); + + dcmipp_dump_status(vcap); + + pm_runtime_put(vcap->dev); + + if (vcap->count.errors) + dev_warn(vcap->dev, "Some errors found while streaming: errors=%d (overrun=%d, limit=%d, nactive=%d), underrun=%d, buffers=%d\n", + vcap->count.errors, vcap->count.overrun, + vcap->count.limit, vcap->count.nactive, + vcap->count.underrun, vcap->count.buffers); +} + +static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb); + unsigned long size; + + size = vcap->format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(vcap->dev, "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + if (!buf->prepared) { + /* Get memory addresses */ + buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); + buf->prepared = true; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + + dev_dbg(vcap->dev, "Setup [%d] phy=%pad size=%zu\n", + vb->index, &buf->addr, buf->size); + } + + return 0; +} + +static void dcmipp_bytecap_buf_queue(struct vb2_buffer *vb2_buf) +{ + struct dcmipp_bytecap_device *vcap = + vb2_get_drv_priv(vb2_buf->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2_buf); + struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb); + + dev_dbg(vcap->dev, "Queue [%d] %p phy=%pad\n", buf->vb.vb2_buf.index, + buf, &buf->addr); + + spin_lock_irq(&vcap->irqlock); + list_add_tail(&buf->list, &vcap->buffers); + + if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) { + vcap->next = buf; + dev_dbg(vcap->dev, "Restart with next [%d] %p phy=%pad\n", + buf->vb.vb2_buf.index, buf, &buf->addr); + + dcmipp_start_capture(vcap, buf); + + vcap->state = DCMIPP_RUNNING; + } + + spin_unlock_irq(&vcap->irqlock); +} + +static int dcmipp_bytecap_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); + unsigned int size; + + size = vcap->format.sizeimage; + + /* Make sure the image size is large enough */ + if (*nplanes) + return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = vcap->format.sizeimage; + + dev_dbg(vcap->dev, "Setup queue, count=%d, size=%d\n", + *nbuffers, size); + + return 0; +} + +static int dcmipp_bytecap_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb); + + INIT_LIST_HEAD(&buf->list); + + return 0; +} + +static const struct vb2_ops dcmipp_bytecap_qops = { + .start_streaming = dcmipp_bytecap_start_streaming, + .stop_streaming = dcmipp_bytecap_stop_streaming, + .buf_init = dcmipp_bytecap_buf_init, + .buf_prepare = dcmipp_bytecap_buf_prepare, + .buf_queue = dcmipp_bytecap_buf_queue, + .queue_setup = dcmipp_bytecap_queue_setup, + /* + * Since q->lock is set we can use the standard + * vb2_ops_wait_prepare/finish helper functions. + */ + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static void dcmipp_bytecap_release(struct video_device *vdev) +{ + struct dcmipp_bytecap_device *vcap = + container_of(vdev, struct dcmipp_bytecap_device, vdev); + + dcmipp_pads_cleanup(vcap->ved.pads); + mutex_destroy(&vcap->lock); + + kfree(vcap); +} + +void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_bytecap_device *vcap = + container_of(ved, struct dcmipp_bytecap_device, ved); + + media_entity_cleanup(ved->ent); + vb2_video_unregister_device(&vcap->vdev); +} + +static void dcmipp_buffer_done(struct dcmipp_bytecap_device *vcap, + struct dcmipp_buf *buf, + size_t bytesused, + int err) +{ + struct vb2_v4l2_buffer *vbuf; + + list_del_init(&buf->list); + + vbuf = &buf->vb; + + vbuf->sequence = vcap->sequence++; + vbuf->field = V4L2_FIELD_NONE; + vbuf->vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused); + vb2_buffer_done(&vbuf->vb2_buf, + err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dev_dbg(vcap->dev, "Done [%d] %p phy=%pad\n", buf->vb.vb2_buf.index, + buf, &buf->addr); + vcap->count.buffers++; +} + +/* irqlock must be held */ +static void +dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap) +{ + if (!vcap->next && list_is_singular(&vcap->buffers)) { + /* + * If there is no available buffer (none or a single one in the + * list while two are expected), stop the capture (effective + * for next frame). On-going frame capture will continue until + * FRAME END but no further capture will be done. + */ + reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); + + dev_dbg(vcap->dev, "Capture restart is deferred to next buffer queueing\n"); + vcap->next = NULL; + vcap->state = DCMIPP_WAIT_FOR_BUFFER; + return; + } + + /* If we don't have buffer yet, pick the one after active */ + if (!vcap->next) + vcap->next = list_next_entry(vcap->active, list); + + /* + * Set buffer address + * This register is shadowed and will be taken into + * account on next VSYNC (start of next frame) + */ + reg_write(vcap, DCMIPP_P0PPM0AR1, vcap->next->addr); + dev_dbg(vcap->dev, "Write [%d] %p phy=%pad\n", + vcap->next->vb.vb2_buf.index, vcap->next, &vcap->next->addr); +} + +/* irqlock must be held */ +static void dcmipp_bytecap_process_frame(struct dcmipp_bytecap_device *vcap, + size_t bytesused) +{ + int err = 0; + struct dcmipp_buf *buf = vcap->active; + + if (!buf) { + vcap->count.nactive++; + vcap->count.errors++; + return; + } + + if (bytesused > buf->size) { + dev_dbg(vcap->dev, "frame larger than expected (%zu > %zu)\n", + bytesused, buf->size); + /* Clip to buffer size and return buffer to V4L2 in error */ + bytesused = buf->size; + vcap->count.limit++; + vcap->count.errors++; + err = -EOVERFLOW; + } + + dcmipp_buffer_done(vcap, buf, bytesused, err); + vcap->active = NULL; +} + +static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg) +{ + struct dcmipp_bytecap_device *vcap = + container_of(arg, struct dcmipp_bytecap_device, ved); + size_t bytesused = 0; + u32 cmsr2; + + spin_lock_irq(&vcap->irqlock); + + cmsr2 = vcap->cmsr2 & vcap->cmier; + + /* + * If we have an overrun, a frame-end will probably not be generated, + * in that case the active buffer will be recycled as next buffer by + * the VSYNC handler + */ + if (cmsr2 & DCMIPP_CMSR2_P0OVRF) { + vcap->count.errors++; + vcap->count.overrun++; + } + + if (cmsr2 & DCMIPP_CMSR2_P0FRAMEF) { + vcap->count.frame++; + + /* Read captured buffer size */ + bytesused = reg_read(vcap, DCMIPP_P0DCCNTR); + dcmipp_bytecap_process_frame(vcap, bytesused); + } + + if (cmsr2 & DCMIPP_CMSR2_P0VSYNCF) { + vcap->count.vsync++; + if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) { + vcap->count.underrun++; + goto out; + } + + /* + * On VSYNC, the previously set next buffer is going to become + * active thanks to the shadowing mechanism of the DCMIPP. In + * most of the cases, since a FRAMEEND has already come, + * pointer next is NULL since active is reset during the + * FRAMEEND handling. However, in case of framerate adjustment, + * there are more VSYNC than FRAMEEND. Thus we recycle the + * active (but not used) buffer and put it back into next. + */ + swap(vcap->active, vcap->next); + dcmipp_bytecap_set_next_frame_or_stop(vcap); + } + +out: + spin_unlock_irq(&vcap->irqlock); + return IRQ_HANDLED; +} + +static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg) +{ + struct dcmipp_bytecap_device *vcap = + container_of(arg, struct dcmipp_bytecap_device, ved); + + /* Store interrupt status register */ + vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & vcap->cmier; + vcap->count.it++; + + /* Clear interrupt */ + reg_write(vcap, DCMIPP_CMFCR, vcap->cmsr2); + + return IRQ_WAKE_THREAD; +} + +static int dcmipp_bytecap_link_validate(struct media_link *link) +{ + struct media_entity *entity = link->sink->entity; + struct video_device *vd = media_entity_to_video_device(entity); + struct dcmipp_bytecap_device *vcap = container_of(vd, + struct dcmipp_bytecap_device, vdev); + struct v4l2_subdev *source_sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct v4l2_subdev_format source_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = link->source->index, + }; + const struct dcmipp_bytecap_pix_map *vpix; + int ret; + + ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt); + if (ret < 0) + return 0; + + if (source_fmt.format.width != vcap->format.width || + source_fmt.format.height != vcap->format.height) { + dev_err(vcap->dev, "Wrong width or height %ux%u (%ux%u expected)\n", + vcap->format.width, vcap->format.height, + source_fmt.format.width, source_fmt.format.height); + return -EINVAL; + } + + vpix = dcmipp_bytecap_pix_map_by_pixelformat(vcap->format.pixelformat); + if (source_fmt.format.code != vpix->code) { + dev_err(vcap->dev, "Wrong mbus_code 0x%x, (0x%x expected)\n", + vpix->code, source_fmt.format.code); + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations dcmipp_bytecap_entity_ops = { + .link_validate = dcmipp_bytecap_link_validate, +}; + +struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_bytecap_device *vcap; + struct video_device *vdev; + struct vb2_queue *q; + const unsigned long pad_flag = MEDIA_PAD_FL_SINK; + int ret = 0; + + /* Allocate the dcmipp_bytecap_device struct */ + vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); + if (!vcap) + return ERR_PTR(-ENOMEM); + + /* Allocate the pads */ + vcap->ved.pads = dcmipp_pads_init(1, &pad_flag); + if (IS_ERR(vcap->ved.pads)) { + ret = PTR_ERR(vcap->ved.pads); + goto err_free_vcap; + } + + /* Initialize the media entity */ + vcap->vdev.entity.name = entity_name; + vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + vcap->vdev.entity.ops = &dcmipp_bytecap_entity_ops; + ret = media_entity_pads_init(&vcap->vdev.entity, 1, vcap->ved.pads); + if (ret) + goto err_clean_pads; + + /* Initialize the lock */ + mutex_init(&vcap->lock); + + /* Initialize the vb2 queue */ + q = &vcap->queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->lock = &vcap->lock; + q->drv_priv = vcap; + q->buf_struct_size = sizeof(struct dcmipp_buf); + q->ops = &dcmipp_bytecap_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_queued_buffers = 1; + q->dev = dev; + + /* DCMIPP requires 16 bytes aligned buffers */ + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32) & ~0x0f); + if (ret) { + dev_err(dev, "Failed to set DMA mask\n"); + goto err_mutex_destroy; + } + + ret = vb2_queue_init(q); + if (ret) { + dev_err(dev, "%s: vb2 queue init failed (err=%d)\n", + entity_name, ret); + goto err_clean_m_ent; + } + + /* Initialize buffer list and its lock */ + INIT_LIST_HEAD(&vcap->buffers); + spin_lock_init(&vcap->irqlock); + + /* Set default frame format */ + vcap->format = fmt_default; + + /* Fill the dcmipp_ent_device struct */ + vcap->ved.ent = &vcap->vdev.entity; + vcap->ved.handler = dcmipp_bytecap_irq_callback; + vcap->ved.thread_fn = dcmipp_bytecap_irq_thread; + vcap->dev = dev; + vcap->regs = regs; + + /* Initialize the video_device struct */ + vdev = &vcap->vdev; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + vdev->release = dcmipp_bytecap_release; + vdev->fops = &dcmipp_bytecap_fops; + vdev->ioctl_ops = &dcmipp_bytecap_ioctl_ops; + vdev->lock = &vcap->lock; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + strscpy(vdev->name, entity_name, sizeof(vdev->name)); + video_set_drvdata(vdev, &vcap->ved); + + /* Register the video_device with the v4l2 and the media framework */ + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(dev, "%s: video register failed (err=%d)\n", + vcap->vdev.name, ret); + goto err_clean_m_ent; + } + + return &vcap->ved; + +err_clean_m_ent: + media_entity_cleanup(&vcap->vdev.entity); +err_mutex_destroy: + mutex_destroy(&vcap->lock); +err_clean_pads: + dcmipp_pads_cleanup(vcap->ved.pads); +err_free_vcap: + kfree(vcap); + + return ERR_PTR(ret); +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c new file mode 100644 index 000000000000..5a361ad6b023 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/vmalloc.h> +#include <linux/v4l2-mediabus.h> +#include <media/v4l2-rect.h> +#include <media/v4l2-subdev.h> + +#include "dcmipp-common.h" + +#define DCMIPP_P0FCTCR 0x500 +#define DCMIPP_P0FCTCR_FRATE_MASK GENMASK(1, 0) +#define DCMIPP_P0SCSTR 0x504 +#define DCMIPP_P0SCSTR_HSTART_SHIFT 0 +#define DCMIPP_P0SCSTR_VSTART_SHIFT 16 +#define DCMIPP_P0SCSZR 0x508 +#define DCMIPP_P0SCSZR_ENABLE BIT(31) +#define DCMIPP_P0SCSZR_HSIZE_SHIFT 0 +#define DCMIPP_P0SCSZR_VSIZE_SHIFT 16 +#define DCMIPP_P0PPCR 0x5c0 +#define DCMIPP_P0PPCR_BSM_1_2 0x1 +#define DCMIPP_P0PPCR_BSM_1_4 0x2 +#define DCMIPP_P0PPCR_BSM_2_4 0x3 +#define DCMIPP_P0PPCR_BSM_MASK GENMASK(8, 7) +#define DCMIPP_P0PPCR_BSM_SHIFT 0x7 +#define DCMIPP_P0PPCR_LSM BIT(10) +#define DCMIPP_P0PPCR_OELS BIT(11) + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_byteproc_pix_map { + unsigned int code; + unsigned int bpp; +}; + +#define PIXMAP_MBUS_BPP(mbus, byteperpixel) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .bpp = byteperpixel, \ + } +static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { + PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2), + PIXMAP_MBUS_BPP(YUYV8_2X8, 2), + PIXMAP_MBUS_BPP(YVYU8_2X8, 2), + PIXMAP_MBUS_BPP(UYVY8_2X8, 2), + PIXMAP_MBUS_BPP(VYUY8_2X8, 2), + PIXMAP_MBUS_BPP(Y8_1X8, 1), + PIXMAP_MBUS_BPP(SBGGR8_1X8, 1), + PIXMAP_MBUS_BPP(SGBRG8_1X8, 1), + PIXMAP_MBUS_BPP(SGRBG8_1X8, 1), + PIXMAP_MBUS_BPP(SRGGB8_1X8, 1), + PIXMAP_MBUS_BPP(JPEG_1X8, 1), +}; + +static const struct dcmipp_byteproc_pix_map * +dcmipp_byteproc_pix_map_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_byteproc_pix_map_list); i++) { + if (dcmipp_byteproc_pix_map_list[i].code == code) + return &dcmipp_byteproc_pix_map_list[i]; + } + + return NULL; +} + +struct dcmipp_byteproc_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; + bool streaming; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static const struct v4l2_rect crop_min = { + .width = DCMIPP_FRAME_MIN_WIDTH, + .height = DCMIPP_FRAME_MIN_HEIGHT, + .top = 0, + .left = 0, +}; + +static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r, + struct v4l2_rect *compose) +{ + /* Disallow rectangles smaller than the minimal one. */ + v4l2_rect_set_min_size(r, &crop_min); + v4l2_rect_map_inside(r, compose); +} + +static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r, + const struct v4l2_mbus_framefmt *fmt) +{ + r->top = 0; + r->left = 0; + + /* Compose is not possible for JPEG or Bayer formats */ + if (fmt->code == MEDIA_BUS_FMT_JPEG_1X8 || + fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 || + fmt->code == MEDIA_BUS_FMT_SGBRG8_1X8 || + fmt->code == MEDIA_BUS_FMT_SGRBG8_1X8 || + fmt->code == MEDIA_BUS_FMT_SRGGB8_1X8) { + r->width = fmt->width; + r->height = fmt->height; + return; + } + + /* Adjust height - we can only perform 1/2 decimation */ + if (r->height <= (fmt->height / 2)) + r->height = fmt->height / 2; + else + r->height = fmt->height; + + /* Adjust width /2 or /4 for 8bits formats and /2 for 16bits formats */ + if (fmt->code == MEDIA_BUS_FMT_Y8_1X8 && r->width <= (fmt->width / 4)) + r->width = fmt->width / 4; + else if (r->width <= (fmt->width / 2)) + r->width = fmt->width / 2; + else + r->width = fmt->width; +} + +static void dcmipp_byteproc_adjust_fmt(struct v4l2_mbus_framefmt *fmt) +{ + const struct dcmipp_byteproc_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_byteproc_pix_map_by_code(fmt->code); + if (!vpix) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_byteproc_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *r; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + + if (IS_SINK(i)) + r = v4l2_subdev_state_get_compose(sd_state, i); + else + r = v4l2_subdev_state_get_crop(sd_state, i); + + r->top = 0; + r->left = 0; + r->width = DCMIPP_FMT_WIDTH_DEFAULT; + r->height = DCMIPP_FMT_HEIGHT_DEFAULT; + } + + return 0; +} + +static int +dcmipp_byteproc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_byteproc_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + + if (IS_SINK(code->pad)) { + if (code->index >= ARRAY_SIZE(dcmipp_byteproc_pix_map_list)) + return -EINVAL; + vpix = &dcmipp_byteproc_pix_map_list[code->index]; + code->code = vpix->code; + } else { + /* byteproc doesn't support transformation on format */ + if (code->index > 0) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + code->code = sink_fmt->code; + } + + return 0; +} + +static int +dcmipp_byteproc_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_rect *compose; + + if (fse->index) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + + if (IS_SINK(fse->pad)) { + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + } else { + compose = v4l2_subdev_state_get_compose(sd_state, 0); + fse->max_width = compose->width; + fse->max_height = compose->height; + } + + return 0; +} + +static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop, *compose; + + if (byteproc->streaming) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + crop = v4l2_subdev_state_get_crop(sd_state, 1); + compose = v4l2_subdev_state_get_compose(sd_state, 0); + + if (IS_SRC(fmt->pad)) { + fmt->format = *v4l2_subdev_state_get_format(sd_state, 0); + fmt->format.width = crop->width; + fmt->format.height = crop->height; + } else { + dcmipp_byteproc_adjust_fmt(&fmt->format); + crop->top = 0; + crop->left = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + *compose = *crop; + /* Set the same format on SOURCE pad as well */ + *v4l2_subdev_state_get_format(sd_state, 1) = fmt->format; + } + *mf = fmt->format; + + return 0; +} + +static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *s) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *crop, *compose; + + /* + * In the HW, the decimation block is located prior to the crop hence: + * Compose is done on the sink pad + * Crop is done on the src pad + */ + if (IS_SINK(s->pad) && + (s->target == V4L2_SEL_TGT_CROP || + s->target == V4L2_SEL_TGT_CROP_BOUNDS || + s->target == V4L2_SEL_TGT_CROP_DEFAULT)) + return -EINVAL; + + if (IS_SRC(s->pad) && + (s->target == V4L2_SEL_TGT_COMPOSE || + s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS || + s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT)) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + crop = v4l2_subdev_state_get_crop(sd_state, 1); + compose = v4l2_subdev_state_get_compose(sd_state, 0); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + s->r = *crop; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r = *compose; + break; + case V4L2_SEL_TGT_COMPOSE: + s->r = *compose; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.top = 0; + s->r.left = 0; + s->r.width = sink_fmt->width; + s->r.height = sink_fmt->height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *s) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop, *compose; + + /* + * In the HW, the decimation block is located prior to the crop hence: + * Compose is done on the sink pad + * Crop is done on the src pad + */ + if ((s->target == V4L2_SEL_TGT_CROP || + s->target == V4L2_SEL_TGT_CROP_BOUNDS || + s->target == V4L2_SEL_TGT_CROP_DEFAULT) && IS_SINK(s->pad)) + return -EINVAL; + + if ((s->target == V4L2_SEL_TGT_COMPOSE || + s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS || + s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT) && IS_SRC(s->pad)) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(sd_state, 1); + compose = v4l2_subdev_state_get_compose(sd_state, 0); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + dcmipp_byteproc_adjust_crop(&s->r, compose); + + *crop = s->r; + mf = v4l2_subdev_state_get_format(sd_state, 1); + mf->width = s->r.width; + mf->height = s->r.height; + + dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n", + crop->width, crop->height, crop->left, crop->top); + break; + case V4L2_SEL_TGT_COMPOSE: + mf = v4l2_subdev_state_get_format(sd_state, 0); + dcmipp_byteproc_adjust_compose(&s->r, mf); + *compose = s->r; + *crop = s->r; + + mf = v4l2_subdev_state_get_format(sd_state, 1); + mf->width = s->r.width; + mf->height = s->r.height; + + dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n", + compose->width, compose->height, + compose->left, compose->top); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = { + .enum_mbus_code = dcmipp_byteproc_enum_mbus_code, + .enum_frame_size = dcmipp_byteproc_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_byteproc_set_fmt, + .get_selection = dcmipp_byteproc_get_selection, + .set_selection = dcmipp_byteproc_set_selection, +}; + +static int dcmipp_byteproc_configure_scale_crop + (struct dcmipp_byteproc_device *byteproc) +{ + const struct dcmipp_byteproc_pix_map *vpix; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *sink_fmt; + u32 hprediv, vprediv; + struct v4l2_rect *compose, *crop; + u32 val = 0; + + state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); + crop = v4l2_subdev_state_get_crop(state, 1); + v4l2_subdev_unlock_state(state); + + /* find output format bpp */ + vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code); + if (!vpix) + return -EINVAL; + + /* clear decimation/crop */ + reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK); + reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM); + reg_write(byteproc, DCMIPP_P0SCSTR, 0); + reg_write(byteproc, DCMIPP_P0SCSZR, 0); + + /* Ignore decimation/crop with JPEG */ + if (vpix->code == MEDIA_BUS_FMT_JPEG_1X8) + return 0; + + /* decimation */ + hprediv = sink_fmt->width / compose->width; + if (hprediv == 4) + val |= DCMIPP_P0PPCR_BSM_1_4 << DCMIPP_P0PPCR_BSM_SHIFT; + else if ((vpix->code == MEDIA_BUS_FMT_Y8_1X8) && (hprediv == 2)) + val |= DCMIPP_P0PPCR_BSM_1_2 << DCMIPP_P0PPCR_BSM_SHIFT; + else if (hprediv == 2) + val |= DCMIPP_P0PPCR_BSM_2_4 << DCMIPP_P0PPCR_BSM_SHIFT; + + vprediv = sink_fmt->height / compose->height; + if (vprediv == 2) + val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS; + + /* decimate using bytes and lines skipping */ + if (val) { + reg_set(byteproc, DCMIPP_P0PPCR, val); + + dev_dbg(byteproc->dev, "decimate to %dx%d [prediv=%dx%d]\n", + compose->width, compose->height, + hprediv, vprediv); + } + + dev_dbg(byteproc->dev, "crop to %dx%d\n", crop->width, crop->height); + + /* expressed in 32-bits words on X axis, lines on Y axis */ + reg_write(byteproc, DCMIPP_P0SCSTR, + (((crop->left * vpix->bpp) / 4) << + DCMIPP_P0SCSTR_HSTART_SHIFT) | + (crop->top << DCMIPP_P0SCSTR_VSTART_SHIFT)); + reg_write(byteproc, DCMIPP_P0SCSZR, + DCMIPP_P0SCSZR_ENABLE | + (((crop->width * vpix->bpp) / 4) << + DCMIPP_P0SCSZR_HSIZE_SHIFT) | + (crop->height << DCMIPP_P0SCSZR_VSIZE_SHIFT)); + + return 0; +} + +static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_subdev *s_subdev; + struct media_pad *pad; + int ret = 0; + + /* Get source subdev */ + pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(pad->entity); + + if (enable) { + ret = dcmipp_byteproc_configure_scale_crop(byteproc); + if (ret) + return ret; + + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", + ret); + return ret; + } + } else { + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to stop source subdev streaming (%d)\n", + ret); + return ret; + } + } + + byteproc->streaming = enable; + + return 0; +} + +static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = { + .s_stream = dcmipp_byteproc_s_stream, +}; + +static const struct v4l2_subdev_ops dcmipp_byteproc_ops = { + .pad = &dcmipp_byteproc_pad_ops, + .video = &dcmipp_byteproc_video_ops, +}; + +static void dcmipp_byteproc_release(struct v4l2_subdev *sd) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + + kfree(byteproc); +} + +static const struct v4l2_subdev_internal_ops dcmipp_byteproc_int_ops = { + .init_state = dcmipp_byteproc_init_state, + .release = dcmipp_byteproc_release, +}; + +void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_byteproc_device *byteproc = + container_of(ved, struct dcmipp_byteproc_device, ved); + + dcmipp_ent_sd_unregister(ved, &byteproc->sd); +} + +struct dcmipp_ent_device * +dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, + struct v4l2_device *v4l2_dev, void __iomem *regs) +{ + struct dcmipp_byteproc_device *byteproc; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the byteproc struct */ + byteproc = kzalloc(sizeof(*byteproc), GFP_KERNEL); + if (!byteproc) + return ERR_PTR(-ENOMEM); + + byteproc->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd, + v4l2_dev, entity_name, + MEDIA_ENT_F_PROC_VIDEO_SCALER, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_byteproc_int_ops, + &dcmipp_byteproc_ops, + NULL, NULL); + if (ret) { + kfree(byteproc); + return ERR_PTR(ret); + } + + byteproc->dev = dev; + + return &byteproc->ved; +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c new file mode 100644 index 000000000000..562933e08d62 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/init.h> +#include <linux/module.h> + +#include "dcmipp-common.h" + +/* Helper function to allocate and initialize pads */ +struct media_pad *dcmipp_pads_init(u16 num_pads, const unsigned long *pads_flags) +{ + struct media_pad *pads; + unsigned int i; + + /* Allocate memory for the pads */ + pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL); + if (!pads) + return ERR_PTR(-ENOMEM); + + /* Initialize the pads */ + for (i = 0; i < num_pads; i++) { + pads[i].index = i; + pads[i].flags = pads_flags[i]; + } + + return pads; +} + +static const struct media_entity_operations dcmipp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int dcmipp_ent_sd_register(struct dcmipp_ent_device *ved, + struct v4l2_subdev *sd, + struct v4l2_device *v4l2_dev, + const char *const name, + u32 function, + u16 num_pads, + const unsigned long *pads_flag, + const struct v4l2_subdev_internal_ops *sd_int_ops, + const struct v4l2_subdev_ops *sd_ops, + irq_handler_t handler, + irq_handler_t thread_fn) +{ + int ret; + + /* Allocate the pads. Should be released from the sd_int_op release */ + ved->pads = dcmipp_pads_init(num_pads, pads_flag); + if (IS_ERR(ved->pads)) + return PTR_ERR(ved->pads); + + /* Fill the dcmipp_ent_device struct */ + ved->ent = &sd->entity; + + /* Initialize the subdev */ + v4l2_subdev_init(sd, sd_ops); + sd->internal_ops = sd_int_ops; + sd->entity.function = function; + sd->entity.ops = &dcmipp_entity_ops; + sd->owner = THIS_MODULE; + strscpy(sd->name, name, sizeof(sd->name)); + v4l2_set_subdevdata(sd, ved); + + /* Expose this subdev to user space */ + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + if (sd->ctrl_handler) + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + + /* Initialize the media entity */ + ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); + if (ret) + goto err_clean_pads; + + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) + goto err_clean_m_ent; + + /* Register the subdev with the v4l2 and the media framework */ + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) { + dev_err(v4l2_dev->dev, + "%s: subdev register failed (err=%d)\n", + name, ret); + goto err_clean_m_ent; + } + + ved->handler = handler; + ved->thread_fn = thread_fn; + + return 0; + +err_clean_m_ent: + media_entity_cleanup(&sd->entity); +err_clean_pads: + dcmipp_pads_cleanup(ved->pads); + return ret; +} + +void +dcmipp_ent_sd_unregister(struct dcmipp_ent_device *ved, struct v4l2_subdev *sd) +{ + media_entity_cleanup(ved->ent); + v4l2_device_unregister_subdev(sd); +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h new file mode 100644 index 000000000000..7a7cf43baf24 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#ifndef _DCMIPP_COMMON_H_ +#define _DCMIPP_COMMON_H_ + +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <media/media-device.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define DCMIPP_PDEV_NAME "dcmipp" + +#define DCMIPP_FRAME_MAX_WIDTH 4096 +#define DCMIPP_FRAME_MAX_HEIGHT 2160 +#define DCMIPP_FRAME_MIN_WIDTH 16 +#define DCMIPP_FRAME_MIN_HEIGHT 16 + +#define DCMIPP_FMT_WIDTH_DEFAULT 640 +#define DCMIPP_FMT_HEIGHT_DEFAULT 480 + +#define DCMIPP_COLORSPACE_DEFAULT V4L2_COLORSPACE_REC709 +#define DCMIPP_YCBCR_ENC_DEFAULT V4L2_YCBCR_ENC_DEFAULT +#define DCMIPP_QUANTIZATION_DEFAULT V4L2_QUANTIZATION_DEFAULT +#define DCMIPP_XFER_FUNC_DEFAULT V4L2_XFER_FUNC_DEFAULT + +/** + * dcmipp_colorimetry_clamp() - Adjust colorimetry parameters + * + * @fmt: the pointer to struct v4l2_pix_format or + * struct v4l2_mbus_framefmt + * + * Entities must check if colorimetry given by the userspace is valid, if not + * then set them as DEFAULT + */ +#define dcmipp_colorimetry_clamp(fmt) \ +do { \ + if ((fmt)->colorspace == V4L2_COLORSPACE_DEFAULT || \ + (fmt)->colorspace > V4L2_COLORSPACE_DCI_P3) { \ + (fmt)->colorspace = DCMIPP_COLORSPACE_DEFAULT; \ + (fmt)->ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT; \ + (fmt)->quantization = DCMIPP_QUANTIZATION_DEFAULT; \ + (fmt)->xfer_func = DCMIPP_XFER_FUNC_DEFAULT; \ + } \ + if ((fmt)->ycbcr_enc > V4L2_YCBCR_ENC_SMPTE240M) \ + (fmt)->ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT; \ + if ((fmt)->quantization > V4L2_QUANTIZATION_LIM_RANGE) \ + (fmt)->quantization = DCMIPP_QUANTIZATION_DEFAULT; \ + if ((fmt)->xfer_func > V4L2_XFER_FUNC_SMPTE2084) \ + (fmt)->xfer_func = DCMIPP_XFER_FUNC_DEFAULT; \ +} while (0) + +/** + * struct dcmipp_ent_device - core struct that represents a node in the topology + * + * @ent: the pointer to struct media_entity for the node + * @pads: the list of pads of the node + * @bus: struct v4l2_mbus_config_parallel describing input bus + * @bus_type: type of input bus (parallel or BT656) + * @handler: irq handler dedicated to the subdev + * @handler_ret: value returned by the irq handler + * @thread_fn: threaded irq handler + * + * The DCMIPP provides a single IRQ line and a IRQ status registers for all + * subdevs, hence once the main irq handler (registered at probe time) is + * called, it will chain calls to the irq handler of each the subdevs of the + * pipelines, using the handler/handler_ret/thread_fn variables. + * + * Each node of the topology must create a dcmipp_ent_device struct. + * Depending on the node it will be of an instance of v4l2_subdev or + * video_device struct where both contains a struct media_entity. + * Those structures should embedded the dcmipp_ent_device struct through + * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the + * dcmipp_ent_device struct to be retrieved from the corresponding struct + * media_entity + */ +struct dcmipp_ent_device { + struct media_entity *ent; + struct media_pad *pads; + + /* Parallel input device */ + struct v4l2_mbus_config_parallel bus; + enum v4l2_mbus_type bus_type; + irq_handler_t handler; + irqreturn_t handler_ret; + irq_handler_t thread_fn; +}; + +/** + * dcmipp_pads_init - initialize pads + * + * @num_pads: number of pads to initialize + * @pads_flags: flags to use in each pad + * + * Helper functions to allocate/initialize pads + */ +struct media_pad *dcmipp_pads_init(u16 num_pads, + const unsigned long *pads_flags); + +/** + * dcmipp_pads_cleanup - free pads + * + * @pads: pointer to the pads + * + * Helper function to free the pads initialized with dcmipp_pads_init + */ +static inline void dcmipp_pads_cleanup(struct media_pad *pads) +{ + kfree(pads); +} + +/** + * dcmipp_ent_sd_register - initialize and register a subdev node + * + * @ved: the dcmipp_ent_device struct to be initialize + * @sd: the v4l2_subdev struct to be initialize and registered + * @v4l2_dev: the v4l2 device to register the v4l2_subdev + * @name: name of the sub-device. Please notice that the name must be + * unique. + * @function: media entity function defined by MEDIA_ENT_F_* macros + * @num_pads: number of pads to initialize + * @pads_flag: flags to use in each pad + * @sd_int_ops: pointer to &struct v4l2_subdev_internal_ops + * @sd_ops: pointer to &struct v4l2_subdev_ops. + * @handler: func pointer of the irq handler + * @thread_fn: func pointer of the threaded irq handler + * + * Helper function initialize and register the struct dcmipp_ent_device and + * struct v4l2_subdev which represents a subdev node in the topology + */ +int dcmipp_ent_sd_register(struct dcmipp_ent_device *ved, + struct v4l2_subdev *sd, + struct v4l2_device *v4l2_dev, + const char *const name, + u32 function, + u16 num_pads, + const unsigned long *pads_flag, + const struct v4l2_subdev_internal_ops *sd_int_ops, + const struct v4l2_subdev_ops *sd_ops, + irq_handler_t handler, + irq_handler_t thread_fn); + +/** + * dcmipp_ent_sd_unregister - cleanup and unregister a subdev node + * + * @ved: the dcmipp_ent_device struct to be cleaned up + * @sd: the v4l2_subdev struct to be unregistered + * + * Helper function cleanup and unregister the struct dcmipp_ent_device and + * struct v4l2_subdev which represents a subdev node in the topology + */ +void dcmipp_ent_sd_unregister(struct dcmipp_ent_device *ved, + struct v4l2_subdev *sd); + +#define reg_write(device, reg, val) \ + (__reg_write((device)->dev, (device)->regs, (reg), (val))) +#define reg_read(device, reg) \ + (__reg_read((device)->dev, (device)->regs, (reg))) +#define reg_set(device, reg, mask) \ + (__reg_set((device)->dev, (device)->regs, (reg), (mask))) +#define reg_clear(device, reg, mask) \ + (__reg_clear((device)->dev, (device)->regs, (reg), (mask))) + +static inline u32 __reg_read(struct device *dev, void __iomem *base, u32 reg) +{ + u32 val = readl_relaxed(base + reg); + + dev_dbg(dev, "RD 0x%x %#10.8x\n", reg, val); + return val; +} + +static inline void __reg_write(struct device *dev, void __iomem *base, u32 reg, + u32 val) +{ + dev_dbg(dev, "WR 0x%x %#10.8x\n", reg, val); + writel_relaxed(val, base + reg); +} + +static inline void __reg_set(struct device *dev, void __iomem *base, u32 reg, + u32 mask) +{ + dev_dbg(dev, "SET 0x%x %#10.8x\n", reg, mask); + __reg_write(dev, base, reg, readl_relaxed(base + reg) | mask); +} + +static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg, + u32 mask) +{ + dev_dbg(dev, "CLR 0x%x %#10.8x\n", reg, mask); + __reg_write(dev, base, reg, readl_relaxed(base + reg) & ~mask); +} + +/* DCMIPP subdev init / release entry points */ +struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs); +void dcmipp_par_ent_release(struct dcmipp_ent_device *ved); +struct dcmipp_ent_device * +dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, + struct v4l2_device *v4l2_dev, void __iomem *regs); +void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved); +struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs); +void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved); + +#endif diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c new file mode 100644 index 000000000000..32c6619be9a2 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <media/media-device.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#include "dcmipp-common.h" + +#define DCMIPP_MDEV_MODEL_NAME "DCMIPP MDEV" + +#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ + .src_ent = src, \ + .src_pad = srcpad, \ + .sink_ent = sink, \ + .sink_pad = sinkpad, \ + .flags = link_flags, \ +} + +struct dcmipp_device { + /* The platform device */ + struct platform_device pdev; + struct device *dev; + + /* Hardware resources */ + void __iomem *regs; + struct clk *kclk; + + /* The pipeline configuration */ + const struct dcmipp_pipeline_config *pipe_cfg; + + /* The Associated media_device parent */ + struct media_device mdev; + + /* Internal v4l2 parent device*/ + struct v4l2_device v4l2_dev; + + /* Entities */ + struct dcmipp_ent_device **entity; + + struct v4l2_async_notifier notifier; +}; + +static inline struct dcmipp_device * +notifier_to_dcmipp(struct v4l2_async_notifier *n) +{ + return container_of(n, struct dcmipp_device, notifier); +} + +/* Structure which describes individual configuration for each entity */ +struct dcmipp_ent_config { + const char *name; + struct dcmipp_ent_device *(*init) + (struct device *dev, const char *entity_name, + struct v4l2_device *v4l2_dev, void __iomem *regs); + void (*release)(struct dcmipp_ent_device *ved); +}; + +/* Structure which describes links between entities */ +struct dcmipp_ent_link { + unsigned int src_ent; + u16 src_pad; + unsigned int sink_ent; + u16 sink_pad; + u32 flags; +}; + +/* Structure which describes the whole topology */ +struct dcmipp_pipeline_config { + const struct dcmipp_ent_config *ents; + size_t num_ents; + const struct dcmipp_ent_link *links; + size_t num_links; +}; + +/* -------------------------------------------------------------------------- + * Topology Configuration + */ + +static const struct dcmipp_ent_config stm32mp13_ent_config[] = { + { + .name = "dcmipp_parallel", + .init = dcmipp_par_ent_init, + .release = dcmipp_par_ent_release, + }, + { + .name = "dcmipp_dump_postproc", + .init = dcmipp_byteproc_ent_init, + .release = dcmipp_byteproc_ent_release, + }, + { + .name = "dcmipp_dump_capture", + .init = dcmipp_bytecap_ent_init, + .release = dcmipp_bytecap_ent_release, + }, +}; + +#define ID_PARALLEL 0 +#define ID_DUMP_BYTEPROC 1 +#define ID_DUMP_CAPTURE 2 + +static const struct dcmipp_ent_link stm32mp13_ent_links[] = { + DCMIPP_ENT_LINK(ID_PARALLEL, 1, ID_DUMP_BYTEPROC, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), +}; + +static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = { + .ents = stm32mp13_ent_config, + .num_ents = ARRAY_SIZE(stm32mp13_ent_config), + .links = stm32mp13_ent_links, + .num_links = ARRAY_SIZE(stm32mp13_ent_links) +}; + +#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\ + (f) == MEDIA_LNK_FL_ENABLED ? "ENABLED" :\ + (f) == MEDIA_LNK_FL_IMMUTABLE ? "IMMUTABLE" :\ + (f) == (MEDIA_LNK_FL_ENABLED |\ + MEDIA_LNK_FL_IMMUTABLE) ?\ + "ENABLED, IMMUTABLE" :\ + "UNKNOWN") + +static int dcmipp_create_links(struct dcmipp_device *dcmipp) +{ + unsigned int i; + int ret; + + /* Initialize the links between entities */ + for (i = 0; i < dcmipp->pipe_cfg->num_links; i++) { + const struct dcmipp_ent_link *link = + &dcmipp->pipe_cfg->links[i]; + struct dcmipp_ent_device *ved_src = + dcmipp->entity[link->src_ent]; + struct dcmipp_ent_device *ved_sink = + dcmipp->entity[link->sink_ent]; + + dev_dbg(dcmipp->dev, "Create link \"%s\":%d -> %d:\"%s\" [%s]\n", + dcmipp->pipe_cfg->ents[link->src_ent].name, + link->src_pad, link->sink_pad, + dcmipp->pipe_cfg->ents[link->sink_ent].name, + LINK_FLAG_TO_STR(link->flags)); + + ret = media_create_pad_link(ved_src->ent, link->src_pad, + ved_sink->ent, link->sink_pad, + link->flags); + if (ret) + return ret; + } + + return 0; +} + +static int dcmipp_graph_init(struct dcmipp_device *dcmipp); + +static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) +{ + int ret, i; + + /* Call all subdev inits */ + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { + const char *name = dcmipp->pipe_cfg->ents[i].name; + + dev_dbg(dcmipp->dev, "add subdev %s\n", name); + dcmipp->entity[i] = + dcmipp->pipe_cfg->ents[i].init(dcmipp->dev, name, + &dcmipp->v4l2_dev, + dcmipp->regs); + if (IS_ERR(dcmipp->entity[i])) { + dev_err(dcmipp->dev, "failed to init subdev %s\n", + name); + ret = PTR_ERR(dcmipp->entity[i]); + goto err_init_entity; + } + } + + /* Initialize links */ + ret = dcmipp_create_links(dcmipp); + if (ret) + goto err_init_entity; + + ret = dcmipp_graph_init(dcmipp); + if (ret < 0) + goto err_init_entity; + + return 0; + +err_init_entity: + while (i > 0) + dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); + return ret; +} + +static const struct of_device_id dcmipp_of_match[] = { + { .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, dcmipp_of_match); + +static irqreturn_t dcmipp_irq_thread(int irq, void *arg) +{ + struct dcmipp_device *dcmipp = arg; + struct dcmipp_ent_device *ved; + unsigned int i; + + /* Call irq thread of each entities of pipeline */ + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { + ved = dcmipp->entity[i]; + if (ved->thread_fn && ved->handler_ret == IRQ_WAKE_THREAD) + ved->thread_fn(irq, ved); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dcmipp_irq_callback(int irq, void *arg) +{ + struct dcmipp_device *dcmipp = arg; + struct dcmipp_ent_device *ved; + irqreturn_t ret = IRQ_HANDLED; + unsigned int i; + + /* Call irq handler of each entities of pipeline */ + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { + ved = dcmipp->entity[i]; + if (ved->handler) + ved->handler_ret = ved->handler(irq, ved); + else if (ved->thread_fn) + ved->handler_ret = IRQ_WAKE_THREAD; + else + ved->handler_ret = IRQ_HANDLED; + if (ved->handler_ret != IRQ_HANDLED) + ret = ved->handler_ret; + } + + return ret; +} + +static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); + unsigned int ret; + int src_pad; + struct dcmipp_ent_device *sink; + struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct fwnode_handle *ep; + + dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name); + + /* + * Link this sub-device to DCMIPP, it could be + * a parallel camera sensor or a CSI-2 to parallel bridge + */ + src_pad = media_entity_get_fwnode_pad(&subdev->entity, + subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + + /* Get bus characteristics from devicetree */ + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dcmipp->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dcmipp->dev, "Could not find the endpoint\n"); + return -ENODEV; + } + + /* Check for parallel bus-type first, then bt656 */ + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) { + vep.bus_type = V4L2_MBUS_BT656; + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) { + dev_err(dcmipp->dev, "Could not parse the endpoint\n"); + fwnode_handle_put(ep); + return ret; + } + } + + fwnode_handle_put(ep); + + if (vep.bus.parallel.bus_width == 0) { + dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n"); + return -ENODEV; + } + + /* Only 8 bits bus width supported with BT656 bus */ + if (vep.bus_type == V4L2_MBUS_BT656 && + vep.bus.parallel.bus_width != 8) { + dev_err(dcmipp->dev, "BT656 bus conflicts with %u bits bus width (8 bits required)\n", + vep.bus.parallel.bus_width); + return -ENODEV; + } + + /* Parallel input device detected, connect it to parallel subdev */ + sink = dcmipp->entity[ID_PARALLEL]; + sink->bus.flags = vep.bus.parallel.flags; + sink->bus.bus_width = vep.bus.parallel.bus_width; + sink->bus.data_shift = vep.bus.parallel.data_shift; + sink->bus_type = vep.bus_type; + ret = media_create_pad_link(&subdev->entity, src_pad, sink->ent, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(dcmipp->dev, "Failed to create media pad link with subdev \"%s\"\n", + subdev->name); + return ret; + } + + dev_dbg(dcmipp->dev, "DCMIPP is now linked to \"%s\"\n", subdev->name); + + return 0; +} + +static void dcmipp_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asd) +{ + struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); + + dev_dbg(dcmipp->dev, "Removing %s\n", sd->name); +} + +static int dcmipp_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); + int ret; + + /* Register the media device */ + ret = media_device_register(&dcmipp->mdev); + if (ret) { + dev_err(dcmipp->mdev.dev, + "media device register failed (err=%d)\n", ret); + return ret; + } + + /* Expose all subdev's nodes*/ + ret = v4l2_device_register_subdev_nodes(&dcmipp->v4l2_dev); + if (ret) { + dev_err(dcmipp->mdev.dev, + "dcmipp subdev nodes registration failed (err=%d)\n", + ret); + media_device_unregister(&dcmipp->mdev); + return ret; + } + + dev_dbg(dcmipp->dev, "Notify complete !\n"); + + return 0; +} + +static const struct v4l2_async_notifier_operations dcmipp_graph_notify_ops = { + .bound = dcmipp_graph_notify_bound, + .unbind = dcmipp_graph_notify_unbind, + .complete = dcmipp_graph_notify_complete, +}; + +static int dcmipp_graph_init(struct dcmipp_device *dcmipp) +{ + struct v4l2_async_connection *asd; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dcmipp->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dcmipp->dev, "Failed to get next endpoint\n"); + return -EINVAL; + } + + v4l2_async_nf_init(&dcmipp->notifier, &dcmipp->v4l2_dev); + + asd = v4l2_async_nf_add_fwnode_remote(&dcmipp->notifier, ep, + struct v4l2_async_connection); + + fwnode_handle_put(ep); + + if (IS_ERR(asd)) { + dev_err(dcmipp->dev, "Failed to add fwnode remote subdev\n"); + return PTR_ERR(asd); + } + + dcmipp->notifier.ops = &dcmipp_graph_notify_ops; + + ret = v4l2_async_nf_register(&dcmipp->notifier); + if (ret < 0) { + dev_err(dcmipp->dev, "Failed to register notifier\n"); + v4l2_async_nf_cleanup(&dcmipp->notifier); + return ret; + } + + return 0; +} + +static int dcmipp_probe(struct platform_device *pdev) +{ + struct dcmipp_device *dcmipp; + struct clk *kclk; + const struct dcmipp_pipeline_config *pipe_cfg; + struct reset_control *rstc; + int irq; + int ret; + + dcmipp = devm_kzalloc(&pdev->dev, sizeof(*dcmipp), GFP_KERNEL); + if (!dcmipp) + return -ENOMEM; + + dcmipp->dev = &pdev->dev; + + pipe_cfg = device_get_match_data(dcmipp->dev); + if (!pipe_cfg) { + dev_err(&pdev->dev, "Can't get device data\n"); + return -ENODEV; + } + dcmipp->pipe_cfg = pipe_cfg; + + platform_set_drvdata(pdev, dcmipp); + + /* Get hardware resources from devicetree */ + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "Could not get reset control\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + if (irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "Could not get irq\n"); + return irq ? irq : -ENXIO; + } + + dcmipp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(dcmipp->regs)) { + dev_err(&pdev->dev, "Could not map registers\n"); + return PTR_ERR(dcmipp->regs); + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, dcmipp_irq_callback, + dcmipp_irq_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), dcmipp); + if (ret) { + dev_err(&pdev->dev, "Unable to request irq %d\n", irq); + return ret; + } + + /* Reset device */ + ret = reset_control_assert(rstc); + if (ret) { + dev_err(&pdev->dev, "Failed to assert the reset line\n"); + return ret; + } + + usleep_range(3000, 5000); + + ret = reset_control_deassert(rstc); + if (ret) { + dev_err(&pdev->dev, "Failed to deassert the reset line\n"); + return ret; + } + + kclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(kclk), + "Unable to get kclk\n"); + dcmipp->kclk = kclk; + + dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents, + sizeof(*dcmipp->entity), GFP_KERNEL); + if (!dcmipp->entity) + return -ENOMEM; + + /* Register the v4l2 struct */ + ret = v4l2_device_register(&pdev->dev, &dcmipp->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, + "v4l2 device register failed (err=%d)\n", ret); + return ret; + } + + /* Link the media device within the v4l2_device */ + dcmipp->v4l2_dev.mdev = &dcmipp->mdev; + + /* Initialize media device */ + strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME, + sizeof(dcmipp->mdev.model)); + dcmipp->mdev.dev = &pdev->dev; + media_device_init(&dcmipp->mdev); + + /* Initialize subdevs */ + ret = dcmipp_create_subdevs(dcmipp); + if (ret) { + media_device_cleanup(&dcmipp->mdev); + v4l2_device_unregister(&dcmipp->v4l2_dev); + return ret; + } + + pm_runtime_enable(dcmipp->dev); + + dev_info(&pdev->dev, "Probe done"); + + return 0; +} + +static int dcmipp_remove(struct platform_device *pdev) +{ + struct dcmipp_device *dcmipp = platform_get_drvdata(pdev); + unsigned int i; + + pm_runtime_disable(&pdev->dev); + + v4l2_async_nf_unregister(&dcmipp->notifier); + v4l2_async_nf_cleanup(&dcmipp->notifier); + + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) + dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); + + media_device_unregister(&dcmipp->mdev); + media_device_cleanup(&dcmipp->mdev); + + v4l2_device_unregister(&dcmipp->v4l2_dev); + + return 0; +} + +static int dcmipp_runtime_suspend(struct device *dev) +{ + struct dcmipp_device *dcmipp = dev_get_drvdata(dev); + + clk_disable_unprepare(dcmipp->kclk); + + return 0; +} + +static int dcmipp_runtime_resume(struct device *dev) +{ + struct dcmipp_device *dcmipp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dcmipp->kclk); + if (ret) + dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__); + + return ret; +} + +static int dcmipp_suspend(struct device *dev) +{ + /* disable clock */ + pm_runtime_force_suspend(dev); + + /* change pinctrl state */ + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int dcmipp_resume(struct device *dev) +{ + /* restore pinctl default state */ + pinctrl_pm_select_default_state(dev); + + /* clock enable */ + pm_runtime_force_resume(dev); + + return 0; +} + +static const struct dev_pm_ops dcmipp_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dcmipp_suspend, dcmipp_resume) + RUNTIME_PM_OPS(dcmipp_runtime_suspend, dcmipp_runtime_resume, NULL) +}; + +static struct platform_driver dcmipp_pdrv = { + .probe = dcmipp_probe, + .remove = dcmipp_remove, + .driver = { + .name = DCMIPP_PDEV_NAME, + .of_match_table = dcmipp_of_match, + .pm = pm_ptr(&dcmipp_pm_ops), + }, +}; + +module_platform_driver(dcmipp_pdrv); + +MODULE_AUTHOR("Hugues Fruchet <hugues.fruchet@foss.st.com>"); +MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface with Pixel Processor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c new file mode 100644 index 000000000000..62c5c3331cfe --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/v4l2-mediabus.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#include "dcmipp-common.h" + +#define DCMIPP_PRCR 0x104 +#define DCMIPP_PRCR_FORMAT_SHIFT 16 +#define DCMIPP_PRCR_FORMAT_YUV422 0x1e +#define DCMIPP_PRCR_FORMAT_RGB565 0x22 +#define DCMIPP_PRCR_FORMAT_RAW8 0x2a +#define DCMIPP_PRCR_FORMAT_G8 0x4a +#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a +#define DCMIPP_PRCR_ESS BIT(4) +#define DCMIPP_PRCR_PCKPOL BIT(5) +#define DCMIPP_PRCR_HSPOL BIT(6) +#define DCMIPP_PRCR_VSPOL BIT(7) +#define DCMIPP_PRCR_ENABLE BIT(14) +#define DCMIPP_PRCR_SWAPCYCLES BIT(25) + +#define DCMIPP_PRESCR 0x108 +#define DCMIPP_PRESUR 0x10c + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_par_pix_map { + unsigned int code_sink; + unsigned int code_src; + u8 prcr_format; + u8 prcr_swapcycles; +}; + +#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \ + { \ + .code_sink = MEDIA_BUS_FMT_##sink, \ + .code_src = MEDIA_BUS_FMT_##src, \ + .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ + .prcr_swapcycles = swap, \ + } +static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = { + /* RGB565 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0), + /* YUV422 */ + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1), + /* GREY */ + PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0), + /* Raw Bayer */ + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0), + /* JPEG */ + PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0), +}; + +/* + * Search through the pix_map table, skipping two consecutive entry with the + * same code + */ +static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index + (unsigned int index, + unsigned int pad) +{ + unsigned int i = 0; + u32 prev_code = 0, cur_code; + + while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) { + if (IS_SRC(pad)) + cur_code = dcmipp_par_pix_map_list[i].code_src; + else + cur_code = dcmipp_par_pix_map_list[i].code_sink; + + if (cur_code == prev_code) { + i++; + continue; + } + prev_code = cur_code; + + if (index == 0) + break; + i++; + index--; + } + + if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list)) + return NULL; + + return &dcmipp_par_pix_map_list[i]; +} + +static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code + (u32 code_sink, u32 code_src) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) { + if ((dcmipp_par_pix_map_list[i].code_sink == code_sink && + dcmipp_par_pix_map_list[i].code_src == code_src) || + (dcmipp_par_pix_map_list[i].code_sink == code_src && + dcmipp_par_pix_map_list[i].code_src == code_sink) || + (dcmipp_par_pix_map_list[i].code_sink == code_sink && + code_src == 0) || + (code_sink == 0 && + dcmipp_par_pix_map_list[i].code_src == code_src)) + return &dcmipp_par_pix_map_list[i]; + } + return NULL; +} + +struct dcmipp_par_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; + bool streaming; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_par_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + } + + return 0; +} + +static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_par_pix_map *vpix = + dcmipp_par_pix_map_by_index(code->index, code->pad); + + if (!vpix) + return -EINVAL; + + code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; + + return 0; +} + +static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct dcmipp_par_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, + IS_SRC(fse->pad) ? fse->code : 0); + if (!vpix) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + + return 0; +} + +static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par, + struct v4l2_mbus_framefmt *fmt, __u32 pad) +{ + const struct dcmipp_par_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, + IS_SRC(pad) ? fmt->code : 0); + if (!vpix) + fmt->code = fmt_default.code; + + /* Exclude JPEG if BT656 bus is selected */ + if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && + par->ved.bus_type == V4L2_MBUS_BT656) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_par_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_par_device *par = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (par->streaming) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + /* Set the new format */ + dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad); + + dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", + par->sd.name, + /* old */ + mf->width, mf->height, mf->code, + mf->colorspace, mf->quantization, + mf->xfer_func, mf->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *mf = fmt->format; + + /* When setting the sink format, report that format on the src pad */ + if (IS_SINK(fmt->pad)) { + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = fmt->format; + dcmipp_par_adjust_fmt(par, mf, 1); + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = { + .enum_mbus_code = dcmipp_par_enum_mbus_code, + .enum_frame_size = dcmipp_par_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_par_set_fmt, +}; + +static int dcmipp_par_configure(struct dcmipp_par_device *par) +{ + u32 val = 0; + const struct dcmipp_par_pix_map *vpix; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Set vertical synchronization polarity */ + if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_HSPOL; + + /* Set pixel clock polarity */ + if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + val |= DCMIPP_PRCR_PCKPOL; + + /* + * BT656 embedded synchronisation bus mode. + * + * Default SAV/EAV mode is supported here with default codes + * SAV=0xff000080 & EAV=0xff00009d. + * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. + */ + if (par->ved.bus_type == V4L2_MBUS_BT656) { + val |= DCMIPP_PRCR_ESS; + + /* Unmask all codes */ + reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + + /* Set format */ + state = v4l2_subdev_lock_and_get_active_state(&par->sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + v4l2_subdev_unlock_state(state); + + vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(par->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; + + /* swap cycles */ + if (vpix->prcr_swapcycles) + val |= DCMIPP_PRCR_SWAPCYCLES; + + reg_write(par, DCMIPP_PRCR, val); + + return 0; +} + +static int dcmipp_par_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct dcmipp_par_device *par = + container_of(sd, struct dcmipp_par_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *pad; + int ret = 0; + + /* Get source subdev */ + pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(pad->entity); + + if (enable) { + ret = dcmipp_par_configure(par); + if (ret) + return ret; + + /* Enable parallel interface */ + reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(par->dev, + "failed to start source subdev streaming (%d)\n", + ret); + return ret; + } + } else { + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(par->dev, + "failed to stop source subdev streaming (%d)\n", + ret); + return ret; + } + + /* Disable parallel interface */ + reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + } + + par->streaming = enable; + + return ret; +} + +static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = { + .s_stream = dcmipp_par_s_stream, +}; + +static const struct v4l2_subdev_ops dcmipp_par_ops = { + .pad = &dcmipp_par_pad_ops, + .video = &dcmipp_par_video_ops, +}; + +static void dcmipp_par_release(struct v4l2_subdev *sd) +{ + struct dcmipp_par_device *par = + container_of(sd, struct dcmipp_par_device, sd); + + kfree(par); +} + +static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = { + .init_state = dcmipp_par_init_state, + .release = dcmipp_par_release, +}; + +void dcmipp_par_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_par_device *par = + container_of(ved, struct dcmipp_par_device, ved); + + dcmipp_ent_sd_unregister(ved, &par->sd); +} + +struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_par_device *par; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the par struct */ + par = kzalloc(sizeof(*par), GFP_KERNEL); + if (!par) + return ERR_PTR(-ENOMEM); + + par->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev, + entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_par_int_ops, &dcmipp_par_ops, + NULL, NULL); + if (ret) { + kfree(par); + return ERR_PTR(ret); + } + + par->dev = dev; + + return &par->ved; +} diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index ad13d447d483..097a3a08ef7d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -211,6 +211,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) /* Initialize subdev */ v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops); + subdev->internal_ops = &sun4i_csi_subdev_internal_ops; subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index 8eeed87bfb13..4e0c2df45d4d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -89,6 +89,7 @@ enum csi_subdev_pads { }; extern const struct v4l2_subdev_ops sun4i_csi_subdev_ops; +extern const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops; struct sun4i_csi_format { u32 mbus; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 95b5633b7914..d1371e130113 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -411,7 +411,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) for (i = 0; i < CSI_MAX_BUFFER; i++) csi->current_buf[i] = NULL; - q->min_buffers_needed = 3; + q->min_queued_buffers = 3; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->io_modes = VB2_MMAP | VB2_DMABUF; q->lock = &csi->lock; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 48702134ccc5..744197b0fccb 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -266,12 +266,12 @@ static const struct v4l2_mbus_framefmt sun4i_csi_pad_fmt_default = { .xfer_func = V4L2_XFER_FUNC_DEFAULT, }; -static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int sun4i_csi_subdev_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(subdev, sd_state, CSI_SUBDEV_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, CSI_SUBDEV_SINK); *fmt = sun4i_csi_pad_fmt_default; return 0; @@ -285,8 +285,7 @@ static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -303,8 +302,7 @@ static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -336,7 +334,6 @@ sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops sun4i_csi_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .init_cfg = sun4i_csi_subdev_init_cfg, .get_fmt = sun4i_csi_subdev_get_fmt, .set_fmt = sun4i_csi_subdev_set_fmt, .enum_mbus_code = sun4i_csi_subdev_enum_mbus_code, @@ -346,6 +343,10 @@ const struct v4l2_subdev_ops sun4i_csi_subdev_ops = { .pad = &sun4i_csi_subdev_pad_ops, }; +const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops = { + .init_state = sun4i_csi_subdev_init_state, +}; + int sun4i_csi_v4l2_register(struct sun4i_csi *csi) { struct video_device *vdev = &csi->vdev; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index e573413123b9..d006d9dd0170 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -501,13 +501,13 @@ sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi_dev->bridge.lock; mutex_lock(lock); @@ -547,8 +547,8 @@ static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi_dev->bridge.mbus_format; @@ -570,7 +570,7 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, sun6i_csi_bridge_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi_dev->bridge.mbus_format = *mbus_format; @@ -581,7 +581,6 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = { - .init_cfg = sun6i_csi_bridge_init_cfg, .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code, .get_fmt = sun6i_csi_bridge_get_fmt, .set_fmt = sun6i_csi_bridge_set_fmt, @@ -592,6 +591,10 @@ static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = { .pad = &sun6i_csi_bridge_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_csi_bridge_internal_ops = { + .init_state = sun6i_csi_bridge_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_csi_bridge_entity_ops = { @@ -782,6 +785,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops); + subdev->internal_ops = &sun6i_csi_bridge_internal_ops; strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c index cf6aadbc130b..14c0dc827c52 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c @@ -1010,7 +1010,7 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev) queue->buf_struct_size = sizeof(struct sun6i_csi_buffer); queue->ops = &sun6i_csi_capture_queue_ops; queue->mem_ops = &vb2_dma_contig_memops; - queue->min_buffers_needed = 2; + queue->min_queued_buffers = 2; queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; queue->lock = &capture->lock; queue->dev = csi_dev->dev; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c index 08d86c17b284..f9d4dc45b490 100644 --- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -305,13 +305,13 @@ sun6i_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -351,8 +351,8 @@ static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -374,7 +374,7 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun6i_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; @@ -385,7 +385,6 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_pad_ops = { - .init_cfg = sun6i_mipi_csi2_init_cfg, .enum_mbus_code = sun6i_mipi_csi2_enum_mbus_code, .get_fmt = sun6i_mipi_csi2_get_fmt, .set_fmt = sun6i_mipi_csi2_set_fmt, @@ -396,6 +395,10 @@ static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = { .pad = &sun6i_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_mipi_csi2_internal_ops = { + .init_state = sun6i_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = { @@ -504,6 +507,7 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun6i_mipi_csi2_internal_ops; strscpy(subdev->name, SUN6I_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c index 14a1844812c0..4a5698eb12b7 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -338,14 +338,14 @@ sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun8i_a83t_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun8i_a83t_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -387,8 +387,8 @@ static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -411,7 +411,7 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; @@ -422,7 +422,6 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = { - .init_cfg = sun8i_a83t_mipi_csi2_init_cfg, .enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code, .get_fmt = sun8i_a83t_mipi_csi2_get_fmt, .set_fmt = sun8i_a83t_mipi_csi2_set_fmt, @@ -433,6 +432,10 @@ static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = { .pad = &sun8i_a83t_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun8i_a83t_mipi_csi2_internal_ops = { + .init_state = sun8i_a83t_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { @@ -542,6 +545,7 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun8i_a83t_mipi_csi2_internal_ops; strscpy(subdev->name, SUN8I_A83T_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index 90ab1d77b6a5..954fabec27f6 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -673,7 +673,7 @@ static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -688,7 +688,7 @@ static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->min_buffers_needed = 2; + dst_vq->min_queued_buffers = 2; dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c index 0b025ec91826..a12323ca89fa 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -536,7 +536,7 @@ static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->ops = &rotate_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -551,7 +551,7 @@ static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->min_buffers_needed = 2; + dst_vq->min_queued_buffers = 2; dst_vq->ops = &rotate_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 5fa2ea9025d9..77e12457d149 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -1771,9 +1771,10 @@ static int vpfe_queue_setup(struct vb2_queue *vq, { struct vpfe_device *vpfe = vb2_get_drv_priv(vq); unsigned size = vpfe->fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; if (*nplanes) { if (sizes[0] < size) @@ -2233,7 +2234,7 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) q->buf_struct_size = sizeof(struct vpfe_cap_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &vpfe->lock; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->dev = vpfe->pdev; err = vb2_queue_init(q); diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 1a4273bbe752..4afc2ad00330 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -57,7 +57,7 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) state = v4l2_subdev_get_locked_active_state(&phy->subdev); - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); fmtinfo = cal_format_by_code(fmt->code); if (!fmtinfo) @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { - struct cal_camerarx *phy = to_cal_camerarx(sd); - /* No transcoding, source and sink codes must match. */ if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; @@ -630,8 +628,8 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); code->code = fmt->code; } else { if (code->index >= cal_num_formats) @@ -656,8 +654,8 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); if (fse->code != fmt->code) return -EINVAL; @@ -713,18 +711,18 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - fmt = v4l2_subdev_get_pad_format(sd, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_FIRST_SOURCE); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_FIRST_SOURCE); *fmt = format->format; return 0; } -static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .which = state ? V4L2_SUBDEV_FORMAT_TRY @@ -784,7 +782,6 @@ static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { }; static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { - .init_cfg = cal_camerarx_sd_init_cfg, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -797,6 +794,10 @@ static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { .pad = &cal_camerarx_pad_ops, }; +static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = { + .init_state = cal_camerarx_sd_init_state, +}; + static struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -848,6 +849,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, /* Initialize the V4L2 subdev and media entity. */ sd = &phy->subdev; v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->internal_ops = &cal_camerarx_internal_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index a8abcd0fee17..e1ba5dfc217e 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -603,9 +603,10 @@ static int cal_queue_setup(struct vb2_queue *vq, { struct cal_ctx *ctx = vb2_get_drv_priv(vq); unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; if (*nplanes) { if (sizes[0] < size) @@ -697,7 +698,7 @@ static int cal_video_check_format(struct cal_ctx *ctx) state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev); - format = v4l2_subdev_get_pad_format(&ctx->phy->subdev, state, remote_pad->index); + format = v4l2_subdev_state_get_format(state, remote_pad->index); if (!format) { ret = -EINVAL; goto out; @@ -1009,7 +1010,7 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; - q->min_buffers_needed = 3; + q->min_queued_buffers = 3; q->dev = ctx->cal->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 99fae8830c41..c31a5566fc5a 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -113,6 +113,7 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; unsigned size = common->fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); vpif_dbg(2, debug, "vpif_buffer_setup\n"); @@ -122,8 +123,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, size = sizes[0]; } - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; *nplanes = 1; sizes[0] = size; @@ -1428,7 +1429,7 @@ static int vpif_probe_complete(void) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_cap_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &common->lock; q->dev = vpif_dev; diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c index f8ec2991c667..02ede1fe12cb 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -115,6 +115,7 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; unsigned size = common->fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); if (*nplanes) { if (sizes[0] < size) @@ -122,8 +123,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, size = sizes[0]; } - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; *nplanes = 1; sizes[0] = size; @@ -1168,7 +1169,7 @@ static int vpif_probe_complete(void) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_disp_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &common->lock; q->dev = vpif_dev; err = vb2_queue_init(q); diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index ada61391c8d2..59b30fc43144 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -873,7 +873,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->dev = dmaengine_get_dma_device(csi->dma.chan); q->lock = &csi->mutex; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/ti/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c index 4143274089c3..1c56b6a87ced 100644 --- a/drivers/media/platform/ti/omap/omap_vout.c +++ b/drivers/media/platform/ti/omap/omap_vout.c @@ -944,10 +944,11 @@ static int omap_vout_vb2_queue_setup(struct vb2_queue *vq, struct device *alloc_devs[]) { struct omap_vout_device *vout = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); int size = vout->pix.sizeimage; - if (is_rotation_enabled(vout) && vq->num_buffers + *nbufs > VRFB_NUM_BUFS) { - *nbufs = VRFB_NUM_BUFS - vq->num_buffers; + if (is_rotation_enabled(vout) && q_num_bufs + *nbufs > VRFB_NUM_BUFS) { + *nbufs = VRFB_NUM_BUFS - q_num_bufs; if (*nbufs == 0) return -EINVAL; } @@ -1403,7 +1404,7 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) vq->ops = &omap_vout_vb2_ops; vq->mem_ops = &vb2_dma_contig_memops; vq->lock = &vout->lock; - vq->min_buffers_needed = 1; + vq->min_queued_buffers = 1; vfd->queue = vq; ret = vb2_queue_init(vq); diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 2fe42aa91800..dd375c4e180d 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1948,8 +1948,7 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccdc->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccdc->formats[pad]; } @@ -1960,8 +1959,8 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&ccdc->subdev, sd_state, - CCDC_PAD_SOURCE_OF); + return v4l2_subdev_state_get_crop(sd_state, + CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } @@ -1969,7 +1968,7 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device - * @cfg : V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: Pad number * @fmt: Format */ @@ -2127,7 +2126,7 @@ static void ccdc_try_crop(struct isp_ccdc_device *ccdc, /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg : V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -2230,7 +2229,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, /* * ccdc_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the output formatter @@ -2274,7 +2273,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, /* * ccdc_set_selection - Set a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the output @@ -2322,7 +2321,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond @@ -2346,7 +2345,7 @@ static int ccdc_get_format(struct v4l2_subdev *sd, /* * ccdc_set_format - Set the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index da5f0176ec78..1204ee221c9e 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -614,7 +614,7 @@ static const unsigned int ccp2_fmts[] = { /* * __ccp2_get_format - helper function for getting ccp2 format * @ccp2 : Pointer to ISP CCP2 device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad : pad number * @which : wanted subdev format * return format structure or NULL on error @@ -625,8 +625,7 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccp2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccp2->formats[pad]; } @@ -634,7 +633,7 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, /* * ccp2_try_format - Handle try format by pad subdev method * @ccp2 : Pointer to ISP CCP2 device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad : pad num * @fmt : pointer to v4l2 mbus format structure * @which : wanted subdev format @@ -689,7 +688,7 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, /* * ccp2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -750,7 +749,7 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, /* * ccp2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -772,7 +771,7 @@ static int ccp2_get_format(struct v4l2_subdev *sd, /* * ccp2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * returns zero */ diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 0f9a54b11f98..ae574e1b6528 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -834,8 +834,7 @@ __csi2_get_format(struct isp_csi2_device *csi2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->formats[pad]; } @@ -894,7 +893,7 @@ csi2_try_format(struct isp_csi2_device *csi2, /* * csi2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -968,7 +967,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, /* * csi2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -990,7 +989,7 @@ static int csi2_get_format(struct v4l2_subdev *sd, /* * csi2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c index 53aedec7990d..e383a57654de 100644 --- a/drivers/media/platform/ti/omap3isp/isppreview.c +++ b/drivers/media/platform/ti/omap3isp/isppreview.c @@ -1684,8 +1684,7 @@ __preview_get_format(struct isp_prev_device *prev, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&prev->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &prev->formats[pad]; } @@ -1696,8 +1695,7 @@ __preview_get_crop(struct isp_prev_device *prev, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&prev->subdev, sd_state, - PREV_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, PREV_PAD_SINK); else return &prev->crop; } @@ -1724,7 +1722,7 @@ static const unsigned int preview_output_fmts[] = { /* * preview_try_format - Validate a format * @prev: ISP preview engine - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad number * @fmt: format to be validated * @which: try/active format selector @@ -1863,7 +1861,7 @@ static void preview_try_crop(struct isp_prev_device *prev, /* * preview_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -1924,7 +1922,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, /* * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1967,7 +1965,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, /* * preview_set_selection - Set a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -2015,7 +2013,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, /* * preview_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -2037,7 +2035,7 @@ static int preview_get_format(struct v4l2_subdev *sd, /* * preview_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c index ed2fb0c7a57e..87d821b02e5c 100644 --- a/drivers/media/platform/ti/omap3isp/ispresizer.c +++ b/drivers/media/platform/ti/omap3isp/ispresizer.c @@ -109,7 +109,7 @@ static const struct isprsz_coef filter_coefs = { * __resizer_get_format - helper function for getting resizer format * @res : pointer to resizer private structure * @pad : pad number - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which : wanted subdev format * return zero */ @@ -119,7 +119,7 @@ __resizer_get_format(struct isp_res_device *res, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&res->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &res->formats[pad]; } @@ -127,7 +127,7 @@ __resizer_get_format(struct isp_res_device *res, /* * __resizer_get_crop - helper function for getting resizer crop rectangle * @res : pointer to resizer private structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which : wanted subdev crop rectangle */ static struct v4l2_rect * @@ -136,8 +136,7 @@ __resizer_get_crop(struct isp_res_device *res, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&res->subdev, sd_state, - RESZ_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RESZ_PAD_SINK); else return &res->crop.request; } @@ -1215,7 +1214,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, /* * resizer_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1265,7 +1264,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, /* * resizer_set_selection - Set a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -1369,7 +1368,7 @@ static unsigned int resizer_max_in_width(struct isp_res_device *res) /* * resizer_try_format - Handle try format by pad subdev method * @res : ISP resizer device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad : pad num * @fmt : pointer to v4l2 format structure * @which : wanted subdev format @@ -1413,7 +1412,7 @@ static void resizer_try_format(struct isp_res_device *res, /* * resizer_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -1474,7 +1473,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, /* * resizer_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -1496,7 +1495,7 @@ static int resizer_get_format(struct v4l2_subdev *sd, /* * resizer_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig index e65b836b9d78..24b927d8f182 100644 --- a/drivers/media/platform/verisilicon/Kconfig +++ b/drivers/media/platform/verisilicon/Kconfig @@ -8,7 +8,6 @@ config VIDEO_HANTRO depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h index 77aee9489516..6f5eb975d0e3 100644 --- a/drivers/media/platform/verisilicon/hantro.h +++ b/drivers/media/platform/verisilicon/hantro.h @@ -328,6 +328,8 @@ struct hantro_vp9_decoded_buffer_info { /* Info needed when the decoded frame serves as a reference frame. */ unsigned short width; unsigned short height; + size_t chroma_offset; + size_t mv_offset; u32 bit_depth : 4; }; @@ -469,11 +471,14 @@ hantro_get_dst_buf(struct hantro_ctx *ctx) bool hantro_needs_postproc(const struct hantro_ctx *ctx, const struct hantro_fmt *fmt); +dma_addr_t +hantro_postproc_get_dec_buf_addr(struct hantro_ctx *ctx, int index); + static inline dma_addr_t hantro_get_dec_buf_addr(struct hantro_ctx *ctx, struct vb2_buffer *vb) { if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - return ctx->postproc.dec_q[vb->index].dma; + return hantro_postproc_get_dec_buf_addr(ctx, vb->index); return vb2_dma_contig_plane_dma_addr(vb, 0); } @@ -485,8 +490,8 @@ vb2_to_hantro_decoded_buf(struct vb2_buffer *buf) void hantro_postproc_disable(struct hantro_ctx *ctx); void hantro_postproc_enable(struct hantro_ctx *ctx); +int hantro_postproc_init(struct hantro_ctx *ctx); void hantro_postproc_free(struct hantro_ctx *ctx); -int hantro_postproc_alloc(struct hantro_ctx *ctx); int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize); diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index a9fa05ac56a9..db3df6cc4513 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -235,8 +235,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) * The Kernel needs access to the JPEG destination buffer for the * JPEG encoder to fill in the JPEG headers. */ - if (!ctx->is_encoder) + if (!ctx->is_encoder) { dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; + dst_vq->max_num_buffers = MAX_POSTPROC_BUFFERS; + } dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; @@ -905,6 +907,8 @@ static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { vpu->encoder = func; + v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); } else { vpu->decoder = func; v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c index ee5f14c5f8f2..b880a6849d58 100644 --- a/drivers/media/platform/verisilicon/hantro_g2.c +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -8,6 +8,8 @@ #include "hantro_hw.h" #include "hantro_g2_regs.h" +#define G2_ALIGN 16 + void hantro_g2_check_idle(struct hantro_dev *vpu) { int i; @@ -42,3 +44,15 @@ irqreturn_t hantro_g2_irq(int irq, void *dev_id) return IRQ_HANDLED; } + +size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx) +{ + return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8; +} + +size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx) +{ + size_t cr_offset = hantro_g2_chroma_offset(ctx); + + return ALIGN((cr_offset * 3) / 2, G2_ALIGN); +} diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c index a9d4ac84a8d8..d3f8c33eb16c 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -8,20 +8,6 @@ #include "hantro_hw.h" #include "hantro_g2_regs.h" -#define G2_ALIGN 16 - -static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx) -{ - return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8; -} - -static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx) -{ - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - static void prepare_tile_info_buffer(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; @@ -384,8 +370,8 @@ static int set_ref(struct hantro_ctx *ctx) struct hantro_dev *vpu = ctx->dev; struct vb2_v4l2_buffer *vb2_dst; struct hantro_decoded_buffer *dst; - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - size_t mv_offset = hantro_hevc_motion_vectors_offset(ctx); + size_t cr_offset = hantro_g2_chroma_offset(ctx); + size_t mv_offset = hantro_g2_motion_vectors_offset(ctx); u32 max_ref_frames; u16 dpb_longterm_e; static const struct hantro_reg cur_poc[] = { diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c index 6fc4b555517f..342e543dee4c 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c @@ -16,8 +16,6 @@ #include "hantro_vp9.h" #include "hantro_g2_regs.h" -#define G2_ALIGN 16 - enum hantro_ref_frames { INTRA_FRAME = 0, LAST_FRAME = 1, @@ -90,22 +88,6 @@ static int start_prepare_run(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_ return 0; } -static size_t chroma_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2; - - return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel; -} - -static size_t mv_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - size_t cr_offset = chroma_offset(ctx, dec_params); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - static struct hantro_decoded_buffer * get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) { @@ -156,11 +138,13 @@ static void config_output(struct hantro_ctx *ctx, luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); hantro_write_addr(ctx->dev, G2_OUT_LUMA_ADDR, luma_addr); - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); + chroma_addr = luma_addr + hantro_g2_chroma_offset(ctx); hantro_write_addr(ctx->dev, G2_OUT_CHROMA_ADDR, chroma_addr); + dst->vp9.chroma_offset = hantro_g2_chroma_offset(ctx); - mv_addr = luma_addr + mv_offset(ctx, dec_params); + mv_addr = luma_addr + hantro_g2_motion_vectors_offset(ctx); hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, mv_addr); + dst->vp9.mv_offset = hantro_g2_motion_vectors_offset(ctx); } struct hantro_vp9_ref_reg { @@ -195,7 +179,7 @@ static void config_ref(struct hantro_ctx *ctx, luma_addr = hantro_get_dec_buf_addr(ctx, &buf->base.vb.vb2_buf); hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr); - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); + chroma_addr = luma_addr + buf->vp9.chroma_offset; hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr); } @@ -238,7 +222,7 @@ static void config_ref_registers(struct hantro_ctx *ctx, config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params->alt_frame_ts); mv_addr = hantro_get_dec_buf_addr(ctx, &mv_ref->base.vb.vb2_buf) + - mv_offset(ctx, dec_params); + mv_ref->vp9.mv_offset; hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_addr); hantro_reg_write(ctx->dev, &vp9_last_sign_bias, diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 7f33f7b07ce4..9aec8a79acdc 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -40,6 +40,8 @@ #define AV1_MAX_FRAME_BUF_COUNT (V4L2_AV1_TOTAL_REFS_PER_FRAME + 1) +#define MAX_POSTPROC_BUFFERS 64 + struct hantro_dev; struct hantro_ctx; struct hantro_buf; @@ -336,7 +338,7 @@ struct hantro_av1_dec_hw_ctx { * @dec_q: References buffers, in decoder format. */ struct hantro_postproc_ctx { - struct hantro_aux_buf dec_q[VB2_MAX_FRAME]; + struct hantro_aux_buf dec_q[MAX_POSTPROC_BUFFERS]; }; /** @@ -519,6 +521,9 @@ hantro_av1_mv_size(unsigned int width, unsigned int height) return ALIGN(num_sbs * 384, 16) * 2 + 512; } +size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx); +size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx); + int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx); int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx); void hantro_mpeg2_dec_copy_qtable(u8 *qtable, diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 64d6fb852ae9..41e93176300b 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -177,9 +177,11 @@ static int hantro_postproc_g2_enum_framesizes(struct hantro_ctx *ctx, void hantro_postproc_free(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *queue = &m2m_ctx->cap_q_ctx.q; unsigned int i; - for (i = 0; i < VB2_MAX_FRAME; ++i) { + for (i = 0; i < queue->max_num_buffers; ++i) { struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; if (priv->cpu) { @@ -190,20 +192,17 @@ void hantro_postproc_free(struct hantro_ctx *ctx) } } -int hantro_postproc_alloc(struct hantro_ctx *ctx) +static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx) { - struct hantro_dev *vpu = ctx->dev; - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; - unsigned int num_buffers = cap_queue->num_buffers; struct v4l2_pix_format_mplane pix_mp; const struct hantro_fmt *fmt; - unsigned int i, buf_size; + unsigned int buf_size; /* this should always pick native format */ fmt = hantro_get_default_fmt(ctx, false, ctx->bit_depth, HANTRO_AUTO_POSTPROC); if (!fmt) - return -EINVAL; + return 0; + v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, ctx->src_fmt.height); @@ -221,23 +220,77 @@ int hantro_postproc_alloc(struct hantro_ctx *ctx) buf_size += hantro_av1_mv_size(pix_mp.width, pix_mp.height); - for (i = 0; i < num_buffers; ++i) { - struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; + return buf_size; +} + +static int hantro_postproc_alloc(struct hantro_ctx *ctx, int index) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_aux_buf *priv = &ctx->postproc.dec_q[index]; + unsigned int buf_size = hantro_postproc_buffer_size(ctx); + + if (!buf_size) + return -EINVAL; + + /* + * The buffers on this queue are meant as intermediate + * buffers for the decoder, so no mapping is needed. + */ + priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; + priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, + GFP_KERNEL, priv->attrs); + if (!priv->cpu) + return -ENOMEM; + priv->size = buf_size; + + return 0; +} - /* - * The buffers on this queue are meant as intermediate - * buffers for the decoder, so no mapping is needed. - */ - priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; - priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, - GFP_KERNEL, priv->attrs); - if (!priv->cpu) - return -ENOMEM; - priv->size = buf_size; +int hantro_postproc_init(struct hantro_ctx *ctx) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; + unsigned int num_buffers = vb2_get_num_buffers(cap_queue); + unsigned int i; + int ret; + + for (i = 0; i < num_buffers; i++) { + ret = hantro_postproc_alloc(ctx, i); + if (ret) + return ret; } + return 0; } +dma_addr_t +hantro_postproc_get_dec_buf_addr(struct hantro_ctx *ctx, int index) +{ + struct hantro_aux_buf *priv = &ctx->postproc.dec_q[index]; + unsigned int buf_size = hantro_postproc_buffer_size(ctx); + struct hantro_dev *vpu = ctx->dev; + int ret; + + if (priv->size < buf_size && priv->cpu) { + /* buffer is too small, release it */ + dma_free_attrs(vpu->dev, priv->size, priv->cpu, + priv->dma, priv->attrs); + priv->cpu = NULL; + } + + if (!priv->cpu) { + /* buffer not already allocated, try getting a new one */ + ret = hantro_postproc_alloc(ctx, index); + if (ret) + return 0; + } + + if (!priv->cpu) + return 0; + + return priv->dma; +} + static void hantro_postproc_g1_disable(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index b3ae037a50f6..941fa23c211a 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -514,25 +514,14 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, return ret; if (!ctx->is_encoder) { - struct vb2_queue *peer_vq; - /* * In order to support dynamic resolution change, * the decoder admits a resolution change, as long - * as the pixelformat remains. Can't be done if streaming. - */ - if (vb2_is_streaming(vq) || (vb2_is_busy(vq) && - pix_mp->pixelformat != ctx->src_fmt.pixelformat)) - return -EBUSY; - /* - * Since format change on the OUTPUT queue will reset - * the CAPTURE queue, we can't allow doing so - * when the CAPTURE queue has buffers allocated. + * as the pixelformat remains. */ - peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(peer_vq)) + if (vb2_is_streaming(vq) && pix_mp->pixelformat != ctx->src_fmt.pixelformat) { return -EBUSY; + } } else { /* * The encoder doesn't admit a format change if @@ -577,15 +566,8 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, static int hantro_set_fmt_cap(struct hantro_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp) { - struct vb2_queue *vq; int ret; - /* Change not allowed if queue is busy. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(vq)) - return -EBUSY; - if (ctx->is_encoder) { struct vb2_queue *peer_vq; @@ -785,6 +767,9 @@ const struct v4l2_ioctl_ops hantro_ioctl_ops = { .vidioc_g_selection = vidioc_g_selection, .vidioc_s_selection = vidioc_s_selection, + .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, .vidioc_encoder_cmd = vidioc_encoder_cmd, }; @@ -933,7 +918,7 @@ static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) } if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) { - ret = hantro_postproc_alloc(ctx); + ret = hantro_postproc_init(ctx); if (ret) goto err_codec_exit; } diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 5de6b6694f53..31e9e92e723e 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -89,10 +89,10 @@ static int video_mux_link_setup(struct media_entity *entity, /* Propagate the active format to the source */ sd_state = v4l2_subdev_lock_and_get_active_state(sd); - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, - source_pad); - *source_mbusformat = *v4l2_subdev_get_pad_format(sd, sd_state, - vmux->active); + source_mbusformat = v4l2_subdev_state_get_format(sd_state, + source_pad); + *source_mbusformat = *v4l2_subdev_state_get_format(sd_state, + vmux->active); v4l2_subdev_unlock_state(sd_state); } else { if (vmux->active != local->index) @@ -154,11 +154,11 @@ static int video_mux_set_format(struct v4l2_subdev *sd, struct media_pad *pad = &vmux->pads[sdformat->pad]; u16 source_pad = sd->entity.num_pads - 1; - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); if (!mbusformat) return -EINVAL; - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, source_pad); + source_mbusformat = v4l2_subdev_state_get_format(sd_state, source_pad); if (!source_mbusformat) return -EINVAL; @@ -268,8 +268,8 @@ static int video_mux_set_format(struct v4l2_subdev *sd, /* Source pad mirrors active sink pad, no limitations on sink pads */ if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0) - sdformat->format = *v4l2_subdev_get_pad_format(sd, sd_state, - vmux->active); + sdformat->format = *v4l2_subdev_state_get_format(sd_state, + vmux->active); *mbusformat = sdformat->format; @@ -282,8 +282,8 @@ static int video_mux_set_format(struct v4l2_subdev *sd, return 0; } -static int video_mux_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int video_mux_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); struct v4l2_mbus_framefmt *mbusformat; @@ -292,7 +292,7 @@ static int video_mux_init_cfg(struct v4l2_subdev *sd, mutex_lock(&vmux->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = video_mux_format_mbus_default; } @@ -302,7 +302,6 @@ static int video_mux_init_cfg(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { - .init_cfg = video_mux_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = video_mux_set_format, }; @@ -312,6 +311,10 @@ static const struct v4l2_subdev_ops video_mux_subdev_ops = { .video = &video_mux_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops video_mux_internal_ops = { + .init_state = video_mux_init_state, +}; + static int video_mux_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_connection *asd) @@ -400,6 +403,7 @@ static int video_mux_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vmux); v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops); + vmux->subdev.internal_ops = &video_mux_internal_ops; snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%pOFn", np); vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; vmux->subdev.dev = dev; diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index 5b53745fe44e..f953d5474ae0 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -386,14 +386,6 @@ static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) } } -/** - * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver - * @sd: Pointer to V4L2 subdevice structure - * - * This function prints the current status of Xilinx MIPI CSI-2 - * - * Return: 0 on success - */ static int xcsi2rxss_log_status(struct v4l2_subdev *sd) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); @@ -631,16 +623,6 @@ static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data) return IRQ_HANDLED; } -/** - * xcsi2rxss_s_stream - It is used to start/stop the streaming. - * @sd: V4L2 Sub device - * @enable: Flag (True / False) - * - * This function controls the start or stop of streaming for the - * Xilinx MIPI CSI-2 Rx Subsystem. - * - * Return: 0 on success, errors otherwise - */ static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); @@ -671,8 +653,7 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xcsi2rxss->format; default: @@ -680,18 +661,8 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, } } -/** - * xcsi2rxss_init_cfg - Initialise the pad format config to default - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * - * This function is used to initialize the pad format with the default - * values. - * - * Return: 0 on success - */ -static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int xcsi2rxss_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); struct v4l2_mbus_framefmt *format; @@ -699,7 +670,7 @@ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, mutex_lock(&xcsi2rxss->lock); for (i = 0; i < XCSI_MEDIA_PADS; i++) { - format = v4l2_subdev_get_try_format(sd, sd_state, i); + format = v4l2_subdev_state_get_format(sd_state, i); *format = xcsi2rxss->default_format; } mutex_unlock(&xcsi2rxss->lock); @@ -707,16 +678,6 @@ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, return 0; } -/** - * xcsi2rxss_get_format - Get the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to get the pad format information. - * - * Return: 0 on success - */ static int xcsi2rxss_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -732,19 +693,6 @@ static int xcsi2rxss_get_format(struct v4l2_subdev *sd, return 0; } -/** - * xcsi2rxss_set_format - This is used to set the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to set the pad format. Since the pad format is fixed - * in hardware, it can't be modified on run time. So when a format set is - * requested by application, all parameters except the format type is saved - * for the pad and the original pad format is sent back to the application. - * - * Return: 0 on success - */ static int xcsi2rxss_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -789,14 +737,6 @@ static int xcsi2rxss_set_format(struct v4l2_subdev *sd, return 0; } -/* - * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration - * @sd: pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * - * Return: -EINVAL or zero on success - */ static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -840,7 +780,6 @@ static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { }; static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { - .init_cfg = xcsi2rxss_init_cfg, .get_fmt = xcsi2rxss_get_format, .set_fmt = xcsi2rxss_set_format, .enum_mbus_code = xcsi2rxss_enum_mbus_code, @@ -853,6 +792,10 @@ static const struct v4l2_subdev_ops xcsi2rxss_ops = { .pad = &xcsi2rxss_pad_ops }; +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = { + .init_state = xcsi2rxss_init_state, +}; + static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) { struct device *dev = xcsi2rxss->dev; @@ -1030,6 +973,7 @@ static int xcsi2rxss_probe(struct platform_device *pdev) /* Initialize V4L2 subdevice and media entity */ subdev = &xcsi2rxss->subdev; v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->internal_ops = &xcsi2rxss_internal_ops; subdev->dev = dev; strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 80353ca44402..e05e528ffc6f 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -256,8 +256,7 @@ __xtpg_get_pad_format(struct xtpg_device *xtpg, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xtpg->formats[pad]; default: @@ -326,7 +325,7 @@ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -354,11 +353,11 @@ static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct xtpg_device *xtpg = to_tpg(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); *format = xtpg->default_format; if (xtpg->npads == 2) { - format = v4l2_subdev_get_try_format(subdev, fh->state, 1); + format = v4l2_subdev_state_get_format(fh->state, 1); *format = xtpg->default_format; } diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c index 5b214bf7f93a..f1574edd2b43 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.c +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -260,7 +260,7 @@ int xvip_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); + format = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = format->code; @@ -295,7 +295,7 @@ int xvip_enum_frame_size(struct v4l2_subdev *subdev, if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; |