diff options
Diffstat (limited to 'drivers/media/platform')
335 files changed, 23197 insertions, 5143 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 91e54215de3a..85d2627776b6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -67,8 +67,10 @@ source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/aspeed/Kconfig" source "drivers/media/platform/atmel/Kconfig" +source "drivers/media/platform/broadcom/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" +source "drivers/media/platform/imagination/Kconfig" source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" source "drivers/media/platform/mediatek/Kconfig" @@ -77,6 +79,7 @@ source "drivers/media/platform/nuvoton/Kconfig" source "drivers/media/platform/nvidia/Kconfig" source "drivers/media/platform/nxp/Kconfig" source "drivers/media/platform/qcom/Kconfig" +source "drivers/media/platform/raspberrypi/Kconfig" source "drivers/media/platform/renesas/Kconfig" source "drivers/media/platform/rockchip/Kconfig" source "drivers/media/platform/samsung/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 3296ec1ebe16..ace4e34483dd 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -10,8 +10,10 @@ obj-y += amlogic/ obj-y += amphion/ obj-y += aspeed/ obj-y += atmel/ +obj-y += broadcom/ obj-y += cadence/ obj-y += chips-media/ +obj-y += imagination/ obj-y += intel/ obj-y += marvell/ obj-y += mediatek/ @@ -20,6 +22,7 @@ obj-y += nuvoton/ obj-y += nvidia/ obj-y += nxp/ obj-y += qcom/ +obj-y += raspberrypi/ obj-y += renesas/ obj-y += rockchip/ obj-y += samsung/ diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index da61f9beb6b4..e491399afcc9 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -179,7 +179,7 @@ struct allegro_dev { struct list_head channels; }; -static struct regmap_config allegro_regmap_config = { +static const struct regmap_config allegro_regmap_config = { .name = "regmap", .reg_bits = 32, .val_bits = 32, @@ -188,7 +188,7 @@ static struct regmap_config allegro_regmap_config = { .cache_type = REGCACHE_NONE, }; -static struct regmap_config allegro_sram_config = { +static const struct regmap_config allegro_sram_config = { .name = "sram", .reg_bits = 32, .val_bits = 32, @@ -1415,11 +1415,11 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, unsigned long timeout_ms) { - unsigned long tmo; + unsigned long time_left; - tmo = wait_for_completion_timeout(&dev->init_complete, - msecs_to_jiffies(timeout_ms)); - if (tmo == 0) + time_left = wait_for_completion_timeout(&dev->init_complete, + msecs_to_jiffies(timeout_ms)); + if (time_left == 0) return -ETIMEDOUT; reinit_completion(&dev->init_complete); @@ -1509,8 +1509,10 @@ static int allocate_buffers_internal(struct allegro_channel *channel, INIT_LIST_HEAD(&buffer->head); err = allegro_alloc_buffer(dev, buffer, size); - if (err) + if (err) { + kfree(buffer); goto err; + } list_add(&buffer->head, list); } @@ -2481,14 +2483,14 @@ static void allegro_mcu_interrupt(struct allegro_dev *dev) static void allegro_destroy_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; - unsigned long timeout; + unsigned long time_left; if (channel_exists(channel)) { reinit_completion(&channel->completion); allegro_mcu_send_destroy_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (time_left == 0) v4l2_warn(&dev->v4l2_dev, "channel %d: timeout while destroying\n", channel->mcu_channel_id); @@ -2544,7 +2546,7 @@ static void allegro_destroy_channel(struct allegro_channel *channel) static int allegro_create_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; - unsigned long timeout; + unsigned long time_left; if (channel_exists(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -2595,9 +2597,9 @@ static int allegro_create_channel(struct allegro_channel *channel) reinit_completion(&channel->completion); allegro_mcu_send_create_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (time_left == 0) channel->error = -ETIMEDOUT; if (channel->error) goto err; @@ -2895,8 +2897,6 @@ static const struct vb2_ops allegro_queue_ops = { .buf_queue = allegro_buf_queue, .start_streaming = allegro_start_streaming, .stop_streaming = allegro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int allegro_queue_init(void *priv, @@ -4003,7 +4003,7 @@ static const struct dev_pm_ops allegro_pm_ops = { static struct platform_driver allegro_driver = { .probe = allegro_probe, - .remove_new = allegro_remove, + .remove = allegro_remove, .driver = { .name = "allegro", .of_match_table = allegro_dt_ids, diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h index eb46f12aae80..361e2f55c254 100644 --- a/drivers/media/platform/allegro-dvt/nal-hevc.h +++ b/drivers/media/platform/allegro-dvt/nal-hevc.h @@ -96,10 +96,11 @@ struct nal_hevc_vps { unsigned int extension_data_flag; }; +#define N_HRD_PARAMS 1 struct nal_hevc_sub_layer_hrd_parameters { - unsigned int bit_rate_value_minus1[1]; - unsigned int cpb_size_value_minus1[1]; - unsigned int cbr_flag[1]; + unsigned int bit_rate_value_minus1[N_HRD_PARAMS]; + unsigned int cpb_size_value_minus1[N_HRD_PARAMS]; + unsigned int cbr_flag[N_HRD_PARAMS]; }; struct nal_hevc_hrd_parameters { diff --git a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c index 09409908ba5d..0c004bb8ba05 100644 --- a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c +++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c @@ -391,8 +391,6 @@ static const struct vb2_ops ge2d_qops = { .buf_queue = ge2d_buf_queue, .start_streaming = ge2d_start_streaming, .stop_streaming = ge2d_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int @@ -1045,7 +1043,7 @@ MODULE_DEVICE_TABLE(of, meson_ge2d_match); static struct platform_driver ge2d_drv = { .probe = ge2d_probe, - .remove_new = ge2d_remove, + .remove = ge2d_remove, .driver = { .name = "meson-ge2d", .of_match_table = meson_ge2d_match, diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index a57f9f4f3b87..6a38a0fa0e2d 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -195,7 +195,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) struct vdec_t *vdec = inst->priv; int ret = 0; - vpu_inst_lock(inst); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: vdec->params.display_delay_enable = ctrl->val; @@ -207,7 +206,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - vpu_inst_unlock(inst); return ret; } diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 4eb57d793a9c..c5c1f1fbaa80 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -52,6 +52,7 @@ struct venc_t { u32 ready_count; u32 enable; u32 stopped; + u32 memory_resource_configured; u32 skipped_count; u32 skipped_bytes; @@ -518,7 +519,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) struct venc_t *venc = inst->priv; int ret = 0; - vpu_inst_lock(inst); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: venc->params.profile = ctrl->val; @@ -579,7 +579,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - vpu_inst_unlock(inst); return ret; } @@ -680,6 +679,9 @@ static int venc_ctrl_init(struct vpu_inst *inst) ~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME), V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0); + if (inst->ctrl_handler.error) { ret = inst->ctrl_handler.error; v4l2_ctrl_handler_free(&inst->ctrl_handler); @@ -819,6 +821,7 @@ static int venc_get_one_encoded_frame(struct vpu_inst *inst, vbuf->field = inst->cap_format.field; vbuf->flags |= frame->info.pic_type; vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + vpu_set_buffer_average_qp(vbuf, frame->info.average_qp); dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); venc->ready_count++; @@ -941,10 +944,19 @@ static int venc_start_session(struct vpu_inst *inst, u32 type) ret = vpu_iface_set_encode_params(inst, &venc->params, 0); if (ret) goto error; + + venc->memory_resource_configured = false; ret = vpu_session_configure_codec(inst); if (ret) goto error; + if (!venc->memory_resource_configured) { + vb2_queue_error(v4l2_m2m_get_src_vq(inst->fh.m2m_ctx)); + vb2_queue_error(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx)); + ret = -ENOMEM; + goto error; + } + inst->state = VPU_CODEC_STATE_CONFIGURED; /*vpu_iface_config_memory_resource*/ @@ -983,6 +995,7 @@ static void venc_cleanup_mem_resource(struct vpu_inst *inst) u32 i; venc = inst->priv; + venc->memory_resource_configured = false; for (i = 0; i < ARRAY_SIZE(venc->enc); i++) vpu_free_dma(&venc->enc[i]); @@ -1046,6 +1059,7 @@ static void venc_request_mem_resource(struct vpu_inst *inst, vpu_iface_config_memory_resource(inst, MEM_RES_REF, i, &venc->ref[i]); for (i = 0; i < act_frame_num; i++) vpu_iface_config_memory_resource(inst, MEM_RES_ACT, i, &venc->act[i]); + venc->memory_resource_configured = true; } static void venc_cleanup_frames(struct venc_t *venc) diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 0246cf0ac3a8..22f0da26ccec 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -306,6 +306,7 @@ struct vpu_vb2_buffer { dma_addr_t chroma_v; unsigned int state; u32 tag; + u32 average_qp; }; void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val); diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 3a2030d02e45..8df85c14ab3f 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -864,7 +864,7 @@ MODULE_DEVICE_TABLE(of, vpu_core_dt_match); static struct platform_driver amphion_vpu_core_driver = { .probe = vpu_core_probe, - .remove_new = vpu_core_remove, + .remove = vpu_core_remove, .driver = { .name = "amphion-vpu-core", .of_match_table = vpu_core_dt_match, diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h index 7320852668d6..428d988cf2f7 100644 --- a/drivers/media/platform/amphion/vpu_defs.h +++ b/drivers/media/platform/amphion/vpu_defs.h @@ -114,6 +114,7 @@ struct vpu_enc_pic_info { u32 wptr; u32 crc; s64 timestamp; + u32 average_qp; }; struct vpu_dec_codec_info { diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c index 2bf70aafd2ba..efbfd2652721 100644 --- a/drivers/media/platform/amphion/vpu_drv.c +++ b/drivers/media/platform/amphion/vpu_drv.c @@ -151,8 +151,8 @@ err_add_decoder: media_device_cleanup(&vpu->mdev); v4l2_device_unregister(&vpu->v4l2_dev); err_vpu_deinit: - pm_runtime_set_suspended(dev); pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); return ret; } @@ -227,7 +227,7 @@ MODULE_DEVICE_TABLE(of, vpu_dt_match); static struct platform_driver amphion_vpu_driver = { .probe = vpu_probe, - .remove_new = vpu_remove, + .remove = vpu_remove, .driver = { .name = "amphion-vpu", .of_match_table = vpu_dt_match, diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index d3425de7bccd..4769c053c6c2 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -207,11 +207,6 @@ struct vpu_malone_dbglog_desc { u32 reserved; }; -struct vpu_malone_frame_buffer { - u32 addr; - u32 size; -}; - struct vpu_malone_udata { u32 base; u32 total_size; diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index c88738e8fff7..45707931bc4f 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -63,6 +63,13 @@ unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf) return vpu_buf->state; } +void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp) +{ + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + + vpu_buf->average_qp = qp; +} + void vpu_v4l2_set_error(struct vpu_inst *inst) { vpu_inst_lock(inst); @@ -539,6 +546,15 @@ static void vpu_vb2_buf_finish(struct vb2_buffer *vb) struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); struct vb2_queue *q = vb->vb2_queue; + if (V4L2_TYPE_IS_CAPTURE(vb->type)) { + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP); + + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, vpu_buf->average_qp); + } + if (vbuf->flags & V4L2_BUF_FLAG_LAST) vpu_notify_eos(inst); @@ -630,8 +646,6 @@ static const struct vb2_ops vpu_vb2_ops = { .start_streaming = vpu_vb2_start_streaming, .stop_streaming = vpu_vb2_stop_streaming, .buf_queue = vpu_vb2_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -825,6 +839,7 @@ int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func) vfd->fops = vdec_get_fops(); vfd->ioctl_ops = vdec_get_ioctl_ops(); } + video_set_drvdata(vfd, vpu); ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { @@ -832,7 +847,6 @@ int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func) v4l2_m2m_release(func->m2m_dev); return ret; } - video_set_drvdata(vfd, vpu); func->vfd = vfd; ret = v4l2_m2m_register_media_controller(func->m2m_dev, func->vfd, func->function); diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h index 60f43056a7a2..56f2939fa84d 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.h +++ b/drivers/media/platform/amphion/vpu_v4l2.h @@ -12,6 +12,7 @@ void vpu_inst_lock(struct vpu_inst *inst); void vpu_inst_unlock(struct vpu_inst *inst); void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state); unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf); +void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp); int vpu_v4l2_open(struct file *file, struct vpu_inst *inst); int vpu_v4l2_close(struct file *file); diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c index 5f1101d7cf9e..e7d37aa4b826 100644 --- a/drivers/media/platform/amphion/vpu_windsor.c +++ b/drivers/media/platform/amphion/vpu_windsor.c @@ -499,6 +499,7 @@ struct windsor_pic_info { u32 proc_dacc_rng_wr_cnt; s32 tv_s; u32 tv_ns; + u32 average_qp; }; u32 vpu_windsor_get_data_size(void) @@ -734,6 +735,7 @@ static void vpu_windsor_unpack_pic_info(struct vpu_rpc_event *pkt, void *data) info->wptr = get_ptr(windsor->str_buff_wptr); info->crc = windsor->frame_crc; info->timestamp = timespec64_to_ns(&ts); + info->average_qp = windsor->average_qp; } static void vpu_windsor_unpack_mem_req(struct vpu_rpc_event *pkt, void *data) diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index fc6050e3be0d..54cae0da9aca 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -1891,8 +1891,6 @@ static void aspeed_video_buf_queue(struct vb2_buffer *vb) static const struct vb2_ops aspeed_video_vb2_ops = { .queue_setup = aspeed_video_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_prepare = aspeed_video_buf_prepare, .start_streaming = aspeed_video_start_streaming, .stop_streaming = aspeed_video_stop_streaming, @@ -2226,7 +2224,7 @@ static struct platform_driver aspeed_video_driver = { .of_match_table = aspeed_video_of_match, }, .probe = aspeed_video_probe, - .remove_new = aspeed_video_remove, + .remove = aspeed_video_remove, }; module_platform_driver(aspeed_video_driver); diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index c1108df72dd5..0d1c39347529 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -242,7 +242,7 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id) #define WAIT_ISI_DISABLE 0 static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) { - unsigned long timeout; + unsigned long time_left; /* * The reset or disable will only succeed if we have a * pixel clock from the camera. @@ -257,9 +257,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); } - timeout = wait_for_completion_timeout(&isi->complete, - msecs_to_jiffies(500)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&isi->complete, + msecs_to_jiffies(500)); + if (time_left == 0) return -ETIMEDOUT; return 0; @@ -526,8 +526,6 @@ static const struct vb2_ops isi_video_qops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int isi_g_fmt_vid_cap(struct file *file, void *priv, @@ -1367,7 +1365,7 @@ static struct platform_driver atmel_isi_driver = { .pm = &atmel_isi_dev_pm_ops, }, .probe = atmel_isi_probe, - .remove_new = atmel_isi_remove, + .remove = atmel_isi_remove, }; module_platform_driver(atmel_isi_driver); diff --git a/drivers/media/platform/broadcom/Kconfig b/drivers/media/platform/broadcom/Kconfig new file mode 100644 index 000000000000..32b76ebfcd9a --- /dev/null +++ b/drivers/media/platform/broadcom/Kconfig @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 + +config VIDEO_BCM2835_UNICAM + tristate "Broadcom BCM283x/BCM271x Unicam video capture driver" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on COMMON_CLK && PM + depends on VIDEO_DEV + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + help + Say Y here to enable support for the BCM283x/BCM271x CSI-2 receiver. + This is a V4L2 driver that controls the CSI-2 receiver directly, + independently from the VC4 firmware. + + This driver is mutually exclusive with the use of bcm2835-camera. The + firmware will disable all access to the peripheral from within the + firmware if it finds a DT node using it, and bcm2835-camera will + therefore fail to probe. + + To compile this driver as a module, choose M here. The module will be + called bcm2835-unicam. diff --git a/drivers/media/platform/broadcom/Makefile b/drivers/media/platform/broadcom/Makefile new file mode 100644 index 000000000000..03d2045aba2e --- /dev/null +++ b/drivers/media/platform/broadcom/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o diff --git a/drivers/media/platform/broadcom/bcm2835-unicam-regs.h b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h new file mode 100644 index 000000000000..fce6fa92707c --- /dev/null +++ b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (C) 2017-2020 Raspberry Pi Trading. + * Dave Stevenson <dave.stevenson@raspberrypi.com> + */ + +#ifndef VC4_REGS_UNICAM_H +#define VC4_REGS_UNICAM_H + +#include <linux/bits.h> + +/* + * The following values are taken from files found within the code drop + * made by Broadcom for the BCM21553 Graphics Driver, predominantly in + * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h. + * They have been modified to be only the register offset. + */ +#define UNICAM_CTRL 0x000 +#define UNICAM_STA 0x004 +#define UNICAM_ANA 0x008 +#define UNICAM_PRI 0x00c +#define UNICAM_CLK 0x010 +#define UNICAM_CLT 0x014 +#define UNICAM_DAT0 0x018 +#define UNICAM_DAT1 0x01c +#define UNICAM_DAT2 0x020 +#define UNICAM_DAT3 0x024 +#define UNICAM_DLT 0x028 +#define UNICAM_CMP0 0x02c +#define UNICAM_CMP1 0x030 +#define UNICAM_CAP0 0x034 +#define UNICAM_CAP1 0x038 +#define UNICAM_ICTL 0x100 +#define UNICAM_ISTA 0x104 +#define UNICAM_IDI0 0x108 +#define UNICAM_IPIPE 0x10c +#define UNICAM_IBSA0 0x110 +#define UNICAM_IBEA0 0x114 +#define UNICAM_IBLS 0x118 +#define UNICAM_IBWP 0x11c +#define UNICAM_IHWIN 0x120 +#define UNICAM_IHSTA 0x124 +#define UNICAM_IVWIN 0x128 +#define UNICAM_IVSTA 0x12c +#define UNICAM_ICC 0x130 +#define UNICAM_ICS 0x134 +#define UNICAM_IDC 0x138 +#define UNICAM_IDPO 0x13c +#define UNICAM_IDCA 0x140 +#define UNICAM_IDCD 0x144 +#define UNICAM_IDS 0x148 +#define UNICAM_DCS 0x200 +#define UNICAM_DBSA0 0x204 +#define UNICAM_DBEA0 0x208 +#define UNICAM_DBWP 0x20c +#define UNICAM_DBCTL 0x300 +#define UNICAM_IBSA1 0x304 +#define UNICAM_IBEA1 0x308 +#define UNICAM_IDI1 0x30c +#define UNICAM_DBSA1 0x310 +#define UNICAM_DBEA1 0x314 +#define UNICAM_MISC 0x400 + +/* + * The following bitmasks are from the kernel released by Broadcom + * for Android - https://android.googlesource.com/kernel/bcm/ + * The Rhea, Hawaii, and Java chips all contain the same VideoCore4 + * Unicam block as BCM2835, as defined in eg + * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar. + * Values reworked to use the kernel BIT and GENMASK macros. + * + * Some of the bit mnenomics have been amended to match the datasheet. + */ +/* UNICAM_CTRL Register */ +#define UNICAM_CPE BIT(0) +#define UNICAM_MEM BIT(1) +#define UNICAM_CPR BIT(2) +#define UNICAM_CPM_MASK GENMASK(3, 3) +#define UNICAM_CPM_CSI2 0 +#define UNICAM_CPM_CCP2 1 +#define UNICAM_SOE BIT(4) +#define UNICAM_DCM_MASK GENMASK(5, 5) +#define UNICAM_DCM_STROBE 0 +#define UNICAM_DCM_DATA 1 +#define UNICAM_SLS BIT(6) +#define UNICAM_PFT_MASK GENMASK(11, 8) +#define UNICAM_OET_MASK GENMASK(20, 12) + +/* UNICAM_STA Register */ +#define UNICAM_SYN BIT(0) +#define UNICAM_CS BIT(1) +#define UNICAM_SBE BIT(2) +#define UNICAM_PBE BIT(3) +#define UNICAM_HOE BIT(4) +#define UNICAM_PLE BIT(5) +#define UNICAM_SSC BIT(6) +#define UNICAM_CRCE BIT(7) +#define UNICAM_OES BIT(8) +#define UNICAM_IFO BIT(9) +#define UNICAM_OFO BIT(10) +#define UNICAM_BFO BIT(11) +#define UNICAM_DL BIT(12) +#define UNICAM_PS BIT(13) +#define UNICAM_IS BIT(14) +#define UNICAM_PI0 BIT(15) +#define UNICAM_PI1 BIT(16) +#define UNICAM_FSI_S BIT(17) +#define UNICAM_FEI_S BIT(18) +#define UNICAM_LCI_S BIT(19) +#define UNICAM_BUF0_RDY BIT(20) +#define UNICAM_BUF0_NO BIT(21) +#define UNICAM_BUF1_RDY BIT(22) +#define UNICAM_BUF1_NO BIT(23) +#define UNICAM_DI BIT(24) + +#define UNICAM_STA_MASK_ALL \ + (UNICAM_SBE | UNICAM_PBE | UNICAM_HOE | UNICAM_PLE | UNICAM_SSC | \ + UNICAM_CRCE | UNICAM_IFO | UNICAM_OFO | UNICAM_DL | UNICAM_PS | \ + UNICAM_PI0 | UNICAM_PI1) + +/* UNICAM_ANA Register */ +#define UNICAM_APD BIT(0) +#define UNICAM_BPD BIT(1) +#define UNICAM_AR BIT(2) +#define UNICAM_DDL BIT(3) +#define UNICAM_CTATADJ_MASK GENMASK(7, 4) +#define UNICAM_PTATADJ_MASK GENMASK(11, 8) + +/* UNICAM_PRI Register */ +#define UNICAM_PE BIT(0) +#define UNICAM_PT_MASK GENMASK(2, 1) +#define UNICAM_NP_MASK GENMASK(7, 4) +#define UNICAM_PP_MASK GENMASK(11, 8) +#define UNICAM_BS_MASK GENMASK(15, 12) +#define UNICAM_BL_MASK GENMASK(17, 16) + +/* UNICAM_CLK Register */ +#define UNICAM_CLE BIT(0) +#define UNICAM_CLPD BIT(1) +#define UNICAM_CLLPE BIT(2) +#define UNICAM_CLHSE BIT(3) +#define UNICAM_CLTRE BIT(4) +#define UNICAM_CLAC_MASK GENMASK(8, 5) +#define UNICAM_CLSTE BIT(29) + +/* UNICAM_CLT Register */ +#define UNICAM_CLT1_MASK GENMASK(7, 0) +#define UNICAM_CLT2_MASK GENMASK(15, 8) + +/* UNICAM_DATn Registers */ +#define UNICAM_DLE BIT(0) +#define UNICAM_DLPD BIT(1) +#define UNICAM_DLLPE BIT(2) +#define UNICAM_DLHSE BIT(3) +#define UNICAM_DLTRE BIT(4) +#define UNICAM_DLSM BIT(5) +#define UNICAM_DLFO BIT(28) +#define UNICAM_DLSTE BIT(29) + +#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE | UNICAM_DLFO) + +/* UNICAM_DLT Register */ +#define UNICAM_DLT1_MASK GENMASK(7, 0) +#define UNICAM_DLT2_MASK GENMASK(15, 8) +#define UNICAM_DLT3_MASK GENMASK(23, 16) + +/* UNICAM_ICTL Register */ +#define UNICAM_FSIE BIT(0) +#define UNICAM_FEIE BIT(1) +#define UNICAM_IBOB BIT(2) +#define UNICAM_FCM BIT(3) +#define UNICAM_TFC BIT(4) +#define UNICAM_LIP_MASK GENMASK(6, 5) +#define UNICAM_LCIE_MASK GENMASK(28, 16) + +/* UNICAM_IDI0/1 Register */ +#define UNICAM_ID0_MASK GENMASK(7, 0) +#define UNICAM_ID1_MASK GENMASK(15, 8) +#define UNICAM_ID2_MASK GENMASK(23, 16) +#define UNICAM_ID3_MASK GENMASK(31, 24) + +/* UNICAM_ISTA Register */ +#define UNICAM_FSI BIT(0) +#define UNICAM_FEI BIT(1) +#define UNICAM_LCI BIT(2) + +#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI | UNICAM_FEI | UNICAM_LCI) + +/* UNICAM_IPIPE Register */ +#define UNICAM_PUM_MASK GENMASK(2, 0) +/* Unpacking modes */ +#define UNICAM_PUM_NONE 0 +#define UNICAM_PUM_UNPACK6 1 +#define UNICAM_PUM_UNPACK7 2 +#define UNICAM_PUM_UNPACK8 3 +#define UNICAM_PUM_UNPACK10 4 +#define UNICAM_PUM_UNPACK12 5 +#define UNICAM_PUM_UNPACK14 6 +#define UNICAM_PUM_UNPACK16 7 +#define UNICAM_DDM_MASK GENMASK(6, 3) +#define UNICAM_PPM_MASK GENMASK(9, 7) +/* Packing modes */ +#define UNICAM_PPM_NONE 0 +#define UNICAM_PPM_PACK8 1 +#define UNICAM_PPM_PACK10 2 +#define UNICAM_PPM_PACK12 3 +#define UNICAM_PPM_PACK14 4 +#define UNICAM_PPM_PACK16 5 +#define UNICAM_DEM_MASK GENMASK(11, 10) +#define UNICAM_DEBL_MASK GENMASK(14, 12) +#define UNICAM_ICM_MASK GENMASK(16, 15) +#define UNICAM_IDM_MASK GENMASK(17, 17) + +/* UNICAM_ICC Register */ +#define UNICAM_ICFL_MASK GENMASK(4, 0) +#define UNICAM_ICFH_MASK GENMASK(9, 5) +#define UNICAM_ICST_MASK GENMASK(12, 10) +#define UNICAM_ICLT_MASK GENMASK(15, 13) +#define UNICAM_ICLL_MASK GENMASK(31, 16) + +/* UNICAM_DCS Register */ +#define UNICAM_DIE BIT(0) +#define UNICAM_DIM BIT(1) +#define UNICAM_DBOB BIT(3) +#define UNICAM_FDE BIT(4) +#define UNICAM_LDP BIT(5) +#define UNICAM_EDL_MASK GENMASK(15, 8) + +/* UNICAM_DBCTL Register */ +#define UNICAM_DBEN BIT(0) +#define UNICAM_BUF0_IE BIT(1) +#define UNICAM_BUF1_IE BIT(2) + +/* UNICAM_CMP[0,1] register */ +#define UNICAM_PCE BIT(31) +#define UNICAM_GI BIT(9) +#define UNICAM_CPH BIT(8) +#define UNICAM_PCVC_MASK GENMASK(7, 6) +#define UNICAM_PCDT_MASK GENMASK(5, 0) + +/* UNICAM_MISC register */ +#define UNICAM_FL0 BIT(6) +#define UNICAM_FL1 BIT(9) + +#endif diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c new file mode 100644 index 000000000000..f10064107d54 --- /dev/null +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -0,0 +1,2757 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BCM283x / BCM271x Unicam Capture Driver + * + * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. + * Copyright (C) 2024 - Ideas on Board + * + * Dave Stevenson <dave.stevenson@raspberrypi.com> + * + * Based on TI am437x driver by + * Benoit Parrot <bparrot@ti.com> + * Lad, Prabhakar <prabhakar.csengg@gmail.com> + * + * and TI CAL camera interface driver by + * Benoit Parrot <bparrot@ti.com> + * + * + * There are two camera drivers in the kernel for BCM283x - this one and + * bcm2835-camera (currently in staging). + * + * This driver directly controls the Unicam peripheral - there is no + * involvement with the VideoCore firmware. Unicam receives CSI-2 or CCP2 data + * and writes it into SDRAM. The only potential processing options are to + * repack Bayer data into an alternate format, and applying windowing. The + * repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P to + * V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, but + * not generically up to V4L2_PIX_FMT_Sxxxx16. Support for windowing may be + * added later. + * + * It should be possible to connect this driver to any sensor with a suitable + * output interface and V4L2 subdevice driver. + */ + +#include <linux/clk.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/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "bcm2835-unicam-regs.h" + +#define UNICAM_MODULE_NAME "unicam" + +/* + * Unicam must request a minimum of 250Mhz from the VPU clock. + * Otherwise the input FIFOs overrun and cause image corruption. + */ +#define UNICAM_MIN_VPU_CLOCK_RATE (250 * 1000 * 1000) + +/* Unicam has an internal DMA alignment constraint of 16 bytes for each line. */ +#define UNICAM_DMA_BPL_ALIGNMENT 16 + +/* + * The image stride is stored in a 16 bit register, and needs to be aligned to + * the DMA constraint. As the ISP in the same SoC has a 32 bytes alignment + * constraint on its input, set the image stride alignment to 32 bytes here as + * well to avoid incompatible configurations. + */ +#define UNICAM_IMAGE_BPL_ALIGNMENT 32 +#define UNICAM_IMAGE_MAX_BPL ((1U << 16) - UNICAM_IMAGE_BPL_ALIGNMENT) + +/* + * Max width is therefore determined by the max stride divided by the number of + * bits per pixel. Take 32bpp as a worst case. No imposed limit on the height, + * so adopt a square image for want of anything better. + */ +#define UNICAM_IMAGE_MIN_WIDTH 16 +#define UNICAM_IMAGE_MIN_HEIGHT 16 +#define UNICAM_IMAGE_MAX_WIDTH (UNICAM_IMAGE_MAX_BPL / 4) +#define UNICAM_IMAGE_MAX_HEIGHT UNICAM_IMAGE_MAX_WIDTH + +/* + * There's no intrinsic limits on the width and height for embedded data. Use + * the same maximum values as for the image, to avoid overflows in the image + * size computation. + */ +#define UNICAM_META_MIN_WIDTH 1 +#define UNICAM_META_MIN_HEIGHT 1 +#define UNICAM_META_MAX_WIDTH UNICAM_IMAGE_MAX_WIDTH +#define UNICAM_META_MAX_HEIGHT UNICAM_IMAGE_MAX_HEIGHT + +/* + * Size of the dummy buffer. Can be any size really, but the DMA + * allocation works in units of page sizes. + */ +#define UNICAM_DUMMY_BUF_SIZE PAGE_SIZE + +enum unicam_pad { + UNICAM_SD_PAD_SINK, + UNICAM_SD_PAD_SOURCE_IMAGE, + UNICAM_SD_PAD_SOURCE_METADATA, + UNICAM_SD_NUM_PADS +}; + +enum unicam_node_type { + UNICAM_IMAGE_NODE, + UNICAM_METADATA_NODE, + UNICAM_MAX_NODES +}; + +/* + * struct unicam_format_info - Unicam media bus format information + * @fourcc: V4L2 pixel format FCC identifier. 0 if n/a. + * @unpacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded + * out to 16bpp. 0 if n/a. + * @code: V4L2 media bus format code. + * @depth: Bits per pixel as delivered from the source. + * @csi_dt: CSI data type. + * @unpack: PUM value when unpacking to @unpacked_fourcc + */ +struct unicam_format_info { + u32 fourcc; + u32 unpacked_fourcc; + u32 code; + u8 depth; + u8 csi_dt; + u8 unpack; +}; + +struct unicam_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + dma_addr_t dma_addr; + unsigned int size; +}; + +static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct unicam_buffer, vb.vb2_buf); +} + +struct unicam_node { + bool registered; + unsigned int id; + + /* Pointer to the current v4l2_buffer */ + struct unicam_buffer *cur_frm; + /* Pointer to the next v4l2_buffer */ + struct unicam_buffer *next_frm; + /* Used to store current pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* Identifies video device for this channel */ + struct video_device video_dev; + /* Pointer to the parent handle */ + struct unicam_device *dev; + struct media_pad pad; + /* + * Dummy buffer intended to be used by unicam + * if we have no other queued buffers to swap to. + */ + struct unicam_buffer dummy_buf; + void *dummy_buf_cpu_addr; +}; + +struct unicam_device { + struct kref kref; + + /* peripheral base address */ + void __iomem *base; + /* clock gating base address */ + void __iomem *clk_gate_base; + /* lp clock handle */ + struct clk *clock; + /* vpu clock handle */ + struct clk *vpu_clock; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; + + /* parent device */ + struct device *dev; + /* subdevice async notifier */ + struct v4l2_async_notifier notifier; + unsigned int sequence; + bool frame_started; + + /* Sensor node */ + struct { + struct v4l2_subdev *subdev; + struct media_pad *pad; + } sensor; + + /* Internal subdev */ + struct { + struct v4l2_subdev sd; + struct media_pad pads[UNICAM_SD_NUM_PADS]; + unsigned int enabled_streams; + } subdev; + + enum v4l2_mbus_type bus_type; + /* + * Stores bus.mipi_csi2.flags for CSI2 sensors, or + * bus.mipi_csi1.strobe for CCP2. + */ + unsigned int bus_flags; + unsigned int max_data_lanes; + + struct { + struct media_pipeline pipe; + unsigned int num_data_lanes; + unsigned int nodes; + } pipe; + + /* Lock used for the video devices of both nodes */ + struct mutex lock; + struct unicam_node node[UNICAM_MAX_NODES]; +}; + +static inline struct unicam_device * +notifier_to_unicam_device(struct v4l2_async_notifier *notifier) +{ + return container_of(notifier, struct unicam_device, notifier); +} + +static inline struct unicam_device * +sd_to_unicam_device(struct v4l2_subdev *sd) +{ + return container_of(sd, struct unicam_device, subdev.sd); +} + +static void unicam_release(struct kref *kref) +{ + struct unicam_device *unicam = + container_of(kref, struct unicam_device, kref); + + if (unicam->mdev.dev) + media_device_cleanup(&unicam->mdev); + + mutex_destroy(&unicam->lock); + kfree(unicam); +} + +static struct unicam_device *unicam_get(struct unicam_device *unicam) +{ + kref_get(&unicam->kref); + + return unicam; +} + +static void unicam_put(struct unicam_device *unicam) +{ + kref_put(&unicam->kref, unicam_release); +} + +/* ----------------------------------------------------------------------------- + * Misc helper functions + */ + +static inline bool unicam_sd_pad_is_source(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad != UNICAM_SD_PAD_SINK; +} + +static inline bool is_metadata_node(struct unicam_node *node) +{ + return node->video_dev.device_caps & V4L2_CAP_META_CAPTURE; +} + +static inline bool is_image_node(struct unicam_node *node) +{ + return node->video_dev.device_caps & V4L2_CAP_VIDEO_CAPTURE; +} + +/* ----------------------------------------------------------------------------- + * Format data table and helper functions + */ + +static const struct v4l2_mbus_framefmt unicam_default_image_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + .flags = 0, +}; + +static const struct v4l2_mbus_framefmt unicam_default_meta_format = { + .width = 640, + .height = 2, + .code = MEDIA_BUS_FMT_META_8, + .field = V4L2_FIELD_NONE, +}; + +static const struct unicam_format_info unicam_image_formats[] = { + /* YUV Formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + /* RGB Formats */ + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, { + /* Bayer Formats */ + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR14P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR14, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG14P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG14, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG14P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG14, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB14P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB14, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + /* 16 bit Bayer formats could be supported. */ + + /* Greyscale formats */ + .fourcc = V4L2_PIX_FMT_GREY, + .code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_Y10P, + .unpacked_fourcc = V4L2_PIX_FMT_Y10, + .code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_Y12P, + .unpacked_fourcc = V4L2_PIX_FMT_Y12, + .code = MEDIA_BUS_FMT_Y12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_Y14P, + .unpacked_fourcc = V4L2_PIX_FMT_Y14, + .code = MEDIA_BUS_FMT_Y14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, +}; + +static const struct unicam_format_info unicam_meta_formats[] = { + { + .fourcc = V4L2_META_FMT_GENERIC_8, + .code = MEDIA_BUS_FMT_META_8, + .depth = 8, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_10, + .code = MEDIA_BUS_FMT_META_10, + .depth = 10, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_12, + .code = MEDIA_BUS_FMT_META_12, + .depth = 12, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_14, + .code = MEDIA_BUS_FMT_META_14, + .depth = 14, + }, +}; + +/* Format setup functions */ +static const struct unicam_format_info * +unicam_find_format_by_code(u32 code, u32 pad) +{ + const struct unicam_format_info *formats; + unsigned int num_formats; + unsigned int i; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + for (i = 0; i < num_formats; i++) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +static const struct unicam_format_info * +unicam_find_format_by_fourcc(u32 fourcc, u32 pad) +{ + const struct unicam_format_info *formats; + unsigned int num_formats; + unsigned int i; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + for (i = 0; i < num_formats; ++i) { + if (formats[i].fourcc == fourcc || + formats[i].unpacked_fourcc == fourcc) + return &formats[i]; + } + + return NULL; +} + +static void unicam_calc_image_size_bpl(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo, + struct v4l2_pix_format *pix) +{ + u32 min_bpl; + + v4l_bound_align_image(&pix->width, UNICAM_IMAGE_MIN_WIDTH, + UNICAM_IMAGE_MAX_WIDTH, 2, + &pix->height, UNICAM_IMAGE_MIN_HEIGHT, + UNICAM_IMAGE_MAX_HEIGHT, 0, 0); + + /* Unpacking always goes to 16bpp */ + if (pix->pixelformat == fmtinfo->unpacked_fourcc) + min_bpl = pix->width * 2; + else + min_bpl = pix->width * fmtinfo->depth / 8; + min_bpl = ALIGN(min_bpl, UNICAM_IMAGE_BPL_ALIGNMENT); + + pix->bytesperline = ALIGN(pix->bytesperline, UNICAM_IMAGE_BPL_ALIGNMENT); + pix->bytesperline = clamp_t(unsigned int, pix->bytesperline, min_bpl, + UNICAM_IMAGE_MAX_BPL); + + pix->sizeimage = pix->height * pix->bytesperline; +} + +static void unicam_calc_meta_size_bpl(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo, + struct v4l2_meta_format *meta) +{ + v4l_bound_align_image(&meta->width, UNICAM_META_MIN_WIDTH, + UNICAM_META_MAX_WIDTH, 0, + &meta->height, UNICAM_META_MIN_HEIGHT, + UNICAM_META_MAX_HEIGHT, 0, 0); + + meta->bytesperline = ALIGN(meta->width * fmtinfo->depth / 8, + UNICAM_DMA_BPL_ALIGNMENT); + meta->buffersize = meta->height * meta->bytesperline; +} + +/* ----------------------------------------------------------------------------- + * Hardware handling + */ + +static inline void unicam_clk_write(struct unicam_device *unicam, u32 val) +{ + /* Pass the CM_PASSWORD along with the value. */ + writel(val | 0x5a000000, unicam->clk_gate_base); +} + +static inline u32 unicam_reg_read(struct unicam_device *unicam, u32 offset) +{ + return readl(unicam->base + offset); +} + +static inline void unicam_reg_write(struct unicam_device *unicam, u32 offset, u32 val) +{ + writel(val, unicam->base + offset); +} + +static inline int unicam_get_field(u32 value, u32 mask) +{ + return (value & mask) >> __ffs(mask); +} + +static inline void unicam_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offset, + u32 field, u32 mask) +{ + u32 val = unicam_reg_read(unicam, offset); + + unicam_set_field(&val, field, mask); + unicam_reg_write(unicam, offset, val); +} + +static void unicam_wr_dma_addr(struct unicam_node *node, + struct unicam_buffer *buf) +{ + /* + * Due to a HW bug causing buffer overruns in circular buffer mode under + * certain (not yet fully known) conditions, the dummy buffer allocation + * is set to a a single page size, but the hardware gets programmed with + * a buffer size of 0. + */ + dma_addr_t endaddr = buf->dma_addr + + (buf != &node->dummy_buf ? buf->size : 0); + + if (node->id == UNICAM_IMAGE_NODE) { + unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr); + unicam_reg_write(node->dev, UNICAM_IBEA0, endaddr); + } else { + unicam_reg_write(node->dev, UNICAM_DBSA0, buf->dma_addr); + unicam_reg_write(node->dev, UNICAM_DBEA0, endaddr); + } +} + +static unsigned int unicam_get_lines_done(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + unsigned int stride = node->fmt.fmt.pix.bytesperline; + struct unicam_buffer *frm = node->cur_frm; + dma_addr_t cur_addr; + + if (!frm) + return 0; + + cur_addr = unicam_reg_read(unicam, UNICAM_IBWP); + return (unsigned int)(cur_addr - frm->dma_addr) / stride; +} + +static void unicam_schedule_next_buffer(struct unicam_node *node) +{ + struct unicam_buffer *buf; + + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->next_frm = buf; + list_del(&buf->list); + + unicam_wr_dma_addr(node, buf); +} + +static void unicam_schedule_dummy_buffer(struct unicam_node *node) +{ + int node_id = is_image_node(node) ? UNICAM_IMAGE_NODE : UNICAM_METADATA_NODE; + + dev_dbg(node->dev->dev, "Scheduling dummy buffer for node %d\n", node_id); + + unicam_wr_dma_addr(node, &node->dummy_buf); + + node->next_frm = NULL; +} + +static void unicam_process_buffer_complete(struct unicam_node *node, + unsigned int sequence) +{ + node->cur_frm->vb.field = node->fmt.fmt.pix.field; + node->cur_frm->vb.sequence = sequence; + + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static void unicam_queue_event_sof(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = unicam->sequence, + }; + + v4l2_event_queue(&node->video_dev, &event); +} + +static irqreturn_t unicam_isr(int irq, void *dev) +{ + struct unicam_device *unicam = dev; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; + unsigned int i; + u32 ista, sta; + bool fe; + u64 ts; + + sta = unicam_reg_read(unicam, UNICAM_STA); + /* Write value back to clear the interrupts */ + unicam_reg_write(unicam, UNICAM_STA, sta); + + ista = unicam_reg_read(unicam, UNICAM_ISTA); + /* Write value back to clear the interrupts */ + unicam_reg_write(unicam, UNICAM_ISTA, ista); + + dev_dbg(unicam->dev, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d\n", + ista, sta, sequence, lines_done); + + if (!(sta & (UNICAM_IS | UNICAM_PI0))) + return IRQ_HANDLED; + + /* + * Look for either the Frame End interrupt or the Packet Capture status + * to signal a frame end. + */ + fe = ista & UNICAM_FEI || sta & UNICAM_PI0; + + /* + * We must run the frame end handler first. If we have a valid next_frm + * and we get a simultaneout FE + FS interrupt, running the FS handler + * first would null out the next_frm ptr and we would have lost the + * buffer forever. + */ + if (fe) { + bool inc_seq = unicam->frame_started; + + /* + * Ensure we have swapped buffers already as we can't + * stop the peripheral. If no buffer is available, use a + * dummy buffer to dump out frames until we get a new buffer + * to use. + */ + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + /* + * If cur_frm == next_frm, it means we have not had + * a chance to swap buffers, likely due to having + * multiple interrupts occurring simultaneously (like FE + * + FS + LS). In this case, we cannot signal the buffer + * as complete, as the HW will reuse that buffer. + */ + if (node->cur_frm && node->cur_frm != node->next_frm) { + unicam_process_buffer_complete(node, sequence); + inc_seq = true; + } + node->cur_frm = node->next_frm; + } + + /* + * Increment the sequence number conditionally on either a FS + * having already occurred, or in the FE + FS condition as + * caught in the FE handler above. This ensures the sequence + * number corresponds to the frames generated by the sensor, not + * the frames dequeued to userland. + */ + if (inc_seq) { + unicam->sequence++; + unicam->frame_started = false; + } + } + + if (ista & UNICAM_FSI) { + /* + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ + ts = ktime_get_ns(); + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + if (node->cur_frm) + node->cur_frm->vb.vb2_buf.timestamp = ts; + else + dev_dbg(unicam->v4l2_dev.dev, + "ISR: [%d] Dropping frame, buffer not available at FS\n", + i); + /* + * Set the next frame output to go to a dummy frame + * if we have not managed to obtain another frame + * from the queue. + */ + unicam_schedule_dummy_buffer(node); + } + + unicam_queue_event_sof(unicam); + unicam->frame_started = true; + } + + /* + * Cannot swap buffer at frame end, there may be a race condition + * where the HW does not actually swap it if the new frame has + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) { + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + spin_lock(&node->dma_queue_lock); + if (!list_empty(&node->dma_queue) && !node->next_frm) + unicam_schedule_next_buffer(node); + spin_unlock(&node->dma_queue_lock); + } + } + + return IRQ_HANDLED; +} + +static void unicam_set_packing_config(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + u32 pack, unpack; + u32 val; + + if (node->fmt.fmt.pix.pixelformat == fmtinfo->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { + unpack = fmtinfo->unpack; + /* Repacking is always to 16bpp */ + pack = UNICAM_PPM_PACK16; + } + + val = 0; + unicam_set_field(&val, unpack, UNICAM_PUM_MASK); + unicam_set_field(&val, pack, UNICAM_PPM_MASK); + unicam_reg_write(unicam, UNICAM_IPIPE, val); +} + +static void unicam_cfg_image_id(struct unicam_device *unicam, u8 vc, u8 dt) +{ + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode */ + unicam_reg_write(unicam, UNICAM_IDI0, (vc << 6) | dt); + } else { + /* CCP2 mode */ + unicam_reg_write(unicam, UNICAM_IDI0, 0x80 | dt); + } +} + +static void unicam_enable_ed(struct unicam_device *unicam) +{ + u32 val = unicam_reg_read(unicam, UNICAM_DCS); + + unicam_set_field(&val, 2, UNICAM_EDL_MASK); + /* Do not wrap at the end of the embedded data buffer */ + unicam_set_field(&val, 0, UNICAM_DBOB); + + unicam_reg_write(unicam, UNICAM_DCS, val); +} + +static int unicam_get_image_vc_dt(struct unicam_device *unicam, + struct v4l2_subdev_state *state, + u8 *vc, u8 *dt) +{ + struct v4l2_mbus_frame_desc fd; + u32 stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + UNICAM_SD_PAD_SOURCE_IMAGE, + 0, NULL, &stream); + if (ret) + return ret; + + ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_frame_desc, + unicam->sensor.pad->index, &fd); + if (ret) + return ret; + + /* Only CSI-2 supports DTs. */ + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (unsigned int i = 0; i < fd.num_entries; ++i) { + const struct v4l2_mbus_frame_desc_entry *fde = &fd.entry[i]; + + if (fde->stream == stream) { + *vc = fde->bus.csi2.vc; + *dt = fde->bus.csi2.dt; + return 0; + } + } + + return -EINVAL; +} + +static void unicam_start_rx(struct unicam_device *unicam, + struct v4l2_subdev_state *state) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + const struct unicam_format_info *fmtinfo; + const struct v4l2_mbus_framefmt *fmt; + unsigned int line_int_freq; + u8 vc, dt; + u32 val; + int ret; + + fmt = v4l2_subdev_state_get_format(state, UNICAM_SD_PAD_SOURCE_IMAGE, 0); + fmtinfo = unicam_find_format_by_code(fmt->code, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (WARN_ON(!fmtinfo)) + return; + + /* + * Enable lane clocks. The register is structured as follows: + * + * [9:8] - DAT3 + * [7:6] - DAT2 + * [5:4] - DAT1 + * [3:2] - DAT0 + * [1:0] - CLK + * + * Enabled lane must be set to b01, and disabled lanes to b00. The clock + * lane is always enabled. + */ + val = 0x155 & GENMASK(unicam->pipe.num_data_lanes * 2 + 1, 0); + unicam_clk_write(unicam, val); + + /* Basic init */ + unicam_reg_write(unicam, UNICAM_CTRL, UNICAM_MEM); + + /* Enable analogue control, and leave in reset. */ + val = UNICAM_AR; + unicam_set_field(&val, 7, UNICAM_CTATADJ_MASK); + unicam_set_field(&val, 7, UNICAM_PTATADJ_MASK); + unicam_reg_write(unicam, UNICAM_ANA, val); + usleep_range(1000, 2000); + + /* Come out of reset */ + unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_AR); + + /* Peripheral reset */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR); + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR); + + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Enable Rx control. */ + val = unicam_reg_read(unicam, UNICAM_CTRL); + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + unicam_set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); + unicam_set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); + } else { + unicam_set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); + unicam_set_field(&val, unicam->bus_flags, UNICAM_DCM_MASK); + } + /* Packet framer timeout */ + unicam_set_field(&val, 0xf, UNICAM_PFT_MASK); + unicam_set_field(&val, 128, UNICAM_OET_MASK); + unicam_reg_write(unicam, UNICAM_CTRL, val); + + unicam_reg_write(unicam, UNICAM_IHWIN, 0); + unicam_reg_write(unicam, UNICAM_IVWIN, 0); + + /* AXI bus access QoS setup */ + val = unicam_reg_read(unicam, UNICAM_PRI); + unicam_set_field(&val, 0, UNICAM_BL_MASK); + unicam_set_field(&val, 0, UNICAM_BS_MASK); + unicam_set_field(&val, 0xe, UNICAM_PP_MASK); + unicam_set_field(&val, 8, UNICAM_NP_MASK); + unicam_set_field(&val, 2, UNICAM_PT_MASK); + unicam_set_field(&val, 1, UNICAM_PE); + unicam_reg_write(unicam, UNICAM_PRI, val); + + unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL); + + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB; + line_int_freq = max(fmt->height >> 2, 128); + unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK); + unicam_reg_write(unicam, UNICAM_ICTL, val); + unicam_reg_write(unicam, UNICAM_STA, UNICAM_STA_MASK_ALL); + unicam_reg_write(unicam, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); + + /* tclk_term_en */ + unicam_reg_write_field(unicam, UNICAM_CLT, 2, UNICAM_CLT1_MASK); + /* tclk_settle */ + unicam_reg_write_field(unicam, UNICAM_CLT, 6, UNICAM_CLT2_MASK); + /* td_term_en */ + unicam_reg_write_field(unicam, UNICAM_DLT, 2, UNICAM_DLT1_MASK); + /* ths_settle */ + unicam_reg_write_field(unicam, UNICAM_DLT, 6, UNICAM_DLT2_MASK); + /* trx_enable */ + unicam_reg_write_field(unicam, UNICAM_DLT, 0, UNICAM_DLT3_MASK); + + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_SOE); + + /* Packet compare setup - required to avoid missing frame ends */ + val = 0; + unicam_set_field(&val, 1, UNICAM_PCE); + unicam_set_field(&val, 1, UNICAM_GI); + unicam_set_field(&val, 1, UNICAM_CPH); + unicam_set_field(&val, 0, UNICAM_PCVC_MASK); + unicam_set_field(&val, 1, UNICAM_PCDT_MASK); + unicam_reg_write(unicam, UNICAM_CMP0, val); + + /* Enable clock lane and set up terminations */ + val = 0; + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + unicam_set_field(&val, 1, UNICAM_CLE); + unicam_set_field(&val, 1, UNICAM_CLLPE); + if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { + unicam_set_field(&val, 1, UNICAM_CLTRE); + unicam_set_field(&val, 1, UNICAM_CLHSE); + } + } else { + /* CCP2 */ + unicam_set_field(&val, 1, UNICAM_CLE); + unicam_set_field(&val, 1, UNICAM_CLHSE); + unicam_set_field(&val, 1, UNICAM_CLTRE); + } + unicam_reg_write(unicam, UNICAM_CLK, val); + + /* + * Enable required data lanes with appropriate terminations. + * The same value needs to be written to UNICAM_DATn registers for + * the active lanes, and 0 for inactive ones. + */ + val = 0; + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + unicam_set_field(&val, 1, UNICAM_DLE); + unicam_set_field(&val, 1, UNICAM_DLLPE); + if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { + unicam_set_field(&val, 1, UNICAM_DLTRE); + unicam_set_field(&val, 1, UNICAM_DLHSE); + } + } else { + /* CCP2 */ + unicam_set_field(&val, 1, UNICAM_DLE); + unicam_set_field(&val, 1, UNICAM_DLHSE); + unicam_set_field(&val, 1, UNICAM_DLTRE); + } + unicam_reg_write(unicam, UNICAM_DAT0, val); + + if (unicam->pipe.num_data_lanes == 1) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT1, val); + + if (unicam->max_data_lanes > 2) { + /* + * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the + * instance supports more than 2 data lanes. + */ + if (unicam->pipe.num_data_lanes == 2) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT2, val); + + if (unicam->pipe.num_data_lanes == 3) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT3, val); + } + + unicam_reg_write(unicam, UNICAM_IBLS, + node->fmt.fmt.pix.bytesperline); + unicam_wr_dma_addr(node, node->cur_frm); + unicam_set_packing_config(unicam, fmtinfo); + + ret = unicam_get_image_vc_dt(unicam, state, &vc, &dt); + if (ret) { + /* + * If the source doesn't support frame descriptors, default to + * VC 0 and use the DT corresponding to the format. + */ + vc = 0; + dt = fmtinfo->csi_dt; + } + + unicam_cfg_image_id(unicam, vc, dt); + + val = unicam_reg_read(unicam, UNICAM_MISC); + unicam_set_field(&val, 1, UNICAM_FL0); + unicam_set_field(&val, 1, UNICAM_FL1); + unicam_reg_write(unicam, UNICAM_MISC, val); + + /* Enable peripheral */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPE); + + /* Load image pointers */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + + /* + * Enable trigger only for the first frame to + * sync correctly to the FS from the source. + */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); +} + +static void unicam_start_metadata(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_METADATA_NODE]; + + unicam_enable_ed(unicam); + unicam_wr_dma_addr(node, node->cur_frm); + unicam_reg_write_field(unicam, UNICAM_DCS, 1, UNICAM_LDP); +} + +static void unicam_disable(struct unicam_device *unicam) +{ + /* Analogue lane control disable */ + unicam_reg_write_field(unicam, UNICAM_ANA, 1, UNICAM_DDL); + + /* Stop the output engine */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_SOE); + + /* Disable the data lanes. */ + unicam_reg_write(unicam, UNICAM_DAT0, 0); + unicam_reg_write(unicam, UNICAM_DAT1, 0); + + if (unicam->max_data_lanes > 2) { + unicam_reg_write(unicam, UNICAM_DAT2, 0); + unicam_reg_write(unicam, UNICAM_DAT3, 0); + } + + /* Peripheral reset */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR); + usleep_range(50, 100); + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR); + + /* Disable peripheral */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Clear ED setup */ + unicam_reg_write(unicam, UNICAM_DCS, 0); + + /* Disable all lane clocks */ + unicam_clk_write(unicam, 0); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static int __unicam_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_route *route; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + for_each_active_route(&state->routing, route) { + const struct v4l2_mbus_framefmt *def_fmt; + struct v4l2_mbus_framefmt *fmt; + + if (route->source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) + def_fmt = &unicam_default_image_format; + else + def_fmt = &unicam_default_meta_format; + + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); + *fmt = *def_fmt; + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + *fmt = *def_fmt; + } + + return 0; +} + +static int unicam_subdev_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UNICAM_SD_PAD_SINK, + .sink_stream = 0, + .source_pad = UNICAM_SD_PAD_SOURCE_IMAGE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + /* Initialize routing to single route to the fist source pad. */ + return __unicam_subdev_set_routing(sd, state, &routing); +} + +static int unicam_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + u32 pad, stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + code->pad, code->stream, + &pad, &stream); + if (ret) + return ret; + + if (unicam_sd_pad_is_source(code->pad)) { + /* No transcoding, source and sink codes must match. */ + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (!fmt) + return -EINVAL; + + if (code->index > 0) + return -EINVAL; + + code->code = fmt->code; + } else { + const struct unicam_format_info *formats; + unsigned int num_formats; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + if (code->index >= num_formats) + return -EINVAL; + + code->code = formats[code->index].code; + } + + return 0; +} + +static int unicam_subdev_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + u32 pad, stream; + int ret; + + if (fse->index > 0) + return -EINVAL; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, fse->pad, + fse->stream, &pad, + &stream); + if (ret) + return ret; + + if (unicam_sd_pad_is_source(fse->pad)) { + /* No transcoding, source and sink formats must match. */ + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (!fmt) + return -EINVAL; + + if (fse->code != fmt->code) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + } else { + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_code(fse->code, pad); + if (!fmtinfo) + return -EINVAL; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + fse->min_width = UNICAM_IMAGE_MIN_WIDTH; + fse->max_width = UNICAM_IMAGE_MAX_WIDTH; + fse->min_height = UNICAM_IMAGE_MIN_HEIGHT; + fse->max_height = UNICAM_IMAGE_MAX_HEIGHT; + } else { + fse->min_width = UNICAM_META_MIN_WIDTH; + fse->max_width = UNICAM_META_MAX_WIDTH; + fse->min_height = UNICAM_META_MIN_HEIGHT; + fse->max_height = UNICAM_META_MAX_HEIGHT; + } + } + + return 0; +} + +static int unicam_subdev_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + struct v4l2_mbus_framefmt *sink_format, *source_format; + const struct unicam_format_info *fmtinfo; + u32 source_pad, source_stream; + int ret; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + unicam->subdev.enabled_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (unicam_sd_pad_is_source(format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + /* + * Allowed formats for the stream on the sink pad depend on what source + * pad the stream is routed to. Find the corresponding source pad and + * use it to validate the media bus code. + */ + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + format->pad, format->stream, + &source_pad, &source_stream); + if (ret) + return ret; + + fmtinfo = unicam_find_format_by_code(format->format.code, source_pad); + if (!fmtinfo) { + fmtinfo = source_pad == UNICAM_SD_PAD_SOURCE_IMAGE + ? &unicam_image_formats[0] : &unicam_meta_formats[0]; + format->format.code = fmtinfo->code; + } + + if (source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + format->format.width = clamp_t(unsigned int, + format->format.width, + UNICAM_IMAGE_MIN_WIDTH, + UNICAM_IMAGE_MAX_WIDTH); + format->format.height = clamp_t(unsigned int, + format->format.height, + UNICAM_IMAGE_MIN_HEIGHT, + UNICAM_IMAGE_MAX_HEIGHT); + format->format.field = V4L2_FIELD_NONE; + } else { + format->format.width = clamp_t(unsigned int, + format->format.width, + UNICAM_META_MIN_WIDTH, + UNICAM_META_MAX_WIDTH); + format->format.height = clamp_t(unsigned int, + format->format.height, + UNICAM_META_MIN_HEIGHT, + UNICAM_META_MAX_HEIGHT); + format->format.field = V4L2_FIELD_NONE; + + /* Colorspace don't apply to metadata. */ + format->format.colorspace = 0; + format->format.ycbcr_enc = 0; + format->format.quantization = 0; + format->format.xfer_func = 0; + } + + sink_format = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + source_format = v4l2_subdev_state_get_format(state, source_pad, + source_stream); + *sink_format = format->format; + *source_format = format->format; + + return 0; +} + +static int unicam_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && unicam->subdev.enabled_streams) + return -EBUSY; + + return __unicam_subdev_set_routing(sd, state, routing); +} + +static int unicam_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + u32 other_pad, other_stream; + int ret; + + if (!unicam->subdev.enabled_streams) { + /* Configure and start Unicam. */ + unicam->sequence = 0; + + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) + unicam_start_metadata(unicam); + + unicam->frame_started = false; + unicam_start_rx(unicam, state); + } + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + &other_pad, &other_stream); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(unicam->sensor.subdev, + unicam->sensor.pad->index, + BIT(other_stream)); + if (ret) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + return ret; + } + + unicam->subdev.enabled_streams |= BIT(other_stream); + + return 0; +} + +static int unicam_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + u32 other_pad, other_stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + &other_pad, &other_stream); + if (ret) + return ret; + + v4l2_subdev_disable_streams(unicam->sensor.subdev, + unicam->sensor.pad->index, + BIT(other_stream)); + + unicam->subdev.enabled_streams &= ~BIT(other_stream); + + if (!unicam->subdev.enabled_streams) + unicam_disable(unicam); + + return 0; +} + +static const struct v4l2_subdev_pad_ops unicam_subdev_pad_ops = { + .enum_mbus_code = unicam_subdev_enum_mbus_code, + .enum_frame_size = unicam_subdev_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = unicam_subdev_set_format, + .set_routing = unicam_subdev_set_routing, + .enable_streams = unicam_sd_enable_streams, + .disable_streams = unicam_sd_disable_streams, +}; + +static const struct v4l2_subdev_ops unicam_subdev_ops = { + .pad = &unicam_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops unicam_subdev_internal_ops = { + .init_state = unicam_subdev_init_state, +}; + +static const struct media_entity_operations unicam_subdev_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static int unicam_subdev_init(struct unicam_device *unicam) +{ + struct v4l2_subdev *sd = &unicam->subdev.sd; + int ret; + + v4l2_subdev_init(sd, &unicam_subdev_ops); + sd->internal_ops = &unicam_subdev_internal_ops; + v4l2_set_subdevdata(sd, unicam); + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &unicam_subdev_media_ops; + sd->dev = unicam->dev; + sd->owner = THIS_MODULE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + + strscpy(sd->name, "unicam", sizeof(sd->name)); + + unicam->subdev.pads[UNICAM_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_IMAGE].flags = MEDIA_PAD_FL_SOURCE; + unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_METADATA].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(unicam->subdev.pads), + unicam->subdev.pads); + if (ret) { + dev_err(unicam->dev, "Failed to initialize media entity: %d\n", + ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(unicam->dev, "Failed to initialize subdev: %d\n", ret); + goto err_entity; + } + + ret = v4l2_device_register_subdev(&unicam->v4l2_dev, sd); + if (ret) { + dev_err(unicam->dev, "Failed to register subdev: %d\n", ret); + goto err_subdev; + } + + return 0; + +err_subdev: + v4l2_subdev_cleanup(sd); +err_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +static void unicam_subdev_cleanup(struct unicam_device *unicam) +{ + v4l2_subdev_cleanup(&unicam->subdev.sd); + media_entity_cleanup(&unicam->subdev.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Videobuf2 queue operations + */ + +static int unicam_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage + : node->fmt.fmt.meta.buffersize; + + if (*nplanes) { + if (sizes[0] < size) { + dev_dbg(node->dev->dev, "sizes[0] %i < size %u\n", + sizes[0], size); + return -EINVAL; + } + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int unicam_buffer_prepare(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage + : node->fmt.fmt.meta.buffersize; + + if (vb2_plane_size(vb, 0) < size) { + dev_dbg(node->dev->dev, + "data will not fit into plane (%lu < %u)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + buf->dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = size; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + + return 0; +} + +static void unicam_return_buffers(struct unicam_node *node, + enum vb2_buffer_state state) +{ + struct unicam_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (node->cur_frm) + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, + state); + if (node->next_frm && node->cur_frm != node->next_frm) + vb2_buffer_done(&node->next_frm->vb.vb2_buf, + state); + + node->cur_frm = NULL; + node->next_frm = NULL; +} + +static int unicam_num_data_lanes(struct unicam_device *unicam) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + unsigned int num_data_lanes; + int ret; + + if (unicam->bus_type != V4L2_MBUS_CSI2_DPHY) + return unicam->max_data_lanes; + + ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_mbus_config, + unicam->sensor.pad->index, &mbus_config); + if (ret == -ENOIOCTLCMD) + return unicam->max_data_lanes; + + if (ret < 0) { + dev_err(unicam->dev, "Failed to get mbus config: %d\n", ret); + return ret; + } + + num_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; + + if (num_data_lanes != 1 && num_data_lanes != 2 && num_data_lanes != 4) { + dev_err(unicam->dev, + "Device %s has requested %u data lanes, invalid\n", + unicam->sensor.subdev->name, num_data_lanes); + return -EINVAL; + } + + if (num_data_lanes > unicam->max_data_lanes) { + dev_err(unicam->dev, + "Device %s has requested %u data lanes, >%u configured in DT\n", + unicam->sensor.subdev->name, num_data_lanes, + unicam->max_data_lanes); + return -EINVAL; + } + + return num_data_lanes; +} + +static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *unicam = node->dev; + struct unicam_buffer *buf; + struct media_pipeline_pad_iter iter; + struct media_pad *pad; + unsigned long flags; + int ret; + + dev_dbg(unicam->dev, "Starting stream on %s device\n", + is_metadata_node(node) ? "metadata" : "image"); + + /* + * Start the pipeline. This validates all links, and populates the + * pipeline structure. + */ + ret = video_device_pipeline_start(&node->video_dev, &unicam->pipe.pipe); + if (ret < 0) { + dev_dbg(unicam->dev, "Failed to start media pipeline: %d\n", ret); + goto err_buffers; + } + + /* + * Determine which video nodes are included in the pipeline, and get the + * number of data lanes. + */ + if (unicam->pipe.pipe.start_count == 1) { + unicam->pipe.nodes = 0; + + media_pipeline_for_each_pad(&unicam->pipe.pipe, &iter, pad) { + if (pad->entity != &unicam->subdev.sd.entity) + continue; + + if (pad->index == UNICAM_SD_PAD_SOURCE_IMAGE) + unicam->pipe.nodes |= BIT(UNICAM_IMAGE_NODE); + else if (pad->index == UNICAM_SD_PAD_SOURCE_METADATA) + unicam->pipe.nodes |= BIT(UNICAM_METADATA_NODE); + } + + if (!(unicam->pipe.nodes & BIT(UNICAM_IMAGE_NODE))) { + dev_dbg(unicam->dev, + "Pipeline does not include image node\n"); + ret = -EPIPE; + goto err_pipeline; + } + + ret = unicam_num_data_lanes(unicam); + if (ret < 0) + goto err_pipeline; + + unicam->pipe.num_data_lanes = ret; + + dev_dbg(unicam->dev, "Running with %u data lanes, nodes %u\n", + unicam->pipe.num_data_lanes, unicam->pipe.nodes); + } + + /* Arm the node with the first buffer from the DMA queue. */ + spin_lock_irqsave(&node->dma_queue_lock, flags); + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->cur_frm = buf; + node->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&node->dma_queue_lock, flags); + + /* + * Wait for all the video devices in the pipeline to have been started + * before starting the hardware. In the general case, this would + * prevent capturing multiple streams independently. However, the + * Unicam DMA engines are not generic, they have been designed to + * capture image data and embedded data from the same camera sensor. + * Not only does the main use case not benefit from independent + * capture, it requires proper synchronization of the streams at start + * time. + */ + if (unicam->pipe.pipe.start_count < hweight32(unicam->pipe.nodes)) + return 0; + + ret = pm_runtime_resume_and_get(unicam->dev); + if (ret < 0) { + dev_err(unicam->dev, "PM runtime resume failed: %d\n", ret); + goto err_pipeline; + } + + /* Enable the streams on the source. */ + ret = v4l2_subdev_enable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, + BIT(0)); + if (ret < 0) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + goto err_pm_put; + } + + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) { + ret = v4l2_subdev_enable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_METADATA, + BIT(0)); + if (ret < 0) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + goto err_disable_streams; + } + } + + return 0; + +err_disable_streams: + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, BIT(0)); +err_pm_put: + pm_runtime_put_sync(unicam->dev); +err_pipeline: + video_device_pipeline_stop(&node->video_dev); +err_buffers: + unicam_return_buffers(node, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void unicam_stop_streaming(struct vb2_queue *vq) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *unicam = node->dev; + + /* Stop the hardware when the first video device gets stopped. */ + if (unicam->pipe.pipe.start_count == hweight32(unicam->pipe.nodes)) { + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_METADATA, + BIT(0)); + + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, + BIT(0)); + + pm_runtime_put(unicam->dev); + } + + video_device_pipeline_stop(&node->video_dev); + + /* Clear all queued buffers for the node */ + unicam_return_buffers(node, VB2_BUF_STATE_ERROR); +} + +static void unicam_buffer_queue(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + + spin_lock_irq(&node->dma_queue_lock); + list_add_tail(&buf->list, &node->dma_queue); + spin_unlock_irq(&node->dma_queue_lock); +} + +static const struct vb2_ops unicam_video_qops = { + .queue_setup = unicam_queue_setup, + .buf_prepare = unicam_buffer_prepare, + .start_streaming = unicam_start_streaming, + .stop_streaming = unicam_stop_streaming, + .buf_queue = unicam_buffer_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 video device operations + */ + +static int unicam_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); + + cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; + + return 0; +} + +static int unicam_enum_fmt_vid(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int index; + unsigned int i; + + for (i = 0, index = 0; i < ARRAY_SIZE(unicam_image_formats); i++) { + if (f->mbus_code && unicam_image_formats[i].code != f->mbus_code) + continue; + + if (index == f->index) { + f->pixelformat = unicam_image_formats[i].fourcc; + return 0; + } + + index++; + + if (!unicam_image_formats[i].unpacked_fourcc) + continue; + + if (index == f->index) { + f->pixelformat = unicam_image_formats[i].unpacked_fourcc; + return 0; + } + + index++; + } + + return -EINVAL; +} + +static int unicam_g_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + *f = node->fmt; + + return 0; +} + +static void __unicam_try_fmt_vid(struct unicam_node *node, + struct v4l2_pix_format *pix) +{ + const struct unicam_format_info *fmtinfo; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = unicam_find_format_by_fourcc(pix->pixelformat, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (!fmtinfo) { + fmtinfo = &unicam_image_formats[0]; + pix->pixelformat = fmtinfo->fourcc; + } + + unicam_calc_image_size_bpl(node->dev, fmtinfo, pix); + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; +} + +static int unicam_try_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + __unicam_try_fmt_vid(node, &f->fmt.pix); + return 0; +} + +static int unicam_s_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + __unicam_try_fmt_vid(node, &f->fmt.pix); + node->fmt = *f; + + return 0; +} + +static int unicam_enum_fmt_meta(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int i, index; + + for (i = 0, index = 0; i < ARRAY_SIZE(unicam_meta_formats); i++) { + if (f->mbus_code && unicam_meta_formats[i].code != f->mbus_code) + continue; + + if (index == f->index) { + f->pixelformat = unicam_meta_formats[i].fourcc; + f->type = V4L2_BUF_TYPE_META_CAPTURE; + f->flags = V4L2_FMT_FLAG_META_LINE_BASED; + return 0; + } + + index++; + } + + return -EINVAL; +} + +static int unicam_g_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + f->fmt.meta = node->fmt.fmt.meta; + + return 0; +} + +static const struct unicam_format_info * +__unicam_try_fmt_meta(struct unicam_node *node, struct v4l2_meta_format *meta) +{ + const struct unicam_format_info *fmtinfo; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = unicam_find_format_by_fourcc(meta->dataformat, + UNICAM_SD_PAD_SOURCE_METADATA); + if (!fmtinfo) { + fmtinfo = &unicam_meta_formats[0]; + meta->dataformat = fmtinfo->fourcc; + } + + unicam_calc_meta_size_bpl(node->dev, fmtinfo, meta); + + return fmtinfo; +} + +static int unicam_try_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + __unicam_try_fmt_meta(node, &f->fmt.meta); + return 0; +} + +static int unicam_s_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + __unicam_try_fmt_meta(node, &f->fmt.meta); + node->fmt = *f; + + return 0; +} + +static int unicam_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct unicam_node *node = video_drvdata(file); + int ret = -EINVAL; + + if (fsize->index > 0) + return ret; + + if (is_image_node(node)) { + if (!unicam_find_format_by_fourcc(fsize->pixel_format, + UNICAM_SD_PAD_SOURCE_IMAGE)) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = UNICAM_IMAGE_MIN_WIDTH; + fsize->stepwise.max_width = UNICAM_IMAGE_MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = UNICAM_IMAGE_MIN_HEIGHT; + fsize->stepwise.max_height = UNICAM_IMAGE_MAX_HEIGHT; + fsize->stepwise.step_height = 1; + } else { + if (!unicam_find_format_by_fourcc(fsize->pixel_format, + UNICAM_SD_PAD_SOURCE_METADATA)) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = UNICAM_META_MIN_WIDTH; + fsize->stepwise.max_width = UNICAM_META_MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = UNICAM_META_MIN_HEIGHT; + fsize->stepwise.max_height = UNICAM_META_MAX_HEIGHT; + fsize->stepwise.step_height = 1; + } + + return 0; +} + +static int unicam_log_status(struct file *file, void *fh) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *unicam = node->dev; + u32 reg; + + /* status for sub devices */ + v4l2_device_call_all(&unicam->v4l2_dev, 0, core, log_status); + + dev_info(unicam->dev, "-----Receiver status-----\n"); + dev_info(unicam->dev, "V4L2 width/height: %ux%u\n", + node->fmt.fmt.pix.width, node->fmt.fmt.pix.height); + dev_info(unicam->dev, "V4L2 format: %08x\n", + node->fmt.fmt.pix.pixelformat); + reg = unicam_reg_read(unicam, UNICAM_IPIPE); + dev_info(unicam->dev, "Unpacking/packing: %u / %u\n", + unicam_get_field(reg, UNICAM_PUM_MASK), + unicam_get_field(reg, UNICAM_PPM_MASK)); + dev_info(unicam->dev, "----Live data----\n"); + dev_info(unicam->dev, "Programmed stride: %4u\n", + unicam_reg_read(unicam, UNICAM_IBLS)); + dev_info(unicam->dev, "Detected resolution: %ux%u\n", + unicam_reg_read(unicam, UNICAM_IHSTA), + unicam_reg_read(unicam, UNICAM_IVSTA)); + dev_info(unicam->dev, "Write pointer: %08x\n", + unicam_reg_read(unicam, UNICAM_IBWP)); + + return 0; +} + +static int unicam_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 2, NULL); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops unicam_ioctl_ops = { + .vidioc_querycap = unicam_querycap, + + .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid, + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid, + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid, + + .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta, + .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta, + .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta, + .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta, + + .vidioc_enum_framesizes = unicam_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, + + .vidioc_log_status = unicam_log_status, + .vidioc_subscribe_event = unicam_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* unicam capture driver file operations */ +static const struct v4l2_file_operations unicam_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int unicam_video_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct unicam_node *node = video_get_drvdata(vdev); + const u32 pad = is_image_node(node) ? UNICAM_SD_PAD_SOURCE_IMAGE + : UNICAM_SD_PAD_SOURCE_METADATA; + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + ret = -EINVAL; + goto out; + } + + if (is_image_node(node)) { + const struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_fourcc(fmt->pixelformat, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (WARN_ON(!fmtinfo)) { + ret = -EPIPE; + goto out; + } + + if (fmtinfo->code != format->code || + fmt->height != format->height || + fmt->width != format->width || + fmt->field != format->field) { + dev_dbg(node->dev->dev, + "image: (%u x %u) 0x%08x %s != (%u x %u) 0x%08x %s\n", + fmt->width, fmt->height, fmtinfo->code, + v4l2_field_names[fmt->field], + format->width, format->height, format->code, + v4l2_field_names[format->field]); + ret = -EPIPE; + } + } else { + const struct v4l2_meta_format *fmt = &node->fmt.fmt.meta; + + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_fourcc(fmt->dataformat, + UNICAM_SD_PAD_SOURCE_METADATA); + if (WARN_ON(!fmtinfo)) { + ret = -EPIPE; + goto out; + } + + if (fmtinfo->code != format->code || + fmt->height != format->height || + fmt->width != format->width) { + dev_dbg(node->dev->dev, + "meta: (%u x %u) 0x%04x != (%u x %u) 0x%04x\n", + fmt->width, fmt->height, fmtinfo->code, + format->width, format->height, format->code); + ret = -EPIPE; + } + } + +out: + v4l2_subdev_unlock_state(state); + return ret; +} + +static const struct media_entity_operations unicam_video_media_ops = { + .link_validate = unicam_video_link_validate, +}; + +static void unicam_node_release(struct video_device *vdev) +{ + struct unicam_node *node = video_get_drvdata(vdev); + + unicam_put(node->dev); +} + +static void unicam_set_default_format(struct unicam_node *node) +{ + if (is_image_node(node)) { + struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; + const struct unicam_format_info *fmtinfo = + &unicam_image_formats[0]; + + node->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + v4l2_fill_pix_format(fmt, &unicam_default_image_format); + fmt->pixelformat = fmtinfo->fourcc; + unicam_calc_image_size_bpl(node->dev, fmtinfo, fmt); + } else { + struct v4l2_meta_format *fmt = &node->fmt.fmt.meta; + const struct unicam_format_info *fmtinfo = + &unicam_meta_formats[0]; + + node->fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + + fmt->dataformat = fmtinfo->fourcc; + fmt->width = unicam_default_meta_format.width; + fmt->height = unicam_default_meta_format.height; + unicam_calc_meta_size_bpl(node->dev, fmtinfo, fmt); + } +} + +static int unicam_register_node(struct unicam_device *unicam, + enum unicam_node_type type) +{ + const u32 pad_index = type == UNICAM_IMAGE_NODE + ? UNICAM_SD_PAD_SOURCE_IMAGE + : UNICAM_SD_PAD_SOURCE_METADATA; + struct unicam_node *node = &unicam->node[type]; + struct video_device *vdev = &node->video_dev; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + node->dev = unicam_get(unicam); + node->id = type; + + spin_lock_init(&node->dma_queue_lock); + + INIT_LIST_HEAD(&node->dma_queue); + + /* Initialize the videobuf2 queue. */ + q->type = type == UNICAM_IMAGE_NODE ? V4L2_BUF_TYPE_VIDEO_CAPTURE + : V4L2_BUF_TYPE_META_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = node; + q->ops = &unicam_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct unicam_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &unicam->lock; + q->min_queued_buffers = 1; + q->dev = unicam->dev; + + ret = vb2_queue_init(q); + if (ret) { + dev_err(unicam->dev, "vb2_queue_init() failed\n"); + goto err_unicam_put; + } + + /* Initialize the video device. */ + vdev->release = unicam_node_release; + vdev->fops = &unicam_fops; + vdev->ioctl_ops = &unicam_ioctl_ops; + vdev->v4l2_dev = &unicam->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &unicam->lock; + vdev->device_caps = type == UNICAM_IMAGE_NODE + ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &unicam_video_media_ops; + + snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, + type == UNICAM_IMAGE_NODE ? "image" : "embedded"); + + video_set_drvdata(vdev, node); + + if (type == UNICAM_IMAGE_NODE) + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + + node->pad.flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret) + goto err_unicam_put; + + node->dummy_buf.size = UNICAM_DUMMY_BUF_SIZE; + node->dummy_buf_cpu_addr = dma_alloc_coherent(unicam->dev, + node->dummy_buf.size, + &node->dummy_buf.dma_addr, + GFP_KERNEL); + if (!node->dummy_buf_cpu_addr) { + dev_err(unicam->dev, "Unable to allocate dummy buffer.\n"); + ret = -ENOMEM; + goto err_entity_cleanup; + } + + unicam_set_default_format(node); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(unicam->dev, "Unable to register video device %s\n", + vdev->name); + goto err_dma_free; + } + + node->registered = true; + + ret = media_create_pad_link(&unicam->subdev.sd.entity, + pad_index, + &node->video_dev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + /* + * No need for cleanup, the caller will unregister the + * video device, which will drop the reference on the + * device and trigger the cleanup. + */ + dev_err(unicam->dev, "Unable to create pad link for %s\n", + unicam->sensor.subdev->name); + return ret; + } + + return 0; + +err_dma_free: + dma_free_coherent(unicam->dev, node->dummy_buf.size, + node->dummy_buf_cpu_addr, + node->dummy_buf.dma_addr); +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_unicam_put: + unicam_put(unicam); + return ret; +} + +static void unicam_unregister_nodes(struct unicam_device *unicam) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (node->registered) { + vb2_video_unregister_device(&node->video_dev); + node->registered = false; + } + + if (node->dummy_buf_cpu_addr) + dma_free_coherent(unicam->dev, node->dummy_buf.size, + node->dummy_buf_cpu_addr, + node->dummy_buf.dma_addr); + } +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int unicam_runtime_resume(struct device *dev) +{ + struct unicam_device *unicam = dev_get_drvdata(dev); + int ret; + + ret = clk_set_min_rate(unicam->vpu_clock, UNICAM_MIN_VPU_CLOCK_RATE); + if (ret) { + dev_err(unicam->dev, "failed to set up VPU clock\n"); + return ret; + } + + ret = clk_prepare_enable(unicam->vpu_clock); + if (ret) { + dev_err(unicam->dev, "Failed to enable VPU clock: %d\n", ret); + goto err_vpu_clock; + } + + ret = clk_set_rate(unicam->clock, 100 * 1000 * 1000); + if (ret) { + dev_err(unicam->dev, "failed to set up CSI clock\n"); + goto err_vpu_prepare; + } + + ret = clk_prepare_enable(unicam->clock); + if (ret) { + dev_err(unicam->dev, "Failed to enable CSI clock: %d\n", ret); + goto err_vpu_prepare; + } + + return 0; + +err_vpu_prepare: + clk_disable_unprepare(unicam->vpu_clock); +err_vpu_clock: + if (clk_set_min_rate(unicam->vpu_clock, 0)) + dev_err(unicam->dev, "failed to reset the VPU clock\n"); + + return ret; +} + +static int unicam_runtime_suspend(struct device *dev) +{ + struct unicam_device *unicam = dev_get_drvdata(dev); + + clk_disable_unprepare(unicam->clock); + + if (clk_set_min_rate(unicam->vpu_clock, 0)) + dev_err(unicam->dev, "failed to reset the VPU clock\n"); + + clk_disable_unprepare(unicam->vpu_clock); + + return 0; +} + +static const struct dev_pm_ops unicam_pm_ops = { + RUNTIME_PM_OPS(unicam_runtime_suspend, unicam_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * V4L2 async notifier + */ + +static int unicam_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct unicam_device *unicam = notifier_to_unicam_device(notifier); + struct media_pad *sink = &unicam->subdev.pads[UNICAM_SD_PAD_SINK]; + struct media_pad *source; + int ret; + + dev_dbg(unicam->dev, "Using sensor %s for capture\n", + subdev->name); + + ret = v4l2_create_fwnode_links_to_pad(subdev, sink, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + return ret; + + source = media_pad_remote_pad_unique(sink); + if (IS_ERR(source)) { + dev_err(unicam->dev, "No connected sensor pad\n"); + return PTR_ERR(source); + } + + unicam->sensor.subdev = subdev; + unicam->sensor.pad = source; + + return 0; +} + +static int unicam_async_complete(struct v4l2_async_notifier *notifier) +{ + struct unicam_device *unicam = notifier_to_unicam_device(notifier); + int ret; + + ret = unicam_register_node(unicam, UNICAM_IMAGE_NODE); + if (ret) { + dev_err(unicam->dev, "Unable to register image video device.\n"); + goto unregister; + } + + ret = unicam_register_node(unicam, UNICAM_METADATA_NODE); + if (ret) { + dev_err(unicam->dev, "Unable to register metadata video device.\n"); + goto unregister; + } + + ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev); + if (ret) { + dev_err(unicam->dev, "Unable to register subdev nodes.\n"); + goto unregister; + } + + return 0; + +unregister: + unicam_unregister_nodes(unicam); + unicam_put(unicam); + + return ret; +} + +static const struct v4l2_async_notifier_operations unicam_async_ops = { + .bound = unicam_async_bound, + .complete = unicam_async_complete, +}; + +static int unicam_async_nf_init(struct unicam_device *unicam) +{ + struct v4l2_fwnode_endpoint ep = { }; + struct fwnode_handle *ep_handle; + struct v4l2_async_connection *asc; + int ret; + + ret = of_property_read_u32(unicam->dev->of_node, "brcm,num-data-lanes", + &unicam->max_data_lanes); + if (ret < 0) { + dev_err(unicam->dev, "Missing %s DT property\n", + "brcm,num-data-lanes"); + return -EINVAL; + } + + /* Get and parse the local endpoint. */ + ep_handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(unicam->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep_handle) { + dev_err(unicam->dev, "No endpoint found\n"); + return -ENODEV; + } + + ret = v4l2_fwnode_endpoint_parse(ep_handle, &ep); + if (ret) { + dev_err(unicam->dev, "Failed to parse endpoint: %d\n", ret); + goto error; + } + + unicam->bus_type = ep.bus_type; + + switch (ep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: { + unsigned int num_data_lanes = ep.bus.mipi_csi2.num_data_lanes; + + if (num_data_lanes != 1 && num_data_lanes != 2 && + num_data_lanes != 4) { + dev_err(unicam->dev, "%u data lanes not supported\n", + num_data_lanes); + ret = -EINVAL; + goto error; + } + + if (num_data_lanes > unicam->max_data_lanes) { + dev_err(unicam->dev, + "Endpoint uses %u data lanes when %u are supported\n", + num_data_lanes, unicam->max_data_lanes); + ret = -EINVAL; + goto error; + } + + unicam->max_data_lanes = num_data_lanes; + unicam->bus_flags = ep.bus.mipi_csi2.flags; + break; + } + + case V4L2_MBUS_CCP2: + unicam->max_data_lanes = 1; + unicam->bus_flags = ep.bus.mipi_csi1.strobe; + break; + + default: + /* Unsupported bus type */ + dev_err(unicam->dev, "Unsupported bus type %u\n", ep.bus_type); + ret = -EINVAL; + goto error; + } + + /* Initialize and register the async notifier. */ + v4l2_async_nf_init(&unicam->notifier, &unicam->v4l2_dev); + + asc = v4l2_async_nf_add_fwnode_remote(&unicam->notifier, ep_handle, + struct v4l2_async_connection); + fwnode_handle_put(ep_handle); + ep_handle = NULL; + + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + dev_err(unicam->dev, "Failed to add entry to notifier: %d\n", + ret); + goto error; + } + + unicam->notifier.ops = &unicam_async_ops; + + ret = v4l2_async_nf_register(&unicam->notifier); + if (ret) { + dev_err(unicam->dev, "Error registering device notifier: %d\n", + ret); + goto error; + } + + return 0; + +error: + fwnode_handle_put(ep_handle); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Probe & remove + */ + +static int unicam_media_init(struct unicam_device *unicam) +{ + int ret; + + unicam->mdev.dev = unicam->dev; + strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, + sizeof(unicam->mdev.model)); + unicam->mdev.hw_revision = 0; + + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; + + ret = v4l2_device_register(unicam->dev, &unicam->v4l2_dev); + if (ret < 0) { + dev_err(unicam->dev, "Unable to register v4l2 device\n"); + goto err_media_cleanup; + } + + ret = media_device_register(&unicam->mdev); + if (ret < 0) { + dev_err(unicam->dev, + "Unable to register media-controller device\n"); + goto err_v4l2_unregister; + } + + return 0; + +err_v4l2_unregister: + v4l2_device_unregister(&unicam->v4l2_dev); +err_media_cleanup: + media_device_cleanup(&unicam->mdev); + return ret; +} + +static int unicam_probe(struct platform_device *pdev) +{ + struct unicam_device *unicam; + int ret; + + unicam = kzalloc(sizeof(*unicam), GFP_KERNEL); + if (!unicam) + return -ENOMEM; + + kref_init(&unicam->kref); + mutex_init(&unicam->lock); + + unicam->dev = &pdev->dev; + platform_set_drvdata(pdev, unicam); + + unicam->base = devm_platform_ioremap_resource_byname(pdev, "unicam"); + if (IS_ERR(unicam->base)) { + ret = PTR_ERR(unicam->base); + goto err_unicam_put; + } + + unicam->clk_gate_base = devm_platform_ioremap_resource_byname(pdev, "cmi"); + if (IS_ERR(unicam->clk_gate_base)) { + ret = PTR_ERR(unicam->clk_gate_base); + goto err_unicam_put; + } + + unicam->clock = devm_clk_get(&pdev->dev, "lp"); + if (IS_ERR(unicam->clock)) { + dev_err(unicam->dev, "Failed to get lp clock\n"); + ret = PTR_ERR(unicam->clock); + goto err_unicam_put; + } + + unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu"); + if (IS_ERR(unicam->vpu_clock)) { + dev_err(unicam->dev, "Failed to get vpu clock\n"); + ret = PTR_ERR(unicam->vpu_clock); + goto err_unicam_put; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_unicam_put; + + ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, + "unicam_capture0", unicam); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + goto err_unicam_put; + } + + /* Enable the block power domain. */ + pm_runtime_enable(&pdev->dev); + + ret = unicam_media_init(unicam); + if (ret) + goto err_pm_runtime; + + ret = unicam_subdev_init(unicam); + if (ret) + goto err_media_unregister; + + ret = unicam_async_nf_init(unicam); + if (ret) + goto err_subdev_unregister; + + return 0; + +err_subdev_unregister: + unicam_subdev_cleanup(unicam); +err_media_unregister: + media_device_unregister(&unicam->mdev); +err_pm_runtime: + pm_runtime_disable(&pdev->dev); +err_unicam_put: + unicam_put(unicam); + + return ret; +} + +static void unicam_remove(struct platform_device *pdev) +{ + struct unicam_device *unicam = platform_get_drvdata(pdev); + + unicam_unregister_nodes(unicam); + v4l2_device_unregister(&unicam->v4l2_dev); + media_device_unregister(&unicam->mdev); + v4l2_async_nf_unregister(&unicam->notifier); + + unicam_subdev_cleanup(unicam); + + unicam_put(unicam); + + pm_runtime_disable(&pdev->dev); +} + +static const struct of_device_id unicam_of_match[] = { + { .compatible = "brcm,bcm2835-unicam", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, unicam_of_match); + +static struct platform_driver unicam_driver = { + .probe = unicam_probe, + .remove = unicam_remove, + .driver = { + .name = UNICAM_MODULE_NAME, + .pm = pm_ptr(&unicam_pm_ops), + .of_match_table = unicam_of_match, + }, +}; + +module_platform_driver(unicam_driver); + +MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>"); +MODULE_DESCRIPTION("BCM2835 Unicam driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 2d7b0508cc9a..4d64df829e75 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -239,10 +239,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); - if (ret) - goto err_disable_pclk; - /* Enable DPHY clk and data lanes. */ if (csi2rx->dphy) { reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; @@ -252,6 +248,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) } writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + ret = csi2rx_configure_ext_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_pclk; + } } /* @@ -291,14 +294,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) reset_control_deassert(csi2rx->sys_rst); - if (csi2rx->dphy) { - ret = csi2rx_configure_ext_dphy(csi2rx); - if (ret) { - dev_err(csi2rx->dev, - "Failed to configure external DPHY: %d\n", ret); - goto err_disable_sysclk; - } - } + ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); + if (ret) + goto err_disable_sysclk; clk_disable_unprepare(csi2rx->p_clk); @@ -312,6 +310,10 @@ err_disable_pixclk: clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); } + if (csi2rx->dphy) { + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + phy_power_off(csi2rx->dphy); + } err_disable_pclk: clk_disable_unprepare(csi2rx->p_clk); @@ -749,7 +751,7 @@ MODULE_DEVICE_TABLE(of, csi2rx_of_table); static struct platform_driver csi2rx_driver = { .probe = csi2rx_probe, - .remove_new = csi2rx_remove, + .remove = csi2rx_remove, .driver = { .name = "cdns-csi2rx", diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index 3d98f91f1bee..e22b133f346d 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -644,7 +644,7 @@ static void csi2tx_remove(struct platform_device *pdev) static struct platform_driver csi2tx_driver = { .probe = csi2tx_probe, - .remove_new = csi2tx_remove, + .remove = csi2tx_remove, .driver = { .name = "cdns-csi2tx", diff --git a/drivers/media/platform/chips-media/coda/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c index ed47d5bd8d61..84ded154adfe 100644 --- a/drivers/media/platform/chips-media/coda/coda-bit.c +++ b/drivers/media/platform/chips-media/coda/coda-bit.c @@ -585,7 +585,7 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->rect.width, 16) * + size = (unsigned long)(DIV_ROUND_UP(q_data->rect.width, 16) * DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c index 7da0194ec850..289a076c3bcc 100644 --- a/drivers/media/platform/chips-media/coda/coda-common.c +++ b/drivers/media/platform/chips-media/coda/coda-common.c @@ -2171,8 +2171,6 @@ static const struct vb2_ops coda_qops = { .buf_queue = coda_buf_queue, .start_streaming = coda_start_streaming, .stop_streaming = coda_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int coda_s_ctrl(struct v4l2_ctrl *ctrl) @@ -3346,7 +3344,7 @@ static const struct dev_pm_ops coda_pm_ops = { static struct platform_driver coda_driver = { .probe = coda_probe, - .remove_new = coda_remove, + .remove = coda_remove, .driver = { .name = CODA_NAME, .of_match_table = coda_dt_ids, diff --git a/drivers/media/platform/chips-media/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c index ba8f41002917..5746892658b1 100644 --- a/drivers/media/platform/chips-media/coda/coda-jpeg.c +++ b/drivers/media/platform/chips-media/coda/coda-jpeg.c @@ -5,7 +5,7 @@ * Copyright (C) 2014 Philipp Zabel, Pengutronix */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/irqreturn.h> #include <linux/kernel.h> #include <linux/ktime.h> diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index 8433ecab230c..2c9d8cbca6e4 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -7,6 +7,8 @@ #include "wave5-helper.h" +#define DEFAULT_BS_SIZE(width, height) ((width) * (height) / 8 * 3) + const char *state_to_str(enum vpu_instance_state state) { switch (state) { @@ -29,7 +31,13 @@ void wave5_cleanup_instance(struct vpu_instance *inst) { int i; - if (list_is_singular(&inst->list)) + /* + * For Wave515 SRAM memory is allocated at + * wave5_vpu_dec_register_device() and freed at + * wave5_vpu_dec_unregister_device(). + */ + if (list_is_singular(&inst->list) && + inst->dev->product_code != WAVE515_CODE) wave5_vdi_free_sram(inst->dev); for (i = 0; i < inst->fbc_buf_count; i++) @@ -52,11 +60,11 @@ int wave5_vpu_release_device(struct file *filp, char *name) { struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data); + int ret = 0; 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) { @@ -72,7 +80,7 @@ int wave5_vpu_release_device(struct file *filp, wave5_cleanup_instance(inst); - return 0; + return ret; } int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq, @@ -211,3 +219,25 @@ void wave5_return_bufs(struct vb2_queue *q, u32 state) v4l2_m2m_buf_done(vbuf, state); } } + +void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, + int pix_fmt_type, + unsigned int width, + unsigned int height, + const struct v4l2_frmsize_stepwise *frmsize) +{ + v4l2_apply_frmsize_constraints(&width, &height, frmsize); + + if (pix_fmt_type == VPU_FMT_TYPE_CODEC) { + pix_mp->width = width; + pix_mp->height = height; + pix_mp->num_planes = 1; + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_BS_SIZE(width, height), + pix_mp->plane_fmt[0].sizeimage); + } else { + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, width, height); + } + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h index 6cee1c14d3ce..9937fce553fc 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.h +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -28,4 +28,9 @@ 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); +void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, + int pix_fmt_type, + unsigned int width, + unsigned int height, + const struct v4l2_frmsize_stepwise *frmsize); #endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 2d82791f575e..c8a905994109 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -18,22 +18,33 @@ #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) +#define W521_FEATURE_HEVC10BIT_ENC BIT(3) +#define W521_FEATURE_AVC10BIT_ENC BIT(11) +#define W521_FEATURE_AVC_ENCODER BIT(1) +#define W521_FEATURE_HEVC_ENCODER BIT(0) + +#define ENC_AVC_INTRA_IDR_PARAM_MASK 0x7ff +#define ENC_AVC_INTRA_PERIOD_SHIFT 6 +#define ENC_AVC_IDR_PERIOD_SHIFT 17 +#define ENC_AVC_FORCED_IDR_HEADER_SHIFT 28 + +#define ENC_HEVC_INTRA_QP_SHIFT 3 +#define ENC_HEVC_FORCED_IDR_HEADER_SHIFT 9 +#define ENC_HEVC_INTRA_PERIOD_SHIFT 16 /* Decoder support fields */ -#define FEATURE_AVC_DECODER BIT(3) -#define FEATURE_HEVC_DECODER BIT(2) +#define W521_FEATURE_AVC_DECODER BIT(3) +#define W521_FEATURE_HEVC_DECODER BIT(2) +#define W515_FEATURE_HEVC10BIT_DEC BIT(1) +#define W515_FEATURE_HEVC_DECODER BIT(0) -#define FEATURE_BACKBONE BIT(16) -#define FEATURE_VCORE_BACKBONE BIT(22) -#define FEATURE_VCPU_BACKBONE BIT(28) +#define W521_FEATURE_BACKBONE BIT(16) +#define W521_FEATURE_VCORE_BACKBONE BIT(22) +#define W521_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) \ + (BIT(31) | ((index) << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS)\ ) #define FASTIO_ADDRESS_MASK GENMASK(15, 0) @@ -155,6 +166,8 @@ 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 == WAVE515_CODE) + gdi_status_check_value = 0x0738; if (vpu_dev->product_code == WAVE521C_CODE || vpu_dev->product_code == WAVE521_CODE || vpu_dev->product_code == WAVE521E1_CODE) @@ -186,6 +199,8 @@ 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 WAVE515_CODE: + return PRODUCT_ID_515; case WAVE521C_CODE: return PRODUCT_ID_521; case WAVE521_CODE: @@ -299,6 +314,27 @@ static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *ins return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); } +static void setup_wave5_interrupts(struct vpu_device *vpu_dev) +{ + u32 reg_val = 0; + + if (vpu_dev->attr.support_encoders) { + /* Encoder interrupt */ + reg_val |= BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + } + + if (vpu_dev->attr.support_decoders) { + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + } + + return vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); +} + static int setup_wave5_properties(struct device *dev) { struct vpu_device *vpu_dev = dev_get_drvdata(dev); @@ -328,17 +364,35 @@ static int setup_wave5_properties(struct device *dev) 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); + if (vpu_dev->product_code == WAVE515_CODE) { + p_attr->support_hevc10bit_dec = FIELD_GET(W515_FEATURE_HEVC10BIT_DEC, + hw_config_feature); + p_attr->support_decoders = FIELD_GET(W515_FEATURE_HEVC_DECODER, + hw_config_def1) << STD_HEVC; + } else { + p_attr->support_hevc10bit_enc = FIELD_GET(W521_FEATURE_HEVC10BIT_ENC, + hw_config_feature); + p_attr->support_avc10bit_enc = FIELD_GET(W521_FEATURE_AVC10BIT_ENC, + hw_config_feature); + + p_attr->support_decoders = FIELD_GET(W521_FEATURE_AVC_DECODER, + hw_config_def1) << STD_AVC; + p_attr->support_decoders |= FIELD_GET(W521_FEATURE_HEVC_DECODER, + hw_config_def1) << STD_HEVC; + p_attr->support_encoders = FIELD_GET(W521_FEATURE_AVC_ENCODER, + hw_config_def1) << STD_AVC; + p_attr->support_encoders |= FIELD_GET(W521_FEATURE_HEVC_ENCODER, + hw_config_def1) << STD_HEVC; + + p_attr->support_backbone = FIELD_GET(W521_FEATURE_BACKBONE, + hw_config_def0); + p_attr->support_vcpu_backbone = FIELD_GET(W521_FEATURE_VCPU_BACKBONE, + hw_config_def0); + p_attr->support_vcore_backbone = FIELD_GET(W521_FEATURE_VCORE_BACKBONE, + hw_config_def0); + } + + setup_wave5_interrupts(vpu_dev); return 0; } @@ -380,12 +434,18 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) return -EINVAL; - temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_base = code_base + code_size; temp_size = WAVE5_TEMPBUF_SIZE; ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); @@ -413,22 +473,15 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t 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); + + if (vpu_dev->product_code != WAVE515_CODE) { + 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); + } reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -440,6 +493,24 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + } + 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); @@ -480,29 +551,40 @@ int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, return -EINVAL; } - p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + if (vpu_dev->product == PRODUCT_ID_515) + p_dec_info->vb_work.size = WAVE515DEC_WORKBUF_SIZE; + else + 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); + if (inst->dev->product_code != WAVE515_CODE) + 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); + if (inst->dev->product_code != WAVE515_CODE) { + 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)); + + if (inst->dev->product_code != WAVE515_CODE) { + /* 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, + WAVE521_COMMAND_QUEUE_DEPTH - 1); + } ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); if (ret) { @@ -553,7 +635,7 @@ static u32 get_bitstream_options(struct dec_info *info) 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 bs_option, cmd_option = INIT_SEQ_NORMAL; u32 reg_val, fail_res; int ret; @@ -563,7 +645,13 @@ int wave5_vpu_dec_init_seq(struct vpu_instance *inst) 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)); + bs_option = get_bitstream_options(p_dec_info); + + /* Without RD_PTR_VALID_FLAG Wave515 ignores RD_PTR value */ + if (inst->dev->product_code == WAVE515_CODE) + bs_option |= BSOPTION_RD_PTR_VALID_FLAG; + + vpu_write_reg(inst->dev, W5_BS_OPTION, bs_option); 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); @@ -629,10 +717,12 @@ static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initi 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; + if (inst->dev->product_code != WAVE515_CODE) { + 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) @@ -734,22 +824,27 @@ int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_b 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 (inst->dev->product_code != WAVE515_CODE) { + vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + + (p_dec_info->param_buf_size * WAVE521_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; + 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; - } + 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); + 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); @@ -830,6 +925,43 @@ free_mv_buffers: return ret; } +static u32 wave5_vpu_dec_validate_sec_axi(struct vpu_instance *inst) +{ + u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 bit_size = 0, ip_size = 0, lf_size = 0, ret = 0; + u32 sram_size = inst->dev->sram_size; + u32 width = inst->src_fmt.width; + + if (!sram_size) + return 0; + + /* + * TODO: calculate bit_size, ip_size, lf_size from width and bitdepth + * for Wave521. + */ + if (inst->dev->product_code == WAVE515_CODE) { + bit_size = DIV_ROUND_UP(width, 16) * 5 * 8; + ip_size = ALIGN(width, 16) * 2 * bitdepth / 8; + lf_size = ALIGN(width, 16) * 10 * bitdepth / 8; + } + + if (p_dec_info->sec_axi_info.use_bit_enable && sram_size >= bit_size) { + ret |= BIT(0); + sram_size -= bit_size; + } + + if (p_dec_info->sec_axi_info.use_ip_enable && sram_size >= ip_size) { + ret |= BIT(9); + sram_size -= ip_size; + } + + if (p_dec_info->sec_axi_info.use_lf_row_enable && sram_size >= lf_size) + ret |= BIT(15); + + return ret; +} + int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) { u32 reg_val; @@ -842,9 +974,7 @@ int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) 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); + reg_val = wave5_vpu_dec_validate_sec_axi(inst); vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val); /* set attributes of user buffer */ @@ -992,11 +1122,18 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) return -EINVAL; - temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + + temp_base = code_base + code_size; temp_size = WAVE5_TEMPBUF_SIZE; old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR); @@ -1030,22 +1167,15 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t 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); + if (vpu_dev->product_code != WAVE515_CODE) { + 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); + } reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -1057,6 +1187,29 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + u32 i; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, + WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, + WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, + vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, + vpu_dev->sram_buf.size); + } + 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); @@ -1075,13 +1228,13 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) 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) +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; + dma_addr_t code_base, temp_base; + u32 code_size, temp_size, reason_code; struct vpu_device *vpu_dev = dev_get_drvdata(dev); int ret; @@ -1111,13 +1264,22 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) { dev_err(dev, "size too small\n"); return -EINVAL; } + temp_base = code_base + code_size; + temp_size = WAVE5_TEMPBUF_SIZE; + /* Power on without DEBUG mode */ vpu_write_reg(vpu_dev, W5_PO_CONF, 0); @@ -1130,22 +1292,17 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin /* 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); + if (vpu_dev->product_code != WAVE515_CODE) { + 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); + } + + setup_wave5_interrupts(vpu_dev); reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -1157,6 +1314,29 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + u32 i; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, + WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, + WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, + vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, + vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU); /* Start VPU after settings */ @@ -1401,7 +1581,7 @@ int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, 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)); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, WAVE521_COMMAND_QUEUE_DEPTH - 1); /* This register must be reset explicitly */ vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0); @@ -1601,12 +1781,19 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) 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)); + ((p_param->intra_period & ENC_AVC_INTRA_IDR_PARAM_MASK) + << ENC_AVC_INTRA_PERIOD_SHIFT) | + ((p_param->avc_idr_period & ENC_AVC_INTRA_IDR_PARAM_MASK) + << ENC_AVC_IDR_PERIOD_SHIFT) | + (p_param->forced_idr_header_enable + << ENC_AVC_FORCED_IDR_HEADER_SHIFT)); 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)); + p_param->decoding_refresh_type | + (p_param->intra_qp << ENC_HEVC_INTRA_QP_SHIFT) | + (p_param->forced_idr_header_enable + << ENC_HEVC_FORCED_IDR_HEADER_SHIFT) | + (p_param->intra_period << ENC_HEVC_INTRA_PERIOD_SHIFT)); reg_val = (p_param->rdo_skip << 2) | (p_param->lambda_scaling_enable << 3) | @@ -1855,7 +2042,7 @@ int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance * 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); + (p_enc_info->param_buf_size * WAVE521_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); @@ -1943,6 +2130,31 @@ free_vb_fbc_y_tbl: return ret; } +static u32 wave5_vpu_enc_validate_sec_axi(struct vpu_instance *inst) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + u32 rdo_size = 0, lf_size = 0, ret = 0; + u32 sram_size = inst->dev->sram_size; + + if (!sram_size) + return 0; + + /* + * TODO: calculate rdo_size and lf_size from inst->src_fmt.width and + * inst->codec_info->enc_info.open_param.wave_param.internal_bit_depth + */ + + if (p_enc_info->sec_axi_info.use_enc_rdo_enable && sram_size >= rdo_size) { + ret |= BIT(11); + sram_size -= rdo_size; + } + + if (p_enc_info->sec_axi_info.use_enc_lf_enable && sram_size >= lf_size) + ret |= BIT(15); + + return ret; +} + int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res) { u32 src_frame_format; @@ -1964,8 +2176,7 @@ int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *f 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); + reg_val = wave5_vpu_enc_validate_sec_axi(inst); 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); diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h index a15c6b2c3d8b..557344754c4c 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h +++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h @@ -205,6 +205,9 @@ enum query_opt { #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_CMD_INIT_NUM_TASK_BUF (W5_REG_BASE + 0x0134) +#define W5_CMD_INIT_ADDR_TASK_BUF0 (W5_REG_BASE + 0x0138) +#define W5_CMD_INIT_TASK_BUF_SIZE (W5_REG_BASE + 0x0178) #define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180) /************************************************************************/ @@ -216,7 +219,9 @@ enum query_opt { #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 W515_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0124) #define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134) +#define W515_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0128) #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) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c index 3809f70bc0b4..bb13267ced38 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -18,7 +18,11 @@ static int wave5_vdi_allocate_common_memory(struct device *dev) if (!vpu_dev->common_mem.vaddr) { int ret; - vpu_dev->common_mem.size = SIZE_COMMON; + if (vpu_dev->product_code == WAVE515_CODE) + vpu_dev->common_mem.size = WAVE515_SIZE_COMMON; + else + vpu_dev->common_mem.size = WAVE521_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"); @@ -174,16 +178,19 @@ int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) { struct vpu_buf *vb = &vpu_dev->sram_buf; + dma_addr_t daddr; + void *vaddr; + size_t size; - if (!vpu_dev->sram_pool || !vpu_dev->sram_size) + if (!vpu_dev->sram_pool || vb->vaddr) 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; + size = min_t(size_t, vpu_dev->sram_size, gen_pool_avail(vpu_dev->sram_pool)); + vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr); + if (vaddr) { + vb->vaddr = vaddr; + vb->daddr = daddr; + vb->size = size; } dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n", @@ -197,9 +204,7 @@ void wave5_vdi_free_sram(struct vpu_device *vpu_dev) if (!vb->size || !vb->vaddr) return; - if (vb->vaddr) - gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, - vb->size); + 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-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index ef227af72348..d3ff420c52ce 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -5,116 +5,98 @@ * Copyright (C) 2021-2023 CHIPS&MEDIA INC */ +#include <linux/pm_runtime.h> #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 v4l2_frmsize_stepwise dec_hevc_frmsize = { + .min_width = W5_MIN_DEC_PIC_8_WIDTH, + .max_width = W5_MAX_DEC_PIC_WIDTH, + .step_width = W5_DEC_CODEC_STEP_WIDTH, + .min_height = W5_MIN_DEC_PIC_8_HEIGHT, + .max_height = W5_MAX_DEC_PIC_HEIGHT, + .step_height = W5_DEC_CODEC_STEP_HEIGHT, +}; + +static const struct v4l2_frmsize_stepwise dec_h264_frmsize = { + .min_width = W5_MIN_DEC_PIC_32_WIDTH, + .max_width = W5_MAX_DEC_PIC_WIDTH, + .step_width = W5_DEC_CODEC_STEP_WIDTH, + .min_height = W5_MIN_DEC_PIC_32_HEIGHT, + .max_height = W5_MAX_DEC_PIC_HEIGHT, + .step_height = W5_DEC_CODEC_STEP_HEIGHT, +}; + +static const struct v4l2_frmsize_stepwise dec_raw_frmsize = { + .min_width = W5_MIN_DEC_PIC_8_WIDTH, + .max_width = W5_MAX_DEC_PIC_WIDTH, + .step_width = W5_DEC_RAW_STEP_WIDTH, + .min_height = W5_MIN_DEC_PIC_8_HEIGHT, + .max_height = W5_MAX_DEC_PIC_HEIGHT, + .step_height = W5_DEC_RAW_STEP_HEIGHT, +}; 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_frmsize = &dec_hevc_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_H264, - .max_width = 8192, - .min_width = 32, - .max_height = 4320, - .min_height = 32, + .v4l2_frmsize = &dec_h264_frmsize, }, }, [VPU_FMT_TYPE_RAW] = { { .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV16, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV61, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, { .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, - .max_width = 8192, - .min_width = 8, - .max_height = 4320, - .min_height = 8, + .v4l2_frmsize = &dec_raw_frmsize, }, } }; @@ -233,74 +215,6 @@ static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr 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; @@ -388,6 +302,8 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst) } if (p_dec_info->initial_info_obtained) { + const struct vpu_format *vpu_fmt; + 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 - @@ -395,10 +311,27 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst) 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); + vpu_fmt = wave5_find_vpu_fmt(inst->src_fmt.pixelformat, + dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) + return -EINVAL; + + wave5_update_pix_fmt(&inst->src_fmt, + VPU_FMT_TYPE_CODEC, + initial_info->pic_width, + initial_info->pic_height, + vpu_fmt->v4l2_frmsize); + + vpu_fmt = wave5_find_vpu_fmt(inst->dst_fmt.pixelformat, + dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + + wave5_update_pix_fmt(&inst->dst_fmt, + VPU_FMT_TYPE_RAW, + initial_info->pic_width, + initial_info->pic_height, + vpu_fmt->v4l2_frmsize); } v4l2_event_queue_fh(fh, &vpu_event_src_ch); @@ -518,6 +451,8 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) 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__); + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } } @@ -545,12 +480,12 @@ static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_f } 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; + fsize->stepwise.min_width = vpu_fmt->v4l2_frmsize->min_width; + fsize->stepwise.max_width = vpu_fmt->v4l2_frmsize->max_width; + fsize->stepwise.step_width = W5_DEC_CODEC_STEP_WIDTH; + fsize->stepwise.min_height = vpu_fmt->v4l2_frmsize->min_height; + fsize->stepwise.max_height = vpu_fmt->v4l2_frmsize->max_height; + fsize->stepwise.step_height = W5_DEC_CODEC_STEP_HEIGHT; return 0; } @@ -573,6 +508,7 @@ static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo { struct vpu_instance *inst = wave5_to_vpu_inst(fh); struct dec_info *p_dec_info = &inst->codec_info->dec_info; + const struct v4l2_frmsize_stepwise *frmsize; const struct vpu_format *vpu_fmt; int width, height; @@ -586,14 +522,12 @@ static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo 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; + frmsize = &dec_raw_frmsize; } 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); + width = f->fmt.pix_mp.width; + height = f->fmt.pix_mp.height; f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; - f->fmt.pix_mp.num_planes = info->mem_planes; + frmsize = vpu_fmt->v4l2_frmsize; } if (p_dec_info->initial_info_obtained) { @@ -601,9 +535,8 @@ static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo 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; + wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_RAW, + width, height, frmsize); f->fmt.pix_mp.colorspace = inst->colorspace; f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; f->fmt.pix_mp.quantization = inst->quantization; @@ -715,7 +648,9 @@ static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_f 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 v4l2_frmsize_stepwise *frmsize; const struct vpu_format *vpu_fmt; + int width, height; dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n", @@ -724,20 +659,19 @@ static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_fo vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]); if (!vpu_fmt) { + width = inst->src_fmt.width; + height = inst->src_fmt.height; 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); + frmsize = &dec_hevc_frmsize; } 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); - + width = f->fmt.pix_mp.width; + height = f->fmt.pix_mp.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); + frmsize = vpu_fmt->v4l2_frmsize; } - f->fmt.pix_mp.flags = 0; - f->fmt.pix_mp.field = V4L2_FIELD_NONE; + wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_CODEC, + width, height, frmsize); return 0; } @@ -745,6 +679,7 @@ static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_fo 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); + const struct vpu_format *vpu_fmt; int i, ret; dev_dbg(inst->dev->dev, @@ -779,7 +714,13 @@ static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_form 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); + vpu_fmt = wave5_find_vpu_fmt(inst->dst_fmt.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + + wave5_update_pix_fmt(&inst->dst_fmt, VPU_FMT_TYPE_RAW, + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + vpu_fmt->v4l2_frmsize); return 0; } @@ -1002,6 +943,7 @@ static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buff 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); @@ -1015,31 +957,9 @@ static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buff 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]); + 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]); } } @@ -1055,6 +975,22 @@ static int wave5_prepare_fb(struct vpu_instance *inst) int ret, i; struct v4l2_m2m_buffer *buf, *n; struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth; + + switch (bitdepth) { + case 8: + break; + case 10: + if (inst->std == W_HEVC_DEC && + inst->dev->attr.support_hevc10bit_dec) + break; + + fallthrough; + default: + dev_err(inst->dev->dev, "no support for %d bit depth\n", bitdepth); + + return -EINVAL; + } linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx); non_linear_num = inst->fbc_buf_count; @@ -1063,7 +999,7 @@ static int wave5_prepare_fb(struct vpu_instance *inst) struct frame_buffer *frame = &inst->frame_buf[i]; struct vpu_buf *vframe = &inst->frame_vbuf[i]; - fb_stride = inst->dst_fmt.width; + fb_stride = ALIGN(inst->dst_fmt.width * bitdepth / 8, 32); fb_height = ALIGN(inst->dst_fmt.height, 32); luma_size = fb_stride * fb_height; @@ -1382,6 +1318,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count int ret = 0; dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + pm_runtime_resume_and_get(inst->dev->dev); v4l2_m2m_update_start_streaming_state(m2m_ctx, q); @@ -1408,30 +1345,20 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count 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; - } - } } - + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); 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); + pm_runtime_put_autosuspend(inst->dev->dev); return ret; } @@ -1517,6 +1444,7 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) bool check_cmd = TRUE; dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + pm_runtime_resume_and_get(inst->dev->dev); while (check_cmd) { struct queue_status_info q_status; @@ -1540,12 +1468,13 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) streamoff_output(q); else streamoff_capture(q); + + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); } 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, @@ -1554,20 +1483,15 @@ static const struct vb2_ops wave5_vpu_dec_vb2_ops = { 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); + wave5_update_pix_fmt(src_fmt, VPU_FMT_TYPE_CODEC, + W5_DEF_DEC_PIC_WIDTH, W5_DEF_DEC_PIC_HEIGHT, + &dec_hevc_frmsize); + + dst_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; + wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_RAW, + W5_DEF_DEC_PIC_WIDTH, W5_DEF_DEC_PIC_HEIGHT, + &dec_raw_frmsize); } static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -1626,7 +1550,7 @@ static void wave5_vpu_dec_device_run(void *priv) int ret = 0; dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__); - + pm_runtime_resume_and_get(inst->dev->dev); ret = fill_ringbuffer(inst); if (ret) { dev_warn(inst->dev->dev, "Filling ring buffer failed\n"); @@ -1709,6 +1633,8 @@ static void wave5_vpu_dec_device_run(void *priv) finish_job_and_return: dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } @@ -1810,7 +1736,6 @@ static int wave5_vpu_open_dec(struct file *filp) 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 = @@ -1865,7 +1790,23 @@ static int wave5_vpu_open_dec(struct file *filp) goto cleanup_inst; } - wave5_vdi_allocate_sram(inst->dev); + /* + * For Wave515 SRAM memory was already allocated + * at wave5_vpu_dec_register_device() + */ + if (inst->dev->product_code != WAVE515_CODE) + wave5_vdi_allocate_sram(inst->dev); + + ret = mutex_lock_interruptible(&dev->dev_lock); + if (ret) + goto cleanup_inst; + + if (list_empty(&dev->instances)) + pm_runtime_use_autosuspend(inst->dev->dev); + + list_add_tail(&inst->list, &dev->instances); + + mutex_unlock(&dev->dev_lock); return 0; @@ -1893,6 +1834,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev) struct video_device *vdev_dec; int ret; + /* + * Secondary AXI setup for Wave515 is done by INIT_VPU command, + * i.e. wave5_vpu_init(), that's why we allocate SRAM memory early. + */ + if (dev->product_code == WAVE515_CODE) + wave5_vdi_allocate_sram(dev); + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL); if (!vdev_dec) return -ENOMEM; @@ -1926,6 +1874,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev) void wave5_vpu_dec_unregister_device(struct vpu_device *dev) { + /* + * Here is a freeing pair for Wave515 SRAM memory allocation + * happened at wave5_vpu_dec_register_device(). + */ + if (dev->product_code == WAVE515_CODE) + wave5_vdi_free_sram(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 index 8bbf9d10b467..1e5fc5f8b856 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -5,70 +5,90 @@ * Copyright (C) 2021-2023 CHIPS&MEDIA INC */ +#include <linux/pm_runtime.h> #include "wave5-helper.h" #define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder" #define VPU_ENC_DRV_NAME "wave5-enc" +static const struct v4l2_frmsize_stepwise enc_frmsize[FMT_TYPES] = { + [VPU_FMT_TYPE_CODEC] = { + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .step_width = W5_ENC_CODEC_STEP_WIDTH, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .step_height = W5_ENC_CODEC_STEP_HEIGHT, + }, + [VPU_FMT_TYPE_RAW] = { + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .step_width = W5_ENC_RAW_STEP_WIDTH, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .step_height = W5_ENC_RAW_STEP_HEIGHT, + }, +}; + 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_frmsize = &enc_frmsize[VPU_FMT_TYPE_CODEC], }, { .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, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_CODEC], }, }, [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_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, { .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_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, { .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_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, { .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_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, { .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_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, { .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, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, } }; @@ -105,46 +125,6 @@ invalid_state_switch: 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; @@ -153,13 +133,26 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res) 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)); + const struct v4l2_format_info *info; + u32 stride = inst->src_fmt.plane_fmt[0].bytesperline; + u32 luma_size = 0; + u32 chroma_size = 0; memset(&pic_param, 0, sizeof(struct enc_param)); memset(&frame_buf, 0, sizeof(struct frame_buffer)); + info = v4l2_format_info(inst->src_fmt.pixelformat); + if (!info) + return -EINVAL; + + if (info->mem_planes == 1) { + luma_size = stride * inst->dst_fmt.height; + chroma_size = luma_size / (info->hdiv * info->vdiv); + } else { + luma_size = inst->src_fmt.plane_fmt[0].sizeimage; + chroma_size = inst->src_fmt.plane_fmt[1].sizeimage; + } + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx); if (!dst_buf) { dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__); @@ -359,13 +352,8 @@ static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_f 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; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = enc_frmsize[VPU_FMT_TYPE_CODEC]; return 0; } @@ -390,7 +378,9 @@ static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_f 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 v4l2_frmsize_stepwise *frmsize; const struct vpu_format *vpu_fmt; + int width, height; 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, @@ -398,20 +388,19 @@ static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]); 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; - wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height); + frmsize = &enc_frmsize[VPU_FMT_TYPE_CODEC]; } 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); - + width = f->fmt.pix_mp.width; + height = f->fmt.pix_mp.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); + frmsize = vpu_fmt->v4l2_frmsize; } - f->fmt.pix_mp.flags = 0; - f->fmt.pix_mp.field = V4L2_FIELD_NONE; + wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_CODEC, + width, height, frmsize); f->fmt.pix_mp.colorspace = inst->colorspace; f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; f->fmt.pix_mp.quantization = inst->quantization; @@ -498,7 +487,9 @@ static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_f 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 v4l2_frmsize_stepwise *frmsize; const struct vpu_format *vpu_fmt; + int width, height; 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, @@ -506,28 +497,27 @@ static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_fo vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]); if (!vpu_fmt) { + width = inst->src_fmt.width; + height = inst->src_fmt.height; 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); + frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW]; } 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); - + width = f->fmt.pix_mp.width; + height = f->fmt.pix_mp.height; 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); + frmsize = vpu_fmt->v4l2_frmsize; } - f->fmt.pix_mp.flags = 0; - f->fmt.pix_mp.field = V4L2_FIELD_NONE; - + wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_RAW, + width, height, frmsize); 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); + const struct vpu_format *vpu_fmt; + const struct v4l2_format_info *info; int i, ret; dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", @@ -549,16 +539,20 @@ static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_form 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; + info = v4l2_format_info(inst->src_fmt.pixelformat); + if (!info) + return -EINVAL; + + inst->cbcr_interleave = (info->comp_planes == 2) ? true : false; + + switch (inst->src_fmt.pixelformat) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV61M: inst->nv21 = true; - } else { - inst->cbcr_interleave = false; + break; + default: inst->nv21 = false; } @@ -567,7 +561,15 @@ static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_form 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); + vpu_fmt = wave5_find_vpu_fmt(inst->dst_fmt.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) + return -EINVAL; + + wave5_update_pix_fmt(&inst->dst_fmt, VPU_FMT_TYPE_CODEC, + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + vpu_fmt->v4l2_frmsize); + inst->conf_win.width = inst->dst_fmt.width; + inst->conf_win.height = inst->dst_fmt.height; return 0; } @@ -583,12 +585,17 @@ static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_se 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; + case V4L2_SEL_TGT_CROP: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->conf_win.width; + s->r.height = inst->conf_win.height; + break; default: return -EINVAL; } @@ -611,8 +618,10 @@ static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_se s->r.left = 0; s->r.top = 0; - s->r.width = inst->src_fmt.width; - s->r.height = inst->src_fmt.height; + s->r.width = min(s->r.width, inst->dst_fmt.width); + s->r.height = min(s->r.height, inst->dst_fmt.height); + + inst->conf_win = s->r; return 0; } @@ -1061,6 +1070,9 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: inst->enc_param.entropy_coding_mode = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: + inst->enc_param.forced_idr_header_enable = ctrl->val; + break; case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: break; default: @@ -1125,13 +1137,23 @@ static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); } -static void wave5_set_enc_openparam(struct enc_open_param *open_param, - struct vpu_instance *inst) +static int wave5_set_enc_openparam(struct enc_open_param *open_param, + struct vpu_instance *inst) { struct enc_wave_param input = inst->enc_param; + const struct v4l2_format_info *info; u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64; u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16; + info = v4l2_format_info(inst->src_fmt.pixelformat); + if (!info) + return -EINVAL; + + if (info->hdiv == 2 && info->vdiv == 1) + open_param->src_format = FORMAT_422; + else + open_param->src_format = FORMAT_420; + 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; @@ -1147,8 +1169,8 @@ static void wave5_set_enc_openparam(struct enc_open_param *open_param, 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->pic_width = inst->conf_win.width; + open_param->pic_height = inst->conf_win.height; open_param->frame_rate_info = inst->frame_rate; open_param->rc_enable = inst->rc_enable; if (inst->rc_enable) { @@ -1219,6 +1241,9 @@ static void wave5_set_enc_openparam(struct enc_open_param *open_param, else open_param->wave_param.intra_refresh_arg = num_ctu_row; } + open_param->wave_param.forced_idr_header_enable = input.forced_idr_header_enable; + + return 0; } static int initialize_sequence(struct vpu_instance *inst) @@ -1247,7 +1272,7 @@ static int initialize_sequence(struct vpu_instance *inst) __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; + WAVE521_COMMAND_QUEUE_DEPTH; ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); @@ -1306,6 +1331,7 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; int ret = 0; + pm_runtime_resume_and_get(inst->dev->dev); v4l2_m2m_update_start_streaming_state(m2m_ctx, q); if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { @@ -1313,7 +1339,12 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count memset(&open_param, 0, sizeof(struct enc_open_param)); - wave5_set_enc_openparam(&open_param, inst); + ret = wave5_set_enc_openparam(&open_param, inst); + if (ret) { + dev_dbg(inst->dev->dev, "%s: wave5_set_enc_openparam, fail: %d\n", + __func__, ret); + goto return_buffers; + } ret = wave5_vpu_enc_open(inst, &open_param); if (ret) { @@ -1360,9 +1391,13 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count if (ret) goto return_buffers; + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); return 0; return_buffers: wave5_return_bufs(q, VB2_BUF_STATE_QUEUED); + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); return ret; } @@ -1404,6 +1439,7 @@ static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q) */ dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + pm_runtime_resume_and_get(inst->dev->dev); if (wave5_vpu_both_queues_are_streaming(inst)) switch_state(inst, VPU_INST_STATE_STOP); @@ -1428,12 +1464,13 @@ static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q) streamoff_output(inst, q); else streamoff_capture(inst, q); + + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); } 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, @@ -1442,20 +1479,15 @@ static const struct vb2_ops wave5_vpu_enc_vb2_ops = { 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); + src_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; + wave5_update_pix_fmt(src_fmt, VPU_FMT_TYPE_RAW, + W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT, + &enc_frmsize[VPU_FMT_TYPE_RAW]); 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); + wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_CODEC, + W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT, + &enc_frmsize[VPU_FMT_TYPE_CODEC]); } static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -1474,6 +1506,7 @@ static void wave5_vpu_enc_device_run(void *priv) u32 fail_res = 0; int ret = 0; + pm_runtime_resume_and_get(inst->dev->dev); switch (inst->state) { case VPU_INST_STATE_PIC_RUN: ret = start_encode(inst, &fail_res); @@ -1487,6 +1520,8 @@ static void wave5_vpu_enc_device_run(void *priv) break; } dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); return; default: WARN(1, "Execution of a job in state %s is invalid.\n", @@ -1494,6 +1529,8 @@ static void wave5_vpu_enc_device_run(void *priv) break; } dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); + pm_runtime_mark_last_busy(inst->dev->dev); + pm_runtime_put_autosuspend(inst->dev->dev); v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } @@ -1554,7 +1591,6 @@ static int wave5_vpu_open_enc(struct file *filp) 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 = @@ -1702,6 +1738,9 @@ static int wave5_vpu_open_enc(struct file *filp) 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); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR, + 0, 1, 1, 0); if (v4l2_ctrl_hdl->error) { ret = -ENODEV; @@ -1712,6 +1751,8 @@ static int wave5_vpu_open_enc(struct file *filp) v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + inst->conf_win.width = inst->dst_fmt.width; + inst->conf_win.height = inst->dst_fmt.height; inst->colorspace = V4L2_COLORSPACE_REC709; inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; inst->quantization = V4L2_QUANTIZATION_DEFAULT; @@ -1729,6 +1770,17 @@ static int wave5_vpu_open_enc(struct file *filp) wave5_vdi_allocate_sram(inst->dev); + ret = mutex_lock_interruptible(&dev->dev_lock); + if (ret) + goto cleanup_inst; + + if (list_empty(&dev->instances)) + pm_runtime_use_autosuspend(inst->dev->dev); + + list_add_tail(&inst->list, &dev->instances); + + mutex_unlock(&dev->dev_lock); + return 0; cleanup_inst: diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index 1b3df5b04249..d1320298a0f7 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -10,6 +10,8 @@ #include <linux/clk.h> #include <linux/firmware.h> #include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> #include "wave5-vpu.h" #include "wave5-regdefine.h" #include "wave5-vpuconfig.h" @@ -24,8 +26,12 @@ struct wave5_match_data { int flags; const char *fw_name; + u32 sram_size; }; +static int vpu_poll_interval = 5; +module_param(vpu_poll_interval, int, 0644); + int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout) { int ret; @@ -40,7 +46,7 @@ int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout) return 0; } -static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) +static void wave5_vpu_handle_irq(void *dev_id) { u32 seq_done; u32 cmd_done; @@ -48,42 +54,73 @@ static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) 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); - } + 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 (dev->product_code == WAVE515_CODE && + (cmd_done & BIT(inst->id))) { + cmd_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, + cmd_done); + complete(&inst->irq_done); + } else 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); - } + 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); } + + wave5_vpu_clear_interrupt(inst, irq_reason); } +} + +static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) +{ + struct vpu_device *dev = dev_id; + + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) + wave5_vpu_handle_irq(dev); return IRQ_HANDLED; } +static void wave5_vpu_irq_work_fn(struct kthread_work *work) +{ + struct vpu_device *dev = container_of(work, struct vpu_device, work); + + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) + wave5_vpu_handle_irq(dev); +} + +static enum hrtimer_restart wave5_vpu_timer_callback(struct hrtimer *timer) +{ + struct vpu_device *dev = + container_of(timer, struct vpu_device, hrtimer); + + kthread_queue_work(dev->worker, &dev->work); + hrtimer_forward_now(timer, ns_to_ktime(vpu_poll_interval * NSEC_PER_MSEC)); + + return HRTIMER_RESTART; +} + static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name, u32 *revision) { @@ -117,6 +154,45 @@ static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name, return 0; } +static __maybe_unused int wave5_pm_suspend(struct device *dev) +{ + struct vpu_device *vpu = dev_get_drvdata(dev); + + if (pm_runtime_suspended(dev)) + return 0; + + if (vpu->irq < 0) + hrtimer_cancel(&vpu->hrtimer); + + wave5_vpu_sleep_wake(dev, true, NULL, 0); + clk_bulk_disable_unprepare(vpu->num_clks, vpu->clks); + + return 0; +} + +static __maybe_unused int wave5_pm_resume(struct device *dev) +{ + struct vpu_device *vpu = dev_get_drvdata(dev); + int ret = 0; + + wave5_vpu_sleep_wake(dev, false, NULL, 0); + ret = clk_bulk_prepare_enable(vpu->num_clks, vpu->clks); + if (ret) { + dev_err(dev, "Enabling clocks, fail: %d\n", ret); + return ret; + } + + if (vpu->irq < 0 && !hrtimer_active(&vpu->hrtimer)) + hrtimer_start(&vpu->hrtimer, ns_to_ktime(vpu->vpu_poll_interval * NSEC_PER_MSEC), + HRTIMER_MODE_REL_PINNED); + + return ret; +} + +static const struct dev_pm_ops wave5_pm_ops = { + SET_RUNTIME_PM_OPS(wave5_pm_suspend, wave5_pm_resume, NULL) +}; + static int wave5_vpu_probe(struct platform_device *pdev) { int ret; @@ -151,6 +227,16 @@ static int wave5_vpu_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, dev); dev->dev = &pdev->dev; + dev->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev); + if (IS_ERR(dev->resets)) { + return dev_err_probe(&pdev->dev, PTR_ERR(dev->resets), + "Failed to get reset control\n"); + } + + ret = reset_control_deassert(dev->resets); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to deassert resets\n"); + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks); /* continue without clock, assume externally managed */ @@ -163,20 +249,15 @@ static int wave5_vpu_probe(struct platform_device *pdev) 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; + goto err_reset_assert; } 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->sram_size = match_data->sram_size; + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER); ret = wave5_vdi_init(&pdev->dev); if (ret < 0) { @@ -185,6 +266,28 @@ static int wave5_vpu_probe(struct platform_device *pdev) } dev->product = wave5_vpu_get_product_id(dev); + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n"); + hrtimer_init(&dev->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + dev->hrtimer.function = &wave5_vpu_timer_callback; + dev->worker = kthread_run_worker(0, "vpu_irq_thread"); + if (IS_ERR(dev->worker)) { + dev_err(&pdev->dev, "failed to create vpu irq worker\n"); + ret = PTR_ERR(dev->worker); + goto err_vdi_release; + } + dev->vpu_poll_interval = vpu_poll_interval; + kthread_init_work(&dev->work, wave5_vpu_irq_work_fn); + } else { + 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; + } + } + INIT_LIST_HEAD(&dev->instances); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { @@ -207,20 +310,6 @@ static int wave5_vpu_probe(struct platform_device *pdev) } } - 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); @@ -232,6 +321,12 @@ static int wave5_vpu_probe(struct platform_device *pdev) (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); + + pm_runtime_set_autosuspend_delay(&pdev->dev, 100); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + wave5_vpu_sleep_wake(&pdev->dev, true, NULL, 0); + return 0; err_enc_unreg: @@ -246,6 +341,8 @@ err_vdi_release: wave5_vdi_release(&pdev->dev); err_clk_dis: clk_bulk_disable_unprepare(dev->num_clks, dev->clks); +err_reset_assert: + reset_control_assert(dev->resets); return ret; } @@ -254,8 +351,17 @@ static void wave5_vpu_remove(struct platform_device *pdev) { struct vpu_device *dev = dev_get_drvdata(&pdev->dev); + if (dev->irq < 0) { + kthread_destroy_worker(dev->worker); + hrtimer_cancel(&dev->hrtimer); + } + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + mutex_destroy(&dev->dev_lock); mutex_destroy(&dev->hw_lock); + reset_control_assert(dev->resets); clk_bulk_disable_unprepare(dev->num_clks, dev->clks); wave5_vpu_enc_unregister_device(dev); wave5_vpu_dec_unregister_device(dev); @@ -267,6 +373,7 @@ static void wave5_vpu_remove(struct platform_device *pdev) static const struct wave5_match_data ti_wave521c_data = { .flags = WAVE5_IS_ENC | WAVE5_IS_DEC, .fw_name = "cnm/wave521c_k3_codec_fw.bin", + .sram_size = (64 * 1024), }; static const struct of_device_id wave5_dt_ids[] = { @@ -279,9 +386,10 @@ static struct platform_driver wave5_vpu_driver = { .driver = { .name = VPU_PLATFORM_DEVICE_NAME, .of_match_table = of_match_ptr(wave5_dt_ids), + .pm = &wave5_pm_ops, }, .probe = wave5_vpu_probe, - .remove_new = wave5_vpu_remove, + .remove = wave5_vpu_remove, }; module_platform_driver(wave5_vpu_driver); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h index 32b7fd3730b5..3847332551fc 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h @@ -38,10 +38,7 @@ enum vpu_fmt_type { struct vpu_format { unsigned int v4l2_pix_fmt; - unsigned int max_width; - unsigned int min_width; - unsigned int max_height; - unsigned int min_height; + const struct v4l2_frmsize_stepwise *v4l2_frmsize; }; static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c index 1a3efb638dde..e16b990041c2 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -6,6 +6,8 @@ */ #include <linux/bug.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> #include "wave5-vpuapi.h" #include "wave5-regdefine.h" #include "wave5.h" @@ -195,14 +197,20 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) int retry = 0; struct vpu_device *vpu_dev = inst->dev; int i; + int inst_count = 0; + struct vpu_instance *inst_elm; *fail_res = 0; if (!inst->codec_info) return -EINVAL; + pm_runtime_resume_and_get(inst->dev->dev); + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); - if (ret) + if (ret) { + pm_runtime_put_sync(inst->dev->dev); return ret; + } do { ret = wave5_vpu_dec_finish_seq(inst, fail_res); @@ -232,9 +240,14 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task); + list_for_each_entry(inst_elm, &vpu_dev->instances, list) + inst_count++; + if (inst_count == 1) + pm_runtime_dont_use_autosuspend(vpu_dev->dev); + unlock_and_return: mutex_unlock(&vpu_dev->hw_lock); - + pm_runtime_put_sync(inst->dev->dev); return ret; } @@ -697,25 +710,33 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res) int ret; int retry = 0; struct vpu_device *vpu_dev = inst->dev; + int inst_count = 0; + struct vpu_instance *inst_elm; *fail_res = 0; if (!inst->codec_info) return -EINVAL; + pm_runtime_resume_and_get(inst->dev->dev); + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); - if (ret) + if (ret) { + pm_runtime_resume_and_get(inst->dev->dev); 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"); + pm_runtime_resume_and_get(inst->dev->dev); mutex_unlock(&vpu_dev->hw_lock); return ret; } if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING && retry++ >= MAX_FIRMWARE_CALL_RETRY) { + pm_runtime_resume_and_get(inst->dev->dev); mutex_unlock(&vpu_dev->hw_lock); return -ETIMEDOUT; } @@ -734,7 +755,13 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res) wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task); + list_for_each_entry(inst_elm, &vpu_dev->instances, list) + inst_count++; + if (inst_count == 1) + pm_runtime_dont_use_autosuspend(vpu_dev->dev); + mutex_unlock(&vpu_dev->hw_lock); + pm_runtime_put_sync(inst->dev->dev); return 0; } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index 352f6e904e50..45615c15beca 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -18,6 +18,7 @@ #include "wave5-vdi.h" enum product_id { + PRODUCT_ID_515, PRODUCT_ID_521, PRODUCT_ID_511, PRODUCT_ID_517, @@ -327,6 +328,7 @@ struct vpu_attr { u32 support_backbone: 1; u32 support_avc10bit_enc: 1; u32 support_hevc10bit_enc: 1; + u32 support_hevc10bit_dec: 1; u32 support_vcore_backbone: 1; u32 support_vcpu_backbone: 1; }; @@ -566,6 +568,7 @@ struct enc_wave_param { 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 */ + u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ }; struct enc_open_param { @@ -756,7 +759,12 @@ struct vpu_device { u32 product_code; struct ida inst_ida; struct clk_bulk_data *clks; + struct hrtimer hrtimer; + struct kthread_work work; + struct kthread_worker *worker; + int vpu_poll_interval; int num_clks; + struct reset_control *resets; }; struct vpu_instance; diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h index d9751eedb0f9..1ea9f5f31499 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -8,6 +8,7 @@ #ifndef _VPU_CONFIG_H_ #define _VPU_CONFIG_H_ +#define WAVE515_CODE 0x5150 #define WAVE517_CODE 0x5170 #define WAVE537_CODE 0x5370 #define WAVE511_CODE 0x5110 @@ -21,19 +22,39 @@ ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \ (c) == WAVE511_CODE || (c) == WAVE521_CODE || \ (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \ - (c) == WAVE521C_DUAL_CODE); \ + (c) == WAVE521C_DUAL_CODE) || (c) == WAVE515_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 WAVE515DEC_WORKBUF_SIZE (2 * 1024 * 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 +#define W5_DEF_DEC_PIC_WIDTH 720U +#define W5_DEF_DEC_PIC_HEIGHT 480U +#define W5_MIN_DEC_PIC_8_WIDTH 8U +#define W5_MIN_DEC_PIC_8_HEIGHT 8U +#define W5_MIN_DEC_PIC_32_WIDTH 32U +#define W5_MIN_DEC_PIC_32_HEIGHT 32U +#define W5_MAX_DEC_PIC_WIDTH 8192U +#define W5_MAX_DEC_PIC_HEIGHT 4320U +#define W5_DEC_CODEC_STEP_WIDTH 1U +#define W5_DEC_CODEC_STEP_HEIGHT 1U +#define W5_DEC_RAW_STEP_WIDTH 32U +#define W5_DEC_RAW_STEP_HEIGHT 16U + +#define W5_DEF_ENC_PIC_WIDTH 416U +#define W5_DEF_ENC_PIC_HEIGHT 240U +#define W5_MIN_ENC_PIC_WIDTH 256U +#define W5_MIN_ENC_PIC_HEIGHT 128U +#define W5_MAX_ENC_PIC_WIDTH 8192U +#define W5_MAX_ENC_PIC_HEIGHT 8192U +#define W5_ENC_CODEC_STEP_WIDTH 8U +#define W5_ENC_CODEC_STEP_HEIGHT 8U +#define W5_ENC_RAW_STEP_WIDTH 32U +#define W5_ENC_RAW_STEP_HEIGHT 16U // application specific configuration #define VPU_ENC_TIMEOUT 60000 @@ -49,17 +70,21 @@ /************************************************************************/ #define VLC_BUF_NUM (2) -#define COMMAND_QUEUE_DEPTH (2) +#define WAVE521_COMMAND_QUEUE_DEPTH (2) +#define WAVE515_COMMAND_QUEUE_DEPTH (4) #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 WAVE521_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) +#define WAVE515_MAX_CODE_BUF_SIZE (1024 * 1024) #define WAVE5_TEMPBUF_SIZE (1024 * 1024) -#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) +#define WAVE521_SIZE_COMMON (WAVE521_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) +#define WAVE515_ONE_TASKBUF_SIZE (8 * 1024 * 1024) +#define WAVE515_SIZE_COMMON (WAVE515_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE + \ + WAVE515_COMMAND_QUEUE_DEPTH * WAVE515_ONE_TASKBUF_SIZE) //=====4. VPU REPORT MEMORY ======================// diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h index 063028eccd3b..2caab356f3e1 100644 --- a/drivers/media/platform/chips-media/wave5/wave5.h +++ b/drivers/media/platform/chips-media/wave5/wave5.h @@ -22,6 +22,12 @@ */ #define BSOPTION_ENABLE_EXPLICIT_END BIT(0) #define BSOPTION_HIGHLIGHT_STREAM_END BIT(1) +/* + * When RD_PTR_VALID_FLAG is 0 Wave515 ignores RD_PTR value and starts to + * decode from the access unit end position of the last decoded picture in + * bitstream buffer. + */ +#define BSOPTION_RD_PTR_VALID_FLAG BIT(31) /* * Currently the driver only supports hardware with little endian but for source @@ -56,6 +62,9 @@ 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_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code, + 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); diff --git a/drivers/media/platform/imagination/Kconfig b/drivers/media/platform/imagination/Kconfig new file mode 100644 index 000000000000..a302c955483d --- /dev/null +++ b/drivers/media/platform/imagination/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_E5010_JPEG_ENC + tristate "Imagination E5010 JPEG Encoder Driver" + depends on VIDEO_DEV + depends on ARCH_K3 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select V4L2_JPEG_HELPER + help + This is a video4linux2 M2M driver for Imagination E5010 JPEG encoder, + which supports JPEG and MJPEG baseline encoding of YUV422 and YUV420 + semiplanar video formats, with resolution ranging from 64x64 to 8K x 8K + pixels. The module will be named as e5010_jpeg_enc. diff --git a/drivers/media/platform/imagination/Makefile b/drivers/media/platform/imagination/Makefile new file mode 100644 index 000000000000..d45b85b88575 --- /dev/null +++ b/drivers/media/platform/imagination/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +e5010_jpeg_enc-objs := e5010-jpeg-enc-hw.o e5010-jpeg-enc.o +obj-$(CONFIG_VIDEO_E5010_JPEG_ENC) += e5010_jpeg_enc.o diff --git a/drivers/media/platform/imagination/e5010-core-regs.h b/drivers/media/platform/imagination/e5010-core-regs.h new file mode 100644 index 000000000000..aaec498fe83f --- /dev/null +++ b/drivers/media/platform/imagination/e5010-core-regs.h @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#ifndef _E5010_CORE_REGS_H +#define _E5010_CORE_REGS_H + +#define JASPER_CORE_ID_OFFSET (0x0000) +#define JASPER_CORE_ID_CR_GROUP_ID_MASK (0xFF000000) +#define JASPER_CORE_ID_CR_GROUP_ID_SHIFT (24) +#define JASPER_CORE_ID_CR_CORE_ID_MASK (0x00FF0000) +#define JASPER_CORE_ID_CR_CORE_ID_SHIFT (16) +#define JASPER_CORE_ID_CR_UNIQUE_NUM_MASK (0x0000FFF8) +#define JASPER_CORE_ID_CR_UNIQUE_NUM_SHIFT (3) +#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_MASK (0x00000007) +#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_SHIFT (0) + +#define JASPER_CORE_REV_OFFSET (0x0004) +#define JASPER_CORE_REV_CR_JASPER_DESIGNER_MASK (0xFF000000) +#define JASPER_CORE_REV_CR_JASPER_DESIGNER_SHIFT (24) +#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_MASK (0x00FF0000) +#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_SHIFT (16) +#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_MASK (0x0000FF00) +#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_SHIFT (8) +#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_MASK (0x000000FF) +#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_SHIFT (0) + +#define JASPER_INTERRUPT_MASK_OFFSET (0x0008) +#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK (0x00000002) +#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT (1) +#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK (0x00000001) +#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT (0) + +#define JASPER_INTERRUPT_STATUS_OFFSET (0x000C) +#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK (0x00000002) +#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_SHIFT (1) +#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK (0x00000001) +#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_SHIFT (0) + +#define JASPER_INTERRUPT_CLEAR_OFFSET (0x0010) +#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK (0x00000002) +#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT (1) +#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK (0x00000001) +#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT (0) + +#define JASPER_CLK_CONTROL_OFFSET (0x0014) +#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK (0x00000002) +#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT (1) +#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK (0x00000001) +#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT (0) + +#define JASPER_CLK_STATUS_OFFSET (0x0018) +#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_MASK (0x00000001) +#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_SHIFT (0) + +#define JASPER_RESET_OFFSET (0x001C) +#define JASPER_RESET_CR_SYS_RESET_MASK (0x00000002) +#define JASPER_RESET_CR_SYS_RESET_SHIFT (1) +#define JASPER_RESET_CR_CORE_RESET_MASK (0x00000001) +#define JASPER_RESET_CR_CORE_RESET_SHIFT (0) + +#define JASPER_CORE_CTRL_OFFSET (0x0020) +#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK (0x00000001) +#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT (0) + +#define JASPER_STATUS_OFFSET (0x0024) +#define JASPER_STATUS_CR_FLUSH_MODE_MASK (0x00000002) +#define JASPER_STATUS_CR_FLUSH_MODE_SHIFT (1) +#define JASPER_STATUS_CR_JASPER_BUSY_MASK (0x00000001) +#define JASPER_STATUS_CR_JASPER_BUSY_SHIFT (0) + +#define JASPER_CRC_CLEAR_OFFSET (0x0028) +#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_MASK (0x00000001) +#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_SHIFT (0) +#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_MASK (0x00000002) +#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_SHIFT (1) +#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_MASK (0x00000004) +#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_SHIFT (2) +#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_MASK (0x00000008) +#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_SHIFT (3) +#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_MASK (0x00000010) +#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_SHIFT (4) +#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_MASK (0x00000020) +#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_SHIFT (5) + +#define JASPER_INPUT_CTRL0_OFFSET (0x002C) +#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK (0x01000000) +#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT (24) +#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK (0x00030000) +#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT (16) +#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK (0x00000004) +#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT (2) + +#define JASPER_INPUT_CTRL1_OFFSET (0x0030) +#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK (0x1FC00000) +#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT (22) +#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK (0x00001FC0) +#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT (6) + +#define JASPER_MMU_CTRL_OFFSET (0x0034) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_MASK (0x00000002) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_SHIFT (1) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_MASK (0x00000001) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_SHIFT (0) + +#define JASPER_IMAGE_SIZE_OFFSET (0x0038) +#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK (0x1FFF0000) +#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT (16) +#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK (0x00001FFF) +#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT (0) + +#define INPUT_LUMA_BASE_OFFSET (0x003C) +#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK (0xFFFFFFC0) +#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_SHIFT (6) + +#define INPUT_CHROMA_BASE_OFFSET (0x0040) +#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK (0xFFFFFFC0) +#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_SHIFT (6) + +#define JASPER_OUTPUT_BASE_OFFSET (0x0044) +#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT (0) + +#define JASPER_OUTPUT_SIZE_OFFSET (0x0048) +#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_SHIFT (0) +#define JASPER_OUTPUT_MAX_SIZE_OFFSET (0x004C) +#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET (0x0050) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE1_OFFSET (0x0054) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE2_OFFSET (0x0058) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE3_OFFSET (0x005C) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE4_OFFSET (0x0060) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE5_OFFSET (0x0064) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_OFFSET (0x0068) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_OFFSET (0x006C) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_OFFSET (0x0070) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_OFFSET (0x0074) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_OFFSET (0x0078) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_OFFSET (0x007C) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_OFFSET (0x0080) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_OFFSET (0x0084) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_OFFSET (0x0088) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_OFFSET (0x008C) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET (0x0090) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_OFFSET (0x0094) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_OFFSET (0x0098) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_OFFSET (0x009C) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_OFFSET (0x00A0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_OFFSET (0x00A4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_OFFSET (0x00A8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_OFFSET (0x00AC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_OFFSET (0x00B0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_OFFSET (0x00B4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_OFFSET (0x00B8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_OFFSET (0x00BC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_OFFSET (0x00C0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_OFFSET (0x00C4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_OFFSET (0x00C8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_OFFSET (0x00CC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_SHIFT (0) + +#define JASPER_CRC_CTRL_OFFSET (0x00D0) +#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK (0x00000001) +#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT (0) + +#define JASPER_FRONT_END_CRC_OFFSET (0x00D4) +#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_SHIFT (0) + +#define JASPER_DCT_CRC_OFFSET (0x00D8) +#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_SHIFT (0) + +#define JASPER_ZZ_CRC_OFFSET (0x00DC) +#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_SHIFT (0) + +#define JASPER_QUANT_CRC_OFFSET (0x00E0) +#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_SHIFT (0) + +#define JASPER_ENTROPY_ENCODER_CRC_OFFSET (0x00E4) +#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_SHIFT (0) + +#define JASPER_PACKING_BUFFER_DATA_CRC_OFFSET (0x00E8) +#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_SHIFT (0) + +#define JASPER_PACKING_BUFFER_ADDR_CRC_OFFSET (0x00EC) +#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_MASK (0xFFFFFFFF) +#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_SHIFT (0) + +#define JASPER_CORE_BYTE_SIZE (0x00F0) + +#endif diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c new file mode 100644 index 000000000000..56d5941020fa --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/dev_printk.h> +#include "e5010-jpeg-enc-hw.h" + +static void write_reg_field(void __iomem *base, unsigned int offset, u32 mask, + unsigned int shift, u32 value) +{ + u32 reg; + + value <<= shift; + if (mask != 0xffffffff) { + reg = readl(base + offset); + value = (value & mask) | (reg & ~mask); + } + writel(value, (base + offset)); +} + +static int write_reg_field_not_busy(void __iomem *jasper_base, void __iomem *wr_base, + unsigned int offset, u32 mask, unsigned int shift, + u32 value) +{ + int ret; + u32 val; + + ret = readl_poll_timeout_atomic(jasper_base + JASPER_STATUS_OFFSET, val, + (val & JASPER_STATUS_CR_JASPER_BUSY_MASK) == 0, + 2000, 50000); + if (ret) + return ret; + + write_reg_field(wr_base, offset, mask, shift, value); + + return 0; +} + +void e5010_reset(struct device *dev, void __iomem *core_base, void __iomem *mmu_base) +{ + int ret = 0; + u32 val; + + write_reg_field(core_base, JASPER_RESET_OFFSET, + JASPER_RESET_CR_CORE_RESET_MASK, + JASPER_RESET_CR_CORE_RESET_SHIFT, 1); + + write_reg_field(mmu_base, MMU_MMU_CONTROL1_OFFSET, + MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK, + MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT, 1); + + ret = readl_poll_timeout_atomic(mmu_base + MMU_MMU_CONTROL1_OFFSET, val, + (val & MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK) == 0, + 2000, 50000); + if (ret) + dev_warn(dev, "MMU soft reset timed out, forcing system soft reset\n"); + + write_reg_field(core_base, JASPER_RESET_OFFSET, + JASPER_RESET_CR_SYS_RESET_MASK, + JASPER_RESET_CR_SYS_RESET_SHIFT, 1); +} + +void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable) +{ + /* Bypass MMU */ + write_reg_field(mmu_base, + MMU_MMU_ADDRESS_CONTROL_OFFSET, + MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK, + MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT, + enable); +} + +int e5010_hw_enable_output_address_error_irq(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INTERRUPT_MASK_OFFSET, + JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK, + JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT, + enable); +} + +bool e5010_hw_pic_done_irq(void __iomem *core_base) +{ + u32 reg; + + reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET); + return reg & JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK; +} + +bool e5010_hw_output_address_irq(void __iomem *core_base) +{ + u32 reg; + + reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET); + return reg & JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK; +} + +int e5010_hw_enable_picture_done_irq(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INTERRUPT_MASK_OFFSET, + JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK, + JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT, + enable); +} + +int e5010_hw_enable_auto_clock_gating(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CLK_CONTROL_OFFSET, + JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK, + JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT, + enable); +} + +int e5010_hw_enable_manual_clock_gating(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CLK_CONTROL_OFFSET, + JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK, + JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT, 0); +} + +int e5010_hw_enable_crc_check(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CRC_CTRL_OFFSET, + JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK, + JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT, enable); +} + +int e5010_hw_set_input_source_to_memory(void __iomem *core_base, u32 set) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT, set); +} + +int e5010_hw_set_input_luma_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + INPUT_LUMA_BASE_OFFSET, + INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK, 0, val); +} + +int e5010_hw_set_input_chroma_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + INPUT_CHROMA_BASE_OFFSET, + INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK, 0, val); +} + +int e5010_hw_set_output_base_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_OUTPUT_BASE_OFFSET, + JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK, + JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT, val); +} + +int e5010_hw_set_horizontal_size(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_IMAGE_SIZE_OFFSET, + JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK, + JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT, + val); +} + +int e5010_hw_set_vertical_size(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_IMAGE_SIZE_OFFSET, + JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK, + JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT, + val); +} + +int e5010_hw_set_luma_stride(void __iomem *core_base, u32 bytesperline) +{ + u32 val = bytesperline / 64; + + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL1_OFFSET, + JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK, + JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT, + val); +} + +int e5010_hw_set_chroma_stride(void __iomem *core_base, u32 bytesperline) +{ + u32 val = bytesperline / 64; + + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL1_OFFSET, + JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK, + JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT, + val); +} + +int e5010_hw_set_input_subsampling(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT, + val); +} + +int e5010_hw_set_chroma_order(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT, + val); +} + +void e5010_hw_set_output_max_size(void __iomem *core_base, u32 val) +{ + write_reg_field(core_base, JASPER_OUTPUT_MAX_SIZE_OFFSET, + JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK, + JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT, + val); +} + +int e5010_hw_set_qpvalue(void __iomem *core_base, u32 offset, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, offset, 0xffffffff, 0, val); +} + +void e5010_hw_clear_output_error(void __iomem *core_base, u32 clear) +{ + /* Make sure interrupts are clear */ + write_reg_field(core_base, JASPER_INTERRUPT_CLEAR_OFFSET, + JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK, + JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT, clear); +} + +void e5010_hw_clear_picture_done(void __iomem *core_base, u32 clear) +{ + write_reg_field(core_base, + JASPER_INTERRUPT_CLEAR_OFFSET, + JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK, + JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT, clear); +} + +int e5010_hw_get_output_size(void __iomem *core_base) +{ + return readl(core_base + JASPER_OUTPUT_SIZE_OFFSET); +} + +void e5010_hw_encode_start(void __iomem *core_base, u32 start) +{ + write_reg_field(core_base, JASPER_CORE_CTRL_OFFSET, + JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK, + JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT, start); +} diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h new file mode 100644 index 000000000000..781d353c3226 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#ifndef _E5010_JPEG_ENC_HW_H +#define _E5010_JPEG_ENC_HW_H + +#include "e5010-core-regs.h" +#include "e5010-mmu-regs.h" + +int e5010_hw_enable_output_address_error_irq(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_picture_done_irq(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_auto_clock_gating(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_manual_clock_gating(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_crc_check(void __iomem *core_offset, u32 enable); +int e5010_hw_set_input_source_to_memory(void __iomem *core_offset, u32 set); +int e5010_hw_set_input_luma_addr(void __iomem *core_offset, u32 val); +int e5010_hw_set_input_chroma_addr(void __iomem *core_offset, u32 val); +int e5010_hw_set_output_base_addr(void __iomem *core_offset, u32 val); +int e5010_hw_get_output_size(void __iomem *core_offset); +int e5010_hw_set_horizontal_size(void __iomem *core_offset, u32 val); +int e5010_hw_set_vertical_size(void __iomem *core_offset, u32 val); +int e5010_hw_set_luma_stride(void __iomem *core_offset, u32 bytesperline); +int e5010_hw_set_chroma_stride(void __iomem *core_offset, u32 bytesperline); +int e5010_hw_set_input_subsampling(void __iomem *core_offset, u32 val); +int e5010_hw_set_chroma_order(void __iomem *core_offset, u32 val); +int e5010_hw_set_qpvalue(void __iomem *core_offset, u32 offset, u32 value); +void e5010_reset(struct device *dev, void __iomem *core_offset, void __iomem *mmu_offset); +void e5010_hw_set_output_max_size(void __iomem *core_offset, u32 val); +void e5010_hw_clear_picture_done(void __iomem *core_offset, u32 clear); +void e5010_hw_encode_start(void __iomem *core_offset, u32 start); +void e5010_hw_clear_output_error(void __iomem *core_offset, u32 clear); +void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable); +bool e5010_hw_pic_done_irq(void __iomem *core_base); +bool e5010_hw_output_address_irq(void __iomem *core_base); +#endif diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c new file mode 100644 index 000000000000..c194f830577f --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c @@ -0,0 +1,1630 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Imagination E5010 JPEG Encoder driver. + * + * TODO: Add MMU and memory tiling support + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <media/jpeg.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-jpeg.h> +#include <media/v4l2-rect.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> +#include "e5010-jpeg-enc.h" +#include "e5010-jpeg-enc-hw.h" + +/* forward declarations */ +static const struct of_device_id e5010_of_match[]; + +static const struct v4l2_file_operations e5010_fops; + +static const struct v4l2_ioctl_ops e5010_ioctl_ops; + +static const struct vb2_ops e5010_video_ops; + +static const struct v4l2_m2m_ops e5010_m2m_ops; + +static struct e5010_fmt e5010_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_JPEG, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .subsampling = 0, + .chroma_order = 0, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 16, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, +}; + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "debug level"); + +#define dprintk(dev, lvl, fmt, arg...) \ + v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg) + +static const struct v4l2_event e5010_eos_event = { + .type = V4L2_EVENT_EOS +}; + +static const char *type_name(enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return "Output"; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return "Capture"; + default: + return "Invalid"; + } +} + +static struct e5010_q_data *get_queue(struct e5010_context *ctx, enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->out_queue : &ctx->cap_queue; +} + +static void calculate_qp_tables(struct e5010_context *ctx) +{ + long long luminosity, contrast; + int quality, i; + + quality = 50 - ctx->quality; + + luminosity = LUMINOSITY * quality / 50; + contrast = CONTRAST * quality / 50; + + if (quality > 0) { + luminosity *= INCREASE; + contrast *= INCREASE; + } + + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) { + long long delta = v4l2_jpeg_ref_table_chroma_qt[i] * contrast + luminosity; + int val = (int)(v4l2_jpeg_ref_table_chroma_qt[i] + delta); + + clamp(val, 1, 255); + ctx->chroma_qp[i] = quality == -50 ? 1 : val; + + delta = v4l2_jpeg_ref_table_luma_qt[i] * contrast + luminosity; + val = (int)(v4l2_jpeg_ref_table_luma_qt[i] + delta); + clamp(val, 1, 255); + ctx->luma_qp[i] = quality == -50 ? 1 : val; + } + + ctx->update_qp = true; +} + +static int update_qp_tables(struct e5010_context *ctx) +{ + struct e5010_dev *e5010 = ctx->e5010; + int i, ret = 0; + u32 lvalue, cvalue; + + lvalue = 0; + cvalue = 0; + + for (i = 0; i < QP_TABLE_SIZE; i++) { + lvalue |= ctx->luma_qp[i] << (8 * (i % 4)); + cvalue |= ctx->chroma_qp[i] << (8 * (i % 4)); + if (i % 4 == 3) { + ret |= e5010_hw_set_qpvalue(e5010->core_base, + JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), + lvalue); + ret |= e5010_hw_set_qpvalue(e5010->core_base, + JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), + cvalue); + lvalue = 0; + cvalue = 0; + } + } + + return ret; +} + +static int e5010_set_input_subsampling(void __iomem *core_base, int subsampling) +{ + switch (subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_420); + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_422); + default: + return -EINVAL; + }; +} + +static int e5010_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + strscpy(cap->driver, E5010_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, E5010_MODULE_NAME, sizeof(cap->card)); + + return 0; +} + +static struct e5010_fmt *find_format(struct v4l2_format *f) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { + if (e5010_formats[i].fourcc == f->fmt.pix_mp.pixelformat && + e5010_formats[i].type == f->type) + return &e5010_formats[i]; + } + + return NULL; +} + +static int e5010_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + int i, index = 0; + struct e5010_fmt *fmt = NULL; + struct e5010_context *ctx = file->private_data; + + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) { + v4l2_err(&ctx->e5010->v4l2_dev, "ENUMFMT with Invalid type: %d\n", f->type); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { + if (e5010_formats[i].type == f->type) { + if (index == f->index) { + fmt = &e5010_formats[i]; + break; + } + index++; + } + } + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static int e5010_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + int i; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + + queue = get_queue(ctx, f->type); + + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->pixelformat = queue->fmt->fourcc; + pix_mp->width = queue->width_adjusted; + pix_mp->height = queue->height_adjusted; + pix_mp->num_planes = queue->fmt->num_planes; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; + + for (i = 0; i < queue->fmt->num_planes; i++) { + plane_fmt[i].sizeimage = queue->sizeimage[i]; + plane_fmt[i].bytesperline = queue->bytesperline[i]; + } + + } else { + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + plane_fmt[0].bytesperline = 0; + plane_fmt[0].sizeimage = queue->sizeimage[0]; + } + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + + return 0; +} + +static int e5010_jpeg_try_fmt(struct v4l2_format *f, struct e5010_context *ctx) +{ + struct e5010_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + + fmt = find_format(f); + if (!fmt) { + if (V4L2_TYPE_IS_OUTPUT(f->type)) + pix_mp->pixelformat = V4L2_PIX_FMT_NV12; + else + pix_mp->pixelformat = V4L2_PIX_FMT_JPEG; + fmt = find_format(f); + if (!fmt) + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + if (!pix_mp->ycbcr_enc) + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + if (!pix_mp->quantization) + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + if (!pix_mp->xfer_func) + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + + } else { + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + plane_fmt[0].sizeimage = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; + plane_fmt[0].sizeimage += HEADER_SIZE; + plane_fmt[0].bytesperline = 0; + pix_mp->pixelformat = fmt->fourcc; + pix_mp->num_planes = fmt->num_planes; + } + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + + dprintk(ctx->e5010, 2, + "ctx: 0x%p: format type %s:, wxh: %dx%d (plane0 : %d bytes, plane1 : %d bytes),fmt: %c%c%c%c\n", + ctx, type_name(f->type), pix_mp->width, pix_mp->height, + plane_fmt[0].sizeimage, plane_fmt[1].sizeimage, + (fmt->fourcc & 0xff), + (fmt->fourcc >> 8) & 0xff, + (fmt->fourcc >> 16) & 0xff, + (fmt->fourcc >> 24) & 0xff); + + return 0; +} + +static int e5010_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + + return e5010_jpeg_try_fmt(f, ctx); +} + +static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + struct vb2_queue *vq; + int ret = 0, i = 0; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + struct e5010_q_data *queue; + struct e5010_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n"); + return -EBUSY; + } + + ret = e5010_jpeg_try_fmt(f, ctx); + if (ret) + return ret; + + fmt = find_format(f); + queue = get_queue(ctx, f->type); + + queue->fmt = fmt; + queue->width = pix_mp->width; + queue->height = pix_mp->height; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = plane_fmt[i].bytesperline; + queue->sizeimage[i] = plane_fmt[i].sizeimage; + } + queue->crop.left = 0; + queue->crop.top = 0; + queue->crop.width = queue->width; + queue->crop.height = queue->height; + } else { + queue->sizeimage[0] = plane_fmt[0].sizeimage; + queue->sizeimage[1] = 0; + queue->bytesperline[0] = 0; + queue->bytesperline[1] = 0; + } + + return 0; +} + +static int e5010_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_format f; + struct e5010_fmt *fmt; + + if (fsize->index != 0) + return -EINVAL; + + f.fmt.pix_mp.pixelformat = fsize->pixel_format; + if (f.fmt.pix_mp.pixelformat == V4L2_PIX_FMT_JPEG) + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + fmt = find_format(&f); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = fmt->frmsize; + fsize->reserved[0] = 0; + fsize->reserved[1] = 0; + + return 0; +} + +static int e5010_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = queue->width; + s->r.height = queue->height; + break; + case V4L2_SEL_TGT_CROP: + memcpy(&s->r, &queue->crop, sizeof(s->r)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + struct vb2_queue *vq; + struct v4l2_rect base_rect; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, s->type); + if (!vq) + return -EINVAL; + + if (vb2_is_streaming(vq)) + return -EBUSY; + + if (s->target != V4L2_SEL_TGT_CROP || + s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + base_rect.top = 0; + base_rect.left = 0; + base_rect.width = queue->width; + base_rect.height = queue->height; + + switch (s->flags) { + case 0: + s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_down(s->r.top, 2); + + if (s->r.left + s->r.width > queue->width) + s->r.width = round_down(s->r.width + s->r.left - queue->width, + queue->fmt->frmsize.step_width); + if (s->r.top + s->r.height > queue->height) + s->r.top = round_down(s->r.top + s->r.height - queue->height, 2); + break; + case V4L2_SEL_FLAG_GE: + s->r.width = round_up(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_up(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_up(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_up(s->r.top, 2); + break; + case V4L2_SEL_FLAG_LE: + s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_down(s->r.top, 2); + break; + case V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE: + if (!IS_ALIGNED(s->r.width, queue->fmt->frmsize.step_width) || + !IS_ALIGNED(s->r.height, queue->fmt->frmsize.step_height) || + !IS_ALIGNED(s->r.left, queue->fmt->frmsize.step_width) || + !IS_ALIGNED(s->r.top, 2)) + return -ERANGE; + break; + default: + return -EINVAL; + } + + if (!v4l2_rect_enclosed(&s->r, &base_rect)) + return -ERANGE; + + memcpy(&queue->crop, &s->r, sizeof(s->r)); + + if (!v4l2_rect_equal(&s->r, &base_rect)) + queue->crop_set = true; + + dprintk(ctx->e5010, 2, "ctx: 0x%p: crop rectangle: w: %d, h : %d, l : %d, t : %d\n", + ctx, queue->crop.width, queue->crop.height, queue->crop.left, queue->crop.top); + + return 0; +} + +static int e5010_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } + + return 0; +} + +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct e5010_context *ctx = priv; + struct e5010_dev *e5010 = ctx->e5010; + int ret = 0; + + /* src_vq */ + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct e5010_buffer); + src_vq->ops = &e5010_video_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &e5010->mutex; + src_vq->dev = e5010->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + /* dst_vq */ + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct e5010_buffer); + dst_vq->ops = &e5010_video_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &e5010->mutex; + dst_vq->dev = e5010->v4l2_dev.dev; + + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int e5010_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct e5010_context *ctx = + container_of(ctrl->handler, struct e5010_context, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->quality = ctrl->val; + calculate_qp_tables(ctx); + dprintk(ctx->e5010, 2, "ctx: 0x%p compression quality set to : %d\n", ctx, + ctx->quality); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops e5010_ctrl_ops = { + .s_ctrl = e5010_s_ctrl, +}; + +static void e5010_encode_ctrls(struct e5010_context *ctx) +{ + v4l2_ctrl_new_std(&ctx->ctrl_handler, &e5010_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 75); +} + +static int e5010_ctrls_setup(struct e5010_context *ctx) +{ + int err; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + e5010_encode_ctrls(ctx); + + if (ctx->ctrl_handler.error) { + err = ctx->ctrl_handler.error; + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + return err; + } + + err = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (err) + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + return err; +} + +static void e5010_jpeg_set_default_params(struct e5010_context *ctx) +{ + struct e5010_q_data *queue; + struct v4l2_format f; + struct e5010_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f.fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + int i = 0; + + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; + fmt = find_format(&f); + queue = &ctx->out_queue; + queue->fmt = fmt; + queue->width = DEFAULT_WIDTH; + queue->height = DEFAULT_HEIGHT; + pix_mp->width = queue->width; + pix_mp->height = queue->height; + queue->crop.left = 0; + queue->crop.top = 0; + queue->crop.width = queue->width; + queue->crop.height = queue->height; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = plane_fmt[i].bytesperline; + queue->sizeimage[i] = plane_fmt[i].sizeimage; + } + queue->width_adjusted = pix_mp->width; + queue->height_adjusted = pix_mp->height; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; + fmt = find_format(&f); + queue = &ctx->cap_queue; + queue->fmt = fmt; + queue->width = DEFAULT_WIDTH; + queue->height = DEFAULT_HEIGHT; + pix_mp->width = queue->width; + pix_mp->height = queue->height; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + queue->sizeimage[0] = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; + queue->sizeimage[0] += HEADER_SIZE; + queue->sizeimage[1] = 0; + queue->bytesperline[0] = 0; + queue->bytesperline[1] = 0; + queue->width_adjusted = pix_mp->width; + queue->height_adjusted = pix_mp->height; +} + +static int e5010_open(struct file *file) +{ + struct e5010_dev *e5010 = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + struct e5010_context *ctx; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (mutex_lock_interruptible(&e5010->mutex)) { + ret = -ERESTARTSYS; + goto free; + } + + v4l2_fh_init(&ctx->fh, vdev); + file->private_data = ctx; + v4l2_fh_add(&ctx->fh); + + ctx->e5010 = e5010; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(e5010->m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + v4l2_err(&e5010->v4l2_dev, "failed to init m2m ctx\n"); + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto exit; + } + + ret = e5010_ctrls_setup(ctx); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to setup e5010 jpeg controls\n"); + goto err_ctrls_setup; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + e5010_jpeg_set_default_params(ctx); + + dprintk(e5010, 1, "Created instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx); + + mutex_unlock(&e5010->mutex); + return 0; + +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +exit: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + mutex_unlock(&e5010->mutex); +free: + kfree(ctx); + return ret; +} + +static int e5010_release(struct file *file) +{ + struct e5010_dev *e5010 = video_drvdata(file); + struct e5010_context *ctx = file->private_data; + + dprintk(e5010, 1, "Releasing instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx); + mutex_lock(&e5010->mutex); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&e5010->mutex); + + return 0; +} + +static void header_write(struct e5010_context *ctx, u8 *addr, unsigned int *offset, + unsigned int no_bytes, unsigned long bits) +{ + u8 *w_addr = addr + *offset; + int i; + + if ((*offset + no_bytes) > HEADER_SIZE) { + v4l2_warn(&ctx->e5010->v4l2_dev, "%s: %s: %d: Problem writing header. %d > HEADER_SIZE %d\n", + __FILE__, __func__, __LINE__, *offset + no_bytes, HEADER_SIZE); + return; + } + + for (i = no_bytes - 1; i >= 0; i--) + *(w_addr++) = ((u8 *)&bits)[i]; + + *offset += no_bytes; +} + +static void encode_marker_segment(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + int i; + + header_write(ctx, buffer, offset, 2, START_OF_IMAGE); + header_write(ctx, buffer, offset, 2, DQT_MARKER); + header_write(ctx, buffer, offset, 3, LQPQ << 4); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, ctx->luma_qp[v4l2_jpeg_zigzag_scan_index[i]]); + + header_write(ctx, buffer, offset, 2, DQT_MARKER); + header_write(ctx, buffer, offset, 3, (LQPQ << 4) | 1); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, + ctx->chroma_qp[v4l2_jpeg_zigzag_scan_index[i]]); + + /* Huffman tables */ + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_DC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_DC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_dc_ht[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_AC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_AC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_ac_ht[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_DC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_DC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_dc_ht[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_AC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_AC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_ac_ht[i]); +} + +static void encode_frame_header(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + + header_write(ctx, buffer, offset, 2, SOF_BASELINE_DCT); + header_write(ctx, buffer, offset, 2, 8 + (3 * UC_NUM_COMP)); + header_write(ctx, buffer, offset, 1, PRECISION); + header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.height); + header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.width); + header_write(ctx, buffer, offset, 1, UC_NUM_COMP); + + /* Luma details */ + header_write(ctx, buffer, offset, 1, 1); + if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) + header_write(ctx, buffer, offset, 1, + HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_422)); + else + header_write(ctx, buffer, offset, 1, + HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_420)); + header_write(ctx, buffer, offset, 1, 0); + /* Chroma details */ + header_write(ctx, buffer, offset, 1, 2); + header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1); + header_write(ctx, buffer, offset, 1, 1); + header_write(ctx, buffer, offset, 1, 3); + header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1); + header_write(ctx, buffer, offset, 1, 1); +} + +static void jpg_encode_sos_header(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + int i; + + header_write(ctx, buffer, offset, 2, START_OF_SCAN); + header_write(ctx, buffer, offset, 2, 6 + (COMPONENTS_IN_SCAN << 1)); + header_write(ctx, buffer, offset, 1, COMPONENTS_IN_SCAN); + + for (i = 0; i < COMPONENTS_IN_SCAN; i++) { + header_write(ctx, buffer, offset, 1, i + 1); + if (i == 0) + header_write(ctx, buffer, offset, 1, 0); + else + header_write(ctx, buffer, offset, 1, 17); + } + + header_write(ctx, buffer, offset, 1, 0); + header_write(ctx, buffer, offset, 1, 63); + header_write(ctx, buffer, offset, 1, 0); +} + +static void write_header(struct e5010_context *ctx, void *addr) +{ + unsigned int offset = 0; + + encode_marker_segment(ctx, addr, &offset); + encode_frame_header(ctx, addr, &offset); + jpg_encode_sos_header(ctx, addr, &offset); +} + +static irqreturn_t e5010_irq(int irq, void *data) +{ + struct e5010_dev *e5010 = data; + struct e5010_context *ctx; + int output_size; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + bool pic_done, out_addr_err; + + spin_lock(&e5010->hw_lock); + pic_done = e5010_hw_pic_done_irq(e5010->core_base); + out_addr_err = e5010_hw_output_address_irq(e5010->core_base); + + if (!pic_done && !out_addr_err) { + spin_unlock(&e5010->hw_lock); + return IRQ_NONE; + } + + ctx = v4l2_m2m_get_curr_priv(e5010->m2m_dev); + if (WARN_ON(!ctx)) + goto job_unlock; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (!dst_buf || !src_buf) { + v4l2_err(&e5010->v4l2_dev, "ctx: 0x%p No source or destination buffer\n", ctx); + goto job_unlock; + } + + if (out_addr_err) { + e5010_hw_clear_output_error(e5010->core_base, 1); + v4l2_warn(&e5010->v4l2_dev, + "ctx: 0x%p Output bitstream size exceeded max size\n", ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_buf->planes[0].length); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx); + } + } + + if (pic_done) { + e5010_hw_clear_picture_done(e5010->core_base, 1); + dprintk(e5010, 3, "ctx: 0x%p Got output bitstream of size %d bytes\n", + ctx, readl(e5010->core_base + JASPER_OUTPUT_SIZE_OFFSET)); + + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx); + } + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + output_size = e5010_hw_get_output_size(e5010->core_base); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, output_size + HEADER_SIZE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + dprintk(e5010, 3, + "ctx: 0x%p frame done for dst_buf->sequence: %d src_buf->sequence: %d\n", + ctx, dst_buf->sequence, src_buf->sequence); + } + + v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx); + dprintk(e5010, 3, "ctx: 0x%p Finish job\n", ctx); + +job_unlock: + spin_unlock(&e5010->hw_lock); + return IRQ_HANDLED; +} + +static int e5010_init_device(struct e5010_dev *e5010) +{ + int ret = 0; + + /*TODO: Set MMU in bypass mode until support for the same is added in driver*/ + e5010_hw_bypass_mmu(e5010->mmu_base, 1); + + if (e5010_hw_enable_auto_clock_gating(e5010->core_base, 1)) + v4l2_warn(&e5010->v4l2_dev, "failed to enable auto clock gating\n"); + + if (e5010_hw_enable_manual_clock_gating(e5010->core_base, 0)) + v4l2_warn(&e5010->v4l2_dev, "failed to disable manual clock gating\n"); + + if (e5010_hw_enable_crc_check(e5010->core_base, 0)) + v4l2_warn(&e5010->v4l2_dev, "failed to disable CRC check\n"); + + if (e5010_hw_enable_output_address_error_irq(e5010->core_base, 1)) + v4l2_err(&e5010->v4l2_dev, "failed to enable Output Address Error interrupts\n"); + + ret = e5010_hw_set_input_source_to_memory(e5010->core_base, 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input source to memory\n"); + return ret; + } + + ret = e5010_hw_enable_picture_done_irq(e5010->core_base, 1); + if (ret) + v4l2_err(&e5010->v4l2_dev, "failed to enable Picture Done interrupts\n"); + + return ret; +} + +static int e5010_probe(struct platform_device *pdev) +{ + struct e5010_dev *e5010; + int irq, ret = 0; + struct device *dev = &pdev->dev; + + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "32-bit consistent DMA enable failed\n"); + + e5010 = devm_kzalloc(dev, sizeof(*e5010), GFP_KERNEL); + if (!e5010) + return -ENOMEM; + + platform_set_drvdata(pdev, e5010); + + e5010->dev = dev; + + mutex_init(&e5010->mutex); + spin_lock_init(&e5010->hw_lock); + + e5010->vdev = video_device_alloc(); + if (!e5010->vdev) { + dev_err(dev, "failed to allocate video device\n"); + return -ENOMEM; + } + + snprintf(e5010->vdev->name, sizeof(e5010->vdev->name), "%s", E5010_MODULE_NAME); + e5010->vdev->fops = &e5010_fops; + e5010->vdev->ioctl_ops = &e5010_ioctl_ops; + e5010->vdev->minor = -1; + e5010->vdev->release = video_device_release; + e5010->vdev->vfl_dir = VFL_DIR_M2M; + e5010->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + e5010->vdev->v4l2_dev = &e5010->v4l2_dev; + e5010->vdev->lock = &e5010->mutex; + + ret = v4l2_device_register(dev, &e5010->v4l2_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to register v4l2 device\n"); + + e5010->m2m_dev = v4l2_m2m_init(&e5010_m2m_ops); + if (IS_ERR(e5010->m2m_dev)) { + ret = PTR_ERR(e5010->m2m_dev); + e5010->m2m_dev = NULL; + dev_err_probe(dev, ret, "failed to init mem2mem device\n"); + goto fail_after_v4l2_register; + } + + video_set_drvdata(e5010->vdev, e5010); + + e5010->core_base = devm_platform_ioremap_resource_byname(pdev, "core"); + if (IS_ERR(e5010->core_base)) { + ret = PTR_ERR(e5010->core_base); + dev_err_probe(dev, ret, "Missing 'core' resources area\n"); + goto fail_after_v4l2_register; + } + + e5010->mmu_base = devm_platform_ioremap_resource_byname(pdev, "mmu"); + if (IS_ERR(e5010->mmu_base)) { + ret = PTR_ERR(e5010->mmu_base); + dev_err_probe(dev, ret, "Missing 'mmu' resources area\n"); + goto fail_after_v4l2_register; + } + + e5010->last_context_run = NULL; + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, e5010_irq, 0, + E5010_MODULE_NAME, e5010); + if (ret) { + dev_err_probe(dev, ret, "failed to register IRQ %d\n", irq); + goto fail_after_v4l2_register; + } + + e5010->clk = devm_clk_get(dev, NULL); + if (IS_ERR(e5010->clk)) { + ret = PTR_ERR(e5010->clk); + dev_err_probe(dev, ret, "failed to get clock\n"); + goto fail_after_v4l2_register; + } + + pm_runtime_enable(dev); + + ret = video_register_device(e5010->vdev, VFL_TYPE_VIDEO, 0); + if (ret) { + dev_err_probe(dev, ret, "failed to register video device\n"); + goto fail_after_video_register_device; + } + + v4l2_info(&e5010->v4l2_dev, "Device registered as /dev/video%d\n", + e5010->vdev->num); + + return 0; + +fail_after_video_register_device: + v4l2_m2m_release(e5010->m2m_dev); +fail_after_v4l2_register: + v4l2_device_unregister(&e5010->v4l2_dev); + return ret; +} + +static void e5010_remove(struct platform_device *pdev) +{ + struct e5010_dev *e5010 = platform_get_drvdata(pdev); + + pm_runtime_disable(e5010->dev); + video_unregister_device(e5010->vdev); + v4l2_m2m_release(e5010->m2m_dev); + v4l2_device_unregister(&e5010->v4l2_dev); +} + +static void e5010_vb2_buffers_return(struct vb2_queue *q, enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vbuf; + struct e5010_context *ctx = vb2_get_drv_priv(q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) { + dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n", + ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index); + v4l2_m2m_buf_done(vbuf, state); + } + } else { + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) { + dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n", + ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(vbuf, state); + } + } +} + +static int e5010_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vq); + struct e5010_q_data *queue; + int i; + + queue = get_queue(ctx, vq->type); + + if (*nplanes) { + if (*nplanes != queue->fmt->num_planes) + return -EINVAL; + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < queue->sizeimage[i]) + return -EINVAL; + } + return 0; + } + + *nplanes = queue->fmt->num_planes; + for (i = 0; i < *nplanes; i++) + sizes[i] = queue->sizeimage[i]; + + dprintk(ctx->e5010, 2, + "ctx: 0x%p, type %s, buffer(s): %d, planes %d, plane1: bytes %d plane2: %d bytes\n", + ctx, type_name(vq->type), *nbuffers, *nplanes, sizes[0], sizes[1]); + + return 0; +} + +static void e5010_buf_finish(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + void *d_addr; + + if (vb->state != VB2_BUF_STATE_DONE || V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + return; + + d_addr = vb2_plane_vaddr(vb, 0); + write_header(ctx, d_addr); +} + +static int e5010_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (vbuf->field != V4L2_FIELD_NONE) + dprintk(ctx->e5010, 1, "ctx: 0x%p, field isn't supported\n", ctx); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int e5010_buf_prepare(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct e5010_q_data *queue; + int i; + + vbuf->field = V4L2_FIELD_NONE; + + queue = get_queue(ctx, vb->vb2_queue->type); + + for (i = 0; i < queue->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < (unsigned long)queue->sizeimage[i]) { + v4l2_err(&ctx->e5010->v4l2_dev, "plane %d too small (%lu < %lu)", i, + vb2_plane_size(vb, i), (unsigned long)queue->sizeimage[i]); + + return -EINVAL; + } + } + + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { + vb2_set_plane_payload(vb, 0, 0); + vb2_set_plane_payload(vb, 1, 0); + } + + return 0; +} + +static void e5010_buf_queue(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + vb2_is_streaming(vb->vb2_queue) && + v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { + struct e5010_q_data *queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + vbuf->sequence = queue->sequence++; + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + return; + } + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int e5010_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *cmd) +{ + struct e5010_context *ctx = file->private_data; + int ret; + struct vb2_queue *cap_vq; + + cap_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, &ctx->fh, cmd); + if (ret < 0) + return ret; + + if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) || + !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) + return 0; + + ret = v4l2_m2m_ioctl_encoder_cmd(file, &ctx->fh, cmd); + if (ret < 0) + return ret; + + if (cmd->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + + if (cmd->cmd == V4L2_ENC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(cap_vq); + + return 0; +} + +static int e5010_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct e5010_context *ctx = vb2_get_drv_priv(q); + int ret; + + struct e5010_q_data *queue = get_queue(ctx, q->type); + + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); + queue->sequence = 0; + + ret = pm_runtime_resume_and_get(ctx->e5010->dev); + if (ret < 0) { + v4l2_err(&ctx->e5010->v4l2_dev, "failed to power up jpeg\n"); + goto fail; + } + + ret = e5010_init_device(ctx->e5010); + if (ret) { + v4l2_err(&ctx->e5010->v4l2_dev, "failed to Enable e5010 device\n"); + goto fail; + } + + return 0; + +fail: + e5010_vb2_buffers_return(q, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void e5010_stop_streaming(struct vb2_queue *q) +{ + struct e5010_context *ctx = vb2_get_drv_priv(q); + + e5010_vb2_buffers_return(q, VB2_BUF_STATE_ERROR); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) { + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + } + + pm_runtime_put_sync(ctx->e5010->dev); +} + +static void e5010_device_run(void *priv) +{ + struct e5010_context *ctx = priv; + struct e5010_dev *e5010 = ctx->e5010; + struct vb2_v4l2_buffer *s_vb, *d_vb; + u32 reg = 0; + int ret = 0, luma_crop_offset = 0, chroma_crop_offset = 0; + unsigned long flags; + int num_planes = ctx->out_queue.fmt->num_planes; + + spin_lock_irqsave(&e5010->hw_lock, flags); + s_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + WARN_ON(!s_vb); + d_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + WARN_ON(!d_vb); + if (!s_vb || !d_vb) + goto no_ready_buf_err; + + s_vb->sequence = ctx->out_queue.sequence++; + d_vb->sequence = ctx->cap_queue.sequence++; + + v4l2_m2m_buf_copy_metadata(s_vb, d_vb, false); + + if (ctx != e5010->last_context_run || ctx->update_qp) { + dprintk(e5010, 1, "ctx updated: 0x%p -> 0x%p, updating qp tables\n", + e5010->last_context_run, ctx); + ret = update_qp_tables(ctx); + } + + if (ret) { + ctx->update_qp = true; + v4l2_err(&e5010->v4l2_dev, "failed to update QP tables\n"); + goto device_busy_err; + } else { + e5010->last_context_run = ctx; + ctx->update_qp = false; + } + + /* Set I/O Buffer addresses */ + reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0); + + if (ctx->out_queue.crop_set) { + luma_crop_offset = ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top + + ctx->out_queue.crop.left; + + if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) { + chroma_crop_offset = + ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top + + ctx->out_queue.crop.left; + } else { + chroma_crop_offset = + ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top / 2 + + ctx->out_queue.crop.left; + } + + dprintk(e5010, 1, "Luma crop offset : %x, chroma crop offset : %x\n", + luma_crop_offset, chroma_crop_offset); + } + + ret = e5010_hw_set_input_luma_addr(e5010->core_base, reg + luma_crop_offset); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set input luma address\n"); + goto device_busy_err; + } + + if (num_planes == 1) + reg += (ctx->out_queue.bytesperline[0]) * (ctx->out_queue.height); + else + reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 1); + + dprintk(e5010, 3, + "ctx: 0x%p, luma_addr: 0x%x, chroma_addr: 0x%x, out_addr: 0x%x\n", + ctx, (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0) + luma_crop_offset, + reg + chroma_crop_offset, (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0)); + + dprintk(e5010, 3, + "ctx: 0x%p, buf indices: src_index: %d, dst_index: %d\n", + ctx, s_vb->vb2_buf.index, d_vb->vb2_buf.index); + + ret = e5010_hw_set_input_chroma_addr(e5010->core_base, reg + chroma_crop_offset); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set input chroma address\n"); + goto device_busy_err; + } + + reg = (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0); + reg += HEADER_SIZE; + ret = e5010_hw_set_output_base_addr(e5010->core_base, reg); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set output base address\n"); + goto device_busy_err; + } + + /* Set input settings */ + ret = e5010_hw_set_horizontal_size(e5010->core_base, ctx->out_queue.crop.width - 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input width\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_vertical_size(e5010->core_base, ctx->out_queue.crop.height - 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input width\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_luma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set luma stride\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_chroma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set chroma stride\n"); + goto device_busy_err; + } + + ret = e5010_set_input_subsampling(e5010->core_base, ctx->out_queue.fmt->subsampling); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input subsampling\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_chroma_order(e5010->core_base, ctx->out_queue.fmt->chroma_order); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set chroma order\n"); + goto device_busy_err; + } + + e5010_hw_set_output_max_size(e5010->core_base, d_vb->planes[0].length); + e5010_hw_encode_start(e5010->core_base, 1); + + spin_unlock_irqrestore(&e5010->hw_lock, flags); + + return; + +device_busy_err: + e5010_reset(e5010->dev, e5010->core_base, e5010->mmu_base); + +no_ready_buf_err: + if (s_vb) { + v4l2_m2m_src_buf_remove_by_buf(ctx->fh.m2m_ctx, s_vb); + v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_ERROR); + } + + if (d_vb) { + v4l2_m2m_dst_buf_remove_by_buf(ctx->fh.m2m_ctx, d_vb); + /* Payload set to 1 since 0 payload can trigger EOS */ + vb2_set_plane_payload(&d_vb->vb2_buf, 0, 1); + v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_ERROR); + } + v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx); + spin_unlock_irqrestore(&e5010->hw_lock, flags); +} + +#ifdef CONFIG_PM +static int e5010_runtime_resume(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(e5010->clk); + if (ret < 0) { + v4l2_err(&e5010->v4l2_dev, "failed to enable clock\n"); + return ret; + } + + return 0; +} + +static int e5010_runtime_suspend(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + + clk_disable_unprepare(e5010->clk); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int e5010_suspend(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + + v4l2_m2m_suspend(e5010->m2m_dev); + + return pm_runtime_force_suspend(dev); +} + +static int e5010_resume(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + + ret = e5010_init_device(e5010); + if (ret) { + dev_err(dev, "Failed to re-enable e5010 device\n"); + return ret; + } + + v4l2_m2m_resume(e5010->m2m_dev); + + return ret; +} +#endif + +static const struct dev_pm_ops e5010_pm_ops = { + SET_RUNTIME_PM_OPS(e5010_runtime_suspend, + e5010_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(e5010_suspend, e5010_resume) +}; + +static const struct v4l2_ioctl_ops e5010_ioctl_ops = { + .vidioc_querycap = e5010_querycap, + + .vidioc_enum_fmt_vid_cap = e5010_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = e5010_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = e5010_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = e5010_s_fmt, + + .vidioc_enum_fmt_vid_out = e5010_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = e5010_g_fmt, + .vidioc_try_fmt_vid_out_mplane = e5010_try_fmt, + .vidioc_s_fmt_vid_out_mplane = e5010_s_fmt, + + .vidioc_g_selection = e5010_g_selection, + .vidioc_s_selection = e5010_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + + .vidioc_subscribe_event = e5010_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = e5010_encoder_cmd, + + .vidioc_enum_framesizes = e5010_enum_framesizes, +}; + +static const struct vb2_ops e5010_video_ops = { + .queue_setup = e5010_queue_setup, + .buf_queue = e5010_buf_queue, + .buf_finish = e5010_buf_finish, + .buf_prepare = e5010_buf_prepare, + .buf_out_validate = e5010_buf_out_validate, + .start_streaming = e5010_start_streaming, + .stop_streaming = e5010_stop_streaming, +}; + +static const struct v4l2_file_operations e5010_fops = { + .owner = THIS_MODULE, + .open = e5010_open, + .release = e5010_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct v4l2_m2m_ops e5010_m2m_ops = { + .device_run = e5010_device_run, +}; + +static const struct of_device_id e5010_of_match[] = { + {.compatible = "img,e5010-jpeg-enc"}, { /* end */}, +}; +MODULE_DEVICE_TABLE(of, e5010_of_match); + +static struct platform_driver e5010_driver = { + .probe = e5010_probe, + .remove = e5010_remove, + .driver = { + .name = E5010_MODULE_NAME, + .of_match_table = e5010_of_match, + .pm = &e5010_pm_ops, + }, +}; +module_platform_driver(e5010_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Imagination E5010 JPEG encoder driver"); diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.h b/drivers/media/platform/imagination/e5010-jpeg-enc.h new file mode 100644 index 000000000000..71f49ead6898 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fh.h> + +#ifndef _E5010_JPEG_ENC_H +#define _E5010_JPEG_ENC_H + +#define MAX_PLANES 2 +#define HEADER_SIZE 0x025D +#define MIN_DIMENSION 64 +#define MAX_DIMENSION 8192 +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define E5010_MODULE_NAME "e5010" +#define JPEG_MAX_BYTES_PER_PIXEL 2 + +/* JPEG marker definitions */ +#define START_OF_IMAGE 0xFFD8 +#define SOF_BASELINE_DCT 0xFFC0 +#define END_OF_IMAGE 0xFFD9 +#define START_OF_SCAN 0xFFDA + +/* Definitions for the huffman table specification in the Marker segment */ +#define DHT_MARKER 0xFFC4 +#define LH_DC 0x001F +#define LH_AC 0x00B5 + +/* Definitions for the quantization table specification in the Marker segment */ +#define DQT_MARKER 0xFFDB +#define ACMAX 0x03FF +#define DCMAX 0x07FF + +/* Length and precision of the quantization table parameters */ +#define LQPQ 0x00430 +#define QMAX 255 + +/* Misc JPEG header definitions */ +#define UC_NUM_COMP 3 +#define PRECISION 8 +#define HORZ_SAMPLING_FACTOR (2 << 4) +#define VERT_SAMPLING_FACTOR_422 1 +#define VERT_SAMPLING_FACTOR_420 2 +#define COMPONENTS_IN_SCAN 3 +#define PELS_IN_BLOCK 64 + +/* Used for Qp table generation */ +#define LUMINOSITY 10 +#define CONTRAST 1 +#define INCREASE 2 +#define QP_TABLE_SIZE (8 * 8) +#define QP_TABLE_FIELD_OFFSET 0x04 + +/* + * vb2 queue structure + * contains queue data information + * + * @fmt: format info + * @width: frame width + * @height: frame height + * @bytesperline: bytes per line in memory + * @size_image: image size in memory + */ +struct e5010_q_data { + struct e5010_fmt *fmt; + u32 width; + u32 height; + u32 width_adjusted; + u32 height_adjusted; + u32 sizeimage[MAX_PLANES]; + u32 bytesperline[MAX_PLANES]; + u32 sequence; + struct v4l2_rect crop; + bool crop_set; +}; + +/* + * Driver device structure + * Holds all memory handles and global parameters + * Shared by all instances + */ +struct e5010_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct video_device *vdev; + void __iomem *core_base; + void __iomem *mmu_base; + struct clk *clk; + struct e5010_context *last_context_run; + /* Protect access to device data */ + struct mutex mutex; + /* Protect access to hardware*/ + spinlock_t hw_lock; +}; + +/* + * Driver context structure + * One of these exists for every m2m context + * Holds context specific data + */ +struct e5010_context { + struct v4l2_fh fh; + struct e5010_dev *e5010; + struct e5010_q_data out_queue; + struct e5010_q_data cap_queue; + int quality; + bool update_qp; + struct v4l2_ctrl_handler ctrl_handler; + u8 luma_qp[QP_TABLE_SIZE]; + u8 chroma_qp[QP_TABLE_SIZE]; +}; + +/* + * Buffer structure + * Contains info for all buffers + */ +struct e5010_buffer { + struct v4l2_m2m_buffer buffer; +}; + +enum { + CHROMA_ORDER_CB_CR = 0, //UV ordering + CHROMA_ORDER_CR_CB = 1, //VU ordering +}; + +enum { + SUBSAMPLING_420 = 1, + SUBSAMPLING_422 = 2, +}; + +/* + * e5010 format structure + * contains format information + */ +struct e5010_fmt { + u32 fourcc; + unsigned int num_planes; + unsigned int type; + u32 subsampling; + u32 chroma_order; + const struct v4l2_frmsize_stepwise frmsize; +}; + +/* + * struct e5010_ctrl - contains info for each supported v4l2 control + */ +struct e5010_ctrl { + unsigned int cid; + enum v4l2_ctrl_type type; + unsigned char name[32]; + int minimum; + int maximum; + int step; + int default_value; + unsigned char compound; +}; + +#endif diff --git a/drivers/media/platform/imagination/e5010-mmu-regs.h b/drivers/media/platform/imagination/e5010-mmu-regs.h new file mode 100644 index 000000000000..bfba06956cf2 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-mmu-regs.h @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#ifndef _E5010_MMU_REGS_H +#define _E5010_MMU_REGS_H + +#define MMU_MMU_DIR_BASE_ADDR_OFFSET (0x0020) +#define MMU_MMU_DIR_BASE_ADDR_STRIDE (4) +#define MMU_MMU_DIR_BASE_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_SHIFT (0) + +#define MMU_MMU_TILE_CFG_OFFSET (0x0040) +#define MMU_MMU_TILE_CFG_STRIDE (4) +#define MMU_MMU_TILE_CFG_NO_ENTRIES (4) + +#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_MASK (0x00000010) +#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_SHIFT (4) + +#define MMU_MMU_TILE_CFG_TILE_ENABLE_MASK (0x00000008) +#define MMU_MMU_TILE_CFG_TILE_ENABLE_SHIFT (3) + +#define MMU_MMU_TILE_CFG_TILE_STRIDE_MASK (0x00000007) +#define MMU_MMU_TILE_CFG_TILE_STRIDE_SHIFT (0) + +#define MMU_MMU_TILE_MIN_ADDR_OFFSET (0x0050) +#define MMU_MMU_TILE_MIN_ADDR_STRIDE (4) +#define MMU_MMU_TILE_MIN_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_SHIFT (0) + +#define MMU_MMU_TILE_MAX_ADDR_OFFSET (0x0060) +#define MMU_MMU_TILE_MAX_ADDR_STRIDE (4) +#define MMU_MMU_TILE_MAX_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_SHIFT (0) + +#define MMU_MMU_CONTROL0_OFFSET (0x0000) + +#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_MASK (0x00000001) +#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_SHIFT (0) + +#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_MASK (0x00000100) +#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_SHIFT (8) + +#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_MASK (0x00000200) +#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_SHIFT (9) + +#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_MASK (0x00001000) +#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_SHIFT (12) + +#define MMU_MMU_CONTROL1_OFFSET (0x0008) + +#define MMU_MMU_CONTROL1_MMU_FLUSH_MASK (0x00000008) +#define MMU_MMU_CONTROL1_MMU_FLUSH_SHIFT (3) +#define MMU_MMU_CONTROL1_MMU_FLUSH_NO_REPS (4) +#define MMU_MMU_CONTROL1_MMU_FLUSH_SIZE (1) + +#define MMU_MMU_CONTROL1_MMU_INVALDC_MASK (0x00000800) +#define MMU_MMU_CONTROL1_MMU_INVALDC_SHIFT (11) +#define MMU_MMU_CONTROL1_MMU_INVALDC_NO_REPS (4) +#define MMU_MMU_CONTROL1_MMU_INVALDC_SIZE (1) + +#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_MASK (0x00010000) +#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_SHIFT (16) + +#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_MASK (0x00100000) +#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_SHIFT (20) + +#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_MASK (0x01000000) +#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_SHIFT (24) + +#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_MASK (0x02000000) +#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_SHIFT (25) + +#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK (0x10000000) +#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT (28) + +#define MMU_MMU_BANK_INDEX_OFFSET (0x0010) + +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_MASK (0xC0000000) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SHIFT (30) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_NO_REPS (16) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SIZE (2) + +#define MMU_REQUEST_PRIORITY_ENABLE_OFFSET (0x0018) + +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_MASK (0x00008000) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SHIFT (15) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_NO_REPS (16) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SIZE (1) + +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_MASK (0x00010000) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_SHIFT (16) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_OFFSET (0x001C) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_MASK (0x000003FF) +#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_SHIFT (0) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_MASK (0x0FFF0000) +#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_SHIFT (16) + +#define MMU_MMU_ADDRESS_CONTROL_OFFSET (0x0070) +#define MMU_MMU_ADDRESS_CONTROL_TRUSTED (IMG_TRUE) + +#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK (0x00000001) +#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT (0) + +#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_MASK (0x00000010) +#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_SHIFT (4) + +#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_MASK (0x00FF0000) +#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_SHIFT (16) + +#define MMU_MMU_CONFIG0_OFFSET (0x0080) + +#define MMU_MMU_CONFIG0_NUM_REQUESTORS_MASK (0x0000000F) +#define MMU_MMU_CONFIG0_NUM_REQUESTORS_SHIFT (0) + +#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_MASK (0x000000F0) +#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_SHIFT (4) + +#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_MASK (0x00000700) +#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_SHIFT (8) + +#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_MASK (0x00001000) +#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_SHIFT (12) + +#define MMU_MMU_CONFIG0_MMU_SUPPORTED_MASK (0x00002000) +#define MMU_MMU_CONFIG0_MMU_SUPPORTED_SHIFT (13) + +#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_MASK (0x001F0000) +#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_SHIFT (16) + +#define MMU_MMU_CONFIG0_NO_READ_REORDER_MASK (0x00200000) +#define MMU_MMU_CONFIG0_NO_READ_REORDER_SHIFT (21) + +#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_MASK (0xFFC00000) +#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_SHIFT (22) + +#define MMU_MMU_CONFIG1_OFFSET (0x0084) + +#define MMU_MMU_CONFIG1_PAGE_SIZE_MASK (0x0000000F) +#define MMU_MMU_CONFIG1_PAGE_SIZE_SHIFT (0) + +#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_MASK (0x0000FF00) +#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_SHIFT (8) + +#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_MASK (0x001F0000) +#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_SHIFT (16) + +#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_MASK (0x01000000) +#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_SHIFT (24) + +#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_MASK (0x02000000) +#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_SHIFT (25) + +#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_MASK (0x04000000) +#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_SHIFT (26) + +#define MMU_MMU_STATUS0_OFFSET (0x0088) + +#define MMU_MMU_STATUS0_MMU_PF_N_RW_MASK (0x00000001) +#define MMU_MMU_STATUS0_MMU_PF_N_RW_SHIFT (0) + +#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_MASK (0xFFFFF000) +#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_SHIFT (12) + +#define MMU_MMU_STATUS1_OFFSET (0x008C) + +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_MASK (0x0000FFFF) +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_SHIFT (0) + +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_MASK (0x000F0000) +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_SHIFT (16) + +#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_MASK (0x03000000) +#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_SHIFT (24) + +#define MMU_MMU_STATUS1_MMU_FAULT_RNW_MASK (0x10000000) +#define MMU_MMU_STATUS1_MMU_FAULT_RNW_SHIFT (28) + +#define MMU_MMU_MEM_REQ_OFFSET (0x0090) + +#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_MASK (0x000003FF) +#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_SHIFT (0) + +#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_MASK (0x00001000) +#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_SHIFT (12) + +#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_MASK (0x00002000) +#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_SHIFT (13) + +#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_MASK (0x00004000) +#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_SHIFT (14) + +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_MASK (0x80000000) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SHIFT (31) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_NO_REPS (16) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SIZE (1) + +#define MMU_MMU_FAULT_SELECT_OFFSET (0x00A0) + +#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_MASK (0x0000000F) +#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_SHIFT (0) + +#define MMU_PROTOCOL_FAULT_OFFSET (0x00A8) + +#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_MASK (0x00000001) +#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_SHIFT (0) + +#define MMU_PROTOCOL_FAULT_FAULT_WRITE_MASK (0x00000010) +#define MMU_PROTOCOL_FAULT_FAULT_WRITE_SHIFT (4) + +#define MMU_PROTOCOL_FAULT_FAULT_READ_MASK (0x00000020) +#define MMU_PROTOCOL_FAULT_FAULT_READ_SHIFT (5) + +#define MMU_TOTAL_READ_REQ_OFFSET (0x0100) + +#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_MASK (0xFFFFFFFF) +#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_SHIFT (0) + +#define MMU_TOTAL_WRITE_REQ_OFFSET (0x0104) + +#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_MASK (0xFFFFFFFF) +#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_SHIFT (0) + +#define MMU_READS_LESS_64_REQ_OFFSET (0x0108) + +#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_MASK (0xFFFFFFFF) +#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_SHIFT (0) + +#define MMU_WRITES_LESS_64_REQ_OFFSET (0x010C) + +#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_MASK (0xFFFFFFFF) +#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_SHIFT (0) + +#define MMU_EXT_CMD_STALL_OFFSET (0x0120) + +#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_MASK (0xFFFFFFFF) +#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_SHIFT (0) + +#define MMU_WRITE_REQ_STALL_OFFSET (0x0124) + +#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_MASK (0xFFFFFFFF) +#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_SHIFT (0) + +#define MMU_MMU_MISS_STALL_OFFSET (0x0128) + +#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_MASK (0xFFFFFFFF) +#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_SHIFT (0) + +#define MMU_ADDRESS_STALL_OFFSET (0x012C) + +#define MMU_ADDRESS_STALL_ADDRESS_STALL_MASK (0xFFFFFFFF) +#define MMU_ADDRESS_STALL_ADDRESS_STALL_SHIFT (0) + +#define MMU_TAG_STALL_OFFSET (0x0130) + +#define MMU_TAG_STALL_TAG_STALL_MASK (0xFFFFFFFF) +#define MMU_TAG_STALL_TAG_STALL_SHIFT (0) + +#define MMU_PEAK_READ_OUTSTANDING_OFFSET (0x0140) + +#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_MASK (0x000003FF) +#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_SHIFT (0) + +#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_MASK (0xFFFF0000) +#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_SHIFT (16) + +#define MMU_AVERAGE_READ_LATENCY_OFFSET (0x0144) + +#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_MASK (0xFFFFFFFF) +#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_SHIFT (0) + +#define MMU_STATISTICS_CONTROL_OFFSET (0x0160) + +#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_MASK (0x00000001) +#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_SHIFT (0) + +#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_MASK (0x00000002) +#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_SHIFT (1) + +#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_MASK (0x00000004) +#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_SHIFT (2) + +#define MMU_MMU_VERSION_OFFSET (0x01D0) + +#define MMU_MMU_VERSION_MMU_MAJOR_REV_MASK (0x00FF0000) +#define MMU_MMU_VERSION_MMU_MAJOR_REV_SHIFT (16) + +#define MMU_MMU_VERSION_MMU_MINOR_REV_MASK (0x0000FF00) +#define MMU_MMU_VERSION_MMU_MINOR_REV_SHIFT (8) + +#define MMU_MMU_VERSION_MMU_MAINT_REV_MASK (0x000000FF) +#define MMU_MMU_VERSION_MMU_MAINT_REV_SHIFT (0) + +#define MMU_BYTE_SIZE (0x01D4) + +#endif diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index d904952bf00e..bef1e7137f23 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -43,6 +43,7 @@ #include <linux/videodev2.h> #include <linux/platform_data/media/camera-pxa.h> +#include <linux/workqueue.h> #define PXA_CAM_VERSION "0.0.6" #define PXA_CAM_DRV_NAME "pxa27x-camera" @@ -683,7 +684,7 @@ struct pxa_camera_dev { unsigned int buf_sequence; struct pxa_buffer *active; - struct tasklet_struct task_eof; + struct work_struct eof_bh_work; u32 save_cicr[5]; }; @@ -1146,9 +1147,9 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) clk_disable_unprepare(pcdev->clk); } -static void pxa_camera_eof(struct tasklet_struct *t) +static void pxa_camera_eof_bh_work(struct work_struct *t) { - struct pxa_camera_dev *pcdev = from_tasklet(pcdev, t, task_eof); + struct pxa_camera_dev *pcdev = from_work(pcdev, t, eof_bh_work); unsigned long cifr; struct pxa_buffer *buf; @@ -1185,7 +1186,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) if (status & CISR_EOF) { cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM; __raw_writel(cicr0, pcdev->base + CICR0); - tasklet_schedule(&pcdev->task_eof); + queue_work(system_bh_wq, &pcdev->eof_bh_work); } return IRQ_HANDLED; @@ -1503,8 +1504,6 @@ static const struct vb2_ops pxac_vb2_ops = { .buf_cleanup = pxac_vb2_cleanup, .start_streaming = pxac_vb2_start_streaming, .stop_streaming = pxac_vb2_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int pxa_camera_init_videobuf2(struct pxa_camera_dev *pcdev) @@ -2383,7 +2382,7 @@ static int pxa_camera_probe(struct platform_device *pdev) } } - tasklet_setup(&pcdev->task_eof, pxa_camera_eof); + INIT_WORK(&pcdev->eof_bh_work, pxa_camera_eof_bh_work); pxa_camera_activate(pcdev); @@ -2409,7 +2408,7 @@ static int pxa_camera_probe(struct platform_device *pdev) return 0; exit_deactivate: pxa_camera_deactivate(pcdev); - tasklet_kill(&pcdev->task_eof); + cancel_work_sync(&pcdev->eof_bh_work); exit_free_dma: dma_release_channel(pcdev->dma_chans[2]); exit_free_dma_u: @@ -2428,7 +2427,7 @@ static void pxa_camera_remove(struct platform_device *pdev) struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); pxa_camera_deactivate(pcdev); - tasklet_kill(&pcdev->task_eof); + cancel_work_sync(&pcdev->eof_bh_work); dma_release_channel(pcdev->dma_chans[0]); dma_release_channel(pcdev->dma_chans[1]); dma_release_channel(pcdev->dma_chans[2]); @@ -2459,7 +2458,7 @@ static struct platform_driver pxa_camera_driver = { .of_match_table = pxa_camera_of_match, }, .probe = pxa_camera_probe, - .remove_new = pxa_camera_remove, + .remove = pxa_camera_remove, }; module_platform_driver(pxa_camera_driver); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 96b35a5d6174..5188f3189096 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -724,10 +724,6 @@ static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { /* * Queue operations */ -struct vb2_dc_conf { - struct device *dev; -}; - static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) @@ -788,8 +784,6 @@ static const struct vb2_ops deinterlace_qops = { .queue_setup = deinterlace_queue_setup, .buf_prepare = deinterlace_buf_prepare, .buf_queue = deinterlace_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -997,7 +991,7 @@ static void deinterlace_remove(struct platform_device *pdev) static struct platform_driver deinterlace_pdrv = { .probe = deinterlace_probe, - .remove_new = deinterlace_remove, + .remove = deinterlace_remove, .driver = { .name = MEM2MEM_NAME, }, diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index 66688b4aece5..b8360d37000a 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -439,9 +439,9 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) /* * Copy data out to user space in the vmalloc case */ -static void mcam_frame_tasklet(struct tasklet_struct *t) +static void mcam_frame_work(struct work_struct *t) { - struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet); + struct mcam_camera *cam = from_work(cam, t, s_bh_work); int i; unsigned long flags; struct mcam_vb_buffer *buf; @@ -493,7 +493,7 @@ static int mcam_check_dma_buffers(struct mcam_camera *cam) static void mcam_vmalloc_done(struct mcam_camera *cam, int frame) { - tasklet_schedule(&cam->s_tasklet); + queue_work(system_bh_wq, &cam->s_bh_work); } #else /* MCAM_MODE_VMALLOC */ @@ -935,7 +935,12 @@ static int mclk_enable(struct clk_hw *hw) ret = pm_runtime_resume_and_get(cam->dev); if (ret < 0) return ret; - clk_enable(cam->clk[0]); + ret = clk_enable(cam->clk[0]); + if (ret) { + pm_runtime_put(cam->dev); + return ret; + } + mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); mcam_ctlr_power_up(cam); @@ -1203,8 +1208,6 @@ static const struct vb2_ops mcam_vb2_ops = { .buf_queue = mcam_vb_buf_queue, .start_streaming = mcam_vb_start_streaming, .stop_streaming = mcam_vb_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; @@ -1267,8 +1270,6 @@ static const struct vb2_ops mcam_vb2_sg_ops = { .buf_cleanup = mcam_vb_sg_buf_cleanup, .start_streaming = mcam_vb_start_streaming, .stop_streaming = mcam_vb_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; #endif /* MCAM_MODE_DMA_SG */ @@ -1305,7 +1306,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) break; case B_vmalloc: #ifdef MCAM_MODE_VMALLOC - tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet); + INIT_WORK(&cam->s_bh_work, mcam_frame_work); vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_vmalloc_memops; cam->dma_setup = mcam_ctlr_dma_vmalloc; @@ -1981,5 +1982,6 @@ int mccic_resume(struct mcam_camera *cam) } EXPORT_SYMBOL_GPL(mccic_resume); +MODULE_DESCRIPTION("Marvell camera core driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); diff --git a/drivers/media/platform/marvell/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h index 51e66db45af6..989dc6859a53 100644 --- a/drivers/media/platform/marvell/mcam-core.h +++ b/drivers/media/platform/marvell/mcam-core.h @@ -9,6 +9,7 @@ #include <linux/list.h> #include <linux/clk-provider.h> +#include <linux/workqueue.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-dev.h> @@ -167,7 +168,7 @@ struct mcam_camera { unsigned int dma_buf_size; /* allocated size */ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ - struct tasklet_struct s_tasklet; + struct work_struct s_bh_work; #endif unsigned int sequence; /* Frame sequence number */ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */ diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index 170907cc1885..d3da7ebb4a2b 100644 --- a/drivers/media/platform/marvell/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -30,6 +30,7 @@ MODULE_ALIAS("platform:mmp-camera"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); +MODULE_DESCRIPTION("Support for the camera device found on Marvell MMP processors"); MODULE_LICENSE("GPL"); static char *mcam_clks[] = {"axi", "func", "phy"}; @@ -231,12 +232,22 @@ static int mmpcam_probe(struct platform_device *pdev) mcam_init_clk(mcam); /* + * Register with V4L. + */ + + ret = v4l2_device_register(mcam->dev, &mcam->v4l2_dev); + if (ret) + return ret; + + /* * Create a match of the sensor against its OF node. */ ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node), NULL); - if (!ep) - return -ENODEV; + if (!ep) { + ret = -ENODEV; + goto out_v4l2_device_unregister; + } v4l2_async_nf_init(&mcam->notifier, &mcam->v4l2_dev); @@ -245,7 +256,7 @@ static int mmpcam_probe(struct platform_device *pdev) fwnode_handle_put(ep); if (IS_ERR(asd)) { ret = PTR_ERR(asd); - goto out; + goto out_v4l2_device_unregister; } /* @@ -253,7 +264,7 @@ static int mmpcam_probe(struct platform_device *pdev) */ ret = mccic_register(mcam); if (ret) - goto out; + goto out_v4l2_device_unregister; /* * Add OF clock provider. @@ -282,6 +293,8 @@ static int mmpcam_probe(struct platform_device *pdev) return 0; out: mccic_shutdown(mcam); +out_v4l2_device_unregister: + v4l2_device_unregister(&mcam->v4l2_dev); return ret; } @@ -292,6 +305,7 @@ static void mmpcam_remove(struct platform_device *pdev) struct mcam_camera *mcam = &cam->mcam; mccic_shutdown(mcam); + v4l2_device_unregister(&mcam->v4l2_dev); pm_runtime_force_suspend(mcam->dev); } @@ -358,7 +372,7 @@ MODULE_DEVICE_TABLE(of, mmpcam_of_match); static struct platform_driver mmpcam_driver = { .probe = mmpcam_probe, - .remove_new = mmpcam_remove, + .remove = mmpcam_remove, .driver = { .name = "mmp-camera", .of_match_table = mmpcam_of_match, diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index ac48658e2de4..834d2a354692 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -884,8 +884,6 @@ static const struct vb2_ops mtk_jpeg_dec_qops = { .queue_setup = mtk_jpeg_queue_setup, .buf_prepare = mtk_jpeg_buf_prepare, .buf_queue = mtk_jpeg_dec_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = mtk_jpeg_dec_stop_streaming, }; @@ -893,8 +891,6 @@ static const struct vb2_ops mtk_jpeg_enc_qops = { .queue_setup = mtk_jpeg_queue_setup, .buf_prepare = mtk_jpeg_buf_prepare, .buf_queue = mtk_jpeg_enc_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = mtk_jpeg_enc_stop_streaming, }; @@ -1293,6 +1289,11 @@ static int mtk_jpeg_single_core_init(struct platform_device *pdev, return 0; } +static void mtk_jpeg_destroy_workqueue(void *data) +{ + destroy_workqueue(data); +} + static int mtk_jpeg_probe(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg; @@ -1337,6 +1338,11 @@ static int mtk_jpeg_probe(struct platform_device *pdev) | WQ_FREEZABLE); if (!jpeg->workqueue) return -EINVAL; + ret = devm_add_action_or_reset(&pdev->dev, + mtk_jpeg_destroy_workqueue, + jpeg->workqueue); + if (ret) + return ret; } ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); @@ -1950,7 +1956,7 @@ MODULE_DEVICE_TABLE(of, mtk_jpeg_match); static struct platform_driver mtk_jpeg_driver = { .probe = mtk_jpeg_probe, - .remove_new = mtk_jpeg_remove, + .remove = mtk_jpeg_remove, .driver = { .name = MTK_JPEG_NAME, .of_match_table = mtk_jpeg_match, diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c index 4a6ee211e18f..2c5d74939d0a 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c @@ -578,11 +578,6 @@ static int mtk_jpegdec_hw_init_irq(struct mtk_jpegdec_comp_dev *dev) return 0; } -static void mtk_jpegdec_destroy_workqueue(void *data) -{ - destroy_workqueue(data); -} - static int mtk_jpegdec_hw_probe(struct platform_device *pdev) { struct mtk_jpegdec_clk *jpegdec_clk; @@ -606,12 +601,6 @@ static int mtk_jpegdec_hw_probe(struct platform_device *pdev) dev->plat_dev = pdev; dev->dev = &pdev->dev; - ret = devm_add_action_or_reset(&pdev->dev, - mtk_jpegdec_destroy_workqueue, - master_dev->workqueue); - if (ret) - return ret; - spin_lock_init(&dev->hw_lock); dev->hw_state = MTK_JPEG_HW_IDLE; diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c index 917cdf38f230..80fdc6ff57e0 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c @@ -298,7 +298,7 @@ static const struct dev_pm_ops mtk_mdp_pm_ops = { static struct platform_driver mtk_mdp_driver = { .probe = mtk_mdp_probe, - .remove_new = mtk_mdp_remove, + .remove = mtk_mdp_remove, .driver = { .name = MTK_MDP_MODULE_NAME, .pm = &mtk_mdp_pm_ops, diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c index f14779e7596e..28c998bd3a81 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c @@ -584,8 +584,6 @@ static const struct vb2_ops mtk_mdp_m2m_qops = { .buf_queue = mtk_mdp_m2m_buf_queue, .stop_streaming = mtk_mdp_m2m_stop_streaming, .start_streaming = mtk_mdp_m2m_start_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int mtk_mdp_m2m_querycap(struct file *file, void *fh, diff --git a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c index ecca52b45307..0b4c50bc1776 100644 --- a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c +++ b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c @@ -46,6 +46,53 @@ enum mt8183_mdp_comp_id { MT8183_MDP_COMP_WROT1, /* 25 */ }; +enum mt8188_mdp_comp_id { + /* MT8188 Comp id */ + /* ISP */ + MT8188_MDP_COMP_WPEI = 0, + MT8188_MDP_COMP_WPEO, /* 1 */ + + /* MDP */ + MT8188_MDP_COMP_CAMIN, /* 2 */ + MT8188_MDP_COMP_RDMA0, /* 3 */ + MT8188_MDP_COMP_RDMA2, /* 4 */ + MT8188_MDP_COMP_RDMA3, /* 5 */ + MT8188_MDP_COMP_FG0, /* 6 */ + MT8188_MDP_COMP_FG2, /* 7 */ + MT8188_MDP_COMP_FG3, /* 8 */ + MT8188_MDP_COMP_TO_SVPP2MOUT, /* 9 */ + MT8188_MDP_COMP_TO_SVPP3MOUT, /* 10 */ + MT8188_MDP_COMP_TO_WARP0MOUT, /* 11 */ + MT8188_MDP_COMP_VPP0_SOUT, /* 12 */ + MT8188_MDP_COMP_VPP1_SOUT, /* 13 */ + MT8188_MDP_COMP_PQ0_SOUT, /* 14 */ + MT8188_MDP_COMP_HDR0, /* 15 */ + MT8188_MDP_COMP_HDR2, /* 16 */ + MT8188_MDP_COMP_HDR3, /* 17 */ + MT8188_MDP_COMP_AAL0, /* 18 */ + MT8188_MDP_COMP_AAL2, /* 19 */ + MT8188_MDP_COMP_AAL3, /* 20 */ + MT8188_MDP_COMP_RSZ0, /* 21 */ + MT8188_MDP_COMP_RSZ2, /* 22 */ + MT8188_MDP_COMP_RSZ3, /* 23 */ + MT8188_MDP_COMP_TDSHP0, /* 24 */ + MT8188_MDP_COMP_TDSHP2, /* 25 */ + MT8188_MDP_COMP_TDSHP3, /* 26 */ + MT8188_MDP_COMP_COLOR0, /* 27 */ + MT8188_MDP_COMP_COLOR2, /* 28 */ + MT8188_MDP_COMP_COLOR3, /* 29 */ + MT8188_MDP_COMP_OVL0, /* 30 */ + MT8188_MDP_COMP_PAD0, /* 31 */ + MT8188_MDP_COMP_PAD2, /* 32 */ + MT8188_MDP_COMP_PAD3, /* 33 */ + MT8188_MDP_COMP_TCC0, /* 34 */ + MT8188_MDP_COMP_WROT0, /* 35 */ + MT8188_MDP_COMP_WROT2, /* 36 */ + MT8188_MDP_COMP_WROT3, /* 37 */ + MT8188_MDP_COMP_MERGE2, /* 38 */ + MT8188_MDP_COMP_MERGE3, /* 39 */ +}; + enum mt8195_mdp_comp_id { /* MT8195 Comp id */ /* ISP */ @@ -123,6 +170,13 @@ static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = { [MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" } }; +static const struct of_device_id mt8188_mdp_probe_infra[MDP_INFRA_MAX] = { + [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8188-vppsys0" }, + [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8188-vppsys1" }, + [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8188-vpp-mutex" }, + [MDP_INFRA_MUTEX2] = { .compatible = "mediatek,mt8188-vpp-mutex" }, +}; + static const struct of_device_id mt8195_mdp_probe_infra[MDP_INFRA_MAX] = { [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8195-vppsys0" }, [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8195-vppsys1" }, @@ -167,6 +221,40 @@ static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = { [MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0, }; +static const u32 mt8188_mutex_idx[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, + [MDP_COMP_RDMA2] = MUTEX_MOD_IDX_MDP_RDMA2, + [MDP_COMP_RDMA3] = MUTEX_MOD_IDX_MDP_RDMA3, + [MDP_COMP_FG0] = MUTEX_MOD_IDX_MDP_FG0, + [MDP_COMP_FG2] = MUTEX_MOD_IDX_MDP_FG2, + [MDP_COMP_FG3] = MUTEX_MOD_IDX_MDP_FG3, + [MDP_COMP_HDR0] = MUTEX_MOD_IDX_MDP_HDR0, + [MDP_COMP_HDR2] = MUTEX_MOD_IDX_MDP_HDR2, + [MDP_COMP_HDR3] = MUTEX_MOD_IDX_MDP_HDR3, + [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0, + [MDP_COMP_AAL2] = MUTEX_MOD_IDX_MDP_AAL2, + [MDP_COMP_AAL3] = MUTEX_MOD_IDX_MDP_AAL3, + [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0, + [MDP_COMP_RSZ2] = MUTEX_MOD_IDX_MDP_RSZ2, + [MDP_COMP_RSZ3] = MUTEX_MOD_IDX_MDP_RSZ3, + [MDP_COMP_MERGE2] = MUTEX_MOD_IDX_MDP_MERGE2, + [MDP_COMP_MERGE3] = MUTEX_MOD_IDX_MDP_MERGE3, + [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0, + [MDP_COMP_TDSHP2] = MUTEX_MOD_IDX_MDP_TDSHP2, + [MDP_COMP_TDSHP3] = MUTEX_MOD_IDX_MDP_TDSHP3, + [MDP_COMP_COLOR0] = MUTEX_MOD_IDX_MDP_COLOR0, + [MDP_COMP_COLOR2] = MUTEX_MOD_IDX_MDP_COLOR2, + [MDP_COMP_COLOR3] = MUTEX_MOD_IDX_MDP_COLOR3, + [MDP_COMP_OVL0] = MUTEX_MOD_IDX_MDP_OVL0, + [MDP_COMP_PAD0] = MUTEX_MOD_IDX_MDP_PAD0, + [MDP_COMP_PAD2] = MUTEX_MOD_IDX_MDP_PAD2, + [MDP_COMP_PAD3] = MUTEX_MOD_IDX_MDP_PAD3, + [MDP_COMP_TCC0] = MUTEX_MOD_IDX_MDP_TCC0, + [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0, + [MDP_COMP_WROT2] = MUTEX_MOD_IDX_MDP_WROT2, + [MDP_COMP_WROT3] = MUTEX_MOD_IDX_MDP_WROT3, +}; + static const u32 mt8195_mutex_idx[MDP_MAX_COMP_COUNT] = { [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, [MDP_COMP_RDMA1] = MUTEX_MOD_IDX_MDP_RDMA1, @@ -288,6 +376,171 @@ static const struct mdp_comp_data mt8183_mdp_comp_data[MDP_MAX_COMP_COUNT] = { }, }; +static const struct mdp_comp_data mt8188_mdp_comp_data[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_WPEI] = { + {MDP_COMP_TYPE_WPEI, 0, MT8188_MDP_COMP_WPEI, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_WPEO] = { + {MDP_COMP_TYPE_EXTO, 0, MT8188_MDP_COMP_WPEO, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_CAMIN] = { + {MDP_COMP_TYPE_DL_PATH, 0, MT8188_MDP_COMP_CAMIN, MDP_MM_SUBSYS_0}, + {3, 3, 0} + }, + [MDP_COMP_RDMA0] = { + {MDP_COMP_TYPE_RDMA, 0, MT8188_MDP_COMP_RDMA0, MDP_MM_SUBSYS_0}, + {3, 0, 0} + }, + [MDP_COMP_RDMA2] = { + {MDP_COMP_TYPE_RDMA, 1, MT8188_MDP_COMP_RDMA2, MDP_MM_SUBSYS_1}, + {3, 0, 0} + }, + [MDP_COMP_RDMA3] = { + {MDP_COMP_TYPE_RDMA, 2, MT8188_MDP_COMP_RDMA3, MDP_MM_SUBSYS_1}, + {3, 0, 0} + }, + [MDP_COMP_FG0] = { + {MDP_COMP_TYPE_FG, 0, MT8188_MDP_COMP_FG0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_FG2] = { + {MDP_COMP_TYPE_FG, 1, MT8188_MDP_COMP_FG2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_FG3] = { + {MDP_COMP_TYPE_FG, 2, MT8188_MDP_COMP_FG3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_HDR0] = { + {MDP_COMP_TYPE_HDR, 0, MT8188_MDP_COMP_HDR0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_HDR2] = { + {MDP_COMP_TYPE_HDR, 1, MT8188_MDP_COMP_HDR2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_HDR3] = { + {MDP_COMP_TYPE_HDR, 2, MT8188_MDP_COMP_HDR3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_AAL0] = { + {MDP_COMP_TYPE_AAL, 0, MT8188_MDP_COMP_AAL0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_AAL2] = { + {MDP_COMP_TYPE_AAL, 1, MT8188_MDP_COMP_AAL2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_AAL3] = { + {MDP_COMP_TYPE_AAL, 2, MT8188_MDP_COMP_AAL3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_RSZ0] = { + {MDP_COMP_TYPE_RSZ, 0, MT8188_MDP_COMP_RSZ0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_RSZ2] = { + {MDP_COMP_TYPE_RSZ, 1, MT8188_MDP_COMP_RSZ2, MDP_MM_SUBSYS_1}, + {2, 0, 0}, + {MDP_COMP_MERGE2, true, true} + }, + [MDP_COMP_RSZ3] = { + {MDP_COMP_TYPE_RSZ, 2, MT8188_MDP_COMP_RSZ3, MDP_MM_SUBSYS_1}, + {2, 0, 0}, + {MDP_COMP_MERGE3, true, true} + }, + [MDP_COMP_TDSHP0] = { + {MDP_COMP_TYPE_TDSHP, 0, MT8188_MDP_COMP_TDSHP0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP2] = { + {MDP_COMP_TYPE_TDSHP, 1, MT8188_MDP_COMP_TDSHP2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP3] = { + {MDP_COMP_TYPE_TDSHP, 2, MT8188_MDP_COMP_TDSHP3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_COLOR0] = { + {MDP_COMP_TYPE_COLOR, 0, MT8188_MDP_COMP_COLOR0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_COLOR2] = { + {MDP_COMP_TYPE_COLOR, 1, MT8188_MDP_COMP_COLOR2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_COLOR3] = { + {MDP_COMP_TYPE_COLOR, 2, MT8188_MDP_COMP_COLOR3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_OVL0] = { + {MDP_COMP_TYPE_OVL, 0, MT8188_MDP_COMP_OVL0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_PAD0] = { + {MDP_COMP_TYPE_PAD, 0, MT8188_MDP_COMP_PAD0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_PAD2] = { + {MDP_COMP_TYPE_PAD, 1, MT8188_MDP_COMP_PAD2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_PAD3] = { + {MDP_COMP_TYPE_PAD, 2, MT8188_MDP_COMP_PAD3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_TCC0] = { + {MDP_COMP_TYPE_TCC, 0, MT8188_MDP_COMP_TCC0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_WROT0] = { + {MDP_COMP_TYPE_WROT, 0, MT8188_MDP_COMP_WROT0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_WROT2] = { + {MDP_COMP_TYPE_WROT, 1, MT8188_MDP_COMP_WROT2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_WROT3] = { + {MDP_COMP_TYPE_WROT, 2, MT8188_MDP_COMP_WROT3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_MERGE2] = { + {MDP_COMP_TYPE_MERGE, 0, MT8188_MDP_COMP_MERGE2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_MERGE3] = { + {MDP_COMP_TYPE_MERGE, 1, MT8188_MDP_COMP_MERGE3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_PQ0_SOUT] = { + {MDP_COMP_TYPE_DUMMY, 0, MT8188_MDP_COMP_PQ0_SOUT, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_TO_WARP0MOUT] = { + {MDP_COMP_TYPE_DUMMY, 1, MT8188_MDP_COMP_TO_WARP0MOUT, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_TO_SVPP2MOUT] = { + {MDP_COMP_TYPE_DUMMY, 2, MT8188_MDP_COMP_TO_SVPP2MOUT, MDP_MM_SUBSYS_1}, + {0, 0, 0} + }, + [MDP_COMP_TO_SVPP3MOUT] = { + {MDP_COMP_TYPE_DUMMY, 3, MT8188_MDP_COMP_TO_SVPP3MOUT, MDP_MM_SUBSYS_1}, + {0, 0, 0} + }, + [MDP_COMP_VPP0_SOUT] = { + {MDP_COMP_TYPE_PATH, 0, MT8188_MDP_COMP_VPP0_SOUT, MDP_MM_SUBSYS_1}, + {2, 6, 0} + }, + [MDP_COMP_VPP1_SOUT] = { + {MDP_COMP_TYPE_PATH, 1, MT8188_MDP_COMP_VPP1_SOUT, MDP_MM_SUBSYS_0}, + {2, 8, 0} + }, +}; + static const struct mdp_comp_data mt8195_mdp_comp_data[MDP_MAX_COMP_COUNT] = { [MDP_COMP_WPEI] = { {MDP_COMP_TYPE_WPEI, 0, MT8195_MDP_COMP_WPEI, MDP_MM_SUBSYS_0}, @@ -1046,6 +1299,15 @@ static const struct mdp_pipe_info mt8183_pipe_info[] = { [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 3} }; +static const struct mdp_pipe_info mt8188_pipe_info[] = { + [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0}, + [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 1}, + [MDP_PIPE_RDMA2] = {MDP_PIPE_RDMA2, MDP_MM_SUBSYS_1, 0}, + [MDP_PIPE_RDMA3] = {MDP_PIPE_RDMA3, MDP_MM_SUBSYS_1, 1}, + [MDP_PIPE_VPP1_SOUT] = {MDP_PIPE_VPP1_SOUT, MDP_MM_SUBSYS_0, 2}, + [MDP_PIPE_VPP0_SOUT] = {MDP_PIPE_VPP0_SOUT, MDP_MM_SUBSYS_1, 2}, +}; + static const struct mdp_pipe_info mt8195_pipe_info[] = { [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0}, [MDP_PIPE_WPEI2] = {MDP_PIPE_WPEI2, MDP_MM_SUBSYS_0, 1}, @@ -1082,6 +1344,24 @@ const struct mtk_mdp_driver_data mt8183_mdp_driver_data = { .pp_used = MDP_PP_USED_1, }; +const struct mtk_mdp_driver_data mt8188_mdp_driver_data = { + .mdp_plat_id = MT8188, + .mdp_con_res = 0x14001000, + .mdp_probe_infra = mt8188_mdp_probe_infra, + .mdp_sub_comp_dt_ids = mt8195_sub_comp_dt_ids, + .mdp_cfg = &mt8195_plat_cfg, + .mdp_mutex_table_idx = mt8188_mutex_idx, + .comp_data = mt8188_mdp_comp_data, + .comp_data_len = ARRAY_SIZE(mt8188_mdp_comp_data), + .format = mt8195_formats, + .format_len = ARRAY_SIZE(mt8195_formats), + .def_limit = &mt8195_mdp_def_limit, + .pipe_info = mt8188_pipe_info, + .pipe_info_len = ARRAY_SIZE(mt8188_pipe_info), + .pp_criteria = &mt8195_mdp_pp_criteria, + .pp_used = MDP_PP_USED_2, +}; + const struct mtk_mdp_driver_data mt8195_mdp_driver_data = { .mdp_plat_id = MT8195, .mdp_con_res = 0x14001000, diff --git a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h index f83ac408306e..4764c5b5107b 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h @@ -116,6 +116,7 @@ struct img_frameparam { /* Platform config indicator */ #define MT8183 8183 +#define MT8188 8195 #define MT8195 8195 #define CFG_CHECK(plat, p_id) ((plat) == (p_id)) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h index 49cdf45f6e59..7f7625299ce7 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h @@ -10,6 +10,7 @@ #include <linux/types.h> extern const struct mtk_mdp_driver_data mt8183_mdp_driver_data; +extern const struct mtk_mdp_driver_data mt8188_mdp_driver_data; extern const struct mtk_mdp_driver_data mt8195_mdp_driver_data; struct mdp_dev; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c index 1d64bac34b90..e5ccf673e152 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c @@ -114,19 +114,15 @@ static struct img_config *__get_config_offset(struct mdp_dev *mdp, if (pp_idx >= mdp->mdp_data->pp_used) goto err_param; - if (CFG_CHECK(MT8183, p_id)) + if (CFG_CHECK(MT8183, p_id)) { cfg_c = CFG_OFST(MT8183, param->config, pp_idx); - else if (CFG_CHECK(MT8195, p_id)) - cfg_c = CFG_OFST(MT8195, param->config, pp_idx); - else - goto err_param; - - if (CFG_CHECK(MT8183, p_id)) cfg_n = CFG_OFST(MT8183, param->config, pp_idx + 1); - else if (CFG_CHECK(MT8195, p_id)) + } else if (CFG_CHECK(MT8195, p_id)) { + cfg_c = CFG_OFST(MT8195, param->config, pp_idx); cfg_n = CFG_OFST(MT8195, param->config, pp_idx + 1); - else + } else { goto err_param; + } if ((long)cfg_n - (long)mdp->vpu.config > bound) { dev_err(dev, "config offset %ld OOB %ld\n", (long)cfg_n, bound); @@ -325,8 +321,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, /* Enable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; - cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, - set->value, 0xFFFFFFFF); + cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, set->value); } /* Config sub-frame information */ for (index = (num_comp - 1); index >= 0; index--) { @@ -381,8 +376,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, /* Disable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; - cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, - 0, 0xFFFFFFFF); + cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, 0); } return 0; @@ -471,43 +465,6 @@ static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd, return 0; } -static int mdp_cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, - size_t size) -{ - struct device *dev; - dma_addr_t dma_addr; - - pkt->va_base = kzalloc(size, GFP_KERNEL); - if (!pkt->va_base) - return -ENOMEM; - - pkt->buf_size = size; - pkt->cl = (void *)client; - - dev = client->chan->mbox->dev; - dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); - kfree(pkt->va_base); - return -ENOMEM; - } - - pkt->pa_base = dma_addr; - - return 0; -} - -static void mdp_cmdq_pkt_destroy(struct cmdq_pkt *pkt) -{ - struct cmdq_client *client = (struct cmdq_client *)pkt->cl; - - dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, - DMA_TO_DEVICE); - kfree(pkt->va_base); - pkt->va_base = NULL; -} - static void mdp_auto_release_work(struct work_struct *work) { struct mdp_cmdq_cmd *cmd; @@ -524,7 +481,7 @@ static void mdp_auto_release_work(struct work_struct *work) mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); - if (atomic_dec_and_test(&mdp->job_count)) { + if (refcount_dec_and_test(&mdp->job_count)) { if (cmd->mdp_ctx) mdp_m2m_job_finish(cmd->mdp_ctx); @@ -538,7 +495,7 @@ static void mdp_auto_release_work(struct work_struct *work) wake_up(&mdp->callback_wq); } - mdp_cmdq_pkt_destroy(&cmd->pkt); + cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt); kfree(cmd->comps); cmd->comps = NULL; kfree(cmd); @@ -575,10 +532,10 @@ static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg) mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); - if (atomic_dec_and_test(&mdp->job_count)) + if (refcount_dec_and_test(&mdp->job_count)) wake_up(&mdp->callback_wq); - mdp_cmdq_pkt_destroy(&cmd->pkt); + cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt); kfree(cmd->comps); cmd->comps = NULL; kfree(cmd); @@ -607,20 +564,13 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, goto err_uninit; } - if (CFG_CHECK(MT8183, p_id)) - num_comp = CFG_GET(MT8183, config, num_components); - else if (CFG_CHECK(MT8195, p_id)) - num_comp = CFG_GET(MT8195, config, num_components); - else - goto err_uninit; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto err_uninit; } - ret = mdp_cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K); + ret = cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K); if (ret) goto err_free_cmd; @@ -632,6 +582,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, ret = -EINVAL; goto err_destroy_pkt; } + comps = kcalloc(num_comp, sizeof(*comps), GFP_KERNEL); if (!comps) { ret = -ENOMEM; @@ -676,7 +627,8 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, dev_err(dev, "mdp_path_config error %d\n", pp_idx); goto err_free_path; } - cmdq_pkt_finalize(&cmd->pkt); + cmdq_pkt_eoc(&cmd->pkt); + cmdq_pkt_jump_rel(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]); for (i = 0; i < num_comp; i++) { s32 inner_id = MDP_COMP_NONE; @@ -699,6 +651,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, cmd->comps = comps; cmd->num_comps = num_comp; cmd->mdp_ctx = param->mdp_ctx; + cmd->pp_idx = pp_idx; kfree(path); return cmd; @@ -710,7 +663,7 @@ err_free_path: err_free_comps: kfree(comps); err_destroy_pkt: - mdp_cmdq_pkt_destroy(&cmd->pkt); + cmdq_pkt_destroy(mdp->cmdq_clt[pp_idx], &cmd->pkt); err_free_cmd: kfree(cmd); err_uninit: @@ -724,9 +677,9 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) int i, ret; u8 pp_used = __get_pp_num(param->param->type); - atomic_set(&mdp->job_count, pp_used); + refcount_set(&mdp->job_count, pp_used); if (atomic_read(&mdp->suspended)) { - atomic_set(&mdp->job_count, 0); + refcount_set(&mdp->job_count, 0); return -ECANCELED; } @@ -764,7 +717,7 @@ err_clock_off: mdp_comp_clocks_off(&mdp->pdev->dev, cmd[i]->comps, cmd[i]->num_comps); err_cancel_job: - atomic_set(&mdp->job_count, 0); + refcount_set(&mdp->job_count, 0); return ret; } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h index 53a30ad7e0b0..935ae9825728 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h @@ -35,6 +35,7 @@ struct mdp_cmdq_cmd { struct mdp_comp *comps; void *mdp_ctx; u8 num_comps; + u8 pp_idx; }; struct mdp_dev; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c index 8f62fb167156..683c066ed975 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c @@ -72,14 +72,14 @@ static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) /* Disable RSZ1 */ if (ctx->comp->inner_id == rdma0 && prz1) - MM_REG_WRITE(cmd, subsys_id, prz1->reg_base, PRZ_ENABLE, - 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, prz1->reg_base, + PRZ_ENABLE, 0x0, BIT(0)); } /* Reset RDMA */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8)); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0)); return 0; } @@ -98,26 +98,25 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, if (mdp_cfg && mdp_cfg->rdma_support_10bit) { if (block10bit) - MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7); else - MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7); } /* Setup smi control */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON, - (7 << 4) + //burst type to 8 - (1 << 16), //enable pre-ultra - 0x00030071); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON, + (7 << 4) + //burst type to 8 + (1 << 16), //enable pre-ultra + 0x00030071); /* Setup source frame info */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.src_ctrl); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.src_ctrl); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg, - 0x03C8FE0F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg, 0x03C8FE0F); if (mdp_cfg) if (mdp_cfg->rdma_support_10bit && en_ufo) { @@ -126,17 +125,15 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_y); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ufo_dec_y); - MM_REG_WRITE(cmd, subsys_id, - base, MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_c); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ufo_dec_c); - MM_REG_WRITE(cmd, subsys_id, - base, MDP_RDMA_UFO_DEC_LENGTH_BASE_C, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_UFO_DEC_LENGTH_BASE_C, reg); /* Set 10bit source frame pitch */ if (block10bit) { @@ -144,9 +141,9 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd_in_pxl); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.mf_bkgd_in_pxl); - MM_REG_WRITE(cmd, subsys_id, - base, MDP_RDMA_MF_BKGD_SIZE_IN_PXL, - reg, 0x001FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_MF_BKGD_SIZE_IN_PXL, + reg, 0x001FFFFF); } } @@ -157,128 +154,121 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8195, ctx->param, rdma.control); rdma_con_mask = 0x1130; } - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, reg, - rdma_con_mask); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_CON, reg, rdma_con_mask); /* Setup source buffer base */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova[0]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova[1]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova[2]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg); /* Setup source buffer end */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[0]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[1]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[2]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, reg); /* Setup source frame pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.mf_bkgd); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, - reg, 0x001FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, + reg, 0x001FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.sf_bkgd); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.sf_bkgd); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE, - reg, 0x001FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE, + reg, 0x001FFFFF); /* Setup color transform */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.transform); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.transform); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0, - reg, 0x0F110000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0, + reg, 0x0F110000); if (!mdp_cfg || !mdp_cfg->rdma_esl_setting) goto rdma_config_done; if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_0, - reg, 0x0FFF00FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_0, + reg, 0x0FFF00FF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_0, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_0, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_0, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_0, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_1, - reg, 0x0F7F007F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_1, + reg, 0x0F7F007F); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_1, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_1, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_1, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_1, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con2); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_2, - reg, 0x0F3F003F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_2, + reg, 0x0F3F003F); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con2); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_2, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_2, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con2); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_2, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_2, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con3); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_3, - reg, 0x0F3F003F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_3, + reg, 0x0F3F003F); rdma_config_done: return 0; @@ -297,15 +287,14 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, u32 reg = 0; /* Enable RDMA */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0)); /* Set Y pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[0]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, reg); /* Set 10bit UFO mode */ if (mdp_cfg) { @@ -315,8 +304,7 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset_0_p); MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_SRC_OFFSET_0_P, - reg, 0xFFFFFFFF); + MDP_RDMA_SRC_OFFSET_0_P, reg); } } @@ -325,40 +313,38 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[1]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, reg); /* Set V pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[2]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, reg); /* Set source size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].src); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, reg, + 0x1FFF1FFF); /* Set target size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE, - reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE, + reg, 0x1FFF1FFF); /* Set crop offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip_ofst); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1, - reg, 0x003F001F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1, + reg, 0x003F001F); if (CFG_CHECK(MT8183, p_id)) { csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); @@ -369,8 +355,8 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, } if (mdp_cfg && mdp_cfg->rdma_upsample_repeat_only) if ((csf_r - csf_l + 1) > 320) - MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2)); return 0; } @@ -393,7 +379,7 @@ static int wait_rdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); /* Disable RDMA */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0)); return 0; } @@ -411,10 +397,10 @@ static int init_rsz(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* Reset RSZ */ - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16)); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16)); /* Enable RSZ */ - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0)); if (CFG_CHECK(MT8195, p_id)) { struct device *dev; @@ -437,7 +423,7 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, u32 reg = 0; if (mdp_cfg && mdp_cfg->rsz_etc_control) - MM_REG_WRITE(cmd, subsys_id, base, RSZ_ETC_CONTROL, 0x0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, RSZ_ETC_CONTROL, 0x0); if (CFG_CHECK(MT8183, p_id)) bypass = CFG_COMP(MT8183, ctx->param, frame.bypass); @@ -446,7 +432,7 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, if (bypass) { /* Disable RSZ */ - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0)); return 0; } @@ -454,29 +440,27 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rsz.control1); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.control1); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, reg, - 0x03FFFDF3); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, reg, 0x03FFFDF3); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.control2); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.control2); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg, - 0x0FFFC290); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x0FFFC290); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_x); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.coeff_step_x); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, - reg, 0x007FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, reg, + 0x007FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_y); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.coeff_step_y); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, - reg, 0x007FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, reg, + 0x007FFFFF); return 0; } @@ -495,15 +479,13 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].control2); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].control2); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg, - 0x00003800); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x00003800); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].src); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg); if (CFG_CHECK(MT8183, p_id)) { csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); @@ -514,60 +496,56 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, } if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample) if ((csf_r - csf_l + 1) <= 16) - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, - BIT(27), BIT(27)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, + BIT(27), BIT(27)); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.left); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET, - reg, 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET, + reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left_subpix); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.left_subpix); - MM_REG_WRITE(cmd, subsys_id, - base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, - reg, 0x1FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, + reg, 0x1FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.top); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET, - reg, 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET, + reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top_subpix); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.top_subpix); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, - reg, 0x1FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, + reg, 0x1FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].chroma.left); - MM_REG_WRITE(cmd, subsys_id, - base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET, - reg, 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET, + reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left_subpix); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].chroma.left_subpix); - MM_REG_WRITE(cmd, subsys_id, - base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET, - reg, 0x1FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET, + reg, 0x1FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].clip); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg); if (CFG_CHECK(MT8195, p_id)) { struct device *dev; @@ -596,19 +574,19 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].merge_cfg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_0, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_0, reg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_4, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_4, reg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_24, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_24, reg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_25, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_25, reg); /* Bypass mode */ MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_12, BIT(0), 0xFFFFFFFF); + MDP_MERGE_CFG_12, BIT(0)); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_ENABLE, BIT(0), 0xFFFFFFFF); + MDP_MERGE_ENABLE, BIT(0)); } rsz_subfrm_done: @@ -634,8 +612,8 @@ static int advance_rsz_subfrm(struct mdp_comp_ctx *ctx, } if ((csf_r - csf_l + 1) <= 16) - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0, - BIT(27)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0, + BIT(27)); } return 0; @@ -655,15 +633,15 @@ static int init_wrot(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* Reset WROT */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0)); /* Reset setting */ if (CFG_CHECK(MT8195, p_id)) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, 0x0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, 0x0); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0)); return 0; } @@ -681,39 +659,36 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.iova[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.iova[0]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.iova[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.iova[1]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.iova[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.iova[2]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg); if (mdp_cfg && mdp_cfg->wrot_support_10bit) { if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.scan_10bit); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_SCAN_10BIT, - reg, 0x0000000F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SCAN_10BIT, + reg, 0x0000000F); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.pending_zero); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_PENDING_ZERO, - reg, 0x04000000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_PENDING_ZERO, + reg, 0x04000000); } if (CFG_CHECK(MT8195, p_id)) { reg = CFG_COMP(MT8195, ctx->param, wrot.bit_number); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL_2, - reg, 0x00000007); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CTRL_2, + reg, 0x00000007); } /* Write frame related registers */ @@ -721,14 +696,13 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.control); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.control); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, reg, - 0xF131510F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CTRL, reg, 0xF131510F); /* Write pre-ultra threshold */ if (CFG_CHECK(MT8195, p_id)) { reg = CFG_COMP(MT8195, ctx->param, wrot.pre_ultra); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_DMA_PREULTRA, reg, - 0x00FFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_DMA_PREULTRA, reg, + 0x00FFFFFF); } /* Write frame Y pitch */ @@ -736,37 +710,34 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.stride[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.stride[0]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, reg, - 0x0000FFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE, reg, 0x0000FFFF); /* Write frame UV pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.stride[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.stride[1]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, reg, - 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE_C, reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.stride[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.stride[2]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, reg, - 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE_V, reg, 0xFFFF); /* Write matrix control */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.mat_ctrl); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.mat_ctrl); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAT_CTRL, reg, 0xF3); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAT_CTRL, reg, 0xF3); /* Set the fixed ALPHA as 0xFF */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000, - 0xFF000000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000, + 0xFF000000); /* Set VIDO_EOL_SEL */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31)); /* Set VIDO_FIFO_TEST */ if (CFG_CHECK(MT8183, p_id)) @@ -775,8 +746,8 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8195, ctx->param, wrot.fifo_test); if (reg != 0) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_FIFO_TEST, - reg, 0xFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_FIFO_TEST, reg, + 0xFFF); /* Filter enable */ if (mdp_cfg && mdp_cfg->wrot_filter_constraint) { @@ -784,13 +755,13 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.filter); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.filter); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, - reg, 0x77); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, reg, + 0x77); /* Turn off WROT DMA DCM */ if (CFG_CHECK(MT8195, p_id)) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, - (0x1 << 23) + (0x1 << 20), 0x900000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, + (0x1 << 23) + (0x1 << 20), 0x900000); } return 0; @@ -808,57 +779,52 @@ static int config_wrot_subfrm(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[0]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR, reg, 0x0FFFFFFF); /* Write U pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[1]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_C, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR_C, reg, 0x0FFFFFFF); /* Write V pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[2]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_V, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR_V, reg, + 0x0FFFFFFF); /* Write source size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].src); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_IN_SIZE, reg, 0x1FFF1FFF); /* Write target size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_TAR_SIZE, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip_ofst); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CROP_OFST, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CROP_OFST, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].main_buf); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].main_buf); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, - reg, 0x1FFF7F00); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, reg, + 0x1FFF7F00); /* Enable WROT */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0)); return 0; } @@ -881,11 +847,11 @@ static int wait_wrot_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); if (mdp_cfg && mdp_cfg->wrot_filter_constraint) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0, - 0x77); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0, + 0x77); /* Disable WROT */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0)); return 0; } @@ -904,9 +870,9 @@ static int init_wdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* Reset WDMA */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0)); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0)); return 0; } @@ -918,40 +884,35 @@ static int config_wdma_frame(struct mdp_comp_ctx *ctx, u8 subsys_id = ctx->comp->subsys_id; u32 reg = 0; - MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050); /* Setup frame information */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.wdma_cfg); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CFG, reg, - 0x0F01B8F0); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CFG, reg, 0x0F01B8F0); /* Setup frame base address */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.iova[0]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.iova[1]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.iova[2]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, reg); /* Setup Y pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.w_in_byte); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE, - reg, 0x0000FFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE, reg, + 0x0000FFFF); /* Setup UV pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.uv_stride); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_UV_PITCH, - reg, 0x0000FFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_UV_PITCH, reg, + 0x0000FFFF); /* Set the fixed ALPHA as 0xFF */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF, - 0x800000FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF, + 0x800000FF); return 0; } @@ -966,36 +927,33 @@ static int config_wdma_subfrm(struct mdp_comp_ctx *ctx, /* Write Y pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[0]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET, reg, + 0x0FFFFFFF); /* Write U pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[1]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET, reg, + 0x0FFFFFFF); /* Write V pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[2]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET, reg, + 0x0FFFFFFF); /* Write source size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_SRC_SIZE, reg, - 0x3FFF3FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_SRC_SIZE, reg, 0x3FFF3FFF); /* Write target size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_SIZE, reg, - 0x3FFF3FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CLIP_SIZE, reg, 0x3FFF3FFF); /* Write clip offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_COORD, reg, - 0x3FFF3FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CLIP_COORD, reg, 0x3FFF3FFF); /* Enable WDMA */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0)); return 0; } @@ -1007,7 +965,7 @@ static int wait_wdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); /* Disable WDMA */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0)); return 0; } @@ -1033,19 +991,17 @@ static int reset_luma_hist(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) /* Reset histogram */ for (i = 0; i <= hist_num; i++) - MM_REG_WRITE_MASK(cmd, subsys_id, base, - (MDP_LUMA_HIST_INIT + (i << 2)), - 0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + (MDP_LUMA_HIST_INIT + (i << 2)), 0); if (mdp_cfg->tdshp_constrain) MM_REG_WRITE(cmd, subsys_id, base, - MDP_DC_TWO_D_W1_RESULT_INIT, 0, 0xFFFFFFFF); + MDP_DC_TWO_D_W1_RESULT_INIT, 0); if (mdp_cfg->tdshp_contour) for (i = 0; i < hist_num; i++) - MM_REG_WRITE_MASK(cmd, subsys_id, base, - (MDP_CONTOUR_HIST_INIT + (i << 2)), - 0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + (MDP_CONTOUR_HIST_INIT + (i << 2)), 0); return 0; } @@ -1055,9 +1011,9 @@ static int init_tdshp(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CTRL, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CTRL, BIT(0), BIT(0)); /* Enable FIFO */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CFG, BIT(1), BIT(1)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CFG, BIT(1), BIT(1)); return reset_luma_hist(ctx, cmd); } @@ -1072,7 +1028,7 @@ static int config_tdshp_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.cfg); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CFG, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CFG, reg, BIT(0)); return 0; } @@ -1086,26 +1042,24 @@ static int config_tdshp_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_INPUT_SIZE, - reg, MDP_TDSHP_INPUT_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_INPUT_SIZE, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_OFFSET, - reg, 0x00FF00FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_OFFSET, reg, + 0x00FF00FF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_SIZE, - reg, MDP_TDSHP_OUTPUT_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_SIZE, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].hist_cfg_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_00, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_00, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].hist_cfg_1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_01, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_01, reg); return 0; } @@ -1122,21 +1076,19 @@ static int init_color(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, - MDP_COLOR_START, 0x1, BIT(1) | BIT(0)); - MM_REG_WRITE(cmd, subsys_id, base, - MDP_COLOR_WIN_X_MAIN, 0xFFFF0000, 0xFFFFFFFF); - MM_REG_WRITE(cmd, subsys_id, base, - MDP_COLOR_WIN_Y_MAIN, 0xFFFF0000, 0xFFFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_START, 0x1, + BIT(1) | BIT(0)); + MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_WIN_X_MAIN, 0xFFFF0000); + MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_WIN_Y_MAIN, 0xFFFF0000); /* Reset color matrix */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_CM1_EN, 0x0, BIT(0)); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_CM2_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_CM1_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_CM2_EN, 0x0, BIT(0)); /* Enable interrupt */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTEN, 0x7, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTEN, 0x7, 0x7); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_OUT_SEL, 0x333, 0x333); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_OUT_SEL, 0x333, 0x333); return 0; } @@ -1151,8 +1103,7 @@ static int config_color_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, color.start); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_START, - reg, MDP_COLOR_START_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_START, reg); return 0; } @@ -1166,13 +1117,13 @@ static int config_color_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, color.subfrms[index].in_hsize); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_WIDTH, - reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_WIDTH, + reg, 0x00003FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, color.subfrms[index].in_vsize); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_HEIGHT, - reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_HEIGHT, + reg, 0x00003FFF); return 0; } @@ -1190,9 +1141,9 @@ static int init_ccorr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* CCORR enable */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0)); /* Relay mode */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0)); return 0; } @@ -1214,8 +1165,8 @@ static int config_ccorr_subfrm(struct mdp_comp_ctx *ctx, hsize = csf_r - csf_l + 1; vsize = csf_b - csf_t + 1; - MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_SIZE, - (hsize << 16) + (vsize << 0), 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_SIZE, + (hsize << 16) + (vsize << 0), 0x1FFF1FFF); return 0; } @@ -1231,7 +1182,7 @@ static int init_aal(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u16 subsys_id = ctx->comp->subsys_id; /* Always set MDP_AAL enable to 1 */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_EN, BIT(0), BIT(0)); return 0; } @@ -1246,11 +1197,11 @@ static int config_aal_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.cfg_main); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_CFG_MAIN, reg, BIT(7)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_CFG_MAIN, reg, BIT(7)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.cfg); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_CFG, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_CFG, reg, BIT(0)); return 0; } @@ -1264,18 +1215,16 @@ static int config_aal_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_SIZE, - reg, MDP_AAL_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_SIZE, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_OFFSET, - reg, 0x00FF00FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_OUTPUT_OFFSET, reg, + 0x00FF00FF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_SIZE, - reg, MDP_AAL_OUTPUT_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_SIZE, reg); return 0; } @@ -1293,7 +1242,7 @@ static int init_hdr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u16 subsys_id = ctx->comp->subsys_id; /* Always set MDP_HDR enable to 1 */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, BIT(0), BIT(0)); return 0; } @@ -1308,11 +1257,11 @@ static int config_hdr_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.top); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(29) | BIT(28)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(29) | BIT(28)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.relay); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_RELAY, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_RELAY, reg, BIT(0)); return 0; } @@ -1326,37 +1275,36 @@ static int config_hdr_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].win_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TILE_POS, - reg, MDP_HDR_TILE_POS_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TILE_POS, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_0, reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_0, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].clip_ofst0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_1, reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_1, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].clip_ofst1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_2, reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_2, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_ctrl_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_0, reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_0, reg, 0x00003FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_ctrl_1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_1, reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_1, reg, 0x00003FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hdr_top); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(6) | BIT(5)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(6) | BIT(5)); /* Enable histogram */ if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_addr); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_ADDR, reg, BIT(9)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_ADDR, reg, BIT(9)); return 0; } @@ -1373,8 +1321,8 @@ static int init_fg(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TRIGGER, BIT(2), BIT(2)); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TRIGGER, 0x0, BIT(2)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_TRIGGER, BIT(2), BIT(2)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_TRIGGER, 0x0, BIT(2)); return 0; } @@ -1389,11 +1337,11 @@ static int config_fg_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.ctrl_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_FG_CTRL_0, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_FG_CTRL_0, reg, BIT(0)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.ck_en); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_FG_CK_EN, reg, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_FG_CK_EN, reg, 0x7); return 0; } @@ -1407,11 +1355,11 @@ static int config_fg_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.subfrms[index].info_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_0, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_0, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.subfrms[index].info_1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_1, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_1, reg); return 0; } @@ -1428,14 +1376,11 @@ static int init_ovl(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_EN, - BIT(0), MDP_OVL_EN_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_EN, BIT(0)); /* Set to relay mode */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, - BIT(9), MDP_OVL_SRC_CON_MASK); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_DP_CON, - BIT(0), MDP_OVL_DP_CON_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, BIT(9)); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_DP_CON, BIT(0)); return 0; } @@ -1450,11 +1395,11 @@ static int config_ovl_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.L0_con); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_CON, reg, BIT(29) | BIT(28)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_OVL_L0_CON, reg, BIT(29) | BIT(28)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.src_con); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_OVL_SRC_CON, reg, BIT(0)); return 0; } @@ -1468,14 +1413,12 @@ static int config_ovl_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.subfrms[index].L0_src_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_SRC_SIZE, - reg, MDP_OVL_L0_SRC_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_SRC_SIZE, reg); /* Setup output size */ if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.subfrms[index].roi_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_ROI_SIZE, - reg, MDP_OVL_ROI_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_ROI_SIZE, reg); return 0; } @@ -1492,13 +1435,10 @@ static int init_pad(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_CON, - BIT(1), MDP_PAD_CON_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_CON, BIT(1)); /* Reset */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_W_SIZE, - 0, MDP_PAD_W_SIZE_MASK); - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_H_SIZE, - 0, MDP_PAD_H_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_W_SIZE, 0); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_H_SIZE, 0); return 0; } @@ -1512,8 +1452,7 @@ static int config_pad_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, pad.subfrms[index].pic_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_PIC_SIZE, - reg, MDP_PAD_PIC_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_PIC_SIZE, reg); return 0; } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h index 3e5d2da1c807..681906c16419 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h @@ -9,18 +9,18 @@ #include "mtk-mdp3-cmdq.h" -#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask, ...) \ - cmdq_pkt_write_mask(&((cmd)->pkt), id, \ - (base) + (ofst), (val), (mask), ##__VA_ARGS__) - -#define MM_REG_WRITE(cmd, id, base, ofst, val, mask, ...) \ +#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask) \ do { \ typeof(mask) (m) = (mask); \ - MM_REG_WRITE_MASK(cmd, id, base, ofst, val, \ + cmdq_pkt_write_mask(&((cmd)->pkt), id, (base) + (ofst), \ + (val), \ (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ - (0xffffffff) : (m), ##__VA_ARGS__); \ + (0xffffffff) : (m)); \ } while (0) +#define MM_REG_WRITE(cmd, id, base, ofst, val) \ + cmdq_pkt_write(&((cmd)->pkt), id, (base) + (ofst), (val)) + #define MM_REG_WAIT(cmd, evt) \ do { \ typeof(cmd) (c) = (cmd); \ @@ -49,20 +49,17 @@ do { \ cmdq_pkt_set_event(&((c)->pkt), (e)); \ } while (0) -#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask, ...) \ +#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask) \ do { \ typeof(_mask) (_m) = (_mask); \ cmdq_pkt_poll_mask(&((cmd)->pkt), id, \ - (base) + (ofst), (val), (_m), ##__VA_ARGS__); \ + (base) + (ofst), (val), \ + (((_m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ + (0xffffffff) : (_m)); \ } while (0) -#define MM_REG_POLL(cmd, id, base, ofst, val, mask, ...) \ -do { \ - typeof(mask) (m) = (mask); \ - MM_REG_POLL_MASK((cmd), id, base, ofst, val, \ - (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ - (0xffffffff) : (m), ##__VA_ARGS__); \ -} while (0) +#define MM_REG_POLL(cmd, id, base, ofst, val) \ + cmdq_pkt_poll(&((cmd)->pkt), id, (base) + (ofst), (val)) enum mtk_mdp_comp_id { MDP_COMP_NONE = -1, /* Invalid engine */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 5209f531ef8d..f571f561f070 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -21,6 +21,9 @@ static const struct of_device_id mdp_of_ids[] = { { .compatible = "mediatek,mt8183-mdp3-rdma", .data = &mt8183_mdp_driver_data, }, + { .compatible = "mediatek,mt8188-mdp3-rdma", + .data = &mt8188_mdp_driver_data, + }, { .compatible = "mediatek,mt8195-mdp3-rdma", .data = &mt8195_mdp_driver_data, }, @@ -309,6 +312,8 @@ static int mdp_probe(struct platform_device *pdev) ret = PTR_ERR(mdp->cmdq_clt[i]); goto err_mbox_destroy; } + + mdp->cmdq_shift_pa[i] = cmdq_get_shift_pa(mdp->cmdq_clt[i]->chan); } init_waitqueue_head(&mdp->callback_wq); @@ -380,14 +385,14 @@ static int __maybe_unused mdp_suspend(struct device *dev) atomic_set(&mdp->suspended, 1); - if (atomic_read(&mdp->job_count)) { + if (refcount_read(&mdp->job_count)) { ret = wait_event_timeout(mdp->callback_wq, - !atomic_read(&mdp->job_count), + !refcount_read(&mdp->job_count), 2 * HZ); if (ret == 0) { dev_err(dev, "%s:flushed cmdq task incomplete, count=%d\n", - __func__, atomic_read(&mdp->job_count)); + __func__, refcount_read(&mdp->job_count)); return -EBUSY; } } @@ -410,7 +415,7 @@ static const struct dev_pm_ops mdp_pm_ops = { static struct platform_driver mdp_driver = { .probe = mdp_probe, - .remove_new = mdp_remove, + .remove = mdp_remove, .driver = { .name = MDP_MODULE_NAME, .pm = &mdp_pm_ops, diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h index 8c09e984fd01..05cade1d098e 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h @@ -126,6 +126,7 @@ struct mdp_dev { u32 id_count; struct ida mdp_ida; struct cmdq_client *cmdq_clt[MDP_PP_MAX]; + u8 cmdq_shift_pa[MDP_PP_MAX]; wait_queue_head_t callback_wq; struct v4l2_device v4l2_dev; @@ -134,7 +135,7 @@ struct mdp_dev { /* synchronization protect for m2m device operation */ struct mutex m2m_lock; atomic_t suspended; - atomic_t job_count; + refcount_t job_count; }; struct mdp_pipe_info { diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c index 35a8b059bde5..59ce5cce0698 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c @@ -104,14 +104,14 @@ static void mdp_m2m_device_run(void *priv) task.cb_data = NULL; task.mdp_ctx = ctx; - if (atomic_read(&ctx->mdp_dev->job_count)) { + if (refcount_read(&ctx->mdp_dev->job_count)) { ret = wait_event_timeout(ctx->mdp_dev->callback_wq, - !atomic_read(&ctx->mdp_dev->job_count), + !refcount_read(&ctx->mdp_dev->job_count), 2 * HZ); if (ret == 0) { dev_err(&ctx->mdp_dev->pdev->dev, "%d jobs not yet done\n", - atomic_read(&ctx->mdp_dev->job_count)); + refcount_read(&ctx->mdp_dev->job_count)); goto worker_end; } } @@ -266,8 +266,6 @@ static void mdp_m2m_buf_queue(struct vb2_buffer *vb) static const struct vb2_ops mdp_m2m_qops = { .queue_setup = mdp_m2m_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_prepare = mdp_m2m_buf_prepare, .start_streaming = mdp_m2m_start_streaming, .stop_streaming = mdp_m2m_stop_streaming, diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c index 6bbe55de6ce9..ff23b225db70 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c @@ -79,6 +79,8 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use } fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return ERR_PTR(-ENOMEM); fw->type = SCP; fw->ops = &mtk_vcodec_rproc_msg; fw->scp = scp; diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c index 9ce34a3b5ee6..fc4e34c29192 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c @@ -49,7 +49,6 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem) { enum mtk_instance_type inst_type = *((unsigned int *)priv); struct platform_device *plat_dev; - unsigned long size = mem->size; int id; if (inst_type == MTK_INST_ENCODER) { @@ -64,15 +63,16 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem) id = dec_ctx->id; } - mem->va = dma_alloc_coherent(&plat_dev->dev, size, &mem->dma_addr, GFP_KERNEL); + mem->va = dma_alloc_attrs(&plat_dev->dev, mem->size, &mem->dma_addr, + GFP_KERNEL, DMA_ATTR_ALLOC_SINGLE_PAGES); if (!mem->va) { - mtk_v4l2_err(plat_dev, "%s dma_alloc size=%ld failed!", - dev_name(&plat_dev->dev), size); + mtk_v4l2_err(plat_dev, "%s dma_alloc size=0x%zx failed!", + __func__, mem->size); return -ENOMEM; } - mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%lx", id, mem->va, - (unsigned long)mem->dma_addr, size); + mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%zx", id, mem->va, + (unsigned long)mem->dma_addr, mem->size); return 0; } @@ -82,7 +82,6 @@ void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem) { enum mtk_instance_type inst_type = *((unsigned int *)priv); struct platform_device *plat_dev; - unsigned long size = mem->size; int id; if (inst_type == MTK_INST_ENCODER) { @@ -98,15 +97,16 @@ void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem) } if (!mem->va) { - mtk_v4l2_err(plat_dev, "%s dma_free size=%ld failed!", - dev_name(&plat_dev->dev), size); + mtk_v4l2_err(plat_dev, "%s: Tried to free a NULL VA", __func__); + if (mem->size) + mtk_v4l2_err(plat_dev, "Failed to free %zu bytes", mem->size); return; } - mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%lx", id, mem->va, - (unsigned long)mem->dma_addr, size); + mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%zx", id, mem->va, + (unsigned long)mem->dma_addr, mem->size); - dma_free_coherent(&plat_dev->dev, size, mem->va, mem->dma_addr); + dma_free_coherent(&plat_dev->dev, mem->size, mem->va, mem->dma_addr); mem->va = NULL; mem->dma_addr = 0; mem->size = 0; 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 ba742f0e391d..98838217b97d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -80,21 +80,18 @@ static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_dec_ctx *ctx, return &ctx->q_data[MTK_Q_DATA_DST]; } -static int vidioc_try_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int stateful_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { return v4l2_m2m_ioctl_try_decoder_cmd(file, priv, cmd); } - -static int vidioc_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int stateful_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct vb2_queue *src_vq, *dst_vq; int ret; - ret = vidioc_try_decoder_cmd(file, priv, cmd); + ret = stateful_try_decoder_cmd(file, priv, cmd); if (ret) return ret; @@ -128,6 +125,57 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, return 0; } +static int stateless_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + return v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd); +} + +static int stateless_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + int ret; + + ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd); + if (ret) + return ret; + + mtk_v4l2_vdec_dbg(3, ctx, "decoder cmd=%u", cmd->cmd); + switch (cmd->cmd) { + case V4L2_DEC_CMD_FLUSH: + /* + * If the flag of the output buffer is equals V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF, + * this command will prevent dequeueing the capture buffer containing the last + * decoded frame. Or do nothing + */ + break; + default: + mtk_v4l2_vdec_err(ctx, "invalid stateless decoder cmd=%u", cmd->cmd); + return -EINVAL; + } + + return 0; +} + +static int vidioc_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return stateless_try_decoder_cmd(file, priv, cmd); + + return stateful_try_decoder_cmd(file, priv, cmd); +} + +static int vidioc_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return stateless_decoder_cmd(file, priv, cmd); + + return stateful_decoder_cmd(file, priv, cmd); +} + void mtk_vdec_unlock(struct mtk_vcodec_dec_ctx *ctx) { mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); @@ -262,7 +310,7 @@ static int vidioc_try_fmt(struct mtk_vcodec_dec_ctx *ctx, struct v4l2_format *f, int tmp_w, tmp_h; /* - * Find next closer width align 64, heign align 64, size align + * Find next closer width align 64, height align 64, size align * 64 rectangle * Note: This only get default value, the real HW needed value * only available when ctx in MTK_STATE_HEADER state 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 2073781ccadb..9247d92d431d 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 @@ -591,7 +591,7 @@ static void mtk_vcodec_dec_remove(struct platform_device *pdev) static struct platform_driver mtk_vcodec_dec_driver = { .probe = mtk_vcodec_probe, - .remove_new = mtk_vcodec_dec_remove, + .remove = mtk_vcodec_dec_remove, .driver = { .name = MTK_VCODEC_DEC_NAME, .of_match_table = mtk_vcodec_match, 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 85b2c0d3d8bc..ac568ed14fa2 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 @@ -67,11 +67,11 @@ enum mtk_vdec_hw_arch { * @pic_w: picture width * @pic_h: picture height * @buf_w: picture buffer width (64 aligned up from pic_w) - * @buf_h: picture buffer heiht (64 aligned up from pic_h) + * @buf_h: picture buffer height (64 aligned up from pic_h) * @fb_sz: bitstream size of each plane * E.g. suppose picture size is 176x144, * buffer size will be aligned to 176x160. - * @cap_fourcc: fourcc number(may changed when resolution change) + * @cap_fourcc: fourcc number(may change on a resolution change) * @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os. */ struct vdec_pic_info { diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c index 11ca2c2fbaad..aa9bdee7a96c 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c @@ -595,11 +595,9 @@ static void mtk_init_vdec_params(struct mtk_vcodec_dec_ctx *ctx) } } -static struct vb2_ops mtk_vdec_frame_vb2_ops = { +static const struct vb2_ops mtk_vdec_frame_vb2_ops = { .queue_setup = vb2ops_vdec_queue_setup, .buf_prepare = vb2ops_vdec_buf_prepare, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vb2ops_vdec_start_streaming, .buf_queue = vb2ops_vdec_stateful_buf_queue, 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 b903e39fee89..afa224da0f41 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 @@ -854,10 +854,8 @@ static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb) return 0; } -static struct vb2_ops mtk_vdec_request_vb2_ops = { +static const struct vb2_ops mtk_vdec_request_vb2_ops = { .queue_setup = vb2ops_vdec_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vb2ops_vdec_start_streaming, .stop_streaming = vb2ops_vdec_stop_streaming, diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c index 2b6a5adbc419..bf21f2467a0f 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c @@ -326,7 +326,7 @@ struct vdec_av1_slice_quantization { * @use_lr: whether to use loop restoration * @use_chroma_lr: whether to use chroma loop restoration * @frame_restoration_type: specifies the type of restoration used for each plane - * @loop_restoration_size: pecifies the size of loop restoration units in units + * @loop_restoration_size: specifies the size of loop restoration units in units * of samples in the current plane */ struct vdec_av1_slice_lr { @@ -347,7 +347,7 @@ struct vdec_av1_slice_lr { * and loop_filter_sharpness together determine when * a block edge is filtered, and by how much the * filtering can change the sample values - * @loop_filter_delta_enabled: filetr level depends on the mode and reference + * @loop_filter_delta_enabled: filter level depends on the mode and reference * frame used to predict a block */ struct vdec_av1_slice_loop_filter { @@ -392,7 +392,7 @@ struct vdec_av1_slice_mfmv { /** * struct vdec_av1_slice_tile - AV1 Tile info * @tile_cols: specifies the number of tiles across the frame - * @tile_rows: pecifies the number of tiles down the frame + * @tile_rows: specifies the number of tiles down the frame * @mi_col_starts: an array specifying the start column * @mi_row_starts: an array specifying the start row * @context_update_tile_id: specifies which tile to use for the CDF update @@ -423,15 +423,15 @@ struct vdec_av1_slice_tile { * or the tile sizes are coded * @interpolation_filter: specifies the filter selection used for performing inter prediction * @allow_warped_motion: motion_mode may be present or not - * @is_motion_mode_switchable : euqlt to 0 specifies that only the SIMPLE motion mode will be used + * @is_motion_mode_switchable : equal to 0 specifies that only the SIMPLE motion mode will be used * @reference_mode : frame reference mode selected * @allow_high_precision_mv: specifies that motion vectors are specified to * quarter pel precision or to eighth pel precision - * @allow_intra_bc: ubducates that intra block copy may be used in this frame + * @allow_intra_bc: allows that intra block copy may be used in this frame * @force_integer_mv: specifies motion vectors will always be integers or * can contain fractional bits * @allow_screen_content_tools: intra blocks may use palette encoding - * @error_resilient_mode: error resislent mode is enable/disable + * @error_resilient_mode: error resilient mode is enable/disable * @frame_type: specifies the AV1 frame type * @primary_ref_frame: specifies which reference frame contains the CDF values * and other state that should be loaded at the start of the frame @@ -440,8 +440,8 @@ struct vdec_av1_slice_tile { * @disable_cdf_update: specified whether the CDF update in the symbol * decoding process should be disables * @skip_mode: av1 skip mode parameters - * @seg: av1 segmentaon parameters - * @delta_q_lf: av1 delta loop fileter + * @seg: av1 segmentation parameters + * @delta_q_lf: av1 delta loop filter * @quant: av1 Quantization params * @lr: av1 Loop Restauration parameters * @superres_denom: the denominator for the upscaling ratio @@ -450,8 +450,8 @@ struct vdec_av1_slice_tile { * @mfmv: av1 mfmv parameters * @tile: av1 Tile info * @frame_is_intra: intra frame - * @loss_less_array: loss less array - * @coded_loss_less: coded lsss less + * @loss_less_array: lossless array + * @coded_loss_less: coded lossless * @mi_rows: size of mi unit in rows * @mi_cols: size of mi unit in cols */ @@ -1023,18 +1023,26 @@ static void vdec_av1_slice_free_working_buffer(struct vdec_av1_slice_instance *i int i; for (i = 0; i < ARRAY_SIZE(instance->mv); i++) - mtk_vcodec_mem_free(ctx, &instance->mv[i]); + if (instance->mv[i].va) + mtk_vcodec_mem_free(ctx, &instance->mv[i]); for (i = 0; i < ARRAY_SIZE(instance->seg); i++) - mtk_vcodec_mem_free(ctx, &instance->seg[i]); + if (instance->seg[i].va) + mtk_vcodec_mem_free(ctx, &instance->seg[i]); for (i = 0; i < ARRAY_SIZE(instance->cdf); i++) - mtk_vcodec_mem_free(ctx, &instance->cdf[i]); + if (instance->cdf[i].va) + mtk_vcodec_mem_free(ctx, &instance->cdf[i]); + - mtk_vcodec_mem_free(ctx, &instance->tile); - mtk_vcodec_mem_free(ctx, &instance->cdf_temp); - mtk_vcodec_mem_free(ctx, &instance->cdf_table); - mtk_vcodec_mem_free(ctx, &instance->iq_table); + if (instance->tile.va) + mtk_vcodec_mem_free(ctx, &instance->tile); + if (instance->cdf_temp.va) + mtk_vcodec_mem_free(ctx, &instance->cdf_temp); + if (instance->cdf_table.va) + mtk_vcodec_mem_free(ctx, &instance->cdf_table); + if (instance->iq_table.va) + mtk_vcodec_mem_free(ctx, &instance->iq_table); instance->level = AV1_RES_NONE; } diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c index bf7dffe60d07..795cb19b075d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c @@ -94,7 +94,7 @@ struct vdec_h264_dec_info { * AP-W/R : AP is writer/reader on this item * VPU-W/R: VPU is write/reader on this item * @hdr_buf : Header parsing buffer (AP-W, VPU-R) - * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) + * @pred_buf_dma : HW working prediction buffer dma address (AP-W, VPU-R) * @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R) * @list_free : free frame buffer ring list (AP-W/R, VPU-W) * @list_disp : display frame buffer ring list (AP-R, VPU-W) @@ -117,7 +117,7 @@ struct vdec_h264_vsi { * struct vdec_h264_inst - h264 decoder instance * @num_nalu : how many nalus be decoded * @ctx : point to mtk_vcodec_dec_ctx - * @pred_buf : HW working predication buffer + * @pred_buf : HW working prediction buffer * @mv_buf : HW working motion vector buffer * @vpu : VPU instance * @vsi : VPU shared information @@ -136,7 +136,7 @@ static unsigned int get_mv_buf_size(unsigned int width, unsigned int height) return HW_MB_STORE_SZ * (width/MB_UNIT_LEN) * (height/MB_UNIT_LEN); } -static int allocate_predication_buf(struct vdec_h264_inst *inst) +static int allocate_prediction_buf(struct vdec_h264_inst *inst) { int err = 0; @@ -151,7 +151,7 @@ static int allocate_predication_buf(struct vdec_h264_inst *inst) return 0; } -static void free_predication_buf(struct vdec_h264_inst *inst) +static void free_prediction_buf(struct vdec_h264_inst *inst) { struct mtk_vcodec_mem *mem = NULL; @@ -286,7 +286,7 @@ static int vdec_h264_init(struct mtk_vcodec_dec_ctx *ctx) } inst->vsi = (struct vdec_h264_vsi *)inst->vpu.vsi; - err = allocate_predication_buf(inst); + err = allocate_prediction_buf(inst); if (err) goto error_deinit; @@ -308,7 +308,7 @@ static void vdec_h264_deinit(void *h_vdec) struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; vpu_dec_deinit(&inst->vpu); - free_predication_buf(inst); + free_prediction_buf(inst); free_mv_buf(inst); kfree(inst); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h index ac82be336055..31ffa13160a3 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h @@ -175,7 +175,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list, int num_valid); /** - * mtk_vdec_h264_get_ctrl_ptr - get each CID contrl address. + * mtk_vdec_h264_get_ctrl_ptr - get each CID control address. * * @ctx: v4l2 ctx * @id: CID control ID @@ -185,7 +185,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list, void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id); /** - * mtk_vdec_h264_fill_dpb_info - get each CID contrl address. + * mtk_vdec_h264_fill_dpb_info - Fill the decoded picture buffer info * * @ctx: v4l2 ctx * @decode_params: slice decode params @@ -225,10 +225,13 @@ void mtk_vdec_h264_copy_slice_hd_params(struct mtk_h264_slice_hd_param *dst_para const struct v4l2_ctrl_h264_decode_params *dec_param); /** - * mtk_vdec_h264_copy_scaling_matrix - get each CID contrl address. + * mtk_vdec_h264_copy_scaling_matrix - Copy scaling matrix from a control to the driver * - * @dst_matrix: scaling list params for hw decoder - * @src_matrix: scaling list params from user driver + * @dst_matrix: scaling list params for the HW decoder + * @src_matrix: scaling list params from a V4L2 control + * + * This function is used to copy the scaling matrix from a + * v4l2 control into the slice parameters for a decode. */ void mtk_vdec_h264_copy_scaling_matrix(struct slice_api_h264_scaling_matrix *dst_matrix, const struct v4l2_ctrl_h264_scaling_matrix *src_matrix); @@ -246,7 +249,7 @@ mtk_vdec_h264_copy_decode_params(struct slice_api_h264_decode_param *dst_params, const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]); /** - * mtk_vdec_h264_update_dpb - updata dpb list. + * mtk_vdec_h264_update_dpb - update dpb list. * * @dec_param: v4l2 control decode params * @dpb: dpb entry informaton diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c index 5600f1df653d..1e1b32faac77 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c @@ -27,7 +27,7 @@ struct mtk_h264_dec_slice_param { /** * struct vdec_h264_dec_info - decode information * @dpb_sz : decoding picture buffer size - * @resolution_changed : resoltion change happen + * @resolution_changed : flag to notify that a resolution change happened * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer * @cap_num_planes : number planes of capture buffer * @bs_dma : Input bit-stream buffer dma address @@ -54,7 +54,7 @@ struct vdec_h264_dec_info { * by VPU. * AP-W/R : AP is writer/reader on this item * VPU-W/R: VPU is write/reader on this item - * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) + * @pred_buf_dma : HW working prediction buffer dma address (AP-W, VPU-R) * @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R) * @dec : decode information (AP-R, VPU-W) * @pic : picture information (AP-R, VPU-W) @@ -74,7 +74,7 @@ struct vdec_h264_vsi { * struct vdec_h264_slice_inst - h264 decoder instance * @num_nalu : how many nalus be decoded * @ctx : point to mtk_vcodec_dec_ctx - * @pred_buf : HW working predication buffer + * @pred_buf : HW working prediction buffer * @mv_buf : HW working motion vector buffer * @vpu : VPU instance * @vsi_ctx : Local VSI data for this decoding context @@ -154,7 +154,7 @@ static int get_vdec_decode_parameters(struct vdec_h264_slice_inst *inst) return 0; } -static int allocate_predication_buf(struct vdec_h264_slice_inst *inst) +static int allocate_prediction_buf(struct vdec_h264_slice_inst *inst) { int err; @@ -169,7 +169,7 @@ static int allocate_predication_buf(struct vdec_h264_slice_inst *inst) return 0; } -static void free_predication_buf(struct vdec_h264_slice_inst *inst) +static void free_prediction_buf(struct vdec_h264_slice_inst *inst) { struct mtk_vcodec_mem *mem = &inst->pred_buf; @@ -292,7 +292,7 @@ static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx) inst->vsi_ctx.dec.resolution_changed = true; inst->vsi_ctx.dec.realloc_mv_buf = true; - err = allocate_predication_buf(inst); + err = allocate_prediction_buf(inst); if (err) goto error_deinit; @@ -320,7 +320,7 @@ static void vdec_h264_slice_deinit(void *h_vdec) struct vdec_h264_slice_inst *inst = h_vdec; vpu_dec_deinit(&inst->vpu); - free_predication_buf(inst); + free_prediction_buf(inst); free_mv_buf(inst); kfree(inst); @@ -347,11 +347,16 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, return vpu_dec_reset(vpu); fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); + if (!fb) { + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + return -ENOMEM; + } + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + y_fb_dma = fb->base_y.dma_addr; + c_fb_dma = fb->base_c.dma_addr; mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", inst->num_nalu, y_fb_dma, c_fb_dma, fb); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index 0e741e0dc8ba..1ed0ccec5665 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -51,7 +51,7 @@ struct vdec_h264_slice_lat_dec_param { * struct vdec_h264_slice_info - decode information * * @nal_info: nal info of current picture - * @timeout: Decode timeout: 1 timeout, 0 no timeount + * @timeout: Decode timeout: 1 timeout, 0 no timeout * @bs_buf_size: bitstream size * @bs_buf_addr: bitstream buffer dma address * @y_fb_dma: Y frame buffer dma address @@ -131,9 +131,9 @@ struct vdec_h264_slice_share_info { /** * struct vdec_h264_slice_inst - h264 decoder instance * - * @slice_dec_num: how many picture be decoded + * @slice_dec_num: Number of frames to be decoded * @ctx: point to mtk_vcodec_dec_ctx - * @pred_buf: HW working predication buffer + * @pred_buf: HW working prediction buffer * @mv_buf: HW working motion vector buffer * @vpu: VPU instance * @vsi: vsi used for lat @@ -724,11 +724,16 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs return vpu_dec_reset(vpu); fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); + if (!fb) { + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + return -ENOMEM; + } + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + y_fb_dma = fb->base_y.dma_addr; + c_fb_dma = fb->base_c.dma_addr; mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx", inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c index 21836dd6ef85..aa721cc43647 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c @@ -254,7 +254,7 @@ struct vdec_hevc_slice_lat_dec_param { * struct vdec_hevc_slice_info - decode information * * @wdma_end_addr_offset: wdma end address offset - * @timeout: Decode timeout: 1 timeout, 0 no timeount + * @timeout: Decode timeout: 1 timeout, 0 no timeout * @vdec_fb_va: VDEC frame buffer struct virtual address * @crc: Used to check whether hardware's status is right */ @@ -342,7 +342,7 @@ struct vdec_hevc_slice_share_info { /** * struct vdec_hevc_slice_inst - hevc decoder instance * - * @slice_dec_num: how many picture be decoded + * @slice_dec_num: Number of frames to be decoded * @ctx: point to mtk_vcodec_dec_ctx * @mv_buf: HW working motion vector buffer * @vpu: VPU instance diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c index 987b3d71b662..5f848691cea4 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c @@ -56,7 +56,7 @@ * @cur_c_fb_dma : current plane C frame buffer dma address * @bs_dma : bitstream dma address * @bs_sz : bitstream size - * @resolution_changed: resolution change flag 1 - changed, 0 - not change + * @resolution_changed: resolution change flag 1 - changed, 0 - not changed * @show_frame : display this frame or not * @wait_key_frame : wait key frame coming */ @@ -109,7 +109,7 @@ struct vdec_vp8_hw_reg_base { /** * struct vdec_vp8_vpu_inst - VPU instance for VP8 decode * @wq_hd : Wait queue to wait VPU message ack - * @signaled : 1 - Host has received ack message from VPU, 0 - not receive + * @signaled : 1 - Host has received ack message from VPU, 0 - not received * @failure : VPU execution result status 0 - success, others - fail * @inst_addr : VPU decoder instance address */ @@ -449,7 +449,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->frm_cnt, y_fb_dma, c_fb_dma, fb); inst->cur_fb = fb; - dec->bs_dma = (uint64_t)bs->dma_addr; + dec->bs_dma = bs->dma_addr; dec->bs_sz = bs->size; dec->cur_y_fb_dma = y_fb_dma; dec->cur_c_fb_dma = c_fb_dma; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c index f677e499fefa..232ef3bd246a 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c @@ -35,7 +35,7 @@ * @cur_c_fb_dma: current plane C frame buffer dma address * @bs_dma: bitstream dma address * @bs_sz: bitstream size - * @resolution_changed:resolution change flag 1 - changed, 0 - not change + * @resolution_changed:resolution change flag 1 - changed, 0 - not changed * @frame_header_type: current frame header type * @crc: used to check whether hardware's status is right * @reserved: reserved, currently unused @@ -334,14 +334,18 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); - dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); + if (!fb) { + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + return -ENOMEM; + } - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; + dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); + y_fb_dma = fb->base_y.dma_addr; if (inst->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) c_fb_dma = y_fb_dma + inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h; else - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + c_fb_dma = fb->base_c.dma_addr; inst->vsi->dec.bs_dma = (u64)bs->dma_addr; inst->vsi->dec.bs_sz = bs->size; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c index 039082f600c8..eb3354192853 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c @@ -42,7 +42,7 @@ struct vp9_dram_buf { /** * struct vp9_fb_info - contains frame buffer info - * @fb : frmae buffer + * @fb : frame buffer * @reserved : reserved field used by vpu */ struct vp9_fb_info { @@ -90,7 +90,7 @@ struct vp9_sf_ref_fb { * AP-W/R : AP is writer/reader on this item * VPU-W/R: VPU is write/reader on this item * @sf_bs_buf : super frame backup buffer (AP-W, VPU-R) - * @sf_ref_fb : record supoer frame reference buffer information + * @sf_ref_fb : record super frame reference buffer information * (AP-R/W, VPU-R/W) * @sf_next_ref_fb_idx : next available super frame (AP-W, VPU-R) * @sf_frm_cnt : super frame count, filled by vpu (AP-R, VPU-W) 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 eea709d93820..47c302745c1d 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 @@ -1188,7 +1188,8 @@ err: return ret; } -static +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack void vdec_vp9_slice_map_counts_eob_coef(unsigned int i, unsigned int j, unsigned int k, struct vdec_vp9_slice_frame_counts *counts, struct v4l2_vp9_frame_symbol_counts *counts_helper) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h index 1d9beb9e4a14..b0f576867f4b 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h @@ -158,14 +158,14 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *ctx); /** - * vdec_msg_queue_update_ube_rptr - used to updata the ube read point. + * vdec_msg_queue_update_ube_rptr - used to update the ube read point. * @msg_queue: used to store the lat buffer information * @ube_rptr: current ube read point */ void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr); /** - * vdec_msg_queue_update_ube_wptr - used to updata the ube write point. + * vdec_msg_queue_update_ube_wptr - used to update the ube write point. * @msg_queue: used to store the lat buffer information * @ube_wptr: current ube write point */ diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index da6be556727b..145958206e38 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -233,6 +233,12 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu); err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); + + if (IS_ERR_OR_NULL(vpu->vsi)) { + mtk_vdec_err(vpu->ctx, "invalid vdec vsi, status=%d", err); + return -EINVAL; + } + mtk_vdec_debug(vpu->ctx, "- ret=%d", err); return err; } diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h index aa7d08afc2f4..57ed9b1f5eaa 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h @@ -81,7 +81,7 @@ int vpu_dec_deinit(struct vdec_vpu_inst *vpu); /** * vpu_dec_reset - reset decoder, use for flush decoder when end of stream or - * seek. Remainig non displayed frame will be pushed to display. + * seek. Remaining non displayed frame will be pushed to display. * * @vpu: instance for vdec_vpu_inst */ @@ -98,7 +98,7 @@ int vpu_dec_core(struct vdec_vpu_inst *vpu); /** * vpu_dec_core_end - core end decoding, basically the function will be invoked once * when core HW decoding done and receive interrupt successfully. The - * decoder in VPU will updata hardware information and deinit hardware + * decoder in VPU will update hardware information and deinit hardware * and check if there is a new decoded frame available to display. * * @vpu : instance for vdec_vpu_inst 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 181884e798fd..a01dc25a7699 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c @@ -311,7 +311,7 @@ static int vidioc_try_fmt_out(struct mtk_vcodec_enc_ctx *ctx, struct v4l2_format pix_fmt_mp->height = clamp(pix_fmt_mp->height, MTK_VENC_MIN_H, max_height); pix_fmt_mp->width = clamp(pix_fmt_mp->width, MTK_VENC_MIN_W, max_width); - /* find next closer width align 16, heign align 32, size align + /* find next closer width align 16, height align 32, size align * 64 rectangle */ tmp_w = pix_fmt_mp->width; @@ -1009,8 +1009,6 @@ static const struct vb2_ops mtk_venc_vb2_ops = { .buf_out_validate = vb2ops_venc_buf_out_validate, .buf_prepare = vb2ops_venc_buf_prepare, .buf_queue = vb2ops_venc_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vb2ops_venc_start_streaming, .stop_streaming = vb2ops_venc_stop_streaming, }; diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c index 3cb8a1622222..a1e4483abcdb 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c @@ -473,7 +473,7 @@ static void mtk_vcodec_enc_remove(struct platform_device *pdev) static struct platform_driver mtk_vcodec_enc_driver = { .probe = mtk_vcodec_probe, - .remove_new = mtk_vcodec_enc_remove, + .remove = mtk_vcodec_enc_remove, .driver = { .name = MTK_VCODEC_ENC_NAME, .of_match_table = mtk_vcodec_enc_match, diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c index a22b7dfc656e..1a2b14a3e219 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c @@ -58,13 +58,15 @@ int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *mtkdev) return 0; } -void mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm) +int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm) { int ret; ret = pm_runtime_resume_and_get(pm->dev); if (ret) dev_err(pm->dev, "pm_runtime_resume_and_get fail: %d", ret); + + return ret; } void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm) diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h index 157ea08ba9e3..2e28f25e36cc 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h @@ -10,7 +10,7 @@ #include "mtk_vcodec_enc_drv.h" int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *dev); -void mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm); +int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm); void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm); void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c index a68dac72c4e4..f8145998fcaf 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c @@ -301,11 +301,12 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst) * other buffers need to be freed by AP. */ for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { - if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME) + if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME && inst->work_bufs[i].va) mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); } - mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); + if (inst->pps_buf.va) + mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); } static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit) diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c index c402a686f3cb..e83747b8d69a 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c @@ -64,7 +64,9 @@ int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx, ctx->dev->curr_ctx = ctx; spin_unlock_irqrestore(&ctx->dev->irqlock, flags); - mtk_vcodec_enc_pw_on(&ctx->dev->pm); + ret = mtk_vcodec_enc_pw_on(&ctx->dev->pm); + if (ret) + goto venc_if_encode_pw_on_err; mtk_vcodec_enc_clock_on(&ctx->dev->pm); ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf, bs_buf, result); @@ -75,6 +77,7 @@ int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx, ctx->dev->curr_ctx = NULL; spin_unlock_irqrestore(&ctx->dev->irqlock, flags); +venc_if_encode_pw_on_err: mtk_venc_unlock(ctx); return ret; } diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h index d00fb68b8235..889440a436b6 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h @@ -156,7 +156,7 @@ int venc_if_set_param(struct mtk_vcodec_enc_ctx *ctx, * @ctx: device context * @opt: encode frame option * @frm_buf: input frame buffer information - * @bs_buf: output bitstream buffer infomraiton + * @bs_buf: output bitstream buffer information * @result: encode result * Return: 0 if encoding frame successfully, otherwise it is failed. */ diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c index 724ae7c2ab3b..8d8319f0cd22 100644 --- a/drivers/media/platform/mediatek/vpu/mtk_vpu.c +++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c @@ -1041,7 +1041,7 @@ static const struct dev_pm_ops mtk_vpu_pm = { static struct platform_driver mtk_vpu_driver = { .probe = mtk_vpu_probe, - .remove_new = mtk_vpu_remove, + .remove = mtk_vpu_remove, .driver = { .name = "mtk_vpu", .pm = &mtk_vpu_pm, diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index fee73260bb1e..70303a0b6919 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -782,7 +782,7 @@ MODULE_DEVICE_TABLE(of, csi2dc_of_match); static struct platform_driver csi2dc_driver = { .probe = csi2dc_probe, - .remove_new = csi2dc_remove, + .remove = csi2dc_remove, .driver = { .name = "microchip-csi2dc", .pm = &csi2dc_dev_pm_ops, diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index f3a5cbacadbe..a7cdc743fda7 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -465,8 +465,6 @@ static void isc_buffer_queue(struct vb2_buffer *vb) static const struct vb2_ops isc_vb2_ops = { .queue_setup = isc_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_prepare = isc_buffer_prepare, .start_streaming = isc_start_streaming, .stop_streaming = isc_stop_streaming, @@ -902,8 +900,11 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) return 0; } -static int isc_validate(struct isc_device *isc) +static int isc_link_validate(struct media_link *link) { + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct isc_device *isc = video_get_drvdata(vdev); int ret; int i; struct isc_format *sd_fmt = NULL; @@ -1906,20 +1907,6 @@ int microchip_isc_pipeline_init(struct isc_device *isc) } EXPORT_SYMBOL_GPL(microchip_isc_pipeline_init); -static int isc_link_validate(struct media_link *link) -{ - struct video_device *vdev = - media_entity_to_video_device(link->sink->entity); - struct isc_device *isc = video_get_drvdata(vdev); - int ret; - - ret = v4l2_subdev_link_validate(link); - if (ret) - return ret; - - return isc_validate(isc); -} - static const struct media_entity_operations isc_entity_operations = { .link_validate = isc_link_validate, }; diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c index 5ac149cf3647..66d3d7891991 100644 --- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c +++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c @@ -353,33 +353,29 @@ static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { static int isc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL; + struct device_node *epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; - int ret; INIT_LIST_HEAD(&isc->subdev_entities); - while (1) { + for_each_endpoint_of_node(np, epn) { struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - return 0; + int ret; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { - ret = -EINVAL; + of_node_put(epn); dev_err(dev, "Could not parse the endpoint\n"); - break; + return -EINVAL; } subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), GFP_KERNEL); if (!subdev_entity) { - ret = -ENOMEM; - break; + of_node_put(epn); + return -ENOMEM; } subdev_entity->epn = epn; @@ -400,9 +396,8 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) list_add_tail(&subdev_entity->list, &isc->subdev_entities); } - of_node_put(epn); - return ret; + return 0; } static int microchip_isc_probe(struct platform_device *pdev) @@ -663,7 +658,7 @@ MODULE_DEVICE_TABLE(of, microchip_isc_of_match); static struct platform_driver microchip_isc_driver = { .probe = microchip_isc_probe, - .remove_new = microchip_isc_remove, + .remove = microchip_isc_remove, .driver = { .name = "microchip-sama5d2-isc", .pm = µchip_isc_dev_pm_ops, diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c index 73445f33d26b..b0302dfc3278 100644 --- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c +++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c @@ -336,36 +336,32 @@ static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { static int xisc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL; + struct device_node *epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; - int ret; bool mipi_mode; INIT_LIST_HEAD(&isc->subdev_entities); mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); - while (1) { + for_each_endpoint_of_node(np, epn) { struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - return 0; + int ret; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { - ret = -EINVAL; + of_node_put(epn); dev_err(dev, "Could not parse the endpoint\n"); - break; + return -EINVAL; } subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), GFP_KERNEL); if (!subdev_entity) { - ret = -ENOMEM; - break; + of_node_put(epn); + return -ENOMEM; } subdev_entity->epn = epn; @@ -389,9 +385,8 @@ static int xisc_parse_dt(struct device *dev, struct isc_device *isc) list_add_tail(&subdev_entity->list, &isc->subdev_entities); } - of_node_put(epn); - return ret; + return 0; } static int microchip_xisc_probe(struct platform_device *pdev) @@ -626,7 +621,7 @@ MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); static struct platform_driver microchip_xisc_driver = { .probe = microchip_xisc_probe, - .remove_new = microchip_xisc_remove, + .remove = microchip_xisc_remove, .driver = { .name = "microchip-sama7g5-xisc", .pm = µchip_xisc_dev_pm_ops, diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 60fbb9140035..024cd8ee1709 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1558,8 +1558,6 @@ static const struct regmap_config npcm_video_ece_regmap_cfg = { static const struct vb2_ops npcm_video_vb2_ops = { .queue_setup = npcm_video_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_prepare = npcm_video_buf_prepare, .buf_finish = npcm_video_buf_finish, .start_streaming = npcm_video_start_streaming, @@ -1667,9 +1665,9 @@ static int npcm_video_ece_init(struct npcm_video *video) dev_info(dev, "Support HEXTILE pixel format\n"); ece_pdev = of_find_device_by_node(ece_node); - if (IS_ERR(ece_pdev)) { + if (!ece_pdev) { dev_err(dev, "Failed to find ECE device\n"); - return PTR_ERR(ece_pdev); + return -ENODEV; } of_node_put(ece_node); @@ -1814,7 +1812,7 @@ static struct platform_driver npcm_video_driver = { .of_match_table = npcm_video_match, }, .probe = npcm_video_probe, - .remove_new = npcm_video_remove, + .remove = npcm_video_remove, }; module_platform_driver(npcm_video_driver); diff --git a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c index 1c5b94989aec..b34244ea14dd 100644 --- a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c +++ b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c @@ -16,7 +16,7 @@ #include "vde.h" -MODULE_IMPORT_NS(DMA_BUF); +MODULE_IMPORT_NS("DMA_BUF"); struct tegra_vde_cache_entry { enum dma_data_direction dma_dir; diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c index 204e474d57f7..0e56a4331b0d 100644 --- a/drivers/media/platform/nvidia/tegra-vde/h264.c +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -19,11 +19,6 @@ #define FLAG_B_FRAME 0x1 #define FLAG_REFERENCE 0x2 -struct tegra_vde_h264_frame { - unsigned int frame_num; - unsigned int flags; -}; - struct tegra_vde_h264_decoder_ctx { unsigned int dpb_frames_nb; unsigned int dpb_ref_frames_with_earlier_poc_nb; @@ -628,12 +623,14 @@ static int tegra_vde_decode_end(struct tegra_vde *vde) unsigned int read_bytes, macroblocks_nb; struct device *dev = vde->dev; dma_addr_t bsev_ptr; - long timeout; + long time_left; int ret; - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &vde->decode_completion, msecs_to_jiffies(1000)); - if (timeout == 0) { + if (time_left < 0) { + ret = time_left; + } else if (time_left == 0) { bsev_ptr = tegra_vde_readl(vde, vde->bsev, 0x10); macroblocks_nb = tegra_vde_readl(vde, vde->sxe, 0xC8) & 0x1FFF; read_bytes = bsev_ptr ? bsev_ptr - vde->bitstream_data_addr : 0; @@ -642,8 +639,6 @@ static int tegra_vde_decode_end(struct tegra_vde *vde) read_bytes, macroblocks_nb); ret = -EIO; - } else if (timeout < 0) { - ret = timeout; } else { ret = 0; } diff --git a/drivers/media/platform/nvidia/tegra-vde/iommu.c b/drivers/media/platform/nvidia/tegra-vde/iommu.c index 5521ed3e465f..b1d9d841d944 100644 --- a/drivers/media/platform/nvidia/tegra-vde/iommu.c +++ b/drivers/media/platform/nvidia/tegra-vde/iommu.c @@ -78,9 +78,10 @@ int tegra_vde_iommu_init(struct tegra_vde *vde) arm_iommu_release_mapping(mapping); } #endif - vde->domain = iommu_domain_alloc(&platform_bus_type); - if (!vde->domain) { - err = -ENOMEM; + vde->domain = iommu_paging_domain_alloc(dev); + if (IS_ERR(vde->domain)) { + err = PTR_ERR(vde->domain); + vde->domain = NULL; goto put_group; } diff --git a/drivers/media/platform/nvidia/tegra-vde/trace.h b/drivers/media/platform/nvidia/tegra-vde/trace.h index 7853ab095ca4..e8a75a7bd05d 100644 --- a/drivers/media/platform/nvidia/tegra-vde/trace.h +++ b/drivers/media/platform/nvidia/tegra-vde/trace.h @@ -20,7 +20,7 @@ DECLARE_EVENT_CLASS(register_access, __field(u32, value) ), TP_fast_assign( - __assign_str(hw_name, tegra_vde_reg_base_name(vde, base)); + __assign_str(hw_name); __entry->offset = offset; __entry->value = value; ), diff --git a/drivers/media/platform/nvidia/tegra-vde/v4l2.c b/drivers/media/platform/nvidia/tegra-vde/v4l2.c index 0f48ce6f243e..e3726cab0c82 100644 --- a/drivers/media/platform/nvidia/tegra-vde/v4l2.c +++ b/drivers/media/platform/nvidia/tegra-vde/v4l2.c @@ -328,8 +328,6 @@ static const struct vb2_ops tegra_qops = { .buf_request_complete = tegra_buf_request_complete, .start_streaming = tegra_start_streaming, .stop_streaming = tegra_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int tegra_queue_init(void *priv, @@ -929,13 +927,13 @@ int tegra_vde_v4l2_init(struct tegra_vde *vde) media_device_init(&vde->mdev); video_set_drvdata(&vde->vdev, vde); - vde->vdev.lock = &vde->v4l2_lock, - vde->vdev.fops = &tegra_v4l2_fops, - vde->vdev.vfl_dir = VFL_DIR_M2M, - vde->vdev.release = video_device_release_empty, + vde->vdev.lock = &vde->v4l2_lock; + vde->vdev.fops = &tegra_v4l2_fops; + vde->vdev.vfl_dir = VFL_DIR_M2M; + vde->vdev.release = video_device_release_empty; vde->vdev.v4l2_dev = &vde->v4l2_dev; - vde->vdev.ioctl_ops = &tegra_v4l2_ioctl_ops, - vde->vdev.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, + vde->vdev.ioctl_ops = &tegra_v4l2_ioctl_ops; + vde->vdev.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; vde->v4l2_dev.mdev = &vde->mdev; vde->mdev.ops = &tegra_media_device_ops; diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.c b/drivers/media/platform/nvidia/tegra-vde/vde.c index 81a0d3b76b88..3232392c60e2 100644 --- a/drivers/media/platform/nvidia/tegra-vde/vde.c +++ b/drivers/media/platform/nvidia/tegra-vde/vde.c @@ -535,7 +535,7 @@ MODULE_DEVICE_TABLE(of, tegra_vde_of_match); static struct platform_driver tegra_vde_driver = { .probe = tegra_vde_probe, - .remove_new = tegra_vde_remove, + .remove = tegra_vde_remove, .shutdown = tegra_vde_shutdown, .driver = { .name = "tegra-vde", diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.h b/drivers/media/platform/nvidia/tegra-vde/vde.h index 0fbb1f3d2c88..b2890484b7c3 100644 --- a/drivers/media/platform/nvidia/tegra-vde/vde.h +++ b/drivers/media/platform/nvidia/tegra-vde/vde.h @@ -47,7 +47,6 @@ struct iommu_group; struct iommu_domain; struct reset_control; struct dma_buf_attachment; -struct tegra_vde_h264_frame; struct tegra_vde_h264_decoder_ctx; struct tegra_video_frame { diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c index 0024d6175ad9..66582e7f92fc 100644 --- a/drivers/media/platform/nxp/dw100/dw100.c +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -558,8 +558,6 @@ static const struct vb2_ops dw100_qops = { .buf_queue = dw100_buf_queue, .start_streaming = dw100_start_streaming, .stop_streaming = dw100_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq, @@ -1311,7 +1309,7 @@ static void dw100_hw_set_destination(struct dw100_device *dw_dev, } dev_dbg(&dw_dev->pdev->dev, - "Set HW source registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n", + "Set HW destination registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n", width, height, stride, &fourcc, &addr_y); /* Pixel Format */ @@ -1688,7 +1686,7 @@ MODULE_DEVICE_TABLE(of, dw100_dt_ids); static struct platform_driver dw100_driver = { .probe = dw100_probe, - .remove_new = dw100_remove, + .remove = dw100_remove, .driver = { .name = DRV_NAME, .pm = &dw100_pm, diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index cc97790ed30f..1221b309a916 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -1634,6 +1634,9 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx); q_data->sequence = 0; + if (V4L2_TYPE_IS_CAPTURE(q->type)) + ctx->need_initial_source_change_evt = false; + ret = pm_runtime_resume_and_get(ctx->mxc_jpeg->dev); if (ret < 0) { dev_err(ctx->mxc_jpeg->dev, "Failed to power up jpeg\n"); @@ -1840,17 +1843,6 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb) q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (q_data_out->w == 0 && q_data_out->h == 0) { - dev_warn(dev, "Invalid user resolution 0x0"); - dev_warn(dev, "Keeping resolution from JPEG: %dx%d", - header.frame.width, header.frame.height); - } else if (header.frame.width != q_data_out->w || - header.frame.height != q_data_out->h) { - dev_err(dev, - "Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)", - header.frame.width, header.frame.height, - q_data_out->w, q_data_out->h); - } q_data_out->w = header.frame.width; q_data_out->h = header.frame.height; if (header.frame.width > MXC_JPEG_MAX_WIDTH || @@ -1973,8 +1965,6 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb) static const struct vb2_ops mxc_jpeg_qops = { .queue_setup = mxc_jpeg_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_out_validate = mxc_jpeg_buf_out_validate, .buf_prepare = mxc_jpeg_buf_prepare, .start_streaming = mxc_jpeg_start_streaming, @@ -2687,9 +2677,12 @@ static void mxc_jpeg_detach_pm_domains(struct mxc_jpeg_dev *jpeg) int i; for (i = 0; i < jpeg->num_domains; i++) { - if (jpeg->pd_link[i] && !IS_ERR(jpeg->pd_link[i])) + if (!IS_ERR_OR_NULL(jpeg->pd_dev[i]) && + !pm_runtime_suspended(jpeg->pd_dev[i])) + pm_runtime_force_suspend(jpeg->pd_dev[i]); + if (!IS_ERR_OR_NULL(jpeg->pd_link[i])) device_link_del(jpeg->pd_link[i]); - if (jpeg->pd_dev[i] && !IS_ERR(jpeg->pd_dev[i])) + if (!IS_ERR_OR_NULL(jpeg->pd_dev[i])) dev_pm_domain_detach(jpeg->pd_dev[i], true); jpeg->pd_dev[i] = NULL; jpeg->pd_link[i] = NULL; @@ -2850,6 +2843,7 @@ static int mxc_jpeg_probe(struct platform_device *pdev) jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M; jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + video_set_drvdata(jpeg->dec_vdev, jpeg); if (mode == MXC_JPEG_ENCODE) { v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_DECODER_CMD); v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_TRY_DECODER_CMD); @@ -2862,7 +2856,6 @@ static int mxc_jpeg_probe(struct platform_device *pdev) dev_err(dev, "failed to register video device\n"); goto err_vdev_register; } - video_set_drvdata(jpeg->dec_vdev, jpeg); if (mode == MXC_JPEG_ENCODE) v4l2_info(&jpeg->v4l2_dev, "encoder device registered as /dev/video%d (%d,%d)\n", @@ -2896,7 +2889,6 @@ err_clk: return ret; } -#ifdef CONFIG_PM static int mxc_jpeg_runtime_resume(struct device *dev) { struct mxc_jpeg_dev *jpeg = dev_get_drvdata(dev); @@ -2919,9 +2911,7 @@ static int mxc_jpeg_runtime_suspend(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int mxc_jpeg_suspend(struct device *dev) { struct mxc_jpeg_dev *jpeg = dev_get_drvdata(dev); @@ -2942,12 +2932,10 @@ static int mxc_jpeg_resume(struct device *dev) v4l2_m2m_resume(jpeg->m2m_dev); return ret; } -#endif static const struct dev_pm_ops mxc_jpeg_pm_ops = { - SET_RUNTIME_PM_OPS(mxc_jpeg_runtime_suspend, - mxc_jpeg_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(mxc_jpeg_suspend, mxc_jpeg_resume) + RUNTIME_PM_OPS(mxc_jpeg_runtime_suspend, mxc_jpeg_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mxc_jpeg_suspend, mxc_jpeg_resume) }; static void mxc_jpeg_remove(struct platform_device *pdev) @@ -2967,11 +2955,11 @@ MODULE_DEVICE_TABLE(of, mxc_jpeg_match); static struct platform_driver mxc_jpeg_driver = { .probe = mxc_jpeg_probe, - .remove_new = mxc_jpeg_remove, + .remove = mxc_jpeg_remove, .driver = { .name = "mxc-jpeg", .of_match_table = mxc_jpeg_match, - .pm = &mxc_jpeg_pm_ops, + .pm = pm_ptr(&mxc_jpeg_pm_ops), }, }; module_platform_driver(mxc_jpeg_driver); diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index db8ff5f5c4d3..29523bb84d95 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -30,6 +30,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> @@ -319,7 +320,11 @@ struct mipi_csis_device { struct v4l2_subdev sd; struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_async_notifier notifier; - struct v4l2_subdev *src_sd; + + struct { + struct v4l2_subdev *sd; + const struct media_pad *pad; + } source; struct v4l2_mbus_config_mipi_csi2 bus; u32 clk_frequency; @@ -596,7 +601,7 @@ static int mipi_csis_calculate_params(struct mipi_csis_device *csis, u32 lane_rate; /* Calculate the line rate from the pixel rate. */ - link_freq = v4l2_get_link_freq(csis->src_sd->ctrl_handler, + link_freq = v4l2_get_link_freq(csis->source.sd->ctrl_handler, csis_fmt->width, csis->bus.num_data_lanes * 2); if (link_freq < 0) { @@ -742,6 +747,18 @@ static void mipi_csis_stop_stream(struct mipi_csis_device *csis) mipi_csis_system_enable(csis, false); } +static void mipi_csis_queue_event_sof(struct mipi_csis_device *csis) +{ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + }; + u32 frame; + + frame = mipi_csis_read(csis, MIPI_CSIS_FRAME_COUNTER_CH(0)); + event.u.frame_sync.frame_sequence = frame; + v4l2_event_queue(csis->sd.devnode, &event); +} + static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id) { struct mipi_csis_device *csis = dev_id; @@ -765,6 +782,10 @@ static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id) event->counter++; } } + + if (status & MIPI_CSIS_INT_SRC_FRAME_START) + mipi_csis_queue_event_sof(csis); + spin_unlock_irqrestore(&csis->slock, flags); mipi_csis_write(csis, MIPI_CSIS_INT_SRC, status); @@ -840,18 +861,21 @@ static void mipi_csis_log_counters(struct mipi_csis_device *csis, bool non_error { unsigned int num_events = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 8; + unsigned int counters[MIPI_CSIS_NUM_EVENTS]; unsigned long flags; unsigned int i; spin_lock_irqsave(&csis->slock, flags); + for (i = 0; i < num_events; ++i) + counters[i] = csis->events[i].counter; + spin_unlock_irqrestore(&csis->slock, flags); for (i = 0; i < num_events; ++i) { - if (csis->events[i].counter > 0 || csis->debug.enable) + if (counters[i] > 0 || csis->debug.enable) dev_info(csis->dev, "%s events: %d\n", csis->events[i].name, - csis->events[i].counter); + counters[i]); } - spin_unlock_irqrestore(&csis->slock, flags); } static int mipi_csis_dump_regs(struct mipi_csis_device *csis) @@ -941,7 +965,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) int ret; if (!enable) { - v4l2_subdev_call(csis->src_sd, video, s_stream, 0); + v4l2_subdev_disable_streams(csis->source.sd, + csis->source.pad->index, BIT(0)); mipi_csis_stop_stream(csis); if (csis->debug.enable) @@ -969,7 +994,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) mipi_csis_start_stream(csis, format, csis_fmt); - ret = v4l2_subdev_call(csis->src_sd, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(csis->source.sd, + csis->source.pad->index, BIT(0)); if (ret < 0) goto err_stop; @@ -1154,8 +1180,23 @@ static int mipi_csis_log_status(struct v4l2_subdev *sd) return 0; } +static int mipi_csis_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type != V4L2_EVENT_FRAME_SYNC) + return -EINVAL; + + /* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */ + if (sub->id != 0) + return -EINVAL; + + return v4l2_event_subscribe(fh, sub, 0, NULL); +} + static const struct v4l2_subdev_core_ops mipi_csis_core_ops = { .log_status = mipi_csis_log_status, + .subscribe_event = mipi_csis_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { @@ -1201,12 +1242,14 @@ static int mipi_csis_link_setup(struct media_entity *entity, remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); if (flags & MEDIA_LNK_FL_ENABLED) { - if (csis->src_sd) + if (csis->source.sd) return -EBUSY; - csis->src_sd = remote_sd; + csis->source.sd = remote_sd; + csis->source.pad = remote_pad; } else { - csis->src_sd = NULL; + csis->source.sd = NULL; + csis->source.pad = NULL; } return 0; @@ -1304,7 +1347,7 @@ err_parse: * Suspend/resume */ -static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev) +static int mipi_csis_runtime_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); @@ -1319,7 +1362,7 @@ static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused mipi_csis_runtime_resume(struct device *dev) +static int mipi_csis_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); @@ -1339,8 +1382,8 @@ static int __maybe_unused mipi_csis_runtime_resume(struct device *dev) } static const struct dev_pm_ops mipi_csis_pm_ops = { - SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume, - NULL) + RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume, + NULL) }; /* ----------------------------------------------------------------------------- @@ -1358,7 +1401,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) snprintf(sd->name, sizeof(sd->name), "csis-%s", dev_name(csis->dev)); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->ctrl_handler = NULL; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; @@ -1527,11 +1570,11 @@ MODULE_DEVICE_TABLE(of, mipi_csis_of_match); static struct platform_driver mipi_csis_driver = { .probe = mipi_csis_probe, - .remove_new = mipi_csis_remove, + .remove = mipi_csis_remove, .driver = { .of_match_table = mipi_csis_of_match, .name = CSIS_DRIVER_NAME, - .pm = &mipi_csis_pm_ops, + .pm = pm_ptr(&mipi_csis_pm_ops), }, }; diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index e62dc5c1a4ae..7f8ffbac582f 100644 --- a/drivers/media/platform/nxp/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c @@ -1606,8 +1606,6 @@ static const struct vb2_ops pxp_qops = { .buf_queue = pxp_buf_queue, .start_streaming = pxp_start_streaming, .stop_streaming = pxp_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -1805,6 +1803,9 @@ static int pxp_probe(struct platform_device *pdev) return PTR_ERR(mmio); dev->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &pxp_regmap_config); + if (IS_ERR(dev->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(dev->regmap), + "Failed to init regmap\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -1940,7 +1941,7 @@ MODULE_DEVICE_TABLE(of, pxp_dt_ids); static struct platform_driver pxp_driver = { .probe = pxp_probe, - .remove_new = pxp_remove, + .remove = pxp_remove, .driver = { .name = MEM2MEM_NAME, .of_match_table = pxp_dt_ids, diff --git a/drivers/media/platform/nxp/imx-pxp.h b/drivers/media/platform/nxp/imx-pxp.h index 44f95c749d2e..476f2042fa6f 100644 --- a/drivers/media/platform/nxp/imx-pxp.h +++ b/drivers/media/platform/nxp/imx-pxp.h @@ -594,12 +594,17 @@ (((v) << 18) & BM_PXP_CSC1_COEF0_C0) #define BP_PXP_CSC1_COEF0_UV_OFFSET 9 #define BM_PXP_CSC1_COEF0_UV_OFFSET 0x0003FE00 + +/* + * We use v * (1 << 9) instead of v << 9, to workaround a gcc5 bug. + * The compiler cannot understand that the expression is constant. + */ #define BF_PXP_CSC1_COEF0_UV_OFFSET(v) \ - (((v) << 9) & BM_PXP_CSC1_COEF0_UV_OFFSET) + (((v) * (1 << 9)) & BM_PXP_CSC1_COEF0_UV_OFFSET) #define BP_PXP_CSC1_COEF0_Y_OFFSET 0 #define BM_PXP_CSC1_COEF0_Y_OFFSET 0x000001FF #define BF_PXP_CSC1_COEF0_Y_OFFSET(v) \ - (((v) << 0) & BM_PXP_CSC1_COEF0_Y_OFFSET) + ((v) & BM_PXP_CSC1_COEF0_Y_OFFSET) #define HW_PXP_CSC1_COEF1 (0x000001b0) diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 9566ff738818..34a92642bbfe 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -1507,8 +1507,6 @@ static const struct vb2_ops imx7_csi_video_qops = { .buf_init = imx7_csi_video_buf_init, .buf_prepare = imx7_csi_video_buf_prepare, .buf_queue = imx7_csi_video_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = imx7_csi_video_start_streaming, .stop_streaming = imx7_csi_video_stop_streaming, }; @@ -2281,7 +2279,7 @@ MODULE_DEVICE_TABLE(of, imx7_csi_of_match); static struct platform_driver imx7_csi_driver = { .probe = imx7_csi_probe, - .remove_new = imx7_csi_remove, + .remove = imx7_csi_remove, .driver = { .of_match_table = imx7_csi_of_match, .name = "imx7-csi", diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index c2013995049c..1e79b1211b60 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -307,6 +307,19 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .has_36bit_dma = true, }; +static const struct mxc_isi_plat_data mxc_imx8ulp_data = { + .model = MXC_ISI_IMX8ULP, + .num_ports = 1, + .num_channels = 1, + .reg_offset = 0x0, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .clks = mxc_imx8mn_clks, + .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + static const struct mxc_isi_plat_data mxc_imx93_data = { .model = MXC_ISI_IMX93, .num_ports = 1, @@ -528,6 +541,7 @@ static void mxc_isi_remove(struct platform_device *pdev) static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data }, { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data }, + { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data }, { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, { /* sentinel */ }, }; @@ -535,7 +549,7 @@ MODULE_DEVICE_TABLE(of, mxc_isi_of_match); static struct platform_driver mxc_isi_driver = { .probe = mxc_isi_probe, - .remove_new = mxc_isi_remove, + .remove = mxc_isi_remove, .driver = { .of_match_table = mxc_isi_of_match, .name = MXC_ISI_DRIVER_NAME, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 2810ebe9b5f7..9c7fe9e5f941 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -158,6 +158,7 @@ struct mxc_gasket_ops { enum model { MXC_ISI_IMX8MN, MXC_ISI_IMX8MP, + MXC_ISI_IMX8ULP, MXC_ISI_IMX93, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index 9745d6219a16..794050a6a919 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -269,8 +269,6 @@ static const struct vb2_ops mxc_isi_m2m_vb2_qops = { .buf_init = mxc_isi_m2m_vb2_buffer_init, .buf_prepare = mxc_isi_m2m_vb2_buffer_prepare, .buf_queue = mxc_isi_m2m_vb2_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = mxc_isi_m2m_vb2_start_streaming, .stop_streaming = mxc_isi_m2m_vb2_stop_streaming, }; 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 4091f1c0e78b..8654150728a8 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -861,6 +861,7 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2 const struct mxc_isi_format_info *info, const struct v4l2_pix_format_mplane *pix) { + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2); unsigned int i; for (i = 0; i < info->mem_planes; i++) { @@ -875,6 +876,8 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2 vb2_set_plane_payload(vb2, i, size); } + v4l2_buf->field = pix->field; + return 0; } @@ -987,8 +990,6 @@ static const struct vb2_ops mxc_isi_vb2_qops = { .buf_init = mxc_isi_vb2_buffer_init, .buf_prepare = mxc_isi_vb2_buffer_prepare, .buf_queue = mxc_isi_vb2_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = mxc_isi_vb2_start_streaming, .stop_streaming = mxc_isi_vb2_stop_streaming, }; diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index ba2e81f24965..1f2657cf6e82 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -693,7 +693,7 @@ unlock: return ret ? -EAGAIN : 0; } -static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) +static int imx8mq_mipi_csi_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -705,7 +705,7 @@ static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) return 0; } -static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) +static int imx8mq_mipi_csi_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -716,7 +716,7 @@ static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) return imx8mq_mipi_csi_pm_resume(dev); } -static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) +static int imx8mq_mipi_csi_runtime_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -731,7 +731,7 @@ static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) return ret; } -static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) +static int imx8mq_mipi_csi_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -747,10 +747,9 @@ static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) } static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = { - SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend, - imx8mq_mipi_csi_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume) + RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend, + imx8mq_mipi_csi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume) }; /* ----------------------------------------------------------------------------- @@ -954,11 +953,11 @@ MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match); static struct platform_driver imx8mq_mipi_csi_driver = { .probe = imx8mq_mipi_csi_probe, - .remove_new = imx8mq_mipi_csi_remove, + .remove = imx8mq_mipi_csi_remove, .driver = { .of_match_table = imx8mq_mipi_csi_of_match, .name = MIPI_CSI2_DRIVER_NAME, - .pm = &imx8mq_mipi_csi_pm_ops, + .pm = pm_ptr(&imx8mq_mipi_csi_pm_ops), }, }; diff --git a/drivers/media/platform/nxp/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c index 023ed40c6b08..0c6cc120fd2a 100644 --- a/drivers/media/platform/nxp/mx2_emmaprp.c +++ b/drivers/media/platform/nxp/mx2_emmaprp.c @@ -677,8 +677,6 @@ static const struct vb2_ops emmaprp_qops = { .queue_setup = emmaprp_queue_setup, .buf_prepare = emmaprp_buf_prepare, .buf_queue = emmaprp_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -902,7 +900,7 @@ static void emmaprp_remove(struct platform_device *pdev) static struct platform_driver emmaprp_pdrv = { .probe = emmaprp_probe, - .remove_new = emmaprp_remove, + .remove = emmaprp_remove, .driver = { .name = MEM2MEM_NAME, }, diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 4e2222358973..e636968a1126 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -14,10 +14,11 @@ qcom-camss-objs += \ camss-vfe-4-1.o \ camss-vfe-4-7.o \ camss-vfe-4-8.o \ - camss-vfe-170.o \ + camss-vfe-17x.o \ camss-vfe-480.o \ camss-vfe-gen1.o \ camss-vfe.o \ camss-video.o \ + camss-format.o \ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c index dd49a40e6a70..c95861420502 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c @@ -45,128 +45,6 @@ #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; @@ -174,7 +52,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) if (enable) { struct v4l2_mbus_framefmt *input_format; - const struct csid_format *format; + const struct csid_format_info *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; @@ -184,7 +62,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) u32 num_lines, num_bytes_per_line; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; @@ -211,7 +90,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); val = phy->lane_cnt - 1; @@ -311,8 +191,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c index 6b26e036294e..08578a143688 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c @@ -44,156 +44,6 @@ #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n)) -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; @@ -203,7 +53,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) if (enable) { struct v4l2_mbus_framefmt *input_format; - const struct csid_format *format; + const struct csid_format_info *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; @@ -213,7 +63,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) u32 num_bytes_per_line, num_lines; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; @@ -240,7 +91,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); val = phy->lane_cnt - 1; @@ -387,8 +239,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index b11de4797cca..e1c757933e27 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -176,306 +176,171 @@ #define TPG_COLOR_BOX_CFG_MODE 0 #define TPG_COLOR_BOX_PATTERN_SEL 2 -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_Y8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, -}; - -static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rx(struct csid_device *csid, + struct csid_phy_config *phy, int vc) { - struct csid_testgen_config *tg = &csid->testgen; - u32 val; - u32 phy_sel = 0; u8 lane_cnt = csid->phy.lane_cnt; - /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; - const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats, - input_format->code); + int val; if (!lane_cnt) lane_cnt = 4; - if (!tg->enabled) - phy_sel = csid->phy.csiphy_id; - - if (enable) { - /* - * DT_ID is a two bit bitfield that is concatenated with - * the four least significant bits of the five bit VC - * bitfield to generate an internal CID value. - * - * CSID_RDI_CFG0(vc) - * DT_ID : 28:27 - * VC : 26:22 - * DT : 21:16 - * - * CID : VC 3:0 << 2 | DT_ID 1:0 - */ - u8 dt_id = vc & 0x03; - - if (tg->enabled) { - /* configure one DT, infinite frames */ - val = vc << TPG_VC_CFG0_VC_NUM; - val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; - val |= 0 << TPG_VC_CFG0_NUM_FRAMES; - writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); - - val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; - val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; - writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); - - writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); - - val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT; - val |= (input_format->width & 0x1fff) << TPG_DT_n_CFG_0_FRAME_WIDTH; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - - val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); - - val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE; - val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; - val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); - - writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); - - writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); - } + val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; + val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; + val |= phy->csiphy_id << CSI2_RX_CFG0_PHY_NUM_SEL; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); - val = 1 << RDI_CFG0_BYTE_CNTR_EN; - val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; - val |= 1 << RDI_CFG0_TIMESTAMP_EN; - /* note: for non-RDI path, this should be format->decode_format */ - val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; - val |= format->data_type << RDI_CFG0_DATA_TYPE; - val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; - val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; + if (vc > 3) + val |= 1 << CSI2_RX_CFG1_VC_MODE; + val |= 1 << CSI2_RX_CFG1_MISR_EN; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); +} - /* CSID_TIMESTAMP_STB_POST_IRQ */ - val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); +static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) +{ + int val; - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + if (enable) + val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + else + val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); +} - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +{ + struct csid_testgen_config *tg = &csid->testgen; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u8 lane_cnt = csid->phy.lane_cnt; + u32 val; - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + if (!lane_cnt) + lane_cnt = 4; - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + /* configure one DT, infinite frames */ + val = vc << TPG_VC_CFG0_VC_NUM; + val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; + val |= 0 << TPG_VC_CFG0_NUM_FRAMES; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; + val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT; + val |= (input_format->width & 0x1fff) << TPG_DT_n_CFG_0_FRAME_WIDTH; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE; + val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; + val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); - val |= 1 << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); - } + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); - if (tg->enabled) { - val = enable << TPG_CTRL_TEST_EN; - val |= 1 << TPG_CTRL_FS_PKT_EN; - val |= 1 << TPG_CTRL_FE_PKT_EN; - val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; - val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; - val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; - writel_relaxed(val, csid->base + CSID_TPG_CTRL); - } + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); - val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; - val |= csid->phy.lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= phy_sel << CSI2_RX_CFG0_PHY_NUM_SEL; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); + val = enable << TPG_CTRL_TEST_EN; + val |= 1 << TPG_CTRL_FS_PKT_EN; + val |= 1 << TPG_CTRL_FE_PKT_EN; + val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; + val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; + val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; + writel_relaxed(val, csid->base + CSID_TPG_CTRL); +} - val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; - if (vc > 3) - val |= 1 << CSI2_RX_CFG1_VC_MODE; - val |= 1 << CSI2_RX_CFG1_MISR_EN; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +{ + /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u32 val; - if (enable) - val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; - else - val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + /* + * DT_ID is a two bit bitfield that is concatenated with + * the four least significant bits of the five bit VC + * bitfield to generate an internal CID value. + * + * CSID_RDI_CFG0(vc) + * DT_ID : 28:27 + * VC : 26:22 + * DT : 21:16 + * + * CID : VC 3:0 << 2 | DT_ID 1:0 + */ + u8 dt_id = vc & 0x03; + + val = 1 << RDI_CFG0_BYTE_CNTR_EN; + val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; + val |= 1 << RDI_CFG0_TIMESTAMP_EN; + /* note: for non-RDI path, this should be format->decode_format */ + val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; + val |= format->data_type << RDI_CFG0_DATA_TYPE; + val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; + val |= dt_id << RDI_CFG0_DT_ID; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + + /* CSID_TIMESTAMP_STB_POST_IRQ */ + val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; + writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + + val = 0; writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + + val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val |= enable << RDI_CFG0_ENABLE; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { + struct csid_testgen_config *tg = &csid->testgen; u8 i; /* Loop through all enabled VCs and configure stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) - if (csid->phy.en_vc & BIT(i)) - __csid_configure_stream(csid, enable, i); + if (csid->phy.en_vc & BIT(i)) { + if (tg->enabled) + __csid_configure_testgen(csid, enable, i); + + __csid_configure_rdi_stream(csid, enable, i); + __csid_configure_rx(csid, &csid->phy, i); + __csid_ctrl_rdi(csid, enable, i); + } } static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) @@ -612,8 +477,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2; } diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index eb27d69e89a1..858db5d4ca75 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -45,6 +45,450 @@ const char * const csid_testgen_modes[] = { NULL }; +static const struct csid_format_info formats_4_1[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static const struct csid_format_info formats_4_7[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static const struct csid_format_info formats_gen2[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_Y8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, +}; + +const struct csid_formats csid_formats_4_1 = { + .nformats = ARRAY_SIZE(formats_4_1), + .formats = formats_4_1 +}; + +const struct csid_formats csid_formats_4_7 = { + .nformats = ARRAY_SIZE(formats_4_7), + .formats = formats_4_7 +}; + +const struct csid_formats csid_formats_gen2 = { + .nformats = ARRAY_SIZE(formats_gen2), + .formats = formats_gen2 +}; + u32 csid_find_code(u32 *codes, unsigned int ncodes, unsigned int match_format_idx, u32 match_code) { @@ -65,9 +509,9 @@ u32 csid_find_code(u32 *codes, unsigned int ncodes, return codes[0]; } -const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, - unsigned int nformats, - u32 code) +const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats, + unsigned int nformats, + u32 code) { unsigned int i; @@ -87,12 +531,12 @@ const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, static int csid_set_clock_rates(struct csid_device *csid) { struct device *dev = csid->camss->dev; - const struct csid_format *fmt; + const struct csid_format_info *fmt; s64 link_freq; int i, j; int ret; - fmt = csid_get_fmt_entry(csid->formats, csid->nformats, + fmt = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, csid->fmt[MSM_CSIPHY_PAD_SINK].code); link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp, csid->phy.lane_cnt); @@ -158,7 +602,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) struct csid_device *csid = v4l2_get_subdevdata(sd); struct camss *camss = csid->camss; struct device *dev = camss->dev; - struct vfe_device *vfe = &camss->vfe[csid->id]; int ret = 0; if (on) { @@ -167,7 +610,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) * switching on the CSID. Do so unconditionally, as there is no * drawback in following the same powering order on older SoCs. */ - ret = vfe_get(vfe); + ret = csid->res->parent_dev_ops->get(camss, csid->id); if (ret < 0) return ret; @@ -202,7 +645,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) enable_irq(csid->irq); - ret = csid->ops->reset(csid); + ret = csid->res->hw_ops->reset(csid); if (ret < 0) { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); @@ -212,14 +655,14 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) return ret; } - csid->ops->hw_version(csid); + csid->res->hw_ops->hw_version(csid); } else { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); regulator_bulk_disable(csid->num_supplies, csid->supplies); pm_runtime_put_sync(dev); - vfe_put(vfe); + csid->res->parent_dev_ops->put(camss, csid->id); } return ret; @@ -253,7 +696,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) } if (csid->phy.need_vc_update) { - csid->ops->configure_stream(csid, enable); + csid->res->hw_ops->configure_stream(csid, enable); csid->phy.need_vc_update = false; } @@ -301,12 +744,12 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < csid->nformats; i++) - if (fmt->code == csid->formats[i].code) + for (i = 0; i < csid->res->formats->nformats; i++) + if (fmt->code == csid->res->formats->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= csid->nformats) + if (i >= csid->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -325,17 +768,17 @@ static void csid_try_format(struct csid_device *csid, *fmt = *__csid_get_format(csid, sd_state, MSM_CSID_PAD_SINK, which); - fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code); + fmt->code = csid->res->hw_ops->src_pad_code(csid, fmt->code, 0, code); } else { /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ - for (i = 0; i < csid->nformats; i++) - if (csid->formats[i].code == fmt->code) + for (i = 0; i < csid->res->formats->nformats; i++) + if (csid->res->formats->formats[i].code == fmt->code) break; /* If not found, use UYVY as default */ - if (i >= csid->nformats) + if (i >= csid->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -363,10 +806,10 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, struct csid_device *csid = v4l2_get_subdevdata(sd); if (code->pad == MSM_CSID_PAD_SINK) { - if (code->index >= csid->nformats) + if (code->index >= csid->res->formats->nformats) return -EINVAL; - code->code = csid->formats[code->index].code; + code->code = csid->res->formats->formats[code->index].code; } else { if (csid->testgen_mode->cur.val == 0) { struct v4l2_mbus_framefmt *sink_fmt; @@ -375,15 +818,15 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, MSM_CSID_PAD_SINK, code->which); - code->code = csid->ops->src_pad_code(csid, sink_fmt->code, - code->index, 0); + code->code = csid->res->hw_ops->src_pad_code(csid, sink_fmt->code, + code->index, 0); if (!code->code) return -EINVAL; } else { - if (code->index >= csid->nformats) + if (code->index >= csid->res->formats->nformats) return -EINVAL; - code->code = csid->formats[code->index].code; + code->code = csid->res->formats->formats[code->index].code; } } @@ -529,7 +972,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value) tg->enabled = !!value; - return csid->ops->configure_testgen_pattern(csid, value); + return csid->res->hw_ops->configure_testgen_pattern(csid, value); } /* @@ -575,9 +1018,14 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->camss = camss; csid->id = id; - csid->ops = res->ops; + csid->res = &res->csid; + + if (dev_WARN_ONCE(dev, !csid->res->parent_dev_ops, + "Error: CSID depends on VFE/IFE device ops!\n")) { + return -EINVAL; + } - csid->ops->subdev_init(csid); + csid->res->hw_ops->subdev_init(csid); /* Memory */ @@ -587,9 +1035,11 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, * VFE to be initialized before CSID */ if (id >= 2) /* VFE/CSID lite */ - csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET; + csid->base = csid->res->parent_dev_ops->get_base_address(camss, id) + + VFE_480_LITE_CSID_OFFSET; else - csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET; + csid->base = csid->res->parent_dev_ops->get_base_address(camss, id) + + VFE_480_CSID_OFFSET; } else { csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); if (IS_ERR(csid->base)) @@ -605,7 +1055,7 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->irq = ret; snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", dev_name(dev), MSM_CSID_NAME, csid->id); - ret = devm_request_irq(dev, csid->irq, csid->ops->isr, + ret = devm_request_irq(dev, csid->irq, csid->res->hw_ops->isr, IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, csid->irq_name, csid); if (ret < 0) { @@ -899,5 +1349,5 @@ void msm_csid_unregister_entity(struct csid_device *csid) inline bool csid_is_lite(struct csid_device *csid) { - return csid->camss->res->csid_res[csid->id].is_lite; + return csid->camss->res->csid_res[csid->id].csid.is_lite; } diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index fddccb69da13..8cdae98e4dca 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -67,7 +67,7 @@ enum csid_testgen_mode { CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2 = 9, /* excluding disabled */ }; -struct csid_format { +struct csid_format_info { u32 code; u8 data_type; u8 decode_format; @@ -75,6 +75,11 @@ struct csid_format { u8 spp; /* bus samples per pixel */ }; +struct csid_formats { + unsigned int nformats; + const struct csid_format_info *formats; +}; + struct csid_testgen_config { enum csid_testgen_mode mode; const char * const*modes; @@ -149,6 +154,13 @@ struct csid_hw_ops { void (*subdev_init)(struct csid_device *csid); }; +struct csid_subdev_resources { + bool is_lite; + const struct csid_hw_ops *hw_ops; + const struct parent_dev_ops *parent_dev_ops; + const struct csid_formats *formats; +}; + struct csid_device { struct camss *camss; u8 id; @@ -167,9 +179,7 @@ struct csid_device { struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *testgen_mode; - const struct csid_format *formats; - unsigned int nformats; - const struct csid_hw_ops *ops; + const struct csid_subdev_resources *res; }; struct camss_subdev_resources; @@ -188,16 +198,16 @@ u32 csid_find_code(u32 *codes, unsigned int ncode, unsigned int match_format_idx, u32 match_code); /* - * csid_get_fmt_entry - Find csid_format entry with matching format code - * @formats: Array of format csid_format entries + * csid_get_fmt_entry - Find csid_format_info entry with matching format code + * @formats: Array of format csid_format_info entries * @nformats: Length of @nformats array * @code: Desired format code * * Return formats[0] on failure to find code */ -const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, - unsigned int nformats, - u32 code); +const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats, + unsigned int nformats, + u32 code); int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct camss_subdev_resources *res, u8 id); @@ -211,6 +221,10 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); extern const char * const csid_testgen_modes[]; +extern const struct csid_formats csid_formats_4_1; +extern const struct csid_formats csid_formats_4_7; +extern const struct csid_formats csid_formats_gen2; + 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; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index f50e2235c37f..f341f7b7fd8a 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -148,6 +148,91 @@ csiphy_reg_t lane_regs_sdm845[5][14] = { }, }; +/* GEN2 1.1 2PH */ +static const struct +csiphy_reg_t lane_regs_sc8280xp[5][14] = { + { + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x000C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + /* GEN2 1.2.1 2PH */ static const struct csiphy_reg_t lane_regs_sm8250[5][20] = { @@ -420,14 +505,22 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, u32 val; switch (csiphy->camss->res->version) { - case CAMSS_845: - r = &lane_regs_sdm845[0][0]; - array_size = ARRAY_SIZE(lane_regs_sdm845[0]); + case CAMSS_7280: + r = &lane_regs_sm8250[0][0]; + array_size = ARRAY_SIZE(lane_regs_sm8250[0]); break; case CAMSS_8250: r = &lane_regs_sm8250[0][0]; array_size = ARRAY_SIZE(lane_regs_sm8250[0]); break; + case CAMSS_8280XP: + r = &lane_regs_sc8280xp[0][0]; + array_size = ARRAY_SIZE(lane_regs_sc8280xp[0]); + break; + case CAMSS_845: + r = &lane_regs_sdm845[0][0]; + array_size = ARRAY_SIZE(lane_regs_sdm845[0]); + break; default: WARN(1, "unknown cspi version\n"); return; @@ -463,13 +556,27 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) return lane_mask; } +static bool csiphy_is_gen2(u32 version) +{ + bool ret = false; + + switch (version) { + case CAMSS_7280: + case CAMSS_8250: + case CAMSS_8280XP: + case CAMSS_845: + ret = true; + break; + } + + return ret; +} + static void csiphy_lanes_enable(struct csiphy_device *csiphy, struct csiphy_config *cfg, s64 link_freq, u8 lane_mask) { struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; - bool is_gen2 = (csiphy->camss->res->version == CAMSS_845 || - csiphy->camss->res->version == CAMSS_8250); u8 settle_cnt; u8 val; int i; @@ -491,7 +598,7 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, val = 0x00; writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); - if (is_gen2) + if (csiphy_is_gen2(csiphy->camss->res->version)) csiphy_gen2_config_lanes(csiphy, settle_cnt); else csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 264c99efeae8..3791c2d8a6cf 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -24,12 +24,7 @@ #define MSM_CSIPHY_NAME "msm_csiphy" -struct csiphy_format { - u32 code; - u8 bpp; -}; - -static const struct csiphy_format csiphy_formats_8x16[] = { +static const struct csiphy_format_info formats_8x16[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -49,7 +44,7 @@ static const struct csiphy_format csiphy_formats_8x16[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; -static const struct csiphy_format csiphy_formats_8x96[] = { +static const struct csiphy_format_info formats_8x96[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -73,7 +68,7 @@ static const struct csiphy_format csiphy_formats_8x96[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; -static const struct csiphy_format csiphy_formats_sdm845[] = { +static const struct csiphy_format_info formats_sdm845[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -98,6 +93,26 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; +const struct csiphy_formats csiphy_formats_8x16 = { + .nformats = ARRAY_SIZE(formats_8x16), + .formats = formats_8x16 +}; + +const struct csiphy_formats csiphy_formats_8x96 = { + .nformats = ARRAY_SIZE(formats_8x96), + .formats = formats_8x96 +}; + +const struct csiphy_formats csiphy_formats_sc7280 = { + .nformats = ARRAY_SIZE(formats_sdm845), + .formats = formats_sdm845 +}; + +const struct csiphy_formats csiphy_formats_sdm845 = { + .nformats = ARRAY_SIZE(formats_sdm845), + .formats = formats_sdm845 +}; + /* * csiphy_get_bpp - map media bus format to bits per pixel * @formats: supported media bus formats array @@ -106,7 +121,7 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { * * Return number of bits per pixel */ -static u8 csiphy_get_bpp(const struct csiphy_format *formats, +static u8 csiphy_get_bpp(const struct csiphy_format_info *formats, unsigned int nformats, u32 code) { unsigned int i; @@ -131,7 +146,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) int i, j; int ret; - u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; @@ -202,28 +217,41 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) if (ret < 0) return ret; + ret = regulator_bulk_enable(csiphy->num_supplies, + csiphy->supplies); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + ret = csiphy_set_clock_rates(csiphy); if (ret < 0) { + regulator_bulk_disable(csiphy->num_supplies, + csiphy->supplies); pm_runtime_put_sync(dev); return ret; } ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); if (ret < 0) { + regulator_bulk_disable(csiphy->num_supplies, + csiphy->supplies); pm_runtime_put_sync(dev); return ret; } enable_irq(csiphy->irq); - csiphy->ops->reset(csiphy); + csiphy->res->hw_ops->reset(csiphy); - csiphy->ops->hw_version_read(csiphy, dev); + csiphy->res->hw_ops->hw_version_read(csiphy, dev); } else { disable_irq(csiphy->irq); camss_disable_clocks(csiphy->nclocks, csiphy->clock); + regulator_bulk_disable(csiphy->num_supplies, csiphy->supplies); + pm_runtime_put_sync(dev); } @@ -243,8 +271,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; - u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg); - u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + u8 lane_mask = csiphy->res->hw_ops->get_lane_mask(&cfg->csi2->lane_cfg); + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; u8 val; @@ -272,7 +300,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) wmb(); } - csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); + csiphy->res->hw_ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); return 0; } @@ -285,7 +313,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); + csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); } @@ -350,12 +378,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy, case MSM_CSIPHY_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < csiphy->nformats; i++) - if (fmt->code == csiphy->formats[i].code) + for (i = 0; i < csiphy->res->formats->nformats; i++) + if (fmt->code == csiphy->res->formats->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= csiphy->nformats) + if (i >= csiphy->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -392,10 +420,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSIPHY_PAD_SINK) { - if (code->index >= csiphy->nformats) + if (code->index >= csiphy->res->formats->nformats) return -EINVAL; - code->code = csiphy->formats[code->index].code; + code->code = csiphy->res->formats->formats[code->index].code; } else { if (code->index > 0) return -EINVAL; @@ -564,24 +592,7 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->camss = camss; csiphy->id = id; csiphy->cfg.combo_mode = 0; - csiphy->ops = res->ops; - - switch (camss->res->version) { - case CAMSS_8x16: - csiphy->formats = csiphy_formats_8x16; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); - break; - case CAMSS_8x96: - case CAMSS_660: - csiphy->formats = csiphy_formats_8x96; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); - break; - case CAMSS_845: - case CAMSS_8250: - csiphy->formats = csiphy_formats_sdm845; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845); - break; - } + csiphy->res = &res->csiphy; /* Memory */ @@ -590,6 +601,7 @@ int msm_csiphy_subdev_init(struct camss *camss, return PTR_ERR(csiphy->base); if (camss->res->version == CAMSS_8x16 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_8x96) { csiphy->base_clk_mux = devm_platform_ioremap_resource_byname(pdev, res->reg[1]); @@ -609,7 +621,7 @@ int msm_csiphy_subdev_init(struct camss *camss, snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); - ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, + ret = devm_request_irq(dev, csiphy->irq, csiphy->res->hw_ops->isr, IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, csiphy->irq_name, csiphy); if (ret < 0) { @@ -683,7 +695,27 @@ int msm_csiphy_subdev_init(struct camss *camss, } } - return 0; + /* CSIPHY supplies */ + for (i = 0; i < ARRAY_SIZE(res->regulators); i++) { + if (res->regulators[i]) + csiphy->num_supplies++; + } + + if (csiphy->num_supplies) { + csiphy->supplies = devm_kmalloc_array(camss->dev, + csiphy->num_supplies, + sizeof(*csiphy->supplies), + GFP_KERNEL); + if (!csiphy->supplies) + return -ENOMEM; + } + + for (i = 0; i < csiphy->num_supplies; i++) + csiphy->supplies[i].supply = res->regulators[i]; + + ret = devm_regulator_bulk_get(camss->dev, csiphy->num_supplies, + csiphy->supplies); + return ret; } /* diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index c9b7fe82b1f0..90cc3f976643 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -26,6 +26,12 @@ struct csiphy_lane { u8 pol; }; +/** + * struct csiphy_lanes_cfg - CSIPHY lanes configuration + * @num_data: number of data lanes + * @data: data lanes configuration + * @clk: clock lane configuration (only for D-PHY) + */ struct csiphy_lanes_cfg { int num_data; struct csiphy_lane *data; @@ -42,6 +48,16 @@ struct csiphy_config { struct csiphy_csi2_cfg *csi2; }; +struct csiphy_format_info { + u32 code; + u8 bpp; +}; + +struct csiphy_formats { + unsigned int nformats; + const struct csiphy_format_info *formats; +}; + struct csiphy_device; struct csiphy_hw_ops { @@ -63,6 +79,11 @@ struct csiphy_hw_ops { irqreturn_t (*isr)(int irq, void *dev); }; +struct csiphy_subdev_resources { + const struct csiphy_hw_ops *hw_ops; + const struct csiphy_formats *formats; +}; + struct csiphy_device { struct camss *camss; u8 id; @@ -76,11 +97,11 @@ struct csiphy_device { bool *rate_set; int nclocks; u32 timer_clk_rate; + struct regulator_bulk_data *supplies; + int num_supplies; struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; - const struct csiphy_hw_ops *ops; - const struct csiphy_format *formats; - unsigned int nformats; + const struct csiphy_subdev_resources *res; }; struct camss_subdev_resources; @@ -94,6 +115,11 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); +extern const struct csiphy_formats csiphy_formats_8x16; +extern const struct csiphy_formats csiphy_formats_8x96; +extern const struct csiphy_formats csiphy_formats_sc7280; +extern const struct csiphy_formats csiphy_formats_sdm845; + extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0; diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c new file mode 100644 index 000000000000..4a3d5549615c --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-format.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-format.c + * + * Qualcomm MSM Camera Subsystem - Format helpers + * + * Copyright (c) 2023, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Technologies, Inc. + */ +#include <linux/bug.h> +#include <linux/errno.h> + +#include "camss-format.h" + +/* + * camss_format_get_bpp - Map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array + * @code: media bus format code + * + * Return number of bits per pixel + */ +u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].mbus_bpp; + + WARN(1, "Unknown format\n"); + + return formats[0].mbus_bpp; +} + +/* + * camss_format_find_code - Find a format code in an array + * @code: a pointer to media bus format codes array + * @n_code: size of @code array + * @index: index of code in the array + * @req_code: required code + * + * Return media bus format code + */ +u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code) +{ + unsigned int i; + + if (!req_code && index >= n_code) + return 0; + + for (i = 0; i < n_code; i++) { + if (req_code) { + if (req_code == code[i]) + return req_code; + } else { + if (i == index) + return code[i]; + } + } + + return code[0]; +} + +/* + * camss_format_find_format - Find a format in an array + * @code: media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @formats: a pointer to formats array + * @nformats: size of @formats array + * + * Return index of a format or a negative error code otherwise + */ +int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, + unsigned int nformats) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code && + formats[i].pixelformat == pixelformat) + return i; + } + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code) + return i; + } + + return -EINVAL; +} diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h new file mode 100644 index 000000000000..923a48c9c3fb --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-format.h + * + * Qualcomm MSM Camera Subsystem - Format helpers + * + * Copyright (c) 2023, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Technologies, Inc. + */ +#ifndef __CAMSS_FORMAT_H__ +#define __CAMSS_FORMAT_H__ + +#include <linux/types.h> + +#define PER_PLANE_DATA(plane, h_fract_num, h_fract_den, v_fract_num, v_fract_den, _bpp) \ + .hsub[(plane)].numerator = (h_fract_num), \ + .hsub[(plane)].denominator = (h_fract_den), \ + .vsub[(plane)].numerator = (v_fract_num), \ + .vsub[(plane)].denominator = (v_fract_den), \ + .bpp[(plane)] = (_bpp) + +/* + * struct fract - Represents a fraction + * @numerator: Store the numerator part of the fraction + * @denominator: Store the denominator part of the fraction + */ +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct camss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @mbus_bpp: Media bus bits per pixel + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct camss_format_info { + u32 code; + u32 mbus_bpp; + u32 pixelformat; + u8 planes; + struct fract hsub[3]; + struct fract vsub[3]; + unsigned int bpp[3]; +}; + +struct camss_formats { + unsigned int nformats; + const struct camss_format_info *formats; +}; + +u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code); +u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code); +int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, + unsigned int nformats); + +#endif /* __CAMSS_FORMAT_H__ */ diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index a12dcc7ff438..2dc585c6123d 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -830,6 +830,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) ispif_select_cid(ispif, intf, cid, vfe, 1); ispif_config_irq(ispif, intf, vfe, 1); if (camss->res->version == CAMSS_8x96 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_660) ispif_config_pack(ispif, line->fmt[MSM_ISPIF_PAD_SINK].code, @@ -848,6 +849,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&ispif->config_lock); if (camss->res->version == CAMSS_8x96 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_660) ispif_config_pack(ispif, line->fmt[MSM_ISPIF_PAD_SINK].code, @@ -1111,6 +1113,7 @@ int msm_ispif_subdev_init(struct camss *camss, if (camss->res->version == CAMSS_8x16) ispif->line_num = 2; else if (camss->res->version == CAMSS_8x96 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_660) ispif->line_num = 4; else @@ -1130,6 +1133,7 @@ int msm_ispif_subdev_init(struct camss *camss, ispif->line[i].nformats = ARRAY_SIZE(ispif_formats_8x16); } else if (camss->res->version == CAMSS_8x96 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_660) { ispif->line[i].formats = ispif_formats_8x96; ispif->line[i].nformats = @@ -1162,6 +1166,7 @@ int msm_ispif_subdev_init(struct camss *camss, ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); else if (camss->res->version == CAMSS_8x96 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_660) ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c index 795ac3815339..380c99321030 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c @@ -353,7 +353,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) writel_relaxed(status0, vfe->base + VFE_IRQ_CLEAR_0); writel_relaxed(status1, vfe->base + VFE_IRQ_CLEAR_1); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { vfe_bus_status[i] = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(i)); writel_relaxed(vfe_bus_status[i], vfe->base + VFE_BUS_IRQ_CLEAR(i)); } @@ -367,11 +367,11 @@ static irqreturn_t vfe_isr(int irq, void *dev) if (status0 & STATUS_0_RESET_ACK) vfe->isr_ops.reset_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (status0 & STATUS_0_RDI_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (status0 & STATUS_1_RDI_SOF(i)) vfe->isr_ops.sof(vfe, i); @@ -442,7 +442,7 @@ static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; struct media_entity *sensor; unsigned long flags; unsigned int frame_skip = 0; @@ -560,7 +560,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); + vfe->res->hw_ops->reg_update_clear(vfe, line_id); output = &vfe->line[line_id].output; 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 ef6b34c915df..9a9007c3ff33 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -892,7 +892,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -901,7 +901,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); @@ -938,7 +938,10 @@ static irqreturn_t vfe_isr(int irq, void *dev) */ static void vfe_4_1_pm_domain_off(struct vfe_device *vfe) { - /* nop */ + if (!vfe->res->has_pd) + return; + + vfe_pm_domain_off(vfe); } /* @@ -947,7 +950,10 @@ static void vfe_4_1_pm_domain_off(struct vfe_device *vfe) */ static int vfe_4_1_pm_domain_on(struct vfe_device *vfe) { - return 0; + if (!vfe->res->has_pd) + return 0; + + return vfe_pm_domain_on(vfe); } static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = { 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 7655d22a9fda..ce0719106bd3 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -1050,7 +1050,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -1059,12 +1059,12 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); 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 f52fa30f3853..6b59c8107a3c 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c @@ -980,7 +980,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -989,12 +989,12 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c index 239d3d4ac666..eb33c03df27e 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c @@ -37,7 +37,7 @@ static int vfe_disable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; unsigned long flags; unsigned long time; unsigned int i; @@ -162,14 +162,14 @@ static void vfe_output_frame_drop(struct vfe_device *vfe, vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern); } - vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); + vfe->res->hw_ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); } static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; struct media_entity *sensor; unsigned long flags; unsigned int frame_skip = 0; @@ -545,7 +545,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); + vfe->res->hw_ops->reg_update_clear(vfe, line_id); output = &line->output; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 2062be668f49..95f6a1ac7eaf 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -32,139 +32,251 @@ #define SCALER_RATIO_MAX 16 -struct vfe_format { - u32 code; - u8 bpp; +static const struct camss_format_info formats_rdi_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, }; -static const struct vfe_format formats_rdi_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, +static const struct camss_format_info formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, }; -static const struct vfe_format formats_pix_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, +static const struct camss_format_info formats_rdi_845[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_Y8_1X8, 8, V4L2_PIX_FMT_GREY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, }; -static const struct vfe_format formats_rdi_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +static const struct camss_format_info formats_pix_8x16[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, }; -static const struct vfe_format formats_pix_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, +static const struct camss_format_info formats_pix_8x96[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, }; -static const struct vfe_format formats_rdi_845[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, - { MEDIA_BUS_FMT_Y8_1X8, 8 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +const struct camss_formats vfe_formats_rdi_8x16 = { + .nformats = ARRAY_SIZE(formats_rdi_8x16), + .formats = formats_rdi_8x16 }; -/* - * vfe_get_bpp - map media bus format to bits per pixel - * @formats: supported media bus formats array - * @nformats: size of @formats array - * @code: media bus format code - * - * Return number of bits per pixel - */ -static u8 vfe_get_bpp(const struct vfe_format *formats, - unsigned int nformats, u32 code) -{ - unsigned int i; - - for (i = 0; i < nformats; i++) - if (code == formats[i].code) - return formats[i].bpp; - - WARN(1, "Unknown format\n"); - - return formats[0].bpp; -} +const struct camss_formats vfe_formats_pix_8x16 = { + .nformats = ARRAY_SIZE(formats_pix_8x16), + .formats = formats_pix_8x16 +}; -static u32 vfe_find_code(u32 *code, unsigned int n_code, - unsigned int index, u32 req_code) -{ - int i; +const struct camss_formats vfe_formats_rdi_8x96 = { + .nformats = ARRAY_SIZE(formats_rdi_8x96), + .formats = formats_rdi_8x96 +}; - if (!req_code && (index >= n_code)) - return 0; +const struct camss_formats vfe_formats_pix_8x96 = { + .nformats = ARRAY_SIZE(formats_pix_8x96), + .formats = formats_pix_8x96 +}; - for (i = 0; i < n_code; i++) - if (req_code) { - if (req_code == code[i]) - return req_code; - } else { - if (i == index) - return code[i]; - } +const struct camss_formats vfe_formats_rdi_845 = { + .nformats = ARRAY_SIZE(formats_rdi_845), + .formats = formats_rdi_845 +}; - return code[0]; -} +/* TODO: Replace with pix formats */ +const struct camss_formats vfe_formats_pix_845 = { + .nformats = ARRAY_SIZE(formats_rdi_845), + .formats = formats_rdi_845 +}; static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, unsigned int index, u32 src_req_code) @@ -173,6 +285,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, switch (vfe->camss->res->version) { case CAMSS_8x16: + case CAMSS_8x53: switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_1X16: { @@ -181,8 +294,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YUYV8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_YVYU8_1X16: { @@ -191,8 +304,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YVYU8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_UYVY8_1X16: { @@ -201,8 +314,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_UYVY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_VYUY8_1X16: { @@ -211,8 +324,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_VYUY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } default: if (index > 0) @@ -221,10 +334,12 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, return sink_code; } break; - case CAMSS_8x96: case CAMSS_660: - case CAMSS_845: + case CAMSS_7280: + case CAMSS_8x96: case CAMSS_8250: + case CAMSS_8280XP: + case CAMSS_845: switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_1X16: { @@ -236,8 +351,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YUYV8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_YVYU8_1X16: { @@ -249,8 +364,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YVYU8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_UYVY8_1X16: { @@ -262,8 +377,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_UYVY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_VYUY8_1X16: { @@ -275,8 +390,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_VYUY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } default: if (index > 0) @@ -295,7 +410,7 @@ int vfe_reset(struct vfe_device *vfe) reinit_completion(&vfe->reset_complete); - vfe->ops->global_reset(vfe); + vfe->res->hw_ops->global_reset(vfe); time = wait_for_completion_timeout(&vfe->reset_complete, msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); @@ -311,7 +426,7 @@ static void vfe_init_outputs(struct vfe_device *vfe) { int i; - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { struct vfe_output *output = &vfe->line[i].output; output->state = VFE_OUTPUT_OFF; @@ -420,7 +535,7 @@ static int vfe_disable_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); for (i = 0; i < output->wm_num; i++) - vfe->ops->vfe_wm_stop(vfe, output->wm_idx[i]); + vfe->res->hw_ops->vfe_wm_stop(vfe, output->wm_idx[i]); output->gen2.active_num = 0; spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -536,7 +651,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) int i, j; int ret; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -550,7 +665,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; long rate; - for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { + for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) { u32 tmp; u8 bpp; @@ -559,9 +674,9 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) } else { struct vfe_line *l = &vfe->line[j]; - bpp = vfe_get_bpp(l->formats, - l->nformats, - l->fmt[MSM_VFE_PAD_SINK].code); + bpp = camss_format_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -617,7 +732,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) int i, j; int ret; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -631,7 +746,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; unsigned long rate; - for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { + for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) { u32 tmp; u8 bpp; @@ -640,9 +755,9 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) } else { struct vfe_line *l = &vfe->line[j]; - bpp = vfe_get_bpp(l->formats, - l->nformats, - l->fmt[MSM_VFE_PAD_SINK].code); + bpp = camss_format_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -674,7 +789,7 @@ int vfe_get(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { - ret = vfe->ops->pm_domain_on(vfe); + ret = vfe->res->hw_ops->pm_domain_on(vfe); if (ret < 0) goto error_pm_domain; @@ -699,7 +814,7 @@ int vfe_get(struct vfe_device *vfe) vfe_init_outputs(vfe); - vfe->ops->hw_version(vfe); + vfe->res->hw_ops->hw_version(vfe); } else { ret = vfe_check_clock_rates(vfe); if (ret < 0) @@ -717,7 +832,7 @@ error_reset: error_pm_runtime_get: pm_runtime_put_sync(vfe->camss->dev); error_domain_off: - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); error_pm_domain: mutex_unlock(&vfe->power_lock); @@ -739,11 +854,11 @@ void vfe_put(struct vfe_device *vfe) } else if (vfe->power_count == 1) { if (vfe->was_streaming) { vfe->was_streaming = 0; - vfe->ops->vfe_halt(vfe); + vfe->res->hw_ops->vfe_halt(vfe); } camss_disable_clocks(vfe->nclocks, vfe->clock); pm_runtime_put_sync(vfe->camss->dev); - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); } vfe->power_count--; @@ -833,12 +948,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) if (enable) { line->output.state = VFE_OUTPUT_RESERVED; - ret = vfe->ops->vfe_enable(line); + ret = vfe->res->hw_ops->vfe_enable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to enable vfe outputs\n"); } else { - ret = vfe->ops->vfe_disable(line); + ret = vfe->res->hw_ops->vfe_disable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to disable vfe outputs\n"); @@ -1375,23 +1490,24 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, int i, j; int ret; - vfe->ops = res->ops; - - if (!res->line_num) + if (!res->vfe.line_num) return -EINVAL; + vfe->res = &res->vfe; + vfe->res->hw_ops->subdev_init(dev, vfe); + /* Power domain */ - if (res->pd_name) { + if (res->vfe.pd_name) { vfe->genpd = dev_pm_domain_attach_by_name(camss->dev, - res->pd_name); + res->vfe.pd_name); if (IS_ERR(vfe->genpd)) { ret = PTR_ERR(vfe->genpd); return ret; } } - if (!vfe->genpd && res->has_pd) { + if (!vfe->genpd && res->vfe.has_pd) { /* * Legacy magic index. * Requires @@ -1408,9 +1524,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, return PTR_ERR(vfe->genpd); } - vfe->line_num = res->line_num; - vfe->ops->subdev_init(dev, vfe); - /* Memory */ vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); @@ -1428,7 +1541,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->irq = ret; snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", dev_name(dev), MSM_VFE_NAME, id); - ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, + ret = devm_request_irq(dev, vfe->irq, vfe->res->hw_ops->isr, IRQF_TRIGGER_RISING, vfe->irq_name, vfe); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); @@ -1487,7 +1600,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->id = id; vfe->reg_update = 0; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { struct vfe_line *l = &vfe->line[i]; l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -1496,31 +1609,12 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, init_completion(&l->output.sof); init_completion(&l->output.reg_update); - switch (camss->res->version) { - case CAMSS_8x16: - if (i == VFE_LINE_PIX) { - l->formats = formats_pix_8x16; - l->nformats = ARRAY_SIZE(formats_pix_8x16); - } else { - l->formats = formats_rdi_8x16; - l->nformats = ARRAY_SIZE(formats_rdi_8x16); - } - break; - case CAMSS_8x96: - case CAMSS_660: - if (i == VFE_LINE_PIX) { - l->formats = formats_pix_8x96; - l->nformats = ARRAY_SIZE(formats_pix_8x96); - } else { - l->formats = formats_rdi_8x96; - l->nformats = ARRAY_SIZE(formats_rdi_8x96); - } - break; - case CAMSS_845: - case CAMSS_8250: - l->formats = formats_rdi_845; - l->nformats = ARRAY_SIZE(formats_rdi_845); - break; + if (i == VFE_LINE_PIX) { + l->nformats = res->vfe.formats_pix->nformats; + l->formats = res->vfe.formats_pix->formats; + } else { + l->nformats = res->vfe.formats_rdi->nformats; + l->formats = res->vfe.formats_rdi->formats; } } @@ -1595,6 +1689,24 @@ static const struct media_entity_operations vfe_media_ops = { .link_validate = v4l2_subdev_link_validate, }; +static int vfe_bpl_align(struct vfe_device *vfe) +{ + int ret = 8; + + switch (vfe->camss->res->version) { + case CAMSS_7280: + case CAMSS_8250: + case CAMSS_8280XP: + case CAMSS_845: + ret = 16; + break; + default: + break; + } + + return ret; +} + /* * msm_vfe_register_entities - Register subdev node for VFE module * @vfe: VFE device @@ -1617,7 +1729,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, int ret; int i; - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { char name[32]; sd = &vfe->line[i].subdev; @@ -1661,20 +1773,19 @@ int msm_vfe_register_entities(struct vfe_device *vfe, } video_out->ops = &vfe->video_ops; - if (vfe->camss->res->version == CAMSS_845 || - vfe->camss->res->version == CAMSS_8250) - video_out->bpl_alignment = 16; - else - video_out->bpl_alignment = 8; + video_out->bpl_alignment = vfe_bpl_align(vfe); video_out->line_based = 0; if (i == VFE_LINE_PIX) { video_out->bpl_alignment = 16; video_out->line_based = 1; } + + video_out->nformats = vfe->line[i].nformats; + video_out->formats = vfe->line[i].formats; + snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", MSM_VFE_NAME, vfe->id, "video", i); - ret = msm_video_register(video_out, v4l2_dev, name, - i == VFE_LINE_PIX ? 1 : 0); + ret = msm_video_register(video_out, v4l2_dev, name); if (ret < 0) { dev_err(dev, "Failed to register video node: %d\n", ret); @@ -1728,7 +1839,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) mutex_destroy(&vfe->power_lock); mutex_destroy(&vfe->stream_lock); - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { struct v4l2_subdev *sd = &vfe->line[i].subdev; struct camss_video *video_out = &vfe->line[i].video_out; @@ -1740,5 +1851,5 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) bool vfe_is_lite(struct vfe_device *vfe) { - return vfe->camss->res->vfe_res[vfe->id].is_lite; + return vfe->camss->res->vfe_res[vfe->id].vfe.is_lite; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 0572c9b08e11..10e2cc3c0b83 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -92,7 +92,7 @@ struct vfe_line { struct v4l2_rect crop; struct camss_video video_out; struct vfe_output output; - const struct vfe_format *formats; + const struct camss_format_info *formats; unsigned int nformats; }; @@ -126,6 +126,16 @@ struct vfe_isr_ops { void (*wm_done)(struct vfe_device *vfe, u8 wm); }; +struct vfe_subdev_resources { + bool is_lite; + u8 line_num; + bool has_pd; + char *pd_name; + const struct vfe_hw_ops *hw_ops; + const struct camss_formats *formats_rdi; + const struct camss_formats *formats_pix; +}; + struct vfe_device { struct camss *camss; u8 id; @@ -143,10 +153,9 @@ struct vfe_device { spinlock_t output_lock; enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; struct vfe_line line[VFE_LINE_NUM_MAX]; - u8 line_num; u32 reg_update; u8 was_streaming; - const struct vfe_hw_ops *ops; + const struct vfe_subdev_resources *res; const struct vfe_hw_ops_gen1 *ops_gen1; struct vfe_isr_ops isr_ops; struct camss_video_ops video_ops; @@ -217,6 +226,13 @@ void vfe_pm_domain_off(struct vfe_device *vfe); */ int vfe_pm_domain_on(struct vfe_device *vfe); +extern const struct camss_formats vfe_formats_rdi_8x16; +extern const struct camss_formats vfe_formats_pix_8x16; +extern const struct camss_formats vfe_formats_rdi_8x96; +extern const struct camss_formats vfe_formats_pix_8x96; +extern const struct camss_formats vfe_formats_rdi_845; +extern const struct camss_formats vfe_formats_pix_845; + 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; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index a89da5ef4710..aa021fd5e123 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -24,269 +24,10 @@ #define CAMSS_FRAME_MAX_HEIGHT_RDI 8191 #define CAMSS_FRAME_MAX_HEIGHT_PIX 4096 -struct fract { - u8 numerator; - u8 denominator; -}; - -/* - * struct camss_format_info - ISP media bus format information - * @code: V4L2 media bus format code - * @pixelformat: V4L2 pixel format FCC identifier - * @planes: Number of planes - * @hsub: Horizontal subsampling (for each plane) - * @vsub: Vertical subsampling (for each plane) - * @bpp: Bits per pixel when stored in memory (for each plane) - */ -struct camss_format_info { - u32 code; - u32 pixelformat; - u8 planes; - struct fract hsub[3]; - struct fract vsub[3]; - unsigned int bpp[3]; -}; - -static const struct camss_format_info formats_rdi_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, -}; - -static const struct camss_format_info formats_rdi_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - -static const struct camss_format_info formats_rdi_845[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - -static const struct camss_format_info formats_pix_8x16[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, -}; - -static const struct camss_format_info formats_pix_8x96[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - /* ----------------------------------------------------------------------------- * Helper functions */ -static int video_find_format(u32 code, u32 pixelformat, - const struct camss_format_info *formats, - unsigned int nformats) -{ - int i; - - for (i = 0; i < nformats; i++) { - if (formats[i].code == code && - formats[i].pixelformat == pixelformat) - return i; - } - - for (i = 0; i < nformats; i++) - if (formats[i].code == code) - return i; - - WARN_ON(1); - - return -EINVAL; -} - /* * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane * @mbus: v4l2_mbus_framefmt format (input) @@ -359,9 +100,8 @@ static int video_get_subdev_format(struct camss_video *video, if (ret) return ret; - ret = video_find_format(fmt.format.code, - format->fmt.pix_mp.pixelformat, - video->formats, video->nformats); + ret = camss_format_find_format(fmt.format.code, format->fmt.pix_mp.pixelformat, + video->formats, video->nformats); if (ret < 0) return ret; @@ -557,12 +297,6 @@ static void video_stop_streaming(struct vb2_queue *q) ret = v4l2_subdev_call(subdev, video, s_stream, 0); - if (entity->use_count > 1) { - /* Don't stop if other instances of the pipeline are still running */ - dev_dbg(video->camss->dev, "Video pipeline still used, don't stop streaming.\n"); - return; - } - if (ret) { dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); return; @@ -576,8 +310,6 @@ static void video_stop_streaming(struct vb2_queue *q) static const struct vb2_ops msm_video_vb2_q_ops = { .queue_setup = video_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_init = video_buf_init, .buf_prepare = video_buf_prepare, .buf_queue = video_buf_queue, @@ -969,7 +701,7 @@ static int msm_video_init_format(struct camss_video *video) */ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix) + const char *name) { struct media_pad *pad = &video->pad; struct video_device *vdev; @@ -1006,33 +738,6 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, mutex_init(&video->lock); - switch (video->camss->res->version) { - case CAMSS_8x16: - if (is_pix) { - video->formats = formats_pix_8x16; - video->nformats = ARRAY_SIZE(formats_pix_8x16); - } else { - video->formats = formats_rdi_8x16; - video->nformats = ARRAY_SIZE(formats_rdi_8x16); - } - break; - case CAMSS_8x96: - case CAMSS_660: - if (is_pix) { - video->formats = formats_pix_8x96; - video->nformats = ARRAY_SIZE(formats_pix_8x96); - } else { - video->formats = formats_rdi_8x96; - video->nformats = ARRAY_SIZE(formats_rdi_8x96); - } - break; - case CAMSS_845: - case CAMSS_8250: - video->formats = formats_rdi_845; - video->nformats = ARRAY_SIZE(formats_rdi_845); - break; - } - ret = msm_video_init_format(video); if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index bdbae8424140..d3e56e240a88 100644 --- a/drivers/media/platform/qcom/camss/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -33,8 +33,6 @@ struct camss_video_ops { enum vb2_buffer_state state); }; -struct camss_format_info; - struct camss_video { struct camss *camss; struct vb2_queue vb2_q; @@ -53,7 +51,7 @@ struct camss_video { }; int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix); + const char *name); void msm_video_unregister(struct camss_video *video); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 58f4be660290..a85e9df0f301 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -32,6 +32,8 @@ #define CAMSS_CLOCK_MARGIN_NUMERATOR 105 #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 +static const struct parent_dev_ops vfe_parent_dev_ops; + static const struct camss_subdev_resources csiphy_res_8x16[] = { /* CSIPHY0 */ { @@ -43,7 +45,10 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { { 100000000, 200000000 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_2ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 + } }, /* CSIPHY1 */ @@ -56,7 +61,10 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { { 100000000, 200000000 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_2ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 + } } }; @@ -76,7 +84,11 @@ static const struct camss_subdev_resources csid_res_8x16[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_1, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } }, /* CSID1 */ @@ -94,7 +106,11 @@ static const struct camss_subdev_resources csid_res_8x16[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_1, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } }, }; @@ -105,8 +121,7 @@ static const struct camss_subdev_resources ispif_res_8x16 = { "csi1", "csi1_pix", "csi1_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } - + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_8x16[] = { @@ -128,11 +143,169 @@ static const struct camss_subdev_resources vfe_res_8x16[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .ops = &vfe_ops_4_1 + .vfe = { + .line_num = 3, + .hw_ops = &vfe_ops_4_1, + .formats_rdi = &vfe_formats_rdi_8x16, + .formats_pix = &vfe_formats_pix_8x16 + } } }; +static const struct camss_subdev_resources csid_res_8x53[] = { + /* CSID0 */ + { + .regulators = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", + "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } + }, + + /* CSID1 */ + { + .regulators = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", + "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } + }, + + /* CSID2 */ + { + .regulators = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", + "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } + }, +}; + +static const struct camss_subdev_resources ispif_res_8x53 = { + /* ISPIF */ + .clock = { "top_ahb", "ahb", "ispif_ahb", + "csi0", "csi0_pix", "csi0_rdi", + "csi1", "csi1_pix", "csi1_rdi", + "csi2", "csi2_pix", "csi2_rdi" }, + .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, + .reg = { "ispif", "csi_clk_mux" }, + .interrupt = { "ispif" }, +}; + +static const struct camss_subdev_resources vfe_res_8x53[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "top_ahb", "ahb", "ispif_ahb", + "vfe0", "csi_vfe0", "vfe0_ahb", "vfe0_axi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 50000000, 100000000, 133330000, + 160000000, 200000000, 266670000, + 310000000, 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "vfe0", + .hw_ops = &vfe_ops_4_1, + .formats_rdi = &vfe_formats_rdi_8x16, + .formats_pix = &vfe_formats_pix_8x16 + } + }, + + /* VFE1 */ + { + .regulators = {}, + .clock = { "top_ahb", "ahb", "ispif_ahb", + "vfe1", "csi_vfe1", "vfe1_ahb", "vfe1_axi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 50000000, 100000000, 133330000, + 160000000, 200000000, 266670000, + 310000000, 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "vfe1", + .hw_ops = &vfe_ops_4_1, + .formats_rdi = &vfe_formats_rdi_8x16, + .formats_pix = &vfe_formats_pix_8x16 + } + } +}; + +static const struct resources_icc icc_res_8x53[] = { + { + .name = "cam_ahb", + .icc_bw_tbl.avg = 38400, + .icc_bw_tbl.peak = 76800, + }, + { + .name = "cam_vfe0_mem", + .icc_bw_tbl.avg = 939524, + .icc_bw_tbl.peak = 1342177, + }, + { + .name = "cam_vfe1_mem", + .icc_bw_tbl.avg = 939524, + .icc_bw_tbl.peak = 1342177, + }, +}; + static const struct camss_subdev_resources csiphy_res_8x96[] = { /* CSIPHY0 */ { @@ -144,7 +317,10 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY1 */ @@ -157,7 +333,10 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY2 */ @@ -170,7 +349,10 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } } }; @@ -190,7 +372,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID1 */ @@ -208,7 +394,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID2 */ @@ -226,7 +416,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID3 */ @@ -244,7 +438,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } } }; @@ -257,7 +455,7 @@ static const struct camss_subdev_resources ispif_res_8x96 = { "csi3", "csi3_pix", "csi3_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_8x96[] = { @@ -277,9 +475,13 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_7 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_7, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } }, /* VFE1 */ @@ -298,9 +500,13 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_7 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_7, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } } }; @@ -317,7 +523,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY1 */ @@ -332,7 +541,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY2 */ @@ -347,7 +559,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } } }; @@ -370,7 +585,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID1 */ @@ -391,7 +610,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID2 */ @@ -412,7 +635,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID3 */ @@ -433,7 +660,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } } }; @@ -446,7 +677,7 @@ static const struct camss_subdev_resources ispif_res_660 = { "csi3", "csi3_pix", "csi3_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_660[] = { @@ -469,9 +700,13 @@ static const struct camss_subdev_resources vfe_res_660[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_8 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_8, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } }, /* VFE1 */ @@ -493,9 +728,13 @@ static const struct camss_subdev_resources vfe_res_660[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_8 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_8, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } } }; @@ -516,7 +755,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY1 */ @@ -535,7 +777,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY2 */ @@ -554,7 +799,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY3 */ @@ -573,7 +821,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } } }; @@ -596,7 +847,11 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID1 */ @@ -617,7 +872,11 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID2 */ @@ -638,8 +897,12 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } } }; @@ -662,9 +925,13 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 4, - .has_pd = true, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .has_pd = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE1 */ @@ -685,9 +952,13 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 4, - .has_pd = true, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .has_pd = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE-lite */ @@ -707,79 +978,101 @@ 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 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } } }; static const struct camss_subdev_resources csiphy_res_8250[] = { /* CSIPHY0 */ { - .regulators = {}, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY1 */ { - .regulators = {}, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY2 */ { - .regulators = {}, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY3 */ { - .regulators = {}, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "csiphy3", "csiphy3_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY4 */ { - .regulators = {}, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY5 */ { - .regulators = {}, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "csiphy5", "csiphy5_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, .reg = { "csiphy5" }, .interrupt = { "csiphy5" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } } }; static const struct camss_subdev_resources csid_res_8250[] = { /* CSID0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = {}, .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_areg", "vfe0_ahb" }, .clock_rate = { { 400000000 }, { 400000000 }, @@ -788,11 +1081,15 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = {}, .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_areg", "vfe1_ahb" }, .clock_rate = { { 400000000 }, { 400000000 }, @@ -801,11 +1098,15 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = {}, .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" }, .clock_rate = { { 400000000 }, { 400000000 }, @@ -813,12 +1114,16 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = {}, .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" }, .clock_rate = { { 400000000 }, { 400000000 }, @@ -826,8 +1131,12 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } } }; @@ -849,10 +1158,14 @@ 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 + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE1 */ { @@ -871,10 +1184,14 @@ 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 + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE2 (lite) */ { @@ -892,9 +1209,13 @@ 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 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE3 (lite) */ { @@ -912,9 +1233,13 @@ 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 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, }; @@ -941,6 +1266,678 @@ static const struct resources_icc icc_res_sm8250[] = { }, }; +static const struct camss_subdev_resources csiphy_res_7280[] = { + /* CSIPHY0 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY1 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY2 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY3 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY4 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy4", "csiphy4_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy4" }, + .interrupt = { "csiphy4" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, +}; + +static const struct camss_subdev_resources csid_res_7280[] = { + /* CSID0 */ + { + .regulators = {}, + + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 } + }, + + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = {}, + + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 } + }, + + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = {}, + + .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 } + }, + + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID3 */ + { + .regulators = {}, + + .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 } + }, + + .reg = { "csid_lite0" }, + .interrupt = { "csid_lite0" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID4 */ + { + .regulators = {}, + + .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 } + }, + + .reg = { "csid_lite1" }, + .interrupt = { "csid_lite1" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, +}; + +static const struct camss_subdev_resources vfe_res_7280[] = { + /* VFE0 */ + { + .regulators = {}, + + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe0", + "vfe0_axi", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 }, + { 0 }, + { 0 } }, + + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe1", + "vfe1_axi", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 }, + { 0 }, + { 0 } }, + + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe2", + "vfe2_axi", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 }, + { 0 }, + { 0 } }, + + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .hw_ops = &vfe_ops_170, + .has_pd = true, + .pd_name = "ife2", + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE3 (lite) */ + { + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", + "vfe_lite0", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 }, + { 0 } }, + + .regulators = {}, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE4 (lite) */ + { + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", + "vfe_lite1", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 }, + { 0 } }, + + .regulators = {}, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sc7280[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 38400, + .icc_bw_tbl.peak = 76800, + }, + { + .name = "hf_0", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + +static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { + /* CSIPHY0 */ + { + .regulators = {}, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY1 */ + { + .regulators = {}, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY2 */ + { + .regulators = {}, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY3 */ + { + .regulators = {}, + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, +}; + +static const struct camss_subdev_resources csid_res_sc8280xp[] = { + /* CSID0 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_axi" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_axi" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2", "vfe2_axi" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID3 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe3_csid", "vfe3_cphy_rx", "vfe3", "vfe3_axi" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid3" }, + .interrupt = { "csid3" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE0 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, }, + .reg = { "csid0_lite" }, + .interrupt = { "csid0_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE1 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, }, + .reg = { "csid1_lite" }, + .interrupt = { "csid1_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE2 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe_lite2_csid", "vfe_lite2_cphy_rx", "vfe_lite2" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, }, + .reg = { "csid2_lite" }, + .interrupt = { "csid2_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE3 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe_lite3_csid", "vfe_lite3_cphy_rx", "vfe_lite3" }, + .clock_rate = { { 400000000, 480000000, 600000000 }, + { 0 }, + { 0 }, }, + .reg = { "csid3_lite" }, + .interrupt = { "csid3_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + } +}; + +static const struct camss_subdev_resources vfe_res_sc8280xp[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe0", "vfe0_axi" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 400000000, 558000000, 637000000, 760000000 }, + { 0 }, }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 4, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe1", "vfe1_axi" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 400000000, 558000000, 637000000, 760000000 }, + { 0 }, }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 4, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe2", "vfe2_axi" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 400000000, 558000000, 637000000, 760000000 }, + { 0 }, }, + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 4, + .pd_name = "ife2", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE3 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe3", "vfe3_axi" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 400000000, 558000000, 637000000, 760000000 }, + { 0 }, }, + .reg = { "vfe3" }, + .interrupt = { "vfe3" }, + .vfe = { + .line_num = 4, + .pd_name = "ife3", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE_0 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite0" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 320000000, 400000000, 480000000, 600000000 }, }, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE_1 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite1" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 320000000, 400000000, 480000000, 600000000 }, }, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE_2 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite2" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 320000000, 400000000, 480000000, 600000000, }, }, + .reg = { "vfe_lite2" }, + .interrupt = { "vfe_lite2" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE_3 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite3" }, + .clock_rate = { { 0 }, + { 0 }, + { 19200000, 80000000}, + { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 }, + { 320000000, 400000000, 480000000, 600000000 }, }, + .reg = { "vfe_lite3" }, + .interrupt = { "vfe_lite3" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sc8280xp[] = { + { + .name = "cam_ahb", + .icc_bw_tbl.avg = 150000, + .icc_bw_tbl.peak = 300000, + }, + { + .name = "cam_hf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "cam_sf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "cam_sf_icp_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + /* * camss_add_clock_margin - Add margin to clock frequency rate * @rate: Clock frequency rate @@ -1083,7 +2080,7 @@ int camss_pm_domain_on(struct camss *camss, int id) if (id < camss->res->vfe_num) { struct vfe_device *vfe = &camss->vfe[id]; - ret = vfe->ops->pm_domain_on(vfe); + ret = vfe->res->hw_ops->pm_domain_on(vfe); } return ret; @@ -1094,10 +2091,52 @@ void camss_pm_domain_off(struct camss *camss, int id) if (id < camss->res->vfe_num) { struct vfe_device *vfe = &camss->vfe[id]; - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); + } +} + +static int vfe_parent_dev_ops_get(struct camss *camss, int id) +{ + int ret = -EINVAL; + + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + ret = vfe_get(vfe); } + + return ret; } +static int vfe_parent_dev_ops_put(struct camss *camss, int id) +{ + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + vfe_put(vfe); + } + + return 0; +} + +static void __iomem +*vfe_parent_dev_ops_get_base_address(struct camss *camss, int id) +{ + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + return vfe->base; + } + + return NULL; +} + +static const struct parent_dev_ops vfe_parent_dev_ops = { + .get = vfe_parent_dev_ops_get, + .put = vfe_parent_dev_ops_put, + .get_base_address = vfe_parent_dev_ops_get_base_address +}; + /* * camss_of_parse_endpoint_node - Parse port endpoint node * @dev: Device @@ -1114,8 +2153,11 @@ static int camss_of_parse_endpoint_node(struct device *dev, struct v4l2_mbus_config_mipi_csi2 *mipi_csi2; struct v4l2_fwnode_endpoint vep = { { 0 } }; unsigned int i; + int ret; - v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + if (ret) + return ret; csd->interface.csiphy_id = vep.base.port; @@ -1196,6 +2238,7 @@ err_cleanup: */ static int camss_init_subdevices(struct camss *camss) { + struct platform_device *pdev = to_platform_device(camss->dev); const struct camss_resources *res = camss->res; unsigned int i; int ret; @@ -1222,6 +2265,17 @@ static int camss_init_subdevices(struct camss *camss) } } + /* Get optional CSID wrapper regs shared between CSID devices */ + if (res->csid_wrapper_res) { + char *reg = res->csid_wrapper_res->reg; + void __iomem *base; + + base = devm_platform_ioremap_resource_byname(pdev, reg); + if (IS_ERR(base)) + return PTR_ERR(base); + camss->csid_wrapper_base = base; + } + for (i = 0; i < camss->res->csid_num; i++) { ret = msm_csid_subdev_init(camss, &camss->csid[i], &res->csid_res[i], i); @@ -1244,72 +2298,47 @@ static int camss_init_subdevices(struct camss *camss) } /* - * camss_register_entities - Register subdev nodes and create links + * camss_link_entities - Register subdev nodes and create links + * camss_link_err - print error in case link creation fails + * @src_name: name for source of the link + * @sink_name: name for sink of the link + */ +inline void camss_link_err(struct camss *camss, + const char *src_name, + const char *sink_name, + int ret) +{ + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + src_name, + sink_name, + ret); +} + +/* + * camss_link_entities - Register subdev nodes and create links * @camss: CAMSS device * * Return 0 on success or a negative error code on failure */ -static int camss_register_entities(struct camss *camss) +static int camss_link_entities(struct camss *camss) { int i, j, k; int ret; for (i = 0; i < camss->res->csiphy_num; i++) { - ret = msm_csiphy_register_entity(&camss->csiphy[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csiphy%d entity: %d\n", - i, ret); - goto err_reg_csiphy; - } - } - - for (i = 0; i < camss->res->csid_num; i++) { - ret = msm_csid_register_entity(&camss->csid[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csid%d entity: %d\n", - i, ret); - goto err_reg_csid; - } - } - - ret = msm_ispif_register_entities(camss->ispif, - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register ispif entities: %d\n", - ret); - goto err_reg_ispif; - } - - for (i = 0; i < camss->res->vfe_num; i++) { - ret = msm_vfe_register_entities(&camss->vfe[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register vfe%d entities: %d\n", - i, ret); - goto err_reg_vfe; - } - } - - for (i = 0; i < camss->res->csiphy_num; i++) { for (j = 0; j < camss->res->csid_num; j++) { - ret = media_create_pad_link( - &camss->csiphy[i].subdev.entity, - MSM_CSIPHY_PAD_SRC, - &camss->csid[j].subdev.entity, - MSM_CSID_PAD_SINK, - 0); + ret = media_create_pad_link(&camss->csiphy[i].subdev.entity, + MSM_CSIPHY_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->csiphy[i].subdev.entity.name, - camss->csid[j].subdev.entity.name, - ret); - goto err_link; + camss_link_err(camss, + camss->csiphy[i].subdev.entity.name, + camss->csid[j].subdev.entity.name, + ret); + return ret; } } } @@ -1317,26 +2346,24 @@ static int camss_register_entities(struct camss *camss) if (camss->ispif) { for (i = 0; i < camss->res->csid_num; i++) { for (j = 0; j < camss->ispif->line_num; j++) { - ret = media_create_pad_link( - &camss->csid[i].subdev.entity, - MSM_CSID_PAD_SRC, - &camss->ispif->line[j].subdev.entity, - MSM_ISPIF_PAD_SINK, - 0); + ret = media_create_pad_link(&camss->csid[i].subdev.entity, + MSM_CSID_PAD_SRC, + &camss->ispif->line[j].subdev.entity, + MSM_ISPIF_PAD_SINK, + 0); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->csid[i].subdev.entity.name, - camss->ispif->line[j].subdev.entity.name, - ret); - goto err_link; + camss_link_err(camss, + camss->csid[i].subdev.entity.name, + camss->ispif->line[j].subdev.entity.name, + ret); + return ret; } } } for (i = 0; i < camss->ispif->line_num; i++) for (k = 0; k < camss->res->vfe_num; k++) - for (j = 0; j < camss->vfe[k].line_num; j++) { + for (j = 0; j < camss->vfe[k].res->line_num; j++) { struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1346,18 +2373,16 @@ static int camss_register_entities(struct camss *camss) MSM_VFE_PAD_SINK, 0); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - ispif->entity.name, - vfe->entity.name, - ret); - goto err_link; + camss_link_err(camss, ispif->entity.name, + vfe->entity.name, + ret); + return ret; } } } else { for (i = 0; i < camss->res->csid_num; i++) for (k = 0; k < camss->res->vfe_num; k++) - for (j = 0; j < camss->vfe[k].line_num; j++) { + for (j = 0; j < camss->vfe[k].res->line_num; j++) { struct v4l2_subdev *csid = &camss->csid[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1367,20 +2392,70 @@ static int camss_register_entities(struct camss *camss) MSM_VFE_PAD_SINK, 0); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - csid->entity.name, - vfe->entity.name, - ret); - goto err_link; + camss_link_err(camss, csid->entity.name, + vfe->entity.name, + ret); + return ret; } } } return 0; +} + +/* + * camss_register_entities - Register subdev nodes and create links + * @camss: CAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int camss_register_entities(struct camss *camss) +{ + int i; + int ret; + + for (i = 0; i < camss->res->csiphy_num; i++) { + ret = msm_csiphy_register_entity(&camss->csiphy[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csiphy%d entity: %d\n", + i, ret); + goto err_reg_csiphy; + } + } + + for (i = 0; i < camss->res->csid_num; i++) { + ret = msm_csid_register_entity(&camss->csid[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csid%d entity: %d\n", + i, ret); + goto err_reg_csid; + } + } + + ret = msm_ispif_register_entities(camss->ispif, + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, "Failed to register ispif entities: %d\n", ret); + goto err_reg_ispif; + } + + for (i = 0; i < camss->res->vfe_num; i++) { + ret = msm_vfe_register_entities(&camss->vfe[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register vfe%d entities: %d\n", + i, ret); + goto err_reg_vfe; + } + } + + return 0; -err_link: - i = camss->res->vfe_num; err_reg_vfe: for (i--; i >= 0; i--) msm_vfe_unregister_entities(&camss->vfe[i]); @@ -1468,9 +2543,9 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) input, MSM_CSIPHY_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - sensor->name, input->name, ret); + camss_link_err(camss, sensor->name, + input->name, + ret); return ret; } } @@ -1518,7 +2593,7 @@ static int camss_configure_pd(struct camss *camss) /* 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) + if (res->vfe_res[i].vfe.has_pd) vfepd_num++; } @@ -1537,10 +2612,8 @@ static int camss_configure_pd(struct camss *camss) 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 (IS_ERR(camss->genpd)) + return PTR_ERR(camss->genpd); } if (!camss->genpd) { @@ -1550,14 +2623,13 @@ static int camss_configure_pd(struct camss *camss) */ camss->genpd = dev_pm_domain_attach_by_id(camss->dev, camss->genpd_num - 1); + if (IS_ERR(camss->genpd)) + return PTR_ERR(camss->genpd); } - if (IS_ERR_OR_NULL(camss->genpd)) { - if (!camss->genpd) - ret = -ENODEV; - else - ret = PTR_ERR(camss->genpd); - goto fail_pm; - } + + if (!camss->genpd) + return -ENODEV; + camss->genpd_link = device_link_add(camss->dev, camss->genpd, DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); @@ -1646,6 +2718,7 @@ static int camss_probe(struct platform_device *pdev) return -ENOMEM; if (camss->res->version == CAMSS_8x16 || + camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_8x96) { camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL); if (!camss->ispif) @@ -1690,6 +2763,8 @@ static int camss_probe(struct platform_device *pdev) v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev); + pm_runtime_enable(dev); + num_subdevs = camss_of_parse_ports(camss); if (num_subdevs < 0) { ret = num_subdevs; @@ -1700,6 +2775,10 @@ static int camss_probe(struct platform_device *pdev) if (ret < 0) goto err_v4l2_device_unregister; + ret = camss->res->link_entities(camss); + if (ret < 0) + goto err_register_subdevs; + if (num_subdevs) { camss->notifier.ops = &camss_subdev_notifier_ops; @@ -1726,8 +2805,6 @@ static int camss_probe(struct platform_device *pdev) } } - pm_runtime_enable(dev); - return 0; err_register_subdevs: @@ -1735,6 +2812,7 @@ err_register_subdevs: err_v4l2_device_unregister: v4l2_device_unregister(&camss->v4l2_dev); v4l2_async_nf_cleanup(&camss->notifier); + pm_runtime_disable(dev); err_genpd_cleanup: camss_genpd_cleanup(camss); @@ -1779,6 +2857,21 @@ static const struct camss_resources msm8916_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8x16), .csid_num = ARRAY_SIZE(csid_res_8x16), .vfe_num = ARRAY_SIZE(vfe_res_8x16), + .link_entities = camss_link_entities +}; + +static const struct camss_resources msm8953_resources = { + .version = CAMSS_8x53, + .icc_res = icc_res_8x53, + .icc_path_num = ARRAY_SIZE(icc_res_8x53), + .csiphy_res = csiphy_res_8x96, + .csid_res = csid_res_8x53, + .ispif_res = &ispif_res_8x53, + .vfe_res = vfe_res_8x53, + .csiphy_num = ARRAY_SIZE(csiphy_res_8x96), + .csid_num = ARRAY_SIZE(csid_res_8x53), + .vfe_num = ARRAY_SIZE(vfe_res_8x53), + .link_entities = camss_link_entities }; static const struct camss_resources msm8996_resources = { @@ -1790,6 +2883,7 @@ static const struct camss_resources msm8996_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8x96), .csid_num = ARRAY_SIZE(csid_res_8x96), .vfe_num = ARRAY_SIZE(vfe_res_8x96), + .link_entities = camss_link_entities }; static const struct camss_resources sdm660_resources = { @@ -1801,6 +2895,7 @@ static const struct camss_resources sdm660_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_660), .csid_num = ARRAY_SIZE(csid_res_660), .vfe_num = ARRAY_SIZE(vfe_res_660), + .link_entities = camss_link_entities }; static const struct camss_resources sdm845_resources = { @@ -1811,6 +2906,7 @@ static const struct camss_resources sdm845_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_845), .csid_num = ARRAY_SIZE(csid_res_845), .vfe_num = ARRAY_SIZE(vfe_res_845), + .link_entities = camss_link_entities }; static const struct camss_resources sm8250_resources = { @@ -1824,11 +2920,44 @@ static const struct camss_resources sm8250_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8250), .csid_num = ARRAY_SIZE(csid_res_8250), .vfe_num = ARRAY_SIZE(vfe_res_8250), + .link_entities = camss_link_entities +}; + +static const struct camss_resources sc8280xp_resources = { + .version = CAMSS_8280XP, + .pd_name = "top", + .csiphy_res = csiphy_res_sc8280xp, + .csid_res = csid_res_sc8280xp, + .ispif_res = NULL, + .vfe_res = vfe_res_sc8280xp, + .icc_res = icc_res_sc8280xp, + .icc_path_num = ARRAY_SIZE(icc_res_sc8280xp), + .csiphy_num = ARRAY_SIZE(csiphy_res_sc8280xp), + .csid_num = ARRAY_SIZE(csid_res_sc8280xp), + .vfe_num = ARRAY_SIZE(vfe_res_sc8280xp), + .link_entities = camss_link_entities +}; + +static const struct camss_resources sc7280_resources = { + .version = CAMSS_7280, + .pd_name = "top", + .csiphy_res = csiphy_res_7280, + .csid_res = csid_res_7280, + .vfe_res = vfe_res_7280, + .icc_res = icc_res_sc7280, + .icc_path_num = ARRAY_SIZE(icc_res_sc7280), + .csiphy_num = ARRAY_SIZE(csiphy_res_7280), + .csid_num = ARRAY_SIZE(csid_res_7280), + .vfe_num = ARRAY_SIZE(vfe_res_7280), + .link_entities = camss_link_entities }; static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss", .data = &msm8916_resources }, + { .compatible = "qcom,msm8953-camss", .data = &msm8953_resources }, { .compatible = "qcom,msm8996-camss", .data = &msm8996_resources }, + { .compatible = "qcom,sc7280-camss", .data = &sc7280_resources }, + { .compatible = "qcom,sc8280xp-camss", .data = &sc8280xp_resources }, { .compatible = "qcom,sdm660-camss", .data = &sdm660_resources }, { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, @@ -1878,7 +3007,7 @@ static const struct dev_pm_ops camss_pm_ops = { static struct platform_driver qcom_camss_driver = { .probe = camss_probe, - .remove_new = camss_remove, + .remove = camss_remove, .driver = { .name = "qcom-camss", .of_match_table = camss_dt_match, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index a0c2dcc779f0..9a046eea334f 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -22,6 +22,7 @@ #include "camss-csiphy.h" #include "camss-ispif.h" #include "camss-vfe.h" +#include "camss-format.h" #define to_camss(ptr_module) \ container_of(ptr_module, struct camss, ptr_module) @@ -48,11 +49,11 @@ 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; + union { + struct csiphy_subdev_resources csiphy; + struct csid_subdev_resources csid; + struct vfe_subdev_resources vfe; + }; }; struct icc_bw_tbl { @@ -65,6 +66,10 @@ struct resources_icc { struct icc_bw_tbl icc_bw_tbl; }; +struct resources_wrapper { + char *reg; +}; + enum pm_domain { PM_DOMAIN_VFE0 = 0, PM_DOMAIN_VFE1 = 1, @@ -72,11 +77,14 @@ enum pm_domain { }; enum camss_version { + CAMSS_660, + CAMSS_7280, CAMSS_8x16, + CAMSS_8x53, CAMSS_8x96, - CAMSS_660, - CAMSS_845, CAMSS_8250, + CAMSS_8280XP, + CAMSS_845, }; enum icc_count { @@ -91,11 +99,13 @@ struct camss_resources { const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; const struct camss_subdev_resources *vfe_res; + const struct resources_wrapper *csid_wrapper_res; const struct resources_icc *icc_res; const unsigned int icc_path_num; const unsigned int csiphy_num; const unsigned int csid_num; const unsigned int vfe_num; + int (*link_entities)(struct camss *camss); }; struct camss { @@ -107,6 +117,7 @@ struct camss { struct csid_device *csid; struct ispif_device *ispif; struct vfe_device *vfe; + void __iomem *csid_wrapper_base; atomic_t ref_count; int genpd_num; struct device *genpd; @@ -132,6 +143,12 @@ struct camss_clock { u32 nfreqs; }; +struct parent_dev_ops { + int (*get)(struct camss *camss, int id); + int (*put)(struct camss *camss, int id); + void __iomem *(*get_base_address)(struct camss *camss, int id); +}; + void camss_add_clock_margin(u64 *rate); int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); @@ -142,6 +159,8 @@ s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock); int camss_pm_domain_on(struct camss *camss, int id); void camss_pm_domain_off(struct camss *camss, int id); +int camss_vfe_get(struct camss *camss, int id); +void camss_vfe_put(struct camss *camss, int id); void camss_delete(struct camss *camss); #endif /* QC_MSM_CAMSS_H */ diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig index bfd50e8f3421..bc2e410b29cb 100644 --- a/drivers/media/platform/qcom/venus/Kconfig +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -3,6 +3,7 @@ config VIDEO_QCOM_VENUS depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV && QCOM_SMEM depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + select OF_DYNAMIC if ARCH_QCOM select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index ce206b709754..77d48578ecd2 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -19,6 +19,7 @@ #include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <media/videobuf2-v4l2.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-mem2mem.h> #include <media/v4l2-ioctl.h> @@ -68,6 +69,7 @@ static void venus_event_notify(struct venus_core *core, u32 event) mutex_lock(&core->lock); set_bit(0, &core->sys_error); + set_bit(0, &core->dump_core); list_for_each_entry(inst, &core->instances, list) inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); mutex_unlock(&core->lock); @@ -110,7 +112,10 @@ static void venus_sys_error_handler(struct work_struct *work) venus_shutdown(core); - venus_coredump(core); + if (test_bit(0, &core->dump_core)) { + venus_coredump(core); + clear_bit(0, &core->dump_core); + } pm_runtime_put_sync(core->dev); @@ -281,6 +286,89 @@ static irqreturn_t venus_isr_thread(int irq, void *dev_id) return ret; } +#if defined(CONFIG_OF_DYNAMIC) +static int venus_add_video_core(struct venus_core *core, const char *node_name, + const char *compat) +{ + struct of_changeset *ocs = core->ocs; + struct device *dev = core->dev; + struct device_node *np, *enp; + int ret; + + if (!node_name) + return 0; + + enp = of_find_node_by_name(dev->of_node, node_name); + if (enp) { + of_node_put(enp); + return 0; + } + + np = of_changeset_create_node(ocs, dev->of_node, node_name); + if (!np) { + dev_err(dev, "Unable to create new node\n"); + return -ENODEV; + } + + ret = of_changeset_add_prop_string(ocs, np, "compatible", compat); + if (ret) + dev_err(dev, "unable to add %s\n", compat); + + of_node_put(np); + + return ret; +} + +static int venus_add_dynamic_nodes(struct venus_core *core) +{ + struct device *dev = core->dev; + int ret; + + core->ocs = kmalloc(sizeof(*core->ocs), GFP_KERNEL); + if (!core->ocs) + return -ENOMEM; + + of_changeset_init(core->ocs); + + ret = venus_add_video_core(core, core->res->dec_nodename, "venus-decoder"); + if (ret) + goto err; + + ret = venus_add_video_core(core, core->res->enc_nodename, "venus-encoder"); + if (ret) + goto err; + + ret = of_changeset_apply(core->ocs); + if (ret) { + dev_err(dev, "applying changeset fail ret %d\n", ret); + goto err; + } + + return 0; +err: + of_changeset_destroy(core->ocs); + kfree(core->ocs); + core->ocs = NULL; + return ret; +} + +static void venus_remove_dynamic_nodes(struct venus_core *core) +{ + if (core->ocs) { + of_changeset_revert(core->ocs); + of_changeset_destroy(core->ocs); + kfree(core->ocs); + } +} +#else +static int venus_add_dynamic_nodes(struct venus_core *core) +{ + return 0; +} + +static void venus_remove_dynamic_nodes(struct venus_core *core) {} +#endif + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -360,9 +448,15 @@ static int venus_probe(struct platform_device *pdev) if (ret < 0) goto err_runtime_disable; + if (core->res->dec_nodename || core->res->enc_nodename) { + ret = venus_add_dynamic_nodes(core); + if (ret) + goto err_runtime_disable; + } + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); if (ret) - goto err_runtime_disable; + goto err_remove_dynamic_nodes; ret = venus_firmware_init(core); if (ret) @@ -406,10 +500,12 @@ err_firmware_deinit: venus_firmware_deinit(core); err_of_depopulate: of_platform_depopulate(dev); +err_remove_dynamic_nodes: + venus_remove_dynamic_nodes(core); err_runtime_disable: pm_runtime_put_noidle(dev); - pm_runtime_set_suspended(dev); pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); hfi_destroy(core); err_core_deinit: hfi_core_deinit(core, false); @@ -426,6 +522,7 @@ static void venus_remove(struct platform_device *pdev) struct device *dev = core->dev; int ret; + cancel_delayed_work_sync(&core->work); ret = pm_runtime_get_sync(dev); WARN_ON(ret < 0); @@ -437,6 +534,8 @@ static void venus_remove(struct platform_device *pdev) venus_firmware_deinit(core); + venus_remove_dynamic_nodes(core); + pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -497,6 +596,26 @@ err_cpucfg_path: return ret; } +void venus_close_common(struct venus_inst *inst) +{ + /* + * Make sure we don't have IRQ/IRQ-thread currently running + * or pending execution, which would race with the inst destruction. + */ + synchronize_irq(inst->core->irq); + + v4l2_m2m_ctx_release(inst->m2m_ctx); + v4l2_m2m_release(inst->m2m_dev); + hfi_session_destroy(inst); + v4l2_fh_del(&inst->fh); + v4l2_fh_exit(&inst->fh); + v4l2_ctrl_handler_free(&inst->ctrl_handler); + + mutex_destroy(&inst->lock); + mutex_destroy(&inst->ctx_q_lock); +} +EXPORT_SYMBOL_GPL(venus_close_common); + static __maybe_unused int venus_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); @@ -552,6 +671,8 @@ static const struct venus_resources msm8916_res = { .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, .fwname = "qcom/venus-1.8/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct freq_tbl msm8996_freq_table[] = { @@ -587,6 +708,44 @@ static const struct venus_resources msm8996_res = { .fwname = "qcom/venus-4.2/venus.mbn", }; +static const struct freq_tbl msm8998_freq_table[] = { + { 1944000, 465000000 }, /* 4k UHD @ 60 (decode only) */ + { 972000, 465000000 }, /* 4k UHD @ 30 */ + { 489600, 360000000 }, /* 1080p @ 60 */ + { 244800, 186000000 }, /* 1080p @ 30 */ + { 108000, 100000000 }, /* 720p @ 30 */ +}; + +static const struct reg_val msm8998_reg_preset[] = { + { 0x80124, 0x00000003 }, + { 0x80550, 0x01111111 }, + { 0x80560, 0x01111111 }, + { 0x80568, 0x01111111 }, + { 0x80570, 0x01111111 }, + { 0x80580, 0x01111111 }, + { 0x80588, 0x01111111 }, + { 0xe2010, 0x00000000 }, +}; + +static const struct venus_resources msm8998_res = { + .freq_tbl = msm8998_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8998_freq_table), + .reg_tbl = msm8998_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8998_reg_preset), + .clks = { "core", "iface", "bus", "mbus" }, + .clks_num = 4, + .vcodec0_clks = { "core" }, + .vcodec1_clks = { "core" }, + .vcodec_clks_num = 1, + .max_load = 2563200, + .hfi_version = HFI_VERSION_3XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-4.4/venus.mbn", +}; + static const struct freq_tbl sdm660_freq_table[] = { { 979200, 518400000 }, { 489600, 441600000 }, @@ -709,7 +868,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_clks_num = 2, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, - .opp_pmdomain = (const char *[]) { "cx", NULL }, + .opp_pmdomain = (const char *[]) { "cx" }, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -723,6 +882,8 @@ static const struct venus_resources sdm845_res_v2 = { .cp_nonpixel_start = 0x1000000, .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.2/venus.mbn", + .dec_nodename = "video-core0", + .enc_nodename = "video-core1", }; static const struct freq_tbl sc7180_freq_table[] = { @@ -758,7 +919,7 @@ static const struct venus_resources sc7180_res = { .vcodec_clks_num = 2, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "cx", NULL }, + .opp_pmdomain = (const char *[]) { "cx" }, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -771,6 +932,8 @@ static const struct venus_resources sc7180_res = { .cp_nonpixel_start = 0x1000000, .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.4/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct freq_tbl sm8250_freq_table[] = { @@ -815,7 +978,7 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx", NULL }, + .opp_pmdomain = (const char *[]) { "mx" }, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -826,6 +989,8 @@ static const struct venus_resources sm8250_res = { .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu-1.0/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct freq_tbl sc7280_freq_table[] = { @@ -874,7 +1039,7 @@ static const struct venus_resources sc7280_res = { .vcodec_clks_num = 2, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "cx", NULL }, + .opp_pmdomain = (const char *[]) { "cx" }, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, @@ -888,11 +1053,14 @@ static const struct venus_resources sc7280_res = { .cp_nonpixel_start = 0x1000000, .cp_nonpixel_size = 0x24800000, .fwname = "qcom/vpu-2.0/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, @@ -905,7 +1073,7 @@ MODULE_DEVICE_TABLE(of, venus_dt_match); static struct platform_driver qcom_venus_driver = { .probe = venus_probe, - .remove_new = venus_remove, + .remove = venus_remove, .driver = { .name = "qcom-venus", .of_match_table = venus_dt_match, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 6a77de374454..abeeafa86697 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -26,6 +26,7 @@ #define VIDC_CLKS_NUM_MAX 4 #define VIDC_VCODEC_CLKS_NUM_MAX 2 #define VIDC_RESETS_NUM_MAX 2 +#define VIDC_MAX_HIER_CODING_LAYER 6 extern int venus_fw_debug; @@ -89,6 +90,8 @@ struct venus_resources { u32 cp_nonpixel_start; u32 cp_nonpixel_size; const char *fwname; + const char *enc_nodename; + const char *dec_nodename; }; enum venus_fmt { @@ -132,9 +135,7 @@ struct venus_format { * @vcodec1_clks: an array of vcodec1 struct clk pointers * @video_path: an interconnect handle to video to/from memory path * @cpucfg_path: an interconnect handle to cpu configuration path - * @has_opp_table: does OPP table exist * @pmdomains: a pointer to a list of pmdomains - * @opp_dl_venus: an device-link for device OPP * @opp_pmdomain: an OPP power-domain * @resets: an array of reset signals * @vdev_dec: a reference to video device structure for decoder instances @@ -169,6 +170,8 @@ struct venus_format { * @core1_usage_count: usage counter for core1 * @root: debugfs root directory * @venus_ver: the venus firmware version + * @dump_core: a flag indicating that a core dump is required + * @ocs: OF changeset pointer */ struct venus_core { void __iomem *base; @@ -185,10 +188,8 @@ struct venus_core { struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; - bool has_opp_table; struct dev_pm_domain_list *pmdomains; - struct device_link *opp_dl_venus; - struct device *opp_pmdomain; + struct dev_pm_domain_list *opp_pmdomain; struct reset_control *resets[VIDC_RESETS_NUM_MAX]; struct video_device *vdev_dec; struct video_device *vdev_enc; @@ -232,6 +233,8 @@ struct venus_core { u32 minor; u32 rev; } venus_ver; + unsigned long dump_core; + struct of_changeset *ocs; }; struct vdec_controls { @@ -253,6 +256,7 @@ struct venc_controls { u32 rc_enable; u32 const_quality; u32 frame_skip_mode; + u32 layer_bitrate; u32 h264_i_period; u32 h264_entropy_mode; @@ -271,6 +275,8 @@ struct venc_controls { s32 h264_loop_filter_alpha; s32 h264_loop_filter_beta; u32 h264_8x8_transform; + u32 h264_hier_layers; + u32 h264_hier_layer_bitrate[VIDC_MAX_HIER_CODING_LAYER]; u32 hevc_i_qp; u32 hevc_p_qp; @@ -562,4 +568,6 @@ is_fw_rev_or_older(struct venus_core *core, u32 vmajor, u32 vminor, u32 vrev) (core)->venus_ver.minor == vminor && (core)->venus_ver.rev <= vrev); } + +void venus_close_common(struct venus_inst *inst); #endif diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index fe7da2b30482..66a18830e66d 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -316,10 +316,10 @@ int venus_firmware_init(struct venus_core *core) core->fw.dev = &pdev->dev; - iommu_dom = iommu_domain_alloc(&platform_bus_type); - if (!iommu_dom) { + iommu_dom = iommu_paging_domain_alloc(core->fw.dev); + if (IS_ERR(iommu_dom)) { dev_err(core->fw.dev, "Failed to allocate iommu domain\n"); - ret = -ENOMEM; + ret = PTR_ERR(iommu_dom); goto err_unregister; } diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index e00aedb41d16..675e6fd1e9fa 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -138,29 +138,6 @@ int hfi_core_trigger_ssr(struct venus_core *core, u32 type) return core->ops->core_trigger_ssr(core, type); } -int hfi_core_ping(struct venus_core *core) -{ - int ret; - - mutex_lock(&core->lock); - - ret = core->ops->core_ping(core, 0xbeef); - if (ret) - goto unlock; - - ret = wait_for_completion_timeout(&core->done, TIMEOUT); - if (!ret) { - ret = -ETIMEDOUT; - goto unlock; - } - ret = 0; - if (core->error != HFI_ERR_NONE) - ret = -ENODEV; -unlock: - mutex_unlock(&core->lock); - return ret; -} - static int wait_session_msg(struct venus_inst *inst) { int ret; diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index f25d412d6553..0338841d5992 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -108,7 +108,6 @@ struct hfi_inst_ops { struct hfi_ops { int (*core_init)(struct venus_core *core); int (*core_deinit)(struct venus_core *core); - int (*core_ping)(struct venus_core *core, u32 cookie); int (*core_trigger_ssr)(struct venus_core *core, u32 trigger_type); int (*session_init)(struct venus_inst *inst, u32 session_type, @@ -152,7 +151,6 @@ int hfi_core_deinit(struct venus_core *core, bool blocking); int hfi_core_suspend(struct venus_core *core); int hfi_core_resume(struct venus_core *core, bool force); int hfi_core_trigger_ssr(struct venus_core *core, u32 type); -int hfi_core_ping(struct venus_core *core); int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops); void hfi_session_destroy(struct venus_inst *inst); int hfi_session_init(struct venus_inst *inst, u32 pixfmt); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 3418d2dd9371..3ae063094e3e 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -156,7 +156,7 @@ void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt) pkt->hdr.size = sizeof(*pkt); pkt->hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY; pkt->num_properties = 1; - pkt->data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION; + pkt->data = HFI_PROPERTY_SYS_IMAGE_VERSION; } int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie, @@ -331,7 +331,7 @@ int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt, void *cookie, pkt->alloc_len = out_frame->alloc_len; pkt->filled_len = out_frame->filled_len; pkt->offset = out_frame->offset; - pkt->data[0] = out_frame->extradata_size; + pkt->data = out_frame->extradata_size; return 0; } @@ -402,7 +402,7 @@ static int pkt_session_get_property_1x(struct hfi_session_get_property_pkt *pkt, pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY; pkt->shdr.session_id = hash32_ptr(cookie); pkt->num_properties = 1; - pkt->data[0] = ptype; + pkt->data = ptype; return 0; } @@ -1110,7 +1110,7 @@ pkt_session_get_property_3xx(struct hfi_session_get_property_pkt *pkt, switch (ptype) { case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: - pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY; + pkt->data = HFI_PROPERTY_CONFIG_VDEC_ENTROPY; break; default: ret = pkt_session_get_property_1x(pkt, cookie, ptype); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 20acd412ee7b..a83125bc17aa 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -74,7 +74,7 @@ struct hfi_sys_set_property_pkt { struct hfi_sys_get_property_pkt { struct hfi_pkt_hdr hdr; u32 num_properties; - u32 data[1]; + u32 data; }; struct hfi_sys_set_buffers_pkt { @@ -82,7 +82,7 @@ struct hfi_sys_set_buffers_pkt { u32 buffer_type; u32 buffer_size; u32 num_buffers; - u32 buffer_addr[1]; + u32 buffer_addr[]; }; struct hfi_sys_ping_pkt { @@ -151,7 +151,7 @@ struct hfi_session_empty_buffer_compressed_pkt { u32 input_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[1]; + u32 data; }; struct hfi_session_empty_buffer_uncompressed_plane0_pkt { @@ -168,7 +168,7 @@ struct hfi_session_empty_buffer_uncompressed_plane0_pkt { u32 input_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[1]; + u32 data; }; struct hfi_session_empty_buffer_uncompressed_plane1_pkt { @@ -177,7 +177,7 @@ struct hfi_session_empty_buffer_uncompressed_plane1_pkt { u32 filled_len; u32 offset; u32 packet_buffer2; - u32 data[1]; + u32 data; }; struct hfi_session_empty_buffer_uncompressed_plane2_pkt { @@ -186,7 +186,7 @@ struct hfi_session_empty_buffer_uncompressed_plane2_pkt { u32 filled_len; u32 offset; u32 packet_buffer3; - u32 data[1]; + u32 data; }; struct hfi_session_fill_buffer_pkt { @@ -198,7 +198,7 @@ struct hfi_session_fill_buffer_pkt { u32 output_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[1]; + u32 data; }; struct hfi_session_flush_pkt { @@ -217,7 +217,7 @@ struct hfi_session_resume_pkt { struct hfi_session_get_property_pkt { struct hfi_session_hdr_pkt shdr; u32 num_properties; - u32 data[1]; + u32 data; }; struct hfi_session_release_buffer_pkt { @@ -227,7 +227,7 @@ struct hfi_session_release_buffer_pkt { u32 extradata_size; u32 response_req; u32 num_buffers; - u32 buffer_info[1]; + u32 buffer_info[] __counted_by(num_buffers); }; struct hfi_session_release_resources_pkt { diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index e4c05d62cfc7..f44059f19505 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -761,7 +761,7 @@ struct hfi_multi_stream_3x { struct hfi_multi_view_format { u32 views; - u32 view_order[1]; + u32 view_order[]; }; #define HFI_MULTI_SLICE_OFF 0x1 @@ -1005,13 +1005,13 @@ struct hfi_uncompressed_plane_constraints { struct hfi_uncompressed_plane_info { u32 format; u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_constraints[1]; + struct hfi_uncompressed_plane_constraints plane_constraints; }; struct hfi_uncompressed_format_supported { u32 buffer_type; u32 format_entries; - struct hfi_uncompressed_plane_info plane_info[1]; + struct hfi_uncompressed_plane_info plane_info; }; struct hfi_uncompressed_plane_actual { @@ -1038,7 +1038,7 @@ struct hfi_codec_supported { struct hfi_properties_supported { u32 num_properties; - u32 properties[1]; + u32 properties[]; }; struct hfi_max_sessions_supported { @@ -1085,12 +1085,12 @@ struct hfi_resource_ocmem_requirement { struct hfi_resource_ocmem_requirement_info { u32 num_entries; - struct hfi_resource_ocmem_requirement requirements[1]; + struct hfi_resource_ocmem_requirement requirements[]; }; struct hfi_property_sys_image_version_info_type { u32 string_size; - u8 str_image_version[1]; + u8 str_image_version[]; }; struct hfi_codec_mask_supported { @@ -1141,7 +1141,7 @@ struct hfi_extradata_header { u32 port_index; u32 type; u32 data_size; - u8 data[1]; + u8 data[]; }; struct hfi_batch_info { @@ -1236,7 +1236,7 @@ static inline void hfi_bufreq_set_count_min_host(struct hfi_buffer_requirements struct hfi_data_payload { u32 size; - u8 data[1]; + u8 data[]; }; struct hfi_enable_picture { @@ -1264,12 +1264,12 @@ struct hfi_interlace_format_supported { struct hfi_buffer_alloc_mode_supported { u32 buffer_type; u32 num_entries; - u32 data[1]; + u32 data[]; }; struct hfi_mb_error_map { u32 error_map_size; - u8 error_map[1]; + u8 error_map[]; }; struct hfi_metadata_pass_through { diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index c43839539d4d..3df241dc3a11 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -157,7 +157,7 @@ static void parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_uncompressed_format_supported *fmt = data; - struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info; + struct hfi_uncompressed_plane_info *pinfo = &fmt->plane_info; struct hfi_uncompressed_plane_constraints *constr; struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {}; u32 entries = fmt->format_entries; diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c index f5a655973c08..6289166786ec 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -1063,51 +1063,51 @@ struct enc_bufsize_ops { u32 (*persist)(void); }; -static struct dec_bufsize_ops dec_h264_ops = { +static const struct dec_bufsize_ops dec_h264_ops = { .scratch = h264d_scratch_size, .scratch1 = h264d_scratch1_size, .persist1 = h264d_persist1_size, }; -static struct dec_bufsize_ops dec_h265_ops = { +static const struct dec_bufsize_ops dec_h265_ops = { .scratch = h265d_scratch_size, .scratch1 = h265d_scratch1_size, .persist1 = h265d_persist1_size, }; -static struct dec_bufsize_ops dec_vp8_ops = { +static const struct dec_bufsize_ops dec_vp8_ops = { .scratch = vpxd_scratch_size, .scratch1 = vp8d_scratch1_size, .persist1 = vp8d_persist1_size, }; -static struct dec_bufsize_ops dec_vp9_ops = { +static const struct dec_bufsize_ops dec_vp9_ops = { .scratch = vpxd_scratch_size, .scratch1 = vp9d_scratch1_size, .persist1 = vp9d_persist1_size, }; -static struct dec_bufsize_ops dec_mpeg2_ops = { +static const struct dec_bufsize_ops dec_mpeg2_ops = { .scratch = mpeg2d_scratch_size, .scratch1 = mpeg2d_scratch1_size, .persist1 = mpeg2d_persist1_size, }; -static struct enc_bufsize_ops enc_h264_ops = { +static const struct enc_bufsize_ops enc_h264_ops = { .scratch = h264e_scratch_size, .scratch1 = h264e_scratch1_size, .scratch2 = enc_scratch2_size, .persist = enc_persist_size, }; -static struct enc_bufsize_ops enc_h265_ops = { +static const struct enc_bufsize_ops enc_h265_ops = { .scratch = h265e_scratch_size, .scratch1 = h265e_scratch1_size, .scratch2 = enc_scratch2_size, .persist = enc_persist_size, }; -static struct enc_bufsize_ops enc_vp8_ops = { +static const struct enc_bufsize_ops enc_vp8_ops = { .scratch = vp8e_scratch_size, .scratch1 = vp8e_scratch1_size, .scratch2 = enc_scratch2_size, @@ -1186,7 +1186,7 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, u32 codec = params->codec; u32 width = params->width, height = params->height, out_min_count; u32 out_width = params->out_width, out_height = params->out_height; - struct dec_bufsize_ops *dec_ops; + const struct dec_bufsize_ops *dec_ops; bool is_secondary_output = params->dec.is_secondary_output; bool is_interlaced = params->dec.is_interlaced; u32 max_mbs_per_frame = params->dec.max_mbs_per_frame; @@ -1260,7 +1260,7 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, struct hfi_buffer_requirements *bufreq) { enum hfi_version version = params->version; - struct enc_bufsize_ops *enc_ops; + const struct enc_bufsize_ops *enc_ops; u32 width = params->width; u32 height = params->height; bool is_tenbit = params->enc.is_tenbit; diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index f9437b6412b9..a9167867063c 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1178,16 +1178,6 @@ static int venus_core_deinit(struct venus_core *core) return 0; } -static int venus_core_ping(struct venus_core *core, u32 cookie) -{ - struct venus_hfi_device *hdev = to_hfi_priv(core); - struct hfi_sys_ping_pkt pkt; - - pkt_sys_ping(&pkt, cookie); - - return venus_iface_cmdq_write(hdev, &pkt, false); -} - static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) { struct venus_hfi_device *hdev = to_hfi_priv(core); @@ -1639,7 +1629,6 @@ static int venus_suspend(struct venus_core *core) static const struct hfi_ops venus_hfi_ops = { .core_init = venus_core_init, .core_deinit = venus_core_deinit, - .core_ping = venus_core_ping, .core_trigger_ssr = venus_core_trigger_ssr, .session_init = venus_session_init, diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 502822059498..33a5a659c0ad 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -412,10 +412,9 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) u32 val; int ret; - if (IS_V6(core)) { - ctrl = core->wrapper_base + WRAPPER_CORE_POWER_CONTROL_V6; - stat = core->wrapper_base + WRAPPER_CORE_POWER_STATUS_V6; - } else if (coreid == VIDC_CORE_ID_1) { + if (IS_V6(core)) + return dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable); + else if (coreid == VIDC_CORE_ID_1) { ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; } else { @@ -451,9 +450,11 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec0_clks); - ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret) + return ret; + } ret = pm_runtime_put_sync(core->pmdomains->pd_devs[1]); if (ret < 0) @@ -467,9 +468,11 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec1_clks); - ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret) + return ret; + } ret = pm_runtime_put_sync(core->pmdomains->pd_devs[2]); if (ret < 0) @@ -488,9 +491,11 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + } ret = vcodec_clks_enable(core, core->vcodec0_clks); if (ret) @@ -506,9 +511,11 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + } ret = vcodec_clks_enable(core, core->vcodec1_clks); if (ret) @@ -857,7 +864,6 @@ static int venc_power_v4(struct device *dev, int on) static int vcodec_domains_get(struct venus_core *core) { int ret; - struct device **opp_virt_dev; struct device *dev = core->dev; const struct venus_resources *res = core->res; struct dev_pm_domain_attach_data vcodec_data = { @@ -865,49 +871,29 @@ static int vcodec_domains_get(struct venus_core *core) .num_pd_names = res->vcodec_pmdomains_num, .pd_flags = PD_FLAG_NO_DEV_LINK, }; + struct dev_pm_domain_attach_data opp_pd_data = { + .pd_names = res->opp_pmdomain, + .num_pd_names = 1, + .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, + }; if (!res->vcodec_pmdomains_num) goto skip_pmdomains; - ret = dev_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains); + ret = devm_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains); if (ret < 0) return ret; skip_pmdomains: - if (!core->res->opp_pmdomain) + if (!res->opp_pmdomain) return 0; /* Attach the power domain for setting performance state */ - ret = devm_pm_opp_attach_genpd(dev, res->opp_pmdomain, &opp_virt_dev); - if (ret) - goto opp_attach_err; - - core->opp_pmdomain = *opp_virt_dev; - core->opp_dl_venus = device_link_add(dev, core->opp_pmdomain, - DL_FLAG_RPM_ACTIVE | - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS); - if (!core->opp_dl_venus) { - ret = -ENODEV; - goto opp_attach_err; - } + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + if (ret < 0) + return ret; return 0; - -opp_attach_err: - dev_pm_domain_detach_list(core->pmdomains); - return ret; -} - -static void vcodec_domains_put(struct venus_core *core) -{ - dev_pm_domain_detach_list(core->pmdomains); - - if (!core->has_opp_table) - return; - - if (core->opp_dl_venus) - device_link_del(core->opp_dl_venus); } static int core_resets_reset(struct venus_core *core) @@ -996,9 +982,7 @@ static int core_get_v4(struct venus_core *core) if (core->res->opp_pmdomain) { ret = devm_pm_opp_of_add_table(dev); - if (!ret) { - core->has_opp_table = true; - } else if (ret != -ENODEV) { + if (ret && ret != -ENODEV) { dev_err(dev, "invalid OPP table in device tree\n"); return ret; } @@ -1009,10 +993,6 @@ static int core_get_v4(struct venus_core *core) static void core_put_v4(struct venus_core *core) { - if (legacy_binding) - return; - - vcodec_domains_put(core); } static int core_power_v4(struct venus_core *core, int on) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 29130a9441e7..9f82882b77bc 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1255,7 +1255,7 @@ static int vdec_stop_output(struct venus_inst *inst) break; case VENUS_DEC_STATE_INIT: case VENUS_DEC_STATE_CAPTURE_SETUP: - ret = hfi_session_flush(inst, HFI_FLUSH_INPUT, true); + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); break; default: break; @@ -1697,10 +1697,6 @@ static int vdec_open(struct file *file) if (ret) goto err_free; - ret = hfi_session_create(inst, &vdec_hfi_ops); - if (ret) - goto err_ctrl_deinit; - vdec_inst_init(inst); ida_init(&inst->dpb_ids); @@ -1712,15 +1708,19 @@ static int vdec_open(struct file *file) inst->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops); if (IS_ERR(inst->m2m_dev)) { ret = PTR_ERR(inst->m2m_dev); - goto err_session_destroy; + goto err_ctrl_deinit; } inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); if (IS_ERR(inst->m2m_ctx)) { ret = PTR_ERR(inst->m2m_ctx); - goto err_m2m_release; + goto err_m2m_dev_release; } + ret = hfi_session_create(inst, &vdec_hfi_ops); + if (ret) + goto err_m2m_ctx_release; + v4l2_fh_init(&inst->fh, core->vdev_dec); inst->fh.ctrl_handler = &inst->ctrl_handler; @@ -1730,12 +1730,12 @@ static int vdec_open(struct file *file) return 0; -err_m2m_release: +err_m2m_ctx_release: + v4l2_m2m_ctx_release(inst->m2m_ctx); +err_m2m_dev_release: v4l2_m2m_release(inst->m2m_dev); -err_session_destroy: - hfi_session_destroy(inst); err_ctrl_deinit: - vdec_ctrl_deinit(inst); + v4l2_ctrl_handler_free(&inst->ctrl_handler); err_free: kfree(inst); return ret; @@ -1746,17 +1746,9 @@ static int vdec_close(struct file *file) struct venus_inst *inst = to_inst(file); vdec_pm_get(inst); - - v4l2_m2m_ctx_release(inst->m2m_ctx); - v4l2_m2m_release(inst->m2m_dev); - vdec_ctrl_deinit(inst); + cancel_work_sync(&inst->delayed_process_work); + venus_close_common(inst); ida_destroy(&inst->dpb_ids); - hfi_session_destroy(inst); - mutex_destroy(&inst->lock); - mutex_destroy(&inst->ctx_q_lock); - v4l2_fh_del(&inst->fh); - v4l2_fh_exit(&inst->fh); - vdec_pm_put(inst, false); kfree(inst); @@ -1874,7 +1866,7 @@ MODULE_DEVICE_TABLE(of, vdec_dt_match); static struct platform_driver qcom_venus_dec_driver = { .probe = vdec_probe, - .remove_new = vdec_remove, + .remove = vdec_remove, .driver = { .name = "qcom-venus-decoder", .of_match_table = vdec_dt_match, diff --git a/drivers/media/platform/qcom/venus/vdec.h b/drivers/media/platform/qcom/venus/vdec.h index 6b262d0bf561..0cf981108ff0 100644 --- a/drivers/media/platform/qcom/venus/vdec.h +++ b/drivers/media/platform/qcom/venus/vdec.h @@ -9,6 +9,5 @@ struct venus_inst; int vdec_ctrl_init(struct venus_inst *inst); -void vdec_ctrl_deinit(struct venus_inst *inst); #endif diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 7e0f29bf7fae..36ed955b0419 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -187,8 +187,3 @@ int vdec_ctrl_init(struct venus_inst *inst) return 0; } - -void vdec_ctrl_deinit(struct venus_inst *inst) -{ - v4l2_ctrl_handler_free(&inst->ctrl_handler); -} diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 3ec2fb8d9fab..c7f8e37dba9b 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -734,6 +734,29 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; + if (ctr->layer_bitrate) { + unsigned int i; + + ptype = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + ret = hfi_session_set_property(inst, ptype, &ctr->h264_hier_layers); + if (ret) + return ret; + + ptype = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER; + ret = hfi_session_set_property(inst, ptype, &ctr->layer_bitrate); + if (ret) + return ret; + + for (i = 0; i < ctr->h264_hier_layers; ++i) { + ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + brate.bitrate = ctr->h264_hier_layer_bitrate[i]; + brate.layer_id = i; + + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + } + } } if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || @@ -823,18 +846,33 @@ static int venc_set_properties(struct venus_inst *inst) return ret; } - if (!ctr->bitrate) - bitrate = 64000; - else - bitrate = ctr->bitrate; + if (!ctr->layer_bitrate) { + if (!ctr->bitrate) + bitrate = 64000; + else + bitrate = ctr->bitrate; - ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; - brate.bitrate = bitrate; - brate.layer_id = 0; + ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + brate.bitrate = bitrate; + brate.layer_id = 0; - ret = hfi_session_set_property(inst, ptype, &brate); - if (ret) - return ret; + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + + if (!ctr->bitrate_peak) + bitrate *= 2; + else + bitrate = ctr->bitrate_peak; + + ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE; + brate.bitrate = bitrate; + brate.layer_id = 0; + + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + } if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { @@ -849,19 +887,6 @@ static int venc_set_properties(struct venus_inst *inst) return ret; } - if (!ctr->bitrate_peak) - bitrate *= 2; - else - bitrate = ctr->bitrate_peak; - - ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE; - brate.bitrate = bitrate; - brate.layer_id = 0; - - ret = hfi_session_set_property(inst, ptype, &brate); - if (ret) - return ret; - ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP; if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { quant.qp_i = ctr->hevc_i_qp; @@ -1467,10 +1492,6 @@ static int venc_open(struct file *file) if (ret) goto err_free; - ret = hfi_session_create(inst, &venc_hfi_ops); - if (ret) - goto err_ctrl_deinit; - venc_inst_init(inst); /* @@ -1480,15 +1501,19 @@ static int venc_open(struct file *file) inst->m2m_dev = v4l2_m2m_init(&venc_m2m_ops); if (IS_ERR(inst->m2m_dev)) { ret = PTR_ERR(inst->m2m_dev); - goto err_session_destroy; + goto err_ctrl_deinit; } inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); if (IS_ERR(inst->m2m_ctx)) { ret = PTR_ERR(inst->m2m_ctx); - goto err_m2m_release; + goto err_m2m_dev_release; } + ret = hfi_session_create(inst, &venc_hfi_ops); + if (ret) + goto err_m2m_ctx_release; + v4l2_fh_init(&inst->fh, core->vdev_enc); inst->fh.ctrl_handler = &inst->ctrl_handler; @@ -1498,12 +1523,12 @@ static int venc_open(struct file *file) return 0; -err_m2m_release: +err_m2m_ctx_release: + v4l2_m2m_ctx_release(inst->m2m_ctx); +err_m2m_dev_release: v4l2_m2m_release(inst->m2m_dev); -err_session_destroy: - hfi_session_destroy(inst); err_ctrl_deinit: - venc_ctrl_deinit(inst); + v4l2_ctrl_handler_free(&inst->ctrl_handler); err_free: kfree(inst); return ret; @@ -1514,18 +1539,8 @@ static int venc_close(struct file *file) struct venus_inst *inst = to_inst(file); venc_pm_get(inst); - - v4l2_m2m_ctx_release(inst->m2m_ctx); - v4l2_m2m_release(inst->m2m_dev); - venc_ctrl_deinit(inst); - hfi_session_destroy(inst); - mutex_destroy(&inst->lock); - mutex_destroy(&inst->ctx_q_lock); - v4l2_fh_del(&inst->fh); - v4l2_fh_exit(&inst->fh); - + venus_close_common(inst); inst->enc_state = VENUS_ENC_STATE_DEINIT; - venc_pm_put(inst, false); kfree(inst); @@ -1643,7 +1658,7 @@ MODULE_DEVICE_TABLE(of, venc_dt_match); static struct platform_driver qcom_venus_enc_driver = { .probe = venc_probe, - .remove_new = venc_remove, + .remove = venc_remove, .driver = { .name = "qcom-venus-encoder", .of_match_table = venc_dt_match, diff --git a/drivers/media/platform/qcom/venus/venc.h b/drivers/media/platform/qcom/venus/venc.h index 4ea37fdcd9b8..719d0f73b14b 100644 --- a/drivers/media/platform/qcom/venus/venc.h +++ b/drivers/media/platform/qcom/venus/venc.h @@ -9,6 +9,5 @@ struct venus_inst; int venc_ctrl_init(struct venus_inst *inst); -void venc_ctrl_deinit(struct venus_inst *inst); #endif diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index d9d2a293f3ef..51801a962ed2 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -67,12 +67,28 @@ static int venc_calc_bpframes(u32 gop_size, u32 conseq_b, u32 *bf, u32 *pf) return 0; } +static int dynamic_bitrate_update(struct venus_inst *inst, u32 bitrate, + u32 layer_id) +{ + int ret = 0; + + mutex_lock(&inst->lock); + if (inst->streamon_out && inst->streamon_cap) { + u32 ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + struct hfi_bitrate brate = { .bitrate = bitrate, .layer_id = layer_id }; + + ret = hfi_session_set_property(inst, ptype, &brate); + } + mutex_unlock(&inst->lock); + + return ret; +} + static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) { struct venus_inst *inst = ctrl_to_inst(ctrl); struct venc_controls *ctr = &inst->controls.enc; struct hfi_enable en = { .enable = 1 }; - struct hfi_bitrate brate; struct hfi_ltr_use ltr_use; struct hfi_ltr_mark ltr_mark; u32 bframes; @@ -85,19 +101,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_BITRATE: ctr->bitrate = ctrl->val; - mutex_lock(&inst->lock); - if (inst->streamon_out && inst->streamon_cap) { - ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; - brate.bitrate = ctr->bitrate; - brate.layer_id = 0; - - ret = hfi_session_set_property(inst, ptype, &brate); - if (ret) { - mutex_unlock(&inst->lock); - return ret; - } - } - mutex_unlock(&inst->lock); + ret = dynamic_bitrate_update(inst, ctr->bitrate, 0); + if (ret) + return ret; break; case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ctr->bitrate_peak = ctrl->val; @@ -340,6 +346,55 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) ctr->h264_8x8_transform = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE: + if (ctrl->val != V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P) + return -EINVAL; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING: + ctr->layer_bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER: + if (ctrl->val > VIDC_MAX_HIER_CODING_LAYER) + return -EINVAL; + ctr->h264_hier_layers = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR: + ctr->h264_hier_layer_bitrate[0] = ctrl->val; + ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[0], 0); + if (ret) + return ret; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR: + ctr->h264_hier_layer_bitrate[1] = ctrl->val; + ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[1], 1); + if (ret) + return ret; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR: + ctr->h264_hier_layer_bitrate[2] = ctrl->val; + ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[2], 2); + if (ret) + return ret; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR: + ctr->h264_hier_layer_bitrate[3] = ctrl->val; + ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[3], 3); + if (ret) + return ret; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR: + ctr->h264_hier_layer_bitrate[4] = ctrl->val; + ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[4], 4); + if (ret) + return ret; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR: + ctr->h264_hier_layer_bitrate[5] = ctrl->val; + ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[5], 5); + if (ret) + return ret; + break; + default: return -EINVAL; } @@ -622,6 +677,49 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD, 0, ((4096 * 2304) >> 8), 1, 0); + if (IS_V4(inst->core) || IS_V6(inst->core)) { + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE, + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + 1, V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER, 0, + VIDC_MAX_HIER_CODING_LAYER, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR, + BITRATE_MIN, BITRATE_MAX, BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR, + BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR, + BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR, + BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR, + BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR, + BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + } + ret = inst->ctrl_handler.error; if (ret) goto err; @@ -635,8 +733,3 @@ err: v4l2_ctrl_handler_free(&inst->ctrl_handler); return ret; } - -void venc_ctrl_deinit(struct venus_inst *inst) -{ - v4l2_ctrl_handler_free(&inst->ctrl_handler); -} diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig new file mode 100644 index 000000000000..bd5101ffefb5 --- /dev/null +++ b/drivers/media/platform/raspberrypi/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Raspberry Pi media platform drivers" + +source "drivers/media/platform/raspberrypi/pisp_be/Kconfig" +source "drivers/media/platform/raspberrypi/rp1-cfe/Kconfig" diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile new file mode 100644 index 000000000000..af7fde84eefe --- /dev/null +++ b/drivers/media/platform/raspberrypi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += pisp_be/ +obj-y += rp1-cfe/ diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig new file mode 100644 index 000000000000..46765a2e4c4d --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig @@ -0,0 +1,13 @@ +config VIDEO_RASPBERRYPI_PISP_BE + tristate "Raspberry Pi PiSP Backend (BE) ISP driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_BCM2835 || COMPILE_TEST + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + help + Say Y here to enable support for the PiSP Backend (BE) ISP driver. + + To compile this driver as a module, choose M here. The module will be + called pisp-be. diff --git a/drivers/media/platform/raspberrypi/pisp_be/Makefile b/drivers/media/platform/raspberrypi/pisp_be/Makefile new file mode 100644 index 000000000000..a70bf5716824 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Raspberry Pi PiSP Backend driver +# +pisp-be-objs := pisp_be.o +obj-$(CONFIG_VIDEO_RASPBERRYPI_PISP_BE) += pisp-be.o diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c new file mode 100644 index 000000000000..7596ae1f7de6 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c @@ -0,0 +1,1797 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PiSP Back End driver. + * Copyright (c) 2021-2024 Raspberry Pi Limited. + * + */ +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> + +#include <uapi/linux/media/raspberrypi/pisp_be_config.h> + +#include "pisp_be_formats.h" + +/* Maximum number of config buffers possible */ +#define PISP_BE_NUM_CONFIG_BUFFERS VB2_MAX_FRAME + +#define PISPBE_NAME "pispbe" + +/* Some ISP-BE registers */ +#define PISP_BE_VERSION_REG 0x0 +#define PISP_BE_CONTROL_REG 0x4 +#define PISP_BE_CONTROL_COPY_CONFIG BIT(1) +#define PISP_BE_CONTROL_QUEUE_JOB BIT(0) +#define PISP_BE_CONTROL_NUM_TILES(n) ((n) << 16) +#define PISP_BE_TILE_ADDR_LO_REG 0x8 +#define PISP_BE_TILE_ADDR_HI_REG 0xc +#define PISP_BE_STATUS_REG 0x10 +#define PISP_BE_STATUS_QUEUED BIT(0) +#define PISP_BE_BATCH_STATUS_REG 0x14 +#define PISP_BE_INTERRUPT_EN_REG 0x18 +#define PISP_BE_INTERRUPT_STATUS_REG 0x1c +#define PISP_BE_AXI_REG 0x20 +#define PISP_BE_CONFIG_BASE_REG 0x40 +#define PISP_BE_IO_ADDR_LOW(n) (PISP_BE_CONFIG_BASE_REG + 8 * (n)) +#define PISP_BE_IO_ADDR_HIGH(n) (PISP_BE_IO_ADDR_LOW((n)) + 4) +#define PISP_BE_GLOBAL_BAYER_ENABLE 0xb0 +#define PISP_BE_GLOBAL_RGB_ENABLE 0xb4 +#define N_HW_ADDRESSES 13 +#define N_HW_ENABLES 2 + +#define PISP_BE_VERSION_2712 0x02252700 +#define PISP_BE_VERSION_MINOR_BITS 0xf + +/* + * This maps our nodes onto the inputs/outputs of the actual PiSP Back End. + * Be wary of the word "OUTPUT" which is used ambiguously here. In a V4L2 + * context it means an input to the hardware (source image or metadata). + * Elsewhere it means an output from the hardware. + */ +enum pispbe_node_ids { + MAIN_INPUT_NODE, + TDN_INPUT_NODE, + STITCH_INPUT_NODE, + OUTPUT0_NODE, + OUTPUT1_NODE, + TDN_OUTPUT_NODE, + STITCH_OUTPUT_NODE, + CONFIG_NODE, + PISPBE_NUM_NODES +}; + +struct pispbe_node_description { + const char *ent_name; + enum v4l2_buf_type buf_type; + unsigned int caps; +}; + +static const struct pispbe_node_description node_desc[PISPBE_NUM_NODES] = { + /* MAIN_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* TDN_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-tdn_input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* STITCH_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-stitch_input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* OUTPUT0_NODE */ + { + .ent_name = PISPBE_NAME "-output0", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* OUTPUT1_NODE */ + { + .ent_name = PISPBE_NAME "-output1", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* TDN_OUTPUT_NODE */ + { + .ent_name = PISPBE_NAME "-tdn_output", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* STITCH_OUTPUT_NODE */ + { + .ent_name = PISPBE_NAME "-stitch_output", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* CONFIG_NODE */ + { + .ent_name = PISPBE_NAME "-config", + .buf_type = V4L2_BUF_TYPE_META_OUTPUT, + .caps = V4L2_CAP_META_OUTPUT, + } +}; + +#define NODE_DESC_IS_OUTPUT(desc) ( \ + ((desc)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \ + ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \ + ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) + +#define NODE_IS_META(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT)) +#define NODE_IS_OUTPUT(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) +#define NODE_IS_CAPTURE(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) +#define NODE_IS_MPLANE(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + +/* + * Structure to describe a single node /dev/video<N> which represents a single + * input or output queue to the PiSP Back End device. + */ +struct pispbe_node { + unsigned int id; + int vfl_dir; + enum v4l2_buf_type buf_type; + struct video_device vfd; + struct media_pad pad; + struct media_intf_devnode *intf_devnode; + struct media_link *intf_link; + struct pispbe_dev *pispbe; + /* Video device lock */ + struct mutex node_lock; + /* vb2_queue lock */ + struct mutex queue_lock; + /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */ + spinlock_t ready_lock; + struct list_head ready_queue; + struct vb2_queue queue; + struct v4l2_format format; + const struct pisp_be_format *pisp_format; +}; + +/* For logging only, use the entity name with "pispbe" and separator removed */ +#define NODE_NAME(node) \ + (node_desc[(node)->id].ent_name + sizeof(PISPBE_NAME)) + +/* Records details of the jobs currently running or queued on the h/w. */ +struct pispbe_job { + bool valid; + /* + * An array of buffer pointers - remember it's source buffers first, + * then captures, then metadata last. + */ + struct pispbe_buffer *buf[PISPBE_NUM_NODES]; +}; + +struct pispbe_hw_enables { + u32 bayer_enables; + u32 rgb_enables; +}; + +/* Records a job configuration and memory addresses. */ +struct pispbe_job_descriptor { + dma_addr_t hw_dma_addrs[N_HW_ADDRESSES]; + struct pisp_be_tiles_config *config; + struct pispbe_hw_enables hw_enables; + dma_addr_t tiles; +}; + +/* + * Structure representing the entire PiSP Back End device, comprising several + * nodes which share platform resources and a mutex for the actual HW. + */ +struct pispbe_dev { + struct device *dev; + struct pispbe_dev *pispbe; + struct pisp_be_tiles_config *config; + void __iomem *be_reg_base; + struct clk *clk; + struct v4l2_device v4l2_dev; + struct v4l2_subdev sd; + struct media_device mdev; + struct media_pad pad[PISPBE_NUM_NODES]; /* output pads first */ + struct pispbe_node node[PISPBE_NUM_NODES]; + dma_addr_t config_dma_addr; + unsigned int sequence; + u32 streaming_map; + struct pispbe_job queued_job, running_job; + spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */ + bool hw_busy; /* non-zero if a job is queued or is being started */ + int irq; + u32 hw_version; + u8 done, started; +}; + +static u32 pispbe_rd(struct pispbe_dev *pispbe, unsigned int offset) +{ + return readl(pispbe->be_reg_base + offset); +} + +static void pispbe_wr(struct pispbe_dev *pispbe, unsigned int offset, u32 val) +{ + writel(val, pispbe->be_reg_base + offset); +} + +/* + * Queue a job to the h/w. If the h/w is idle it will begin immediately. + * Caller must ensure it is "safe to queue", i.e. we don't already have a + * queued, unstarted job. + */ +static void pispbe_queue_job(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job) +{ + unsigned int begin, end; + + if (pispbe_rd(pispbe, PISP_BE_STATUS_REG) & PISP_BE_STATUS_QUEUED) + dev_err(pispbe->dev, "ERROR: not safe to queue new job!\n"); + + /* + * Write configuration to hardware. DMA addresses and enable flags + * are passed separately, because the driver needs to sanitize them, + * and we don't want to modify (or be vulnerable to modifications of) + * the mmap'd buffer. + */ + for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) { + pispbe_wr(pispbe, PISP_BE_IO_ADDR_LOW(u), + lower_32_bits(job->hw_dma_addrs[u])); + pispbe_wr(pispbe, PISP_BE_IO_ADDR_HIGH(u), + upper_32_bits(job->hw_dma_addrs[u])); + } + pispbe_wr(pispbe, PISP_BE_GLOBAL_BAYER_ENABLE, + job->hw_enables.bayer_enables); + pispbe_wr(pispbe, PISP_BE_GLOBAL_RGB_ENABLE, + job->hw_enables.rgb_enables); + + /* Everything else is as supplied by the user. */ + begin = offsetof(struct pisp_be_config, global.bayer_order) / + sizeof(u32); + end = sizeof(struct pisp_be_config) / sizeof(u32); + for (unsigned int u = begin; u < end; u++) + pispbe_wr(pispbe, PISP_BE_CONFIG_BASE_REG + sizeof(u32) * u, + ((u32 *)job->config)[u]); + + /* Read back the addresses -- an error here could be fatal */ + for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) { + unsigned int offset = PISP_BE_IO_ADDR_LOW(u); + u64 along = pispbe_rd(pispbe, offset); + + along += ((u64)pispbe_rd(pispbe, offset + 4)) << 32; + if (along != (u64)(job->hw_dma_addrs[u])) { + dev_dbg(pispbe->dev, + "ISP BE config error: check if ISP RAMs enabled?\n"); + return; + } + } + + /* + * Write tile pointer to hardware. The IOMMU should prevent + * out-of-bounds offsets reaching non-ISP buffers. + */ + pispbe_wr(pispbe, PISP_BE_TILE_ADDR_LO_REG, lower_32_bits(job->tiles)); + pispbe_wr(pispbe, PISP_BE_TILE_ADDR_HI_REG, upper_32_bits(job->tiles)); + + /* Enqueue the job */ + pispbe_wr(pispbe, PISP_BE_CONTROL_REG, + PISP_BE_CONTROL_COPY_CONFIG | PISP_BE_CONTROL_QUEUE_JOB | + PISP_BE_CONTROL_NUM_TILES(job->config->num_tiles)); +} + +struct pispbe_buffer { + struct vb2_v4l2_buffer vb; + struct list_head ready_list; + unsigned int config_index; +}; + +static int pispbe_get_planes_addr(dma_addr_t addr[3], struct pispbe_buffer *buf, + struct pispbe_node *node) +{ + unsigned int num_planes = node->format.fmt.pix_mp.num_planes; + unsigned int plane_factor = 0; + unsigned int size; + unsigned int p; + + if (!buf || !node->pisp_format) + return 0; + + /* + * Determine the base plane size. This will not be the same + * as node->format.fmt.pix_mp.plane_fmt[0].sizeimage for a single + * plane buffer in an mplane format. + */ + size = node->format.fmt.pix_mp.plane_fmt[0].bytesperline * + node->format.fmt.pix_mp.height; + + for (p = 0; p < num_planes && p < PISPBE_MAX_PLANES; p++) { + addr[p] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, p); + plane_factor += node->pisp_format->plane_factor[p]; + } + + for (; p < PISPBE_MAX_PLANES && node->pisp_format->plane_factor[p]; p++) { + /* + * Calculate the address offset of this plane as needed + * by the hardware. This is specifically for non-mplane + * buffer formats, where there are 3 image planes, e.g. + * for the V4L2_PIX_FMT_YUV420 format. + */ + addr[p] = addr[0] + ((size * plane_factor) >> 3); + plane_factor += node->pisp_format->plane_factor[p]; + } + + return num_planes; +} + +static dma_addr_t pispbe_get_addr(struct pispbe_buffer *buf) +{ + if (buf) + return vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + + return 0; +} + +static void pispbe_xlate_addrs(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job, + struct pispbe_buffer *buf[PISPBE_NUM_NODES]) +{ + struct pispbe_hw_enables *hw_en = &job->hw_enables; + struct pisp_be_tiles_config *config = job->config; + dma_addr_t *addrs = job->hw_dma_addrs; + int ret; + + /* Take a copy of the "enable" bitmaps so we can modify them. */ + hw_en->bayer_enables = config->config.global.bayer_enables; + hw_en->rgb_enables = config->config.global.rgb_enables; + + /* + * Main input first. There are 3 address pointers, corresponding to up + * to 3 planes. + */ + ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE], + &pispbe->node[MAIN_INPUT_NODE]); + if (ret <= 0) { + /* + * This shouldn't happen; pispbe_schedule_internal should insist + * on an input. + */ + dev_warn(pispbe->dev, "ISP-BE missing input\n"); + hw_en->bayer_enables = 0; + hw_en->rgb_enables = 0; + return; + } + + /* + * Now TDN/Stitch inputs and outputs. These are single-plane and only + * used with Bayer input. Input enables must match the requirements + * of the processing stages, otherwise the hardware can lock up! + */ + if (hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) { + addrs[3] = pispbe_get_addr(buf[TDN_INPUT_NODE]); + if (addrs[3] == 0 || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN_INPUT) || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN) || + (config->config.tdn.reset & 1)) { + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_TDN_INPUT | + PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS); + if (!(config->config.tdn.reset & 1)) + hw_en->bayer_enables &= + ~PISP_BE_BAYER_ENABLE_TDN; + } + + addrs[4] = pispbe_get_addr(buf[STITCH_INPUT_NODE]); + if (addrs[4] == 0 || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_INPUT) || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH)) { + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_STITCH_INPUT | + PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS | + PISP_BE_BAYER_ENABLE_STITCH); + } + + addrs[5] = pispbe_get_addr(buf[TDN_OUTPUT_NODE]); + if (addrs[5] == 0) + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_TDN_COMPRESS | + PISP_BE_BAYER_ENABLE_TDN_OUTPUT); + + addrs[6] = pispbe_get_addr(buf[STITCH_OUTPUT_NODE]); + if (addrs[6] == 0) + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_STITCH_COMPRESS | + PISP_BE_BAYER_ENABLE_STITCH_OUTPUT); + } else { + /* No Bayer input? Disable entire Bayer pipe (else lockup) */ + hw_en->bayer_enables = 0; + } + + /* Main image output channels. */ + for (unsigned int i = 0; i < PISP_BACK_END_NUM_OUTPUTS; i++) { + ret = pispbe_get_planes_addr(addrs + 7 + 3 * i, + buf[OUTPUT0_NODE + i], + &pispbe->node[OUTPUT0_NODE + i]); + if (ret <= 0) + hw_en->rgb_enables &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 << i); + } +} + +/* + * Prepare a job description to be submitted to the HW. + * + * To schedule a job, we need all streaming nodes (apart from Output0, + * Output1, Tdn and Stitch) to have a buffer ready, which must + * include at least a config buffer and a main input image. + * + * For Output0, Output1, Tdn and Stitch, a buffer only needs to be + * available if the blocks are enabled in the config. + * + * Needs to be called with hw_lock held. + * + * Returns 0 if a job has been successfully prepared, < 0 otherwise. + */ +static int pispbe_prepare_job(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job) +{ + struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {}; + unsigned int config_index; + struct pispbe_node *node; + unsigned long flags; + + lockdep_assert_held(&pispbe->hw_lock); + + memset(job, 0, sizeof(struct pispbe_job_descriptor)); + + if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) & + pispbe->streaming_map) != + (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE))) + return -ENODEV; + + node = &pispbe->node[CONFIG_NODE]; + spin_lock_irqsave(&node->ready_lock, flags); + buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf[CONFIG_NODE]) { + list_del(&buf[CONFIG_NODE]->ready_list); + pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE]; + } + spin_unlock_irqrestore(&node->ready_lock, flags); + + /* Exit early if no config buffer has been queued. */ + if (!buf[CONFIG_NODE]) + return -ENODEV; + + config_index = buf[CONFIG_NODE]->vb.vb2_buf.index; + job->config = &pispbe->config[config_index]; + job->tiles = pispbe->config_dma_addr + + config_index * sizeof(struct pisp_be_tiles_config) + + offsetof(struct pisp_be_tiles_config, tiles); + + /* remember: srcimages, captures then metadata */ + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + unsigned int bayer_en = + job->config->config.global.bayer_enables; + unsigned int rgb_en = + job->config->config.global.rgb_enables; + bool ignore_buffers = false; + + /* Config node is handled outside the loop above. */ + if (i == CONFIG_NODE) + continue; + + buf[i] = NULL; + if (!(pispbe->streaming_map & BIT(i))) + continue; + + if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) && + i == OUTPUT0_NODE) || + (!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT1) && + i == OUTPUT1_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_INPUT) && + i == TDN_INPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) && + i == TDN_OUTPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_INPUT) && + i == STITCH_INPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) && + i == STITCH_OUTPUT_NODE)) { + /* + * Ignore Output0/Output1/Tdn/Stitch buffer check if the + * global enables aren't set for these blocks. If a + * buffer has been provided, we dequeue it back to the + * user with the other in-use buffers. + */ + ignore_buffers = true; + } + + node = &pispbe->node[i]; + + /* Pull a buffer from each V4L2 queue to form the queued job */ + spin_lock_irqsave(&node->ready_lock, flags); + buf[i] = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf[i]) { + list_del(&buf[i]->ready_list); + pispbe->queued_job.buf[i] = buf[i]; + } + spin_unlock_irqrestore(&node->ready_lock, flags); + + if (!buf[i] && !ignore_buffers) + goto err_return_buffers; + } + + pispbe->queued_job.valid = true; + + /* Convert buffers to DMA addresses for the hardware */ + pispbe_xlate_addrs(pispbe, job, buf); + + return 0; + +err_return_buffers: + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + struct pispbe_node *n = &pispbe->node[i]; + + if (!buf[i]) + continue; + + /* Return the buffer to the ready_list queue */ + spin_lock_irqsave(&n->ready_lock, flags); + list_add(&buf[i]->ready_list, &n->ready_queue); + spin_unlock_irqrestore(&n->ready_lock, flags); + } + + memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); + + return -ENODEV; +} + +static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy) +{ + struct pispbe_job_descriptor job; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pispbe->hw_lock, flags); + + if (clear_hw_busy) + pispbe->hw_busy = false; + + if (pispbe->hw_busy) + goto unlock_and_return; + + ret = pispbe_prepare_job(pispbe, &job); + if (ret) + goto unlock_and_return; + + /* + * We can kick the job off without the hw_lock, as this can + * never run again until hw_busy is cleared, which will happen + * only when the following job has been queued and an interrupt + * is rised. + */ + pispbe->hw_busy = true; + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + if (job.config->num_tiles <= 0 || + job.config->num_tiles > PISP_BACK_END_NUM_TILES || + !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) & + PISP_BE_BAYER_ENABLE_INPUT)) { + /* + * Bad job. We can't let it proceed as it could lock up + * the hardware, or worse! + * + * For now, just force num_tiles to 0, which causes the + * H/W to do something bizarre but survivable. It + * increments (started,done) counters by more than 1, + * but we seem to survive... + */ + dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n", + job.config->num_tiles); + job.config->num_tiles = 0; + } + + pispbe_queue_job(pispbe, &job); + + return; + +unlock_and_return: + /* No job has been queued, just release the lock and return. */ + spin_unlock_irqrestore(&pispbe->hw_lock, flags); +} + +static void pispbe_isr_jobdone(struct pispbe_dev *pispbe, + struct pispbe_job *job) +{ + struct pispbe_buffer **buf = job->buf; + u64 ts = ktime_get_ns(); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + if (buf[i]) { + buf[i]->vb.vb2_buf.timestamp = ts; + buf[i]->vb.sequence = pispbe->sequence; + vb2_buffer_done(&buf[i]->vb.vb2_buf, + VB2_BUF_STATE_DONE); + } + } + + pispbe->sequence++; +} + +static irqreturn_t pispbe_isr(int irq, void *dev) +{ + struct pispbe_dev *pispbe = (struct pispbe_dev *)dev; + bool can_queue_another = false; + u8 started, done; + u32 u; + + u = pispbe_rd(pispbe, PISP_BE_INTERRUPT_STATUS_REG); + if (u == 0) + return IRQ_NONE; + + pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, u); + u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG); + done = (uint8_t)u; + started = (uint8_t)(u >> 8); + + /* + * Be aware that done can go up by 2 and started by 1 when: a job that + * we previously saw "start" now finishes, and we then queued a new job + * which we see both start and finish "simultaneously". + */ + if (pispbe->running_job.valid && pispbe->done != done) { + pispbe_isr_jobdone(pispbe, &pispbe->running_job); + memset(&pispbe->running_job, 0, sizeof(pispbe->running_job)); + pispbe->done++; + } + + if (pispbe->started != started) { + pispbe->started++; + can_queue_another = 1; + + if (pispbe->done != done && pispbe->queued_job.valid) { + pispbe_isr_jobdone(pispbe, &pispbe->queued_job); + pispbe->done++; + } else { + pispbe->running_job = pispbe->queued_job; + } + + memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); + } + + if (pispbe->done != done || pispbe->started != started) { + dev_dbg(pispbe->dev, + "Job counters not matching: done = %u, expected %u - started = %u, expected %u\n", + pispbe->done, done, pispbe->started, started); + pispbe->started = started; + pispbe->done = done; + } + + /* check if there's more to do before going to sleep */ + pispbe_schedule(pispbe, can_queue_another); + + return IRQ_HANDLED; +} + +static int pisp_be_validate_config(struct pispbe_dev *pispbe, + struct pisp_be_tiles_config *config) +{ + u32 bayer_enables = config->config.global.bayer_enables; + u32 rgb_enables = config->config.global.rgb_enables; + struct device *dev = pispbe->dev; + struct v4l2_format *fmt; + unsigned int bpl, size; + + if (!(bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) == + !(rgb_enables & PISP_BE_RGB_ENABLE_INPUT)) { + dev_dbg(dev, "%s: Not one input enabled\n", __func__); + return -EIO; + } + + /* Ensure output config strides and buffer sizes match the V4L2 formats. */ + fmt = &pispbe->node[TDN_OUTPUT_NODE].format; + if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) { + bpl = config->config.tdn_output_format.stride; + size = bpl * config->config.tdn_output_format.height; + + if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on tdn_output\n", + __func__); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on tdn_output\n", + __func__); + return -EINVAL; + } + } + + fmt = &pispbe->node[STITCH_OUTPUT_NODE].format; + if (bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) { + bpl = config->config.stitch_output_format.stride; + size = bpl * config->config.stitch_output_format.height; + + if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on stitch_output\n", + __func__); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on stitch_output\n", + __func__); + return -EINVAL; + } + } + + for (unsigned int j = 0; j < PISP_BACK_END_NUM_OUTPUTS; j++) { + if (!(rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT(j))) + continue; + + if (config->config.output_format[j].image.format & + PISP_IMAGE_FORMAT_WALLPAPER_ROLL) + continue; /* TODO: Size checks for wallpaper formats */ + + fmt = &pispbe->node[OUTPUT0_NODE + j].format; + for (unsigned int i = 0; i < fmt->fmt.pix_mp.num_planes; i++) { + bpl = !i ? config->config.output_format[j].image.stride + : config->config.output_format[j].image.stride2; + size = bpl * config->config.output_format[j].image.height; + + if (config->config.output_format[j].image.format & + PISP_IMAGE_FORMAT_SAMPLING_420) + size >>= 1; + + if (fmt->fmt.pix_mp.plane_fmt[i].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on output %d\n", + __func__, j); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[i].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on output\n", + __func__); + return -EINVAL; + } + } + } + + return 0; +} + +static int pispbe_node_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + unsigned int num_planes = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.num_planes : 1; + + if (*nplanes) { + if (*nplanes != num_planes) + return -EINVAL; + + for (unsigned int i = 0; i < *nplanes; i++) { + unsigned int size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + + if (sizes[i] < size) + return -EINVAL; + } + + return 0; + } + + *nplanes = num_planes; + for (unsigned int i = 0; i < *nplanes; i++) { + unsigned int size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + sizes[i] = size; + } + + dev_dbg(pispbe->dev, + "Image (or metadata) size %u, nbuffers %u for node %s\n", + sizes[0], *nbuffers, NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_buffer_prepare(struct vb2_buffer *vb) +{ + struct pispbe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct pispbe_dev *pispbe = node->pispbe; + unsigned int num_planes = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.num_planes : 1; + + for (unsigned int i = 0; i < num_planes; i++) { + unsigned long size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + + if (vb2_plane_size(vb, i) < size) { + dev_dbg(pispbe->dev, + "data will not fit into plane %d (%lu < %lu)\n", + i, vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + if (node->id == CONFIG_NODE) { + void *dst = &node->pispbe->config[vb->index]; + void *src = vb2_plane_vaddr(vb, 0); + + memcpy(dst, src, sizeof(struct pisp_be_tiles_config)); + + return pisp_be_validate_config(pispbe, dst); + } + + return 0; +} + +static void pispbe_node_buffer_queue(struct vb2_buffer *buf) +{ + struct vb2_v4l2_buffer *vbuf = + container_of(buf, struct vb2_v4l2_buffer, vb2_buf); + struct pispbe_buffer *buffer = + container_of(vbuf, struct pispbe_buffer, vb); + struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue); + struct pispbe_dev *pispbe = node->pispbe; + unsigned long flags; + + dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); + spin_lock_irqsave(&node->ready_lock, flags); + list_add_tail(&buffer->ready_list, &node->ready_queue); + spin_unlock_irqrestore(&node->ready_lock, flags); + + /* + * Every time we add a buffer, check if there's now some work for the hw + * to do. + */ + pispbe_schedule(pispbe, false); +} + +static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_buffer *buf, *tmp; + unsigned long flags; + int ret; + + ret = pm_runtime_resume_and_get(pispbe->dev); + if (ret < 0) + goto err_return_buffers; + + spin_lock_irqsave(&pispbe->hw_lock, flags); + node->pispbe->streaming_map |= BIT(node->id); + node->pispbe->sequence = 0; + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n", + __func__, NODE_NAME(node), count); + dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n", + node->pispbe->streaming_map); + + /* Maybe we're ready to run. */ + pispbe_schedule(pispbe, false); + + return 0; + +err_return_buffers: + spin_lock_irqsave(&pispbe->hw_lock, flags); + list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) { + list_del(&buf->ready_list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + return ret; +} + +static void pispbe_node_stop_streaming(struct vb2_queue *q) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_buffer *buf; + unsigned long flags; + + /* + * Now this is a bit awkward. In a simple M2M device we could just wait + * for all queued jobs to complete, but here there's a risk that a + * partial set of buffers was queued and cannot be run. For now, just + * cancel all buffers stuck in the "ready queue", then wait for any + * running job. + * + * This may return buffers out of order. + */ + dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); + spin_lock_irqsave(&pispbe->hw_lock, flags); + do { + unsigned long flags1; + + spin_lock_irqsave(&node->ready_lock, flags1); + buf = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf) { + list_del(&buf->ready_list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&node->ready_lock, flags1); + } while (buf); + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + vb2_wait_for_all_buffers(&node->queue); + + spin_lock_irqsave(&pispbe->hw_lock, flags); + pispbe->streaming_map &= ~BIT(node->id); + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + pm_runtime_mark_last_busy(pispbe->dev); + pm_runtime_put_autosuspend(pispbe->dev); + + dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n", + pispbe->streaming_map); +} + +static const struct vb2_ops pispbe_node_queue_ops = { + .queue_setup = pispbe_node_queue_setup, + .buf_prepare = pispbe_node_buffer_prepare, + .buf_queue = pispbe_node_buffer_queue, + .start_streaming = pispbe_node_start_streaming, + .stop_streaming = pispbe_node_stop_streaming, +}; + +static const struct v4l2_file_operations pispbe_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap +}; + +static int pispbe_node_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + strscpy(cap->driver, PISPBE_NAME, sizeof(cap->driver)); + strscpy(cap->card, PISPBE_NAME, sizeof(cap->card)); + + dev_dbg(pispbe->dev, "Caps for node %s: %x and %x (dev %x)\n", + NODE_NAME(node), cap->capabilities, cap->device_caps, + node->vfd.device_caps); + + return 0; +} + +static int pispbe_node_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get capture format for node %s\n", + NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get output format for node %s\n", + NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for meta output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get output format for meta node %s\n", + NODE_NAME(node)); + + return 0; +} + +static const struct pisp_be_format *pispbe_find_fmt(unsigned int fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].fourcc == fourcc) + return &supported_formats[i]; + } + + return NULL; +} + +static void pispbe_set_plane_params(struct v4l2_format *f, + const struct pisp_be_format *fmt) +{ + unsigned int nplanes = f->fmt.pix_mp.num_planes; + unsigned int total_plane_factor = 0; + + for (unsigned int i = 0; i < PISPBE_MAX_PLANES; i++) + total_plane_factor += fmt->plane_factor[i]; + + for (unsigned int i = 0; i < nplanes; i++) { + struct v4l2_plane_pix_format *p = &f->fmt.pix_mp.plane_fmt[i]; + unsigned int bpl, plane_size; + + bpl = (f->fmt.pix_mp.width * fmt->bit_depth) >> 3; + bpl = ALIGN(max(p->bytesperline, bpl), fmt->align); + + plane_size = bpl * f->fmt.pix_mp.height * + (nplanes > 1 ? fmt->plane_factor[i] : total_plane_factor); + /* + * The shift is to divide out the plane_factor fixed point + * scaling of 8. + */ + plane_size = max(p->sizeimage, plane_size >> 3); + + p->bytesperline = bpl; + p->sizeimage = plane_size; + } +} + +static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node) +{ + struct pispbe_dev *pispbe = node->pispbe; + u32 pixfmt = f->fmt.pix_mp.pixelformat; + const struct pisp_be_format *fmt; + bool is_rgb; + + dev_dbg(pispbe->dev, + "%s: [%s] req %ux%u %p4cc, planes %d\n", + __func__, NODE_NAME(node), f->fmt.pix_mp.width, + f->fmt.pix_mp.height, &pixfmt, + f->fmt.pix_mp.num_planes); + + fmt = pispbe_find_fmt(pixfmt); + if (!fmt) { + dev_dbg(pispbe->dev, + "%s: [%s] Format not found, defaulting to YUV420\n", + __func__, NODE_NAME(node)); + fmt = pispbe_find_fmt(V4L2_PIX_FMT_YUV420); + } + + f->fmt.pix_mp.pixelformat = fmt->fourcc; + f->fmt.pix_mp.num_planes = fmt->num_planes; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u), + PISP_BACK_END_MIN_TILE_WIDTH); + f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u), + PISP_BACK_END_MIN_TILE_HEIGHT); + + /* + * Fill in the actual colour space when the requested one was + * not supported. This also catches the case when the "default" + * colour space was requested (as that's never in the mask). + */ + if (!(V4L2_COLORSPACE_MASK(f->fmt.pix_mp.colorspace) & + fmt->colorspace_mask)) + f->fmt.pix_mp.colorspace = fmt->colorspace_default; + + /* In all cases, we only support the defaults for these: */ + f->fmt.pix_mp.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix_mp.colorspace); + f->fmt.pix_mp.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix_mp.colorspace); + + is_rgb = f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_SRGB; + f->fmt.pix_mp.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix_mp.colorspace, + f->fmt.pix_mp.ycbcr_enc); + + /* Set plane size and bytes/line for each plane. */ + pispbe_set_plane_params(f, fmt); + + for (unsigned int i = 0; i < f->fmt.pix_mp.num_planes; i++) { + dev_dbg(pispbe->dev, + "%s: [%s] calc plane %d, %ux%u, depth %u, bpl %u size %u\n", + __func__, NODE_NAME(node), i, f->fmt.pix_mp.width, + f->fmt.pix_mp.height, fmt->bit_depth, + f->fmt.pix_mp.plane_fmt[i].bytesperline, + f->fmt.pix_mp.plane_fmt[i].sizeimage); + } +} + +static int pispbe_node_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + pispbe_try_format(f, node); + + return 0; +} + +static int pispbe_node_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_OUTPUT(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + pispbe_try_format(f, node); + + return 0; +} + +static int pispbe_node_try_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for meta output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config); + + return 0; +} + +static int pispbe_node_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat); + + dev_dbg(pispbe->dev, "Set capture format for node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int pispbe_node_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_vid_out(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat); + + dev_dbg(pispbe->dev, "Set output format for node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int pispbe_node_s_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_meta_out(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = &meta_out_supported_formats[0]; + + dev_dbg(pispbe->dev, "Set output format for meta node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.meta.dataformat); + + return 0; +} + +static int pispbe_node_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct pispbe_node *node = video_drvdata(file); + + if (f->type != node->queue.type) + return -EINVAL; + + if (NODE_IS_META(node)) { + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_RPI_BE_CFG; + f->flags = 0; + return 0; + } + + if (f->index >= ARRAY_SIZE(supported_formats)) + return -EINVAL; + + f->pixelformat = supported_formats[f->index].fourcc; + f->flags = 0; + + return 0; +} + +static int pispbe_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (NODE_IS_META(node) || fsize->index) + return -EINVAL; + + if (!pispbe_find_fmt(fsize->pixel_format)) { + dev_dbg(pispbe->dev, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = 32; + fsize->stepwise.max_width = 65535; + fsize->stepwise.step_width = 2; + + fsize->stepwise.min_height = 32; + fsize->stepwise.max_height = 65535; + fsize->stepwise.step_height = 2; + + return 0; +} + +static const struct v4l2_ioctl_ops pispbe_node_ioctl_ops = { + .vidioc_querycap = pispbe_node_querycap, + .vidioc_g_fmt_vid_cap_mplane = pispbe_node_g_fmt_vid_cap, + .vidioc_g_fmt_vid_out_mplane = pispbe_node_g_fmt_vid_out, + .vidioc_g_fmt_meta_out = pispbe_node_g_fmt_meta_out, + .vidioc_try_fmt_vid_cap_mplane = pispbe_node_try_fmt_vid_cap, + .vidioc_try_fmt_vid_out_mplane = pispbe_node_try_fmt_vid_out, + .vidioc_try_fmt_meta_out = pispbe_node_try_fmt_meta_out, + .vidioc_s_fmt_vid_cap_mplane = pispbe_node_s_fmt_vid_cap, + .vidioc_s_fmt_vid_out_mplane = pispbe_node_s_fmt_vid_out, + .vidioc_s_fmt_meta_out = pispbe_node_s_fmt_meta_out, + .vidioc_enum_fmt_vid_cap = pispbe_node_enum_fmt, + .vidioc_enum_fmt_vid_out = pispbe_node_enum_fmt, + .vidioc_enum_fmt_meta_out = pispbe_node_enum_fmt, + .vidioc_enum_framesizes = pispbe_enum_framesizes, + .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_reqbufs = vb2_ioctl_reqbufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device pispbe_videodev = { + .name = PISPBE_NAME, + .vfl_dir = VFL_DIR_M2M, /* gets overwritten */ + .fops = &pispbe_fops, + .ioctl_ops = &pispbe_node_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +static void pispbe_node_def_fmt(struct pispbe_node *node) +{ + if (NODE_IS_META(node) && NODE_IS_OUTPUT(node)) { + /* Config node */ + struct v4l2_format *f = &node->format; + + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config); + f->type = node->buf_type; + } else { + struct v4l2_format f = { + .fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420, + .fmt.pix_mp.width = 1920, + .fmt.pix_mp.height = 1080, + .type = node->buf_type, + }; + pispbe_try_format(&f, node); + node->format = f; + } + + node->pisp_format = pispbe_find_fmt(node->format.fmt.pix_mp.pixelformat); +} + +/* + * Initialise a struct pispbe_node and register it as /dev/video<N> + * to represent one of the PiSP Back End's input or output streams. + */ +static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id) +{ + bool output = NODE_DESC_IS_OUTPUT(&node_desc[id]); + struct pispbe_node *node = &pispbe->node[id]; + struct media_entity *entity = &node->vfd.entity; + struct video_device *vdev = &node->vfd; + struct vb2_queue *q = &node->queue; + int ret; + + node->id = id; + node->pispbe = pispbe; + node->buf_type = node_desc[id].buf_type; + + mutex_init(&node->node_lock); + mutex_init(&node->queue_lock); + INIT_LIST_HEAD(&node->ready_queue); + spin_lock_init(&node->ready_lock); + + node->format.type = node->buf_type; + pispbe_node_def_fmt(node); + + q->type = node->buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->mem_ops = &vb2_dma_contig_memops; + q->drv_priv = node; + q->ops = &pispbe_node_queue_ops; + q->buf_struct_size = sizeof(struct pispbe_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->dev = pispbe->dev; + /* get V4L2 to handle node->queue locking */ + q->lock = &node->queue_lock; + + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(pispbe->dev, "vb2_queue_init failed\n"); + goto err_mutex_destroy; + } + + *vdev = pispbe_videodev; /* default initialization */ + strscpy(vdev->name, node_desc[id].ent_name, sizeof(vdev->name)); + vdev->v4l2_dev = &pispbe->v4l2_dev; + vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX; + /* get V4L2 to serialise our ioctls */ + vdev->lock = &node->node_lock; + vdev->queue = &node->queue; + vdev->device_caps = V4L2_CAP_STREAMING | node_desc[id].caps; + + node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(entity, 1, &node->pad); + if (ret) { + dev_err(pispbe->dev, + "Failed to register media pads for %s device node\n", + NODE_NAME(node)); + goto err_unregister_queue; + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(pispbe->dev, + "Failed to register video %s device node\n", + NODE_NAME(node)); + goto err_unregister_queue; + } + video_set_drvdata(vdev, node); + + if (output) + ret = media_create_pad_link(entity, 0, &pispbe->sd.entity, + id, MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + else + ret = media_create_pad_link(&pispbe->sd.entity, id, entity, + 0, MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_unregister_video_dev; + + dev_dbg(pispbe->dev, "%s device node registered as /dev/video%d\n", + NODE_NAME(node), node->vfd.num); + + return 0; + +err_unregister_video_dev: + video_unregister_device(&node->vfd); +err_unregister_queue: + vb2_queue_release(&node->queue); +err_mutex_destroy: + mutex_destroy(&node->node_lock); + mutex_destroy(&node->queue_lock); + return ret; +} + +static const struct v4l2_subdev_pad_ops pispbe_pad_ops = { + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops pispbe_sd_ops = { + .pad = &pispbe_pad_ops, +}; + +static int pispbe_init_subdev(struct pispbe_dev *pispbe) +{ + struct v4l2_subdev *sd = &pispbe->sd; + int ret; + + v4l2_subdev_init(sd, &pispbe_sd_ops); + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->owner = THIS_MODULE; + sd->dev = pispbe->dev; + strscpy(sd->name, PISPBE_NAME, sizeof(sd->name)); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) + pispbe->pad[i].flags = + NODE_DESC_IS_OUTPUT(&node_desc[i]) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, PISPBE_NUM_NODES, + pispbe->pad); + if (ret) + goto error; + + ret = v4l2_device_register_subdev(&pispbe->v4l2_dev, sd); + if (ret) + goto error; + + return 0; + +error: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int pispbe_init_devices(struct pispbe_dev *pispbe) +{ + struct v4l2_device *v4l2_dev; + struct media_device *mdev; + unsigned int num_regist; + int ret; + + /* Register v4l2_device and media_device */ + mdev = &pispbe->mdev; + mdev->hw_revision = pispbe->hw_version; + mdev->dev = pispbe->dev; + strscpy(mdev->model, PISPBE_NAME, sizeof(mdev->model)); + media_device_init(mdev); + + v4l2_dev = &pispbe->v4l2_dev; + v4l2_dev->mdev = &pispbe->mdev; + strscpy(v4l2_dev->name, PISPBE_NAME, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(pispbe->dev, v4l2_dev); + if (ret) + goto err_media_dev_cleanup; + + /* Register the PISPBE subdevice. */ + ret = pispbe_init_subdev(pispbe); + if (ret) + goto err_unregister_v4l2; + + /* Create device video nodes */ + for (num_regist = 0; num_regist < PISPBE_NUM_NODES; num_regist++) { + ret = pispbe_init_node(pispbe, num_regist); + if (ret) + goto err_unregister_nodes; + } + + ret = media_device_register(mdev); + if (ret) + goto err_unregister_nodes; + + pispbe->config = + dma_alloc_coherent(pispbe->dev, + sizeof(struct pisp_be_tiles_config) * + PISP_BE_NUM_CONFIG_BUFFERS, + &pispbe->config_dma_addr, GFP_KERNEL); + if (!pispbe->config) { + dev_err(pispbe->dev, "Unable to allocate cached config buffers.\n"); + ret = -ENOMEM; + goto err_unregister_mdev; + } + + return 0; + +err_unregister_mdev: + media_device_unregister(mdev); +err_unregister_nodes: + while (num_regist-- > 0) { + video_unregister_device(&pispbe->node[num_regist].vfd); + vb2_queue_release(&pispbe->node[num_regist].queue); + } + v4l2_device_unregister_subdev(&pispbe->sd); + media_entity_cleanup(&pispbe->sd.entity); +err_unregister_v4l2: + v4l2_device_unregister(v4l2_dev); +err_media_dev_cleanup: + media_device_cleanup(mdev); + return ret; +} + +static void pispbe_destroy_devices(struct pispbe_dev *pispbe) +{ + if (pispbe->config) { + dma_free_coherent(pispbe->dev, + sizeof(struct pisp_be_tiles_config) * + PISP_BE_NUM_CONFIG_BUFFERS, + pispbe->config, + pispbe->config_dma_addr); + } + + dev_dbg(pispbe->dev, "Unregister from media controller\n"); + + v4l2_device_unregister_subdev(&pispbe->sd); + media_entity_cleanup(&pispbe->sd.entity); + media_device_unregister(&pispbe->mdev); + + for (int i = PISPBE_NUM_NODES - 1; i >= 0; i--) { + video_unregister_device(&pispbe->node[i].vfd); + vb2_queue_release(&pispbe->node[i].queue); + mutex_destroy(&pispbe->node[i].node_lock); + mutex_destroy(&pispbe->node[i].queue_lock); + } + + media_device_cleanup(&pispbe->mdev); + v4l2_device_unregister(&pispbe->v4l2_dev); +} + +static int pispbe_runtime_suspend(struct device *dev) +{ + struct pispbe_dev *pispbe = dev_get_drvdata(dev); + + clk_disable_unprepare(pispbe->clk); + + return 0; +} + +static int pispbe_runtime_resume(struct device *dev) +{ + struct pispbe_dev *pispbe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pispbe->clk); + if (ret) { + dev_err(dev, "Unable to enable clock\n"); + return ret; + } + + dev_dbg(dev, "%s: Enabled clock, rate=%lu\n", + __func__, clk_get_rate(pispbe->clk)); + + return 0; +} + +static int pispbe_hw_init(struct pispbe_dev *pispbe) +{ + u32 u; + + /* Check the HW is present and has a known version */ + u = pispbe_rd(pispbe, PISP_BE_VERSION_REG); + dev_dbg(pispbe->dev, "pispbe_probe: HW version: 0x%08x", u); + pispbe->hw_version = u; + if ((u & ~PISP_BE_VERSION_MINOR_BITS) != PISP_BE_VERSION_2712) + return -ENODEV; + + /* Clear leftover interrupts */ + pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, 0xFFFFFFFFu); + u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG); + dev_dbg(pispbe->dev, "pispbe_probe: BatchStatus: 0x%08x", u); + + pispbe->done = (uint8_t)u; + pispbe->started = (uint8_t)(u >> 8); + u = pispbe_rd(pispbe, PISP_BE_STATUS_REG); + dev_dbg(pispbe->dev, "pispbe_probe: Status: 0x%08x", u); + + if (u != 0 || pispbe->done != pispbe->started) { + dev_err(pispbe->dev, "pispbe_probe: HW is stuck or busy\n"); + return -EBUSY; + } + + /* + * AXI QOS=0, CACHE=4'b0010, PROT=3'b011 + * Also set "chicken bits" 22:20 which enable sub-64-byte bursts + * and AXI AWID/BID variability (on versions which support this). + */ + pispbe_wr(pispbe, PISP_BE_AXI_REG, 0x32703200u); + + /* Enable both interrupt flags */ + pispbe_wr(pispbe, PISP_BE_INTERRUPT_EN_REG, 0x00000003u); + + return 0; +} + +/* Probe the ISP-BE hardware block, as a single platform device. */ +static int pispbe_probe(struct platform_device *pdev) +{ + struct pispbe_dev *pispbe; + int ret; + + pispbe = devm_kzalloc(&pdev->dev, sizeof(*pispbe), GFP_KERNEL); + if (!pispbe) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, pispbe); + pispbe->dev = &pdev->dev; + platform_set_drvdata(pdev, pispbe); + + pispbe->be_reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pispbe->be_reg_base)) { + dev_err(&pdev->dev, "Failed to get ISP-BE registers address\n"); + return PTR_ERR(pispbe->be_reg_base); + } + + pispbe->irq = platform_get_irq(pdev, 0); + if (pispbe->irq <= 0) + return -EINVAL; + + ret = devm_request_irq(&pdev->dev, pispbe->irq, pispbe_isr, 0, + PISPBE_NAME, pispbe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + return ret; + } + + ret = dma_set_mask_and_coherent(pispbe->dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + pispbe->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pispbe->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pispbe->clk), + "Failed to get clock"); + + /* Hardware initialisation */ + pm_runtime_set_autosuspend_delay(pispbe->dev, 200); + pm_runtime_use_autosuspend(pispbe->dev); + pm_runtime_enable(pispbe->dev); + + ret = pispbe_runtime_resume(pispbe->dev); + if (ret) + goto pm_runtime_disable_err; + + pispbe->hw_busy = false; + spin_lock_init(&pispbe->hw_lock); + ret = pispbe_hw_init(pispbe); + if (ret) + goto pm_runtime_suspend_err; + + ret = pispbe_init_devices(pispbe); + if (ret) + goto disable_devs_err; + + pm_runtime_mark_last_busy(pispbe->dev); + pm_runtime_put_autosuspend(pispbe->dev); + + return 0; + +disable_devs_err: + pispbe_destroy_devices(pispbe); +pm_runtime_suspend_err: + pispbe_runtime_suspend(pispbe->dev); +pm_runtime_disable_err: + pm_runtime_dont_use_autosuspend(pispbe->dev); + pm_runtime_disable(pispbe->dev); + + return ret; +} + +static void pispbe_remove(struct platform_device *pdev) +{ + struct pispbe_dev *pispbe = platform_get_drvdata(pdev); + + pispbe_destroy_devices(pispbe); + + pispbe_runtime_suspend(pispbe->dev); + pm_runtime_dont_use_autosuspend(pispbe->dev); + pm_runtime_disable(pispbe->dev); +} + +static const struct dev_pm_ops pispbe_pm_ops = { + SET_RUNTIME_PM_OPS(pispbe_runtime_suspend, pispbe_runtime_resume, NULL) +}; + +static const struct of_device_id pispbe_of_match[] = { + { + .compatible = "raspberrypi,pispbe", + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, pispbe_of_match); + +static struct platform_driver pispbe_pdrv = { + .probe = pispbe_probe, + .remove = pispbe_remove, + .driver = { + .name = PISPBE_NAME, + .of_match_table = pispbe_of_match, + .pm = &pispbe_pm_ops, + }, +}; + +module_platform_driver(pispbe_pdrv); + +MODULE_DESCRIPTION("PiSP Back End driver"); +MODULE_AUTHOR("David Plowman <david.plowman@raspberrypi.com>"); +MODULE_AUTHOR("Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h new file mode 100644 index 000000000000..b5cb7b8c7531 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h @@ -0,0 +1,519 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PiSP Back End driver image format definitions. + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd + */ + +#ifndef _PISP_BE_FORMATS_ +#define _PISP_BE_FORMATS_ + +#include <linux/bits.h> +#include <linux/videodev2.h> + +#define PISPBE_MAX_PLANES 3 +#define P3(x) ((x) * 8) + +struct pisp_be_format { + unsigned int fourcc; + unsigned int align; + unsigned int bit_depth; + /* 0P3 factor for plane sizing */ + unsigned int plane_factor[PISPBE_MAX_PLANES]; + unsigned int num_planes; + unsigned int colorspace_mask; + enum v4l2_colorspace colorspace_default; +}; + +#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace) + +#define V4L2_COLORSPACE_MASK_JPEG \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG) +#define V4L2_COLORSPACE_MASK_SMPTE170M \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M) +#define V4L2_COLORSPACE_MASK_REC709 \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709) +#define V4L2_COLORSPACE_MASK_SRGB \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB) +#define V4L2_COLORSPACE_MASK_RAW \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW) + +/* + * All three colour spaces SRGB, SMPTE170M and REC709 are fundamentally sRGB + * underneath (as near as makes no difference to us), just with different YCbCr + * encodings. Therefore the ISP can generate sRGB on its main output and any of + * the others on its low resolution output. Applications should, when using both + * outputs, program the colour spaces on them to be the same, matching whatever + * is requested for the low resolution output, even if the main output is + * producing an RGB format. In turn this requires us to allow all these colour + * spaces for every YUV/RGB output format. + */ +#define V4L2_COLORSPACE_MASK_ALL_SRGB (V4L2_COLORSPACE_MASK_JPEG | \ + V4L2_COLORSPACE_MASK_SRGB | \ + V4L2_COLORSPACE_MASK_SMPTE170M | \ + V4L2_COLORSPACE_MASK_REC709) + +static const struct pisp_be_format supported_formats[] = { + /* Single plane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420, + /* 128 alignment to ensure U/V planes are 64 byte aligned. */ + .align = 128, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + /* 128 alignment to ensure U/V planes are 64 byte aligned. */ + .align = 128, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + /* Multiplane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 2, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 2, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5), P3(0.5) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU422M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5), P3(0.5) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUV444M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(1), P3(1) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU444M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(1), P3(1) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + /* RGB formats */ + { + .fourcc = V4L2_PIX_FMT_RGB24, + .align = 32, + .bit_depth = 24, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .align = 32, + .bit_depth = 24, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .align = 64, + .bit_depth = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_RGBX32, + .align = 64, + .bit_depth = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_RGB48, + .align = 64, + .bit_depth = 48, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_BGR48, + .align = 64, + .bit_depth = 48, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + /* Bayer formats - 8-bit */ + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Bayer formats - 16-bit */ + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* Bayer formats unpacked to 16bpp */ + /* 10 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* 12 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* 14 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Bayer formats - 16-bit PiSP Compressed */ + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Greyscale Formats */ + { + .fourcc = V4L2_PIX_FMT_GREY, + .bit_depth = 8, + .align = 32, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, +}; + +static const struct pisp_be_format meta_out_supported_formats[] = { + /* Configuration buffer format. */ + { + .fourcc = V4L2_META_FMT_RPI_BE_CFG, + }, +}; + +#endif /* _PISP_BE_FORMATS_ */ diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig new file mode 100644 index 000000000000..327b61f1134b --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig @@ -0,0 +1,15 @@ +# RP1 V4L2 camera support + +config VIDEO_RP1_CFE + tristate "Raspberry Pi RP1 Camera Front End (CFE) video capture driver" + depends on VIDEO_DEV + depends on PM + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Say Y here to enable support for the Raspberry Pi RP1 Camera Front End. + + To compile this driver as a module, choose M here. The module will be + called rp1-cfe. diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/Makefile b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile new file mode 100644 index 000000000000..3f0d0fc8570e --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for RP1 Camera Front End driver +# +rp1-cfe-objs := cfe.o csi2.o pisp-fe.o dphy.o +obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h new file mode 100644 index 000000000000..7aecf7f83733 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RP1 Camera Front End formats definition + * + * Copyright (C) 2021-2024 - Raspberry Pi Ltd. + */ +#ifndef _CFE_FMTS_H_ +#define _CFE_FMTS_H_ + +#include "cfe.h" +#include <media/mipi-csi2.h> + +static const struct cfe_fmt formats[] = { + /* YUV Formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + /* RGB Formats */ + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, + { .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB555, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB555, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .depth = 32, + .csi_dt = 0x0, + }, + + /* Bayer Formats */ + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR14P, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG14P, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG14P, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB14P, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + /* PiSP Compressed Mode 1 */ + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG, + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG, + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + /* Greyscale format */ + { + .fourcc = V4L2_PIX_FMT_GREY, + .code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y10P, + .code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y12P, + .code = MEDIA_BUS_FMT_Y12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y14P, + .code = MEDIA_BUS_FMT_Y14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .code = MEDIA_BUS_FMT_Y16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO, + .code = MEDIA_BUS_FMT_Y16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + + /* Embedded data formats */ + { + .fourcc = V4L2_META_FMT_GENERIC_8, + .code = MEDIA_BUS_FMT_META_8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_10, + .code = MEDIA_BUS_FMT_META_10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_12, + .code = MEDIA_BUS_FMT_META_12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + + /* Frontend formats */ + { + .fourcc = V4L2_META_FMT_RPI_FE_CFG, + .code = MEDIA_BUS_FMT_FIXED, + .flags = CFE_FORMAT_FLAG_META_OUT, + }, + { + .fourcc = V4L2_META_FMT_RPI_FE_STATS, + .code = MEDIA_BUS_FMT_FIXED, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, +}; + +#endif /* _CFE_FMTS_H_ */ diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h new file mode 100644 index 000000000000..1a36259f51b7 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * Copyright (c) 2024 Ideas on Board Oy + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cfe + +#if !defined(_CFE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CFE_TRACE_H + +#include <linux/tracepoint.h> +#include <media/videobuf2-v4l2.h> + +TRACE_EVENT(cfe_return_buffer, + TP_PROTO(u32 node_id, u32 buf_idx, u32 queue_id), + TP_ARGS(node_id, buf_idx, queue_id), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(u32, queue_id) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf_idx; + __entry->queue_id = queue_id; + ), + TP_printk("node=%u buf=%u, queue=%u", __entry->node_id, + __entry->buf_idx, __entry->queue_id) +); + +DECLARE_EVENT_CLASS(cfe_buffer_template, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->index; + ), + TP_printk("node=%u buf=%u", __entry->node_id, __entry->buf_idx) +); + +DEFINE_EVENT(cfe_buffer_template, cfe_buffer_prepare, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +TRACE_EVENT(cfe_buffer_queue, + TP_PROTO(u32 node_id, struct vb2_buffer *buf, bool schedule_now), + TP_ARGS(node_id, buf, schedule_now), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(bool, schedule_now) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->index; + __entry->schedule_now = schedule_now; + ), + TP_printk("node=%u buf=%u%s", __entry->node_id, __entry->buf_idx, + __entry->schedule_now ? " schedule immediately" : "") +); + +DEFINE_EVENT(cfe_buffer_template, cfe_csi2_schedule, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +DEFINE_EVENT(cfe_buffer_template, cfe_fe_schedule, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +TRACE_EVENT(cfe_buffer_complete, + TP_PROTO(u32 node_id, struct vb2_v4l2_buffer *buf), + TP_ARGS(node_id, buf), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(u32, seq) + __field(u64, ts) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->vb2_buf.index; + __entry->seq = buf->sequence; + __entry->ts = buf->vb2_buf.timestamp; + ), + TP_printk("node=%u buf=%u seq=%u ts=%llu", __entry->node_id, + __entry->buf_idx, __entry->seq, __entry->ts) +); + +TRACE_EVENT(cfe_frame_start, + TP_PROTO(u32 node_id, u32 fs_count), + TP_ARGS(node_id, fs_count), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, fs_count) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->fs_count = fs_count; + ), + TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count) +); + +TRACE_EVENT(cfe_frame_end, + TP_PROTO(u32 node_id, u32 fs_count), + TP_ARGS(node_id, fs_count), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, fs_count) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->fs_count = fs_count; + ), + TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count) +); + +TRACE_EVENT(cfe_prepare_next_job, + TP_PROTO(bool fe_enabled), + TP_ARGS(fe_enabled), + TP_STRUCT__entry( + __field(bool, fe_enabled) + ), + TP_fast_assign( + __entry->fe_enabled = fe_enabled; + ), + TP_printk("fe_enabled=%u", __entry->fe_enabled) +); + +/* These are copied from csi2.c */ +#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) +#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) +#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) +#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) +#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) + +TRACE_EVENT(csi2_irq, + TP_PROTO(u32 channel, u32 status, u32 dbg), + TP_ARGS(channel, status, dbg), + TP_STRUCT__entry( + __field(u32, channel) + __field(u32, status) + __field(u32, dbg) + ), + TP_fast_assign( + __entry->channel = channel; + __entry->status = status; + __entry->dbg = dbg; + ), + TP_printk("ch=%u flags=[ %s%s%s%s%s] frame=%u line=%u\n", + __entry->channel, + (__entry->status & CSI2_STATUS_IRQ_FS(__entry->channel)) ? + "FS " : "", + (__entry->status & CSI2_STATUS_IRQ_FE(__entry->channel)) ? + "FE " : "", + (__entry->status & CSI2_STATUS_IRQ_FE_ACK(__entry->channel)) ? + "FE_ACK " : "", + (__entry->status & CSI2_STATUS_IRQ_LE(__entry->channel)) ? + "LE " : "", + (__entry->status & CSI2_STATUS_IRQ_LE_ACK(__entry->channel)) ? + "LE_ACK " : "", + __entry->dbg >> 16, __entry->dbg & 0xffff) +); + +TRACE_EVENT(fe_irq, + TP_PROTO(u32 status, u32 output_status, u32 frame_status, + u32 error_status, u32 int_status), + TP_ARGS(status, output_status, frame_status, error_status, int_status), + TP_STRUCT__entry( + __field(u32, status) + __field(u32, output_status) + __field(u32, frame_status) + __field(u32, error_status) + __field(u32, int_status) + ), + TP_fast_assign( + __entry->status = status; + __entry->output_status = output_status; + __entry->frame_status = frame_status; + __entry->error_status = error_status; + __entry->int_status = int_status; + ), + TP_printk("status 0x%x out_status 0x%x frame_status 0x%x error_status 0x%x int_status 0x%x", + __entry->status, + __entry->output_status, + __entry->frame_status, + __entry->error_status, + __entry->int_status) +); + +#endif /* _CFE_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE ../../drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace +#include <trace/define_trace.h> diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c new file mode 100644 index 000000000000..12660087b12f --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c @@ -0,0 +1,2509 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RP1 Camera Front End Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/fwnode.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/lcm.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-dma-contig.h> + +#include <linux/media/raspberrypi/pisp_fe_config.h> +#include <linux/media/raspberrypi/pisp_fe_statistics.h> + +#include "cfe-fmts.h" +#include "cfe.h" +#include "csi2.h" +#include "pisp-fe.h" + +#define CREATE_TRACE_POINTS +#include "cfe-trace.h" + +#define CFE_MODULE_NAME "rp1-cfe" +#define CFE_VERSION "1.0" + +#define cfe_dbg(cfe, fmt, arg...) dev_dbg(&(cfe)->pdev->dev, fmt, ##arg) +#define cfe_info(cfe, fmt, arg...) dev_info(&(cfe)->pdev->dev, fmt, ##arg) +#define cfe_err(cfe, fmt, arg...) dev_err(&(cfe)->pdev->dev, fmt, ##arg) + +/* MIPICFG registers */ +#define MIPICFG_CFG 0x004 +#define MIPICFG_INTR 0x028 +#define MIPICFG_INTE 0x02c +#define MIPICFG_INTF 0x030 +#define MIPICFG_INTS 0x034 + +#define MIPICFG_CFG_SEL_CSI BIT(0) + +#define MIPICFG_INT_CSI_DMA BIT(0) +#define MIPICFG_INT_CSI_HOST BIT(2) +#define MIPICFG_INT_PISP_FE BIT(4) + +#define BPL_ALIGNMENT 16 +#define MAX_BYTESPERLINE 0xffffff00 +#define MAX_BUFFER_SIZE 0xffffff00 +/* + * Max width is therefore determined by the max stride divided by the number of + * bits per pixel. + * + * However, to avoid overflow issues let's use a 16k maximum. This lets us + * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful + * review and adjustment of the code is needed so that it will deal with + * overflows correctly. + */ +#define MAX_WIDTH 16384 +#define MAX_HEIGHT MAX_WIDTH +/* Define a nominal minimum image size */ +#define MIN_WIDTH 16 +#define MIN_HEIGHT 16 + +#define MIN_META_WIDTH 4 +#define MIN_META_HEIGHT 1 + +const struct v4l2_mbus_framefmt cfe_default_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, +}; + +enum node_ids { + /* CSI2 HW output nodes first. */ + CSI2_CH0, + CSI2_CH1, + CSI2_CH2, + CSI2_CH3, + /* FE only nodes from here on. */ + FE_OUT0, + FE_OUT1, + FE_STATS, + FE_CONFIG, + NUM_NODES +}; + +struct node_description { + enum node_ids id; + const char *name; + unsigned int caps; + unsigned int pad_flags; + unsigned int link_pad; +}; + +/* Must match the ordering of enum ids */ +static const struct node_description node_desc[NUM_NODES] = { + [CSI2_CH0] = { + .name = "csi2-ch0", + .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 0 + }, + /* + * At the moment the main userspace component (libcamera) doesn't + * support metadata with video nodes that support both video and + * metadata. So for the time being this node is set to only support + * V4L2_CAP_META_CAPTURE. + */ + [CSI2_CH1] = { + .name = "csi2-ch1", + .caps = V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 1 + }, + [CSI2_CH2] = { + .name = "csi2-ch2", + .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 2 + }, + [CSI2_CH3] = { + .name = "csi2-ch3", + .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 3 + }, + [FE_OUT0] = { + .name = "fe-image0", + .caps = V4L2_CAP_VIDEO_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_OUTPUT0_PAD + }, + [FE_OUT1] = { + .name = "fe-image1", + .caps = V4L2_CAP_VIDEO_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_OUTPUT1_PAD + }, + [FE_STATS] = { + .name = "fe-stats", + .caps = V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_STATS_PAD + }, + [FE_CONFIG] = { + .name = "fe-config", + .caps = V4L2_CAP_META_OUTPUT, + .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_CONFIG_PAD + }, +}; + +#define is_fe_node(node) (((node)->id) >= FE_OUT0) +#define is_csi2_node(node) (!is_fe_node(node)) + +#define node_supports_image_output(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE) +#define node_supports_meta_output(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE) +#define node_supports_image_input(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT) +#define node_supports_meta_input(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT) +#define node_supports_image(node) \ + (node_supports_image_output(node) || node_supports_image_input(node)) +#define node_supports_meta(node) \ + (node_supports_meta_output(node) || node_supports_meta_input(node)) + +#define is_image_output_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) +#define is_image_input_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) +#define is_image_node(node) \ + (is_image_output_node(node) || is_image_input_node(node)) +#define is_meta_output_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE) +#define is_meta_input_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT) +#define is_meta_node(node) \ + (is_meta_output_node(node) || is_meta_input_node(node)) + +/* To track state across all nodes. */ +#define NODE_REGISTERED BIT(0) +#define NODE_ENABLED BIT(1) +#define NODE_STREAMING BIT(2) +#define FS_INT BIT(3) +#define FE_INT BIT(4) +#define NUM_STATES 5 + +struct cfe_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct cfe_config_buffer { + struct cfe_buffer buf; + struct pisp_fe_config config; +}; + +static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct cfe_buffer, vb.vb2_buf); +} + +static inline +struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf) +{ + return container_of(buf, struct cfe_config_buffer, buf); +} + +struct cfe_node { + /* Node id */ + enum node_ids id; + /* Pointer pointing to current v4l2_buffer */ + struct cfe_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct cfe_buffer *next_frm; + /* Used to store current pixel format */ + struct v4l2_format vid_fmt; + /* Used to store current meta format */ + struct v4l2_format meta_fmt; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* lock used to access this structure */ + struct mutex lock; + /* Identifies video device for this channel */ + struct video_device video_dev; + /* Pointer to the parent handle */ + struct cfe_device *cfe; + /* Media pad for this node */ + struct media_pad pad; + /* Frame-start counter */ + unsigned int fs_count; + /* Timestamp of the current buffer */ + u64 ts; +}; + +struct cfe_device { + struct dentry *debugfs; + struct kref kref; + + /* peripheral base address */ + void __iomem *mipi_cfg_base; + + struct clk *clk; + + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; + struct media_pipeline pipe; + + /* IRQ lock for node state and DMA queues */ + spinlock_t state_lock; + bool job_ready; + bool job_queued; + + /* parent device */ + struct platform_device *pdev; + /* subdevice async Notifier */ + struct v4l2_async_notifier notifier; + + /* Source sub device */ + struct v4l2_subdev *source_sd; + /* Source subdev's pad */ + u32 source_pad; + + struct cfe_node node[NUM_NODES]; + DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES); + + struct csi2_device csi2; + struct pisp_fe_device fe; + + int fe_csi2_channel; + + /* Mask of enabled streams */ + u64 streams_mask; +}; + +static inline bool is_fe_enabled(struct cfe_device *cfe) +{ + return cfe->fe_csi2_channel != -1; +} + +static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cfe_device, v4l2_dev); +} + +static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset) +{ + return readl(cfe->mipi_cfg_base + offset); +} + +static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val) +{ + writel(val, cfe->mipi_cfg_base + offset); +} + +static bool check_state(struct cfe_device *cfe, unsigned long state, + unsigned int node_id) +{ + unsigned long bit; + + for_each_set_bit(bit, &state, sizeof(state)) { + if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags)) + return false; + } + + return true; +} + +static void set_state(struct cfe_device *cfe, unsigned long state, + unsigned int node_id) +{ + unsigned long bit; + + for_each_set_bit(bit, &state, sizeof(state)) + set_bit(bit + (node_id * NUM_STATES), cfe->node_flags); +} + +static void clear_state(struct cfe_device *cfe, unsigned long state, + unsigned int node_id) +{ + unsigned long bit; + + for_each_set_bit(bit, &state, sizeof(state)) + clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags); +} + +static bool test_any_node(struct cfe_device *cfe, unsigned long cond) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (check_state(cfe, cond, i)) + return true; + } + + return false; +} + +static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond, + unsigned long cond) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (check_state(cfe, precond, i)) { + if (!check_state(cfe, cond, i)) + return false; + } + } + + return true; +} + +static int mipi_cfg_regs_show(struct seq_file *s, void *data) +{ + struct cfe_device *cfe = s->private; + int ret; + + ret = pm_runtime_resume_and_get(&cfe->pdev->dev); + if (ret) + return ret; + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg)) + DUMP(MIPICFG_CFG); + DUMP(MIPICFG_INTR); + DUMP(MIPICFG_INTE); + DUMP(MIPICFG_INTF); + DUMP(MIPICFG_INTS); +#undef DUMP + + pm_runtime_put(&cfe->pdev->dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs); + +/* Format setup functions */ +const struct cfe_fmt *find_format_by_code(u32 code) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +const struct cfe_fmt *find_format_by_pix(u32 pixelformat) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == pixelformat) + return &formats[i]; + } + + return NULL; +} + +static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code, + u32 fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code && formats[i].fourcc == fourcc) + return &formats[i]; + } + + return NULL; +} + +/* + * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap + * possible. + */ +u32 cfe_find_16bit_code(u32 code) +{ + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(code); + + if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT]) + return 0; + + cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]); + if (!cfe_fmt) + return 0; + + return cfe_fmt->code; +} + +/* + * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap + * possible. + */ +u32 cfe_find_compressed_code(u32 code) +{ + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(code); + + if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED]) + return 0; + + cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]); + if (!cfe_fmt) + return 0; + + return cfe_fmt->code; +} + +static void cfe_calc_vid_format_size_bpl(struct cfe_device *cfe, + const struct cfe_fmt *fmt, + struct v4l2_format *f) +{ + unsigned int min_bytesperline; + + v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, + &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0); + + min_bytesperline = + ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT); + + if (f->fmt.pix.bytesperline > min_bytesperline && + f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) + f->fmt.pix.bytesperline = + ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT); + else + f->fmt.pix.bytesperline = min_bytesperline; + + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u img_size:%u\n", __func__, + &f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); +} + +static void cfe_calc_meta_format_size_bpl(struct cfe_device *cfe, + const struct cfe_fmt *fmt, + struct v4l2_format *f) +{ + v4l_bound_align_image(&f->fmt.meta.width, MIN_META_WIDTH, MAX_WIDTH, 2, + &f->fmt.meta.height, MIN_META_HEIGHT, MAX_HEIGHT, + 0, 0); + + f->fmt.meta.bytesperline = (f->fmt.meta.width * fmt->depth) >> 3; + f->fmt.meta.buffersize = f->fmt.meta.height * f->fmt.pix.bytesperline; + + cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u buf_size:%u\n", __func__, + &f->fmt.meta.dataformat, f->fmt.meta.width, f->fmt.meta.height, + f->fmt.meta.bytesperline, f->fmt.meta.buffersize); +} + +static void cfe_schedule_next_csi2_job(struct cfe_device *cfe) +{ + struct cfe_buffer *buf; + dma_addr_t addr; + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { + struct cfe_node *node = &cfe->node[i]; + unsigned int stride, size; + + if (!check_state(cfe, NODE_STREAMING, i)) + continue; + + buf = list_first_entry(&node->dma_queue, struct cfe_buffer, + list); + node->next_frm = buf; + list_del(&buf->list); + + trace_cfe_csi2_schedule(node->id, &buf->vb.vb2_buf); + + if (is_meta_node(node)) { + size = node->meta_fmt.fmt.meta.buffersize; + /* We use CSI2_CH_CTRL_PACK_BYTES, so stride == 0 */ + stride = 0; + } else { + size = node->vid_fmt.fmt.pix.sizeimage; + stride = node->vid_fmt.fmt.pix.bytesperline; + } + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size); + } +} + +static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) +{ + struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 }; + struct cfe_config_buffer *config_buf; + struct cfe_buffer *buf; + + for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (!check_state(cfe, NODE_STREAMING, i)) + continue; + + buf = list_first_entry(&node->dma_queue, struct cfe_buffer, + list); + + trace_cfe_fe_schedule(node->id, &buf->vb.vb2_buf); + + node->next_frm = buf; + vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf; + list_del(&buf->list); + } + + config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm); + pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config); +} + +static bool cfe_check_job_ready(struct cfe_device *cfe) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (!check_state(cfe, NODE_ENABLED, i)) + continue; + + if (list_empty(&node->dma_queue)) + return false; + } + + return true; +} + +static void cfe_prepare_next_job(struct cfe_device *cfe) +{ + trace_cfe_prepare_next_job(is_fe_enabled(cfe)); + + cfe->job_queued = true; + cfe_schedule_next_csi2_job(cfe); + if (is_fe_enabled(cfe)) + cfe_schedule_next_pisp_job(cfe); + + /* Flag if another job is ready after this. */ + cfe->job_ready = cfe_check_job_ready(cfe); +} + +static void cfe_process_buffer_complete(struct cfe_node *node, + enum vb2_buffer_state state) +{ + trace_cfe_buffer_complete(node->id, &node->cur_frm->vb); + + node->cur_frm->vb.sequence = node->fs_count - 1; + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); +} + +static void cfe_queue_event_sof(struct cfe_node *node) +{ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = node->fs_count - 1, + }; + + v4l2_event_queue(&node->video_dev, &event); +} + +static void cfe_sof_isr(struct cfe_node *node) +{ + struct cfe_device *cfe = node->cfe; + bool matching_fs = true; + + trace_cfe_frame_start(node->id, node->fs_count); + + /* + * If the sensor is producing unexpected frame event ordering over a + * sustained period of time, guard against the possibility of coming + * here and orphaning the cur_frm if it's not been dequeued already. + * Unfortunately, there is not enough hardware state to tell if this + * may have occurred. + */ + if (WARN(node->cur_frm, "%s: [%s] Orphaned frame at seq %u\n", + __func__, node_desc[node->id].name, node->fs_count)) + cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR); + + node->cur_frm = node->next_frm; + node->next_frm = NULL; + node->fs_count++; + + node->ts = ktime_get_ns(); + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (!check_state(cfe, NODE_STREAMING, i) || i == node->id) + continue; + /* + * This checks if any other node has seen a FS. If yes, use the + * same timestamp, eventually across all node buffers. + */ + if (cfe->node[i].fs_count >= node->fs_count) + node->ts = cfe->node[i].ts; + /* + * This checks if all other node have seen a matching FS. If + * yes, we can flag another job to be queued. + */ + if (matching_fs && cfe->node[i].fs_count != node->fs_count) + matching_fs = false; + } + + if (matching_fs) + cfe->job_queued = false; + + if (node->cur_frm) + node->cur_frm->vb.vb2_buf.timestamp = node->ts; + + set_state(cfe, FS_INT, node->id); + clear_state(cfe, FE_INT, node->id); + + if (is_image_output_node(node)) + cfe_queue_event_sof(node); +} + +static void cfe_eof_isr(struct cfe_node *node) +{ + struct cfe_device *cfe = node->cfe; + + trace_cfe_frame_end(node->id, node->fs_count - 1); + + if (node->cur_frm) + cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE); + + node->cur_frm = NULL; + set_state(cfe, FE_INT, node->id); + clear_state(cfe, FS_INT, node->id); +} + +static irqreturn_t cfe_isr(int irq, void *dev) +{ + struct cfe_device *cfe = dev; + bool sof[NUM_NODES] = { 0 }, eof[NUM_NODES] = { 0 }; + u32 sts; + + sts = cfg_reg_read(cfe, MIPICFG_INTS); + + if (sts & MIPICFG_INT_CSI_DMA) + csi2_isr(&cfe->csi2, sof, eof); + + if (sts & MIPICFG_INT_PISP_FE) + pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS, + eof + CSI2_NUM_CHANNELS); + + spin_lock(&cfe->state_lock); + + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + /* + * The check_state(NODE_STREAMING) is to ensure we do not loop + * over the CSI2_CHx nodes when the FE is active since they + * generate interrupts even though the node is not streaming. + */ + if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i])) + continue; + + /* + * There are 3 cases where we could get FS + FE_ACK at + * the same time: + * 1) FE of the current frame, and FS of the next frame. + * 2) FS + FE of the same frame. + * 3) FE of the current frame, and FS + FE of the next + * frame. To handle this, see the sof handler below. + * + * (1) is handled implicitly by the ordering of the FE and FS + * handlers below. + */ + if (eof[i]) { + /* + * The condition below tests for (2). Run the FS handler + * first before the FE handler, both for the current + * frame. + */ + if (sof[i] && !check_state(cfe, FS_INT, i)) { + cfe_sof_isr(node); + sof[i] = false; + } + + cfe_eof_isr(node); + } + + if (sof[i]) { + /* + * The condition below tests for (3). In such cases, we + * come in here with FS flag set in the node state from + * the previous frame since it only gets cleared in + * cfe_eof_isr(). Handle the FE for the previous + * frame first before the FS handler for the current + * frame. + */ + if (check_state(cfe, FS_INT, node->id) && + !check_state(cfe, FE_INT, node->id)) { + cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n", + __func__, node_desc[node->id].name); + cfe_eof_isr(node); + } + + cfe_sof_isr(node); + } + + if (!cfe->job_queued && cfe->job_ready) + cfe_prepare_next_job(cfe); + } + + spin_unlock(&cfe->state_lock); + + return IRQ_HANDLED; +} + +/* + * Stream helpers + */ + +static int cfe_get_vc_dt_fallback(struct cfe_device *cfe, u8 *vc, u8 *dt) +{ + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + const struct cfe_fmt *cfe_fmt; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0); + if (!fmt) + return -EINVAL; + + cfe_fmt = find_format_by_code(fmt->code); + if (!cfe_fmt) + return -EINVAL; + + *vc = 0; + *dt = cfe_fmt->csi_dt; + + return 0; +} + +static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc, + u8 *dt) +{ + struct v4l2_mbus_frame_desc remote_desc; + struct v4l2_subdev_state *state; + u32 sink_stream; + unsigned int i; + int ret; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream); + if (ret) + return ret; + + ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc, + cfe->source_pad, &remote_desc); + if (ret == -ENOIOCTLCMD) { + cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n"); + return cfe_get_vc_dt_fallback(cfe, vc, dt); + } else if (ret) { + cfe_err(cfe, "Failed to get frame descriptor\n"); + return ret; + } + + if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + cfe_err(cfe, "Frame descriptor does not describe CSI-2 link"); + return -EINVAL; + } + + for (i = 0; i < remote_desc.num_entries; i++) { + if (remote_desc.entry[i].stream == sink_stream) + break; + } + + if (i == remote_desc.num_entries) { + cfe_err(cfe, "Stream %u not found in remote frame desc\n", + sink_stream); + return -EINVAL; + } + + *vc = remote_desc.entry[i].bus.csi2.vc; + *dt = remote_desc.entry[i].bus.csi2.dt; + + return 0; +} + +static int cfe_start_channel(struct cfe_node *node) +{ + struct cfe_device *cfe = node->cfe; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *source_fmt; + const struct cfe_fmt *fmt; + unsigned long flags; + bool start_fe; + int ret; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + start_fe = is_fe_enabled(cfe) && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + if (start_fe) { + unsigned int width, height; + u8 vc, dt; + + cfe_dbg(cfe, "%s: %s using csi2 channel %d\n", __func__, + node_desc[FE_OUT0].name, cfe->fe_csi2_channel); + + ret = cfe_get_vc_dt(cfe, cfe->fe_csi2_channel, &vc, &dt); + if (ret) + return ret; + + source_fmt = v4l2_subdev_state_get_format(state, + node_desc[cfe->fe_csi2_channel].link_pad); + fmt = find_format_by_code(source_fmt->code); + + width = source_fmt->width; + height = source_fmt->height; + + /* Must have a valid CSI2 datatype. */ + WARN_ON(!fmt->csi_dt); + + /* + * Start the associated CSI2 Channel as well. + * + * Must write to the ADDR register to latch the ctrl values + * even if we are connected to the front end. Once running, + * this is handled by the CSI2 AUTO_ARM mode. + */ + csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel, + CSI2_MODE_FE_STREAMING, + true, false, width, height, vc, dt); + csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1); + pisp_fe_start(&cfe->fe); + } + + if (is_csi2_node(node)) { + unsigned int width = 0, height = 0; + u8 vc, dt; + + ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt); + if (ret) { + if (start_fe) { + csi2_stop_channel(&cfe->csi2, + cfe->fe_csi2_channel); + pisp_fe_stop(&cfe->fe); + } + + return ret; + } + + u32 mode = CSI2_MODE_NORMAL; + + source_fmt = v4l2_subdev_state_get_format(state, + node_desc[node->id].link_pad); + fmt = find_format_by_code(source_fmt->code); + + /* Must have a valid CSI2 datatype. */ + WARN_ON(!fmt->csi_dt); + + if (is_image_output_node(node)) { + u32 pixfmt; + + width = source_fmt->width; + height = source_fmt->height; + + pixfmt = node->vid_fmt.fmt.pix.pixelformat; + + if (pixfmt == fmt->remap[CFE_REMAP_16BIT]) { + mode = CSI2_MODE_REMAP; + } else if (pixfmt == fmt->remap[CFE_REMAP_COMPRESSED]) { + mode = CSI2_MODE_COMPRESSED; + csi2_set_compression(&cfe->csi2, node->id, + CSI2_COMPRESSION_DELTA, 0, + 0); + } + } + /* Unconditionally start this CSI2 channel. */ + csi2_start_channel(&cfe->csi2, node->id, + mode, + /* Auto arm */ + false, + /* Pack bytes */ + is_meta_node(node) ? true : false, + width, height, vc, dt); + } + + spin_lock_irqsave(&cfe->state_lock, flags); + if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) + cfe_prepare_next_job(cfe); + spin_unlock_irqrestore(&cfe->state_lock, flags); + + return 0; +} + +static void cfe_stop_channel(struct cfe_node *node, bool fe_stop) +{ + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s] fe_stop %u\n", __func__, + node_desc[node->id].name, fe_stop); + + if (fe_stop) { + csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel); + pisp_fe_stop(&cfe->fe); + } + + if (is_csi2_node(node)) + csi2_stop_channel(&cfe->csi2, node->id); +} + +static void cfe_return_buffers(struct cfe_node *node, + enum vb2_buffer_state state) +{ + struct cfe_device *cfe = node->cfe; + struct cfe_buffer *buf, *tmp; + unsigned long flags; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + spin_lock_irqsave(&cfe->state_lock, flags); + list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { + list_del(&buf->list); + trace_cfe_return_buffer(node->id, buf->vb.vb2_buf.index, 2); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (node->cur_frm) { + trace_cfe_return_buffer(node->id, + node->cur_frm->vb.vb2_buf.index, 0); + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); + } + if (node->next_frm && node->cur_frm != node->next_frm) { + trace_cfe_return_buffer(node->id, + node->next_frm->vb.vb2_buf.index, 1); + vb2_buffer_done(&node->next_frm->vb.vb2_buf, state); + } + + node->cur_frm = NULL; + node->next_frm = NULL; + spin_unlock_irqrestore(&cfe->state_lock, flags); +} + +/* + * vb2 ops + */ + +static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct cfe_node *node = vb2_get_drv_priv(vq); + struct cfe_device *cfe = node->cfe; + unsigned int size = is_image_node(node) ? + node->vid_fmt.fmt.pix.sizeimage : + node->meta_fmt.fmt.meta.buffersize; + + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + node->buffer_queue.type); + + if (vq->max_num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->max_num_buffers; + + if (*nplanes) { + if (sizes[0] < size) { + cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size); + return -EINVAL; + } + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int cfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct cfe_device *cfe = node->cfe; + struct cfe_buffer *buf = to_cfe_buffer(vb); + unsigned long size; + + trace_cfe_buffer_prepare(node->id, vb); + + size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : + node->meta_fmt.fmt.meta.buffersize; + if (vb2_plane_size(vb, 0) < size) { + cfe_err(cfe, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + + if (node->id == FE_CONFIG) { + struct cfe_config_buffer *b = to_cfe_config_buffer(buf); + void *addr = vb2_plane_vaddr(vb, 0); + + memcpy(&b->config, addr, sizeof(struct pisp_fe_config)); + return pisp_fe_validate_config(&cfe->fe, &b->config, + &cfe->node[FE_OUT0].vid_fmt, + &cfe->node[FE_OUT1].vid_fmt); + } + + return 0; +} + +static void cfe_buffer_queue(struct vb2_buffer *vb) +{ + struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct cfe_device *cfe = node->cfe; + struct cfe_buffer *buf = to_cfe_buffer(vb); + unsigned long flags; + bool schedule_now; + + spin_lock_irqsave(&cfe->state_lock, flags); + + list_add_tail(&buf->list, &node->dma_queue); + + if (!cfe->job_ready) + cfe->job_ready = cfe_check_job_ready(cfe); + + schedule_now = !cfe->job_queued && cfe->job_ready && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + trace_cfe_buffer_queue(node->id, vb, schedule_now); + + if (schedule_now) + cfe_prepare_next_job(cfe); + + spin_unlock_irqrestore(&cfe->state_lock, flags); +} + +static s64 cfe_get_source_link_freq(struct cfe_device *cfe) +{ + struct v4l2_subdev_state *state; + s64 link_freq; + u32 bpp; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + /* + * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back + * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. + * + * With multistream input there is no single pixel rate, and thus we + * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which + * causes v4l2_get_link_freq() to return an error if it falls back to + * V4L2_CID_PIXEL_RATE. + */ + + if (state->routing.num_routes == 1) { + struct v4l2_subdev_route *route = &state->routing.routes[0]; + struct v4l2_mbus_framefmt *source_fmt; + const struct cfe_fmt *fmt; + + source_fmt = v4l2_subdev_state_get_format(state, + route->sink_pad, + route->sink_stream); + + fmt = find_format_by_code(source_fmt->code); + if (!fmt) + return -EINVAL; + + bpp = fmt->depth; + } else { + bpp = 0; + } + + link_freq = v4l2_get_link_freq(cfe->source_sd->ctrl_handler, bpp, + 2 * cfe->csi2.dphy.active_lanes); + if (link_freq < 0) + cfe_err(cfe, "failed to get link freq for subdev '%s'\n", + cfe->source_sd->name); + + return link_freq; +} + +static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + struct cfe_node *node = vb2_get_drv_priv(vq); + struct cfe_device *cfe = node->cfe; + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *route; + s64 link_freq; + int ret; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (!check_state(cfe, NODE_ENABLED, node->id)) { + cfe_err(cfe, "%s node link is not enabled.\n", + node_desc[node->id].name); + ret = -EINVAL; + goto err_streaming; + } + + ret = pm_runtime_resume_and_get(&cfe->pdev->dev); + if (ret < 0) { + cfe_err(cfe, "pm_runtime_resume_and_get failed\n"); + goto err_streaming; + } + + /* When using the Frontend, we must enable the FE_CONFIG node. */ + if (is_fe_enabled(cfe) && + !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) { + cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n"); + ret = -EINVAL; + goto err_pm_put; + } + + ret = media_pipeline_start(&node->pad, &cfe->pipe); + if (ret < 0) { + cfe_err(cfe, "Failed to start media pipeline: %d\n", ret); + goto err_pm_put; + } + + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + + clear_state(cfe, FS_INT | FE_INT, node->id); + set_state(cfe, NODE_STREAMING, node->id); + node->fs_count = 0; + + ret = cfe_start_channel(node); + if (ret) + goto err_unlock_state; + + if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { + cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n"); + v4l2_subdev_unlock_state(state); + return 0; + } + + cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI); + cfg_reg_write(cfe, MIPICFG_INTE, + MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE); + + ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0, + &mbus_config); + if (ret < 0 && ret != -ENOIOCTLCMD) { + cfe_err(cfe, "g_mbus_config failed\n"); + goto err_clear_inte; + } + + cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; + if (!cfe->csi2.dphy.active_lanes) + cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes; + if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) { + cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n", + cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes); + ret = -EINVAL; + goto err_clear_inte; + } + + link_freq = cfe_get_source_link_freq(cfe); + if (link_freq < 0) + goto err_clear_inte; + + cfe->csi2.dphy.dphy_rate = div_s64(link_freq * 2, 1000000); + csi2_open_rx(&cfe->csi2); + + cfe->streams_mask = 0; + + for_each_active_route(&state->routing, route) + cfe->streams_mask |= BIT_ULL(route->sink_stream); + + ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad, + cfe->streams_mask); + if (ret) { + cfe_err(cfe, "stream on failed in subdev\n"); + goto err_disable_cfe; + } + + cfe_dbg(cfe, "Streaming enabled\n"); + + v4l2_subdev_unlock_state(state); + + return 0; + +err_disable_cfe: + csi2_close_rx(&cfe->csi2); +err_clear_inte: + cfg_reg_write(cfe, MIPICFG_INTE, 0); + + cfe_stop_channel(node, + is_fe_enabled(cfe) && test_all_nodes(cfe, NODE_ENABLED, + NODE_STREAMING)); +err_unlock_state: + v4l2_subdev_unlock_state(state); + media_pipeline_stop(&node->pad); +err_pm_put: + pm_runtime_put(&cfe->pdev->dev); +err_streaming: + cfe_return_buffers(node, VB2_BUF_STATE_QUEUED); + clear_state(cfe, NODE_STREAMING, node->id); + + return ret; +} + +static void cfe_stop_streaming(struct vb2_queue *vq) +{ + struct cfe_node *node = vb2_get_drv_priv(vq); + struct cfe_device *cfe = node->cfe; + unsigned long flags; + bool fe_stop; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + spin_lock_irqsave(&cfe->state_lock, flags); + fe_stop = is_fe_enabled(cfe) && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + cfe->job_ready = false; + clear_state(cfe, NODE_STREAMING, node->id); + spin_unlock_irqrestore(&cfe->state_lock, flags); + + cfe_stop_channel(node, fe_stop); + + if (!test_any_node(cfe, NODE_STREAMING)) { + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + + ret = v4l2_subdev_disable_streams(cfe->source_sd, + cfe->source_pad, + cfe->streams_mask); + if (ret) + cfe_err(cfe, "stream disable failed in subdev\n"); + + v4l2_subdev_unlock_state(state); + + csi2_close_rx(&cfe->csi2); + + cfg_reg_write(cfe, MIPICFG_INTE, 0); + + cfe_dbg(cfe, "%s: Streaming disabled\n", __func__); + } + + media_pipeline_stop(&node->pad); + + /* Clear all queued buffers for the node */ + cfe_return_buffers(node, VB2_BUF_STATE_ERROR); + + pm_runtime_put(&cfe->pdev->dev); +} + +static const struct vb2_ops cfe_video_qops = { + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .queue_setup = cfe_queue_setup, + .buf_prepare = cfe_buffer_prepare, + .buf_queue = cfe_buffer_queue, + .start_streaming = cfe_start_streaming, + .stop_streaming = cfe_stop_streaming, +}; + +/* + * v4l2 ioctl ops + */ + +static int cfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card)); + + cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE | + V4L2_CAP_META_OUTPUT; + + return 0; +} + +static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + unsigned int i, j; + + if (!node_supports_image_output(node)) + return -EINVAL; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { + if (f->mbus_code && formats[i].code != f->mbus_code) + continue; + + if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT || + formats[i].flags & CFE_FORMAT_FLAG_META_CAP) + continue; + + if (is_fe_node(node) && + !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT)) + continue; + + if (j == f->index) { + f->pixelformat = formats[i].fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; + } + j++; + } + + return -EINVAL; +} + +static int cfe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + + if (!node_supports_image(node)) + return -EINVAL; + + *f = node->vid_fmt; + + return 0; +} + +static int cfe_validate_fmt_vid_cap(struct cfe_node *node, + struct v4l2_format *f) +{ + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 pix %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.pix.width, f->fmt.pix.height, + &f->fmt.pix.pixelformat); + + if (!node_supports_image_output(node)) + return -EINVAL; + + /* + * Default to a format that works for both CSI2 and FE. + */ + fmt = find_format_by_pix(f->fmt.pix.pixelformat); + if (!fmt) + fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10); + + f->fmt.pix.pixelformat = fmt->fourcc; + + if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) { + f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT]; + fmt = find_format_by_pix(f->fmt.pix.pixelformat); + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + + cfe_calc_vid_format_size_bpl(cfe, fmt, f); + + return 0; +} + +static int cfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + if (vb2_is_busy(q)) + return -EBUSY; + + ret = cfe_validate_fmt_vid_cap(node, f); + if (ret) + return ret; + + node->vid_fmt = *f; + + cfe_dbg(cfe, "%s: Set %ux%u, V4L2 pix %p4cc\n", __func__, + node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height, + &node->vid_fmt.fmt.pix.pixelformat); + + return 0; +} + +static int cfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + return cfe_validate_fmt_vid_cap(node, f); +} + +static int cfe_enum_fmt_meta(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (!node_supports_meta(node)) + return -EINVAL; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + f->flags = V4L2_FMT_FLAG_META_LINE_BASED; + + switch (f->index) { + case 0: + f->pixelformat = V4L2_META_FMT_GENERIC_8; + return 0; + case 1: + f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_10; + return 0; + case 2: + f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_12; + return 0; + default: + return -EINVAL; + } + default: + break; + } + + if (f->index != 0) + return -EINVAL; + + switch (node->id) { + case FE_STATS: + f->pixelformat = V4L2_META_FMT_RPI_FE_STATS; + return 0; + case FE_CONFIG: + f->pixelformat = V4L2_META_FMT_RPI_FE_CFG; + return 0; + default: + return -EINVAL; + } +} + +static int cfe_validate_fmt_meta(struct cfe_node *node, struct v4l2_format *f) +{ + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 meta %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.meta.width, + f->fmt.meta.height, &f->fmt.meta.dataformat); + break; + case FE_STATS: + case FE_CONFIG: + cfe_dbg(cfe, "%s: [%s] %u bytes, V4L2 meta %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.meta.buffersize, + &f->fmt.meta.dataformat); + break; + default: + return -EINVAL; + } + + if (!node_supports_meta(node)) + return -EINVAL; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + fmt = find_format_by_pix(f->fmt.meta.dataformat); + if (!fmt || !(fmt->flags & CFE_FORMAT_FLAG_META_CAP)) + fmt = find_format_by_pix(V4L2_META_FMT_GENERIC_CSI2_10); + + f->fmt.meta.dataformat = fmt->fourcc; + + cfe_calc_meta_format_size_bpl(cfe, fmt, f); + + return 0; + case FE_STATS: + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS; + f->fmt.meta.buffersize = sizeof(struct pisp_statistics); + return 0; + case FE_CONFIG: + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_fe_config); + return 0; + default: + return -EINVAL; + } +} + +static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (!node_supports_meta(node)) + return -EINVAL; + + *f = node->meta_fmt; + + return 0; +} + +static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (vb2_is_busy(q)) + return -EBUSY; + + if (!node_supports_meta(node)) + return -EINVAL; + + ret = cfe_validate_fmt_meta(node, f); + if (ret) + return ret; + + node->meta_fmt = *f; + + cfe_dbg(cfe, "%s: Set %p4cc\n", __func__, + &node->meta_fmt.fmt.meta.dataformat); + + return 0; +} + +static int cfe_try_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + return cfe_validate_fmt_meta(node, f); +} + +static int cfe_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + cfe_dbg(cfe, "%s [%s]\n", __func__, node_desc[node->id].name); + + if (fsize->index > 0) + return -EINVAL; + + /* check for valid format */ + fmt = find_format_by_pix(fsize->pixel_format); + if (!fmt) { + cfe_dbg(cfe, "Invalid pixel code: %x\n", fsize->pixel_format); + return -EINVAL; + } + + /* TODO: Do we have limits on the step_width? */ + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = MIN_WIDTH; + fsize->stepwise.max_width = MAX_WIDTH; + fsize->stepwise.step_width = 2; + fsize->stepwise.min_height = MIN_HEIGHT; + fsize->stepwise.max_height = MAX_HEIGHT; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *vdev = video_devdata(file); + struct cfe_node *node = video_get_drvdata(vdev); + struct cfe_device *cfe = node->cfe; + int ret; + + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + p->type); + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->type != V4L2_BUF_TYPE_META_CAPTURE && + p->type != V4L2_BUF_TYPE_META_OUTPUT) + return -EINVAL; + + ret = vb2_queue_change_type(vdev->queue, p->type); + if (ret) + return ret; + + return vb2_ioctl_reqbufs(file, priv, p); +} + +static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct video_device *vdev = video_devdata(file); + struct cfe_node *node = video_get_drvdata(vdev); + struct cfe_device *cfe = node->cfe; + int ret; + + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + p->format.type); + + if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->format.type != V4L2_BUF_TYPE_META_CAPTURE && + p->format.type != V4L2_BUF_TYPE_META_OUTPUT) + return -EINVAL; + + ret = vb2_queue_change_type(vdev->queue, p->format.type); + if (ret) + return ret; + + return vb2_ioctl_create_bufs(file, priv, p); +} + +static int cfe_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct cfe_node *node = video_get_drvdata(fh->vdev); + + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + if (!node_supports_image_output(node)) + break; + + return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (!node_supports_image_output(node) && + !node_supports_meta_output(node)) + break; + + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops cfe_ioctl_ops = { + .vidioc_querycap = cfe_querycap, + .vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cfe_g_fmt, + .vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap, + + .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta, + .vidioc_g_fmt_meta_cap = cfe_g_fmt_meta, + .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta, + .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta, + + .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta, + .vidioc_g_fmt_meta_out = cfe_g_fmt_meta, + .vidioc_s_fmt_meta_out = cfe_s_fmt_meta, + .vidioc_try_fmt_meta_out = cfe_try_fmt_meta, + + .vidioc_enum_framesizes = cfe_enum_framesizes, + + .vidioc_reqbufs = cfe_vb2_ioctl_reqbufs, + .vidioc_create_bufs = cfe_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, + + .vidioc_subscribe_event = cfe_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) +{ + struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev); + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (check_state(cfe, NODE_REGISTERED, i)) + continue; + + v4l2_event_queue(&node->video_dev, arg); + } + break; + default: + break; + } +} + +/* cfe capture driver file operations */ +static const struct v4l2_file_operations cfe_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int cfe_video_link_validate(struct media_link *link) +{ + struct video_device *vd = container_of(link->sink->entity, + struct video_device, entity); + struct cfe_node *node = container_of(vd, struct cfe_node, video_dev); + struct cfe_device *cfe = node->cfe; + struct v4l2_mbus_framefmt *source_fmt; + struct v4l2_subdev_state *state; + struct v4l2_subdev *source_sd; + int ret = 0; + + cfe_dbg(cfe, "%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__, + node_desc[node->id].name, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + if (!media_entity_remote_source_pad_unique(link->sink->entity)) { + cfe_err(cfe, "video node %s pad not connected\n", vd->name); + return -ENOTCONN; + } + + source_sd = media_entity_to_v4l2_subdev(link->source->entity); + + state = v4l2_subdev_lock_and_get_active_state(source_sd); + + source_fmt = v4l2_subdev_state_get_format(state, link->source->index); + if (!source_fmt) { + ret = -EINVAL; + goto out; + } + + if (is_image_output_node(node)) { + struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix; + const struct cfe_fmt *fmt; + + if (source_fmt->width != pix_fmt->width || + source_fmt->height != pix_fmt->height) { + cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", + pix_fmt->width, pix_fmt->height, + source_fmt->width, source_fmt->height); + ret = -EINVAL; + goto out; + } + + fmt = find_format_by_code_and_fourcc(source_fmt->code, + pix_fmt->pixelformat); + if (!fmt) { + cfe_err(cfe, "Format mismatch!\n"); + ret = -EINVAL; + goto out; + } + } else if (is_csi2_node(node) && is_meta_output_node(node)) { + struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta; + const struct cfe_fmt *fmt; + + if (source_fmt->width != meta_fmt->width || + source_fmt->height != meta_fmt->height) { + cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", + meta_fmt->width, meta_fmt->height, + source_fmt->width, source_fmt->height); + ret = -EINVAL; + goto out; + } + + fmt = find_format_by_code_and_fourcc(source_fmt->code, + meta_fmt->dataformat); + if (!fmt) { + cfe_err(cfe, "Format mismatch!\n"); + ret = -EINVAL; + goto out; + } + } + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct media_entity_operations cfe_media_entity_ops = { + .link_validate = cfe_video_link_validate, +}; + +static int cfe_video_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_device *mdev = link->graph_obj.mdev; + struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev); + struct media_entity *fe = &cfe->fe.sd.entity; + struct media_entity *csi2 = &cfe->csi2.sd.entity; + unsigned long lock_flags; + + if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH) + return 0; + + cfe_dbg(cfe, "%s: %s[%u] -> %s[%u] 0x%x", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, flags); + + spin_lock_irqsave(&cfe->state_lock, lock_flags); + + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (link->sink->entity != &cfe->node[i].video_dev.entity && + link->source->entity != &cfe->node[i].video_dev.entity) + continue; + + if (link->flags & MEDIA_LNK_FL_ENABLED) + set_state(cfe, NODE_ENABLED, i); + else + clear_state(cfe, NODE_ENABLED, i); + + break; + } + + spin_unlock_irqrestore(&cfe->state_lock, lock_flags); + + if (link->source->entity != csi2) + return 0; + if (link->sink->entity != fe) + return 0; + if (link->sink->index != 0) + return 0; + + cfe->fe_csi2_channel = -1; + if (link->flags & MEDIA_LNK_FL_ENABLED) { + if (link->source->index == node_desc[CSI2_CH0].link_pad) + cfe->fe_csi2_channel = CSI2_CH0; + else if (link->source->index == node_desc[CSI2_CH1].link_pad) + cfe->fe_csi2_channel = CSI2_CH1; + else if (link->source->index == node_desc[CSI2_CH2].link_pad) + cfe->fe_csi2_channel = CSI2_CH2; + else if (link->source->index == node_desc[CSI2_CH3].link_pad) + cfe->fe_csi2_channel = CSI2_CH3; + } + + if (is_fe_enabled(cfe)) + cfe_dbg(cfe, "%s: Found CSI2:%d -> FE:0 link\n", __func__, + cfe->fe_csi2_channel); + else + cfe_dbg(cfe, "%s: Unable to find CSI2:x -> FE:0 link\n", + __func__); + + return 0; +} + +static const struct media_device_ops cfe_media_device_ops = { + .link_notify = cfe_video_link_notify, +}; + +static void cfe_release(struct kref *kref) +{ + struct cfe_device *cfe = container_of(kref, struct cfe_device, kref); + + media_device_cleanup(&cfe->mdev); + + kfree(cfe); +} + +static void cfe_put(struct cfe_device *cfe) +{ + kref_put(&cfe->kref, cfe_release); +} + +static void cfe_get(struct cfe_device *cfe) +{ + kref_get(&cfe->kref); +} + +static void cfe_node_release(struct video_device *vdev) +{ + struct cfe_node *node = video_get_drvdata(vdev); + + cfe_put(node->cfe); +} + +static int cfe_register_node(struct cfe_device *cfe, int id) +{ + struct video_device *vdev; + const struct cfe_fmt *fmt; + struct vb2_queue *q; + struct cfe_node *node = &cfe->node[id]; + int ret; + + node->cfe = cfe; + node->id = id; + + if (node_supports_image(node)) { + if (node_supports_image_output(node)) + node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else + node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + fmt = find_format_by_code(cfe_default_format.code); + if (!fmt) { + cfe_err(cfe, "Failed to find format code\n"); + return -EINVAL; + } + + node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc; + v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, + &cfe_default_format); + + ret = cfe_validate_fmt_vid_cap(node, &node->vid_fmt); + if (ret) + return ret; + } + + if (node_supports_meta(node)) { + if (node_supports_meta_output(node)) + node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + else + node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT; + + ret = cfe_validate_fmt_meta(node, &node->meta_fmt); + if (ret) + return ret; + } + + mutex_init(&node->lock); + + q = &node->buffer_queue; + q->type = node_supports_image(node) ? node->vid_fmt.type : + node->meta_fmt.type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = node; + q->ops = &cfe_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer) + : sizeof(struct cfe_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &node->lock; + q->min_queued_buffers = 1; + q->dev = &cfe->pdev->dev; + + ret = vb2_queue_init(q); + if (ret) { + cfe_err(cfe, "vb2_queue_init() failed\n"); + return ret; + } + + INIT_LIST_HEAD(&node->dma_queue); + + vdev = &node->video_dev; + vdev->release = cfe_node_release; + vdev->fops = &cfe_fops; + vdev->ioctl_ops = &cfe_ioctl_ops; + vdev->entity.ops = &cfe_media_entity_ops; + vdev->v4l2_dev = &cfe->v4l2_dev; + vdev->vfl_dir = (node_supports_image_output(node) || + node_supports_meta_output(node)) ? + VFL_DIR_RX : + VFL_DIR_TX; + vdev->queue = q; + vdev->lock = &node->lock; + vdev->device_caps = node_desc[id].caps; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + + /* Define the device names */ + snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME, + node_desc[id].name); + + video_set_drvdata(vdev, node); + node->pad.flags = node_desc[id].pad_flags; + media_entity_pads_init(&vdev->entity, 1, &node->pad); + + if (!node_supports_image(node)) { + v4l2_disable_ioctl(&node->video_dev, + VIDIOC_ENUM_FRAMEINTERVALS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + cfe_err(cfe, "Unable to register video device %s\n", + vdev->name); + return ret; + } + + cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n", + vdev->name, id, vdev->num); + + /* + * Acquire a reference to cfe, which will be released when the video + * device will be unregistered and userspace will have closed all open + * file handles. + */ + cfe_get(cfe); + set_state(cfe, NODE_REGISTERED, id); + + return 0; +} + +static void cfe_unregister_nodes(struct cfe_device *cfe) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (check_state(cfe, NODE_REGISTERED, i)) { + clear_state(cfe, NODE_REGISTERED, i); + video_unregister_device(&node->video_dev); + } + } +} + +static int cfe_link_node_pads(struct cfe_device *cfe) +{ + struct media_pad *remote_pad; + int ret; + + /* Source -> CSI2 */ + + ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd, + &cfe->csi2.pad[CSI2_PAD_SINK], + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + if (ret) { + cfe_err(cfe, "Failed to create links to the source: %d\n", ret); + return ret; + } + + remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]); + if (IS_ERR(remote_pad)) { + ret = PTR_ERR(remote_pad); + cfe_err(cfe, "Failed to get unique remote source pad: %d\n", + ret); + return ret; + } + + cfe->source_pad = remote_pad->index; + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (!check_state(cfe, NODE_REGISTERED, i)) + continue; + + /* CSI2 channel # -> /dev/video# */ + ret = media_create_pad_link(&cfe->csi2.sd.entity, + node_desc[i].link_pad, + &node->video_dev.entity, 0, 0); + if (ret) + return ret; + + if (node_supports_image(node)) { + /* CSI2 channel # -> FE Input */ + ret = media_create_pad_link(&cfe->csi2.sd.entity, + node_desc[i].link_pad, + &cfe->fe.sd.entity, + FE_STREAM_PAD, 0); + if (ret) + return ret; + } + } + + for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + struct media_entity *src, *dst; + unsigned int src_pad, dst_pad; + + if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) { + /* FE -> /dev/video# */ + src = &cfe->fe.sd.entity; + src_pad = node_desc[i].link_pad; + dst = &node->video_dev.entity; + dst_pad = 0; + } else { + /* /dev/video# -> FE */ + dst = &cfe->fe.sd.entity; + dst_pad = node_desc[i].link_pad; + src = &node->video_dev.entity; + src_pad = 0; + } + + ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0); + if (ret) + return ret; + } + + return 0; +} + +static int cfe_probe_complete(struct cfe_device *cfe) +{ + int ret; + + cfe->v4l2_dev.notify = cfe_notify; + + for (unsigned int i = 0; i < NUM_NODES; i++) { + ret = cfe_register_node(cfe, i); + if (ret) { + cfe_err(cfe, "Unable to register video node %u.\n", i); + goto unregister; + } + } + + ret = cfe_link_node_pads(cfe); + if (ret) { + cfe_err(cfe, "Unable to link node pads.\n"); + goto unregister; + } + + ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev); + if (ret) { + cfe_err(cfe, "Unable to register subdev nodes.\n"); + goto unregister; + } + + return 0; + +unregister: + cfe_unregister_nodes(cfe); + return ret; +} + +static int cfe_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); + + if (cfe->source_sd) { + cfe_err(cfe, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + cfe->source_sd = subdev; + + cfe_dbg(cfe, "Using source %s for capture\n", subdev->name); + + return 0; +} + +static int cfe_async_complete(struct v4l2_async_notifier *notifier) +{ + struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); + + return cfe_probe_complete(cfe); +} + +static const struct v4l2_async_notifier_operations cfe_async_ops = { + .bound = cfe_async_bound, + .complete = cfe_async_complete, +}; + +static int cfe_register_async_nf(struct cfe_device *cfe) +{ + struct platform_device *pdev = cfe->pdev; + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct fwnode_handle *local_ep_fwnode; + struct v4l2_async_connection *asd; + int ret; + + local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0, + 0, 0); + if (!local_ep_fwnode) { + cfe_err(cfe, "Failed to find local endpoint fwnode\n"); + return -ENODEV; + } + + /* Parse the local endpoint and validate its configuration. */ + ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep); + if (ret) { + cfe_err(cfe, "Failed to find remote endpoint fwnode\n"); + goto err_put_local_fwnode; + } + + for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; + lane++) { + if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { + cfe_err(cfe, "Data lanes reordering not supported\n"); + ret = -EINVAL; + goto err_put_local_fwnode; + } + } + + cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes; + cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags; + + /* Initialize and register the async notifier. */ + v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev); + cfe->notifier.ops = &cfe_async_ops; + + asd = v4l2_async_nf_add_fwnode_remote(&cfe->notifier, local_ep_fwnode, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + cfe_err(cfe, "Error adding subdevice: %d\n", ret); + goto err_put_local_fwnode; + } + + ret = v4l2_async_nf_register(&cfe->notifier); + if (ret) { + cfe_err(cfe, "Error registering async notifier: %d\n", ret); + goto err_nf_cleanup; + } + + fwnode_handle_put(local_ep_fwnode); + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(&cfe->notifier); +err_put_local_fwnode: + fwnode_handle_put(local_ep_fwnode); + + return ret; +} + +static int cfe_probe(struct platform_device *pdev) +{ + struct cfe_device *cfe; + char debugfs_name[32]; + int ret; + + cfe = kzalloc(sizeof(*cfe), GFP_KERNEL); + if (!cfe) + return -ENOMEM; + + platform_set_drvdata(pdev, cfe); + + kref_init(&cfe->kref); + cfe->pdev = pdev; + cfe->fe_csi2_channel = -1; + spin_lock_init(&cfe->state_lock); + + cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(cfe->csi2.base)) { + dev_err(&pdev->dev, "Failed to get dma io block\n"); + ret = PTR_ERR(cfe->csi2.base); + goto err_cfe_put; + } + + cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(cfe->csi2.dphy.base)) { + dev_err(&pdev->dev, "Failed to get host io block\n"); + ret = PTR_ERR(cfe->csi2.dphy.base); + goto err_cfe_put; + } + + cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(cfe->mipi_cfg_base)) { + dev_err(&pdev->dev, "Failed to get mipi cfg io block\n"); + ret = PTR_ERR(cfe->mipi_cfg_base); + goto err_cfe_put; + } + + cfe->fe.base = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(cfe->fe.base)) { + dev_err(&pdev->dev, "Failed to get pisp fe io block\n"); + ret = PTR_ERR(cfe->fe.base); + goto err_cfe_put; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + ret = -EINVAL; + goto err_cfe_put; + } + + ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + ret = -EINVAL; + goto err_cfe_put; + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(&pdev->dev, "DMA enable failed\n"); + goto err_cfe_put; + } + + ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, UINT_MAX); + if (ret) + goto err_cfe_put; + + /* TODO: Enable clock only when running. */ + cfe->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(cfe->clk)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk), + "clock not found\n"); + goto err_cfe_put; + } + + cfe->mdev.dev = &pdev->dev; + cfe->mdev.ops = &cfe_media_device_ops; + strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model)); + strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial)); + snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s", + dev_name(&pdev->dev)); + + media_device_init(&cfe->mdev); + + cfe->v4l2_dev.mdev = &cfe->mdev; + + ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev); + if (ret) { + cfe_err(cfe, "Unable to register v4l2 device.\n"); + goto err_cfe_put; + } + + snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s", + dev_name(&pdev->dev)); + cfe->debugfs = debugfs_create_dir(debugfs_name, NULL); + debugfs_create_file("regs", 0440, cfe->debugfs, cfe, + &mipi_cfg_regs_fops); + + /* Enable the block power domain */ + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_resume_and_get(&cfe->pdev->dev); + if (ret) + goto err_runtime_disable; + + cfe->csi2.v4l2_dev = &cfe->v4l2_dev; + ret = csi2_init(&cfe->csi2, cfe->debugfs); + if (ret) { + cfe_err(cfe, "Failed to init csi2 (%d)\n", ret); + goto err_runtime_put; + } + + cfe->fe.v4l2_dev = &cfe->v4l2_dev; + ret = pisp_fe_init(&cfe->fe, cfe->debugfs); + if (ret) { + cfe_err(cfe, "Failed to init pisp fe (%d)\n", ret); + goto err_csi2_uninit; + } + + cfe->mdev.hw_revision = cfe->fe.hw_revision; + ret = media_device_register(&cfe->mdev); + if (ret < 0) { + cfe_err(cfe, "Unable to register media-controller device.\n"); + goto err_pisp_fe_uninit; + } + + ret = cfe_register_async_nf(cfe); + if (ret) { + cfe_err(cfe, "Failed to connect subdevs\n"); + goto err_media_unregister; + } + + pm_runtime_put(&cfe->pdev->dev); + + return 0; + +err_media_unregister: + media_device_unregister(&cfe->mdev); +err_pisp_fe_uninit: + pisp_fe_uninit(&cfe->fe); +err_csi2_uninit: + csi2_uninit(&cfe->csi2); +err_runtime_put: + pm_runtime_put(&cfe->pdev->dev); +err_runtime_disable: + pm_runtime_disable(&pdev->dev); + debugfs_remove(cfe->debugfs); + v4l2_device_unregister(&cfe->v4l2_dev); +err_cfe_put: + cfe_put(cfe); + + return ret; +} + +static void cfe_remove(struct platform_device *pdev) +{ + struct cfe_device *cfe = platform_get_drvdata(pdev); + + debugfs_remove(cfe->debugfs); + + v4l2_async_nf_unregister(&cfe->notifier); + v4l2_async_nf_cleanup(&cfe->notifier); + + media_device_unregister(&cfe->mdev); + cfe_unregister_nodes(cfe); + + pisp_fe_uninit(&cfe->fe); + csi2_uninit(&cfe->csi2); + + pm_runtime_disable(&pdev->dev); + + v4l2_device_unregister(&cfe->v4l2_dev); + + cfe_put(cfe); +} + +static int cfe_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cfe_device *cfe = platform_get_drvdata(pdev); + + clk_disable_unprepare(cfe->clk); + + return 0; +} + +static int cfe_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cfe_device *cfe = platform_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(cfe->clk); + if (ret) { + dev_err(dev, "Unable to enable clock\n"); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops cfe_pm_ops = { + SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id cfe_of_match[] = { + { .compatible = "raspberrypi,rp1-cfe" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cfe_of_match); + +static struct platform_driver cfe_driver = { + .probe = cfe_probe, + .remove = cfe_remove, + .driver = { + .name = CFE_MODULE_NAME, + .of_match_table = cfe_of_match, + .pm = &cfe_pm_ops, + }, +}; + +module_platform_driver(cfe_driver); + +MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); +MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CFE_VERSION); diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h new file mode 100644 index 000000000000..c63cc314be3c --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RP1 CFE Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ +#ifndef _RP1_CFE_ +#define _RP1_CFE_ + +#include <linux/media-bus-format.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +extern bool cfe_debug_verbose; + +enum cfe_remap_types { + CFE_REMAP_16BIT, + CFE_REMAP_COMPRESSED, + CFE_NUM_REMAP, +}; + +#define CFE_FORMAT_FLAG_META_OUT BIT(0) +#define CFE_FORMAT_FLAG_META_CAP BIT(1) +#define CFE_FORMAT_FLAG_FE_OUT BIT(2) + +struct cfe_fmt { + u32 fourcc; + u32 code; + u8 depth; + u8 csi_dt; + u32 remap[CFE_NUM_REMAP]; + u32 flags; +}; + +extern const struct v4l2_mbus_framefmt cfe_default_format; + +const struct cfe_fmt *find_format_by_code(u32 code); +const struct cfe_fmt *find_format_by_pix(u32 pixelformat); +u32 cfe_find_16bit_code(u32 code); +u32 cfe_find_compressed_code(u32 code); + +#endif diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c new file mode 100644 index 000000000000..35c2ab1e2cd4 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RP1 CSI-2 Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#include <linux/delay.h> +#include <linux/moduleparam.h> +#include <linux/pm_runtime.h> +#include <linux/seq_file.h> + +#include <media/videobuf2-dma-contig.h> + +#include "cfe.h" +#include "csi2.h" + +#include "cfe-trace.h" + +static bool csi2_track_errors; +module_param_named(track_csi2_errors, csi2_track_errors, bool, 0); +MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors"); + +#define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg) +#define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg) + +/* CSI2-DMA registers */ +#define CSI2_STATUS 0x000 +#define CSI2_QOS 0x004 +#define CSI2_DISCARDS_OVERFLOW 0x008 +#define CSI2_DISCARDS_INACTIVE 0x00c +#define CSI2_DISCARDS_UNMATCHED 0x010 +#define CSI2_DISCARDS_LEN_LIMIT 0x014 + +#define CSI2_DISCARDS_AMOUNT_SHIFT 0 +#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0) +#define CSI2_DISCARDS_DT_SHIFT 24 +#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24) +#define CSI2_DISCARDS_VC_SHIFT 30 +#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30) + +#define CSI2_LLEV_PANICS 0x018 +#define CSI2_ULEV_PANICS 0x01c +#define CSI2_IRQ_MASK 0x020 +#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0) +#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1) +#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2) +#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3) +#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4) +#define CSI2_IRQ_MASK_IRQ_ALL \ + (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \ + CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \ + CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \ + CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE) + +#define CSI2_CTRL 0x024 +#define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28) +#define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c) +#define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c) +#define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30) +#define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34) +#define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38) +#define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40) +#define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44) +#define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48) + +/* CSI2_STATUS */ +#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) +#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) +#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) +#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) +#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) +#define CSI2_STATUS_IRQ_CH_MASK(x) \ + (CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \ + CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \ + CSI2_STATUS_IRQ_LE_ACK(x)) +#define CSI2_STATUS_IRQ_OVERFLOW BIT(20) +#define CSI2_STATUS_IRQ_DISCARD_OVERFLOW BIT(21) +#define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT BIT(22) +#define CSI2_STATUS_IRQ_DISCARD_UNMATCHED BIT(23) +#define CSI2_STATUS_IRQ_DISCARD_INACTIVE BIT(24) + +/* CSI2_CTRL */ +#define CSI2_CTRL_EOP_IS_EOL BIT(0) + +/* CSI2_CH_CTRL */ +#define CSI2_CH_CTRL_DMA_EN BIT(0) +#define CSI2_CH_CTRL_FORCE BIT(3) +#define CSI2_CH_CTRL_AUTO_ARM BIT(4) +#define CSI2_CH_CTRL_IRQ_EN_FS BIT(13) +#define CSI2_CH_CTRL_IRQ_EN_FE BIT(14) +#define CSI2_CH_CTRL_IRQ_EN_FE_ACK BIT(15) +#define CSI2_CH_CTRL_IRQ_EN_LE BIT(16) +#define CSI2_CH_CTRL_IRQ_EN_LE_ACK BIT(17) +#define CSI2_CH_CTRL_FLUSH_FE BIT(28) +#define CSI2_CH_CTRL_PACK_LINE BIT(29) +#define CSI2_CH_CTRL_PACK_BYTES BIT(30) +#define CSI2_CH_CTRL_CH_MODE_MASK GENMASK(2, 1) +#define CSI2_CH_CTRL_VC_MASK GENMASK(6, 5) +#define CSI2_CH_CTRL_DT_MASK GENMASK(12, 7) +#define CSI2_CH_CTRL_LC_MASK GENMASK(27, 18) + +/* CHx_COMPRESSION_CONTROL */ +#define CSI2_CH_COMP_CTRL_OFFSET_MASK GENMASK(15, 0) +#define CSI2_CH_COMP_CTRL_SHIFT_MASK GENMASK(19, 16) +#define CSI2_CH_COMP_CTRL_MODE_MASK GENMASK(25, 24) + +static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset) +{ + return readl(csi2->base + offset); +} + +static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val) +{ + writel(val, csi2->base + offset); +} + +static inline void set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +static int csi2_regs_show(struct seq_file *s, void *data) +{ + struct csi2_device *csi2 = s->private; + int ret; + + ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev); + if (ret) + return ret; + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg)) +#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \ + csi2_reg_read(csi2, reg(idx))) + + DUMP(CSI2_STATUS); + DUMP(CSI2_DISCARDS_OVERFLOW); + DUMP(CSI2_DISCARDS_INACTIVE); + DUMP(CSI2_DISCARDS_UNMATCHED); + DUMP(CSI2_DISCARDS_LEN_LIMIT); + DUMP(CSI2_LLEV_PANICS); + DUMP(CSI2_ULEV_PANICS); + DUMP(CSI2_IRQ_MASK); + DUMP(CSI2_CTRL); + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) { + DUMP_CH(i, CSI2_CH_CTRL); + DUMP_CH(i, CSI2_CH_ADDR0); + DUMP_CH(i, CSI2_CH_ADDR1); + DUMP_CH(i, CSI2_CH_STRIDE); + DUMP_CH(i, CSI2_CH_LENGTH); + DUMP_CH(i, CSI2_CH_DEBUG); + DUMP_CH(i, CSI2_CH_FRAME_SIZE); + DUMP_CH(i, CSI2_CH_COMP_CTRL); + DUMP_CH(i, CSI2_CH_FE_FRAME_ID); + } + +#undef DUMP +#undef DUMP_CH + + pm_runtime_put(csi2->v4l2_dev->dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(csi2_regs); + +static int csi2_errors_show(struct seq_file *s, void *data) +{ + struct csi2_device *csi2 = s->private; + unsigned long flags; + u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; + u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; + u32 overflows; + + spin_lock_irqsave(&csi2->errors_lock, flags); + + memcpy(discards_table, csi2->discards_table, sizeof(discards_table)); + memcpy(discards_dt_table, csi2->discards_dt_table, + sizeof(discards_dt_table)); + overflows = csi2->overflows; + + csi2->overflows = 0; + memset(csi2->discards_table, 0, sizeof(discards_table)); + memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table)); + + spin_unlock_irqrestore(&csi2->errors_lock, flags); + + seq_printf(s, "Overflows %u\n", overflows); + seq_puts(s, "Discards:\n"); + seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n"); + + for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) { + seq_printf(s, "%u %10u %10u %10u %10u\n", vc, + discards_table[vc][DISCARDS_TABLE_OVERFLOW], + discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT], + discards_table[vc][DISCARDS_TABLE_UNMATCHED], + discards_table[vc][DISCARDS_TABLE_INACTIVE]); + } + + seq_printf(s, "Last DT %10u %10u %10u %10u\n", + discards_dt_table[DISCARDS_TABLE_OVERFLOW], + discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT], + discards_dt_table[DISCARDS_TABLE_UNMATCHED], + discards_dt_table[DISCARDS_TABLE_INACTIVE]); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(csi2_errors); + +static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status) +{ + spin_lock(&csi2->errors_lock); + + if (status & CSI2_STATUS_IRQ_OVERFLOW) + csi2->overflows++; + + for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) { + static const u32 discard_bits[] = { + CSI2_STATUS_IRQ_DISCARD_OVERFLOW, + CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT, + CSI2_STATUS_IRQ_DISCARD_UNMATCHED, + CSI2_STATUS_IRQ_DISCARD_INACTIVE, + }; + static const u8 discard_regs[] = { + CSI2_DISCARDS_OVERFLOW, + CSI2_DISCARDS_LEN_LIMIT, + CSI2_DISCARDS_UNMATCHED, + CSI2_DISCARDS_INACTIVE, + }; + u32 amount; + u8 dt, vc; + u32 v; + + if (!(status & discard_bits[i])) + continue; + + v = csi2_reg_read(csi2, discard_regs[i]); + csi2_reg_write(csi2, discard_regs[i], 0); + + amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >> + CSI2_DISCARDS_AMOUNT_SHIFT; + dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT; + vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT; + + csi2->discards_table[vc][i] += amount; + csi2->discards_dt_table[i] = dt; + } + + spin_unlock(&csi2->errors_lock); +} + +void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof) +{ + u32 status; + + status = csi2_reg_read(csi2, CSI2_STATUS); + + /* Write value back to clear the interrupts */ + csi2_reg_write(csi2, CSI2_STATUS, status); + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { + u32 dbg; + + if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0) + continue; + + dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i)); + + trace_csi2_irq(i, status, dbg); + + sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i)); + eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i)); + } + + if (csi2_track_errors) + csi2_isr_handle_errors(csi2, status); +} + +void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, + dma_addr_t dmaaddr, unsigned int stride, unsigned int size) +{ + u64 addr = dmaaddr; + /* + * ADDRESS0 must be written last as it triggers the double buffering + * mechanism for all buffer registers within the hardware. + */ + addr >>= 4; + csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4); + csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4); + csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32); + csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff); +} + +void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, + enum csi2_compression_mode mode, unsigned int shift, + unsigned int offset) +{ + u32 compression = 0; + + set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset); + set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift); + set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode); + csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression); +} + +void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, + enum csi2_mode mode, bool auto_arm, bool pack_bytes, + unsigned int width, unsigned int height, + u8 vc, u8 dt) +{ + u32 ctrl; + + csi2_dbg(csi2, "%s [%u]\n", __func__, channel); + + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0); + csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0); + csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel)); + + /* Enable channel and FS/FE interrupts. */ + ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS | + CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE; + /* PACK_BYTES ensures no striding for embedded data. */ + if (pack_bytes) + ctrl |= CSI2_CH_CTRL_PACK_BYTES; + + if (auto_arm) + ctrl |= CSI2_CH_CTRL_AUTO_ARM; + + if (width && height) { + set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK); + csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), + (height << 16) | width); + } else { + set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK); + csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0); + } + + set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK); + set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK); + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl); + csi2->num_lines[channel] = height; +} + +void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel) +{ + csi2_dbg(csi2, "%s [%u]\n", __func__, channel); + + /* Channel disable. Use FORCE to allow stopping mid-frame. */ + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE); + /* Latch the above change by writing to the ADDR0 register. */ + csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); + /* Write this again, the HW needs it! */ + csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); +} + +void csi2_open_rx(struct csi2_device *csi2) +{ + csi2_reg_write(csi2, CSI2_IRQ_MASK, + csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0); + + dphy_start(&csi2->dphy); + + csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL); +} + +void csi2_close_rx(struct csi2_device *csi2) +{ + dphy_stop(&csi2->dphy); + + csi2_reg_write(csi2, CSI2_IRQ_MASK, 0); +} + +static int csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { { + .sink_pad = CSI2_PAD_SINK, + .sink_stream = 0, + .source_pad = CSI2_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + int ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, + &cfe_default_format); + if (ret) + return ret; + + return 0; +} + +static int csi2_pad_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + if (format->pad == CSI2_PAD_SINK) { + /* Store the sink format and propagate it to the source. */ + + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt) { + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10); + format->format.code = cfe_fmt->code; + } + + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, + format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + format->format.field = V4L2_FIELD_NONE; + + *fmt = format->format; + } else { + /* Only allow changing the source pad mbus code. */ + + struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; + u32 sink_code; + u32 code; + + sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state, + format->pad, + format->stream); + if (!sink_fmt) + return -EINVAL; + + source_fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + if (!source_fmt) + return -EINVAL; + + sink_code = sink_fmt->code; + code = format->format.code; + + /* + * Only allow changing the mbus code to: + * - The sink's mbus code + * - The 16-bit version of the sink's mbus code + * - The compressed version of the sink's mbus code + */ + if (code == sink_code || + code == cfe_find_16bit_code(sink_code) || + code == cfe_find_compressed_code(sink_code)) + source_fmt->code = code; + + format->format.code = source_fmt->code; + } + + return 0; +} + +static int csi2_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + if (ret) + return ret; + + /* Only stream ID 0 allowed on source pads */ + for (unsigned int i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream != 0) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, + &cfe_default_format); + if (ret) + return ret; + + return 0; +} + +static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2_pad_set_fmt, + .set_routing = csi2_set_routing, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct media_entity_operations csi2_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static const struct v4l2_subdev_ops csi2_subdev_ops = { + .pad = &csi2_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .init_state = csi2_init_state, +}; + +int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) +{ + unsigned int ret; + + spin_lock_init(&csi2->errors_lock); + + csi2->dphy.dev = csi2->v4l2_dev->dev; + dphy_probe(&csi2->dphy); + + debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops); + + if (csi2_track_errors) + debugfs_create_file("csi2_errors", 0440, debugfs, csi2, + &csi2_errors_fops); + + csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (unsigned int i = CSI2_PAD_FIRST_SOURCE; + i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++) + csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad), + csi2->pad); + if (ret) + return ret; + + /* Initialize subdev */ + v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops); + csi2->sd.internal_ops = &csi2_internal_ops; + csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csi2->sd.entity.ops = &csi2_entity_ops; + csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + csi2->sd.owner = THIS_MODULE; + snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2"); + + ret = v4l2_subdev_init_finalize(&csi2->sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd); + if (ret) { + csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret); + goto err_subdev_cleanup; + } + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(&csi2->sd); +err_entity_cleanup: + media_entity_cleanup(&csi2->sd.entity); + + return ret; +} + +void csi2_uninit(struct csi2_device *csi2) +{ + v4l2_device_unregister_subdev(&csi2->sd); + v4l2_subdev_cleanup(&csi2->sd); + media_entity_cleanup(&csi2->sd.entity); +} diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h new file mode 100644 index 000000000000..a8ee5de565fb --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RP1 CSI-2 Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#ifndef _RP1_CSI2_ +#define _RP1_CSI2_ + +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/types.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "dphy.h" + +#define CSI2_NUM_CHANNELS 4 + +#define CSI2_PAD_SINK 0 +#define CSI2_PAD_FIRST_SOURCE 1 +#define CSI2_PAD_NUM_SOURCES 4 +#define CSI2_NUM_PADS 5 + +#define DISCARDS_TABLE_NUM_VCS 4 + +enum csi2_mode { + CSI2_MODE_NORMAL = 0, + CSI2_MODE_REMAP = 1, + CSI2_MODE_COMPRESSED = 2, + CSI2_MODE_FE_STREAMING = 3, +}; + +enum csi2_compression_mode { + CSI2_COMPRESSION_DELTA = 1, + CSI2_COMPRESSION_SIMPLE = 2, + CSI2_COMPRESSION_COMBINED = 3, +}; + +enum discards_table_index { + DISCARDS_TABLE_OVERFLOW = 0, + DISCARDS_TABLE_LENGTH_LIMIT, + DISCARDS_TABLE_UNMATCHED, + DISCARDS_TABLE_INACTIVE, + DISCARDS_TABLE_NUM_ENTRIES, +}; + +struct csi2_device { + /* Parent V4l2 device */ + struct v4l2_device *v4l2_dev; + + void __iomem *base; + + struct dphy_data dphy; + + enum v4l2_mbus_type bus_type; + unsigned int bus_flags; + unsigned int num_lines[CSI2_NUM_CHANNELS]; + + struct media_pad pad[CSI2_NUM_PADS]; + struct v4l2_subdev sd; + + /* lock for csi2 errors counters */ + spinlock_t errors_lock; + u32 overflows; + u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; + u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; +}; + +void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof); +void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, + dma_addr_t dmaaddr, unsigned int stride, + unsigned int size); +void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, + enum csi2_compression_mode mode, unsigned int shift, + unsigned int offset); +void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, + enum csi2_mode mode, bool auto_arm, + bool pack_bytes, unsigned int width, + unsigned int height, u8 vc, u8 dt); +void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel); +void csi2_open_rx(struct csi2_device *csi2); +void csi2_close_rx(struct csi2_device *csi2); +int csi2_init(struct csi2_device *csi2, struct dentry *debugfs); +void csi2_uninit(struct csi2_device *csi2); + +#endif diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c new file mode 100644 index 000000000000..b443f0f56ddc --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RP1 CSI-2 Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#include <linux/delay.h> +#include <linux/pm_runtime.h> + +#include "dphy.h" + +#define dphy_dbg(dphy, fmt, arg...) dev_dbg((dphy)->dev, fmt, ##arg) +#define dphy_err(dphy, fmt, arg...) dev_err((dphy)->dev, fmt, ##arg) + +/* DW dphy Host registers */ +#define DPHY_VERSION 0x000 +#define DPHY_N_LANES 0x004 +#define DPHY_RESETN 0x008 +#define DPHY_PHY_SHUTDOWNZ 0x040 +#define DPHY_PHY_RSTZ 0x044 +#define DPHY_PHY_RX 0x048 +#define DPHY_PHY_STOPSTATE 0x04c +#define DPHY_PHY_TST_CTRL0 0x050 +#define DPHY_PHY_TST_CTRL1 0x054 +#define DPHY_PHY2_TST_CTRL0 0x058 +#define DPHY_PHY2_TST_CTRL1 0x05c + +/* DW dphy Host Transactions */ +#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44 +#define DPHY_PLL_INPUT_DIV_OFFSET 0x17 +#define DPHY_PLL_LOOP_DIV_OFFSET 0x18 +#define DPHY_PLL_DIV_CTRL_OFFSET 0x19 + +static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset) +{ + return readl(dphy->base + offset); +} + +static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data) +{ + writel(data, dphy->base + offset); +} + +static void set_tstclr(struct dphy_data *dphy, u32 val) +{ + u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~1) | val); +} + +static void set_tstclk(struct dphy_data *dphy, u32 val) +{ + u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1)); +} + +static uint8_t get_tstdout(struct dphy_data *dphy) +{ + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); + + return ((ctrl1 >> 8) & 0xff); +} + +static void set_testen(struct dphy_data *dphy, u32 val) +{ + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, + (ctrl1 & ~(1 << 16)) | (val << 16)); +} + +static void set_testdin(struct dphy_data *dphy, u32 val) +{ + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~0xff) | val); +} + +static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code, + uint8_t test_data) +{ + /* See page 101 of the MIPI DPHY databook. */ + set_tstclk(dphy, 1); + set_testen(dphy, 0); + set_testdin(dphy, test_code); + set_testen(dphy, 1); + set_tstclk(dphy, 0); + set_testen(dphy, 0); + set_testdin(dphy, test_data); + set_tstclk(dphy, 1); + return get_tstdout(dphy); +} + +static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps) +{ + /* See Table 5-1 on page 65 of dphy databook */ + static const u16 hsfreqrange_table[][2] = { + { 89, 0b000000 }, { 99, 0b010000 }, { 109, 0b100000 }, + { 129, 0b000001 }, { 139, 0b010001 }, { 149, 0b100001 }, + { 169, 0b000010 }, { 179, 0b010010 }, { 199, 0b100010 }, + { 219, 0b000011 }, { 239, 0b010011 }, { 249, 0b100011 }, + { 269, 0b000100 }, { 299, 0b010100 }, { 329, 0b000101 }, + { 359, 0b010101 }, { 399, 0b100101 }, { 449, 0b000110 }, + { 499, 0b010110 }, { 549, 0b000111 }, { 599, 0b010111 }, + { 649, 0b001000 }, { 699, 0b011000 }, { 749, 0b001001 }, + { 799, 0b011001 }, { 849, 0b101001 }, { 899, 0b111001 }, + { 949, 0b001010 }, { 999, 0b011010 }, { 1049, 0b101010 }, + { 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 }, + { 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 }, + { 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 }, + }; + unsigned int i; + + if (mbps < 80 || mbps > 1500) + dphy_err(dphy, "DPHY: Datarate %u Mbps out of range\n", mbps); + + for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) { + if (mbps <= hsfreqrange_table[i][0]) + break; + } + + dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET, + hsfreqrange_table[i][1] << 1); +} + +static void dphy_init(struct dphy_data *dphy) +{ + dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 0); + dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 0); + set_tstclk(dphy, 1); + set_testen(dphy, 0); + set_tstclr(dphy, 1); + usleep_range(15, 20); + set_tstclr(dphy, 0); + usleep_range(15, 20); + + dphy_set_hsfreqrange(dphy, dphy->dphy_rate); + + usleep_range(5, 10); + dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 1); + usleep_range(5, 10); + dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 1); +} + +void dphy_start(struct dphy_data *dphy) +{ + dphy_dbg(dphy, "%s: Link rate %u Mbps, %u data lanes\n", __func__, + dphy->dphy_rate, dphy->active_lanes); + + dw_csi2_host_write(dphy, DPHY_N_LANES, (dphy->active_lanes - 1)); + dphy_init(dphy); + dw_csi2_host_write(dphy, DPHY_RESETN, 0xffffffff); + usleep_range(10, 50); +} + +void dphy_stop(struct dphy_data *dphy) +{ + dphy_dbg(dphy, "%s\n", __func__); + + /* Set only one lane (lane 0) as active (ON) */ + dw_csi2_host_write(dphy, DPHY_N_LANES, 0); + dw_csi2_host_write(dphy, DPHY_RESETN, 0); +} + +void dphy_probe(struct dphy_data *dphy) +{ + u32 host_ver; + u8 host_ver_major, host_ver_minor; + + host_ver = dw_csi2_host_read(dphy, DPHY_VERSION); + host_ver_major = (u8)((host_ver >> 24) - '0'); + host_ver_minor = (u8)((host_ver >> 16) - '0'); + host_ver_minor = host_ver_minor * 10; + host_ver_minor += (u8)((host_ver >> 8) - '0'); + + dphy_dbg(dphy, "DW dphy Host HW v%u.%u\n", host_ver_major, + host_ver_minor); +} diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h new file mode 100644 index 000000000000..84fa370957cc --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#ifndef _RP1_DPHY_ +#define _RP1_DPHY_ + +#include <linux/io.h> +#include <linux/types.h> + +struct dphy_data { + struct device *dev; + + void __iomem *base; + + u32 dphy_rate; + u32 max_lanes; + u32 active_lanes; +}; + +void dphy_probe(struct dphy_data *dphy); +void dphy_start(struct dphy_data *dphy); +void dphy_stop(struct dphy_data *dphy); + +#endif diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c new file mode 100644 index 000000000000..05762b1be2bc --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PiSP Front End Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/moduleparam.h> +#include <linux/pm_runtime.h> +#include <linux/seq_file.h> + +#include <media/videobuf2-dma-contig.h> + +#include "cfe.h" +#include "pisp-fe.h" + +#include "cfe-trace.h" + +#define FE_VERSION 0x000 +#define FE_CONTROL 0x004 +#define FE_STATUS 0x008 +#define FE_FRAME_STATUS 0x00c +#define FE_ERROR_STATUS 0x010 +#define FE_OUTPUT_STATUS 0x014 +#define FE_INT_EN 0x018 +#define FE_INT_STATUS 0x01c + +/* CONTROL */ +#define FE_CONTROL_QUEUE BIT(0) +#define FE_CONTROL_ABORT BIT(1) +#define FE_CONTROL_RESET BIT(2) +#define FE_CONTROL_LATCH_REGS BIT(3) + +/* INT_EN / INT_STATUS */ +#define FE_INT_EOF BIT(0) +#define FE_INT_SOF BIT(1) +#define FE_INT_LINES0 BIT(8) +#define FE_INT_LINES1 BIT(9) +#define FE_INT_STATS BIT(16) +#define FE_INT_QREADY BIT(24) + +/* STATUS */ +#define FE_STATUS_QUEUED BIT(0) +#define FE_STATUS_WAITING BIT(1) +#define FE_STATUS_ACTIVE BIT(2) + +#define PISP_FE_CONFIG_BASE_OFFSET 0x0040 + +#define PISP_FE_ENABLE_STATS_CLUSTER \ + (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \ + PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \ + PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \ + PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS) + +#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \ + ((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \ + PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i))) + +struct pisp_fe_config_param { + u32 dirty_flags; + u32 dirty_flags_extra; + size_t offset; + size_t size; +}; + +static const struct pisp_fe_config_param pisp_fe_config_map[] = { + /* *_dirty_flag_extra types */ + { 0, PISP_FE_DIRTY_GLOBAL, + offsetof(struct pisp_fe_config, global), + sizeof(struct pisp_fe_global_config) }, + { 0, PISP_FE_DIRTY_FLOATING, + offsetof(struct pisp_fe_config, floating_stats), + sizeof(struct pisp_fe_floating_stats_config) }, + { 0, PISP_FE_DIRTY_OUTPUT_AXI, + offsetof(struct pisp_fe_config, output_axi), + sizeof(struct pisp_fe_output_axi_config) }, + /* *_dirty_flag types */ + { PISP_FE_ENABLE_INPUT, 0, + offsetof(struct pisp_fe_config, input), + sizeof(struct pisp_fe_input_config) }, + { PISP_FE_ENABLE_DECOMPRESS, 0, + offsetof(struct pisp_fe_config, decompress), + sizeof(struct pisp_decompress_config) }, + { PISP_FE_ENABLE_DECOMPAND, 0, + offsetof(struct pisp_fe_config, decompand), + sizeof(struct pisp_fe_decompand_config) }, + { PISP_FE_ENABLE_BLA, 0, + offsetof(struct pisp_fe_config, bla), + sizeof(struct pisp_bla_config) }, + { PISP_FE_ENABLE_DPC, 0, + offsetof(struct pisp_fe_config, dpc), + sizeof(struct pisp_fe_dpc_config) }, + { PISP_FE_ENABLE_STATS_CROP, 0, + offsetof(struct pisp_fe_config, stats_crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_BLC, 0, + offsetof(struct pisp_fe_config, blc), + sizeof(struct pisp_bla_config) }, + { PISP_FE_ENABLE_CDAF_STATS, 0, + offsetof(struct pisp_fe_config, cdaf_stats), + sizeof(struct pisp_fe_cdaf_stats_config) }, + { PISP_FE_ENABLE_AWB_STATS, 0, + offsetof(struct pisp_fe_config, awb_stats), + sizeof(struct pisp_fe_awb_stats_config) }, + { PISP_FE_ENABLE_RGBY, 0, + offsetof(struct pisp_fe_config, rgby), + sizeof(struct pisp_fe_rgby_config) }, + { PISP_FE_ENABLE_LSC, 0, + offsetof(struct pisp_fe_config, lsc), + sizeof(struct pisp_fe_lsc_config) }, + { PISP_FE_ENABLE_AGC_STATS, 0, + offsetof(struct pisp_fe_config, agc_stats), + sizeof(struct pisp_agc_statistics) }, + { PISP_FE_ENABLE_CROP0, 0, + offsetof(struct pisp_fe_config, ch[0].crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_DOWNSCALE0, 0, + offsetof(struct pisp_fe_config, ch[0].downscale), + sizeof(struct pisp_fe_downscale_config) }, + { PISP_FE_ENABLE_COMPRESS0, 0, + offsetof(struct pisp_fe_config, ch[0].compress), + sizeof(struct pisp_compress_config) }, + { PISP_FE_ENABLE_OUTPUT0, 0, + offsetof(struct pisp_fe_config, ch[0].output), + sizeof(struct pisp_fe_output_config) }, + { PISP_FE_ENABLE_CROP1, 0, + offsetof(struct pisp_fe_config, ch[1].crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_DOWNSCALE1, 0, + offsetof(struct pisp_fe_config, ch[1].downscale), + sizeof(struct pisp_fe_downscale_config) }, + { PISP_FE_ENABLE_COMPRESS1, 0, + offsetof(struct pisp_fe_config, ch[1].compress), + sizeof(struct pisp_compress_config) }, + { PISP_FE_ENABLE_OUTPUT1, 0, + offsetof(struct pisp_fe_config, ch[1].output), + sizeof(struct pisp_fe_output_config) }, +}; + +#define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg) + +static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset) +{ + return readl(fe->base + offset); +} + +static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset, + u32 val) +{ + writel(val, fe->base + offset); +} + +static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, + u32 offset, u32 val) +{ + writel_relaxed(val, fe->base + offset); +} + +static int pisp_fe_regs_show(struct seq_file *s, void *data) +{ + struct pisp_fe_device *fe = s->private; + int ret; + + ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev); + if (ret) + return ret; + + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg)) + DUMP(FE_VERSION); + DUMP(FE_CONTROL); + DUMP(FE_STATUS); + DUMP(FE_FRAME_STATUS); + DUMP(FE_ERROR_STATUS); + DUMP(FE_OUTPUT_STATUS); + DUMP(FE_INT_EN); + DUMP(FE_INT_STATUS); +#undef DUMP + + pm_runtime_put(fe->v4l2_dev->dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs); + +static void pisp_fe_config_write(struct pisp_fe_device *fe, + struct pisp_fe_config *config, + unsigned int start_offset, unsigned int size) +{ + const unsigned int max_offset = + offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]); + unsigned int end_offset; + u32 *cfg = (u32 *)config; + + start_offset = min(start_offset, max_offset); + end_offset = min(start_offset + size, max_offset); + + cfg += start_offset >> 2; + for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++) + pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i, + *cfg); +} + +void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) +{ + u32 status, int_status, out_status, frame_status, error_status; + + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); + status = pisp_fe_reg_read(fe, FE_STATUS); + out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS); + frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS); + error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS); + + int_status = pisp_fe_reg_read(fe, FE_INT_STATUS); + pisp_fe_reg_write(fe, FE_INT_STATUS, int_status); + + trace_fe_irq(status, out_status, frame_status, error_status, + int_status); + + /* We do not report interrupts for the input/stream pad. */ + for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) { + sof[i] = !!(int_status & FE_INT_SOF); + eof[i] = !!(int_status & FE_INT_EOF); + } +} + +static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, + unsigned int c, struct v4l2_format const *f) +{ + unsigned int wbytes; + + wbytes = cfg->ch[c].output.format.width; + if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK) + wbytes *= 2; + + /* Check output image dimensions are nonzero and not too big */ + if (cfg->ch[c].output.format.width < 2 || + cfg->ch[c].output.format.height < 2 || + cfg->ch[c].output.format.height > f->fmt.pix.height || + cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline || + wbytes > f->fmt.pix.bytesperline) + return false; + + /* Check for zero-sized crops, which could cause lockup */ + if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) && + ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) || + cfg->ch[c].crop.offset_y >= cfg->input.format.height || + cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2))) + return false; + + if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) && + (cfg->ch[c].downscale.output_width < 2 || + cfg->ch[c].downscale.output_height < 2)) + return false; + + return true; +} + +static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg) +{ + /* Check for zero-sized crop, which could cause lockup */ + return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) || + (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) && + cfg->stats_crop.offset_y < cfg->input.format.height && + cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2)); +} + +int pisp_fe_validate_config(struct pisp_fe_device *fe, + struct pisp_fe_config *cfg, + struct v4l2_format const *f0, + struct v4l2_format const *f1) +{ + /* + * Check the input is enabled, streaming and has nonzero size; + * to avoid cases where the hardware might lock up or try to + * read inputs from memory (which this driver doesn't support). + */ + if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) || + cfg->input.streaming != 1 || cfg->input.format.width < 2 || + cfg->input.format.height < 2) { + pisp_fe_err(fe, "%s: Input config not valid", __func__); + return -EINVAL; + } + + for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { + if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) { + if (cfg->global.enables & + PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) { + pisp_fe_err(fe, "%s: Output %u not valid", + __func__, i); + return -EINVAL; + } + continue; + } + + if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0)) + return -EINVAL; + } + + if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) && + !pisp_fe_validate_stats(cfg)) { + pisp_fe_err(fe, "%s: Stats config not valid", __func__); + return -EINVAL; + } + + return 0; +} + +void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, + struct pisp_fe_config *cfg) +{ + u64 addr; + u32 status; + + /* + * Check output buffers exist and outputs are correctly configured. + * If valid, set the buffer's DMA address; otherwise disable. + */ + for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { + struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i]; + + if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) + continue; + + addr = vb2_dma_contig_plane_dma_addr(buf, 0); + cfg->output_buffer[i].addr_lo = addr & 0xffffffff; + cfg->output_buffer[i].addr_hi = addr >> 32; + } + + if (vb2_bufs[FE_STATS_PAD]) { + addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0); + cfg->stats_buffer.addr_lo = addr & 0xffffffff; + cfg->stats_buffer.addr_hi = addr >> 32; + } + + /* Set up ILINES interrupts 3/4 of the way down each output */ + cfg->ch[0].output.ilines = + max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2); + cfg->ch[1].output.ilines = + max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2); + + /* + * The hardware must have consumed the previous config by now. + * This read of status also serves as a memory barrier before the + * sequence of relaxed writes which follow. + */ + status = pisp_fe_reg_read(fe, FE_STATUS); + if (WARN_ON(status & FE_STATUS_QUEUED)) + return; + + /* + * Unconditionally write buffers, global and input parameters. + * Write cropping and output parameters whenever they are enabled. + * Selectively write other parameters that have been marked as + * changed through the dirty flags. + */ + pisp_fe_config_write(fe, cfg, 0, + offsetof(struct pisp_fe_config, decompress)); + cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL; + cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT; + cfg->dirty_flags |= (cfg->global.enables & + (PISP_FE_ENABLE_STATS_CROP | + PISP_FE_ENABLE_OUTPUT_CLUSTER(0) | + PISP_FE_ENABLE_OUTPUT_CLUSTER(1))); + for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) { + const struct pisp_fe_config_param *p = &pisp_fe_config_map[i]; + + if (cfg->dirty_flags & p->dirty_flags || + cfg->dirty_flags_extra & p->dirty_flags_extra) + pisp_fe_config_write(fe, cfg, p->offset, p->size); + } + + /* This final non-relaxed write serves as a memory barrier */ + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE); +} + +void pisp_fe_start(struct pisp_fe_device *fe) +{ + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET); + pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); + pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | + FE_INT_LINES0 | FE_INT_LINES1); + fe->inframe_count = 0; +} + +void pisp_fe_stop(struct pisp_fe_device *fe) +{ + pisp_fe_reg_write(fe, FE_INT_EN, 0); + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT); + usleep_range(1000, 2000); + WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); + pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); +} + +static int pisp_fe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); + *fmt = cfe_default_format; + fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + + fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD); + fmt->code = MEDIA_BUS_FMT_FIXED; + fmt->width = sizeof(struct pisp_fe_config); + fmt->height = 1; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); + *fmt = cfe_default_format; + fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); + *fmt = cfe_default_format; + fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + + fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD); + fmt->code = MEDIA_BUS_FMT_FIXED; + fmt->width = sizeof(struct pisp_statistics); + fmt->height = 1; + + return 0; +} + +static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + const struct cfe_fmt *cfe_fmt; + + /* TODO: format propagation to source pads */ + /* TODO: format validation */ + + switch (format->pad) { + case FE_STREAM_PAD: + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); + + format->format.code = cfe_fmt->code; + format->format.field = V4L2_FIELD_NONE; + + fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); + *fmt = format->format; + + return 0; + + case FE_OUTPUT0_PAD: + case FE_OUTPUT1_PAD: { + /* + * TODO: we should allow scaling and cropping by allowing the + * user to set the size here. + */ + struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; + u32 sink_code; + u32 code; + + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); + + format->format.code = cfe_fmt->code; + + sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); + if (!sink_fmt) + return -EINVAL; + + source_fmt = v4l2_subdev_state_get_format(state, format->pad); + if (!source_fmt) + return -EINVAL; + + sink_code = sink_fmt->code; + code = format->format.code; + + /* + * If the source code from the user does not match the code in + * the sink pad, check that the source code matches the + * compressed version of the sink code. + */ + + if (code != sink_code && + code == cfe_find_compressed_code(sink_code)) + source_fmt->code = code; + + return 0; + } + + case FE_CONFIG_PAD: + case FE_STATS_PAD: + default: + return v4l2_subdev_get_fmt(sd, state, format); + } +} + +static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = pisp_fe_pad_set_fmt, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static int pisp_fe_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity); + struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd); + + pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + if (link->sink->index == FE_STREAM_PAD) + return v4l2_subdev_link_validate(link); + + if (link->sink->index == FE_CONFIG_PAD) + return 0; + + return -EINVAL; +} + +static const struct media_entity_operations pisp_fe_entity_ops = { + .link_validate = pisp_fe_link_validate, +}; + +static const struct v4l2_subdev_ops pisp_fe_subdev_ops = { + .pad = &pisp_fe_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = { + .init_state = pisp_fe_init_state, +}; + +int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) +{ + int ret; + + debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops); + + fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION); + pisp_fe_info(fe, "PiSP FE HW v%u.%u\n", + (fe->hw_revision >> 24) & 0xff, + (fe->hw_revision >> 20) & 0x0f); + + fe->pad[FE_STREAM_PAD].flags = + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK; + fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE; + fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE; + fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad), + fe->pad); + if (ret) + return ret; + + /* Initialize subdev */ + v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops); + fe->sd.internal_ops = &pisp_fe_internal_ops; + fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + fe->sd.entity.ops = &pisp_fe_entity_ops; + fe->sd.entity.name = "pisp-fe"; + fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + fe->sd.owner = THIS_MODULE; + snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe"); + + ret = v4l2_subdev_init_finalize(&fe->sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd); + if (ret) { + pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret); + goto err_subdev_cleanup; + } + + /* Must be in IDLE state (STATUS == 0) here. */ + WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(&fe->sd); +err_entity_cleanup: + media_entity_cleanup(&fe->sd.entity); + + return ret; +} + +void pisp_fe_uninit(struct pisp_fe_device *fe) +{ + v4l2_device_unregister_subdev(&fe->sd); + v4l2_subdev_cleanup(&fe->sd); + media_entity_cleanup(&fe->sd.entity); +} diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h new file mode 100644 index 000000000000..54d506e19cf2 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PiSP Front End Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + */ +#ifndef _PISP_FE_H_ +#define _PISP_FE_H_ + +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include <linux/media/raspberrypi/pisp_fe_config.h> + +enum pisp_fe_pads { + FE_STREAM_PAD, + FE_CONFIG_PAD, + FE_OUTPUT0_PAD, + FE_OUTPUT1_PAD, + FE_STATS_PAD, + FE_NUM_PADS +}; + +struct pisp_fe_device { + /* Parent V4l2 device */ + struct v4l2_device *v4l2_dev; + void __iomem *base; + u32 hw_revision; + + u16 inframe_count; + struct media_pad pad[FE_NUM_PADS]; + struct v4l2_subdev sd; +}; + +void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof); +int pisp_fe_validate_config(struct pisp_fe_device *fe, + struct pisp_fe_config *cfg, + struct v4l2_format const *f0, + struct v4l2_format const *f1); +void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, + struct pisp_fe_config *cfg); +void pisp_fe_start(struct pisp_fe_device *fe); +void pisp_fe_stop(struct pisp_fe_device *fe); +int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs); +void pisp_fe_uninit(struct pisp_fe_device *fe); + +#endif diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 582d5e35db0e..0a53dd47d7bf 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -135,13 +135,23 @@ struct rcar_csi2; /* V4H BASE registers */ #define V4H_N_LANES_REG 0x0004 #define V4H_CSI2_RESETN_REG 0x0008 + #define V4H_PHY_MODE_REG 0x001c +#define V4H_PHY_MODE_DPHY 0 +#define V4H_PHY_MODE_CPHY 1 + #define V4H_PHY_SHUTDOWNZ_REG 0x0040 #define V4H_DPHY_RSTZ_REG 0x0044 #define V4H_FLDC_REG 0x0804 #define V4H_FLDD_REG 0x0808 #define V4H_IDIC_REG 0x0810 + #define V4H_PHY_EN_REG 0x2000 +#define V4H_PHY_EN_ENABLE_3 BIT(7) +#define V4H_PHY_EN_ENABLE_2 BIT(6) +#define V4H_PHY_EN_ENABLE_1 BIT(5) +#define V4H_PHY_EN_ENABLE_0 BIT(4) +#define V4H_PHY_EN_ENABLE_CLK BIT(0) #define V4H_ST_PHYST_REG 0x2814 #define V4H_ST_PHYST_ST_PHY_READY BIT(31) @@ -173,17 +183,19 @@ struct rcar_csi2; #define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */ #define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */ #define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */ -#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c /* V4H C-PHY */ #define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */ #define V4H_CORE_DIG_RW_TRIO1_REG(n) (0x22500 + ((n) * 2)) /* n = 0 - 3 */ #define V4H_CORE_DIG_RW_TRIO2_REG(n) (0x22900 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG 0x2a000 #define V4H_CORE_DIG_CLANE_0_RW_LP_0_REG 0x2a080 #define V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(n) (0x2a100 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480 #define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG 0x2a800 #define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880 #define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */ @@ -237,17 +249,37 @@ static const struct rcsi2_cphy_setting cphy_setting_table_r8a779g0[] = { { /* sentinel */ }, }; +/* V4M registers */ +#define V4M_OVR1_REG 0x0848 +#define V4M_OVR1_FORCERXMODE_3 BIT(12) +#define V4M_OVR1_FORCERXMODE_2 BIT(11) +#define V4M_OVR1_FORCERXMODE_1 BIT(10) +#define V4M_OVR1_FORCERXMODE_0 BIT(9) + +#define V4M_FRXM_REG 0x2004 +#define V4M_FRXM_FORCERXMODE_3 BIT(3) +#define V4M_FRXM_FORCERXMODE_2 BIT(2) +#define V4M_FRXM_FORCERXMODE_1 BIT(1) +#define V4M_FRXM_FORCERXMODE_0 BIT(0) + +#define V4M_PHYPLL_REG 0x02050 +#define V4M_CSI0CLKFCPR_REG 0x02054 +#define V4M_PHTW_REG 0x02060 +#define V4M_PHTR_REG 0x02064 +#define V4M_PHTC_REG 0x02068 + struct phtw_value { - u16 data; - u16 code; + u8 data; + u8 code; }; -struct rcsi2_mbps_reg { +struct rcsi2_mbps_info { u16 mbps; - u16 reg; + u8 reg; + u16 osc_freq; /* V4M */ }; -static const struct rcsi2_mbps_reg phtw_mbps_v3u[] = { +static const struct rcsi2_mbps_info phtw_mbps_v3u[] = { { .mbps = 1500, .reg = 0xcc }, { .mbps = 1550, .reg = 0x1d }, { .mbps = 1600, .reg = 0x27 }, @@ -272,7 +304,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_v3u[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { +static const struct rcsi2_mbps_info phtw_mbps_h3_v3h_m3n[] = { { .mbps = 80, .reg = 0x86 }, { .mbps = 90, .reg = 0x86 }, { .mbps = 100, .reg = 0x87 }, @@ -292,7 +324,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { +static const struct rcsi2_mbps_info phtw_mbps_v3m_e3[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x20 }, { .mbps = 100, .reg = 0x40 }, @@ -336,7 +368,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { #define PHYPLL_REG 0x68 #define PHYPLL_HSFREQRANGE(n) ((n) << 16) -static const struct rcsi2_mbps_reg hsfreqrange_v3u[] = { +static const struct rcsi2_mbps_info hsfreqrange_v3u[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -402,7 +434,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_v3u[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = { +static const struct rcsi2_mbps_info hsfreqrange_h3_v3h_m3n[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -449,7 +481,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = { +static const struct rcsi2_mbps_info hsfreqrange_m3w[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -496,6 +528,73 @@ static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = { { /* sentinel */ }, }; +static const struct rcsi2_mbps_info hsfreqrange_v4m[] = { + { .mbps = 80, .reg = 0x00, .osc_freq = 0x01a9 }, + { .mbps = 90, .reg = 0x10, .osc_freq = 0x01a9 }, + { .mbps = 100, .reg = 0x20, .osc_freq = 0x01a9 }, + { .mbps = 110, .reg = 0x30, .osc_freq = 0x01a9 }, + { .mbps = 120, .reg = 0x01, .osc_freq = 0x01a9 }, + { .mbps = 130, .reg = 0x11, .osc_freq = 0x01a9 }, + { .mbps = 140, .reg = 0x21, .osc_freq = 0x01a9 }, + { .mbps = 150, .reg = 0x31, .osc_freq = 0x01a9 }, + { .mbps = 160, .reg = 0x02, .osc_freq = 0x01a9 }, + { .mbps = 170, .reg = 0x12, .osc_freq = 0x01a9 }, + { .mbps = 180, .reg = 0x22, .osc_freq = 0x01a9 }, + { .mbps = 190, .reg = 0x32, .osc_freq = 0x01a9 }, + { .mbps = 205, .reg = 0x03, .osc_freq = 0x01a9 }, + { .mbps = 220, .reg = 0x13, .osc_freq = 0x01a9 }, + { .mbps = 235, .reg = 0x23, .osc_freq = 0x01a9 }, + { .mbps = 250, .reg = 0x33, .osc_freq = 0x01a9 }, + { .mbps = 275, .reg = 0x04, .osc_freq = 0x01a9 }, + { .mbps = 300, .reg = 0x14, .osc_freq = 0x01a9 }, + { .mbps = 325, .reg = 0x25, .osc_freq = 0x01a9 }, + { .mbps = 350, .reg = 0x35, .osc_freq = 0x01a9 }, + { .mbps = 400, .reg = 0x05, .osc_freq = 0x01a9 }, + { .mbps = 450, .reg = 0x16, .osc_freq = 0x01a9 }, + { .mbps = 500, .reg = 0x26, .osc_freq = 0x01a9 }, + { .mbps = 550, .reg = 0x37, .osc_freq = 0x01a9 }, + { .mbps = 600, .reg = 0x07, .osc_freq = 0x01a9 }, + { .mbps = 650, .reg = 0x18, .osc_freq = 0x01a9 }, + { .mbps = 700, .reg = 0x28, .osc_freq = 0x01a9 }, + { .mbps = 750, .reg = 0x39, .osc_freq = 0x01a9 }, + { .mbps = 800, .reg = 0x09, .osc_freq = 0x01a9 }, + { .mbps = 850, .reg = 0x19, .osc_freq = 0x01a9 }, + { .mbps = 900, .reg = 0x29, .osc_freq = 0x01a9 }, + { .mbps = 950, .reg = 0x3a, .osc_freq = 0x01a9 }, + { .mbps = 1000, .reg = 0x0a, .osc_freq = 0x01a9 }, + { .mbps = 1050, .reg = 0x1a, .osc_freq = 0x01a9 }, + { .mbps = 1100, .reg = 0x2a, .osc_freq = 0x01a9 }, + { .mbps = 1150, .reg = 0x3b, .osc_freq = 0x01a9 }, + { .mbps = 1200, .reg = 0x0b, .osc_freq = 0x01a9 }, + { .mbps = 1250, .reg = 0x1b, .osc_freq = 0x01a9 }, + { .mbps = 1300, .reg = 0x2b, .osc_freq = 0x01a9 }, + { .mbps = 1350, .reg = 0x3c, .osc_freq = 0x01a9 }, + { .mbps = 1400, .reg = 0x0c, .osc_freq = 0x01a9 }, + { .mbps = 1450, .reg = 0x1c, .osc_freq = 0x01a9 }, + { .mbps = 1500, .reg = 0x2c, .osc_freq = 0x01a9 }, + { .mbps = 1550, .reg = 0x3d, .osc_freq = 0x0108 }, + { .mbps = 1600, .reg = 0x0d, .osc_freq = 0x0110 }, + { .mbps = 1650, .reg = 0x1d, .osc_freq = 0x0119 }, + { .mbps = 1700, .reg = 0x2e, .osc_freq = 0x0121 }, + { .mbps = 1750, .reg = 0x3e, .osc_freq = 0x012a }, + { .mbps = 1800, .reg = 0x0e, .osc_freq = 0x0132 }, + { .mbps = 1850, .reg = 0x1e, .osc_freq = 0x013b }, + { .mbps = 1900, .reg = 0x2f, .osc_freq = 0x0143 }, + { .mbps = 1950, .reg = 0x3f, .osc_freq = 0x014c }, + { .mbps = 2000, .reg = 0x0f, .osc_freq = 0x0154 }, + { .mbps = 2050, .reg = 0x40, .osc_freq = 0x015d }, + { .mbps = 2100, .reg = 0x41, .osc_freq = 0x0165 }, + { .mbps = 2150, .reg = 0x42, .osc_freq = 0x016e }, + { .mbps = 2200, .reg = 0x43, .osc_freq = 0x0176 }, + { .mbps = 2250, .reg = 0x44, .osc_freq = 0x017f }, + { .mbps = 2300, .reg = 0x45, .osc_freq = 0x0187 }, + { .mbps = 2350, .reg = 0x46, .osc_freq = 0x0190 }, + { .mbps = 2400, .reg = 0x47, .osc_freq = 0x0198 }, + { .mbps = 2450, .reg = 0x48, .osc_freq = 0x01a1 }, + { .mbps = 2500, .reg = 0x49, .osc_freq = 0x01a9 }, + { /* sentinel */ }, +}; + /* PHY ESC Error Monitor */ #define PHEERM_REG 0x74 @@ -575,6 +674,21 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) return NULL; } +struct rcsi2_cphy_line_order { + enum v4l2_mbus_csi2_cphy_line_orders_type order; + u16 cfg; + u16 ctrl29; +}; + +static const struct rcsi2_cphy_line_order rcsi2_cphy_line_orders[] = { + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC, .cfg = 0x0, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ACB, .cfg = 0xa, .ctrl29 = 0x1 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BAC, .cfg = 0xc, .ctrl29 = 0x1 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BCA, .cfg = 0x5, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CAB, .cfg = 0x3, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CBA, .cfg = 0x9, .ctrl29 = 0x1 } +}; + enum rcar_csi2_pads { RCAR_CSI2_SINK, RCAR_CSI2_SOURCE_VC0, @@ -584,12 +698,19 @@ enum rcar_csi2_pads { NR_OF_RCAR_CSI2_PAD, }; +struct rcsi2_register_layout { + unsigned int phtw; + unsigned int phypll; +}; + struct rcar_csi2_info { + const struct rcsi2_register_layout *regs; int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); int (*phy_post_init)(struct rcar_csi2 *priv); - int (*start_receiver)(struct rcar_csi2 *priv); + int (*start_receiver)(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state); void (*enter_standby)(struct rcar_csi2 *priv); - const struct rcsi2_mbps_reg *hsfreqrange; + const struct rcsi2_mbps_info *hsfreqrange; unsigned int csi0clkfreqrange; unsigned int num_channels; bool clear_ulps; @@ -613,13 +734,12 @@ struct rcar_csi2 { int channel_vc[4]; - struct mutex lock; /* Protects mf and stream_count. */ - struct v4l2_mbus_framefmt mf; int stream_count; bool cphy; unsigned short lanes; unsigned char lane_swap[4]; + enum v4l2_mbus_csi2_cphy_line_orders_type line_orders[3]; }; static inline struct rcar_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -632,6 +752,16 @@ static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n) return container_of(n, struct rcar_csi2, notifier); } +static unsigned int rcsi2_num_pads(const struct rcar_csi2 *priv) +{ + /* Used together with R-Car ISP: one sink and one source pad. */ + if (priv->info->use_isp) + return 2; + + /* Used together with R-Car VIN: one sink and four source pads. */ + return 5; +} + static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg) { return ioread32(priv->base + reg); @@ -642,11 +772,88 @@ static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data) iowrite32(data, priv->base + reg); } +static u16 rcsi2_read16(struct rcar_csi2 *priv, unsigned int reg) +{ + return ioread16(priv->base + reg); +} + static void rcsi2_write16(struct rcar_csi2 *priv, unsigned int reg, u16 data) { iowrite16(data, priv->base + reg); } +static void rcsi2_modify16(struct rcar_csi2 *priv, unsigned int reg, u16 data, u16 mask) +{ + u16 val; + + val = rcsi2_read16(priv, reg) & ~mask; + rcsi2_write16(priv, reg, val | data); +} + +static int rcsi2_phtw_write(struct rcar_csi2 *priv, u8 data, u8 code) +{ + unsigned int timeout; + + rcsi2_write(priv, priv->info->regs->phtw, + PHTW_DWEN | PHTW_TESTDIN_DATA(data) | + PHTW_CWEN | PHTW_TESTDIN_CODE(code)); + + /* Wait for DWEN and CWEN to be cleared by hardware. */ + for (timeout = 0; timeout <= 20; timeout++) { + if (!(rcsi2_read(priv, priv->info->regs->phtw) & (PHTW_DWEN | PHTW_CWEN))) + return 0; + + usleep_range(1000, 2000); + } + + dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n"); + + return -ETIMEDOUT; +} + +static int rcsi2_phtw_write_array(struct rcar_csi2 *priv, + const struct phtw_value *values, + unsigned int size) +{ + int ret; + + for (unsigned int i = 0; i < size; i++) { + ret = rcsi2_phtw_write(priv, values[i].data, values[i].code); + if (ret) + return ret; + } + + return 0; +} + +static const struct rcsi2_mbps_info * +rcsi2_mbps_to_info(struct rcar_csi2 *priv, + const struct rcsi2_mbps_info *infotable, unsigned int mbps) +{ + const struct rcsi2_mbps_info *info; + const struct rcsi2_mbps_info *prev = NULL; + + if (mbps < infotable->mbps) + dev_warn(priv->dev, "%u Mbps less than min PHY speed %u Mbps", + mbps, infotable->mbps); + + for (info = infotable; info->mbps != 0; info++) { + if (info->mbps >= mbps) + break; + prev = info; + } + + if (!info->mbps) { + dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return NULL; + } + + if (prev && ((mbps - prev->mbps) <= (info->mbps - mbps))) + info = prev; + + return info; +} + static void rcsi2_enter_standby_gen3(struct rcar_csi2 *priv) { rcsi2_write(priv, PHYCNT_REG, 0); @@ -699,29 +906,13 @@ static int rcsi2_wait_phy_start(struct rcar_csi2 *priv, static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) { - const struct rcsi2_mbps_reg *hsfreq; - const struct rcsi2_mbps_reg *hsfreq_prev = NULL; - - if (mbps < priv->info->hsfreqrange->mbps) - dev_warn(priv->dev, "%u Mbps less than min PHY speed %u Mbps", - mbps, priv->info->hsfreqrange->mbps); + const struct rcsi2_mbps_info *info; - for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++) { - if (hsfreq->mbps >= mbps) - break; - hsfreq_prev = hsfreq; - } - - if (!hsfreq->mbps) { - dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + info = rcsi2_mbps_to_info(priv, priv->info->hsfreqrange, mbps); + if (!info) return -ERANGE; - } - if (hsfreq_prev && - ((mbps - hsfreq_prev->mbps) <= (hsfreq->mbps - mbps))) - hsfreq = hsfreq_prev; - - rcsi2_write(priv, PHYPLL_REG, PHYPLL_HSFREQRANGE(hsfreq->reg)); + rcsi2_write(priv, priv->info->regs->phypll, PHYPLL_HSFREQRANGE(info->reg)); return 0; } @@ -808,20 +999,25 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, return 0; } -static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; unsigned int i; int mbps, ret; + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + dev_dbg(priv->dev, "Input size (%ux%u%c)\n", - priv->mf.width, priv->mf.height, - priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); + fmt->width, fmt->height, + fmt->field == V4L2_FIELD_NONE ? 'p' : 'i'); /* Code is validated in set_fmt. */ - format = rcsi2_code_to_fmt(priv->mf.code); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -849,11 +1045,11 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) vcdt2 |= vcdt_part << ((i % 2) * 16); } - if (priv->mf.field == V4L2_FIELD_ALTERNATE) { + if (fmt->field == V4L2_FIELD_ALTERNATE) { fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN; - if (priv->mf.height == 240) + if (fmt->height == 240) fld |= FLD_FLD_NUM(0); else fld |= FLD_FLD_NUM(1); @@ -947,6 +1143,26 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) return 0; } +static void rsci2_set_line_order(struct rcar_csi2 *priv, + enum v4l2_mbus_csi2_cphy_line_orders_type order, + unsigned int cfgreg, unsigned int ctrlreg) +{ + const struct rcsi2_cphy_line_order *info = NULL; + + for (unsigned int i = 0; i < ARRAY_SIZE(rcsi2_cphy_line_orders); i++) { + if (rcsi2_cphy_line_orders[i].order == order) { + info = &rcsi2_cphy_line_orders[i]; + break; + } + } + + if (!info) + return; + + rcsi2_modify16(priv, cfgreg, info->cfg, 0x000f); + rcsi2_modify16(priv, ctrlreg, info->ctrl29, 0x0100); +} + static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match) { unsigned int timeout; @@ -1024,12 +1240,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(1), conf->trio1); rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(1), conf->trio1); - /* - * Configure pin-swap. - * TODO: This registers is not documented yet, the values should depend - * on the 'clock-lanes' and 'data-lanes' devicetree properties. - */ - rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, 0xf5); + /* Configure data line order. */ + rsci2_set_line_order(priv, priv->line_orders[0], + V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9)); + rsci2_set_line_order(priv, priv->line_orders[1], + V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(9)); + rsci2_set_line_order(priv, priv->line_orders[2], + V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(9)); + + /* TODO: This registers is not documented. */ rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000); /* Leave Shutdown mode */ @@ -1049,15 +1271,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) return 0; } -static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; int msps; int ret; - /* Calculate parameters */ - format = rcsi2_code_to_fmt(priv->mf.code); + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -1075,11 +1300,11 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0); /* PHY static setting */ - rcsi2_write(priv, V4H_PHY_EN_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK); rcsi2_write(priv, V4H_FLDC_REG, 0); rcsi2_write(priv, V4H_FLDD_REG, 0); rcsi2_write(priv, V4H_IDIC_REG, 0); - rcsi2_write(priv, V4H_PHY_MODE_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_CPHY); rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1); /* Reset CSI2 */ @@ -1114,7 +1339,201 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) return 0; } -static int rcsi2_start(struct rcar_csi2 *priv) +static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int data_rate) +{ + unsigned int timeout; + int ret; + + static const struct phtw_value step1[] = { + { .data = 0x00, .code = 0x00 }, + { .data = 0x00, .code = 0x1e }, + }; + + /* Shutdown and reset PHY. */ + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0)); + + /* Start internal calibration (POR). */ + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); + if (ret) + return ret; + + /* Wait for POR to complete. */ + for (timeout = 10; timeout > 0; timeout--) { + if ((rcsi2_read(priv, V4M_PHTR_REG) & 0xf0000) == 0x70000) + break; + usleep_range(1000, 2000); + } + + if (!timeout) { + dev_err(priv->dev, "D-PHY calibration failed\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rcsi2_set_osc_freq(struct rcar_csi2 *priv, unsigned int mbps) +{ + const struct rcsi2_mbps_info *info; + struct phtw_value steps[] = { + { .data = 0x00, .code = 0x00 }, + { .code = 0xe2 }, /* Data filled in below. */ + { .code = 0xe3 }, /* Data filled in below. */ + { .data = 0x01, .code = 0xe4 }, + }; + + info = rcsi2_mbps_to_info(priv, priv->info->hsfreqrange, mbps); + if (!info) + return -ERANGE; + + /* Fill in data for command. */ + steps[1].data = (info->osc_freq & 0x00ff) >> 0; + steps[2].data = (info->osc_freq & 0x0f00) >> 8; + + return rcsi2_phtw_write_array(priv, steps, ARRAY_SIZE(steps)); +} + +static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps) +{ + int ret; + + static const struct phtw_value step1[] = { + { .data = 0x00, .code = 0x00 }, + { .data = 0x3c, .code = 0x08 }, + }; + + static const struct phtw_value step2[] = { + { .data = 0x00, .code = 0x00 }, + { .data = 0x80, .code = 0xe0 }, + { .data = 0x31, .code = 0xe1 }, + { .data = 0x06, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x08, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x0a, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x0c, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x01, .code = 0x00 }, + { .data = 0x31, .code = 0xaa }, + { .data = 0x05, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + { .data = 0x07, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + { .data = 0x09, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + { .data = 0x0b, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + }; + + static const struct phtw_value step3[] = { + { .data = 0x01, .code = 0x00 }, + { .data = 0x06, .code = 0xab }, + }; + + if (priv->info->hsfreqrange) { + ret = rcsi2_set_phypll(priv, mbps); + if (ret) + return ret; + + ret = rcsi2_set_osc_freq(priv, mbps); + if (ret) + return ret; + } + + if (mbps <= 1500) { + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); + if (ret) + return ret; + } + + if (priv->info->csi0clkfreqrange) + rcsi2_write(priv, V4M_CSI0CLKFCPR_REG, + CSI0CLKFREQRANGE(priv->info->csi0clkfreqrange)); + + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK | + V4H_PHY_EN_ENABLE_0 | V4H_PHY_EN_ENABLE_1 | + V4H_PHY_EN_ENABLE_2 | V4H_PHY_EN_ENABLE_3); + + if (mbps > 1500) { + ret = rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2)); + if (ret) + return ret; + } + + return rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3)); +} + +static int rcsi2_start_receiver_v4m(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) +{ + const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; + unsigned int lanes; + int mbps; + int ret; + + /* Calculate parameters */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + format = rcsi2_code_to_fmt(fmt->code); + if (!format) + return -EINVAL; + + ret = rcsi2_get_active_lanes(priv, &lanes); + if (ret) + return ret; + + mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + if (mbps < 0) + return mbps; + + /* Reset LINK and PHY */ + rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0); + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0); + rcsi2_write(priv, V4M_PHTC_REG, PHTC_TESTCLR); + + /* PHY static setting */ + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK); + rcsi2_write(priv, V4H_FLDC_REG, 0); + rcsi2_write(priv, V4H_FLDD_REG, 0); + rcsi2_write(priv, V4H_IDIC_REG, 0); + rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_DPHY); + rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1); + + rcsi2_write(priv, V4M_FRXM_REG, + V4M_FRXM_FORCERXMODE_0 | V4M_FRXM_FORCERXMODE_1 | + V4M_FRXM_FORCERXMODE_2 | V4M_FRXM_FORCERXMODE_3); + rcsi2_write(priv, V4M_OVR1_REG, + V4M_OVR1_FORCERXMODE_0 | V4M_OVR1_FORCERXMODE_1 | + V4M_OVR1_FORCERXMODE_2 | V4M_OVR1_FORCERXMODE_3); + + /* Reset CSI2 */ + rcsi2_write(priv, V4M_PHTC_REG, 0); + rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0)); + + /* Common settings */ + ret = rcsi2_init_common_v4m(priv, mbps); + if (ret) + return ret; + + /* D-PHY settings */ + ret = rcsi2_d_phy_setting_v4m(priv, mbps); + if (ret) + return ret; + + rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_STOPSTATE_0 | + V4H_ST_PHYST_ST_STOPSTATE_1 | + V4H_ST_PHYST_ST_STOPSTATE_2 | + V4H_ST_PHYST_ST_STOPSTATE_3); + + rcsi2_write(priv, V4M_FRXM_REG, 0); + + return 0; +} + +static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { int ret; @@ -1122,7 +1541,7 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret < 0) return ret; - ret = priv->info->start_receiver(priv); + ret = priv->info->start_receiver(priv, state); if (ret) { rcsi2_enter_standby(priv); return ret; @@ -1146,17 +1565,16 @@ static void rcsi2_stop(struct rcar_csi2 *priv) static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) { struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&priv->lock); + if (!priv->remote) + return -ENODEV; - if (!priv->remote) { - ret = -ENODEV; - goto out; - } + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); if (enable && priv->stream_count == 0) { - ret = rcsi2_start(priv); + ret = rcsi2_start(priv, state); if (ret) goto out; } else if (!enable && priv->stream_count == 1) { @@ -1165,49 +1583,29 @@ static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) priv->stream_count += enable ? 1 : -1; out: - mutex_unlock(&priv->lock); + v4l2_subdev_unlock_state(state); return ret; } static int rcsi2_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct rcar_csi2 *priv = sd_to_csi2(sd); - struct v4l2_mbus_framefmt *framefmt; + unsigned int num_pads = rcsi2_num_pads(priv); - mutex_lock(&priv->lock); + if (format->pad > RCAR_CSI2_SINK) + return v4l2_subdev_get_fmt(sd, state, format); if (!rcsi2_code_to_fmt(format->format.code)) format->format.code = rcar_csi2_formats[0].code; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - priv->mf = format->format; - } else { - framefmt = v4l2_subdev_state_get_format(sd_state, 0); - *framefmt = format->format; - } + *v4l2_subdev_state_get_format(state, format->pad) = format->format; - mutex_unlock(&priv->lock); - - return 0; -} - -static int rcsi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct rcar_csi2 *priv = sd_to_csi2(sd); - - mutex_lock(&priv->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - format->format = priv->mf; - else - format->format = *v4l2_subdev_state_get_format(sd_state, 0); - - mutex_unlock(&priv->lock); + /* Propagate the format to the source pads. */ + for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = format->format; return 0; } @@ -1218,7 +1616,7 @@ static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = { .set_fmt = rcsi2_set_pad_format, - .get_fmt = rcsi2_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, }; static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { @@ -1226,6 +1624,33 @@ static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { .pad = &rcar_csi2_pad_ops, }; +static int rcsi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + unsigned int num_pads = rcsi2_num_pads(priv); + + static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + + for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt; + + return 0; +} + +static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = { + .init_state = rcsi2_init_state, +}; + static irqreturn_t rcsi2_irq(int irq, void *data) { struct rcar_csi2 *priv = data; @@ -1251,14 +1676,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data) static irqreturn_t rcsi2_irq_thread(int irq, void *data) { + struct v4l2_subdev_state *state; struct rcar_csi2 *priv = data; - mutex_lock(&priv->lock); + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); + rcsi2_stop(priv); usleep_range(1000, 2000); - if (rcsi2_start(priv)) + if (rcsi2_start(priv, state)) dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n"); - mutex_unlock(&priv->lock); + + v4l2_subdev_unlock_state(state); return IRQ_HANDLED; } @@ -1366,6 +1794,9 @@ static int rcsi2_parse_v4l2(struct rcar_csi2 *priv, } } + for (i = 0; i < ARRAY_SIZE(priv->line_orders); i++) + priv->line_orders[i] = vep->bus.mipi_csi2.line_orders[i]; + return 0; } @@ -1425,64 +1856,16 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) * NOTE: Magic values are from the datasheet and lack documentation. */ -static int rcsi2_phtw_write(struct rcar_csi2 *priv, u16 data, u16 code) -{ - unsigned int timeout; - - rcsi2_write(priv, PHTW_REG, - PHTW_DWEN | PHTW_TESTDIN_DATA(data) | - PHTW_CWEN | PHTW_TESTDIN_CODE(code)); - - /* Wait for DWEN and CWEN to be cleared by hardware. */ - for (timeout = 0; timeout <= 20; timeout++) { - if (!(rcsi2_read(priv, PHTW_REG) & (PHTW_DWEN | PHTW_CWEN))) - return 0; - - usleep_range(1000, 2000); - } - - dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n"); - - return -ETIMEDOUT; -} - -static int rcsi2_phtw_write_array(struct rcar_csi2 *priv, - const struct phtw_value *values) -{ - const struct phtw_value *value; - int ret; - - for (value = values; value->data || value->code; value++) { - ret = rcsi2_phtw_write(priv, value->data, value->code); - if (ret) - return ret; - } - - return 0; -} - static int rcsi2_phtw_write_mbps(struct rcar_csi2 *priv, unsigned int mbps, - const struct rcsi2_mbps_reg *values, u16 code) + const struct rcsi2_mbps_info *values, u8 code) { - const struct rcsi2_mbps_reg *value; - const struct rcsi2_mbps_reg *prev_value = NULL; + const struct rcsi2_mbps_info *info; - for (value = values; value->mbps; value++) { - if (value->mbps >= mbps) - break; - prev_value = value; - } - - if (prev_value && - ((mbps - prev_value->mbps) <= (value->mbps - mbps))) - value = prev_value; - - if (!value->mbps) { - dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + info = rcsi2_mbps_to_info(priv, values, mbps); + if (!info) return -ERANGE; - } - return rcsi2_phtw_write(priv, value->reg, code); + return rcsi2_phtw_write(priv, info->reg, code); } static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, @@ -1494,7 +1877,6 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, { .data = 0x11, .code = 0xe4 }, { .data = 0x01, .code = 0xe5 }, { .data = 0x10, .code = 0x04 }, - { /* sentinel */ }, }; static const struct phtw_value step2[] = { @@ -1503,12 +1885,11 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, { .data = 0x4b, .code = 0xac }, { .data = 0x03, .code = 0x00 }, { .data = 0x80, .code = 0x07 }, - { /* sentinel */ }, }; int ret; - ret = rcsi2_phtw_write_array(priv, step1); + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); if (ret) return ret; @@ -1523,7 +1904,7 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, return ret; } - return rcsi2_phtw_write_array(priv, step2); + return rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2)); } static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) @@ -1549,10 +1930,9 @@ static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) { .data = 0xee, .code = 0x54 }, { .data = 0xee, .code = 0x84 }, { .data = 0xee, .code = 0x94 }, - { /* sentinel */ }, }; - return rcsi2_phtw_write_array(priv, step1); + return rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); } static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, @@ -1561,20 +1941,17 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, /* In case of 1500Mbps or less */ static const struct phtw_value step1[] = { { .data = 0xcc, .code = 0xe2 }, - { /* sentinel */ }, }; static const struct phtw_value step2[] = { { .data = 0x01, .code = 0xe3 }, { .data = 0x11, .code = 0xe4 }, { .data = 0x01, .code = 0xe5 }, - { /* sentinel */ }, }; /* In case of 1500Mbps or less */ static const struct phtw_value step3[] = { { .data = 0x38, .code = 0x08 }, - { /* sentinel */ }, }; static const struct phtw_value step4[] = { @@ -1582,29 +1959,28 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, { .data = 0x4b, .code = 0xac }, { .data = 0x03, .code = 0x00 }, { .data = 0x80, .code = 0x07 }, - { /* sentinel */ }, }; int ret; if (mbps != 0 && mbps <= 1500) - ret = rcsi2_phtw_write_array(priv, step1); + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); else ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3u, 0xe2); if (ret) return ret; - ret = rcsi2_phtw_write_array(priv, step2); + ret = rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2)); if (ret) return ret; if (mbps != 0 && mbps <= 1500) { - ret = rcsi2_phtw_write_array(priv, step3); + ret = rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3)); if (ret) return ret; } - ret = rcsi2_phtw_write_array(priv, step4); + ret = rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4)); if (ret) return ret; @@ -1688,7 +2064,13 @@ static int rcsi2_probe_resources(struct rcar_csi2 *priv, return PTR_ERR_OR_ZERO(priv->rstc); } +static const struct rcsi2_register_layout rcsi2_registers_gen3 = { + .phtw = PHTW_REG, + .phypll = PHYPLL_REG, +}; + static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1700,6 +2082,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3es2, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1711,6 +2094,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { + .regs = &rcsi2_registers_gen3, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, .hsfreqrange = hsfreqrange_m3w, @@ -1719,6 +2103,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = { + .regs = &rcsi2_registers_gen3, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, .hsfreqrange = hsfreqrange_m3w, @@ -1727,6 +2112,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1738,6 +2124,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_v3m_e3, .phy_post_init = rcsi2_phy_post_init_v3m_e3, .start_receiver = rcsi2_start_receiver_gen3, @@ -1747,6 +2134,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1757,6 +2145,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_v3m_e3, .phy_post_init = rcsi2_phy_post_init_v3m_e3, .start_receiver = rcsi2_start_receiver_gen3, @@ -1766,6 +2155,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_v3u, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1777,11 +2167,26 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = { + .regs = &rcsi2_registers_gen3, .start_receiver = rcsi2_start_receiver_v4h, .use_isp = true, .support_cphy = true, }; +static const struct rcsi2_register_layout rcsi2_registers_v4m = { + .phtw = V4M_PHTW_REG, + .phypll = V4M_PHYPLL_REG, +}; + +static const struct rcar_csi2_info rcar_csi2_info_r8a779h0 = { + .regs = &rcsi2_registers_v4m, + .start_receiver = rcsi2_start_receiver_v4m, + .hsfreqrange = hsfreqrange_v4m, + .csi0clkfreqrange = 0x0c, + .use_isp = true, + .support_dphy = true, +}; + static const struct of_device_id rcar_csi2_of_table[] = { { .compatible = "renesas,r8a774a1-csi2", @@ -1835,6 +2240,10 @@ static const struct of_device_id rcar_csi2_of_table[] = { .compatible = "renesas,r8a779g0-csi2", .data = &rcar_csi2_info_r8a779g0, }, + { + .compatible = "renesas,r8a779h0-csi2", + .data = &rcar_csi2_info_r8a779h0, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); @@ -1870,23 +2279,23 @@ static int rcsi2_probe(struct platform_device *pdev) priv->dev = &pdev->dev; - mutex_init(&priv->lock); priv->stream_count = 0; ret = rcsi2_probe_resources(priv, pdev); if (ret) { dev_err(priv->dev, "Failed to get resources\n"); - goto error_mutex; + return ret; } platform_set_drvdata(pdev, priv); ret = rcsi2_parse_dt(priv); if (ret) - goto error_mutex; + return ret; priv->subdev.owner = THIS_MODULE; priv->subdev.dev = &pdev->dev; + priv->subdev.internal_ops = &rcar_csi2_internal_ops; v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); v4l2_set_subdevdata(&priv->subdev, &pdev->dev); snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s", @@ -1896,7 +2305,7 @@ static int rcsi2_probe(struct platform_device *pdev) priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; priv->subdev.entity.ops = &rcar_csi2_entity_ops; - num_pads = priv->info->use_isp ? 2 : NR_OF_RCAR_CSI2_PAD; + num_pads = rcsi2_num_pads(priv); priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; for (i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) @@ -1912,19 +2321,25 @@ static int rcsi2_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); + ret = v4l2_subdev_init_finalize(&priv->subdev); + if (ret) + goto error_pm_runtime; + ret = v4l2_async_register_subdev(&priv->subdev); if (ret < 0) - goto error_async; + goto error_subdev; dev_info(priv->dev, "%d lanes found\n", priv->lanes); return 0; +error_subdev: + v4l2_subdev_cleanup(&priv->subdev); +error_pm_runtime: + pm_runtime_disable(&pdev->dev); error_async: v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); -error_mutex: - mutex_destroy(&priv->lock); return ret; } @@ -1936,14 +2351,13 @@ static void rcsi2_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); v4l2_async_unregister_subdev(&priv->subdev); + v4l2_subdev_cleanup(&priv->subdev); pm_runtime_disable(&pdev->dev); - - mutex_destroy(&priv->lock); } static struct platform_driver rcar_csi2_pdrv = { - .remove_new = rcsi2_remove, + .remove = rcsi2_remove, .probe = rcsi2_probe, .driver = { .name = "rcar-csi2", diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c index bcef7b87da7c..cee9bbce4e3a 100644 --- a/drivers/media/platform/renesas/rcar-fcp.c +++ b/drivers/media/platform/renesas/rcar-fcp.c @@ -164,7 +164,7 @@ MODULE_DEVICE_TABLE(of, rcar_fcp_of_match); static struct platform_driver rcar_fcp_platform_driver = { .probe = rcar_fcp_probe, - .remove_new = rcar_fcp_remove, + .remove = rcar_fcp_remove, .driver = { .name = "rcar-fcp", .of_match_table = rcar_fcp_of_match, diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 4512ac338ca5..c515278e3be5 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -431,7 +431,9 @@ static int risp_probe_resources(struct rcar_isp *isp, static const struct of_device_id risp_of_id_table[] = { { .compatible = "renesas,r8a779a0-isp" }, { .compatible = "renesas,r8a779g0-isp" }, - { /* sentinel */ }, + /* Keep above for compatibility with old DTB files. */ + { .compatible = "renesas,rcar-gen4-isp" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, risp_of_id_table); @@ -522,7 +524,7 @@ static struct platform_driver rcar_isp_driver = { .of_match_table = risp_of_id_table, }, .probe = risp_probe, - .remove_new = risp_remove, + .remove = risp_remove, }; module_platform_driver(rcar_isp_driver); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 809c3a38cc4a..ddfb18e6e7a4 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -1274,20 +1274,12 @@ static const struct rvin_info rcar_info_r8a77995 = { .scaler = rvin_scaler_gen3, }; -static const struct rvin_info rcar_info_r8a779a0 = { - .model = RCAR_GEN3, - .use_mc = true, - .use_isp = true, - .nv12 = true, - .max_width = 4096, - .max_height = 4096, -}; - -static const struct rvin_info rcar_info_r8a779g0 = { +static const struct rvin_info rcar_info_gen4 = { .model = RCAR_GEN3, .use_mc = true, .use_isp = true, .nv12 = true, + .raw10 = true, .max_width = 4096, .max_height = 4096, }; @@ -1354,12 +1346,18 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_r8a77995, }, { + /* Keep to be compatible with old DTS files. */ .compatible = "renesas,vin-r8a779a0", - .data = &rcar_info_r8a779a0, + .data = &rcar_info_gen4, }, { + /* Keep to be compatible with old DTS files. */ .compatible = "renesas,vin-r8a779g0", - .data = &rcar_info_r8a779g0, + .data = &rcar_info_gen4, + }, + { + .compatible = "renesas,rcar-gen4-vin", + .data = &rcar_info_gen4, }, { /* Sentinel */ }, }; @@ -1446,7 +1444,7 @@ static struct platform_driver rcar_vin_driver = { .of_match_table = rvin_of_id_table, }, .probe = rcar_vin_probe, - .remove_new = rcar_vin_remove, + .remove = rcar_vin_remove, }; module_platform_driver(rcar_vin_driver); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index e2c40abc6d3d..8773998101ff 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -123,7 +123,9 @@ /* Video n Data Mode Register bits */ #define VNDMR_A8BIT(n) (((n) & 0xff) << 24) #define VNDMR_A8BIT_MASK (0xff << 24) +#define VNDMR_RMODE_RAW10 (2 << 19) #define VNDMR_YMODE_Y8 (1 << 12) +#define VNDMR_YC_THR (1 << 11) #define VNDMR_EXRGB (1 << 8) #define VNDMR_BPSM (1 << 4) #define VNDMR_ABIT (1 << 2) @@ -742,12 +744,22 @@ static int rvin_setup(struct rvin_dev *vin) */ switch (vin->mbus_code) { case MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_1X16: - vnmc |= VNMC_INF_YUV16 | VNMC_YCAL; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + vnmc |= VNMC_YCAL; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_2X8: @@ -780,6 +792,12 @@ static int rvin_setup(struct rvin_dev *vin) case MEDIA_BUS_FMT_Y8_1X8: vnmc |= VNMC_INF_RAW8; break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + vnmc |= VNMC_INF_RGB666; + break; default: break; } @@ -888,6 +906,12 @@ static int rvin_setup(struct rvin_dev *vin) dmr = 0; } break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + dmr = VNDMR_RMODE_RAW10 | VNDMR_YC_THR; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); @@ -1270,6 +1294,22 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, if (vin->format.pixelformat != V4L2_PIX_FMT_GREY) return -EPIPE; break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SBGGR10) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SGBRG10) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SGRBG10) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SRGGB10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB10) + return -EPIPE; + break; default: return -EPIPE; } @@ -1519,8 +1559,6 @@ static const struct vb2_ops rvin_qops = { .buf_queue = rvin_buffer_queue, .start_streaming = rvin_start_streaming_vq, .stop_streaming = rvin_stop_streaming_vq, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; void rvin_dma_unregister(struct rvin_dev *vin) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index 073f70c6ac68..756fdfdbce61 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -86,6 +86,22 @@ static const struct rvin_video_format rvin_formats[] = { .fourcc = V4L2_PIX_FMT_GREY, .bpp = 1, }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .bpp = 4, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .bpp = 4, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .bpp = 4, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .bpp = 4, + }, }; const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, @@ -106,6 +122,13 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333)) return NULL; break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + if (!vin->info->raw10) + return NULL; + break; default: break; } @@ -407,6 +430,26 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; f->pixelformat = V4L2_PIX_FMT_SRGGB8; return 0; + case MEDIA_BUS_FMT_SBGGR10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SBGGR10; + return 0; + case MEDIA_BUS_FMT_SGBRG10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SGBRG10; + return 0; + case MEDIA_BUS_FMT_SGRBG10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SGRBG10; + return 0; + case MEDIA_BUS_FMT_SRGGB10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SRGGB10; + return 0; default: return -EINVAL; } @@ -730,7 +773,8 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh, struct v4l2_subdev *sd = vin_to_source(vin); int ret; - ret = v4l2_subdev_call(sd, video, s_dv_timings, timings); + ret = v4l2_subdev_call(sd, pad, s_dv_timings, + vin->parallel.sink_pad, timings); if (ret) return ret; @@ -744,7 +788,8 @@ static int rvin_g_dv_timings(struct file *file, void *priv_fh, struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, video, g_dv_timings, timings); + return v4l2_subdev_call(sd, pad, g_dv_timings, + vin->parallel.sink_pad, timings); } static int rvin_query_dv_timings(struct file *file, void *priv_fh, @@ -753,7 +798,8 @@ static int rvin_query_dv_timings(struct file *file, void *priv_fh, struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, video, query_dv_timings, timings); + return v4l2_subdev_call(sd, pad, query_dv_timings, + vin->parallel.sink_pad, timings); } static int rvin_dv_timings_cap(struct file *file, void *priv_fh, diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index 792336dada44..f87d4bc9e53e 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -59,7 +59,7 @@ enum rvin_isp_id { #define RVIN_REMOTES_MAX \ (((unsigned int)RVIN_CSI_MAX) > ((unsigned int)RVIN_ISP_MAX) ? \ - RVIN_CSI_MAX : RVIN_ISP_MAX) + (unsigned int)RVIN_CSI_MAX : (unsigned int)RVIN_ISP_MAX) /** * enum rvin_dma_state - DMA states @@ -151,7 +151,8 @@ struct rvin_group_route { * @model: VIN model * @use_mc: use media controller instead of controlling subdevice * @use_isp: the VIN is connected to the ISP and not to the CSI-2 - * @nv12: support outputing NV12 pixel format + * @nv12: support outputting NV12 pixel format + * @raw10: support outputting RAW10 pixel format * @max_width: max input width the VIN supports * @max_height: max input height the VIN supports * @routes: list of possible routes from the CSI-2 recivers to @@ -163,6 +164,7 @@ struct rvin_info { bool use_mc; bool use_isp; bool nv12; + bool raw10; unsigned int max_width; unsigned int max_height; diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index f21d05054341..fc8b6bbef793 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -861,8 +861,6 @@ static const struct vb2_ops rcar_drif_vb2_ops = { .buf_queue = rcar_drif_buf_queue, .start_streaming = rcar_drif_start_streaming, .stop_streaming = rcar_drif_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int rcar_drif_querycap(struct file *file, void *fh, @@ -1071,7 +1069,6 @@ static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr) sdr->vdev->release = video_device_release; sdr->vdev->lock = &sdr->v4l2_mutex; sdr->vdev->queue = &sdr->vb_queue; - sdr->vdev->queue->lock = &sdr->vb_queue_mutex; sdr->vdev->ctrl_handler = &sdr->ctrl_hdl; sdr->vdev->v4l2_dev = &sdr->v4l2_dev; sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | @@ -1316,6 +1313,7 @@ static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr) sdr->vb_queue.ops = &rcar_drif_vb2_ops; sdr->vb_queue.mem_ops = &vb2_vmalloc_memops; sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + sdr->vb_queue.lock = &sdr->vb_queue_mutex; /* Init videobuf2 queue */ ret = vb2_queue_init(&sdr->vb_queue); @@ -1475,7 +1473,7 @@ static struct platform_driver rcar_drif_driver = { .pm = &rcar_drif_pm_ops, }, .probe = rcar_drif_probe, - .remove_new = rcar_drif_remove, + .remove = rcar_drif_remove, }; module_platform_driver(rcar_drif_driver); diff --git a/drivers/media/platform/renesas/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c index a2565b269f3b..5d453a7a8988 100644 --- a/drivers/media/platform/renesas/rcar_fdp1.c +++ b/drivers/media/platform/renesas/rcar_fdp1.c @@ -2032,8 +2032,6 @@ static const struct vb2_ops fdp1_qops = { .buf_queue = fdp1_buf_queue, .start_streaming = fdp1_start_streaming, .stop_streaming = fdp1_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -2444,7 +2442,7 @@ MODULE_DEVICE_TABLE(of, fdp1_dt_ids); static struct platform_driver fdp1_pdrv = { .probe = fdp1_probe, - .remove_new = fdp1_remove, + .remove = fdp1_remove, .driver = { .name = DRIVER_NAME, .of_match_table = fdp1_dt_ids, diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c index fff349e45067..81038df71bb5 100644 --- a/drivers/media/platform/renesas/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -14,7 +14,7 @@ * 3) V4L2_CID_JPEG_ACTIVE_MARKER */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/interrupt.h> @@ -1171,8 +1171,6 @@ static const struct vb2_ops jpu_qops = { .buf_finish = jpu_buf_finish, .start_streaming = jpu_start_streaming, .stop_streaming = jpu_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int jpu_queue_init(void *priv, struct vb2_queue *src_vq, @@ -1736,7 +1734,7 @@ static const struct dev_pm_ops jpu_pm_ops = { static struct platform_driver jpu_driver = { .probe = jpu_probe, - .remove_new = jpu_remove, + .remove = jpu_remove, .driver = { .of_match_table = jpu_dt_ids, .name = DRV_NAME, diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c index 167760276796..8cceafe491b1 100644 --- a/drivers/media/platform/renesas/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c @@ -761,8 +761,6 @@ static const struct vb2_ops ceu_vb2_ops = { .queue_setup = ceu_vb2_setup, .buf_queue = ceu_vb2_queue, .buf_prepare = ceu_vb2_prepare, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = ceu_start_streaming, .stop_streaming = ceu_stop_streaming, }; @@ -1723,7 +1721,7 @@ static struct platform_driver ceu_driver = { .of_match_table = of_match_ptr(ceu_of_match), }, .probe = ceu_probe, - .remove_new = ceu_remove, + .remove = ceu_remove, }; module_platform_driver(ceu_driver); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 280efd2a8185..89be584a4988 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -72,7 +72,6 @@ static int rzg2l_cru_group_notify_complete(struct v4l2_async_notifier *notifier) source->name, sink->name); return ret; } - cru->csi.channel = 0; cru->ip.remote = cru->csi.subdev; /* Create media device link between CRU IP <-> CRU OUTPUT */ @@ -209,7 +208,7 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) const struct of_device_id *match; int ret; - cru->pad.flags = MEDIA_PAD_FL_SINK; + cru->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; ret = media_entity_pads_init(&cru->vdev.entity, 1, &cru->pad); if (ret) return ret; @@ -242,7 +241,7 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) static int rzg2l_cru_probe(struct platform_device *pdev) { struct rzg2l_cru_dev *cru; - int ret; + int irq, ret; cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL); if (!cru) @@ -270,9 +269,14 @@ static int rzg2l_cru_probe(struct platform_device *pdev) cru->dev = &pdev->dev; cru->info = of_device_get_match_data(&pdev->dev); - cru->image_conv_irq = platform_get_irq(pdev, 0); - if (cru->image_conv_irq < 0) - return cru->image_conv_irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, rzg2l_cru_irq, 0, + KBUILD_MODNAME, cru); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to request irq\n"); platform_set_drvdata(pdev, cru); @@ -325,7 +329,7 @@ static struct platform_driver rzg2l_cru_driver = { .of_match_table = rzg2l_cru_of_id_table, }, .probe = rzg2l_cru_probe, - .remove_new = rzg2l_cru_remove, + .remove = rzg2l_cru_remove, }; module_platform_driver(rzg2l_cru_driver); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h new file mode 100644 index 000000000000..1c9f22118a5d --- /dev/null +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * rzg2l-cru-regs.h--RZ/G2L (and alike SoCs) CRU Registers Definitions + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ + +#ifndef __RZG2L_CRU_REGS_H__ +#define __RZG2L_CRU_REGS_H__ + +/* HW CRU Registers Definition */ + +/* CRU Control Register */ +#define CRUnCTRL 0x0 +#define CRUnCTRL_VINSEL(x) ((x) << 0) + +/* CRU Interrupt Enable Register */ +#define CRUnIE 0x4 +#define CRUnIE_EFE BIT(17) + +/* CRU Interrupt Status Register */ +#define CRUnINTS 0x8 +#define CRUnINTS_SFS BIT(16) + +/* CRU Reset Register */ +#define CRUnRST 0xc +#define CRUnRST_VRESETN BIT(0) + +/* Memory Bank Base Address (Lower) Register for CRU Image Data */ +#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) + +/* Memory Bank Base Address (Higher) Register for CRU Image Data */ +#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) + +/* Memory Bank Enable Register for CRU Image Data */ +#define AMnMBVALID 0x148 +#define AMnMBVALID_MBVALID(x) GENMASK(x, 0) + +/* Memory Bank Status Register for CRU Image Data */ +#define AMnMBS 0x14c +#define AMnMBS_MBSTS 0x7 + +/* AXI Master Transfer Setting Register for CRU Image Data */ +#define AMnAXIATTR 0x158 +#define AMnAXIATTR_AXILEN_MASK GENMASK(3, 0) +#define AMnAXIATTR_AXILEN (0xf) + +/* AXI Master FIFO Pointer Register for CRU Image Data */ +#define AMnFIFOPNTR 0x168 +#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) +#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) + +/* AXI Master Transfer Stop Register for CRU Image Data */ +#define AMnAXISTP 0x174 +#define AMnAXISTP_AXI_STOP BIT(0) + +/* AXI Master Transfer Stop Status Register for CRU Image Data */ +#define AMnAXISTPACK 0x178 +#define AMnAXISTPACK_AXI_STOP_ACK BIT(0) + +/* CRU Image Processing Enable Register */ +#define ICnEN 0x200 +#define ICnEN_ICEN BIT(0) + +/* CRU Image Processing Main Control Register */ +#define ICnMC 0x208 +#define ICnMC_CSCTHR BIT(5) +#define ICnMC_INF(x) ((x) << 16) +#define ICnMC_VCSEL(x) ((x) << 22) +#define ICnMC_INF_MASK GENMASK(21, 16) + +/* CRU Module Status Register */ +#define ICnMS 0x254 +#define ICnMS_IA BIT(2) + +/* CRU Data Output Mode Register */ +#define ICnDMR 0x26c +#define ICnDMR_YCMODE_UYVY (1 << 4) + +#endif /* __RZG2L_CRU_REGS_H__ */ diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index a5a99b004322..8b898ce05b84 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -8,6 +8,7 @@ #ifndef __RZG2L_CRU__ #define __RZG2L_CRU__ +#include <linux/irqreturn.h> #include <linux/reset.h> #include <media/v4l2-async.h> @@ -30,6 +31,11 @@ #define RZG2L_CRU_MIN_INPUT_HEIGHT 240 #define RZG2L_CRU_MAX_INPUT_HEIGHT 4095 +enum rzg2l_csi2_pads { + RZG2L_CRU_IP_SINK = 0, + RZG2L_CRU_IP_SOURCE, +}; + /** * enum rzg2l_cru_dma_state - DMA states * @RZG2L_CRU_DMA_STOPPED: No operation in progress @@ -47,7 +53,6 @@ enum rzg2l_cru_dma_state { struct rzg2l_cru_csi { struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; - u32 channel; }; struct rzg2l_cru_ip { @@ -58,6 +63,24 @@ struct rzg2l_cru_ip { }; /** + * struct rzg2l_cru_ip_format - CRU IP format + * @code: Media bus code + * @datatype: MIPI CSI2 data type + * @format: 4CC format identifier (V4L2_PIX_FMT_*) + * @icndmr: ICnDMR register value + * @bpp: bytes per pixel + * @yuv: Flag to indicate whether the format is YUV-based. + */ +struct rzg2l_cru_ip_format { + u32 code; + u32 datatype; + u32 format; + u32 icndmr; + u8 bpp; + bool yuv; +}; + +/** * struct rzg2l_cru_dev - Renesas CRU device structure * @dev: (OF) device * @base: device I/O register space remapped to virtual memory @@ -68,8 +91,6 @@ struct rzg2l_cru_ip { * * @vclk: CRU Main clock * - * @image_conv_irq: Holds image conversion interrupt number - * * @vdev: V4L2 video device associated with CRU * @v4l2_dev: V4L2 device * @num_buf: Holds the current number of buffers enabled @@ -105,8 +126,6 @@ struct rzg2l_cru_dev { struct clk *vclk; - int image_conv_irq; - struct video_device vdev; struct v4l2_device v4l2_dev; u8 num_buf; @@ -141,6 +160,7 @@ void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru); int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru); void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru); +irqreturn_t rzg2l_cru_irq(int irq, void *data); const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format); @@ -148,4 +168,8 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru); void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru); struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru); +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code); +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format); +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); + #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index e68fcdaea207..881e910dce02 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -183,12 +183,15 @@ static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = { struct rzg2l_csi2_format { u32 code; - unsigned int datatype; unsigned int bpp; }; static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = { - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, }; static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -574,6 +577,9 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd, if (fse->index != 0) return -EINVAL; + if (!rzg2l_csi2_code_to_fmt(fse->code)) + return -EINVAL; + fse->min_width = RZG2L_CSI2_MIN_WIDTH; fse->min_height = RZG2L_CSI2_MIN_HEIGHT; fse->max_width = RZG2L_CSI2_MAX_WIDTH; @@ -582,6 +588,25 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd, return 0; } +static int rzg2l_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); + struct media_pad *remote_pad; + + if (!csi2->remote_source) + return -ENODEV; + + remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]); + if (IS_ERR(remote_pad)) { + dev_err(csi2->dev, "can't get source pad of %s (%ld)\n", + csi2->remote_source->name, PTR_ERR(remote_pad)); + return PTR_ERR(remote_pad); + } + return v4l2_subdev_call(csi2->remote_source, pad, get_frame_desc, + remote_pad->index, fd); +} + static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { .s_stream = rzg2l_csi2_s_stream, .pre_streamon = rzg2l_csi2_pre_streamon, @@ -593,6 +618,7 @@ static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, + .get_frame_desc = rzg2l_csi2_get_frame_desc, }; static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { @@ -795,14 +821,17 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; csi2->subdev.entity.ops = &rzg2l_csi2_entity_ops; - csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; + csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; /* * TODO: RZ/G2L CSI2 supports 4 virtual channels, as virtual * channels should be implemented by streams API which is under * development lets hardcode to VC0 for now. */ - csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&csi2->subdev.entity, 2, csi2->pads); + csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; + ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads), + csi2->pads); if (ret) goto error_pm; @@ -865,9 +894,10 @@ static const struct of_device_id rzg2l_csi2_of_table[] = { { .compatible = "renesas,rzg2l-csi2", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table); static struct platform_driver rzg2l_csi2_pdrv = { - .remove_new = rzg2l_csi2_remove, + .remove = rzg2l_csi2_remove, .probe = rzg2l_csi2_probe, .driver = { .name = "rzg2l-csi2", diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index ac8ebae4ed07..76a2b451f1da 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -6,24 +6,55 @@ */ #include <linux/delay.h> -#include "rzg2l-cru.h" +#include <media/mipi-csi2.h> -struct rzg2l_cru_ip_format { - u32 code; - unsigned int datatype; - unsigned int bpp; -}; +#include "rzg2l-cru.h" +#include "rzg2l-cru-regs.h" static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, -}; - -enum rzg2l_csi2_pads { - RZG2L_CRU_IP_SINK = 0, - RZG2L_CRU_IP_SOURCE, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .format = V4L2_PIX_FMT_UYVY, + .bpp = 2, + .icndmr = ICnDMR_YCMODE_UYVY, + .yuv = true, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .format = V4L2_PIX_FMT_SBGGR8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .format = V4L2_PIX_FMT_SGBRG8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .format = V4L2_PIX_FMT_SGRBG8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .format = V4L2_PIX_FMT_SRGGB8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, }; -static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) { unsigned int i; @@ -34,6 +65,26 @@ static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int c return NULL; } +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { + if (rzg2l_cru_ip_formats[i].format == format) + return &rzg2l_cru_ip_formats[i]; + } + + return NULL; +} + +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index) +{ + if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) + return NULL; + + return &rzg2l_cru_ip_formats[index]; +} + struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) { struct v4l2_subdev_state *state; @@ -149,7 +200,7 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, if (fse->index != 0) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16) + if (!rzg2l_cru_ip_code_to_fmt(fse->code)) return -EINVAL; fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; @@ -217,8 +268,10 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops; - ip->pads[0].flags = MEDIA_PAD_FL_SINK; - ip->pads[1].flags = MEDIA_PAD_FL_SOURCE; + ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads); if (ret) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b16b8af6e8f8..cd69c8a686d3 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -15,75 +15,12 @@ #include <linux/delay.h> #include <linux/pm_runtime.h> +#include <media/mipi-csi2.h> #include <media/v4l2-ioctl.h> #include <media/videobuf2-dma-contig.h> #include "rzg2l-cru.h" - -/* HW CRU Registers Definition */ - -/* CRU Control Register */ -#define CRUnCTRL 0x0 -#define CRUnCTRL_VINSEL(x) ((x) << 0) - -/* CRU Interrupt Enable Register */ -#define CRUnIE 0x4 -#define CRUnIE_EFE BIT(17) - -/* CRU Interrupt Status Register */ -#define CRUnINTS 0x8 -#define CRUnINTS_SFS BIT(16) - -/* CRU Reset Register */ -#define CRUnRST 0xc -#define CRUnRST_VRESETN BIT(0) - -/* Memory Bank Base Address (Lower) Register for CRU Image Data */ -#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) - -/* Memory Bank Base Address (Higher) Register for CRU Image Data */ -#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) - -/* Memory Bank Enable Register for CRU Image Data */ -#define AMnMBVALID 0x148 -#define AMnMBVALID_MBVALID(x) GENMASK(x, 0) - -/* Memory Bank Status Register for CRU Image Data */ -#define AMnMBS 0x14c -#define AMnMBS_MBSTS 0x7 - -/* AXI Master FIFO Pointer Register for CRU Image Data */ -#define AMnFIFOPNTR 0x168 -#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) -#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) - -/* AXI Master Transfer Stop Register for CRU Image Data */ -#define AMnAXISTP 0x174 -#define AMnAXISTP_AXI_STOP BIT(0) - -/* AXI Master Transfer Stop Status Register for CRU Image Data */ -#define AMnAXISTPACK 0x178 -#define AMnAXISTPACK_AXI_STOP_ACK BIT(0) - -/* CRU Image Processing Enable Register */ -#define ICnEN 0x200 -#define ICnEN_ICEN BIT(0) - -/* CRU Image Processing Main Control Register */ -#define ICnMC 0x208 -#define ICnMC_CSCTHR BIT(5) -#define ICnMC_INF_YUV8_422 (0x1e << 16) -#define ICnMC_INF_USER (0x30 << 16) -#define ICnMC_VCSEL(x) ((x) << 22) -#define ICnMC_INF_MASK GENMASK(21, 16) - -/* CRU Module Status Register */ -#define ICnMS 0x254 -#define ICnMS_IA BIT(2) - -/* CRU Data Output Mode Register */ -#define ICnDMR 0x26c -#define ICnDMR_YCMODE_UYVY (1 << 4) +#include "rzg2l-cru-regs.h" #define RZG2L_TIMEOUT_MS 100 #define RZG2L_RETRIES 10 @@ -184,46 +121,6 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&cru->qlock, flags); } -static int rzg2l_cru_mc_validate_format(struct rzg2l_cru_dev *cru, - struct v4l2_subdev *sd, - struct media_pad *pad) -{ - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - fmt.pad = pad->index; - if (v4l2_subdev_call_state_active(sd, pad, get_fmt, &fmt)) - return -EPIPE; - - switch (fmt.format.code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - break; - default: - return -EPIPE; - } - - switch (fmt.format.field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_NONE: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: - break; - default: - return -EPIPE; - } - - if (fmt.format.width != cru->format.width || - fmt.format.height != cru->format.height) - return -EPIPE; - - return 0; -} - static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, int slot, dma_addr_t addr) { @@ -278,6 +175,7 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { unsigned int slot; + u32 amnaxiattr; /* * Set image data memory banks. @@ -287,55 +185,47 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) for (slot = 0; slot < cru->num_buf; slot++) rzg2l_cru_fill_hw_slot(cru, slot); + + /* Set AXI burst max length to recommended setting */ + amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK; + amnaxiattr |= AMnAXIATTR_AXILEN; + rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); } -static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, bool *input_is_yuv, - struct v4l2_mbus_framefmt *ip_sd_fmt) +static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc) { - u32 icnmc; - - switch (ip_sd_fmt->code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - icnmc = ICnMC_INF_YUV8_422; - *input_is_yuv = true; - break; - default: - *input_is_yuv = false; - icnmc = ICnMC_INF_USER; - break; - } + u32 icnmc = ICnMC_INF(ip_fmt->datatype); icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK); /* Set virtual channel CSI2 */ - icnmc |= ICnMC_VCSEL(cru->csi.channel); + icnmc |= ICnMC_VCSEL(csi_vc); rzg2l_cru_write(cru, ICnMC, icnmc); } static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, - struct v4l2_mbus_framefmt *ip_sd_fmt) + struct v4l2_mbus_framefmt *ip_sd_fmt, + u8 csi_vc) { - bool output_is_yuv = false; - bool input_is_yuv = false; - u32 icndmr; + const struct rzg2l_cru_ip_format *cru_video_fmt; + const struct rzg2l_cru_ip_format *cru_ip_fmt; - rzg2l_cru_csi2_setup(cru, &input_is_yuv, ip_sd_fmt); + cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code); + rzg2l_cru_csi2_setup(cru, cru_ip_fmt, csi_vc); /* Output format */ - switch (cru->format.pixelformat) { - case V4L2_PIX_FMT_UYVY: - icndmr = ICnDMR_YCMODE_UYVY; - output_is_yuv = true; - break; - default: + cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); + if (!cru_video_fmt) { dev_err(cru->dev, "Invalid pixelformat (0x%x)\n", cru->format.pixelformat); return -EINVAL; } /* If input and output use same colorspace, do bypass mode */ - if (output_is_yuv == input_is_yuv) + if (cru_ip_fmt->yuv == cru_video_fmt->yuv) rzg2l_cru_write(cru, ICnMC, rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR); else @@ -343,7 +233,7 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR)); /* Set output data format */ - rzg2l_cru_write(cru, ICnDMR, icndmr); + rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr); return 0; } @@ -422,12 +312,47 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) spin_unlock_irqrestore(&cru->qlock, flags); } +static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) +{ + struct v4l2_mbus_frame_desc fd = { }; + struct media_pad *remote_pad; + int ret; + + remote_pad = media_pad_remote_pad_unique(&cru->ip.pads[RZG2L_CRU_IP_SINK]); + ret = v4l2_subdev_call(cru->ip.remote, pad, get_frame_desc, remote_pad->index, &fd); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(cru->dev, "get_frame_desc failed on IP remote subdev\n"); + return ret; + } + /* If remote subdev does not implement .get_frame_desc default to VC0. */ + if (ret == -ENOIOCTLCMD) + return 0; + + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(cru->dev, "get_frame_desc returned invalid bus type %d\n", fd.type); + return -EINVAL; + } + + if (!fd.num_entries) { + dev_err(cru->dev, "get_frame_desc returned zero entries\n"); + return -EINVAL; + } + + return fd.entry[0].bus.csi2.vc; +} + int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); unsigned long flags; + u8 csi_vc; int ret; + ret = rzg2l_cru_get_virtual_channel(cru); + if (ret < 0) + return ret; + csi_vc = ret; + spin_lock_irqsave(&cru->qlock, flags); /* Select a video input */ @@ -444,7 +369,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) rzg2l_cru_initialize_axi(cru); /* Initialize image convert */ - ret = rzg2l_cru_initialize_image_conv(cru, fmt); + ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); if (ret) { spin_unlock_irqrestore(&cru->qlock, flags); return ret; @@ -492,10 +417,6 @@ static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) return stream_off_ret; } - ret = rzg2l_cru_mc_validate_format(cru, sd, pad); - if (ret) - return ret; - pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe; ret = video_device_pipeline_start(&cru->vdev, pipe); if (ret) @@ -527,7 +448,7 @@ static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) rzg2l_cru_set_stream(cru, 0); } -static irqreturn_t rzg2l_cru_irq(int irq, void *data) +irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; unsigned int handled = 0; @@ -637,21 +558,14 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count goto assert_aresetn; } - ret = request_irq(cru->image_conv_irq, rzg2l_cru_irq, - IRQF_SHARED, KBUILD_MODNAME, cru); - if (ret) { - dev_err(cru->dev, "failed to request irq\n"); - goto assert_presetn; - } - - /* Allocate scratch buffer. */ + /* Allocate scratch buffer */ cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, &cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) { return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); dev_err(cru->dev, "Failed to allocate scratch buffer\n"); ret = -ENOMEM; - goto free_image_conv_irq; + goto assert_presetn; } cru->sequence = 0; @@ -670,9 +584,6 @@ out: if (ret) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); -free_image_conv_irq: - free_irq(cru->image_conv_irq, cru); - assert_presetn: reset_control_assert(cru->presetn); @@ -698,7 +609,6 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); - free_irq(cru->image_conv_irq, cru); return_unused_buffers(cru, VB2_BUF_STATE_ERROR); reset_control_assert(cru->presetn); @@ -712,8 +622,6 @@ static const struct vb2_ops rzg2l_cru_qops = { .buf_queue = rzg2l_cru_buffer_queue, .start_streaming = rzg2l_cru_start_streaming_vq, .stop_streaming = rzg2l_cru_stop_streaming_vq, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru) @@ -775,46 +683,16 @@ error: * V4L2 stuff */ -static const struct v4l2_format_info rzg2l_cru_formats[] = { - { - .format = V4L2_PIX_FMT_UYVY, - .bpp[0] = 2, - }, -}; - -const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rzg2l_cru_formats); i++) - if (rzg2l_cru_formats[i].format == format) - return rzg2l_cru_formats + i; - - return NULL; -} - -static u32 rzg2l_cru_format_bytesperline(struct v4l2_pix_format *pix) -{ - const struct v4l2_format_info *fmt; - - fmt = rzg2l_cru_format_from_pixel(pix->pixelformat); - - if (WARN_ON(!fmt)) - return -EINVAL; - - return pix->width * fmt->bpp[0]; -} - -static u32 rzg2l_cru_format_sizeimage(struct v4l2_pix_format *pix) -{ - return pix->bytesperline * pix->height; -} - static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, struct v4l2_pix_format *pix) { - if (!rzg2l_cru_format_from_pixel(pix->pixelformat)) + const struct rzg2l_cru_ip_format *fmt; + + fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); + if (!fmt) { pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT; + fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); + } switch (pix->field) { case V4L2_FIELD_TOP: @@ -833,8 +711,8 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1, &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0); - pix->bytesperline = rzg2l_cru_format_bytesperline(pix); - pix->sizeimage = rzg2l_cru_format_sizeimage(pix); + pix->bytesperline = pix->width * fmt->bpp; + pix->sizeimage = pix->bytesperline * pix->height; dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", pix->width, pix->height, pix->bytesperline, pix->sizeimage); @@ -905,10 +783,13 @@ static int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv, static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index >= ARRAY_SIZE(rzg2l_cru_formats)) + const struct rzg2l_cru_ip_format *fmt; + + fmt = rzg2l_cru_ip_index_to_fmt(f->index); + if (!fmt) return -EINVAL; - f->pixelformat = rzg2l_cru_formats[f->index].format; + f->pixelformat = fmt->format; return 0; } @@ -984,6 +865,43 @@ static const struct v4l2_file_operations rzg2l_cru_fops = { .read = vb2_fop_read, }; +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int rzg2l_cru_video_link_validate(struct media_link *link) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + const struct rzg2l_cru_ip_format *video_fmt; + struct v4l2_subdev *subdev; + struct rzg2l_cru_dev *cru; + int ret; + + subdev = media_entity_to_v4l2_subdev(link->source->entity); + fmt.pad = link->source->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + cru = container_of(media_entity_to_video_device(link->sink->entity), + struct rzg2l_cru_dev, vdev); + video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); + + if (fmt.format.width != cru->format.width || + fmt.format.height != cru->format.height || + fmt.format.field != cru->format.field || + video_fmt->code != fmt.format.code) + return -EPIPE; + + return 0; +} + +static const struct media_entity_operations rzg2l_cru_video_media_ops = { + .link_validate = rzg2l_cru_video_link_validate, +}; + static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) { struct video_device *vdev = &cru->vdev; @@ -995,6 +913,7 @@ static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) vdev->lock = &cru->lock; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; vdev->device_caps |= V4L2_CAP_IO_MC; + vdev->entity.ops = &rzg2l_cru_video_media_ops; vdev->fops = &rzg2l_cru_fops; vdev->ioctl_ops = &rzg2l_cru_ioctl_ops; diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index 1e74dd601c2b..4ad7ae188d5b 100644 --- a/drivers/media/platform/renesas/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c @@ -360,8 +360,6 @@ static const struct vb2_ops sh_vou_qops = { .buf_queue = sh_vou_buf_queue, .start_streaming = sh_vou_start_streaming, .stop_streaming = sh_vou_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* Video IOCTLs */ @@ -1359,7 +1357,7 @@ static void sh_vou_remove(struct platform_device *pdev) } static struct platform_driver sh_vou = { - .remove_new = sh_vou_remove, + .remove = sh_vou_remove, .driver = { .name = "sh-vou", }, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index a8535c6e2c46..5dee0490c593 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -96,13 +96,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_compose(sd_state, pad); -} - static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -119,8 +112,8 @@ static void brx_try_format(struct vsp1_brx *brx, default: /* The BRx can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&brx->entity, sd_state, - BRX_PAD_SINK(0)); + format = v4l2_subdev_state_get_format(sd_state, + BRX_PAD_SINK(0)); fmt->code = format->code; break; } @@ -150,14 +143,14 @@ static int brx_set_format(struct v4l2_subdev *subdev, brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(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, state, fmt->pad); + compose = v4l2_subdev_state_get_compose(state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -169,8 +162,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, unsigned int i; for (i = 0; i <= brx->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&brx->entity, - state, i); + format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } } @@ -205,7 +197,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, state, sel->pad); + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -242,8 +234,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, state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(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); @@ -251,11 +242,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, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, state, sel->pad); + compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; done: @@ -281,6 +272,7 @@ static const struct v4l2_subdev_ops brx_ops = { */ static void brx_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -290,8 +282,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.state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 625776a9bda4..98645bd2a983 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = { */ static void clu_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -181,8 +182,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.state, - CLU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 9b087bd8df7d..b5d1f238f7be 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, list_add_tail(&released_brx->list_pipe, &pipe->entities); - /* Add the BRx to the pipeline. */ + /* + * Add the BRx to the pipeline, inserting it just before the + * WPF. + */ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", __func__, pipe->lif->index, BRX_NAME(brx)); @@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, pipe->brx->sink = &pipe->output->entity; pipe->brx->sink_pad = 0; - list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + list_add_tail(&pipe->brx->list_pipe, + &pipe->output->entity.list_pipe); } /* @@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, if (!rpf->entity.pipe) { rpf->entity.pipe = pipe; - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + list_add(&rpf->entity.list_pipe, &pipe->entities); } brx->inputs[i].rpf = rpf; @@ -546,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) struct vsp1_dl_body *dlb; unsigned int dl_flags = 0; + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + if (drm_pipe->force_brx_release) dl_flags |= VSP1_DL_FRAME_END_INTERNAL; if (pipe->output->writeback) @@ -567,9 +574,11 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) } vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, pipe, dl, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); } vsp1_dl_list_commit(dl, dl_flags); @@ -733,6 +742,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; + vsp1_pipeline_dump(pipe, "LIF setup"); + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) @@ -906,6 +917,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, } vsp1_du_pipeline_setup_inputs(vsp1, pipe); + + vsp1_pipeline_dump(pipe, "atomic update"); + vsp1_du_pipeline_configure(pipe); done: @@ -959,6 +973,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1) vsp1_pipeline_init(pipe); + pipe->partitions = 1; + pipe->part_table = &drm_pipe->partition; + pipe->frame_end = vsp1_du_pipeline_frame_end; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index ab8b7e3161a2..3fd95b53f27e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -20,6 +20,7 @@ /** * struct vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display + * @partition: the pre-calculated partition used by the pipeline * @width: output display width * @height: output display height * @force_brx_release: when set, release the BRx during the next reconfiguration @@ -31,6 +32,7 @@ */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; + struct vsp1_partition partition; unsigned int width; unsigned int height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 1aac44d68731..9fc6bf624a52 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -1005,7 +1005,7 @@ MODULE_DEVICE_TABLE(of, vsp1_of_match); static struct platform_driver vsp1_platform_driver = { .probe = vsp1_probe, - .remove_new = vsp1_remove, + .remove = vsp1_remove, .driver = { .name = "vsp1", .pm = &vsp1_pm_ops, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 0a5a7f9cc870..8b8945bd8f10 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -70,12 +70,13 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, } void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_stream) - entity->ops->configure_stream(entity, pipe, dl, dlb); + entity->ops->configure_stream(entity, state, pipe, dl, dlb); } void vsp1_entity_configure_frame(struct vsp1_entity *entity, @@ -89,11 +90,13 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_partition) - entity->ops->configure_partition(entity, pipe, dl, dlb); + entity->ops->configure_partition(entity, pipe, partition, + dl, dlb); } /* ----------------------------------------------------------------------------- @@ -127,49 +130,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity, } } -/** - * vsp1_entity_get_pad_format - Get a pad format from storage for an entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * - * Return the format stored in the given configuration for an entity's pad. The - * configuration can be an ACTIVE or TRY configuration. - */ -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_format(sd_state, pad); -} - -/** - * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * @target: the selection target - * - * Return the selection rectangle stored in the given configuration for an - * entity's pad. The configuration can be an ACTIVE or TRY configuration. The - * selection target can be COMPOSE or CROP. - */ -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target) -{ - switch (target) { - case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_state_get_compose(sd_state, pad); - case V4L2_SEL_TGT_CROP: - return v4l2_subdev_state_get_crop(sd_state, pad); - default: - return NULL; - } -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -191,7 +151,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -238,7 +198,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, state, 0); + format = v4l2_subdev_state_get_format(state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -276,7 +236,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); mutex_lock(&entity->lock); @@ -346,7 +306,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -374,19 +334,17 @@ 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, state, entity->source_pad); + format = v4l2_subdev_state_get_format(state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_COMPOSE); + selection = v4l2_subdev_state_get_compose(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 735f32dde4b5..1bcc9e27dfdc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -19,7 +19,6 @@ struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; -struct vsp1_partition_window; enum vsp1_entity_type { VSP1_ENTITY_BRS, @@ -78,19 +77,30 @@ struct vsp1_route { * configuration. */ struct vsp1_entity_operations { - void (*destroy)(struct vsp1_entity *); - void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_partition)(struct vsp1_entity *, - struct vsp1_pipeline *, - struct vsp1_dl_list *, - struct vsp1_dl_body *); - unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); - void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_partition *, unsigned int, - struct vsp1_partition_window *); + void (*destroy)(struct vsp1_entity *entity); + void (*configure_stream)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_frame)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_partition)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + unsigned int (*max_width)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe); + void (*partition)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window); }; struct vsp1_entity { @@ -138,20 +148,13 @@ struct v4l2_subdev_state * 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, - unsigned int pad); -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb); void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); @@ -163,6 +166,7 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 40c571a987ef..2c8ce7175a4e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { */ static void hgo_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -140,11 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); @@ -194,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) if (hgo == NULL) return ERR_PTR(-ENOMEM); + /* Initialize the video device and queue for statistics data. */ + ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", + &hgo_entity_ops, hgo_mbus_formats, + ARRAY_SIZE(hgo_mbus_formats), + HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); + if (ret < 0) { + vsp1_entity_destroy(&hgo->histo.entity); + return ERR_PTR(ret); + } + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&hgo->ctrls.handler, vsp1->info->gen >= 3 ? 2 : 1); @@ -209,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler; - /* Initialize the video device and queue for statistics data. */ - ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", - &hgo_entity_ops, hgo_mbus_formats, - ARRAY_SIZE(hgo_mbus_formats), - HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); - if (ret < 0) { - vsp1_entity_destroy(&hgo->histo.entity); - return ERR_PTR(ret); - } - return hgo; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 8281b86874ab..858f330d44fa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { */ static void hgt_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -139,11 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); @@ -193,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) if (hgt == NULL) return ERR_PTR(-ENOMEM); - /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&hgt->ctrls, 1); - v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); - - hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; - /* Initialize the video device and queue for statistics data. */ ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt", &hgt_entity_ops, hgt_mbus_formats, @@ -209,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) return ERR_PTR(ret); } + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&hgt->ctrls, 1); + v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); + + hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; + v4l2_ctrl_handler_setup(&hgt->ctrls); return hgt; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 71155282ca11..c762202877ba 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -36,9 +36,8 @@ struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { struct vsp1_histogram_buffer *buf = NULL; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); if (list_empty(&histo->irqqueue)) goto done; @@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo) histo->readout = true; done: - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); return buf; } @@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, size_t size) { struct vsp1_pipeline *pipe = histo->entity.pipe; - unsigned long flags; /* * The pipeline pointer is guaranteed to be valid as this function is @@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq) { struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -152,15 +148,13 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { .queue_setup = histo_queue_setup, .buf_prepare = histo_buffer_prepare, .buf_queue = histo_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = histo_start_streaming, .stop_streaming = histo_stop_streaming, }; @@ -222,9 +216,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, state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = crop->width; @@ -233,8 +225,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, state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -242,9 +233,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + break; + case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -261,13 +254,10 @@ static int histo_set_crop(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&histo->entity, sd_state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, HISTO_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); sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE, @@ -276,14 +266,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Set the crop rectangle and reset the compose rectangle. */ - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, V4L2_SEL_TGT_CROP); - *selection = sel->r; - - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); - *selection = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r; return 0; } @@ -292,7 +276,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_rect *compose; struct v4l2_rect *crop; unsigned int ratio; @@ -305,9 +288,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, sel->r.left = 0; sel->r.top = 0; - crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); /* * Clamp the width and height to acceptable values first and then @@ -332,9 +313,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, ratio = 1 << (crop->height * 2 / sel->r.height / 3); sel->r.height = crop->height / ratio; - compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(sd_state, sel->pad); *compose = sel->r; return 0; @@ -371,31 +350,22 @@ done: return ret; } -static int histo_get_format(struct v4l2_subdev *subdev, +static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct vsp1_histogram *histo = subdev_to_histo(subdev); + if (fmt->pad == HISTO_PAD_SOURCE) { fmt->format.code = MEDIA_BUS_FMT_FIXED; fmt->format.width = 0; fmt->format.height = 0; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; + return 0; } - return vsp1_subdev_get_pad_format(subdev, sd_state, fmt); -} - -static int histo_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_histogram *histo = subdev_to_histo(subdev); - - if (fmt->pad != HISTO_PAD_SINK) - return histo_get_format(subdev, sd_state, fmt); - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, histo->formats, histo->num_formats, HISTO_MIN_SIZE, HISTO_MIN_SIZE, @@ -405,7 +375,7 @@ static int histo_set_format(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops histo_pad_ops = { .enum_mbus_code = histo_enum_mbus_code, .enum_frame_size = histo_enum_frame_size, - .get_fmt = histo_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = histo_set_format, .get_selection = histo_get_selection, .set_selection = histo_set_selection, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index bc1299c29ac9..8ba2a7c7305c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -78,7 +78,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -101,8 +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, state, - HSIT_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; @@ -128,6 +127,7 @@ static const struct v4l2_subdev_ops hsit_ops = { */ static void hsit_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index b1d21a54837b..b3d83d1c5306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = { */ static void lif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -93,8 +94,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.state, - LIF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { case VI6_IP_VERSION_MODEL_VSPD_GEN2: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 451d24ab0b56..dd264e6532e0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = { */ static void lut_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 68d05243c3ee..bb0739f684f3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -301,6 +301,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPED; } +void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe, + const char *msg) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; + bool first = true; + + printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + const char *name; + + name = strchrnul(entity->subdev.name, ' '); + name = name ? name + 1 : entity->subdev.name; + + pr_cont("%s%s", first ? "" : ", ", name); + first = false; + } + + pr_cont("\n"); +} + /* Must be called with the pipe irqlock held. */ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { @@ -444,6 +466,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } +/* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + /* * Propagate the partition calculations through the pipeline * @@ -452,17 +478,82 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, * source. Each entity must produce the partition required for the previous * entity in the pipeline. */ -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window) +static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window) { struct vsp1_entity *entity; list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) { if (entity->ops->partition) - entity->ops->partition(entity, pipe, partition, index, - window); + entity->ops->partition(entity, entity->state, pipe, + partition, index, window); } } +/* + * vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a + * partition + * + * @pipe: the pipeline + * @partition: partition that will hold the calculated values + * @div_size: pre-determined maximum partition division size + * @index: partition index + */ +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct v4l2_rect window; + unsigned int modulus; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + + /* Initialise the partition with sane starting conditions. */ + window.left = index * div_size; + window.width = div_size; + window.top = 0; + window.height = format->height; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevent this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + window.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + window.width = (div_size / 2) + modulus; + window.left -= div_size / 2; + } + } else if (index == partitions) { + window.width = modulus; + } + } + + vsp1_pipeline_propagate_partition(pipe, partition, index, &window); +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 674b5748d929..1ba7bdbad5a8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -9,6 +9,7 @@ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ +#include <linux/dynamic_debug.h> #include <linux/kref.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -54,17 +55,6 @@ enum vsp1_pipeline_state { }; /* - * struct vsp1_partition_window - Partition window coordinates - * @left: horizontal coordinate of the partition start in pixels relative to the - * left edge of the image - * @width: partition width in pixels - */ -struct vsp1_partition_window { - unsigned int left; - unsigned int width; -}; - -/* * struct vsp1_partition - A description of a slice for the partition algorithm * @rpf: The RPF partition window configuration * @uds_sink: The UDS input partition window configuration @@ -73,11 +63,11 @@ struct vsp1_partition_window { * @wpf: The WPF partition window configuration */ struct vsp1_partition { - struct vsp1_partition_window rpf; - struct vsp1_partition_window uds_sink; - struct vsp1_partition_window uds_source; - struct vsp1_partition_window sru; - struct vsp1_partition_window wpf; + struct v4l2_rect rpf[VSP1_MAX_RPF]; + struct v4l2_rect uds_sink; + struct v4l2_rect uds_source; + struct v4l2_rect sru; + struct v4l2_rect wpf; }; /* @@ -106,7 +96,6 @@ struct vsp1_partition { * @configured: when false the @stream_config shall be written to the hardware * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame - * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline */ struct vsp1_pipeline { @@ -146,7 +135,6 @@ struct vsp1_pipeline { bool interlaced; unsigned int partitions; - struct vsp1_partition *partition; struct vsp1_partition *part_table; u32 underrun_count; @@ -155,6 +143,24 @@ struct vsp1_pipeline { void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); void vsp1_pipeline_init(struct vsp1_pipeline *pipe); +void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe, + const char *msg); + +#if defined(CONFIG_DYNAMIC_DEBUG) || \ + (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) +#define vsp1_pipeline_dump(pipe, msg) \ + _dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg) +#elif defined(DEBUG) +#define vsp1_pipeline_dump(pipe, msg) \ + __vsp1_pipeline_dump(NULL, pipe, msg) +#else +#define vsp1_pipeline_dump(pipe, msg) \ +({ \ + if (0) \ + __vsp1_pipeline_dump(NULL, pipe, msg); \ +}) +#endif + void vsp1_pipeline_run(struct vsp1_pipeline *pipe); bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); @@ -166,10 +172,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb, unsigned int alpha); -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window); + unsigned int div_size, + unsigned int index); const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index c47579efc65f..5c8b3ba1bd3c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, */ static void rpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -80,12 +81,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ - sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -157,10 +154,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->state, - rpf->brx_input, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(pipe->brx->state, + rpf->brx_input); left = compose->left; top = compose->top; } @@ -284,6 +279,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity, static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { @@ -292,7 +288,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_device *vsp1 = rpf->entity.vsp1; const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; - struct v4l2_rect crop; + struct v4l2_rect crop = partition->rpf[rpf->entity.index]; /* * Source size and crop offsets. @@ -302,22 +298,6 @@ 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.state); - - /* - * Partition Algorithm Control - * - * The partition algorithm can split this frame into multiple - * slices. We must scale our partition window based on the pipe - * configuration to match the destination partition window. - * To achieve this, we adjust our crop to provide a 'sub-crop' - * matching the expected partition window. Only 'left' and - * 'width' need to be adjusted. - */ - if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf.width; - crop.left += pipe->partition->rpf.left; - } if (pipe->interlaced) { crop.height = round_down(crop.height / 2, fmtinfo->vsub); @@ -366,12 +346,30 @@ static void rpf_configure_partition(struct vsp1_entity *entity, } static void rpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - partition->rpf = *window; + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index]; + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple slices. We + * must adjust our partition window based on the pipe configuration to + * match the destination partition window. To achieve this, we adjust + * our crop to provide a 'sub-crop' matching the expected partition + * window. + */ + *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); + + if (pipe->partitions > 1) { + rpf_rect->width = window->width; + rpf_rect->left += window->left; + } } static const struct vsp1_entity_operations rpf_entity_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 09fb6ffa14e2..9d38203e73d0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -16,12 +16,6 @@ #define RWPF_MIN_WIDTH 1 #define RWPF_MIN_HEIGHT 1 -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state) -{ - return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -79,7 +73,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, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* @@ -105,7 +99,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, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -113,8 +107,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, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); *format = fmt->format; if (rwpf->flip.rotate) { @@ -153,12 +146,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, state); + sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -204,8 +196,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); /* * Restrict the crop rectangle coordinates to multiples of 2 to avoid @@ -225,12 +216,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, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index e0d212c70b2f..5ac9f0a6fafc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h @@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops; -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state); - #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 11e008aa9f20..1759ce642e6e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -131,7 +131,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -184,8 +184,7 @@ static void sru_try_format(struct vsp1_sru *sru, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, sd_state, - SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); fmt->code = format->code; /* @@ -234,13 +233,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(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, state, - SRU_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); *format = fmt->format; sru_try_format(sru, state, SRU_PAD_SOURCE, format); @@ -267,6 +265,7 @@ static const struct v4l2_subdev_ops sru_ops = { */ static void sru_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -277,10 +276,8 @@ 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.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -301,16 +298,14 @@ static void sru_configure_stream(struct vsp1_entity *entity, } static unsigned int sru_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* * The maximum input width of the SRU is 288 input pixels, but 32 @@ -324,24 +319,24 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, } static void sru_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ if (input->width != output->width) { window->width /= 2; window->left /= 2; + window->height /= 2; + window->top /= 2; } partition->sru = *window; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index d89f1197b86c..c5a38478cf8c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -136,7 +136,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -183,8 +183,7 @@ static void uds_try_format(struct vsp1_uds *uds, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, sd_state, - UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK); fmt->code = format->code; uds_output_limits(format->width, &minimum, &maximum); @@ -217,13 +216,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(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, state, - UDS_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); *format = fmt->format; uds_try_format(uds, state, UDS_PAD_SOURCE, format); @@ -254,6 +252,7 @@ static const struct v4l2_subdev_ops uds_ops = { */ static void uds_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -265,10 +264,8 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -303,15 +300,11 @@ static void uds_configure_stream(struct vsp1_entity *entity, static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); - struct vsp1_partition *partition = pipe->partition; - const struct v4l2_mbus_framefmt *output; - - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); /* Input size clipping. */ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | @@ -323,22 +316,20 @@ static void uds_configure_partition(struct vsp1_entity *entity, vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, (partition->uds_source.width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height + (partition->uds_source.height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); } static unsigned int uds_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = output->width / input->width; /* @@ -364,28 +355,26 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, */ static void uds_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; - /* Initialise the partition state. */ - partition->uds_sink = *window; - partition->uds_source = *window; - - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width / output->width; partition->uds_sink.left = window->left * input->width / output->width; + partition->uds_sink.height = input->height; + partition->uds_sink.top = 0; + + partition->uds_source = *window; *window = partition->uds_sink; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index f66936a28a2a..edaf28b544d2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -104,8 +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, state, - UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -113,8 +112,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, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -150,7 +148,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(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); @@ -160,8 +158,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, state, - sel->pad, V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; done: @@ -191,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = { */ static void uif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -203,8 +201,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->state, - UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK); left = crop->left; width = crop->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 5a9cb0e5640e..03f4efd6b82b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (video->rwpf->fmtinfo->mbus != fmt.format.code || video->rwpf->format.height != fmt.format.height || - video->rwpf->format.width != fmt.format.width) - return -EINVAL; + video->rwpf->format.width != fmt.format.width) { + dev_dbg(video->vsp1->dev, + "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n", + video->rwpf->fmtinfo->mbus, video->rwpf->format.width, + video->rwpf->format.height, fmt.format.code, + fmt.format.width, fmt.format.height); + return -EPIPE; + } return 0; } @@ -173,131 +179,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video, } /* ----------------------------------------------------------------------------- - * VSP1 Partition Algorithm support - */ - -/** - * vsp1_video_calculate_partition - Calculate the active partition output window - * - * @pipe: the pipeline - * @partition: partition that will hold the calculated values - * @div_size: pre-determined maximum partition division size - * @index: partition index - */ -static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int div_size, - unsigned int index) -{ - const struct v4l2_mbus_framefmt *format; - struct vsp1_partition_window window; - unsigned int modulus; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - - /* A single partition simply processes the output size in full. */ - if (pipe->partitions <= 1) { - window.left = 0; - window.width = format->width; - - vsp1_pipeline_propagate_partition(pipe, partition, index, - &window); - return; - } - - /* Initialise the partition with sane starting conditions. */ - window.left = index * div_size; - window.width = div_size; - - modulus = format->width % div_size; - - /* - * We need to prevent the last partition from being smaller than the - * *minimum* width of the hardware capabilities. - * - * If the modulus is less than half of the partition size, - * the penultimate partition is reduced to half, which is added - * to the final partition: |1234|1234|1234|12|341| - * to prevent this: |1234|1234|1234|1234|1|. - */ - if (modulus) { - /* - * pipe->partitions is 1 based, whilst index is a 0 based index. - * Normalise this locally. - */ - unsigned int partitions = pipe->partitions - 1; - - if (modulus < div_size / 2) { - if (index == partitions - 1) { - /* Halve the penultimate partition. */ - window.width = div_size / 2; - } else if (index == partitions) { - /* Increase the final partition. */ - window.width = (div_size / 2) + modulus; - window.left -= div_size / 2; - } - } else if (index == partitions) { - window.width = modulus; - } - } - - vsp1_pipeline_propagate_partition(pipe, partition, index, &window); -} - -static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - const struct v4l2_mbus_framefmt *format; - struct vsp1_entity *entity; - unsigned int div_size; - unsigned int i; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - div_size = format->width; - - /* - * Only Gen3+ hardware requires image partitioning, Gen2 will operate - * with a single partition that covers the whole output. - */ - if (vsp1->info->gen >= 3) { - list_for_each_entry(entity, &pipe->entities, list_pipe) { - unsigned int entity_max; - - if (!entity->ops->max_width) - continue; - - entity_max = entity->ops->max_width(entity, pipe); - if (entity_max) - div_size = min(div_size, entity_max); - } - } - - pipe->partitions = DIV_ROUND_UP(format->width, div_size); - pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), - GFP_KERNEL); - if (!pipe->part_table) - return -ENOMEM; - - for (i = 0; i < pipe->partitions; ++i) - vsp1_video_calculate_partition(pipe, &pipe->part_table[i], - div_size, i); - - return 0; -} - -/* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -365,13 +246,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_partition *part = &pipe->part_table[partition]; struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; - pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, part, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) @@ -646,11 +526,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, struct vsp1_video *video) { + int ret; + vsp1_pipeline_init(pipe); pipe->frame_end = vsp1_video_pipeline_frame_end; - return vsp1_video_pipeline_build(pipe, video); + ret = vsp1_video_pipeline_build(pipe, video); + if (ret) + return ret; + + vsp1_pipeline_dump(pipe, "video"); + + return 0; } static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) @@ -784,6 +672,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } +static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + unsigned int i; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + div_size = format->width; + + /* + * Only Gen3+ hardware requires image partitioning, Gen2 will operate + * with a single partition that covers the whole output. + */ + if (vsp1->info->gen >= 3) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max; + + if (!entity->ops->max_width) + continue; + + entity_max = entity->ops->max_width(entity, + entity->state, + pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->partitions = DIV_ROUND_UP(format->width, div_size); + pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), + GFP_KERNEL); + if (!pipe->part_table) + return -ENOMEM; + + for (i = 0; i < pipe->partitions; ++i) + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i], + div_size, i); + + return 0; +} + static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; @@ -826,7 +762,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity, pipe, pipe->stream_config); - vsp1_entity_configure_stream(entity, pipe, NULL, + vsp1_entity_configure_stream(entity, entity->state, pipe, NULL, pipe->stream_config); } @@ -937,8 +873,6 @@ static const struct vb2_ops vsp1_video_queue_qops = { .queue_setup = vsp1_video_queue_setup, .buf_prepare = vsp1_video_buffer_prepare, .buf_queue = vsp1_video_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vsp1_video_start_streaming, .stop_streaming = vsp1_video_stop_streaming, }; @@ -1146,6 +1080,27 @@ static const struct v4l2_file_operations vsp1_video_fops = { }; /* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int vsp1_video_link_validate(struct media_link *link) +{ + /* + * Ideally, link validation should be implemented here instead of + * calling vsp1_video_verify_format() in vsp1_video_streamon() + * manually. That would however break userspace that start one video + * device before configures formats on other video devices in the + * pipeline. This operation is just a no-op to silence the warnings + * from v4l2_subdev_link_validate(). + */ + return 0; +} + +static const struct media_entity_operations vsp1_video_media_ops = { + .link_validate = vsp1_video_link_validate, +}; + +/* ----------------------------------------------------------------------------- * Suspend and Resume */ @@ -1279,6 +1234,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, /* ... and the video node... */ video->video.v4l2_dev = &video->vsp1->v4l2_dev; + video->video.entity.ops = &vsp1_video_media_ops; video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", rwpf->entity.subdev.name, direction); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 9693aeab1cac..f176750ccd98 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) goto done; } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -231,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, } static void wpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -245,12 +244,8 @@ static void wpf_configure_stream(struct vsp1_entity *entity, u32 srcrpf = 0; int ret; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); /* Format */ if (!pipe->lif || wpf->writeback) { @@ -367,13 +362,13 @@ static void wpf_configure_frame(struct vsp1_entity *entity, static void wpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; struct vsp1_rwpf_memory mem = wpf->mem; - const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; unsigned int width; @@ -383,21 +378,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int flip; unsigned int i; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - width = sink_format->width; - height = sink_format->height; - left = 0; - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. + * Cropping. The partition algorithm can split the image into multiple + * slices. */ - if (pipe->partitions > 1) { - width = pipe->partition->wpf.width; - left = pipe->partition->wpf.left; - } + width = partition->wpf.width; + left = partition->wpf.left; + height = partition->wpf.height; vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | @@ -508,6 +495,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, } static unsigned int wpf_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); @@ -516,10 +504,11 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity, } static void wpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { partition->wpf = *window; } diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 662c81b6d0b5..4396348811c8 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ @@ -195,6 +195,11 @@ static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) return ret; } + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->osequence = 0; + else + ctx->csequence = 0; + return 0; } @@ -213,8 +218,6 @@ const struct vb2_ops rga_qops = { .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, }; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 11c3d7234757..bf55beec0fac 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index e8917e5630a4..cc6bd7f5b030 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ #ifndef __RGA_HW_H__ diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 00fdfa9e10bc..3dccab5fa4a1 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ @@ -43,6 +43,8 @@ static void device_run(void *prv) rga->curr = ctx; src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + src->sequence = ctx->osequence++; + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); @@ -75,6 +77,8 @@ static irqreturn_t rga_isr(int irq, void *prv) v4l2_m2m_buf_copy_metadata(src, dst, true); + dst->sequence = ctx->csequence++; + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx); @@ -98,7 +102,7 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) src_vq->drv_priv = ctx; src_vq->ops = &rga_qops; src_vq->mem_ops = &vb2_dma_sg_memops; - dst_vq->gfp_flags = __GFP_DMA32; + src_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; @@ -968,7 +972,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rga_match); static struct platform_driver rga_pdrv = { .probe = rga_probe, - .remove_new = rga_remove, + .remove = rga_remove, .driver = { .name = RGA_NAME, .pm = &rga_pm, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 3502dff6055c..530e12de73c4 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ #ifndef __RGA_H__ @@ -57,6 +57,9 @@ struct rga_ctx { struct rga_frame out; struct v4l2_ctrl_handler ctrl_handler; + int osequence; + int csequence; + /* Control values */ u32 op; u32 hflip; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 2bddb4fa8a5c..6dcefd144d5a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -35,8 +35,6 @@ #define RKISP1_SP_DEV_NAME RKISP1_DRIVER_NAME "_selfpath" #define RKISP1_MP_DEV_NAME RKISP1_DRIVER_NAME "_mainpath" -#define RKISP1_MIN_BUFFERS_NEEDED 3 - enum rkisp1_plane { RKISP1_PLANE_Y = 0, RKISP1_PLANE_CB = 1, @@ -1203,8 +1201,6 @@ static const struct vb2_ops rkisp1_vb2_ops = { .buf_init = rkisp1_vb2_buf_init, .buf_queue = rkisp1_vb2_buf_queue, .buf_prepare = rkisp1_vb2_buf_prepare, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = rkisp1_vb2_stop_streaming, .start_streaming = rkisp1_vb2_start_streaming, }; @@ -1563,7 +1559,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_queued_buffers = RKISP1_MIN_BUFFERS_NEEDED; + q->min_queued_buffers = 1; 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.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c index f956b90a407a..60c97bb7b18b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c @@ -178,3 +178,17 @@ void rkisp1_sd_adjust_crop(struct v4l2_rect *crop, rkisp1_sd_adjust_crop_rect(crop, &crop_bounds); } + +void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern, + const u32 input[4], u32 output[4]) +{ + static const unsigned int swap[4][4] = { + [RKISP1_RAW_RGGB] = { 0, 1, 2, 3 }, + [RKISP1_RAW_GRBG] = { 1, 0, 3, 2 }, + [RKISP1_RAW_GBRG] = { 2, 3, 0, 1 }, + [RKISP1_RAW_BGGR] = { 3, 2, 1, 0 }, + }; + + for (unsigned int i = 0; i < 4; ++i) + output[i] = input[swap[pattern][i]]; +} diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 26573f6ae575..ca952fd0829b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -33,9 +33,10 @@ struct regmap; #define RKISP1_ISP_SD_SRC BIT(0) #define RKISP1_ISP_SD_SINK BIT(1) -/* min and max values for the widths and heights of the entities */ -#define RKISP1_ISP_MAX_WIDTH 4032 -#define RKISP1_ISP_MAX_HEIGHT 3024 +/* + * Minimum values for the width and height of entities. The maximum values are + * model-specific and stored in the rkisp1_info structure. + */ #define RKISP1_ISP_MIN_WIDTH 32 #define RKISP1_ISP_MIN_HEIGHT 32 @@ -115,6 +116,8 @@ enum rkisp1_isp_pad { * @RKISP1_FEATURE_SELF_PATH: The ISP has a self path * @RKISP1_FEATURE_DUAL_CROP: The ISP has the dual crop block at the resizer input * @RKISP1_FEATURE_DMA_34BIT: The ISP uses 34-bit DMA addresses + * @RKISP1_FEATURE_BLS: The ISP has a dedicated BLS block + * @RKISP1_FEATURE_COMPAND: The ISP has a companding block * * The ISP features are stored in a bitmask in &rkisp1_info.features and allow * the driver to implement support for features present in some ISP versions @@ -126,6 +129,8 @@ enum rkisp1_feature { RKISP1_FEATURE_SELF_PATH = BIT(2), RKISP1_FEATURE_DUAL_CROP = BIT(3), RKISP1_FEATURE_DMA_34BIT = BIT(4), + RKISP1_FEATURE_BLS = BIT(5), + RKISP1_FEATURE_COMPAND = BIT(6), }; #define rkisp1_has_feature(rkisp1, feature) \ @@ -140,6 +145,8 @@ enum rkisp1_feature { * @isr_size: number of entries in the @isrs array * @isp_ver: ISP version * @features: bitmask of rkisp1_feature features implemented by the ISP + * @max_width: maximum input frame width + * @max_height: maximum input frame height * * This structure contains information about the ISP specific to a particular * ISP model, version, or integration in a particular SoC. @@ -151,6 +158,8 @@ struct rkisp1_info { unsigned int isr_size; enum rkisp1_cif_isp_version isp_ver; unsigned int features; + unsigned int max_width; + unsigned int max_height; }; /* @@ -232,7 +241,7 @@ struct rkisp1_vdev_node { /* * struct rkisp1_buffer - A container for the vb2 buffers used by the video devices: - * params, stats, mainpath, selfpath + * stats, mainpath, selfpath * * @vb: vb2 buffer * @queue: entry of the buffer in the queue @@ -245,6 +254,26 @@ struct rkisp1_buffer { }; /* + * struct rkisp1_params_buffer - A container for the vb2 buffers used by the + * params video device + * + * @vb: vb2 buffer + * @queue: entry of the buffer in the queue + * @cfg: scratch buffer used for caching the ISP configuration parameters + */ +struct rkisp1_params_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; + void *cfg; +}; + +static inline struct rkisp1_params_buffer * +to_rkisp1_params_buffer(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct rkisp1_params_buffer, vb); +} + +/* * struct rkisp1_dummy_buffer - A buffer to write the next frame to in case * there are no vb2 buffers available. * @@ -372,9 +401,11 @@ struct rkisp1_params_ops { * @ops: pointer to the variant-specific operations * @config_lock: locks the buffer list 'params' * @params: queue of rkisp1_buffer - * @vdev_fmt: v4l2_format of the metadata format + * @metafmt the currently enabled metadata format * @quantization: the quantization configured on the isp's src pad + * @ycbcr_encoding the YCbCr encoding * @raw_type: the bayer pattern on the isp video sink pad + * @enabled_blocks: bitmask of enabled ISP blocks */ struct rkisp1_params { struct rkisp1_vdev_node vnode; @@ -383,11 +414,14 @@ struct rkisp1_params { spinlock_t config_lock; /* locks the buffers list 'params' */ struct list_head params; - struct v4l2_format vdev_fmt; + + const struct v4l2_meta_format *metafmt; enum v4l2_quantization quantization; enum v4l2_ycbcr_encoding ycbcr_encoding; enum rkisp1_fmt_raw_pat_type raw_type; + + u32 enabled_blocks; }; /* @@ -573,6 +607,9 @@ void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop, void rkisp1_sd_adjust_crop(struct v4l2_rect *crop, const struct v4l2_mbus_framefmt *bounds); +void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern, + const u32 input[4], u32 output[4]); + /* * rkisp1_mbus_info_get_by_code - get the isp info of the media bus code * diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 4202642e0523..841e58c20f7f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -307,6 +307,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct rkisp1_csi *csi = to_rkisp1_csi(sd); const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; @@ -326,10 +327,10 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, sink_fmt->width = clamp_t(u32, fmt->format.width, RKISP1_ISP_MIN_WIDTH, - RKISP1_ISP_MAX_WIDTH); + csi->rkisp1->info->max_width); sink_fmt->height = clamp_t(u32, fmt->format.height, RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + csi->rkisp1->info->max_height); fmt->format = *sink_fmt; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index bb0202386c70..dc65a7924f8a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -228,6 +228,9 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) break; } + if (ret) + break; + /* Parse the endpoint and validate the bus type. */ ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) { @@ -509,7 +512,10 @@ static const struct rkisp1_info px30_isp_info = { .isp_ver = RKISP1_V12, .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH - | RKISP1_FEATURE_DUAL_CROP, + | RKISP1_FEATURE_DUAL_CROP + | RKISP1_FEATURE_BLS, + .max_width = 3264, + .max_height = 2448, }; static const char * const rk3399_isp_clks[] = { @@ -530,7 +536,10 @@ static const struct rkisp1_info rk3399_isp_info = { .isp_ver = RKISP1_V10, .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH - | RKISP1_FEATURE_DUAL_CROP, + | RKISP1_FEATURE_DUAL_CROP + | RKISP1_FEATURE_BLS, + .max_width = 4416, + .max_height = 3312, }; static const char * const imx8mp_isp_clks[] = { @@ -550,7 +559,10 @@ static const struct rkisp1_info imx8mp_isp_info = { .isr_size = ARRAY_SIZE(imx8mp_isp_isrs), .isp_ver = RKISP1_V_IMX8MP, .features = RKISP1_FEATURE_MAIN_STRIDE - | RKISP1_FEATURE_DMA_34BIT, + | RKISP1_FEATURE_DMA_34BIT + | RKISP1_FEATURE_COMPAND, + .max_width = 4096, + .max_height = 3072, }; static const struct of_device_id rkisp1_of_match[] = { @@ -743,7 +755,7 @@ static struct platform_driver rkisp1_drv = { .pm = &rkisp1_pm_ops, }, .probe = rkisp1_probe, - .remove_new = rkisp1_remove, + .remove = rkisp1_remove, }; module_platform_driver(rkisp1_drv); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index e45a213baf49..d94917211828 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -173,7 +173,7 @@ static void rkisp1_gasket_disable(struct rkisp1_device *rkisp1) * or at the frame end interrupt */ static void rkisp1_config_ism(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state) + const struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = v4l2_subdev_state_get_crop(sd_state, @@ -201,7 +201,7 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp, * configure ISP blocks with input format, size...... */ static int rkisp1_config_isp(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { struct rkisp1_device *rkisp1 = isp->rkisp1; @@ -309,7 +309,7 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { rkisp1_params_disable(&rkisp1->params); } else { - struct v4l2_mbus_framefmt *src_frm; + const struct v4l2_mbus_framefmt *src_frm; src_frm = v4l2_subdev_state_get_format(sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); @@ -429,7 +429,7 @@ static void rkisp1_config_clk(struct rkisp1_isp *isp) } static int rkisp1_isp_start(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, struct media_pad *source) { struct rkisp1_device *rkisp1 = isp->rkisp1; @@ -517,6 +517,7 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { + struct rkisp1_isp *isp = to_rkisp1_isp(sd); const struct rkisp1_mbus_info *mbus_info; if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS || @@ -539,9 +540,9 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; fse->min_width = RKISP1_ISP_MIN_WIDTH; - fse->max_width = RKISP1_ISP_MAX_WIDTH; + fse->max_width = isp->rkisp1->info->max_width; fse->min_height = RKISP1_ISP_MIN_HEIGHT; - fse->max_height = RKISP1_ISP_MAX_HEIGHT; + fse->max_height = isp->rkisp1->info->max_height; return 0; } @@ -772,10 +773,10 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, - RKISP1_ISP_MAX_WIDTH); + isp->rkisp1->info->max_width); sink_fmt->height = clamp_t(u32, format->height, RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + isp->rkisp1->info->max_height); /* * Adjust the color space fields. Accept any color primaries and diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 173d1ea41874..b28f4140c8a3 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -5,6 +5,9 @@ * Copyright (C) 2017 Rockchip Electronics Co., Ltd. */ +#include <linux/math.h> +#include <linux/string.h> + #include <media/v4l2-common.h> #include <media/v4l2-event.h> #include <media/v4l2-ioctl.h> @@ -33,6 +36,59 @@ #define RKISP1_ISP_CC_COEFF(n) \ (RKISP1_CIF_ISP_CC_COEFF_0 + (n) * 4) +#define RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS BIT(0) +#define RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC BIT(1) + +union rkisp1_ext_params_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_ext_params_bls_config bls; + struct rkisp1_ext_params_dpcc_config dpcc; + struct rkisp1_ext_params_sdg_config sdg; + struct rkisp1_ext_params_lsc_config lsc; + struct rkisp1_ext_params_awb_gain_config awbg; + struct rkisp1_ext_params_flt_config flt; + struct rkisp1_ext_params_bdm_config bdm; + struct rkisp1_ext_params_ctk_config ctk; + struct rkisp1_ext_params_goc_config goc; + struct rkisp1_ext_params_dpf_config dpf; + struct rkisp1_ext_params_dpf_strength_config dpfs; + struct rkisp1_ext_params_cproc_config cproc; + struct rkisp1_ext_params_ie_config ie; + struct rkisp1_ext_params_awb_meas_config awbm; + struct rkisp1_ext_params_hst_config hst; + struct rkisp1_ext_params_aec_config aec; + struct rkisp1_ext_params_afc_config afc; + struct rkisp1_ext_params_compand_bls_config compand_bls; + struct rkisp1_ext_params_compand_curve_config compand_curve; +}; + +enum rkisp1_params_formats { + RKISP1_PARAMS_FIXED, + RKISP1_PARAMS_EXTENSIBLE, +}; + +static const struct v4l2_meta_format rkisp1_params_formats[] = { + [RKISP1_PARAMS_FIXED] = { + .dataformat = V4L2_META_FMT_RK_ISP1_PARAMS, + .buffersize = sizeof(struct rkisp1_params_cfg), + }, + [RKISP1_PARAMS_EXTENSIBLE] = { + .dataformat = V4L2_META_FMT_RK_ISP1_EXT_PARAMS, + .buffersize = sizeof(struct rkisp1_ext_params_cfg), + }, +}; + +static const struct v4l2_meta_format * +rkisp1_params_get_format_info(u32 dataformat) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(rkisp1_params_formats); i++) { + if (rkisp1_params_formats[i].dataformat == dataformat) + return &rkisp1_params_formats[i]; + } + + return &rkisp1_params_formats[RKISP1_PARAMS_FIXED]; +} + static inline void rkisp1_param_set_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask) { @@ -112,54 +168,20 @@ static void rkisp1_bls_config(struct rkisp1_params *params, new_control &= RKISP1_CIF_ISP_BLS_ENA; /* fixed subtraction values */ if (!arg->enable_auto) { - const struct rkisp1_cif_isp_bls_fixed_val *pval = - &arg->fixed_val; - - switch (params->raw_type) { - case RKISP1_RAW_BGGR: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->b); - break; - case RKISP1_RAW_GBRG: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->b); - break; - case RKISP1_RAW_GRBG: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->b); - break; - case RKISP1_RAW_RGGB: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->b); - break; - default: - break; - } - + static const u32 regs[] = { + RKISP1_CIF_ISP_BLS_A_FIXED, + RKISP1_CIF_ISP_BLS_B_FIXED, + RKISP1_CIF_ISP_BLS_C_FIXED, + RKISP1_CIF_ISP_BLS_D_FIXED, + }; + u32 swapped[4]; + + rkisp1_bls_swap_regs(params->raw_type, regs, swapped); + + rkisp1_write(params->rkisp1, swapped[0], arg->fixed_val.r); + rkisp1_write(params->rkisp1, swapped[1], arg->fixed_val.gr); + rkisp1_write(params->rkisp1, swapped[2], arg->fixed_val.gb); + rkisp1_write(params->rkisp1, swapped[3], arg->fixed_val.b); } else { if (arg->en_windows & BIT(1)) { rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_START, @@ -1239,6 +1261,93 @@ rkisp1_dpf_strength_config(struct rkisp1_params *params, rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_R, arg->r); } +static void rkisp1_compand_write_px_curve(struct rkisp1_params *params, + unsigned int addr, const u8 *curve) +{ + const unsigned int points_per_reg = 6; + const unsigned int num_regs = + DIV_ROUND_UP(RKISP1_CIF_ISP_COMPAND_NUM_POINTS, + points_per_reg); + + /* + * The compand curve is specified as a piecewise linear function with + * 64 points. X coordinates are stored as a log2 of the displacement + * from the previous point, in 5 bits, with 6 values per register. The + * last register stores 4 values. + */ + for (unsigned int reg = 0; reg < num_regs; ++reg) { + unsigned int num_points = + min(RKISP1_CIF_ISP_COMPAND_NUM_POINTS - + reg * points_per_reg, points_per_reg); + u32 val = 0; + + for (unsigned int i = 0; i < num_points; i++) + val |= (*curve++ & 0x1f) << (i * 5); + + rkisp1_write(params->rkisp1, addr, val); + addr += 4; + } +} + +static void +rkisp1_compand_write_curve_mem(struct rkisp1_params *params, + unsigned int reg_addr, unsigned int reg_data, + const u32 curve[RKISP1_CIF_ISP_COMPAND_NUM_POINTS]) +{ + for (unsigned int i = 0; i < RKISP1_CIF_ISP_COMPAND_NUM_POINTS; i++) { + rkisp1_write(params->rkisp1, reg_addr, i); + rkisp1_write(params->rkisp1, reg_data, curve[i]); + } +} + +static void +rkisp1_compand_bls_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_compand_bls_config *arg) +{ + static const u32 regs[] = { + RKISP1_CIF_ISP_COMPAND_BLS_A_FIXED, + RKISP1_CIF_ISP_COMPAND_BLS_B_FIXED, + RKISP1_CIF_ISP_COMPAND_BLS_C_FIXED, + RKISP1_CIF_ISP_COMPAND_BLS_D_FIXED, + }; + u32 swapped[4]; + + rkisp1_bls_swap_regs(params->raw_type, regs, swapped); + + rkisp1_write(params->rkisp1, swapped[0], arg->r); + rkisp1_write(params->rkisp1, swapped[1], arg->gr); + rkisp1_write(params->rkisp1, swapped[2], arg->gb); + rkisp1_write(params->rkisp1, swapped[3], arg->b); +} + +static void +rkisp1_compand_expand_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_compand_curve_config *arg) +{ + rkisp1_compand_write_px_curve(params, RKISP1_CIF_ISP_COMPAND_EXPAND_PX_N(0), + arg->px); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_EXPAND_Y_ADDR, + RKISP1_CIF_ISP_COMPAND_EXPAND_Y_WRITE_DATA, + arg->y); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_EXPAND_X_ADDR, + RKISP1_CIF_ISP_COMPAND_EXPAND_X_WRITE_DATA, + arg->x); +} + +static void +rkisp1_compand_compress_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_compand_curve_config *arg) +{ + rkisp1_compand_write_px_curve(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_PX_N(0), + arg->px); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_ADDR, + RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_WRITE_DATA, + arg->y); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_X_ADDR, + RKISP1_CIF_ISP_COMPAND_COMPRESS_X_WRITE_DATA, + arg->x); +} + static void rkisp1_isp_isr_other_config(struct rkisp1_params *params, const struct rkisp1_params_cfg *new_params) @@ -1249,6 +1358,12 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params, module_cfg_update = new_params->module_cfg_update; module_ens = new_params->module_ens; + if (!rkisp1_has_feature(params->rkisp1, BLS)) { + module_en_update &= ~RKISP1_CIF_ISP_MODULE_BLS; + module_cfg_update &= ~RKISP1_CIF_ISP_MODULE_BLS; + module_ens &= ~RKISP1_CIF_ISP_MODULE_BLS; + } + /* update dpc config */ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC) rkisp1_dpcc_config(params, @@ -1501,21 +1616,551 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params, } } -static bool rkisp1_params_get_buffer(struct rkisp1_params *params, - struct rkisp1_buffer **buf, - struct rkisp1_params_cfg **cfg) +/*------------------------------------------------------------------------------ + * Extensible parameters format handling + */ + +static void +rkisp1_ext_params_bls(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_bls_config *bls = &block->bls; + + if (bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL, + RKISP1_CIF_ISP_BLS_ENA); + return; + } + + rkisp1_bls_config(params, &bls->config); + + if ((bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(bls->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_BLS_CTRL, + RKISP1_CIF_ISP_BLS_ENA); +} + +static void +rkisp1_ext_params_dpcc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) { - if (list_empty(¶ms->params)) - return false; + const struct rkisp1_ext_params_dpcc_config *dpcc = &block->dpcc; + + if (dpcc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPCC_MODE, + RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE); + return; + } - *buf = list_first_entry(¶ms->params, struct rkisp1_buffer, queue); - *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); + rkisp1_dpcc_config(params, &dpcc->config); - return true; + if ((dpcc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(dpcc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPCC_MODE, + RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE); +} + +static void +rkisp1_ext_params_sdg(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_sdg_config *sdg = &block->sdg; + + if (sdg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); + return; + } + + rkisp1_sdg_config(params, &sdg->config); + + if ((sdg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(sdg->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); +} + +static void +rkisp1_ext_params_lsc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_lsc_config *lsc = &block->lsc; + + if (lsc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL, + RKISP1_CIF_ISP_LSC_CTRL_ENA); + return; + } + + rkisp1_lsc_config(params, &lsc->config); + + if ((lsc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(lsc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_LSC_CTRL, + RKISP1_CIF_ISP_LSC_CTRL_ENA); +} + +static void +rkisp1_ext_params_awbg(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_awb_gain_config *awbg = &block->awbg; + + if (awbg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); + return; + } + + params->ops->awb_gain_config(params, &awbg->config); + + if ((awbg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(awbg->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); +} + +static void +rkisp1_ext_params_flt(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_flt_config *flt = &block->flt; + + if (flt->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_FILT_MODE, + RKISP1_CIF_ISP_FLT_ENA); + return; + } + + rkisp1_flt_config(params, &flt->config); + + if ((flt->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(flt->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_FILT_MODE, + RKISP1_CIF_ISP_FLT_ENA); +} + +static void +rkisp1_ext_params_bdm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_bdm_config *bdm = &block->bdm; + + if (bdm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_BYPASS); + return; + } + + rkisp1_bdm_config(params, &bdm->config); + + if ((bdm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(bdm->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_BYPASS); +} + +static void +rkisp1_ext_params_ctk(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_ctk_config *ctk = &block->ctk; + + if (ctk->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_ctk_enable(params, false); + return; + } + + rkisp1_ctk_config(params, &ctk->config); + + if ((ctk->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(ctk->header.type))) + rkisp1_ctk_enable(params, true); +} + +static void +rkisp1_ext_params_goc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_goc_config *goc = &block->goc; + + if (goc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); + return; + } + + params->ops->goc_config(params, &goc->config); + + /* + * Unconditionally re-enable the GOC module which gets disabled by + * goc_config(). + */ + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); +} + +static void +rkisp1_ext_params_dpf(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_dpf_config *dpf = &block->dpf; + + if (dpf->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE, + RKISP1_CIF_ISP_DPF_MODE_EN); + return; + } + + rkisp1_dpf_config(params, &dpf->config); + + if ((dpf->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(dpf->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPF_MODE, + RKISP1_CIF_ISP_DPF_MODE_EN); +} + +static void +rkisp1_ext_params_dpfs(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_dpf_strength_config *dpfs = &block->dpfs; + + rkisp1_dpf_strength_config(params, &dpfs->config); +} + +static void +rkisp1_ext_params_cproc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_cproc_config *cproc = &block->cproc; + + if (cproc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_C_PROC_CTRL, + RKISP1_CIF_C_PROC_CTR_ENABLE); + return; + } + + rkisp1_cproc_config(params, &cproc->config); + + if ((cproc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(cproc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_C_PROC_CTRL, + RKISP1_CIF_C_PROC_CTR_ENABLE); +} + +static void +rkisp1_ext_params_ie(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_ie_config *ie = &block->ie; + + if (ie->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_ie_enable(params, false); + return; + } + + rkisp1_ie_config(params, &ie->config); + + if ((ie->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(ie->header.type))) + rkisp1_ie_enable(params, true); +} + +static void +rkisp1_ext_params_awbm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_awb_meas_config *awbm = &block->awbm; + + if (awbm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + params->ops->awb_meas_enable(params, &awbm->config, + false); + return; + } + + params->ops->awb_meas_config(params, &awbm->config); + + if ((awbm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(awbm->header.type))) + params->ops->awb_meas_enable(params, &awbm->config, + true); +} + +static void +rkisp1_ext_params_hstm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_hst_config *hst = &block->hst; + + if (hst->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + params->ops->hst_enable(params, &hst->config, false); + return; + } + + params->ops->hst_config(params, &hst->config); + + if ((hst->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(hst->header.type))) + params->ops->hst_enable(params, &hst->config, true); +} + +static void +rkisp1_ext_params_aecm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_aec_config *aec = &block->aec; + + if (aec->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_EXP_CTRL, + RKISP1_CIF_ISP_EXP_ENA); + return; + } + + params->ops->aec_config(params, &aec->config); + + if ((aec->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(aec->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_EXP_CTRL, + RKISP1_CIF_ISP_EXP_ENA); +} + +static void +rkisp1_ext_params_afcm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_afc_config *afc = &block->afc; + + if (afc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_AFM_CTRL, + RKISP1_CIF_ISP_AFM_ENA); + return; + } + + params->ops->afm_config(params, &afc->config); + + if ((afc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(afc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_AFM_CTRL, + RKISP1_CIF_ISP_AFM_ENA); +} + +static void rkisp1_ext_params_compand_bls(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_compand_bls_config *bls = + &block->compand_bls; + + if (bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE); + return; + } + + rkisp1_compand_bls_config(params, &bls->config); + + if ((bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(bls->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE); +} + +static void rkisp1_ext_params_compand_expand(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_compand_curve_config *curve = + &block->compand_curve; + + if (curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE); + return; + } + + rkisp1_compand_expand_config(params, &curve->config); + + if ((curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(curve->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE); +} + +static void rkisp1_ext_params_compand_compress(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_compand_curve_config *curve = + &block->compand_curve; + + if (curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE); + return; + } + + rkisp1_compand_compress_config(params, &curve->config); + + if ((curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(curve->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE); +} + +typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, + const union rkisp1_ext_params_config *config); + +static const struct rkisp1_ext_params_handler { + size_t size; + rkisp1_block_handler handler; + unsigned int group; + unsigned int features; +} rkisp1_ext_params_handlers[] = { + [RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS] = { + .size = sizeof(struct rkisp1_ext_params_bls_config), + .handler = rkisp1_ext_params_bls, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_BLS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPCC] = { + .size = sizeof(struct rkisp1_ext_params_dpcc_config), + .handler = rkisp1_ext_params_dpcc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_SDG] = { + .size = sizeof(struct rkisp1_ext_params_sdg_config), + .handler = rkisp1_ext_params_sdg, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_GAIN] = { + .size = sizeof(struct rkisp1_ext_params_awb_gain_config), + .handler = rkisp1_ext_params_awbg, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_FLT] = { + .size = sizeof(struct rkisp1_ext_params_flt_config), + .handler = rkisp1_ext_params_flt, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_BDM] = { + .size = sizeof(struct rkisp1_ext_params_bdm_config), + .handler = rkisp1_ext_params_bdm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CTK] = { + .size = sizeof(struct rkisp1_ext_params_ctk_config), + .handler = rkisp1_ext_params_ctk, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_GOC] = { + .size = sizeof(struct rkisp1_ext_params_goc_config), + .handler = rkisp1_ext_params_goc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF] = { + .size = sizeof(struct rkisp1_ext_params_dpf_config), + .handler = rkisp1_ext_params_dpf, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF_STRENGTH] = { + .size = sizeof(struct rkisp1_ext_params_dpf_strength_config), + .handler = rkisp1_ext_params_dpfs, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CPROC] = { + .size = sizeof(struct rkisp1_ext_params_cproc_config), + .handler = rkisp1_ext_params_cproc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_IE] = { + .size = sizeof(struct rkisp1_ext_params_ie_config), + .handler = rkisp1_ext_params_ie, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_LSC] = { + .size = sizeof(struct rkisp1_ext_params_lsc_config), + .handler = rkisp1_ext_params_lsc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_awb_meas_config), + .handler = rkisp1_ext_params_awbm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_hst_config), + .handler = rkisp1_ext_params_hstm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_aec_config), + .handler = rkisp1_ext_params_aecm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_afc_config), + .handler = rkisp1_ext_params_afcm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS] = { + .size = sizeof(struct rkisp1_ext_params_compand_bls_config), + .handler = rkisp1_ext_params_compand_bls, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_COMPAND, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND] = { + .size = sizeof(struct rkisp1_ext_params_compand_curve_config), + .handler = rkisp1_ext_params_compand_expand, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_COMPAND, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS] = { + .size = sizeof(struct rkisp1_ext_params_compand_curve_config), + .handler = rkisp1_ext_params_compand_compress, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_COMPAND, + }, +}; + +static void rkisp1_ext_params_config(struct rkisp1_params *params, + struct rkisp1_ext_params_cfg *cfg, + u32 block_group_mask) +{ + size_t block_offset = 0; + + if (WARN_ON(!cfg)) + return; + + /* Walk the list of parameter blocks and process them. */ + while (block_offset < cfg->data_size) { + const struct rkisp1_ext_params_handler *block_handler; + const union rkisp1_ext_params_config *block; + + block = (const union rkisp1_ext_params_config *) + &cfg->data[block_offset]; + block_offset += block->header.size; + + /* + * Make sure the block is supported by the platform and in the + * list of groups to configure. + */ + block_handler = &rkisp1_ext_params_handlers[block->header.type]; + if (!(block_handler->group & block_group_mask)) + continue; + + if ((params->rkisp1->info->features & block_handler->features) != + block_handler->features) + continue; + + block_handler->handler(params, block); + + if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) + params->enabled_blocks &= ~BIT(block->header.type); + else if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) + params->enabled_blocks |= BIT(block->header.type); + } } static void rkisp1_params_complete_buffer(struct rkisp1_params *params, - struct rkisp1_buffer *buf, + struct rkisp1_params_buffer *buf, unsigned int frame_sequence) { list_del(&buf->queue); @@ -1527,17 +2172,24 @@ static void rkisp1_params_complete_buffer(struct rkisp1_params *params, void rkisp1_params_isr(struct rkisp1_device *rkisp1) { struct rkisp1_params *params = &rkisp1->params; - struct rkisp1_params_cfg *new_params; - struct rkisp1_buffer *cur_buf; + struct rkisp1_params_buffer *cur_buf; spin_lock(¶ms->config_lock); - if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params)) + cur_buf = list_first_entry_or_null(¶ms->params, + struct rkisp1_params_buffer, queue); + if (!cur_buf) goto unlock; - rkisp1_isp_isr_other_config(params, new_params); - rkisp1_isp_isr_lsc_config(params, new_params); - rkisp1_isp_isr_meas_config(params, new_params); + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) { + rkisp1_isp_isr_other_config(params, cur_buf->cfg); + rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); + rkisp1_isp_isr_meas_config(params, cur_buf->cfg); + } else { + rkisp1_ext_params_config(params, cur_buf->cfg, + RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS | + RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC); + } /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1603,8 +2255,7 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params, enum v4l2_ycbcr_encoding ycbcr_encoding) { struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config; - struct rkisp1_params_cfg *new_params; - struct rkisp1_buffer *cur_buf; + struct rkisp1_params_buffer *cur_buf; params->quantization = quantization; params->ycbcr_encoding = ycbcr_encoding; @@ -1633,11 +2284,18 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params, /* apply the first buffer if there is one already */ - if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params)) + cur_buf = list_first_entry_or_null(¶ms->params, + struct rkisp1_params_buffer, queue); + if (!cur_buf) goto unlock; - rkisp1_isp_isr_other_config(params, new_params); - rkisp1_isp_isr_meas_config(params, new_params); + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) { + rkisp1_isp_isr_other_config(params, cur_buf->cfg); + rkisp1_isp_isr_meas_config(params, cur_buf->cfg); + } else { + rkisp1_ext_params_config(params, cur_buf->cfg, + RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS); + } /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1649,8 +2307,7 @@ unlock: void rkisp1_params_post_configure(struct rkisp1_params *params) { - struct rkisp1_params_cfg *new_params; - struct rkisp1_buffer *cur_buf; + struct rkisp1_params_buffer *cur_buf; spin_lock_irq(¶ms->config_lock); @@ -1662,11 +2319,16 @@ void rkisp1_params_post_configure(struct rkisp1_params *params) * ordering doesn't affect other ISP versions negatively, do so * unconditionally. */ - - if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params)) + cur_buf = list_first_entry_or_null(¶ms->params, + struct rkisp1_params_buffer, queue); + if (!cur_buf) goto unlock; - rkisp1_isp_isr_lsc_config(params, new_params); + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) + rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); + else + rkisp1_ext_params_config(params, cur_buf->cfg, + RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC); /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1742,12 +2404,12 @@ static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct video_device *video = video_devdata(file); - struct rkisp1_params *params = video_get_drvdata(video); - if (f->index > 0 || f->type != video->queue->type) + if (f->index >= ARRAY_SIZE(rkisp1_params_formats) || + f->type != video->queue->type) return -EINVAL; - f->pixelformat = params->vdev_fmt.fmt.meta.dataformat; + f->pixelformat = rkisp1_params_formats[f->index].dataformat; return 0; } @@ -1762,9 +2424,40 @@ static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh, if (f->type != video->queue->type) return -EINVAL; - memset(meta, 0, sizeof(*meta)); - meta->dataformat = params->vdev_fmt.fmt.meta.dataformat; - meta->buffersize = params->vdev_fmt.fmt.meta.buffersize; + *meta = *params->metafmt; + + return 0; +} + +static int rkisp1_params_try_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *video = video_devdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != video->queue->type) + return -EINVAL; + + *meta = *rkisp1_params_get_format_info(meta->dataformat); + + return 0; +} + +static int rkisp1_params_s_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *video = video_devdata(file); + struct rkisp1_params *params = video_get_drvdata(video); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != video->queue->type) + return -EINVAL; + + if (vb2_is_busy(video->queue)) + return -EBUSY; + + params->metafmt = rkisp1_params_get_format_info(meta->dataformat); + *meta = *params->metafmt; return 0; } @@ -1794,8 +2487,8 @@ static const struct v4l2_ioctl_ops rkisp1_params_ioctl = { .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out, .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out, - .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out, - .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = rkisp1_params_s_fmt_meta_out, + .vidioc_try_fmt_meta_out = rkisp1_params_try_fmt_meta_out, .vidioc_querycap = rkisp1_params_querycap, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -1807,22 +2500,46 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { + struct rkisp1_params *params = vq->drv_priv; + *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_PARAMS_REQ_BUFS_MIN, RKISP1_ISP_PARAMS_REQ_BUFS_MAX); *num_planes = 1; - sizes[0] = sizeof(struct rkisp1_params_cfg); + sizes[0] = params->metafmt->buffersize; return 0; } +static int rkisp1_params_vb2_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + struct rkisp1_params *params = vb->vb2_queue->drv_priv; + + params_buf->cfg = kvmalloc(params->metafmt->buffersize, + GFP_KERNEL); + if (!params_buf->cfg) + return -ENOMEM; + + return 0; +} + +static void rkisp1_params_vb2_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + + kvfree(params_buf->cfg); + params_buf->cfg = NULL; +} + static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct rkisp1_buffer *params_buf = - container_of(vbuf, struct rkisp1_buffer, vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); struct vb2_queue *vq = vb->vb2_queue; struct rkisp1_params *params = vq->drv_priv; @@ -1831,12 +2548,133 @@ static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) spin_unlock_irq(¶ms->config_lock); } +static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params, + struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + size_t header_size = offsetof(struct rkisp1_ext_params_cfg, data); + struct rkisp1_ext_params_cfg *cfg = params_buf->cfg; + size_t payload_size = vb2_get_plane_payload(vb, 0); + struct rkisp1_ext_params_cfg *usr_cfg = + vb2_plane_vaddr(&vbuf->vb2_buf, 0); + size_t block_offset = 0; + size_t cfg_size; + + /* + * Validate the buffer payload size before copying the parameters. The + * payload has to be smaller than the destination buffer size and larger + * than the header size. + */ + if (payload_size > params->metafmt->buffersize) { + dev_dbg(params->rkisp1->dev, + "Too large buffer payload size %zu\n", payload_size); + return -EINVAL; + } + + if (payload_size < header_size) { + dev_dbg(params->rkisp1->dev, + "Buffer payload %zu smaller than header size %zu\n", + payload_size, header_size); + return -EINVAL; + } + + /* + * Copy the parameters buffer to the internal scratch buffer to avoid + * userspace modifying the buffer content while the driver processes it. + */ + memcpy(cfg, usr_cfg, payload_size); + + /* Only v1 is supported at the moment. */ + if (cfg->version != RKISP1_EXT_PARAM_BUFFER_V1) { + dev_dbg(params->rkisp1->dev, + "Unsupported extensible format version: %u\n", + cfg->version); + return -EINVAL; + } + + /* Validate the size reported in the parameters buffer header. */ + cfg_size = header_size + cfg->data_size; + if (cfg_size != payload_size) { + dev_dbg(params->rkisp1->dev, + "Data size %zu different than buffer payload size %zu\n", + cfg_size, payload_size); + return -EINVAL; + } + + /* Walk the list of parameter blocks and validate them. */ + cfg_size = cfg->data_size; + while (cfg_size >= sizeof(struct rkisp1_ext_params_block_header)) { + const struct rkisp1_ext_params_block_header *block; + const struct rkisp1_ext_params_handler *handler; + + block = (const struct rkisp1_ext_params_block_header *) + &cfg->data[block_offset]; + + if (block->type >= ARRAY_SIZE(rkisp1_ext_params_handlers)) { + dev_dbg(params->rkisp1->dev, + "Invalid parameters block type\n"); + return -EINVAL; + } + + if (block->size > cfg_size) { + dev_dbg(params->rkisp1->dev, + "Premature end of parameters data\n"); + return -EINVAL; + } + + if ((block->flags & (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE | + RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) == + (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE | + RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) { + dev_dbg(params->rkisp1->dev, + "Invalid parameters block flags\n"); + return -EINVAL; + } + + handler = &rkisp1_ext_params_handlers[block->type]; + if (block->size != handler->size) { + dev_dbg(params->rkisp1->dev, + "Invalid parameters block size\n"); + return -EINVAL; + } + + block_offset += block->size; + cfg_size -= block->size; + } + + if (cfg_size) { + dev_dbg(params->rkisp1->dev, + "Unexpected data after the parameters buffer end\n"); + return -EINVAL; + } + + return 0; +} + static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb) { - if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_params_cfg)) + struct rkisp1_params *params = vb->vb2_queue->drv_priv; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + struct rkisp1_params_cfg *cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + size_t payload = vb2_get_plane_payload(vb, 0); + + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_EXT_PARAMS) + return rkisp1_params_prepare_ext_params(params, vb); + + /* + * For the fixed parameters format the payload size must be exactly the + * size of the parameters structure. + */ + if (payload != sizeof(*cfg)) return -EINVAL; - vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_params_cfg)); + /* + * Copy the parameters buffer to the internal scratch buffer to avoid + * userspace modifying the buffer content while the driver processes it. + */ + memcpy(params_buf->cfg, cfg, payload); return 0; } @@ -1844,7 +2682,7 @@ static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb) static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) { struct rkisp1_params *params = vq->drv_priv; - struct rkisp1_buffer *buf; + struct rkisp1_params_buffer *buf; LIST_HEAD(tmp_list); /* @@ -1858,16 +2696,17 @@ static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) list_for_each_entry(buf, &tmp_list, queue) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + + params->enabled_blocks = 0; } static const struct vb2_ops rkisp1_params_vb2_ops = { .queue_setup = rkisp1_params_vb2_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, + .buf_init = rkisp1_params_vb2_buf_init, + .buf_cleanup = rkisp1_params_vb2_buf_cleanup, .buf_queue = rkisp1_params_vb2_buf_queue, .buf_prepare = rkisp1_params_vb2_buf_prepare, .stop_streaming = rkisp1_params_vb2_stop_streaming, - }; static const struct v4l2_file_operations rkisp1_params_fops = { @@ -1890,26 +2729,13 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q, q->drv_priv = params; q->ops = &rkisp1_params_vb2_ops; q->mem_ops = &vb2_vmalloc_memops; - q->buf_struct_size = sizeof(struct rkisp1_buffer); + q->buf_struct_size = sizeof(struct rkisp1_params_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->vlock; return vb2_queue_init(q); } -static void rkisp1_init_params(struct rkisp1_params *params) -{ - params->vdev_fmt.fmt.meta.dataformat = - V4L2_META_FMT_RK_ISP1_PARAMS; - params->vdev_fmt.fmt.meta.buffersize = - sizeof(struct rkisp1_params_cfg); - - if (params->rkisp1->info->isp_ver == RKISP1_V12) - params->ops = &rkisp1_v12_params_ops; - else - params->ops = &rkisp1_v10_params_ops; -} - int rkisp1_params_register(struct rkisp1_device *rkisp1) { struct rkisp1_params *params = &rkisp1->params; @@ -1938,7 +2764,14 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1) vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT; vdev->vfl_dir = VFL_DIR_TX; rkisp1_params_init_vb2_queue(vdev->queue, params); - rkisp1_init_params(params); + + params->metafmt = &rkisp1_params_formats[RKISP1_PARAMS_FIXED]; + + if (params->rkisp1->info->isp_ver == RKISP1_V12) + params->ops = &rkisp1_v12_params_ops; + else + params->ops = &rkisp1_v10_params_ops; + video_set_drvdata(vdev, params); node->pad.flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index fccf4c17ee8d..bf0260600a19 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -704,6 +704,12 @@ #define RKISP1_CIF_ISP_DPF_SPATIAL_COEFF_MAX 0x1f #define RKISP1_CIF_ISP_DPF_NLL_COEFF_N_MAX 0x3ff +/* COMPAND */ +#define RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE BIT(0) +#define RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE BIT(1) +#define RKISP1_CIF_ISP_COMPAND_CTRL_SOFT_RESET_FLAG BIT(2) +#define RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE BIT(3) + /* =================================================================== */ /* CIF Registers */ /* =================================================================== */ @@ -1394,6 +1400,23 @@ #define RKISP1_CIF_ISP_VSM_DELTA_H (RKISP1_CIF_ISP_VSM_BASE + 0x0000001c) #define RKISP1_CIF_ISP_VSM_DELTA_V (RKISP1_CIF_ISP_VSM_BASE + 0x00000020) +#define RKISP1_CIF_ISP_COMPAND_BASE 0x00003200 +#define RKISP1_CIF_ISP_COMPAND_CTRL (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000000) +#define RKISP1_CIF_ISP_COMPAND_BLS_A_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000004) +#define RKISP1_CIF_ISP_COMPAND_BLS_B_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000008) +#define RKISP1_CIF_ISP_COMPAND_BLS_C_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000000c) +#define RKISP1_CIF_ISP_COMPAND_BLS_D_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000010) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_PX_N(n) (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000014 + (n) * 4) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_PX_N(n) (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000040 + (n) * 4) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_Y_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000006c) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_Y_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000070) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000074) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000078) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_X_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000007c) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_X_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000080) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_X_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000084) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_X_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000088) + #define RKISP1_CIF_ISP_CSI0_BASE 0x00007000 #define RKISP1_CIF_ISP_CSI0_CTRL0 (RKISP1_CIF_ISP_CSI0_BASE + 0x00000000) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 6f3931ca5b51..f073e72a0d37 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -135,11 +135,11 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz, /* configure dual-crop unit */ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state) + const struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = rsz->rkisp1; - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *sink_crop; + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_rect *sink_crop; u32 dc_ctrl; sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); @@ -264,7 +264,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, enum rkisp1_shadow_regs_when when) { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; @@ -494,10 +494,10 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, - RKISP1_ISP_MAX_WIDTH); + rsz->rkisp1->info->max_width); sink_fmt->height = clamp_t(u32, format->height, RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + rsz->rkisp1->info->max_height); /* * Adjust the color space fields. Accept any color primaries and diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c index 2795eef91bdd..d5fdb8f82dc7 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c @@ -150,8 +150,6 @@ static const struct vb2_ops rkisp1_stats_vb2_ops = { .queue_setup = rkisp1_stats_vb2_queue_setup, .buf_queue = rkisp1_stats_vb2_buf_queue, .buf_prepare = rkisp1_stats_vb2_buf_prepare, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = rkisp1_stats_vb2_stop_streaming, }; @@ -304,48 +302,25 @@ static void rkisp1_stats_get_hst_meas_v12(struct rkisp1_stats *stats, static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats, struct rkisp1_stat_buffer *pbuf) { + static const u32 regs[] = { + RKISP1_CIF_ISP_BLS_A_MEASURED, + RKISP1_CIF_ISP_BLS_B_MEASURED, + RKISP1_CIF_ISP_BLS_C_MEASURED, + RKISP1_CIF_ISP_BLS_D_MEASURED, + }; struct rkisp1_device *rkisp1 = stats->rkisp1; const struct rkisp1_mbus_info *in_fmt = rkisp1->isp.sink_fmt; struct rkisp1_cif_isp_bls_meas_val *bls_val; + u32 swapped[4]; + + rkisp1_bls_swap_regs(in_fmt->bayer_pat, regs, swapped); bls_val = &pbuf->params.ae.bls_val; - if (in_fmt->bayer_pat == RKISP1_RAW_BGGR) { - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } else if (in_fmt->bayer_pat == RKISP1_RAW_GBRG) { - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } else if (in_fmt->bayer_pat == RKISP1_RAW_GRBG) { - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } else if (in_fmt->bayer_pat == RKISP1_RAW_RGGB) { - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } + + bls_val->meas_r = rkisp1_read(rkisp1, swapped[0]); + bls_val->meas_gr = rkisp1_read(rkisp1, swapped[1]); + bls_val->meas_gb = rkisp1_read(rkisp1, swapped[2]); + bls_val->meas_b = rkisp1_read(rkisp1, swapped[3]); } static const struct rkisp1_stats_ops rkisp1_v10_stats_ops = { diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c index 618ae55fe396..a06d7cace92f 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -1225,7 +1225,7 @@ static void gsc_remove(struct platform_device *pdev) static int gsc_m2m_suspend(struct gsc_dev *gsc) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&gsc->slock, flags); if (!gsc_m2m_pending(gsc)) { @@ -1236,12 +1236,12 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc) set_bit(ST_M2M_SUSPENDING, &gsc->state); spin_unlock_irqrestore(&gsc->slock, flags); - timeout = wait_event_timeout(gsc->irq_queue, - test_bit(ST_M2M_SUSPENDED, &gsc->state), - GSC_SHUTDOWN_TIMEOUT); + time_left = wait_event_timeout(gsc->irq_queue, + test_bit(ST_M2M_SUSPENDED, &gsc->state), + GSC_SHUTDOWN_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &gsc->state); - return timeout == 0 ? -EAGAIN : 0; + return time_left == 0 ? -EAGAIN : 0; } static void gsc_m2m_resume(struct gsc_dev *gsc) @@ -1309,7 +1309,7 @@ static const struct dev_pm_ops gsc_pm_ops = { static struct platform_driver gsc_driver = { .probe = gsc_probe, - .remove_new = gsc_remove, + .remove = gsc_remove, .driver = { .name = GSC_MODULE_NAME, .pm = &gsc_pm_ops, diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c index b7854ce5fb8e..4bda1c369c44 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c @@ -276,8 +276,6 @@ static const struct vb2_ops gsc_m2m_qops = { .queue_setup = gsc_m2m_queue_setup, .buf_prepare = gsc_m2m_buf_prepare, .buf_queue = gsc_m2m_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = gsc_m2m_stop_streaming, .start_streaming = gsc_m2m_start_streaming, }; diff --git a/drivers/media/platform/samsung/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c index e41333535eac..77007f1a909b 100644 --- a/drivers/media/platform/samsung/exynos4-is/common.c +++ b/drivers/media/platform/samsung/exynos4-is/common.c @@ -44,4 +44,5 @@ void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap) } EXPORT_SYMBOL(__fimc_vidioc_querycap); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS4 SoC Camera Subsystem driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index ffa4ea21387d..c3c2e474a18a 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -455,8 +455,6 @@ static const struct vb2_ops fimc_capture_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index aae74b501a42..2c9edd0a559b 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -822,7 +822,7 @@ err: static int fimc_m2m_suspend(struct fimc_dev *fimc) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&fimc->slock, flags); if (!fimc_m2m_pending(fimc)) { @@ -833,12 +833,12 @@ static int fimc_m2m_suspend(struct fimc_dev *fimc) set_bit(ST_M2M_SUSPENDING, &fimc->state); spin_unlock_irqrestore(&fimc->slock, flags); - timeout = wait_event_timeout(fimc->irq_queue, - test_bit(ST_M2M_SUSPENDED, &fimc->state), - FIMC_SHUTDOWN_TIMEOUT); + time_left = wait_event_timeout(fimc->irq_queue, + test_bit(ST_M2M_SUSPENDED, &fimc->state), + FIMC_SHUTDOWN_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &fimc->state); - return timeout == 0 ? -EAGAIN : 0; + return time_left == 0 ? -EAGAIN : 0; } static int fimc_m2m_resume(struct fimc_dev *fimc) @@ -1157,7 +1157,7 @@ static const struct dev_pm_ops fimc_pm_ops = { static struct platform_driver fimc_driver = { .probe = fimc_probe, - .remove_new = fimc_remove, + .remove = fimc_remove, .driver = { .of_match_table = fimc_of_match, .name = FIMC_DRIVER_NAME, diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c index 7a48fad1df16..ac67a04e5eeb 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c @@ -12,137 +12,6 @@ #include "fimc-is-errno.h" -const char *fimc_is_param_strerr(unsigned int error) -{ - switch (error) { - case ERROR_COMMON_CMD: - return "ERROR_COMMON_CMD: Invalid Command"; - case ERROR_COMMON_PARAMETER: - return "ERROR_COMMON_PARAMETER: Invalid Parameter"; - case ERROR_COMMON_SETFILE_LOAD: - return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading"; - case ERROR_COMMON_SETFILE_ADJUST: - return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted"; - case ERROR_COMMON_SETFILE_INDEX: - return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index"; - case ERROR_COMMON_INPUT_PATH: - return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state"; - case ERROR_COMMON_INPUT_INIT: - return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set"; - case ERROR_COMMON_OUTPUT_PATH: - return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)"; - case ERROR_COMMON_OUTPUT_INIT: - return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set"; - case ERROR_CONTROL_BYPASS: - return "ERROR_CONTROL_BYPASS"; - case ERROR_OTF_INPUT_FORMAT: - return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)"; - case ERROR_OTF_INPUT_WIDTH: - return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; - case ERROR_OTF_INPUT_HEIGHT: - return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_OTF_INPUT_BIT_WIDTH: - return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_DMA_INPUT_WIDTH: - return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; - case ERROR_DMA_INPUT_HEIGHT: - return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)"; - case ERROR_DMA_INPUT_FORMAT: - return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)"; - case ERROR_DMA_INPUT_BIT_WIDTH: - return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_DMA_INPUT_ORDER: - return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)"; - case ERROR_DMA_INPUT_PLANE: - return "ERROR_DMA_INPUT_PLANE: Invalid plane (DRC: 3, FD: 1, 2, 3)"; - case ERROR_OTF_OUTPUT_WIDTH: - return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)"; - case ERROR_OTF_OUTPUT_HEIGHT: - return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)"; - case ERROR_OTF_OUTPUT_FORMAT: - return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)"; - case ERROR_OTF_OUTPUT_BIT_WIDTH: - return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_DMA_OUTPUT_WIDTH: - return "ERROR_DMA_OUTPUT_WIDTH"; - case ERROR_DMA_OUTPUT_HEIGHT: - return "ERROR_DMA_OUTPUT_HEIGHT"; - case ERROR_DMA_OUTPUT_FORMAT: - return "ERROR_DMA_OUTPUT_FORMAT"; - case ERROR_DMA_OUTPUT_BIT_WIDTH: - return "ERROR_DMA_OUTPUT_BIT_WIDTH"; - case ERROR_DMA_OUTPUT_PLANE: - return "ERROR_DMA_OUTPUT_PLANE"; - case ERROR_DMA_OUTPUT_ORDER: - return "ERROR_DMA_OUTPUT_ORDER"; - - /* Sensor Error(100~199) */ - case ERROR_SENSOR_I2C_FAIL: - return "ERROR_SENSOR_I2C_FAIL"; - case ERROR_SENSOR_INVALID_FRAMERATE: - return "ERROR_SENSOR_INVALID_FRAMERATE"; - case ERROR_SENSOR_INVALID_EXPOSURETIME: - return "ERROR_SENSOR_INVALID_EXPOSURETIME"; - case ERROR_SENSOR_INVALID_SIZE: - return "ERROR_SENSOR_INVALID_SIZE"; - case ERROR_SENSOR_INVALID_SETTING: - return "ERROR_SENSOR_INVALID_SETTING"; - case ERROR_SENSOR_ACTUATOR_INIT_FAIL: - return "ERROR_SENSOR_ACTUATOR_INIT_FAIL"; - case ERROR_SENSOR_INVALID_AF_POS: - return "ERROR_SENSOR_INVALID_AF_POS"; - case ERROR_SENSOR_UNSUPPORT_FUNC: - return "ERROR_SENSOR_UNSUPPORT_FUNC"; - case ERROR_SENSOR_UNSUPPORT_PERI: - return "ERROR_SENSOR_UNSUPPORT_PERI"; - case ERROR_SENSOR_UNSUPPORT_AF: - return "ERROR_SENSOR_UNSUPPORT_AF"; - - /* ISP Error (200~299) */ - case ERROR_ISP_AF_BUSY: - return "ERROR_ISP_AF_BUSY"; - case ERROR_ISP_AF_INVALID_COMMAND: - return "ERROR_ISP_AF_INVALID_COMMAND"; - case ERROR_ISP_AF_INVALID_MODE: - return "ERROR_ISP_AF_INVALID_MODE"; - - /* DRC Error (300~399) */ - /* FD Error (400~499) */ - case ERROR_FD_CONFIG_MAX_NUMBER_STATE: - return "ERROR_FD_CONFIG_MAX_NUMBER_STATE"; - case ERROR_FD_CONFIG_MAX_NUMBER_INVALID: - return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID"; - case ERROR_FD_CONFIG_YAW_ANGLE_STATE: - return "ERROR_FD_CONFIG_YAW_ANGLE_STATE"; - case ERROR_FD_CONFIG_YAW_ANGLE_INVALID: - return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n"; - case ERROR_FD_CONFIG_ROLL_ANGLE_STATE: - return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE"; - case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID: - return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID"; - case ERROR_FD_CONFIG_SMILE_MODE_INVALID: - return "ERROR_FD_CONFIG_SMILE_MODE_INVALID"; - case ERROR_FD_CONFIG_BLINK_MODE_INVALID: - return "ERROR_FD_CONFIG_BLINK_MODE_INVALID"; - case ERROR_FD_CONFIG_EYES_DETECT_INVALID: - return "ERROR_FD_CONFIG_EYES_DETECT_INVALID"; - case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID: - return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID"; - case ERROR_FD_CONFIG_ORIENTATION_STATE: - return "ERROR_FD_CONFIG_ORIENTATION_STATE"; - case ERROR_FD_CONFIG_ORIENTATION_INVALID: - return "ERROR_FD_CONFIG_ORIENTATION_INVALID"; - case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID: - return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID"; - case ERROR_FD_RESULT: - return "ERROR_FD_RESULT"; - case ERROR_FD_MODE: - return "ERROR_FD_MODE"; - default: - return "Unknown"; - } -} - const char *fimc_is_strerr(unsigned int error) { error &= ~IS_ERROR_TIME_OUT_FLAG; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h index 809e117331c0..fa8204ffec7b 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h @@ -240,6 +240,5 @@ enum fimc_is_error { }; const char *fimc_is_strerr(unsigned int error); -const char *fimc_is_param_strerr(unsigned int error); #endif /* FIMC_IS_ERR_H_ */ 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 44363c4241d5..b243cbb1d010 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c @@ -137,7 +137,7 @@ static const struct of_device_id fimc_is_i2c_of_match[] = { static struct platform_driver fimc_is_i2c_driver = { .probe = fimc_is_i2c_probe, - .remove_new = fimc_is_i2c_remove, + .remove = fimc_is_i2c_remove, .driver = { .of_match_table = fimc_is_i2c_of_match, .name = "fimc-isp-i2c", diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c index 9c816ae3b3e5..443362da8cc8 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c @@ -204,15 +204,6 @@ int __is_hw_update_params(struct fimc_is *is) return ret; } -void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) -{ - struct isp_param *isp; - - isp = &is->config[is->config_index].isp; - mf->width = isp->otf_input.width; - mf->height = isp->otf_input.height; -} - void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) { unsigned int index = is->config_index; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h index 206904674927..10ad02f36fed 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h @@ -994,7 +994,6 @@ void fimc_is_set_initial_params(struct fimc_is *is); unsigned int __get_pending_param_count(struct fimc_is *is); int __is_hw_update_params(struct fimc_is *is); -void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); void __is_set_sensor(struct fimc_is *is, int fps); void __is_set_isp_aa_ae(struct fimc_is *is); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index 39aab667910d..2e8fe9e49735 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -963,7 +963,7 @@ static const struct dev_pm_ops fimc_is_pm_ops = { static struct platform_driver fimc_is_driver = { .probe = fimc_is_probe, - .remove_new = fimc_is_remove, + .remove = fimc_is_remove, .driver = { .of_match_table = fimc_is_of_match, .name = FIMC_IS_DRV_NAME, @@ -999,4 +999,5 @@ module_exit(fimc_is_module_exit); MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_DESCRIPTION("Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c index 06c4352562b3..ad219ac1b951 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c @@ -255,8 +255,6 @@ static const struct vb2_ops isp_video_capture_qops = { .queue_setup = isp_video_capture_queue_setup, .buf_prepare = isp_video_capture_buffer_prepare, .buf_queue = isp_video_capture_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = isp_video_capture_start_streaming, .stop_streaming = isp_video_capture_stop_streaming, }; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index d1d860fa3454..f23e51e3da2f 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -441,8 +441,6 @@ static const struct vb2_ops fimc_lite_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -1654,7 +1652,7 @@ MODULE_DEVICE_TABLE(of, flite_of_match); static struct platform_driver fimc_lite_driver = { .probe = fimc_lite_probe, - .remove_new = fimc_lite_remove, + .remove = fimc_lite_remove, .driver = { .of_match_table = flite_of_match, .name = FIMC_LITE_DRV_NAME, @@ -1662,5 +1660,6 @@ static struct platform_driver fimc_lite_driver = { } }; module_platform_driver(fimc_lite_driver); +MODULE_DESCRIPTION("Samsung EXYNOS FIMC-LITE (camera host interface) driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c index 199997eec1cc..951433c8e92a 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c @@ -216,8 +216,6 @@ static const struct vb2_ops fimc_qops = { .queue_setup = fimc_queue_setup, .buf_prepare = fimc_buf_prepare, .buf_queue = fimc_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = stop_streaming, .start_streaming = start_streaming, }; diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index 5f10bb4eb4f7..b5ee3c547789 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c @@ -1564,7 +1564,7 @@ MODULE_DEVICE_TABLE(of, fimc_md_of_match); static struct platform_driver fimc_md_driver = { .probe = fimc_md_probe, - .remove_new = fimc_md_remove, + .remove = fimc_md_remove, .driver = { .of_match_table = of_match_ptr(fimc_md_of_match), .name = "s5p-fimc-md", diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h index 786264cf79dc..a50e58ab7ef7 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.h +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h @@ -178,8 +178,9 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); #ifdef CONFIG_OF static inline bool fimc_md_is_isp_available(struct device_node *node) { - node = of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME); - return node ? of_device_is_available(node) : false; + struct device_node *child __free(device_node) = + of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME); + return child ? of_device_is_available(child) : false; } #else #define fimc_md_is_isp_available(node) (false) diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 4b9b20ba3504..452880b5350c 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -940,13 +940,19 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime) state->supplies); goto unlock; } - clk_enable(state->clock[CSIS_CLK_GATE]); + ret = clk_enable(state->clock[CSIS_CLK_GATE]); + if (ret) { + phy_power_off(state->phy); + regulator_bulk_disable(CSIS_NUM_SUPPLIES, + state->supplies); + goto unlock; + } } if (state->flags & ST_STREAMING) s5pcsis_start_stream(state); state->flags &= ~ST_SUSPENDED; - unlock: +unlock: mutex_unlock(&state->lock); return ret ? -EAGAIN : 0; } @@ -1020,7 +1026,7 @@ MODULE_DEVICE_TABLE(of, s5pcsis_of_match); static struct platform_driver s5pcsis_driver = { .probe = s5pcsis_probe, - .remove_new = s5pcsis_remove, + .remove = s5pcsis_remove, .driver = { .of_match_table = s5pcsis_of_match, .name = CSIS_DRIVER_NAME, diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index be58260ea67e..bd1149e8abc2 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -525,8 +525,6 @@ static const struct vb2_ops s3c_camif_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index e4529f666e20..221e3c447f36 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -527,10 +527,19 @@ static void s3c_camif_remove(struct platform_device *pdev) static int s3c_camif_runtime_resume(struct device *dev) { struct camif_dev *camif = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(camif->clock[CLK_GATE]); + if (ret) + return ret; - clk_enable(camif->clock[CLK_GATE]); /* null op on s3c244x */ - clk_enable(camif->clock[CLK_CAM]); + ret = clk_enable(camif->clock[CLK_CAM]); + if (ret) { + clk_disable(camif->clock[CLK_GATE]); + return ret; + } + return 0; } @@ -622,7 +631,7 @@ static const struct dev_pm_ops s3c_camif_pm_ops = { static struct platform_driver s3c_camif_driver = { .probe = s3c_camif_probe, - .remove_new = s3c_camif_remove, + .remove = s3c_camif_remove, .id_table = s3c_camif_driver_ids, .driver = { .name = S3C_CAMIF_DRIVER_NAME, diff --git a/drivers/media/platform/samsung/s5p-g2d/g2d.c b/drivers/media/platform/samsung/s5p-g2d/g2d.c index 89aeba47ed07..ffed16a34493 100644 --- a/drivers/media/platform/samsung/s5p-g2d/g2d.c +++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c @@ -133,8 +133,6 @@ static const struct vb2_ops g2d_qops = { .queue_setup = g2d_queue_setup, .buf_prepare = g2d_buf_prepare, .buf_queue = g2d_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -777,7 +775,7 @@ MODULE_DEVICE_TABLE(of, exynos_g2d_match); static struct platform_driver g2d_pdrv = { .probe = g2d_probe, - .remove_new = g2d_remove, + .remove = g2d_remove, .driver = { .name = G2D_NAME, .of_match_table = exynos_g2d_match, diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c index d2c4a0178b3c..ac4cf269456a 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -775,11 +775,14 @@ static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2; jpeg_buffer.curr = 0; - word = 0; - if (get_word_be(&jpeg_buffer, &word)) return; - jpeg_buffer.size = (long)word - 2; + + if (word < 2) + jpeg_buffer.size = 0; + else + jpeg_buffer.size = (long)word - 2; + jpeg_buffer.data += 2; jpeg_buffer.curr = 0; @@ -1058,6 +1061,7 @@ static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word) if (byte == -1) return -1; *word = (unsigned int)byte | temp; + return 0; } @@ -1145,7 +1149,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, if (get_word_be(&jpeg_buffer, &word)) break; length = (long)word - 2; - if (!length) + if (length <= 0) return false; sof = jpeg_buffer.curr; /* after 0xffc0 */ sof_len = length; @@ -1176,7 +1180,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, if (get_word_be(&jpeg_buffer, &word)) break; length = (long)word - 2; - if (!length) + if (length <= 0) return false; if (n_dqt >= S5P_JPEG_MAX_MARKER) return false; @@ -1189,7 +1193,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, if (get_word_be(&jpeg_buffer, &word)) break; length = (long)word - 2; - if (!length) + if (length <= 0) return false; if (n_dht >= S5P_JPEG_MAX_MARKER) return false; @@ -1214,6 +1218,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, if (get_word_be(&jpeg_buffer, &word)) break; length = (long)word - 2; + /* No need to check underflows as skip() does it */ skip(&jpeg_buffer, length); break; } @@ -2590,8 +2595,6 @@ static const struct vb2_ops s5p_jpeg_qops = { .queue_setup = s5p_jpeg_queue_setup, .buf_prepare = s5p_jpeg_buf_prepare, .buf_queue = s5p_jpeg_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = s5p_jpeg_start_streaming, .stop_streaming = s5p_jpeg_stop_streaming, }; @@ -3160,7 +3163,7 @@ static void *jpeg_get_drv_data(struct device *dev) static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, - .remove_new = s5p_jpeg_remove, + .remove = s5p_jpeg_remove, .driver = { .of_match_table = samsung_jpeg_match, .name = S5P_JPEG_M2M_NAME, diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c index 637a5104d948..6657d294c10a 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c @@ -427,11 +427,6 @@ void exynos3250_jpeg_clear_int_status(void __iomem *regs, writel(value, regs + EXYNOS3250_JPGINTST); } -unsigned int exynos3250_jpeg_operating(void __iomem *regs) -{ - return readl(regs + S5P_JPGOPR) & EXYNOS3250_JPGOPR_MASK; -} - unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs) { return readl(regs + EXYNOS3250_JPGCNT) & EXYNOS3250_JPGCNT_MASK; diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h index 15af928fad76..709c61ae322c 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h @@ -45,7 +45,6 @@ void exynos3250_jpeg_rstart(void __iomem *regs); unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs); void exynos3250_jpeg_clear_int_status(void __iomem *regs, unsigned int value); -unsigned int exynos3250_jpeg_operating(void __iomem *regs); unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs); void exynos3250_jpeg_dec_stream_size(void __iomem *regs, unsigned int size); void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, unsigned int sratio); diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c index 0828cfa783fe..479288fc8c77 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c @@ -185,11 +185,6 @@ unsigned int exynos4_jpeg_get_int_status(void __iomem *base) return readl(base + EXYNOS4_INT_STATUS_REG); } -unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base) -{ - return readl(base + EXYNOS4_FIFO_STATUS_REG); -} - void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value) { unsigned int reg; @@ -300,22 +295,8 @@ void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size) writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG); } -void exynos4_jpeg_get_frame_size(void __iomem *base, - unsigned int *width, unsigned int *height) -{ - *width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) & - EXYNOS4_DECODED_SIZE_MASK); - *height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) & - EXYNOS4_DECODED_SIZE_MASK; -} - unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base) { return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) & EXYNOS4_JPEG_DECODED_IMG_FMT_MASK; } - -void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size) -{ - writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG); -} diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h index 3e2887526960..b941cc89e4ba 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h @@ -35,10 +35,6 @@ void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x); void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt); void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size); unsigned int exynos4_jpeg_get_stream_size(void __iomem *base); -void exynos4_jpeg_get_frame_size(void __iomem *base, - unsigned int *width, unsigned int *height); unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base); -unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base); -void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size); #endif /* JPEG_HW_EXYNOS4_H_ */ diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index 50451984d59f..5f80931f056d 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -774,8 +774,10 @@ static int s5p_mfc_open(struct file *file) int ret = 0; mfc_debug_enter(); - if (mutex_lock_interruptible(&dev->mfc_mutex)) - return -ERESTARTSYS; + if (mutex_lock_interruptible(&dev->mfc_mutex)) { + ret = -ERESTARTSYS; + goto err_enter; + } dev->num_inst++; /* It is guarded by mfc_mutex in vfd */ /* Allocate memory for context */ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -946,6 +948,7 @@ err_no_ctx: err_alloc: dev->num_inst--; mutex_unlock(&dev->mfc_mutex); +err_enter: mfc_debug_leave(); return ret; } @@ -1721,7 +1724,7 @@ MODULE_DEVICE_TABLE(of, exynos_mfc_match); static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, - .remove_new = s5p_mfc_remove, + .remove = s5p_mfc_remove, .driver = { .name = S5P_MFC_NAME, .pm = &s5p_mfc_pm_ops, 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 91e102d4ec4e..3efbc3367906 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c @@ -1161,8 +1161,6 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) static const struct vb2_ops s5p_mfc_dec_qops = { .queue_setup = s5p_mfc_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_init = s5p_mfc_buf_init, .start_streaming = s5p_mfc_start_streaming, .stop_streaming = s5p_mfc_stop_streaming, 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 81cbb36fb382..6c603dcd5664 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c @@ -2652,8 +2652,6 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) static const struct vb2_ops s5p_mfc_enc_qops = { .queue_setup = s5p_mfc_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_init = s5p_mfc_buf_init, .buf_prepare = s5p_mfc_buf_prepare, .start_streaming = s5p_mfc_start_streaming, diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c index 1328b4eb6b9f..73ad66ed20f2 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c @@ -531,8 +531,6 @@ static const struct vb2_ops bdisp_qops = { .queue_setup = bdisp_queue_setup, .buf_prepare = bdisp_buf_prepare, .buf_queue = bdisp_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = bdisp_stop_streaming, .start_streaming = bdisp_start_streaming, }; @@ -1160,7 +1158,7 @@ static void bdisp_irq_timeout(struct work_struct *ptr) static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&bdisp->slock, flags); if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) { @@ -1171,13 +1169,13 @@ static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) set_bit(ST_M2M_SUSPENDING, &bdisp->state); spin_unlock_irqrestore(&bdisp->slock, flags); - timeout = wait_event_timeout(bdisp->irq_queue, - test_bit(ST_M2M_SUSPENDED, &bdisp->state), - BDISP_WORK_TIMEOUT); + time_left = wait_event_timeout(bdisp->irq_queue, + test_bit(ST_M2M_SUSPENDED, &bdisp->state), + BDISP_WORK_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &bdisp->state); - if (!timeout) { + if (!time_left) { dev_err(bdisp->dev, "%s IRQ timeout\n", __func__); return -EAGAIN; } @@ -1411,7 +1409,7 @@ MODULE_DEVICE_TABLE(of, bdisp_match_types); static struct platform_driver bdisp_driver = { .probe = bdisp_probe, - .remove_new = bdisp_remove, + .remove = bdisp_remove, .driver = { .name = BDISP_NAME, .of_match_table = bdisp_match_types, diff --git a/drivers/media/platform/st/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig index 702b910509c9..01c33d9c9ec3 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/Kconfig +++ b/drivers/media/platform/st/sti/c8sectpfe/Kconfig @@ -5,7 +5,6 @@ config DVB_C8SECTPFE depends on PINCTRL && DVB_CORE && I2C depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST select FW_LOADER - select DEBUG_FS select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/platform/st/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile index aedfc725cc19..99425137ee0a 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/Makefile +++ b/drivers/media/platform/st/sti/c8sectpfe/Makefile @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o \ - c8sectpfe-debugfs.o +c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o + +ifneq ($(CONFIG_DEBUG_FS),) +c8sectpfe-y += c8sectpfe-debugfs.o +endif obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index e4cf27b5a072..7b3a37957e3a 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -24,7 +24,6 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/pinctrl.h> @@ -73,16 +72,16 @@ static void c8sectpfe_timer_interrupt(struct timer_list *t) /* is this descriptor initialised and TP enabled */ if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) - tasklet_schedule(&channel->tsklet); + queue_work(system_bh_wq, &channel->bh_work); } fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); add_timer(&fei->timer); } -static void channel_swdemux_tsklet(struct tasklet_struct *t) +static void channel_swdemux_bh_work(struct work_struct *t) { - struct channel_info *channel = from_tasklet(channel, t, tsklet); + struct channel_info *channel = from_work(channel, t, bh_work); struct c8sectpfei *fei; unsigned long wp, rp; int pos, num_packets, n, size; @@ -211,7 +210,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) dev_dbg(fei->dev, "Starting channel=%p\n", channel); - tasklet_setup(&channel->tsklet, channel_swdemux_tsklet); + INIT_WORK(&channel->bh_work, channel_swdemux_bh_work); /* Reset the internal inputblock sram pointers */ writel(channel->fifo, @@ -304,7 +303,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) /* disable this channels descriptor */ writel(0, channel->irec + DMA_PRDS_TPENABLE); - tasklet_disable(&channel->tsklet); + disable_work_sync(&channel->bh_work); /* now request memdma channel goes idle */ idlereq = (1 << channel->tsin_id) | IDLEREQ; @@ -631,8 +630,8 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0)); writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); - /* initialize tasklet */ - tasklet_setup(&tsin->tsklet, channel_swdemux_tsklet); + /* initialize bh work */ + INIT_WORK(&tsin->bh_work, channel_swdemux_bh_work); return 0; @@ -1097,7 +1096,6 @@ static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei) } } - release_firmware(fw); return err; } @@ -1121,6 +1119,7 @@ static int load_c8sectpfe_fw(struct c8sectpfei *fei) } err = load_slim_core_fw(fw, fei); + release_firmware(fw); if (err) { dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); return err; @@ -1159,7 +1158,7 @@ static struct platform_driver c8sectpfe_driver = { .of_match_table = c8sectpfe_match, }, .probe = c8sectpfe_probe, - .remove_new = c8sectpfe_remove, + .remove = c8sectpfe_remove, }; module_platform_driver(c8sectpfe_driver); diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h index bf377cc82225..c1b124c6ef12 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h @@ -51,7 +51,7 @@ struct channel_info { unsigned long fifo; struct completion idle_completion; - struct tasklet_struct tsklet; + struct work_struct bh_work; struct c8sectpfei *fei; void __iomem *irec; diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h index d2c35fb32d7e..3fe177b59b16 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h @@ -12,7 +12,12 @@ #include "c8sectpfe-core.h" +#if defined(CONFIG_DEBUG_FS) void c8sectpfe_debugfs_init(struct c8sectpfei *); void c8sectpfe_debugfs_exit(struct c8sectpfei *); +#else +static inline void c8sectpfe_debugfs_init(struct c8sectpfei *fei) {}; +static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) {}; +#endif #endif /* __C8SECTPFE_DEBUG_H */ diff --git a/drivers/media/platform/st/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c index da402d1e9171..196e6a40335d 100644 --- a/drivers/media/platform/st/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c @@ -1559,8 +1559,6 @@ static const struct vb2_ops delta_vb2_au_ops = { .queue_setup = delta_vb2_au_queue_setup, .buf_prepare = delta_vb2_au_prepare, .buf_queue = delta_vb2_au_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = delta_vb2_au_start_streaming, .stop_streaming = delta_vb2_au_stop_streaming, }; @@ -1570,8 +1568,6 @@ static const struct vb2_ops delta_vb2_frame_ops = { .buf_prepare = delta_vb2_frame_prepare, .buf_finish = delta_vb2_frame_finish, .buf_queue = delta_vb2_frame_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = delta_vb2_frame_stop_streaming, }; @@ -1954,7 +1950,7 @@ MODULE_DEVICE_TABLE(of, delta_match_types); static struct platform_driver delta_driver = { .probe = delta_probe, - .remove_new = delta_remove, + .remove = delta_remove, .driver = { .name = DELTA_NAME, .of_match_table = delta_match_types, diff --git a/drivers/media/platform/st/sti/hva/hva-hw.c b/drivers/media/platform/st/sti/hva/hva-hw.c index fe4ea2e7f37e..fcb18fb52fdd 100644 --- a/drivers/media/platform/st/sti/hva/hva-hw.c +++ b/drivers/media/platform/st/sti/hva/hva-hw.c @@ -406,8 +406,7 @@ err_pm: err_disable: pm_runtime_disable(dev); err_clk: - if (hva->clk) - clk_unprepare(hva->clk); + clk_unprepare(hva->clk); return ret; } diff --git a/drivers/media/platform/st/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c index 161a5c0fbc4e..5366c0f92549 100644 --- a/drivers/media/platform/st/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c @@ -1114,8 +1114,6 @@ static const struct vb2_ops hva_qops = { .buf_queue = hva_buf_queue, .start_streaming = hva_start_streaming, .stop_streaming = hva_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* @@ -1456,7 +1454,7 @@ MODULE_DEVICE_TABLE(of, hva_match_types); static struct platform_driver hva_driver = { .probe = hva_probe, - .remove_new = hva_remove, + .remove = hva_remove, .driver = { .name = HVA_NAME, .of_match_table = hva_match_types, diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig index 9df9a2a17728..f12e67bcc9bc 100644 --- a/drivers/media/platform/st/stm32/Kconfig +++ b/drivers/media/platform/st/stm32/Kconfig @@ -1,6 +1,20 @@ # SPDX-License-Identifier: GPL-2.0-only # V4L drivers +config VIDEO_STM32_CSI + tristate "STM32 Camera Serial Interface (CSI) support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_STM32 || COMPILE_TEST + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This module makes the STM32 Camera Serial Interface (CSI) + available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-csi. + config VIDEO_STM32_DCMI tristate "STM32 Digital Camera Memory Interface (DCMI) support" depends on V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/st/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile index 7ed8297b9b19..9ae57897f030 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_CSI) += stm32-csi.o 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 diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c index 92f1edee58f8..b6c8400fb92d 100644 --- a/drivers/media/platform/st/stm32/dma2d/dma2d.c +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c @@ -186,8 +186,6 @@ static const struct vb2_ops dma2d_qops = { .buf_queue = dma2d_buf_queue, .start_streaming = dma2d_start_streaming, .stop_streaming = dma2d_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -717,7 +715,7 @@ MODULE_DEVICE_TABLE(of, stm32_dma2d_match); static struct platform_driver dma2d_pdrv = { .probe = dma2d_probe, - .remove_new = dma2d_remove, + .remove = dma2d_remove, .driver = { .name = DMA2D_NAME, .of_match_table = stm32_dma2d_match, diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c new file mode 100644 index 000000000000..48941aae8c9b --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-csi.c @@ -0,0 +1,1137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Camera Serial Interface + * + * Copyright (C) STMicroelectronics SA 2024 + * Author: Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define STM32_CSI_CR 0x0000 +#define STM32_CSI_CR_CSIEN BIT(0) +#define STM32_CSI_CR_VCXSTART(x) BIT(2 + ((x) * 4)) +#define STM32_CSI_CR_VCXSTOP(x) BIT(3 + ((x) * 4)) +#define STM32_CSI_PCR 0x0004 +#define STM32_CSI_PCR_DL1EN BIT(3) +#define STM32_CSI_PCR_DL0EN BIT(2) +#define STM32_CSI_PCR_CLEN BIT(1) +#define STM32_CSI_PCR_PWRDOWN BIT(0) +#define STM32_CSI_VCXCFGR1(x) ((((x) + 1) * 0x0010) + 0x0) +#define STM32_CSI_VCXCFGR1_ALLDT BIT(0) +#define STM32_CSI_VCXCFGR1_DT0EN BIT(1) +#define STM32_CSI_VCXCFGR1_DT1EN BIT(2) +#define STM32_CSI_VCXCFGR1_CDTFT_SHIFT 8 +#define STM32_CSI_VCXCFGR1_DT0_SHIFT 16 +#define STM32_CSI_VCXCFGR1_DT0FT_SHIFT 24 +#define STM32_CSI_VCXCFGR2(x) ((((x) + 1) * 0x0010) + 0x4) +#define STM32_CSI_VCXCFGR2_DT1_SHIFT 0 +#define STM32_CSI_VCXCFGR2_DT1FT_SHIFT 8 +#define STM32_CSI_INPUT_BPP8 2 +#define STM32_CSI_INPUT_BPP10 3 +#define STM32_CSI_INPUT_BPP12 4 +#define STM32_CSI_INPUT_BPP14 5 +#define STM32_CSI_LMCFGR 0x0070 +#define STM32_CSI_LMCFGR_LANENB_SHIFT 8 +#define STM32_CSI_LMCFGR_DLMAP_SHIFT 16 +#define STM32_CSI_IER0 0x0080 +#define STM32_CSI_IER1 0x0084 +#define STM32_CSI_SR0 0x0090 +#define STM32_CSI_SR0_SYNCERRF BIT(30) +#define STM32_CSI_SR0_SPKTERRF BIT(28) +#define STM32_CSI_SR0_IDERRF BIT(27) +#define STM32_CSI_SR0_CECCERRF BIT(26) +#define STM32_CSI_SR0_ECCERRF BIT(25) +#define STM32_CSI_SR0_CRCERRF BIT(24) +#define STM32_CSI_SR0_CCFIFOFF BIT(21) +#define STM32_CSI_SR0_VCXSTATEF(x) BIT(17 + (x)) +#define STM32_CSI_SR1 0x0094 +#define STM32_CSI_SR1_ECTRLDL1F BIT(12) +#define STM32_CSI_SR1_ESYNCESCDL1F BIT(11) +#define STM32_CSI_SR1_EESCDL1F BIT(10) +#define STM32_CSI_SR1_ESOTSYNCDL1F BIT(9) +#define STM32_CSI_SR1_ESOTDL1F BIT(8) +#define STM32_CSI_SR1_ECTRLDL0F BIT(4) +#define STM32_CSI_SR1_ESYNCESCDL0F BIT(3) +#define STM32_CSI_SR1_EESCDL0F BIT(2) +#define STM32_CSI_SR1_ESOTSYNCDL0F BIT(1) +#define STM32_CSI_SR1_ESOTDL0F BIT(0) +#define STM32_CSI_FCR0 0x0100 +#define STM32_CSI_FCR1 0x0104 +#define STM32_CSI_SPDFR 0x0110 +#define STM32_CSI_DT_MASK 0x3f +#define STM32_CSI_VC_MASK 0x03 +#define STM32_CSI_ERR1 0x0114 +#define STM32_CSI_ERR1_IDVCERR_SHIFT 22 +#define STM32_CSI_ERR1_IDDTERR_SHIFT 16 +#define STM32_CSI_ERR1_CECCVCERR_SHIFT 14 +#define STM32_CSI_ERR1_CECCDTERR_SHIFT 8 +#define STM32_CSI_ERR1_CRCVCERR_SHIFT 6 +#define STM32_CSI_ERR1_CRCDTERR_SHIFT 0 +#define STM32_CSI_ERR2 0x0118 +#define STM32_CSI_ERR2_SYNCVCERR_SHIFT 18 +#define STM32_CSI_ERR2_SPKTVCERR_SHIFT 6 +#define STM32_CSI_ERR2_SPKTDTERR_SHIFT 0 +#define STM32_CSI_PRCR 0x1000 +#define STM32_CSI_PRCR_PEN BIT(1) +#define STM32_CSI_PMCR 0x1004 +#define STM32_CSI_PFCR 0x1008 +#define STM32_CSI_PFCR_CCFR_MASK GENMASK(5, 0) +#define STM32_CSI_PFCR_CCFR_SHIFT 0 +#define STM32_CSI_PFCR_HSFR_MASK GENMASK(14, 8) +#define STM32_CSI_PFCR_HSFR_SHIFT 8 +#define STM32_CSI_PFCR_DLD BIT(16) +#define STM32_CSI_PTCR0 0x1010 +#define STM32_CSI_PTCR0_TCKEN BIT(0) +#define STM32_CSI_PTCR1 0x1014 +#define STM32_CSI_PTCR1_TWM BIT(16) +#define STM32_CSI_PTCR1_TDI_MASK GENMASK(7, 0) +#define STM32_CSI_PTCR1_TDI_SHIFT 0 +#define STM32_CSI_PTSR 0x1018 + +#define STM32_CSI_LANES_MAX 2 + +#define STM32_CSI_SR0_ERRORS (STM32_CSI_SR0_SYNCERRF | STM32_CSI_SR0_SPKTERRF |\ + STM32_CSI_SR0_IDERRF | STM32_CSI_SR0_CECCERRF |\ + STM32_CSI_SR0_ECCERRF | STM32_CSI_SR0_CRCERRF |\ + STM32_CSI_SR0_CCFIFOFF) +#define STM32_CSI_SR1_DL0_ERRORS (STM32_CSI_SR1_ECTRLDL0F | STM32_CSI_SR1_ESYNCESCDL0F |\ + STM32_CSI_SR1_EESCDL0F | STM32_CSI_SR1_ESOTSYNCDL0F |\ + STM32_CSI_SR1_ESOTDL0F) +#define STM32_CSI_SR1_DL1_ERRORS (STM32_CSI_SR1_ECTRLDL1F | STM32_CSI_SR1_ESYNCESCDL1F |\ + STM32_CSI_SR1_EESCDL1F | STM32_CSI_SR1_ESOTSYNCDL1F |\ + STM32_CSI_SR1_ESOTDL1F) +#define STM32_CSI_SR1_ERRORS (STM32_CSI_SR1_DL0_ERRORS | STM32_CSI_SR1_DL1_ERRORS) + +enum stm32_csi_pads { + STM32_CSI_PAD_SINK, + STM32_CSI_PAD_SOURCE, + STM32_CSI_PAD_MAX, +}; + +struct stm32_csi_event { + u32 mask; + const char * const name; +}; + +static const struct stm32_csi_event stm32_csi_events_sr0[] = { + {STM32_CSI_SR0_SYNCERRF, "Synchronization error"}, + {STM32_CSI_SR0_SPKTERRF, "Short packet error"}, + {STM32_CSI_SR0_IDERRF, "Data type ID error"}, + {STM32_CSI_SR0_CECCERRF, "Corrected ECC error"}, + {STM32_CSI_SR0_ECCERRF, "ECC error"}, + {STM32_CSI_SR0_CRCERRF, "CRC error"}, + {STM32_CSI_SR0_CCFIFOFF, "Clk changer FIFO full error"}, +}; + +#define STM32_CSI_NUM_SR0_EVENTS ARRAY_SIZE(stm32_csi_events_sr0) + +static const struct stm32_csi_event stm32_csi_events_sr1[] = { + {STM32_CSI_SR1_ECTRLDL1F, "L1: D-PHY control error"}, + {STM32_CSI_SR1_ESYNCESCDL1F, + "L1: D-PHY low power data transmission synchro error"}, + {STM32_CSI_SR1_EESCDL1F, "L1: D-PHY escape entry error"}, + {STM32_CSI_SR1_ESOTSYNCDL1F, + "L1: Start of transmission synchro error"}, + {STM32_CSI_SR1_ESOTDL1F, "L1: Start of transmission error"}, + {STM32_CSI_SR1_ECTRLDL0F, "L0: D-PHY control error"}, + {STM32_CSI_SR1_ESYNCESCDL0F, + "L0: D-PHY low power data transmission synchro error"}, + {STM32_CSI_SR1_EESCDL0F, "L0: D-PHY escape entry error"}, + {STM32_CSI_SR1_ESOTSYNCDL0F, + "L0: Start of transmission synchro error"}, + {STM32_CSI_SR1_ESOTDL0F, "L0: Start of transmission error"}, +}; + +#define STM32_CSI_NUM_SR1_EVENTS ARRAY_SIZE(stm32_csi_events_sr1) + +enum stm32_csi_clk { + STM32_CSI_CLK_PCLK, + STM32_CSI_CLK_TXESC, + STM32_CSI_CLK_CSI2PHY, + STM32_CSI_CLK_NB, +}; + +static const char * const stm32_csi_clks_id[] = { + "pclk", + "txesc", + "csi2phy", +}; + +struct stm32_csi_dev { + struct device *dev; + + void __iomem *base; + + struct clk_bulk_data clks[STM32_CSI_CLK_NB]; + struct regulator_bulk_data supplies[2]; + + u8 lanes[STM32_CSI_LANES_MAX]; + u8 num_lanes; + + /* + * spinlock slock is used to protect to srX_counters tables being + * accessed from log_status and interrupt context + */ + spinlock_t slock; + + u32 sr0_counters[STM32_CSI_NUM_SR0_EVENTS]; + u32 sr1_counters[STM32_CSI_NUM_SR1_EVENTS]; + + struct v4l2_subdev sd; + struct v4l2_async_notifier notifier; + struct media_pad pads[STM32_CSI_PAD_MAX]; + + /* Remote source */ + struct v4l2_subdev *s_subdev; + u32 s_subdev_pad_nb; +}; + +struct stm32_csi_fmts { + u32 code; + u32 datatype; + u32 input_fmt; + u8 bpp; +}; + +#define FMT_MBUS_DT_DTFMT_BPP(mbus, dt, input, byteperpixel) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .datatype = MIPI_CSI2_DT_##dt, \ + .input_fmt = STM32_CSI_INPUT_##input, \ + .bpp = byteperpixel, \ + } +static const struct stm32_csi_fmts stm32_csi_formats[] = { + /* YUV 422 8 bit */ + FMT_MBUS_DT_DTFMT_BPP(UYVY8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(YUYV8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(YVYU8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(VYUY8_1X16, YUV422_8B, BPP8, 8), + + /* Raw Bayer */ + /* 8 bit */ + FMT_MBUS_DT_DTFMT_BPP(SBGGR8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SGBRG8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SGRBG8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SRGGB8_1X8, RAW8, BPP8, 8), + /* 10 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SGBRG10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SGRBG10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10), + /* 12 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SGBRG12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SGRBG12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12), + /* 14 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SGBRG14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SGRBG14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14), + + /* RGB 565 */ + FMT_MBUS_DT_DTFMT_BPP(RGB565_1X16, RGB565, BPP8, 8), + + /* JPEG (datatype isn't used) */ + FMT_MBUS_DT_DTFMT_BPP(JPEG_1X8, NULL, BPP8, 8), +}; + +struct stm32_csi_mbps_phy_reg { + unsigned int mbps; + unsigned int hsfreqrange; + unsigned int osc_freq_target; +}; + +/* + * Table describing configuration of the PHY depending on the + * intended Bit Rate. From table 5-8 Frequency Ranges and Defaults + * of the Synopsis DWC MIPI PHY databook + */ +static const struct stm32_csi_mbps_phy_reg snps_stm32mp25[] = { + { .mbps = 80, .hsfreqrange = 0x00, .osc_freq_target = 460 }, + { .mbps = 90, .hsfreqrange = 0x10, .osc_freq_target = 460 }, + { .mbps = 100, .hsfreqrange = 0x20, .osc_freq_target = 460 }, + { .mbps = 110, .hsfreqrange = 0x30, .osc_freq_target = 460 }, + { .mbps = 120, .hsfreqrange = 0x01, .osc_freq_target = 460 }, + { .mbps = 130, .hsfreqrange = 0x11, .osc_freq_target = 460 }, + { .mbps = 140, .hsfreqrange = 0x21, .osc_freq_target = 460 }, + { .mbps = 150, .hsfreqrange = 0x31, .osc_freq_target = 460 }, + { .mbps = 160, .hsfreqrange = 0x02, .osc_freq_target = 460 }, + { .mbps = 170, .hsfreqrange = 0x12, .osc_freq_target = 460 }, + { .mbps = 180, .hsfreqrange = 0x22, .osc_freq_target = 460 }, + { .mbps = 190, .hsfreqrange = 0x32, .osc_freq_target = 460 }, + { .mbps = 205, .hsfreqrange = 0x03, .osc_freq_target = 460 }, + { .mbps = 220, .hsfreqrange = 0x13, .osc_freq_target = 460 }, + { .mbps = 235, .hsfreqrange = 0x23, .osc_freq_target = 460 }, + { .mbps = 250, .hsfreqrange = 0x33, .osc_freq_target = 460 }, + { .mbps = 275, .hsfreqrange = 0x04, .osc_freq_target = 460 }, + { .mbps = 300, .hsfreqrange = 0x14, .osc_freq_target = 460 }, + { .mbps = 325, .hsfreqrange = 0x25, .osc_freq_target = 460 }, + { .mbps = 350, .hsfreqrange = 0x35, .osc_freq_target = 460 }, + { .mbps = 400, .hsfreqrange = 0x05, .osc_freq_target = 460 }, + { .mbps = 450, .hsfreqrange = 0x16, .osc_freq_target = 460 }, + { .mbps = 500, .hsfreqrange = 0x26, .osc_freq_target = 460 }, + { .mbps = 550, .hsfreqrange = 0x37, .osc_freq_target = 460 }, + { .mbps = 600, .hsfreqrange = 0x07, .osc_freq_target = 460 }, + { .mbps = 650, .hsfreqrange = 0x18, .osc_freq_target = 460 }, + { .mbps = 700, .hsfreqrange = 0x28, .osc_freq_target = 460 }, + { .mbps = 750, .hsfreqrange = 0x39, .osc_freq_target = 460 }, + { .mbps = 800, .hsfreqrange = 0x09, .osc_freq_target = 460 }, + { .mbps = 850, .hsfreqrange = 0x19, .osc_freq_target = 460 }, + { .mbps = 900, .hsfreqrange = 0x29, .osc_freq_target = 460 }, + { .mbps = 950, .hsfreqrange = 0x3a, .osc_freq_target = 460 }, + { .mbps = 1000, .hsfreqrange = 0x0a, .osc_freq_target = 460 }, + { .mbps = 1050, .hsfreqrange = 0x1a, .osc_freq_target = 460 }, + { .mbps = 1100, .hsfreqrange = 0x2a, .osc_freq_target = 460 }, + { .mbps = 1150, .hsfreqrange = 0x3b, .osc_freq_target = 460 }, + { .mbps = 1200, .hsfreqrange = 0x0b, .osc_freq_target = 460 }, + { .mbps = 1250, .hsfreqrange = 0x1b, .osc_freq_target = 460 }, + { .mbps = 1300, .hsfreqrange = 0x2b, .osc_freq_target = 460 }, + { .mbps = 1350, .hsfreqrange = 0x3c, .osc_freq_target = 460 }, + { .mbps = 1400, .hsfreqrange = 0x0c, .osc_freq_target = 460 }, + { .mbps = 1450, .hsfreqrange = 0x1c, .osc_freq_target = 460 }, + { .mbps = 1500, .hsfreqrange = 0x2c, .osc_freq_target = 460 }, + { .mbps = 1550, .hsfreqrange = 0x3d, .osc_freq_target = 285 }, + { .mbps = 1600, .hsfreqrange = 0x0d, .osc_freq_target = 295 }, + { .mbps = 1650, .hsfreqrange = 0x1d, .osc_freq_target = 304 }, + { .mbps = 1700, .hsfreqrange = 0x2e, .osc_freq_target = 313 }, + { .mbps = 1750, .hsfreqrange = 0x3e, .osc_freq_target = 322 }, + { .mbps = 1800, .hsfreqrange = 0x0e, .osc_freq_target = 331 }, + { .mbps = 1850, .hsfreqrange = 0x1e, .osc_freq_target = 341 }, + { .mbps = 1900, .hsfreqrange = 0x2f, .osc_freq_target = 350 }, + { .mbps = 1950, .hsfreqrange = 0x3f, .osc_freq_target = 359 }, + { .mbps = 2000, .hsfreqrange = 0x0f, .osc_freq_target = 368 }, + { .mbps = 2050, .hsfreqrange = 0x40, .osc_freq_target = 377 }, + { .mbps = 2100, .hsfreqrange = 0x41, .osc_freq_target = 387 }, + { .mbps = 2150, .hsfreqrange = 0x42, .osc_freq_target = 396 }, + { .mbps = 2200, .hsfreqrange = 0x43, .osc_freq_target = 405 }, + { .mbps = 2250, .hsfreqrange = 0x44, .osc_freq_target = 414 }, + { .mbps = 2300, .hsfreqrange = 0x45, .osc_freq_target = 423 }, + { .mbps = 2350, .hsfreqrange = 0x46, .osc_freq_target = 432 }, + { .mbps = 2400, .hsfreqrange = 0x47, .osc_freq_target = 442 }, + { .mbps = 2450, .hsfreqrange = 0x48, .osc_freq_target = 451 }, + { .mbps = 2500, .hsfreqrange = 0x49, .osc_freq_target = 460 }, + { /* sentinel */ } +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_RGB565_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_REC709, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + +static const struct stm32_csi_fmts *stm32_csi_code_to_fmt(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stm32_csi_formats); i++) + if (stm32_csi_formats[i].code == code) + return &stm32_csi_formats[i]; + + return NULL; +} + +static inline struct stm32_csi_dev *to_csidev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct stm32_csi_dev, sd); +} + +static int stm32_csi_setup_lane_merger(struct stm32_csi_dev *csidev) +{ + u32 lmcfgr = 0; + int i; + + for (i = 0; i < csidev->num_lanes; i++) { + if (!csidev->lanes[i] || csidev->lanes[i] > STM32_CSI_LANES_MAX) { + dev_err(csidev->dev, "Invalid lane id (%d)\n", csidev->lanes[i]); + return -EINVAL; + } + lmcfgr |= (csidev->lanes[i] << ((i * 4) + STM32_CSI_LMCFGR_DLMAP_SHIFT)); + } + + lmcfgr |= (csidev->num_lanes << STM32_CSI_LMCFGR_LANENB_SHIFT); + + writel_relaxed(lmcfgr, csidev->base + STM32_CSI_LMCFGR); + + return 0; +} + +static void stm32_csi_phy_reg_write(struct stm32_csi_dev *csidev, + u32 addr, u32 val) +{ + /* Based on sequence described at section 5.2.3.2 of DesignWave document */ + /* For writing the 4-bit testcode MSBs */ + /* Set testen to high */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Place 0x00 in testdin */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* + * Set testclk to low (with the falling edge on testclk, the testdin + * signal content is latched internally) + */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR1); + + /* Place the 8-bit word corresponding to the testcode MSBs in testdin */ + writel_relaxed(((addr >> 8) & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT, + csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* For writing the 8-bit testcode LSBs */ + /* Set testclk to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to high */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Place the 8-bit word test data in testdin */ + writel_relaxed((addr & STM32_CSI_PTCR1_TDI_MASK) << + STM32_CSI_PTCR1_TDI_SHIFT | STM32_CSI_PTCR1_TWM, + csidev->base + STM32_CSI_PTCR1); + + /* + * Set testclk to low (with the falling edge on testclk, the testdin + * signal content is latched internally) + */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR1); + + /* For writing the data */ + /* Place the 8-bit word corresponding to the page offset in testdin */ + writel_relaxed((val & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT, + csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high (test data is programmed internally */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Finish by setting testclk to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); +} + +static int stm32_csi_start(struct stm32_csi_dev *csidev, + struct v4l2_subdev_state *state) +{ + const struct stm32_csi_mbps_phy_reg *phy_regs; + struct v4l2_mbus_framefmt *sink_fmt; + const struct stm32_csi_fmts *fmt; + unsigned long phy_clk_frate; + unsigned int mbps; + u32 lanes_ie = 0; + u32 lanes_en = 0; + s64 link_freq; + int ret; + u32 ccfr; + + dev_dbg(csidev->dev, "Starting the CSI2\n"); + + /* Get the bpp value on pad0 (input of CSI) */ + sink_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK); + fmt = stm32_csi_code_to_fmt(sink_fmt->code); + + /* Get the remote sensor link frequency */ + if (!csidev->s_subdev) + return -EIO; + + link_freq = v4l2_get_link_freq(csidev->s_subdev->ctrl_handler, + fmt->bpp, 2 * csidev->num_lanes); + if (link_freq < 0) + return link_freq; + + /* MBPS is expressed in Mbps, hence link_freq / 100000 * 2 */ + mbps = div_s64(link_freq, 500000); + dev_dbg(csidev->dev, "Computed Mbps: %u\n", mbps); + + for (phy_regs = snps_stm32mp25; phy_regs->mbps != 0; phy_regs++) + if (phy_regs->mbps >= mbps) + break; + + if (!phy_regs->mbps) { + dev_err(csidev->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return -ERANGE; + } + + dev_dbg(csidev->dev, "PHY settings: (%u Mbps, %u HS FRange, %u OSC Freq)\n", + phy_regs->mbps, phy_regs->hsfreqrange, + phy_regs->osc_freq_target); + + /* Prepare lanes related configuration bits */ + lanes_ie |= STM32_CSI_SR1_DL0_ERRORS; + lanes_en |= STM32_CSI_PCR_DL0EN; + if (csidev->num_lanes == 2) { + lanes_ie |= STM32_CSI_SR1_DL1_ERRORS; + lanes_en |= STM32_CSI_PCR_DL1EN; + } + + ret = pm_runtime_get_sync(csidev->dev); + if (ret < 0) + return ret; + + /* Retrieve CSI2PHY clock rate to compute CCFR value */ + phy_clk_frate = clk_get_rate(csidev->clks[STM32_CSI_CLK_CSI2PHY].clk); + if (!phy_clk_frate) { + pm_runtime_put(csidev->dev); + dev_err(csidev->dev, "CSI2PHY clock rate invalid (0)\n"); + return ret; + } + + ret = stm32_csi_setup_lane_merger(csidev); + if (ret) { + pm_runtime_put(csidev->dev); + return ret; + } + + /* Enable the CSI */ + writel_relaxed(STM32_CSI_CR_CSIEN, csidev->base + STM32_CSI_CR); + + /* Enable some global CSI related interrupts - bits are same as SR0 */ + writel_relaxed(STM32_CSI_SR0_ERRORS, csidev->base + STM32_CSI_IER0); + + /* Enable lanes related error interrupts */ + writel_relaxed(lanes_ie, csidev->base + STM32_CSI_IER1); + + /* Initialization of the D-PHY */ + /* Stop the D-PHY */ + writel_relaxed(0, csidev->base + STM32_CSI_PRCR); + + /* Keep the D-PHY in power down state */ + writel_relaxed(0, csidev->base + STM32_CSI_PCR); + + /* Enable testclr clock during 15ns */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + udelay(1); + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set hsfreqrange */ + phy_clk_frate /= 1000000; + ccfr = (phy_clk_frate - 17) * 4; + writel_relaxed((ccfr << STM32_CSI_PFCR_CCFR_SHIFT) | + (phy_regs->hsfreqrange << STM32_CSI_PFCR_HSFR_SHIFT), + csidev->base + STM32_CSI_PFCR); + + /* set reg @08 deskew_polarity_rw 1'b1 */ + stm32_csi_phy_reg_write(csidev, 0x08, 0x38); + + /* set reg @0xE4 counter_for_des_en_config_if_rx 0x10 + DLL prog EN */ + /* This is because 13<= cfgclkfreqrange[5:0]<=38 */ + stm32_csi_phy_reg_write(csidev, 0xe4, 0x11); + + /* set reg @0xe2 & reg @0xe3 value DLL target oscilation freq */ + /* Based on the table page 77, osc_freq_target */ + stm32_csi_phy_reg_write(csidev, 0xe2, phy_regs->osc_freq_target & 0xFF); + stm32_csi_phy_reg_write(csidev, 0xe3, (phy_regs->osc_freq_target >> 8) & 0x0F); + + writel_relaxed(STM32_CSI_PFCR_DLD | readl_relaxed(csidev->base + STM32_CSI_PFCR), + csidev->base + STM32_CSI_PFCR); + + /* Enable Lanes */ + writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN, csidev->base + STM32_CSI_PCR); + writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN | STM32_CSI_PCR_PWRDOWN, + csidev->base + STM32_CSI_PCR); + + writel_relaxed(STM32_CSI_PRCR_PEN, csidev->base + STM32_CSI_PRCR); + + /* Remove the force */ + writel_relaxed(0, csidev->base + STM32_CSI_PMCR); + + return ret; +} + +static void stm32_csi_stop(struct stm32_csi_dev *csidev) +{ + dev_dbg(csidev->dev, "Stopping the CSI2\n"); + + /* Disable the D-PHY */ + writel_relaxed(0, csidev->base + STM32_CSI_PCR); + + /* Disable ITs */ + writel_relaxed(0, csidev->base + STM32_CSI_IER0); + writel_relaxed(0, csidev->base + STM32_CSI_IER1); + + /* Disable the CSI */ + writel_relaxed(0, csidev->base + STM32_CSI_CR); + + pm_runtime_put(csidev->dev); +} + +static int stm32_csi_start_vc(struct stm32_csi_dev *csidev, + struct v4l2_subdev_state *state, u32 vc) +{ + struct v4l2_mbus_framefmt *mbus_fmt; + const struct stm32_csi_fmts *fmt; + u32 cfgr1 = 0; + int ret = 0; + u32 status; + + mbus_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE); + fmt = stm32_csi_code_to_fmt(mbus_fmt->code); + + /* If the mbus code is JPEG, don't enable filtering */ + if (mbus_fmt->code == MEDIA_BUS_FMT_JPEG_1X8) { + cfgr1 |= STM32_CSI_VCXCFGR1_ALLDT; + cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_CDTFT_SHIFT; + dev_dbg(csidev->dev, "VC%d: enable AllDT mode\n", vc); + } else { + cfgr1 |= fmt->datatype << STM32_CSI_VCXCFGR1_DT0_SHIFT; + cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_DT0FT_SHIFT; + cfgr1 |= STM32_CSI_VCXCFGR1_DT0EN; + dev_dbg(csidev->dev, "VC%d: enable DT0(0x%x)/DT0FT(0x%x)\n", + vc, fmt->datatype, fmt->input_fmt); + } + writel_relaxed(cfgr1, csidev->base + STM32_CSI_VCXCFGR1(vc)); + + /* Enable processing of the virtual-channel and wait for its status */ + writel_relaxed(STM32_CSI_CR_VCXSTART(vc) | STM32_CSI_CR_CSIEN, + csidev->base + STM32_CSI_CR); + + ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0, + status, + status & STM32_CSI_SR0_VCXSTATEF(vc), + 1000, 1000000); + if (ret) { + dev_err(csidev->dev, "failed to start VC(%d)\n", vc); + return ret; + } + + return 0; +} + +static int stm32_csi_stop_vc(struct stm32_csi_dev *csidev, u32 vc) +{ + int ret = 0; + u32 status; + + /* Stop the Virtual Channel */ + writel_relaxed(STM32_CSI_CR_VCXSTOP(vc) | STM32_CSI_CR_CSIEN, + csidev->base + STM32_CSI_CR); + + ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0, + status, + !(status & STM32_CSI_SR0_VCXSTATEF(vc)), + 1000, 1000000); + if (ret) { + dev_err(csidev->dev, "failed to stop VC(%d)\n", vc); + return ret; + } + + /* Disable all DTs */ + writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR1(vc)); + writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR2(vc)); + + return 0; +} + +static int stm32_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + int ret; + + ret = v4l2_subdev_disable_streams(csidev->s_subdev, + csidev->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + return ret; + + /* Stop the VC0 */ + ret = stm32_csi_stop_vc(csidev, 0); + if (ret) + dev_err(csidev->dev, "Failed to stop VC0\n"); + + stm32_csi_stop(csidev); + + return 0; +} + +static int stm32_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + int ret; + + ret = stm32_csi_start(csidev, state); + if (ret) + return ret; + + /* Configure & start the VC0 */ + ret = stm32_csi_start_vc(csidev, state, 0); + if (ret) { + dev_err(csidev->dev, "Failed to start VC0\n"); + stm32_csi_stop(csidev); + return ret; + } + + ret = v4l2_subdev_enable_streams(csidev->s_subdev, + csidev->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) { + stm32_csi_stop_vc(csidev, 0); + stm32_csi_stop(csidev); + return ret; + } + + return 0; +} + +static int stm32_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + int i; + + for (i = 0; i < sd->entity.num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = fmt_default; + + return 0; +} + +static int stm32_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(stm32_csi_formats)) + return -EINVAL; + + code->code = stm32_csi_formats[code->index].code; + return 0; +} + +static int stm32_csi_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + struct v4l2_mbus_framefmt *framefmt; + const struct stm32_csi_fmts *fmt; + + fmt = stm32_csi_code_to_fmt(format->format.code); + if (!fmt) { + dev_dbg(csidev->dev, "Unsupported code %d, use default\n", + format->format.code); + format->format.code = fmt_default.code; + } + + framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK); + + if (format->pad == STM32_CSI_PAD_SOURCE) + format->format = *framefmt; + else + *framefmt = format->format; + + framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE); + *framefmt = format->format; + + return 0; +} + +static int stm32_csi_log_status(struct v4l2_subdev *sd) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&csidev->slock, flags); + + for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) { + if (csidev->sr0_counters[i]) + dev_info(csidev->dev, "%s events: %d\n", + stm32_csi_events_sr0[i].name, + csidev->sr0_counters[i]); + } + + for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) { + if (csidev->sr1_counters[i]) + dev_info(csidev->dev, "%s events: %d\n", + stm32_csi_events_sr1[i].name, + csidev->sr1_counters[i]); + } + + spin_unlock_irqrestore(&csidev->slock, flags); + + return 0; +} + +static const struct v4l2_subdev_core_ops stm32_csi_core_ops = { + .log_status = stm32_csi_log_status, +}; + +static const struct v4l2_subdev_video_ops stm32_csi_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops stm32_csi_pad_ops = { + .enum_mbus_code = stm32_csi_enum_mbus_code, + .set_fmt = stm32_csi_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .enable_streams = stm32_csi_enable_streams, + .disable_streams = stm32_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops stm32_csi_subdev_ops = { + .core = &stm32_csi_core_ops, + .pad = &stm32_csi_pad_ops, + .video = &stm32_csi_video_ops, +}; + +static const struct v4l2_subdev_internal_ops stm32_csi_subdev_internal_ops = { + .init_state = stm32_csi_init_state, +}; + +static int stm32_csi_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *s_subdev, + struct v4l2_async_connection *asd) +{ + struct v4l2_subdev *sd = notifier->sd; + struct stm32_csi_dev *csidev = to_csidev(sd); + int remote_pad; + + remote_pad = media_entity_get_fwnode_pad(&s_subdev->entity, + s_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (remote_pad < 0) { + dev_err(csidev->dev, "Couldn't find output pad for subdev %s\n", + s_subdev->name); + return remote_pad; + } + + csidev->s_subdev = s_subdev; + csidev->s_subdev_pad_nb = remote_pad; + + return media_create_pad_link(&csidev->s_subdev->entity, + remote_pad, &csidev->sd.entity, + STM32_CSI_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static const struct v4l2_async_notifier_operations stm32_csi_notifier_ops = { + .bound = stm32_csi_async_bound, +}; + +static irqreturn_t stm32_csi_irq_thread(int irq, void *arg) +{ + struct stm32_csi_dev *csidev = arg; + unsigned long flags; + u32 sr0, sr1; + int i; + + sr0 = readl_relaxed(csidev->base + STM32_CSI_SR0); + sr1 = readl_relaxed(csidev->base + STM32_CSI_SR1); + + /* Clear interrupt */ + writel_relaxed(sr0 & STM32_CSI_SR0_ERRORS, + csidev->base + STM32_CSI_FCR0); + writel_relaxed(sr1 & STM32_CSI_SR1_ERRORS, + csidev->base + STM32_CSI_FCR1); + + spin_lock_irqsave(&csidev->slock, flags); + + for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) + if (sr0 & stm32_csi_events_sr0[i].mask) + csidev->sr0_counters[i]++; + + for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) + if (sr1 & stm32_csi_events_sr1[i].mask) + csidev->sr1_counters[i]++; + + spin_unlock_irqrestore(&csidev->slock, flags); + + return IRQ_HANDLED; +} + +static int stm32_csi_get_resources(struct stm32_csi_dev *csidev, + struct platform_device *pdev) +{ + int irq, ret, i; + + csidev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(csidev->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(csidev->base), + "Failed to ioremap resource\n"); + + for (i = 0; i < STM32_CSI_CLK_NB; i++) + csidev->clks[i].id = stm32_csi_clks_id[i]; + + ret = devm_clk_bulk_get(&pdev->dev, STM32_CSI_CLK_NB, + csidev->clks); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Couldn't get clks\n"); + + csidev->supplies[0].supply = "vdd"; + csidev->supplies[1].supply = "vdda18"; + ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request regulator vdd\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + stm32_csi_irq_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), csidev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to request irq"); + + return 0; +} + +static int stm32_csi_parse_dt(struct stm32_csi_dev *csidev) +{ + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep; + int ret; + + /* Get bus characteristics from devicetree */ + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csidev->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(csidev->dev, "Could not find the endpoint\n"); + return -ENODEV; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + fwnode_handle_put(ep); + if (ret) { + dev_err(csidev->dev, "Could not parse v4l2 endpoint\n"); + return ret; + } + + csidev->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + if (csidev->num_lanes > STM32_CSI_LANES_MAX) { + dev_err(csidev->dev, "Unsupported number of data-lanes: %d\n", + csidev->num_lanes); + return -EINVAL; + } + + memcpy(csidev->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, + sizeof(csidev->lanes)); + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(csidev->dev), NULL); + if (!ep) { + dev_err(csidev->dev, "Failed to get next endpoint\n"); + return -EINVAL; + } + + v4l2_async_subdev_nf_init(&csidev->notifier, &csidev->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&csidev->notifier, ep, + struct v4l2_async_connection); + + fwnode_handle_put(ep); + + if (IS_ERR(asd)) { + dev_err(csidev->dev, "Failed to add fwnode remote subdev\n"); + return PTR_ERR(asd); + } + + csidev->notifier.ops = &stm32_csi_notifier_ops; + + ret = v4l2_async_nf_register(&csidev->notifier); + if (ret) { + dev_err(csidev->dev, "Failed to register notifier\n"); + v4l2_async_nf_cleanup(&csidev->notifier); + return ret; + } + + return ret; +} + +static int stm32_csi_probe(struct platform_device *pdev) +{ + struct stm32_csi_dev *csidev; + struct reset_control *rstc; + int ret; + + csidev = devm_kzalloc(&pdev->dev, sizeof(*csidev), GFP_KERNEL); + if (!csidev) + return -ENOMEM; + + platform_set_drvdata(pdev, csidev); + csidev->dev = &pdev->dev; + + spin_lock_init(&csidev->slock); + + ret = stm32_csi_get_resources(csidev, pdev); + if (ret) + goto err_free_priv; + + ret = stm32_csi_parse_dt(csidev); + if (ret) + goto err_free_priv; + + csidev->sd.owner = THIS_MODULE; + csidev->sd.dev = &pdev->dev; + csidev->sd.internal_ops = &stm32_csi_subdev_internal_ops; + v4l2_subdev_init(&csidev->sd, &stm32_csi_subdev_ops); + v4l2_set_subdevdata(&csidev->sd, &pdev->dev); + snprintf(csidev->sd.name, sizeof(csidev->sd.name), "%s", + dev_name(&pdev->dev)); + + /* Create our media pads */ + csidev->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csidev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csidev->pads[STM32_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + csidev->pads[STM32_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csidev->sd.entity, STM32_CSI_PAD_MAX, + csidev->pads); + if (ret) + goto err_cleanup; + + ret = v4l2_subdev_init_finalize(&csidev->sd); + if (ret < 0) + goto err_cleanup; + + ret = v4l2_async_register_subdev(&csidev->sd); + if (ret < 0) + goto err_cleanup; + + /* Reset device */ + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "Couldn't get reset control\n"); + goto err_cleanup; + } + + ret = reset_control_assert(rstc); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "Failed to assert the reset line\n"); + goto err_cleanup; + } + + usleep_range(3000, 5000); + + ret = reset_control_deassert(rstc); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "Failed to deassert the reset line\n"); + goto err_cleanup; + } + + pm_runtime_enable(&pdev->dev); + + dev_info(&pdev->dev, + "Probed CSI with %u lanes\n", csidev->num_lanes); + + return 0; + +err_cleanup: + v4l2_async_nf_cleanup(&csidev->notifier); +err_free_priv: + return ret; +} + +static void stm32_csi_remove(struct platform_device *pdev) +{ + struct stm32_csi_dev *csidev = platform_get_drvdata(pdev); + + v4l2_async_unregister_subdev(&csidev->sd); + + pm_runtime_disable(&pdev->dev); +} + +static int stm32_csi_runtime_suspend(struct device *dev) +{ + struct stm32_csi_dev *csidev = dev_get_drvdata(dev); + int ret; + + clk_bulk_disable_unprepare(STM32_CSI_CLK_NB, csidev->clks); + + ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret < 0) + dev_err(dev, "cannot disable regulators %d\n", ret); + + return 0; +} + +static int stm32_csi_runtime_resume(struct device *dev) +{ + struct stm32_csi_dev *csidev = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret) + goto error_out; + + ret = clk_bulk_prepare_enable(STM32_CSI_CLK_NB, csidev->clks); + if (ret) + goto error_disable_supplies; + + return 0; + +error_disable_supplies: + ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), csidev->supplies); + if (ret < 0) + dev_err(dev, "cannot disable regulators %d\n", ret); +error_out: + dev_err(csidev->dev, "Failed to resume: %d\n", ret); + + return ret; +} + +static const struct of_device_id stm32_csi_of_table[] = { + { .compatible = "st,stm32mp25-csi", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_csi_of_table); + +static const struct dev_pm_ops stm32_csi_pm_ops = { + RUNTIME_PM_OPS(stm32_csi_runtime_suspend, + stm32_csi_runtime_resume, NULL) +}; + +static struct platform_driver stm32_csi_driver = { + .driver = { + .name = "stm32-csi", + .of_match_table = stm32_csi_of_table, + .pm = pm_ptr(&stm32_csi_pm_ops), + }, + .probe = stm32_csi_probe, + .remove = stm32_csi_remove, +}; + +module_platform_driver(stm32_csi_driver); + +MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>"); +MODULE_DESCRIPTION("STM32 CSI controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index ff3331af9406..9b699ee2b1e0 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -898,8 +898,6 @@ static const struct vb2_ops dcmi_video_qops = { .buf_queue = dcmi_buf_queue, .start_streaming = dcmi_start_streaming, .stop_streaming = dcmi_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int dcmi_g_fmt_vid_cap(struct file *file, void *priv, @@ -2149,7 +2147,7 @@ static const struct dev_pm_ops dcmi_pm_ops = { static struct platform_driver stm32_dcmi_driver = { .probe = dcmi_probe, - .remove_new = dcmi_remove, + .remove = dcmi_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(stm32_dcmi_of_match), diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile index 8920d9388a21..159105fb40b8 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile @@ -1,4 +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 +stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.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 index 9f768f011fa2..1c1b6b48918e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -56,15 +56,32 @@ struct dcmipp_bytecap_pix_map { static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565), + PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565), PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV), + PIXMAP_MBUS_PFMT(YUYV8_1X16, YUYV), PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU), + PIXMAP_MBUS_PFMT(YVYU8_1X16, YVYU), PIXMAP_MBUS_PFMT(UYVY8_2X8, UYVY), + PIXMAP_MBUS_PFMT(UYVY8_1X16, UYVY), PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY), + PIXMAP_MBUS_PFMT(VYUY8_1X16, 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(SBGGR10_1X10, SBGGR10), + PIXMAP_MBUS_PFMT(SGBRG10_1X10, SGBRG10), + PIXMAP_MBUS_PFMT(SGRBG10_1X10, SGRBG10), + PIXMAP_MBUS_PFMT(SRGGB10_1X10, SRGGB10), + PIXMAP_MBUS_PFMT(SBGGR12_1X12, SBGGR12), + PIXMAP_MBUS_PFMT(SGBRG12_1X12, SGBRG12), + PIXMAP_MBUS_PFMT(SGRBG12_1X12, SGRBG12), + PIXMAP_MBUS_PFMT(SRGGB12_1X12, SRGGB12), + PIXMAP_MBUS_PFMT(SBGGR14_1X14, SBGGR14), + PIXMAP_MBUS_PFMT(SGBRG14_1X14, SGBRG14), + PIXMAP_MBUS_PFMT(SGRBG14_1X14, SGRBG14), + PIXMAP_MBUS_PFMT(SRGGB14_1X14, SRGGB14), PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG), }; @@ -112,6 +129,7 @@ struct dcmipp_bytecap_device { u32 sequence; struct media_pipeline pipe; struct v4l2_subdev *s_subdev; + u32 s_subdev_pad_nb; enum dcmipp_state state; @@ -250,34 +268,34 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv, { const struct dcmipp_bytecap_pix_map *vpix; unsigned int index = f->index; - unsigned int i; + unsigned int i, prev_pixelformat = 0; - 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; + /* + * List up all formats (or only ones matching f->mbus_code), taking + * care of removing duplicated entries (due to support of both + * parallel & csi 16 bits formats + */ + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + vpix = &dcmipp_bytecap_pix_map_list[i]; + /* Skip formats not matching requested mbus code */ + if (f->mbus_code && vpix->code != f->mbus_code) + continue; - if (index == 0) - break; + /* Skip duplicated pixelformat */ + if (vpix->pixelformat == prev_pixelformat) + continue; - index--; - } + prev_pixelformat = vpix->pixelformat; - 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; + if (index == 0) + break; - vpix = &dcmipp_bytecap_pix_map_list[f->index]; + index--; } + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + f->pixelformat = vpix->pixelformat; return 0; @@ -337,33 +355,6 @@ static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = { .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) { @@ -395,11 +386,24 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); struct media_entity *entity = &vcap->vdev.entity; struct dcmipp_buf *buf; + struct media_pad *pad; int ret; vcap->sequence = 0; memset(&vcap->count, 0, sizeof(vcap->count)); + /* + * 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); + vcap->s_subdev_pad_nb = pad->index; + } + 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", @@ -414,7 +418,8 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, goto err_pm_put; } - ret = dcmipp_pipeline_s_stream(vcap, 1); + ret = v4l2_subdev_enable_streams(vcap->s_subdev, + vcap->s_subdev_pad_nb, BIT_ULL(0)); if (ret) goto err_media_pipeline_stop; @@ -482,7 +487,10 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) int ret; u32 status; - dcmipp_pipeline_s_stream(vcap, 0); + ret = v4l2_subdev_disable_streams(vcap->s_subdev, + vcap->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + dev_warn(vcap->dev, "Failed to disable stream\n"); /* Stop the media pipeline */ media_pipeline_stop(vcap->vdev.entity.pads); @@ -625,12 +633,6 @@ static const struct vb2_ops dcmipp_bytecap_qops = { .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) @@ -816,8 +818,7 @@ static int dcmipp_bytecap_link_validate(struct media_link *link) .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = link->source->index, }; - const struct dcmipp_bytecap_pix_map *vpix; - int ret; + int ret, i; ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt); if (ret < 0) @@ -831,10 +832,17 @@ static int dcmipp_bytecap_link_validate(struct media_link *link) 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); + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + if (dcmipp_bytecap_pix_map_list[i].pixelformat == + vcap->format.pixelformat && + dcmipp_bytecap_pix_map_list[i].code == + source_fmt.format.code) + break; + } + + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) { + dev_err(vcap->dev, "mbus code 0x%x do not match capture device format (0x%x)\n", + vcap->format.pixelformat, source_fmt.format.code); return -EINVAL; } @@ -893,7 +901,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, q->dev = dev; /* DCMIPP requires 16 bytes aligned buffers */ - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32) & ~0x0f); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { dev_err(dev, "Failed to set DMA mask\n"); goto err_mutex_destroy; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c index 5a361ad6b023..3c742a546441 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -48,15 +48,32 @@ struct dcmipp_byteproc_pix_map { } static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2), + PIXMAP_MBUS_BPP(RGB565_1X16, 2), PIXMAP_MBUS_BPP(YUYV8_2X8, 2), + PIXMAP_MBUS_BPP(YUYV8_1X16, 2), PIXMAP_MBUS_BPP(YVYU8_2X8, 2), + PIXMAP_MBUS_BPP(YVYU8_1X16, 2), PIXMAP_MBUS_BPP(UYVY8_2X8, 2), + PIXMAP_MBUS_BPP(UYVY8_1X16, 2), PIXMAP_MBUS_BPP(VYUY8_2X8, 2), + PIXMAP_MBUS_BPP(VYUY8_1X16, 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(SBGGR10_1X10, 2), + PIXMAP_MBUS_BPP(SGBRG10_1X10, 2), + PIXMAP_MBUS_BPP(SGRBG10_1X10, 2), + PIXMAP_MBUS_BPP(SRGGB10_1X10, 2), + PIXMAP_MBUS_BPP(SBGGR12_1X12, 2), + PIXMAP_MBUS_BPP(SGBRG12_1X12, 2), + PIXMAP_MBUS_BPP(SGRBG12_1X12, 2), + PIXMAP_MBUS_BPP(SRGGB12_1X12, 2), + PIXMAP_MBUS_BPP(SBGGR14_1X14, 2), + PIXMAP_MBUS_BPP(SGBRG14_1X14, 2), + PIXMAP_MBUS_BPP(SGRBG14_1X14, 2), + PIXMAP_MBUS_BPP(SRGGB14_1X14, 2), PIXMAP_MBUS_BPP(JPEG_1X8, 1), }; @@ -78,7 +95,6 @@ struct dcmipp_byteproc_device { struct v4l2_subdev sd; struct device *dev; void __iomem *regs; - bool streaming; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -239,11 +255,10 @@ 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) + if (v4l2_subdev_is_streaming(sd)) return -EBUSY; mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); @@ -382,30 +397,19 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, 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) + (struct dcmipp_byteproc_device *byteproc, + struct v4l2_subdev_state *state) { 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); @@ -460,48 +464,73 @@ static int dcmipp_byteproc_configure_scale_crop return 0; } -static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable) +static int dcmipp_byteproc_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); struct v4l2_subdev *s_subdev; - struct media_pad *pad; - int ret = 0; + struct media_pad *s_pad; + int ret; /* Get source subdev */ - pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_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; - } + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = dcmipp_byteproc_configure_scale_crop(byteproc, state); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; } - byteproc->streaming = enable; + return 0; +} + +static int dcmipp_byteproc_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } 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, + .enable_streams = dcmipp_byteproc_enable_streams, + .disable_streams = dcmipp_byteproc_disable_streams, +}; + static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = { - .s_stream = dcmipp_byteproc_s_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_ops dcmipp_byteproc_ops = { diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h index 7a7cf43baf24..fe5f97233f5e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h @@ -199,11 +199,11 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg, } /* DCMIPP subdev init / release entry points */ -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, +struct dcmipp_ent_device *dcmipp_inp_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); +void dcmipp_inp_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); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c index bce821eb71ce..71acf539e1f3 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -40,6 +40,7 @@ struct dcmipp_device { /* Hardware resources */ void __iomem *regs; + struct clk *mclk; struct clk *kclk; /* The pipeline configuration */ @@ -87,6 +88,7 @@ struct dcmipp_pipeline_config { size_t num_ents; const struct dcmipp_ent_link *links; size_t num_links; + u32 hw_revision; }; /* -------------------------------------------------------------------------- @@ -95,9 +97,9 @@ struct dcmipp_pipeline_config { static const struct dcmipp_ent_config stm32mp13_ent_config[] = { { - .name = "dcmipp_parallel", - .init = dcmipp_par_ent_init, - .release = dcmipp_par_ent_release, + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_ent_release, }, { .name = "dcmipp_dump_postproc", @@ -111,22 +113,58 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = { }, }; -#define ID_PARALLEL 0 +#define ID_INPUT 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, + DCMIPP_ENT_LINK(ID_INPUT, 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), }; +#define DCMIPP_STM32MP13_VERR 0x10 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) + .num_links = ARRAY_SIZE(stm32mp13_ent_links), + .hw_revision = DCMIPP_STM32MP13_VERR +}; + +static const struct dcmipp_ent_config stm32mp25_ent_config[] = { + { + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_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, + }, +}; + +static const struct dcmipp_ent_link stm32mp25_ent_links[] = { + DCMIPP_ENT_LINK(ID_INPUT, 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), +}; + +#define DCMIPP_STM32MP25_VERR 0x30 +static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = { + .ents = stm32mp25_ent_config, + .num_ents = ARRAY_SIZE(stm32mp25_ent_config), + .links = stm32mp25_ent_links, + .num_links = ARRAY_SIZE(stm32mp25_ent_links), + .hw_revision = DCMIPP_STM32MP25_VERR }; #define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\ @@ -202,13 +240,14 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) return 0; err_init_entity: - while (i > 0) - dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); + while (i-- > 0) + dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); return ret; } static const struct of_device_id dcmipp_of_match[] = { { .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg }, + { .compatible = "st,stm32mp25-dcmipp", .data = &stm32mp25_pipe_cfg }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, dcmipp_of_match); @@ -258,13 +297,22 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, { struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); unsigned int ret; - int src_pad; + int src_pad, i; struct dcmipp_ent_device *sink; - struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct v4l2_fwnode_endpoint vep = { 0 }; struct fwnode_handle *ep; + enum v4l2_mbus_type supported_types[] = { + V4L2_MBUS_PARALLEL, V4L2_MBUS_BT656, V4L2_MBUS_CSI2_DPHY + }; + int supported_types_nb = ARRAY_SIZE(supported_types); dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name); + /* Only MP25 supports CSI input */ + if (!of_device_is_compatible(dcmipp->dev->of_node, + "st,stm32mp25-dcmipp")) + supported_types_nb--; + /* * Link this sub-device to DCMIPP, it could be * a parallel camera sensor or a CSI-2 to parallel bridge @@ -281,21 +329,23 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, 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; + /* Check for supported MBUS type */ + for (i = 0; i < supported_types_nb; i++) { + vep.bus_type = supported_types[i]; 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; - } + if (!ret) + break; } fwnode_handle_put(ep); - if (vep.bus.parallel.bus_width == 0) { + if (ret) { + dev_err(dcmipp->dev, "Could not parse the endpoint\n"); + return ret; + } + + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && + vep.bus.parallel.bus_width == 0) { dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n"); return -ENODEV; } @@ -308,11 +358,13 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, 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; + /* Connect input device to the dcmipp_input subdev */ + sink = dcmipp->entity[ID_INPUT]; + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) { + 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 | @@ -411,7 +463,7 @@ static int dcmipp_graph_init(struct dcmipp_device *dcmipp) static int dcmipp_probe(struct platform_device *pdev) { struct dcmipp_device *dcmipp; - struct clk *kclk; + struct clk *kclk, *mclk; const struct dcmipp_pipeline_config *pipe_cfg; struct reset_control *rstc; int irq; @@ -439,11 +491,8 @@ static int dcmipp_probe(struct platform_device *pdev) "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; - } + if (irq < 0) + return irq; dcmipp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(dcmipp->regs)) { @@ -474,12 +523,20 @@ static int dcmipp_probe(struct platform_device *pdev) return ret; } - kclk = devm_clk_get(&pdev->dev, NULL); + kclk = devm_clk_get(&pdev->dev, "kclk"); if (IS_ERR(kclk)) return dev_err_probe(&pdev->dev, PTR_ERR(kclk), "Unable to get kclk\n"); dcmipp->kclk = kclk; + if (!of_device_is_compatible(pdev->dev.of_node, "st,stm32mp13-dcmipp")) { + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(mclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(mclk), + "Unable to get mclk\n"); + dcmipp->mclk = mclk; + } + dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents, sizeof(*dcmipp->entity), GFP_KERNEL); if (!dcmipp->entity) @@ -499,6 +556,7 @@ static int dcmipp_probe(struct platform_device *pdev) /* Initialize media device */ strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME, sizeof(dcmipp->mdev.model)); + dcmipp->mdev.hw_revision = pipe_cfg->hw_revision; dcmipp->mdev.dev = &pdev->dev; media_device_init(&dcmipp->mdev); @@ -541,6 +599,7 @@ static int dcmipp_runtime_suspend(struct device *dev) struct dcmipp_device *dcmipp = dev_get_drvdata(dev); clk_disable_unprepare(dcmipp->kclk); + clk_disable_unprepare(dcmipp->mclk); return 0; } @@ -550,9 +609,17 @@ static int dcmipp_runtime_resume(struct device *dev) struct dcmipp_device *dcmipp = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(dcmipp->mclk); + if (ret) { + dev_err(dev, "%s: Failed to prepare_enable mclk\n", __func__); + return ret; + } + ret = clk_prepare_enable(dcmipp->kclk); - if (ret) + if (ret) { + clk_disable_unprepare(dcmipp->mclk); dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__); + } return ret; } @@ -586,7 +653,7 @@ static const struct dev_pm_ops dcmipp_pm_ops = { static struct platform_driver dcmipp_pdrv = { .probe = dcmipp_probe, - .remove_new = dcmipp_remove, + .remove = dcmipp_remove, .driver = { .name = DCMIPP_PDEV_NAME, .of_match_table = dcmipp_of_match, diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c new file mode 100644 index 000000000000..7e5311b67d7e --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c @@ -0,0 +1,540 @@ +// 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/mipi-csi2.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_RAW10 0x2b +#define DCMIPP_PRCR_FORMAT_RAW12 0x2c +#define DCMIPP_PRCR_FORMAT_RAW14 0x2d +#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 DCMIPP_CMCR 0x204 +#define DCMIPP_CMCR_INSEL BIT(0) + +#define DCMIPP_P0FSCR 0x404 +#define DCMIPP_P0FSCR_DTMODE_MASK GENMASK(17, 16) +#define DCMIPP_P0FSCR_DTMODE_SHIFT 16 +#define DCMIPP_P0FSCR_DTMODE_DTIDA 0x00 +#define DCMIPP_P0FSCR_DTMODE_ALLDT 0x03 +#define DCMIPP_P0FSCR_DTIDA_MASK GENMASK(5, 0) +#define DCMIPP_P0FSCR_DTIDA_SHIFT 0 + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_inp_pix_map { + unsigned int code_sink; + unsigned int code_src; + /* Parallel related information */ + u8 prcr_format; + u8 prcr_swapcycles; + /* CSI related information */ + unsigned int dt; +}; + +#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap, data_type) \ + { \ + .code_sink = MEDIA_BUS_FMT_##sink, \ + .code_src = MEDIA_BUS_FMT_##src, \ + .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ + .prcr_swapcycles = swap, \ + .dt = data_type, \ + } +static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = { + /* RGB565 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1, MIPI_CSI2_DT_RGB565), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0, MIPI_CSI2_DT_RGB565), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_1X16, RGB565_1X16, RGB565, 0, MIPI_CSI2_DT_RGB565), + /* YUV422 */ + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_1X16, UYVY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_1X16, YVYU8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + /* GREY */ + PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8), + /* Raw Bayer */ + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR10_1X10, SBGGR10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG10_1X10, SGBRG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG10_1X10, SGRBG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB10_1X10, SRGGB10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR12_1X12, SBGGR12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG12_1X12, SGBRG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG12_1X12, SGRBG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB12_1X12, SRGGB12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR14_1X14, SBGGR14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG14_1X14, SGBRG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG14_1X14, SGRBG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB14_1X14, SRGGB14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + /* JPEG */ + PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0, 0), +}; + +/* + * Search through the pix_map table, skipping two consecutive entry with the + * same code + */ +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_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_inp_pix_map_list)) { + if (IS_SRC(pad)) + cur_code = dcmipp_inp_pix_map_list[i].code_src; + else + cur_code = dcmipp_inp_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_inp_pix_map_list)) + return NULL; + + return &dcmipp_inp_pix_map_list[i]; +} + +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_code + (u32 code_sink, u32 code_src) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_inp_pix_map_list); i++) { + if ((dcmipp_inp_pix_map_list[i].code_sink == code_sink && + dcmipp_inp_pix_map_list[i].code_src == code_src) || + (dcmipp_inp_pix_map_list[i].code_sink == code_src && + dcmipp_inp_pix_map_list[i].code_src == code_sink) || + (dcmipp_inp_pix_map_list[i].code_sink == code_sink && + code_src == 0) || + (code_sink == 0 && + dcmipp_inp_pix_map_list[i].code_src == code_src)) + return &dcmipp_inp_pix_map_list[i]; + } + return NULL; +} + +struct dcmipp_inp_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; +}; + +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_inp_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_inp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_inp_pix_map *vpix = + dcmipp_inp_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_inp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct dcmipp_inp_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_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_inp_adjust_fmt(struct dcmipp_inp_device *inp, + struct v4l2_mbus_framefmt *fmt, __u32 pad) +{ + const struct dcmipp_inp_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_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 && + inp->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_inp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_inp_device *inp = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + /* Set the new format */ + dcmipp_inp_adjust_fmt(inp, &fmt->format, fmt->pad); + + dev_dbg(inp->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", + inp->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_inp_adjust_fmt(inp, mf, 1); + } + + return 0; +} + +static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + u32 val = 0; + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Set vertical synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_HSPOL; + + /* Set pixel clock polarity */ + if (inp->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 (inp->ved.bus_type == V4L2_MBUS_BT656) { + val |= DCMIPP_PRCR_ESS; + + /* Unmask all codes */ + reg_write(inp, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(inp, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + + /* Set format */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->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(inp, DCMIPP_PRCR, val); + + /* Select the DCMIPP parallel interface */ + reg_write(inp, DCMIPP_CMCR, 0); + + /* Enable parallel interface */ + reg_set(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + return 0; +} + +static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Get format information */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + /* Apply configuration on each input pipe */ + reg_clear(inp, DCMIPP_P0FSCR, + DCMIPP_P0FSCR_DTMODE_MASK | DCMIPP_P0FSCR_DTIDA_MASK); + + /* In case of JPEG we don't know the DT so we allow all data */ + /* + * TODO - check instead dt == 0 for the time being to allow other + * unknown data-type + */ + if (!vpix->dt) + reg_set(inp, DCMIPP_P0FSCR, + DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_P0FSCR_DTMODE_SHIFT); + else + reg_set(inp, DCMIPP_P0FSCR, + vpix->dt << DCMIPP_P0FSCR_DTIDA_SHIFT | + DCMIPP_P0FSCR_DTMODE_DTIDA); + + /* Select the DCMIPP CSI interface */ + reg_write(inp, DCMIPP_CMCR, DCMIPP_CMCR_INSEL); + + return 0; +} + +static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret = 0; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + if (inp->ved.bus_type == V4L2_MBUS_PARALLEL || + inp->ved.bus_type == V4L2_MBUS_BT656) + ret = dcmipp_inp_configure_parallel(inp, state); + else if (inp->ved.bus_type == V4L2_MBUS_CSI2_DPHY) + ret = dcmipp_inp_configure_csi(inp, state); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } + + return 0; +} + +static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to stop source subdev streaming (%d)\n", ret); + return ret; + } + + if (inp->ved.bus_type == V4L2_MBUS_PARALLEL || + inp->ved.bus_type == V4L2_MBUS_BT656) { + /* Disable parallel interface */ + reg_clear(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_inp_pad_ops = { + .enum_mbus_code = dcmipp_inp_enum_mbus_code, + .enum_frame_size = dcmipp_inp_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_inp_set_fmt, + .enable_streams = dcmipp_inp_enable_streams, + .disable_streams = dcmipp_inp_disable_streams, +}; + +static const struct v4l2_subdev_video_ops dcmipp_inp_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops dcmipp_inp_ops = { + .pad = &dcmipp_inp_pad_ops, + .video = &dcmipp_inp_video_ops, +}; + +static void dcmipp_inp_release(struct v4l2_subdev *sd) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + + kfree(inp); +} + +static const struct v4l2_subdev_internal_ops dcmipp_inp_int_ops = { + .init_state = dcmipp_inp_init_state, + .release = dcmipp_inp_release, +}; + +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_inp_device *inp = + container_of(ved, struct dcmipp_inp_device, ved); + + dcmipp_ent_sd_unregister(ved, &inp->sd); +} + +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_inp_device *inp; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the inp struct */ + inp = kzalloc(sizeof(*inp), GFP_KERNEL); + if (!inp) + return ERR_PTR(-ENOMEM); + + inp->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev, + entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_inp_int_ops, &dcmipp_inp_ops, + NULL, NULL); + if (ret) { + kfree(inp); + return ERR_PTR(ret); + } + + inp->dev = dev; + + return &inp->ved; +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c deleted file mode 100644 index 62c5c3331cfe..000000000000 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c +++ /dev/null @@ -1,440 +0,0 @@ -// 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 097a3a08ef7d..e53a07b770b7 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -35,7 +35,18 @@ struct sun4i_csi_traits { bool has_isp; }; +static int sun4i_csi_video_link_validate(struct media_link *link) +{ + dev_warn_once(link->graph_obj.mdev->dev, + "Driver bug: link validation not implemented\n"); + return 0; +} + static const struct media_entity_operations sun4i_csi_video_entity_ops = { + .link_validate = sun4i_csi_video_link_validate, +}; + +static const struct media_entity_operations sun4i_csi_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -214,6 +225,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) 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->entity.ops = &sun4i_csi_subdev_entity_ops; subdev->owner = THIS_MODULE; snprintf(subdev->name, sizeof(subdev->name), "sun4i-csi-0"); v4l2_set_subdevdata(subdev, csi); @@ -328,7 +340,7 @@ static const struct dev_pm_ops sun4i_csi_pm_ops = { static struct platform_driver sun4i_csi_driver = { .probe = sun4i_csi_probe, - .remove_new = sun4i_csi_remove, + .remove = sun4i_csi_remove, .driver = { .name = "sun4i-csi", .of_match_table = sun4i_csi_of_match, diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index d1371e130113..e911c7f7acc5 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -371,8 +371,6 @@ static const struct vb2_ops sun4i_csi_qops = { .buf_queue = sun4i_csi_buffer_queue, .start_streaming = sun4i_csi_start_streaming, .stop_streaming = sun4i_csi_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static irqreturn_t sun4i_csi_irq(int irq, void *data) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index c6ba385c0c86..af2a32c226a5 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -423,7 +423,7 @@ MODULE_DEVICE_TABLE(of, sun6i_csi_of_match); static struct platform_driver sun6i_csi_platform_driver = { .probe = sun6i_csi_probe, - .remove_new = sun6i_csi_remove, + .remove = sun6i_csi_remove, .driver = { .name = SUN6I_CSI_NAME, .of_match_table = sun6i_csi_of_match, 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 14c0dc827c52..76356bc7f10e 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c @@ -657,8 +657,6 @@ static const struct vb2_ops sun6i_csi_capture_queue_ops = { .buf_queue = sun6i_csi_capture_buffer_queue, .start_streaming = sun6i_csi_capture_start_streaming, .stop_streaming = sun6i_csi_capture_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* V4L2 Device */ 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 f9d4dc45b490..b06cb73015cd 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 @@ -757,7 +757,7 @@ MODULE_DEVICE_TABLE(of, sun6i_mipi_csi2_of_match); static struct platform_driver sun6i_mipi_csi2_platform_driver = { .probe = sun6i_mipi_csi2_probe, - .remove_new = sun6i_mipi_csi2_remove, + .remove = sun6i_mipi_csi2_remove, .driver = { .name = SUN6I_MIPI_CSI2_NAME, .of_match_table = sun6i_mipi_csi2_of_match, diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig index 47a8c0fb7eb9..99c401e653bc 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig @@ -8,6 +8,7 @@ config VIDEO_SUN8I_A83T_MIPI_CSI2 select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE select REGMAP_MMIO + select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY help Support for the Allwinner A83T MIPI CSI-2 controller and D-PHY. 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 4a5698eb12b7..dbc51daa4fe3 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 @@ -824,7 +824,7 @@ MODULE_DEVICE_TABLE(of, sun8i_a83t_mipi_csi2_of_match); static struct platform_driver sun8i_a83t_mipi_csi2_platform_driver = { .probe = sun8i_a83t_mipi_csi2_probe, - .remove_new = sun8i_a83t_mipi_csi2_remove, + .remove = sun8i_a83t_mipi_csi2_remove, .driver = { .name = SUN8I_A83T_MIPI_CSI2_NAME, .of_match_table = sun8i_a83t_mipi_csi2_of_match, diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index a1c35a2b68ed..3e7f2df70408 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -659,8 +659,6 @@ static const struct vb2_ops deinterlace_qops = { .buf_queue = deinterlace_buf_queue, .start_streaming = deinterlace_start_streaming, .stop_streaming = deinterlace_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, @@ -1001,7 +999,7 @@ static const struct dev_pm_ops deinterlace_pm_ops = { static struct platform_driver deinterlace_driver = { .probe = deinterlace_probe, - .remove_new = deinterlace_remove, + .remove = deinterlace_remove, .driver = { .name = DEINTERLACE_NAME, .of_match_table = deinterlace_dt_match, diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c index a12323ca89fa..abd10b218aa1 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -522,8 +522,6 @@ static const struct vb2_ops rotate_qops = { .buf_queue = rotate_buf_queue, .start_streaming = rotate_start_streaming, .stop_streaming = rotate_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, @@ -905,7 +903,7 @@ static const struct dev_pm_ops rotate_pm_ops = { static struct platform_driver rotate_driver = { .probe = rotate_probe, - .remove_new = rotate_remove, + .remove = rotate_remove, .driver = { .name = ROTATE_NAME, .of_match_table = rotate_dt_match, diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 77e12457d149..44cdccb89377 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -2079,8 +2079,6 @@ static long vpfe_ioctl_default(struct file *file, void *priv, } static const struct vb2_ops vpfe_video_qops = { - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .queue_setup = vpfe_queue_setup, .buf_prepare = vpfe_buffer_prepare, .buf_queue = vpfe_buffer_queue, @@ -2287,7 +2285,7 @@ static const struct v4l2_async_notifier_operations vpfe_async_ops = { static struct vpfe_config * vpfe_get_pdata(struct vpfe_device *vpfe) { - struct device_node *endpoint = NULL; + struct device_node *endpoint; struct device *dev = vpfe->pdev; struct vpfe_subdev_info *sdinfo; struct vpfe_config *pdata; @@ -2306,14 +2304,11 @@ vpfe_get_pdata(struct vpfe_device *vpfe) if (!pdata) return NULL; - for (i = 0; ; i++) { + i = 0; + for_each_endpoint_of_node(dev->of_node, endpoint) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *rem; - endpoint = of_graph_get_next_endpoint(dev->of_node, endpoint); - if (!endpoint) - break; - sdinfo = &pdata->sub_devs[i]; sdinfo->grp_id = 0; @@ -2371,9 +2366,10 @@ vpfe_get_pdata(struct vpfe_device *vpfe) of_node_put(rem); if (IS_ERR(pdata->asd[i])) goto cleanup; + + i++; } - of_node_put(endpoint); return pdata; cleanup: @@ -2619,7 +2615,7 @@ MODULE_DEVICE_TABLE(of, vpfe_of_match); static struct platform_driver vpfe_driver = { .probe = vpfe_probe, - .remove_new = vpfe_remove, + .remove = vpfe_remove, .driver = { .name = VPFE_MODULE_NAME, .pm = &vpfe_pm_ops, diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 4afc2ad00330..42dfe08b765f 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -798,7 +798,7 @@ 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 = { +static const struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index e1ba5dfc217e..e29743ae61e2 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -808,8 +808,6 @@ static const struct vb2_ops cal_video_qops = { .buf_queue = cal_buffer_queue, .start_streaming = cal_start_streaming, .stop_streaming = cal_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* ------------------------------------------------------------------ diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 528909ae4bd6..4bd2092e0255 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -549,7 +549,7 @@ void cal_ctx_start(struct cal_ctx *ctx) void cal_ctx_stop(struct cal_ctx *ctx) { struct cal_camerarx *phy = ctx->phy; - long timeout; + long time_left; WARN_ON(phy->vc_enable_count[ctx->vc] == 0); @@ -565,9 +565,9 @@ void cal_ctx_stop(struct cal_ctx *ctx) ctx->dma.state = CAL_DMA_STOP_REQUESTED; spin_unlock_irq(&ctx->dma.lock); - timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), - msecs_to_jiffies(500)); - if (!timeout) { + time_left = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), + msecs_to_jiffies(500)); + if (!time_left) { ctx_err(ctx, "failed to disable dma cleanly\n"); cal_ctx_wr_dma_disable(ctx); } @@ -1332,7 +1332,7 @@ static const struct dev_pm_ops cal_pm_ops = { static struct platform_driver cal_pdrv = { .probe = cal_probe, - .remove_new = cal_remove, + .remove = cal_remove, .driver = { .name = CAL_MODULE_NAME, .pm = &cal_pm_ops, diff --git a/drivers/media/platform/ti/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c index f4e1fa76bf37..a81719702a22 100644 --- a/drivers/media/platform/ti/davinci/vpif.c +++ b/drivers/media/platform/ti/davinci/vpif.c @@ -589,7 +589,7 @@ static struct platform_driver vpif_driver = { .name = VPIF_DRIVER_NAME, .pm = vpif_pm_ops, }, - .remove_new = vpif_remove, + .remove = vpif_remove, .probe = vpif_probe, }; diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index c31a5566fc5a..d053972888d1 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -310,8 +310,6 @@ static const struct vb2_ops video_qops = { .start_streaming = vpif_start_streaming, .stop_streaming = vpif_stop_streaming, .buf_queue = vpif_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /** @@ -1132,7 +1130,7 @@ vpif_query_dv_timings(struct file *file, void *priv, if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) return -ENODATA; - ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings); + ret = v4l2_subdev_call(ch->sd, pad, query_dv_timings, 0, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -ENODATA; @@ -1177,7 +1175,7 @@ static int vpif_s_dv_timings(struct file *file, void *priv, return -EBUSY; /* Configure subdevice timings, if any */ - ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings); + ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) ret = 0; if (ret < 0) { @@ -1487,7 +1485,7 @@ static struct vpif_capture_config * vpif_capture_get_pdata(struct platform_device *pdev, struct v4l2_device *v4l2_dev) { - struct device_node *endpoint = NULL; + struct device_node *endpoint; struct device_node *rem = NULL; struct vpif_capture_config *pdata; struct vpif_subdev_info *sdinfo; @@ -1517,16 +1515,12 @@ vpif_capture_get_pdata(struct platform_device *pdev, if (!pdata->subdev_info) return NULL; - for (i = 0; i < VPIF_CAPTURE_NUM_CHANNELS; i++) { + i = 0; + for_each_endpoint_of_node(pdev->dev.of_node, endpoint) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; unsigned int flags; int err; - endpoint = of_graph_get_next_endpoint(pdev->dev.of_node, - endpoint); - if (!endpoint) - break; - rem = of_graph_get_remote_port_parent(endpoint); if (!rem) { dev_dbg(&pdev->dev, "Remote device at %pOF not found\n", @@ -1577,6 +1571,10 @@ vpif_capture_get_pdata(struct platform_device *pdev, goto err_cleanup; of_node_put(rem); + + i++; + if (i >= VPIF_CAPTURE_NUM_CHANNELS) + break; } done: @@ -1815,7 +1813,7 @@ static __refdata struct platform_driver vpif_driver = { .pm = &vpif_pm_ops, }, .probe = vpif_probe, - .remove_new = vpif_remove, + .remove = vpif_remove, }; module_platform_driver(vpif_driver); diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c index 02ede1fe12cb..70c89549f4b6 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -293,8 +293,6 @@ static void vpif_stop_streaming(struct vb2_queue *vq) static const struct vb2_ops video_qops = { .queue_setup = vpif_buffer_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_prepare = vpif_buffer_prepare, .start_streaming = vpif_start_streaming, .stop_streaming = vpif_stop_streaming, @@ -934,7 +932,7 @@ static int vpif_s_dv_timings(struct file *file, void *priv, } /* Configure subdevice timings, if any */ - ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings); + ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) ret = 0; if (ret < 0) { @@ -1398,7 +1396,7 @@ static __refdata struct platform_driver vpif_driver = { .pm = &vpif_pm_ops, }, .probe = vpif_probe, - .remove_new = vpif_remove, + .remove = vpif_remove, }; module_platform_driver(vpif_driver); diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 6da83d0cffaa..6412a00be8ea 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -786,15 +786,14 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) dev_warn(csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); + spin_lock_irqsave(&dma->lock, flags); ret = ti_csi2rx_start_dma(csi, buf); if (ret) { - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); - spin_lock_irqsave(&dma->lock, flags); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dma->state = TI_CSI2RX_DMA_IDLE; spin_unlock_irqrestore(&dma->lock, flags); + dev_err(csi->dev, "Failed to start DMA: %d\n", ret); } else { - spin_lock_irqsave(&dma->lock, flags); list_add_tail(&buf->list, &dma->submitted); spin_unlock_irqrestore(&dma->lock, flags); } @@ -879,8 +878,6 @@ static const struct vb2_ops csi_vb2_qops = { .buf_queue = ti_csi2rx_buffer_queue, .start_streaming = ti_csi2rx_start_streaming, .stop_streaming = ti_csi2rx_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) @@ -1015,9 +1012,9 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) pix_fmt->height = 480; pix_fmt->field = V4L2_FIELD_NONE; pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; - pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601, - pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE, - pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB, + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; ti_csi2rx_fill_fmt(fmt, &csi->v_fmt); @@ -1164,7 +1161,7 @@ MODULE_DEVICE_TABLE(of, ti_csi2rx_of_match); static struct platform_driver ti_csi2rx_pdrv = { .probe = ti_csi2rx_probe, - .remove_new = ti_csi2rx_remove, + .remove = ti_csi2rx_remove, .driver = { .name = TI_CSI2RX_MODULE_NAME, .of_match_table = ti_csi2rx_of_match, diff --git a/drivers/media/platform/ti/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c index 1c56b6a87ced..a87d5030ac35 100644 --- a/drivers/media/platform/ti/omap/omap_vout.c +++ b/drivers/media/platform/ti/omap/omap_vout.c @@ -1300,8 +1300,6 @@ static const struct vb2_ops omap_vout_vb2_ops = { .buf_prepare = omap_vout_vb2_prepare, .start_streaming = omap_vout_vb2_start_streaming, .stop_streaming = omap_vout_vb2_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* Init functions used during driver initialization */ @@ -1721,7 +1719,7 @@ static struct platform_driver omap_vout_driver = { .driver = { .name = VOUT_NAME, }, - .remove_new = omap_vout_remove, + .remove = omap_vout_remove, }; static int __init omap_vout_init(void) diff --git a/drivers/media/platform/ti/omap/omap_voutdef.h b/drivers/media/platform/ti/omap/omap_voutdef.h index b586193341d2..159e5e670d91 100644 --- a/drivers/media/platform/ti/omap/omap_voutdef.h +++ b/drivers/media/platform/ti/omap/omap_voutdef.h @@ -48,7 +48,7 @@ #define VRFB_TX_TIMEOUT 1000 #define VRFB_NUM_BUFS 4 -/* Max buffer size tobe allocated during init */ +/* Max buffer size to be allocated during init */ #define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4) enum dma_channel_state { diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index 1cda23244c7b..405ca215179d 100644 --- a/drivers/media/platform/ti/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c @@ -1965,7 +1965,7 @@ static int isp_attach_iommu(struct isp_device *isp) * Create the ARM mapping, used by the ARM DMA mapping core to allocate * VAs. This will allocate a corresponding IOMMU domain. */ - mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G); + mapping = arm_iommu_create_mapping(isp->dev, SZ_1G, SZ_2G); if (IS_ERR(mapping)) { dev_err(isp->dev, "failed to create ARM IOMMU mapping\n"); return PTR_ERR(mapping); @@ -2472,7 +2472,7 @@ MODULE_DEVICE_TABLE(of, omap3isp_of_table); static struct platform_driver omap3isp_driver = { .probe = isp_probe, - .remove_new = isp_remove, + .remove = isp_remove, .id_table = omap3isp_id_table, .driver = { .name = "omap3isp", diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index daca689dc082..5c9aa80023fd 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -480,11 +480,29 @@ static int isp_video_start_streaming(struct vb2_queue *queue, return 0; } +static void omap3isp_wait_prepare(struct vb2_queue *vq) +{ + struct isp_video_fh *vfh = vb2_get_drv_priv(vq); + struct isp_video *video = vfh->video; + + mutex_unlock(&video->queue_lock); +} + +static void omap3isp_wait_finish(struct vb2_queue *vq) +{ + struct isp_video_fh *vfh = vb2_get_drv_priv(vq); + struct isp_video *video = vfh->video; + + mutex_lock(&video->queue_lock); +} + static const struct vb2_ops isp_video_queue_ops = { .queue_setup = isp_video_queue_setup, .buf_prepare = isp_video_buffer_prepare, .buf_queue = isp_video_buffer_queue, .start_streaming = isp_video_start_streaming, + .wait_prepare = omap3isp_wait_prepare, + .wait_finish = omap3isp_wait_finish, }; /* diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c index f8998a8ad371..da90d7f03f82 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -1173,4 +1173,5 @@ EXPORT_SYMBOL(vpdma_create); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_FIRMWARE(VPDMA_FIRMWARE); +MODULE_DESCRIPTION("TI VPDMA helper library"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index 6848cbc82f52..636d76ecebcd 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -2210,8 +2210,6 @@ static const struct vb2_ops vpe_qops = { .queue_setup = vpe_queue_setup, .buf_prepare = vpe_buf_prepare, .buf_queue = vpe_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vpe_start_streaming, .stop_streaming = vpe_stop_streaming, }; @@ -2649,7 +2647,7 @@ MODULE_DEVICE_TABLE(of, vpe_of_match); static struct platform_driver vpe_pdrv = { .probe = vpe_probe, - .remove_new = vpe_remove, + .remove = vpe_remove, .driver = { .name = VPE_MODULE_NAME, .of_match_table = of_match_ptr(vpe_of_match), diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig index 9a34d14c6e40..3272a24db71d 100644 --- a/drivers/media/platform/verisilicon/Kconfig +++ b/drivers/media/platform/verisilicon/Kconfig @@ -12,6 +12,7 @@ config VIDEO_HANTRO select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select V4L2_H264 + select V4L2_JPEG_HELPER select V4L2_VP9 help Support for the Hantro IP based Video Processing Units present on @@ -20,6 +21,14 @@ config VIDEO_HANTRO To compile this driver as a module, choose M here: the module will be called hantro-vpu. +config VIDEO_HANTRO_HEVC_RFC + bool "Use reference frame compression for HEVC" + depends on VIDEO_HANTRO + default n + help + Enable the reference frame compression feature for the HEVC codec. + It will use more memory but save bandwidth on memory bus. + config VIDEO_HANTRO_IMX8M bool "Hantro VPU i.MX8M support" depends on VIDEO_HANTRO diff --git a/drivers/media/platform/verisilicon/Makefile b/drivers/media/platform/verisilicon/Makefile index eb38a1833b02..f6f019d04ff0 100644 --- a/drivers/media/platform/verisilicon/Makefile +++ b/drivers/media/platform/verisilicon/Makefile @@ -14,13 +14,6 @@ hantro-vpu-y += \ hantro_g2.o \ hantro_g2_hevc_dec.o \ hantro_g2_vp9_dec.o \ - rockchip_vpu2_hw_jpeg_enc.o \ - rockchip_vpu2_hw_h264_dec.o \ - rockchip_vpu2_hw_mpeg2_dec.o \ - rockchip_vpu2_hw_vp8_dec.o \ - rockchip_vpu981_hw_av1_dec.o \ - rockchip_av1_filmgrain.o \ - rockchip_av1_entropymode.o \ hantro_jpeg.o \ hantro_h264.o \ hantro_hevc.o \ @@ -35,6 +28,13 @@ hantro-vpu-$(CONFIG_VIDEO_HANTRO_SAMA5D4) += \ sama5d4_vdec_hw.o hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ + rockchip_vpu2_hw_jpeg_enc.o \ + rockchip_vpu2_hw_h264_dec.o \ + rockchip_vpu2_hw_mpeg2_dec.o \ + rockchip_vpu2_hw_vp8_dec.o \ + rockchip_vpu981_hw_av1_dec.o \ + rockchip_av1_filmgrain.o \ + rockchip_av1_entropymode.o \ rockchip_vpu_hw.o hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \ diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h index 811260dc3c77..edc217eed293 100644 --- a/drivers/media/platform/verisilicon/hantro.h +++ b/drivers/media/platform/verisilicon/hantro.h @@ -227,6 +227,7 @@ struct hantro_dev { * @src_fmt: V4L2 pixel format of active source format. * @vpu_dst_fmt: Descriptor of active destination format. * @dst_fmt: V4L2 pixel format of active destination format. + * @ref_fmt: V4L2 pixel format of the reference frames format. * * @ctrl_handler: Control handler used to register controls. * @jpeg_quality: User-specified JPEG compression quality. @@ -255,6 +256,7 @@ struct hantro_ctx { struct v4l2_pix_format_mplane src_fmt; const struct hantro_fmt *vpu_dst_fmt; struct v4l2_pix_format_mplane dst_fmt; + struct v4l2_pix_format_mplane ref_fmt; struct v4l2_ctrl_handler ctrl_handler; int jpeg_quality; @@ -332,12 +334,19 @@ struct hantro_vp9_decoded_buffer_info { u32 bit_depth : 4; }; +struct hantro_av1_decoded_buffer_info { + /* Info needed when the decoded frame serves as a reference frame. */ + size_t chroma_offset; + size_t mv_offset; +}; + struct hantro_decoded_buffer { /* Must be the first field in this struct. */ struct v4l2_m2m_buffer base; union { struct hantro_vp9_decoded_buffer_info vp9; + struct hantro_av1_decoded_buffer_info av1; }; }; diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index 34b123dafd89..8542238e0fb1 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -722,6 +722,7 @@ static const struct of_device_id of_hantro_match[] = { { .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, }, { .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, }, { .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, }, + { .compatible = "rockchip,rk3588-vepu121", .data = &rk3568_vepu_variant, }, { .compatible = "rockchip,rk3588-av1-vpu", .data = &rk3588_vpu981_variant, }, #endif #ifdef CONFIG_VIDEO_HANTRO_IMX8M @@ -992,6 +993,49 @@ static const struct media_device_ops hantro_m2m_media_ops = { .req_queue = v4l2_m2m_request_queue, }; +/* + * Some SoCs, like RK3588 have multiple identical Hantro cores, but the + * kernel is currently missing support for multi-core handling. Exposing + * separate devices for each core to userspace is bad, since that does + * not allow scheduling tasks properly (and creates ABI). With this workaround + * the driver will only probe for the first core and early exit for the other + * cores. Once the driver gains multi-core support, the same technique + * for detecting the main core can be used to cluster all cores together. + */ +static int hantro_disable_multicore(struct hantro_dev *vpu) +{ + struct device_node *node = NULL; + const char *compatible; + bool is_main_core; + int ret; + + /* Intentionally ignores the fallback strings */ + ret = of_property_read_string(vpu->dev->of_node, "compatible", &compatible); + if (ret) + return ret; + + /* The first compatible and available node found is considered the main core */ + do { + node = of_find_compatible_node(node, NULL, compatible); + if (of_device_is_available(node)) + break; + } while (node); + + if (!node) + return -EINVAL; + + is_main_core = (vpu->dev->of_node == node); + + of_node_put(node); + + if (!is_main_core) { + dev_info(vpu->dev, "missing multi-core support, ignoring this instance\n"); + return -ENODEV; + } + + return 0; +} + static int hantro_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1011,6 +1055,10 @@ static int hantro_probe(struct platform_device *pdev) match = of_match_node(of_hantro_match, pdev->dev.of_node); vpu->variant = match->data; + ret = hantro_disable_multicore(vpu); + if (ret) + return ret; + /* * Support for nxp,imx8mq-vpu is kept for backwards compatibility * but it's deprecated. Please update your DTS file to use @@ -1229,7 +1277,7 @@ static const struct dev_pm_ops hantro_pm_ops = { static struct platform_driver hantro_driver = { .probe = hantro_probe, - .remove_new = hantro_remove, + .remove = hantro_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_hantro_match, diff --git a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c index 9aea331e1a3c..e0d6bd0a6e44 100644 --- a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c @@ -5,7 +5,7 @@ * Copyright (C) 2018 Rockchip Electronics Co., Ltd. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/bitfield.h> #include <media/v4l2-mem2mem.h> #include "hantro.h" diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c index b880a6849d58..aae0b562fabb 100644 --- a/drivers/media/platform/verisilicon/hantro_g2.c +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -47,7 +47,7 @@ irqreturn_t hantro_g2_irq(int irq, void *dev_id) size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx) { - return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8; + return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height; } size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx) @@ -56,3 +56,32 @@ size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx) return ALIGN((cr_offset * 3) / 2, G2_ALIGN); } + +static size_t hantro_g2_mv_size(struct hantro_ctx *ctx) +{ + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + unsigned int pic_width_in_ctbs, pic_height_in_ctbs; + unsigned int max_log2_ctb_size; + + max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 + + sps->log2_diff_max_min_luma_coding_block_size; + pic_width_in_ctbs = (sps->pic_width_in_luma_samples + + (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size; + pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1) + >> max_log2_ctb_size; + + return pic_width_in_ctbs * pic_height_in_ctbs * (1 << (2 * (max_log2_ctb_size - 4))) * 16; +} + +size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx) +{ + return hantro_g2_motion_vectors_offset(ctx) + + hantro_g2_mv_size(ctx); +} + +size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx) +{ + return hantro_g2_luma_compress_offset(ctx) + + hantro_hevc_luma_compressed_size(ctx->dst_fmt.width, ctx->dst_fmt.height); +} diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c index d3f8c33eb16c..85a44143b378 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -367,11 +367,14 @@ static int set_ref(struct hantro_ctx *ctx) const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; dma_addr_t luma_addr, chroma_addr, mv_addr = 0; + dma_addr_t compress_luma_addr, compress_chroma_addr = 0; struct hantro_dev *vpu = ctx->dev; struct vb2_v4l2_buffer *vb2_dst; struct hantro_decoded_buffer *dst; size_t cr_offset = hantro_g2_chroma_offset(ctx); size_t mv_offset = hantro_g2_motion_vectors_offset(ctx); + size_t compress_luma_offset = hantro_g2_luma_compress_offset(ctx); + size_t compress_chroma_offset = hantro_g2_chroma_compress_offset(ctx); u32 max_ref_frames; u16 dpb_longterm_e; static const struct hantro_reg cur_poc[] = { @@ -445,6 +448,8 @@ static int set_ref(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; + compress_luma_addr = luma_addr + compress_luma_offset; + compress_chroma_addr = luma_addr + compress_chroma_offset; if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i); @@ -452,6 +457,8 @@ static int set_ref(struct hantro_ctx *ctx) hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); + hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), compress_luma_addr); + hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i), compress_chroma_addr); } vb2_dst = hantro_get_dst_buf(ctx); @@ -465,19 +472,27 @@ static int set_ref(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; + compress_luma_addr = luma_addr + compress_luma_offset; + compress_chroma_addr = luma_addr + compress_chroma_offset; hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i++), mv_addr); + hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); + hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), compress_luma_addr); + hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i++), compress_chroma_addr); hantro_write_addr(vpu, G2_OUT_LUMA_ADDR, luma_addr); hantro_write_addr(vpu, G2_OUT_CHROMA_ADDR, chroma_addr); hantro_write_addr(vpu, G2_OUT_MV_ADDR, mv_addr); + hantro_write_addr(vpu, G2_OUT_COMP_LUMA_ADDR, compress_luma_addr); + hantro_write_addr(vpu, G2_OUT_COMP_CHROMA_ADDR, compress_chroma_addr); for (; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), 0); hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), 0); hantro_write_addr(vpu, G2_REF_MV_ADDR(i), 0); + hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), 0); + hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i), 0); } hantro_reg_write(vpu, &g2_refer_lterm_e, dpb_longterm_e); @@ -594,8 +609,7 @@ int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx) /* Don't disable output */ hantro_reg_write(vpu, &g2_out_dis, 0); - /* Don't compress buffers */ - hantro_reg_write(vpu, &g2_ref_compress_bypass, 1); + hantro_reg_write(vpu, &g2_ref_compress_bypass, !ctx->hevc_dec.use_compression); /* Bus width and max burst */ hantro_reg_write(vpu, &g2_buswidth, BUS_WIDTH_128); diff --git a/drivers/media/platform/verisilicon/hantro_g2_regs.h b/drivers/media/platform/verisilicon/hantro_g2_regs.h index 82606783591a..b943b1816db7 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_regs.h +++ b/drivers/media/platform/verisilicon/hantro_g2_regs.h @@ -318,6 +318,10 @@ #define G2_TILE_BSD_ADDR (G2_SWREG(183)) #define G2_DS_DST (G2_SWREG(186)) #define G2_DS_DST_CHR (G2_SWREG(188)) +#define G2_OUT_COMP_LUMA_ADDR (G2_SWREG(190)) +#define G2_REF_COMP_LUMA_ADDR(i) (G2_SWREG(192) + ((i) * 0x8)) +#define G2_OUT_COMP_CHROMA_ADDR (G2_SWREG(224)) +#define G2_REF_COMP_CHROMA_ADDR(i) (G2_SWREG(226) + ((i) * 0x8)) #define g2_strm_buffer_len G2_DEC_REG(258, 0, 0xffffffff) #define g2_strm_start_offset G2_DEC_REG(259, 0, 0xffffffff) diff --git a/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c b/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c index 12d69503d6ba..86cc1a07026f 100644 --- a/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c +++ b/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c @@ -5,7 +5,7 @@ * Copyright (C) 2018 Rockchip Electronics Co., Ltd. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <media/v4l2-mem2mem.h> #include "hantro_jpeg.h" #include "hantro.h" diff --git a/drivers/media/platform/verisilicon/hantro_h1_regs.h b/drivers/media/platform/verisilicon/hantro_h1_regs.h index 30e7e7b920b5..8650cc489392 100644 --- a/drivers/media/platform/verisilicon/hantro_h1_regs.h +++ b/drivers/media/platform/verisilicon/hantro_h1_regs.h @@ -62,7 +62,7 @@ #define H1_REG_ENC_CTRL1_INTRA_PRED_MODE(x) ((x) << 16) #define H1_REG_ENC_CTRL1_FRAME_NUM(x) ((x)) #define H1_REG_ENC_CTRL2 0x048 -#define H1_REG_ENC_CTRL2_DEBLOCKING_FILETER_MODE(x) ((x) << 30) +#define H1_REG_ENC_CTRL2_DEBLOCKING_FILTER_MODE(x) ((x) << 30) #define H1_REG_ENC_CTRL2_H264_SLICE_SIZE(x) ((x) << 23) #define H1_REG_ENC_CTRL2_DISABLE_QUARTER_PIXMV BIT(22) #define H1_REG_ENC_CTRL2_TRANS8X8_MODE_EN BIT(21) @@ -89,7 +89,7 @@ #define H1_REG_STR_BUF_LIMIT 0x060 #define H1_REG_MAD_CTRL 0x064 #define H1_REG_MAD_CTRL_QP_ADJUST(x) ((x) << 28) -#define H1_REG_MAD_CTRL_MAD_THREDHOLD(x) ((x) << 22) +#define H1_REG_MAD_CTRL_MAD_THRESHOLD(x) ((x) << 22) #define H1_REG_MAD_CTRL_QP_SUM_DIV2(x) ((x)) #define H1_REG_ADDR_VP8_PROB_CNT 0x068 #define H1_REG_QP_VAL 0x06c diff --git a/drivers/media/platform/verisilicon/hantro_hevc.c b/drivers/media/platform/verisilicon/hantro_hevc.c index 2c14330bc562..83cd12b0ddd6 100644 --- a/drivers/media/platform/verisilicon/hantro_hevc.c +++ b/drivers/media/platform/verisilicon/hantro_hevc.c @@ -25,6 +25,11 @@ #define MAX_TILE_COLS 20 #define MAX_TILE_ROWS 22 +static bool hevc_use_compression = IS_ENABLED(CONFIG_VIDEO_HANTRO_HEVC_RFC); +module_param_named(hevc_use_compression, hevc_use_compression, bool, 0644); +MODULE_PARM_DESC(hevc_use_compression, + "Use reference frame compression for HEVC"); + void hantro_hevc_ref_init(struct hantro_ctx *ctx) { struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; @@ -275,5 +280,8 @@ int hantro_hevc_dec_init(struct hantro_ctx *ctx) hantro_hevc_ref_init(ctx); + hevc_dec->use_compression = + hevc_use_compression & hantro_needs_postproc(ctx, ctx->vpu_dst_fmt); + return 0; } diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 7737320cc8cc..c9b6556f8b2b 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -42,6 +42,13 @@ #define MAX_POSTPROC_BUFFERS 64 +#define CBS_SIZE 16 /* compression table size in bytes */ +#define CBS_LUMA 8 /* luminance CBS is composed of 1 8x8 coded block */ +#define CBS_CHROMA_W (8 * 2) /* chrominance CBS is composed of two 8x4 coded + * blocks, with Cb CB first then Cr CB following + */ +#define CBS_CHROMA_H 4 + struct hantro_dev; struct hantro_ctx; struct hantro_buf; @@ -144,6 +151,7 @@ struct hantro_hevc_dec_ctrls { * @ref_bufs_used: Bitfield of used reference buffers * @ctrls: V4L2 controls attached to a run * @num_tile_cols_allocated: number of allocated tiles + * @use_compression: use reference buffer compression */ struct hantro_hevc_dec_hw_ctx { struct hantro_aux_buf tile_sizes; @@ -156,6 +164,7 @@ struct hantro_hevc_dec_hw_ctx { u32 ref_bufs_used; struct hantro_hevc_dec_ctrls ctrls; unsigned int num_tile_cols_allocated; + bool use_compression; }; /** @@ -510,6 +519,33 @@ hantro_hevc_mv_size(unsigned int width, unsigned int height) return width * height / 16; } +static inline size_t +hantro_hevc_luma_compressed_size(unsigned int width, unsigned int height) +{ + u32 pic_width_in_cbsy = + round_up((width + CBS_LUMA - 1) / CBS_LUMA, CBS_SIZE); + u32 pic_height_in_cbsy = (height + CBS_LUMA - 1) / CBS_LUMA; + + return round_up(pic_width_in_cbsy * pic_height_in_cbsy, CBS_SIZE); +} + +static inline size_t +hantro_hevc_chroma_compressed_size(unsigned int width, unsigned int height) +{ + u32 pic_width_in_cbsc = + round_up((width + CBS_CHROMA_W - 1) / CBS_CHROMA_W, CBS_SIZE); + u32 pic_height_in_cbsc = (height / 2 + CBS_CHROMA_H - 1) / CBS_CHROMA_H; + + return round_up(pic_width_in_cbsc * pic_height_in_cbsc, CBS_SIZE); +} + +static inline size_t +hantro_hevc_compressed_size(unsigned int width, unsigned int height) +{ + return hantro_hevc_luma_compressed_size(width, height) + + hantro_hevc_chroma_compressed_size(width, height); +} + static inline unsigned short hantro_av1_num_sbs(unsigned short dimension) { return DIV_ROUND_UP(dimension, 64); @@ -525,6 +561,8 @@ hantro_av1_mv_size(unsigned int width, unsigned int height) size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx); size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx); +size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx); +size_t hantro_g2_chroma_compress_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); diff --git a/drivers/media/platform/verisilicon/hantro_jpeg.c b/drivers/media/platform/verisilicon/hantro_jpeg.c index d07b1b449b61..13a60638e43f 100644 --- a/drivers/media/platform/verisilicon/hantro_jpeg.c +++ b/drivers/media/platform/verisilicon/hantro_jpeg.c @@ -11,6 +11,7 @@ #include <linux/build_bug.h> #include <linux/kernel.h> #include <linux/string.h> +#include <media/v4l2-jpeg.h> #include "hantro_jpeg.h" #include "hantro.h" @@ -24,42 +25,6 @@ #define HUFF_CHROMA_DC_OFF 394 #define HUFF_CHROMA_AC_OFF 427 -/* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 - */ -static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, - 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, - 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, - 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, - 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, - 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, - 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 -}; - -static const unsigned char chroma_q_table[] = { - 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, - 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, - 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const unsigned char zigzag[] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - static const u32 hw_reorder[] = { 0, 8, 16, 24, 1, 9, 17, 25, 32, 40, 48, 56, 33, 41, 49, 57, @@ -71,73 +36,6 @@ static const u32 hw_reorder[] = { 38, 46, 54, 62, 39, 47, 55, 63 }; -/* Huffman tables are shared with CODA */ -static const unsigned char luma_dc_table[] = { - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc_table[] = { - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac_table[] = { - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -static const unsigned char chroma_ac_table[] = { - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - /* For simplicity, we keep a pre-formatted JPEG header, * and we'll use fixed offsets to change the width, height * quantization tables, etc. @@ -292,11 +190,11 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, { int i; - BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_zigzag_scan_index) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); for (i = 0; i < JPEG_QUANT_SIZE; i++) { - file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + file_q_tab[i] = jpeg_scale_qp(tab[v4l2_jpeg_zigzag_scan_index[i]], scale); reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } } @@ -304,7 +202,6 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) { int scale; - /* * Non-linear scaling factor: * [5,50] -> [1000..100], [51,100] -> [98..0] @@ -314,15 +211,17 @@ static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) else scale = 200 - 2 * ctx->quality; - BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_luma_qt) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_qt) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, - ctx->hw_luma_qtable, luma_q_table, scale); + ctx->hw_luma_qtable, + (const unsigned char *)v4l2_jpeg_ref_table_luma_qt, scale); jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, - ctx->hw_chroma_qtable, chroma_q_table, scale); + ctx->hw_chroma_qtable, + (const unsigned char *)v4l2_jpeg_ref_table_chroma_qt, scale); } void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) @@ -337,12 +236,10 @@ void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) buf[WIDTH_OFF + 0] = ctx->width >> 8; buf[WIDTH_OFF + 1] = ctx->width; - memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table)); - memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table)); - memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table, - sizeof(chroma_dc_table)); - memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table, - sizeof(chroma_ac_table)); + memcpy(buf + HUFF_LUMA_DC_OFF, v4l2_jpeg_ref_table_luma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN); + memcpy(buf + HUFF_LUMA_AC_OFF, v4l2_jpeg_ref_table_luma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN); + memcpy(buf + HUFF_CHROMA_DC_OFF, v4l2_jpeg_ref_table_chroma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN); + memcpy(buf + HUFF_CHROMA_AC_OFF, v4l2_jpeg_ref_table_chroma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN); jpeg_set_quality(ctx); } diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 41e93176300b..c435a393e0cb 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -194,31 +194,25 @@ void hantro_postproc_free(struct hantro_ctx *ctx) static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx) { - struct v4l2_pix_format_mplane pix_mp; - const struct hantro_fmt *fmt; 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 0; - - v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, - ctx->src_fmt.height); - - buf_size = pix_mp.plane_fmt[0].sizeimage; + buf_size = ctx->ref_fmt.plane_fmt[0].sizeimage; if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) - buf_size += hantro_h264_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_h264_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME) - buf_size += hantro_vp9_mv_size(pix_mp.width, - pix_mp.height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) - buf_size += hantro_hevc_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_vp9_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); + else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) { + buf_size += hantro_hevc_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); + if (ctx->hevc_dec.use_compression) + buf_size += hantro_hevc_compressed_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); + } else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_AV1_FRAME) - buf_size += hantro_av1_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_av1_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); return buf_size; } diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index 941fa23c211a..2bce940a5822 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -126,6 +126,24 @@ hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc) return NULL; } +static int +hantro_set_reference_frames_format(struct hantro_ctx *ctx) +{ + const struct hantro_fmt *fmt; + int dst_bit_depth = hantro_get_format_depth(ctx->vpu_dst_fmt->fourcc); + + fmt = hantro_get_default_fmt(ctx, false, dst_bit_depth, HANTRO_AUTO_POSTPROC); + if (!fmt) + return -EINVAL; + + ctx->ref_fmt.width = ctx->src_fmt.width; + ctx->ref_fmt.height = ctx->src_fmt.height; + + v4l2_apply_frmsize_constraints(&ctx->ref_fmt.width, &ctx->ref_fmt.height, &fmt->frmsize); + return v4l2_fill_pixfmt_mp(&ctx->ref_fmt, fmt->fourcc, + ctx->ref_fmt.width, ctx->ref_fmt.height); +} + const struct hantro_fmt * hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream, int bit_depth, bool need_postproc) @@ -201,7 +219,15 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct hantro_ctx *ctx = fh_to_ctx(priv); const struct hantro_fmt *fmt, *formats; unsigned int num_fmts, i, j = 0; - bool skip_mode_none; + bool skip_mode_none, enum_all_formats; + u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL; + + /* + * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all + * hardware supported pixel formats + */ + enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL); + f->index = index; /* * When dealing with an encoder: @@ -222,9 +248,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, if (skip_mode_none == mode_none) continue; - if (!hantro_check_depth_match(fmt, ctx->bit_depth)) + if (!hantro_check_depth_match(fmt, ctx->bit_depth) && !enum_all_formats) continue; - if (j == f->index) { + if (j == index) { f->pixelformat = fmt->fourcc; return 0; } @@ -242,9 +268,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, for (i = 0; i < num_fmts; i++) { fmt = &formats[i]; - if (!hantro_check_depth_match(fmt, ctx->bit_depth)) + if (!hantro_check_depth_match(fmt, ctx->bit_depth) && !enum_all_formats) continue; - if (j == f->index) { + if (j == index) { f->pixelformat = fmt->fourcc; return 0; } @@ -303,11 +329,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, coded = capture == ctx->is_encoder; - vpu_debug(4, "trying format %c%c%c%c\n", - (pix_mp->pixelformat & 0x7f), - (pix_mp->pixelformat >> 8) & 0x7f, - (pix_mp->pixelformat >> 16) & 0x7f, - (pix_mp->pixelformat >> 24) & 0x7f); + vpu_debug(4, "trying format %p4cc\n", &pix_mp->pixelformat); fmt = hantro_find_format(ctx, pix_mp->pixelformat); if (!fmt) { @@ -591,6 +613,9 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, ctx->vpu_dst_fmt = hantro_find_format(ctx, pix_mp->pixelformat); ctx->dst_fmt = *pix_mp; + ret = hantro_set_reference_frames_format(ctx); + if (ret) + return ret; /* * Current raw format might have become invalid with newly @@ -756,6 +781,7 @@ const struct v4l2_ioctl_ops hantro_ioctl_ops = { .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, @@ -999,6 +1025,4 @@ const struct vb2_ops hantro_queue_ops = { .buf_request_complete = hantro_buf_request_complete, .start_streaming = hantro_start_streaming, .stop_streaming = hantro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c index f850d8bddef6..35799da534ed 100644 --- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c +++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c @@ -187,23 +187,23 @@ static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = { .frmsize = { .min_width = FMT_MIN_WIDTH, .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, + .step_width = 8, .min_height = FMT_MIN_HEIGHT, .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, + .step_height = 32, }, }, { - .fourcc = V4L2_PIX_FMT_P010_4L4, + .fourcc = V4L2_PIX_FMT_NV15_4L4, .codec_mode = HANTRO_MODE_NONE, .match_depth = true, .frmsize = { .min_width = FMT_MIN_WIDTH, .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, + .step_width = 8, .min_height = FMT_MIN_HEIGHT, .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, + .step_height = 32, }, }, { diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c index 8395c4d48dd0..61621b1be8a2 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c @@ -22,7 +22,7 @@ * zigzag, nor linear. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <media/v4l2-mem2mem.h> #include "hantro_jpeg.h" #include "hantro.h" diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c index b66737fab46b..50a3a3eeaa00 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c @@ -5,7 +5,7 @@ * Copyright (C) 2018 Rockchip Electronics Co., Ltd. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/bitfield.h> #include <media/v4l2-mem2mem.h> #include "hantro.h" diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c index cc4483857489..69b5d9e12926 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c @@ -161,8 +161,7 @@ static int rockchip_vpu981_av1_dec_frame_ref(struct hantro_ctx *ctx, av1_dec->frame_refs[i].timestamp = timestamp; av1_dec->frame_refs[i].frame_type = frame->frame_type; av1_dec->frame_refs[i].order_hint = frame->order_hint; - if (!av1_dec->frame_refs[i].vb2_ref) - av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx); + av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx); for (j = 0; j < V4L2_AV1_TOTAL_REFS_PER_FRAME; j++) av1_dec->frame_refs[i].order_hints[j] = frame->order_hints[j]; @@ -257,7 +256,8 @@ static int rockchip_vpu981_av1_dec_tiles_reallocate(struct hantro_ctx *ctx) struct hantro_dev *vpu = ctx->dev; struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec; struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls; - unsigned int num_tile_cols = 1 << ctrls->tile_group_entry->tile_col; + const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info; + unsigned int num_tile_cols = tile_info->tile_cols; unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64); unsigned int height_in_sb = height / 64; unsigned int stripe_num = ((height + 8) + 63) / 64; @@ -686,8 +686,6 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx, struct hantro_dev *vpu = ctx->dev; struct hantro_decoded_buffer *dst; dma_addr_t luma_addr, chroma_addr, mv_addr = 0; - size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx); - size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx); int cur_width = frame->frame_width_minus_1 + 1; int cur_height = frame->frame_height_minus_1 + 1; int scale_width = @@ -744,8 +742,8 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx, dst = vb2_to_hantro_decoded_buf(&av1_dec->frame_refs[idx].vb2_ref->vb2_buf); luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); - chroma_addr = luma_addr + cr_offset; - mv_addr = luma_addr + mv_offset; + chroma_addr = luma_addr + dst->av1.chroma_offset; + mv_addr = luma_addr + dst->av1.mv_offset; hantro_write_addr(vpu, AV1_REFERENCE_Y(ref), luma_addr); hantro_write_addr(vpu, AV1_REFERENCE_CB(ref), chroma_addr); @@ -2089,6 +2087,9 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; + dst->av1.chroma_offset = cr_offset; + dst->av1.mv_offset = mv_offset; + hantro_write_addr(vpu, AV1_TILE_OUT_LU, luma_addr); hantro_write_addr(vpu, AV1_TILE_OUT_CH, chroma_addr); hantro_write_addr(vpu, AV1_TILE_OUT_MV, mv_addr); diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h b/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h index 850ff0f84424..e4008da64f19 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h @@ -327,7 +327,7 @@ #define av1_apf_threshold AV1_DEC_REG(55, 0, 0xffff) #define av1_apf_single_pu_mode AV1_DEC_REG(55, 30, 0x1) -#define av1_apf_disable AV1_DEC_REG(55, 30, 0x1) +#define av1_apf_disable AV1_DEC_REG(55, 31, 0x1) #define av1_dec_max_burst AV1_DEC_REG(58, 0, 0xff) #define av1_dec_buswidth AV1_DEC_REG(58, 8, 0x7) @@ -337,10 +337,10 @@ #define av1_dec_mc_polltime AV1_DEC_REG(58, 17, 0x3ff) #define av1_dec_mc_pollmode AV1_DEC_REG(58, 27, 0x3) -#define av1_filt_ref_adj_3 AV1_DEC_REG(59, 0, 0x3f) -#define av1_filt_ref_adj_2 AV1_DEC_REG(59, 7, 0x3f) -#define av1_filt_ref_adj_1 AV1_DEC_REG(59, 14, 0x3f) -#define av1_filt_ref_adj_0 AV1_DEC_REG(59, 21, 0x3f) +#define av1_filt_ref_adj_3 AV1_DEC_REG(59, 0, 0x7f) +#define av1_filt_ref_adj_2 AV1_DEC_REG(59, 7, 0x7f) +#define av1_filt_ref_adj_1 AV1_DEC_REG(59, 14, 0x7f) +#define av1_filt_ref_adj_0 AV1_DEC_REG(59, 21, 0x7f) #define av1_ref0_sign_bias AV1_DEC_REG(59, 28, 0x1) #define av1_ref1_sign_bias AV1_DEC_REG(59, 29, 0x1) #define av1_ref2_sign_bias AV1_DEC_REG(59, 30, 0x1) diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c index f97527670783..964122e7c355 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c @@ -82,7 +82,6 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV12, .codec_mode = HANTRO_MODE_NONE, - .match_depth = true, .postprocessed = true, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, diff --git a/drivers/media/platform/via/via-camera.c b/drivers/media/platform/via/via-camera.c index 4cb8f29e2f14..5702eff664d4 100644 --- a/drivers/media/platform/via/via-camera.c +++ b/drivers/media/platform/via/via-camera.c @@ -666,8 +666,6 @@ static const struct vb2_ops viacam_vb2_ops = { .buf_prepare = viacam_vb2_prepare, .start_streaming = viacam_vb2_start_streaming, .stop_streaming = viacam_vb2_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* --------------------------------------------------------------------------*/ @@ -1307,7 +1305,7 @@ static struct platform_driver viacam_driver = { .name = "viafb-camera", }, .probe = viacam_probe, - .remove_new = viacam_remove, + .remove = viacam_remove, }; module_platform_driver(viacam_driver); diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 31e9e92e723e..cba34893258a 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -52,6 +52,7 @@ static int video_mux_link_setup(struct media_entity *entity, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct v4l2_subdev_state *sd_state; struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); u16 source_pad = entity->num_pads - 1; int ret = 0; @@ -67,10 +68,10 @@ static int video_mux_link_setup(struct media_entity *entity, remote->entity->name, remote->index, local->entity->name, local->index, flags & MEDIA_LNK_FL_ENABLED); + sd_state = v4l2_subdev_lock_and_get_active_state(sd); mutex_lock(&vmux->lock); if (flags & MEDIA_LNK_FL_ENABLED) { - struct v4l2_subdev_state *sd_state; struct v4l2_mbus_framefmt *source_mbusformat; if (vmux->active == local->index) @@ -88,12 +89,10 @@ static int video_mux_link_setup(struct media_entity *entity, vmux->active = local->index; /* Propagate the active format to the source */ - sd_state = v4l2_subdev_lock_and_get_active_state(sd); 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) goto out; @@ -105,6 +104,7 @@ static int video_mux_link_setup(struct media_entity *entity, out: mutex_unlock(&vmux->lock); + v4l2_subdev_unlock_state(sd_state); return ret; } @@ -486,7 +486,7 @@ MODULE_DEVICE_TABLE(of, video_mux_dt_ids); static struct platform_driver video_mux_driver = { .probe = video_mux_probe, - .remove_new = video_mux_remove, + .remove = video_mux_remove, .driver = { .of_match_table = video_mux_dt_ids, .name = "video-mux", diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index f953d5474ae0..146131b8f37e 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -1028,7 +1028,7 @@ static struct platform_driver xcsi2rxss_driver = { .of_match_table = xcsi2rxss_of_id_table, }, .probe = xcsi2rxss_probe, - .remove_new = xcsi2rxss_remove, + .remove = xcsi2rxss_remove, }; module_platform_driver(xcsi2rxss_driver); diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index a96de5d388a1..18bfa6001909 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -348,8 +348,8 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) } dma->xt.frame_size = 1; - dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; - dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; + dma->sgl.size = dma->format.width * dma->fmtinfo->bpp; + dma->sgl.icg = dma->format.bytesperline - dma->sgl.size; dma->xt.numf = dma->format.height; desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); @@ -458,8 +458,6 @@ static const struct vb2_ops xvip_dma_queue_qops = { .queue_setup = xvip_dma_queue_setup, .buf_prepare = xvip_dma_buffer_prepare, .buf_queue = xvip_dma_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = xvip_dma_start_streaming, .stop_streaming = xvip_dma_stop_streaming, }; diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h index 9c6d4c18d1a9..18f77e1a7b39 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.h +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -97,7 +97,7 @@ struct xvip_dma { struct dma_chan *dma; unsigned int align; struct dma_interleaved_template xt; - struct data_chunk sgl[1]; + struct data_chunk sgl; }; #define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video) diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index e05e528ffc6f..cb93711ea3e3 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -13,6 +13,7 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/xilinx-v4l2-controls.h> @@ -711,22 +712,13 @@ static int xtpg_parse_of(struct xtpg_device *xtpg) { struct device *dev = xtpg->xvip.dev; struct device_node *node = xtpg->xvip.dev->of_node; - struct device_node *ports; - struct device_node *port; unsigned int nports = 0; bool has_endpoint = false; - ports = of_get_child_by_name(node, "ports"); - if (ports == NULL) - ports = node; - - for_each_child_of_node(ports, port) { + for_each_of_graph_port(node, port) { const struct xvip_video_format *format; struct device_node *endpoint; - if (!of_node_name_eq(port, "port")) - continue; - format = xvip_of_get_format(port); if (IS_ERR(format)) { dev_err(dev, "invalid format in DT"); @@ -744,7 +736,7 @@ static int xtpg_parse_of(struct xtpg_device *xtpg) } if (nports == 0) { - endpoint = of_get_next_child(port, NULL); + endpoint = of_graph_get_next_port_endpoint(port, NULL); if (endpoint) has_endpoint = true; of_node_put(endpoint); @@ -920,7 +912,7 @@ static struct platform_driver xtpg_driver = { .of_match_table = xtpg_of_id_table, }, .probe = xtpg_probe, - .remove_new = xtpg_remove, + .remove = xtpg_remove, }; module_platform_driver(xtpg_driver); diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index 996684a73038..024b439feec9 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -199,18 +199,13 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) struct media_pad *sink_pad; struct xvip_graph_entity *ent; struct v4l2_fwnode_link link; - struct device_node *ep = NULL; + struct device_node *ep; struct xvip_dma *dma; int ret = 0; dev_dbg(xdev->dev, "creating links for DMA engines\n"); - while (1) { - /* Get the next endpoint and parse its link. */ - ep = of_graph_get_next_endpoint(node, ep); - if (ep == NULL) - break; - + for_each_endpoint_of_node(node, ep) { dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); @@ -623,7 +618,7 @@ static struct platform_driver xvip_composite_driver = { .of_match_table = xvip_composite_of_id_table, }, .probe = xvip_composite_probe, - .remove_new = xvip_composite_remove, + .remove = xvip_composite_remove, }; module_platform_driver(xvip_composite_driver); diff --git a/drivers/media/platform/xilinx/xilinx-vtc.c b/drivers/media/platform/xilinx/xilinx-vtc.c index dda70719f004..92fec7bb47da 100644 --- a/drivers/media/platform/xilinx/xilinx-vtc.c +++ b/drivers/media/platform/xilinx/xilinx-vtc.c @@ -365,7 +365,7 @@ static struct platform_driver xvtc_driver = { .of_match_table = xvtc_of_id_table, }, .probe = xvtc_probe, - .remove_new = xvtc_remove, + .remove = xvtc_remove, }; module_platform_driver(xvtc_driver); |