diff options
Diffstat (limited to 'drivers/media/platform')
170 files changed, 8873 insertions, 1873 deletions
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 2423714afcb9..ec03e17727d7 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -3919,7 +3919,7 @@ static int allegro_probe(struct platform_device *pdev) return 0; } -static int allegro_remove(struct platform_device *pdev) +static void allegro_remove(struct platform_device *pdev) { struct allegro_dev *dev = platform_get_drvdata(pdev); @@ -3935,8 +3935,6 @@ static int allegro_remove(struct platform_device *pdev) pm_runtime_disable(&dev->plat_dev->dev); v4l2_device_unregister(&dev->v4l2_dev); - - return 0; } static int allegro_runtime_resume(struct device *device) @@ -4006,7 +4004,7 @@ static const struct dev_pm_ops allegro_pm_ops = { static struct platform_driver allegro_driver = { .probe = allegro_probe, - .remove = allegro_remove, + .remove_new = allegro_remove, .driver = { .name = "allegro", .of_match_table = of_match_ptr(allegro_dt_ids), diff --git a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c index 142d421a8d76..09409908ba5d 100644 --- a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c +++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c @@ -1024,7 +1024,7 @@ disable_clks: return ret; } -static int ge2d_remove(struct platform_device *pdev) +static void ge2d_remove(struct platform_device *pdev) { struct meson_ge2d *ge2d = platform_get_drvdata(pdev); @@ -1032,8 +1032,6 @@ static int ge2d_remove(struct platform_device *pdev) v4l2_m2m_release(ge2d->m2m_dev); v4l2_device_unregister(&ge2d->v4l2_dev); clk_disable_unprepare(ge2d->clk); - - return 0; } static const struct of_device_id meson_ge2d_match[] = { @@ -1047,7 +1045,7 @@ MODULE_DEVICE_TABLE(of, meson_ge2d_match); static struct platform_driver ge2d_drv = { .probe = ge2d_probe, - .remove = ge2d_remove, + .remove_new = 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 87f9f8e90ab1..3fa1a74a2e20 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -165,10 +165,55 @@ static const struct vpu_format vdec_formats[] = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED }, + { + .pixfmt = V4L2_PIX_FMT_SPK, + .mem_planes = 1, + .comp_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED + }, + { + .pixfmt = V4L2_PIX_FMT_RV30, + .mem_planes = 1, + .comp_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED + }, + { + .pixfmt = V4L2_PIX_FMT_RV40, + .mem_planes = 1, + .comp_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED + }, {0, 0, 0, 0}, }; +static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_inst *inst = ctrl_to_inst(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; + break; + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: + vdec->params.display_delay = ctrl->val; + break; + default: + ret = -EINVAL; + break; + } + vpu_inst_unlock(inst); + + return ret; +} + static const struct v4l2_ctrl_ops vdec_ctrl_ops = { + .s_ctrl = vdec_op_s_ctrl, .g_volatile_ctrl = vpu_helper_g_volatile_ctrl, }; @@ -181,6 +226,14 @@ static int vdec_ctrl_init(struct vpu_inst *inst) if (ret) return ret; + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY, + 0, 0, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE, + 0, 1, 1, 0); + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2); if (ctrl) diff --git a/drivers/media/platform/amphion/vpu_codec.h b/drivers/media/platform/amphion/vpu_codec.h index 528a93f08ecd..bac6d0d94f8a 100644 --- a/drivers/media/platform/amphion/vpu_codec.h +++ b/drivers/media/platform/amphion/vpu_codec.h @@ -55,7 +55,8 @@ struct vpu_encode_params { struct vpu_decode_params { u32 codec_format; u32 output_format; - u32 b_dis_reorder; + u32 display_delay_enable; + u32 display_delay; u32 b_non_frame; u32 frame_count; u32 end_flag; diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index f9ec1753f7c8..de23627a119a 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -709,7 +709,7 @@ err_runtime_disable: return ret; } -static int vpu_core_remove(struct platform_device *pdev) +static void vpu_core_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct vpu_core *core = platform_get_drvdata(pdev); @@ -728,8 +728,6 @@ static int vpu_core_remove(struct platform_device *pdev) memunmap(core->rpc.virt); mutex_destroy(&core->lock); mutex_destroy(&core->cmd_lock); - - return 0; } static int __maybe_unused vpu_core_runtime_resume(struct device *dev) @@ -864,7 +862,7 @@ MODULE_DEVICE_TABLE(of, vpu_core_dt_match); static struct platform_driver amphion_vpu_core_driver = { .probe = vpu_core_probe, - .remove = vpu_core_remove, + .remove_new = vpu_core_remove, .driver = { .name = "amphion-vpu-core", .of_match_table = vpu_core_dt_match, diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c index f01ce49d27e8..4187b2b5562f 100644 --- a/drivers/media/platform/amphion/vpu_drv.c +++ b/drivers/media/platform/amphion/vpu_drv.c @@ -157,7 +157,7 @@ err_vpu_deinit: return ret; } -static int vpu_remove(struct platform_device *pdev) +static void vpu_remove(struct platform_device *pdev) { struct vpu_dev *vpu = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -173,8 +173,6 @@ static int vpu_remove(struct platform_device *pdev) media_device_cleanup(&vpu->mdev); v4l2_device_unregister(&vpu->v4l2_dev); mutex_destroy(&vpu->lock); - - return 0; } static int __maybe_unused vpu_runtime_resume(struct device *dev) @@ -229,7 +227,7 @@ MODULE_DEVICE_TABLE(of, vpu_dt_match); static struct platform_driver amphion_vpu_driver = { .probe = vpu_probe, - .remove = vpu_remove, + .remove_new = 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 2c9bfc6a5a72..ef44bff9fbaf 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -68,6 +68,8 @@ #define STREAM_CONFIG_NUM_DBE_SET(x, y) CONFIG_SET(x, y, 30, 0x40000000) #define STREAM_CONFIG_FS_CTRL_MODE_SET(x, y) CONFIG_SET(x, y, 31, 0x80000000) +#define MALONE_DEC_FMT_RV_MASK BIT(21) + enum vpu_malone_stream_input_mode { INVALID_MODE = 0, FRAME_LVL, @@ -478,6 +480,9 @@ u32 vpu_malone_get_version(struct vpu_shared_addr *shared) { struct malone_iface *iface = shared->iface; + vpu_malone_enable_format(V4L2_PIX_FMT_RV30, iface->fw_version & MALONE_DEC_FMT_RV_MASK); + vpu_malone_enable_format(V4L2_PIX_FMT_RV40, iface->fw_version & MALONE_DEC_FMT_RV_MASK); + return iface->fw_version; } @@ -562,8 +567,23 @@ static struct malone_fmt_mapping fmt_mappings[] = { {V4L2_PIX_FMT_H263, MALONE_FMT_ASP}, {V4L2_PIX_FMT_JPEG, MALONE_FMT_JPG}, {V4L2_PIX_FMT_VP8, MALONE_FMT_VP8}, + {V4L2_PIX_FMT_SPK, MALONE_FMT_SPK}, + {V4L2_PIX_FMT_RV30, MALONE_FMT_RV}, + {V4L2_PIX_FMT_RV40, MALONE_FMT_RV}, }; +void vpu_malone_enable_format(u32 pixelformat, int enable) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(fmt_mappings); i++) { + if (pixelformat == fmt_mappings[i].pixelformat) { + fmt_mappings[i].is_disabled = enable ? 0 : 1; + return; + } + } +} + static enum vpu_malone_format vpu_malone_format_remap(u32 pixelformat) { u32 i; @@ -641,7 +661,9 @@ static int vpu_malone_set_params(struct vpu_shared_addr *shared, hc->jpg[instance].jpg_mjpeg_interlaced = 0; } - hc->codec_param[instance].disp_imm = params->b_dis_reorder ? 1 : 0; + hc->codec_param[instance].disp_imm = params->display_delay_enable ? 1 : 0; + if (malone_format != MALONE_FMT_AVC) + hc->codec_param[instance].disp_imm = 0; hc->codec_param[instance].dbglog_enable = 0; iface->dbglog_desc.level = 0; @@ -987,6 +1009,9 @@ static const struct malone_padding_scode padding_scodes[] = { {SCODE_PADDING_EOS, V4L2_PIX_FMT_XVID, {0xb1010000, 0x0}}, {SCODE_PADDING_EOS, V4L2_PIX_FMT_H263, {0xb1010000, 0x0}}, {SCODE_PADDING_EOS, V4L2_PIX_FMT_VP8, {0x34010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_SPK, {0x34010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_RV30, {0x34010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_RV40, {0x34010000, 0x0}}, {SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0xefff0000, 0x0}}, {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H264, {0x0B010000, 0}}, {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H264_MVC, {0x0B010000, 0}}, @@ -998,6 +1023,9 @@ static const struct malone_padding_scode padding_scodes[] = { {SCODE_PADDING_ABORT, V4L2_PIX_FMT_XVID, {0xb1010000, 0x0}}, {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H263, {0xb1010000, 0x0}}, {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VP8, {0x34010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_SPK, {0x34010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_RV30, {0x34010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_RV40, {0x34010000, 0x0}}, {SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0x0, 0x0}}, {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264, {0x15010000, 0x0}}, {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264_MVC, {0x15010000, 0x0}}, @@ -1411,6 +1439,16 @@ static int vpu_malone_insert_scode_vp8_pic(struct malone_scode_t *scode) return size; } +static int vpu_malone_insert_scode_spk_seq(struct malone_scode_t *scode) +{ + return vpu_malone_insert_scode_seq(scode, MALONE_CODEC_ID_SPK, 0); +} + +static int vpu_malone_insert_scode_spk_pic(struct malone_scode_t *scode) +{ + return vpu_malone_insert_scode_pic(scode, MALONE_CODEC_ID_SPK, 0); +} + static const struct malone_scode_handler scode_handlers[] = { { /* fix me, need to swap return operation after gstreamer swap */ @@ -1427,6 +1465,11 @@ static const struct malone_scode_handler scode_handlers[] = { .insert_scode_seq = vpu_malone_insert_scode_vp8_seq, .insert_scode_pic = vpu_malone_insert_scode_vp8_pic, }, + { + .pixelformat = V4L2_PIX_FMT_SPK, + .insert_scode_seq = vpu_malone_insert_scode_spk_seq, + .insert_scode_pic = vpu_malone_insert_scode_spk_pic, + }, }; static const struct malone_scode_handler *get_scode_handler(u32 pixelformat) diff --git a/drivers/media/platform/amphion/vpu_malone.h b/drivers/media/platform/amphion/vpu_malone.h index 02a9d9530970..c95b53629199 100644 --- a/drivers/media/platform/amphion/vpu_malone.h +++ b/drivers/media/platform/amphion/vpu_malone.h @@ -41,5 +41,6 @@ int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance); int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance); u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared); bool vpu_malone_check_fmt(enum vpu_core_type type, u32 pixelfmt); +void vpu_malone_enable_format(u32 pixelformat, int enable); #endif diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 794d4dc3a654..374eb7781936 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2206,7 +2206,7 @@ static int aspeed_video_probe(struct platform_device *pdev) return 0; } -static int aspeed_video_remove(struct platform_device *pdev) +static void aspeed_video_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); @@ -2228,8 +2228,6 @@ static int aspeed_video_remove(struct platform_device *pdev) aspeed_video_free_buf(video, &video->jpeg); of_reserved_mem_device_release(dev); - - return 0; } static struct platform_driver aspeed_video_driver = { @@ -2238,7 +2236,7 @@ static struct platform_driver aspeed_video_driver = { .of_match_table = aspeed_video_of_match, }, .probe = aspeed_video_probe, - .remove = aspeed_video_remove, + .remove_new = 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 4d15814e4481..c29e04864445 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -587,8 +587,8 @@ static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, struct v4l2_pix_format *pixfmt = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg = {}; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; + .pads = &pad_cfg, + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1317,7 +1317,7 @@ err_vdev_alloc: return ret; } -static int atmel_isi_remove(struct platform_device *pdev) +static void atmel_isi_remove(struct platform_device *pdev) { struct atmel_isi *isi = platform_get_drvdata(pdev); @@ -1329,8 +1329,6 @@ static int atmel_isi_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&isi->notifier); v4l2_async_nf_cleanup(&isi->notifier); v4l2_device_unregister(&isi->v4l2_dev); - - return 0; } #ifdef CONFIG_PM @@ -1368,7 +1366,7 @@ static struct platform_driver atmel_isi_driver = { .pm = &atmel_isi_dev_pm_ops, }, .probe = atmel_isi_probe, - .remove = atmel_isi_remove, + .remove_new = atmel_isi_remove, }; module_platform_driver(atmel_isi_driver); diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index cc3ebb0d96f6..9755d1c8ceb9 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -473,14 +473,12 @@ err_free_priv: return ret; } -static int csi2rx_remove(struct platform_device *pdev) +static void csi2rx_remove(struct platform_device *pdev) { struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); v4l2_async_unregister_subdev(&csi2rx->subdev); kfree(csi2rx); - - return 0; } static const struct of_device_id csi2rx_of_table[] = { @@ -491,7 +489,7 @@ MODULE_DEVICE_TABLE(of, csi2rx_of_table); static struct platform_driver csi2rx_driver = { .probe = csi2rx_probe, - .remove = csi2rx_remove, + .remove_new = 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 58e405b69f67..1e0400b7803e 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -635,19 +635,17 @@ err_free_priv: return ret; } -static int csi2tx_remove(struct platform_device *pdev) +static void csi2tx_remove(struct platform_device *pdev) { struct csi2tx_priv *csi2tx = platform_get_drvdata(pdev); v4l2_async_unregister_subdev(&csi2tx->subdev); kfree(csi2tx); - - return 0; } static struct platform_driver csi2tx_driver = { .probe = csi2tx_probe, - .remove = csi2tx_remove, + .remove_new = csi2tx_remove, .driver = { .name = "cdns-csi2tx", diff --git a/drivers/media/platform/chips-media/coda-common.c b/drivers/media/platform/chips-media/coda-common.c index af71eea04dbd..d013ea5d9d3d 100644 --- a/drivers/media/platform/chips-media/coda-common.c +++ b/drivers/media/platform/chips-media/coda-common.c @@ -3300,7 +3300,7 @@ err_v4l2_register: return ret; } -static int coda_remove(struct platform_device *pdev) +static void coda_remove(struct platform_device *pdev) { struct coda_dev *dev = platform_get_drvdata(pdev); int i; @@ -3322,7 +3322,6 @@ static int coda_remove(struct platform_device *pdev) coda_free_aux_buf(dev, &dev->workbuf); debugfs_remove_recursive(dev->debugfs_root); ida_destroy(&dev->ida); - return 0; } #ifdef CONFIG_PM @@ -3347,7 +3346,7 @@ static const struct dev_pm_ops coda_pm_ops = { static struct platform_driver coda_driver = { .probe = coda_probe, - .remove = coda_remove, + .remove_new = coda_remove, .driver = { .name = CODA_NAME, .of_match_table = coda_dt_ids, diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index 54270d6b6f50..9ed3c2e063de 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -1794,8 +1794,8 @@ static int pxac_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; + .pads = &pad_cfg, + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -2421,7 +2421,7 @@ exit_free_dma_y: return err; } -static int pxa_camera_remove(struct platform_device *pdev) +static void pxa_camera_remove(struct platform_device *pdev) { struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); @@ -2437,8 +2437,6 @@ static int pxa_camera_remove(struct platform_device *pdev) v4l2_device_unregister(&pcdev->v4l2_dev); dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); - - return 0; } static const struct dev_pm_ops pxa_camera_pm = { @@ -2459,7 +2457,7 @@ static struct platform_driver pxa_camera_driver = { .of_match_table = of_match_ptr(pxa_camera_of_match), }, .probe = pxa_camera_probe, - .remove = pxa_camera_remove, + .remove_new = 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 1f89e71cdccf..96b35a5d6174 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -984,7 +984,7 @@ rel_dma: return ret; } -static int deinterlace_remove(struct platform_device *pdev) +static void deinterlace_remove(struct platform_device *pdev) { struct deinterlace_dev *pcdev = platform_get_drvdata(pdev); @@ -993,13 +993,11 @@ static int deinterlace_remove(struct platform_device *pdev) video_unregister_device(&pcdev->vfd); v4l2_device_unregister(&pcdev->v4l2_dev); dma_release_channel(pcdev->dma_chan); - - return 0; } static struct platform_driver deinterlace_pdrv = { .probe = deinterlace_probe, - .remove = deinterlace_remove, + .remove_new = 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 ad4a7922d0d7..154bdcb3f2cc 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -1351,8 +1351,8 @@ static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_pix_format *pix = &fmt->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; + .pads = &pad_cfg, + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index ef22bf8f276c..e93feefb447b 100644 --- a/drivers/media/platform/marvell/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -287,23 +287,13 @@ out: return ret; } - -static int mmpcam_remove(struct mmp_camera *cam) +static void mmpcam_remove(struct platform_device *pdev) { + struct mmp_camera *cam = platform_get_drvdata(pdev); struct mcam_camera *mcam = &cam->mcam; mccic_shutdown(mcam); pm_runtime_force_suspend(mcam->dev); - return 0; -} - -static int mmpcam_platform_remove(struct platform_device *pdev) -{ - struct mmp_camera *cam = platform_get_drvdata(pdev); - - if (cam == NULL) - return -ENODEV; - return mmpcam_remove(cam); } /* @@ -369,7 +359,7 @@ MODULE_DEVICE_TABLE(of, mmpcam_of_match); static struct platform_driver mmpcam_driver = { .probe = mmpcam_probe, - .remove = mmpcam_platform_remove, + .remove_new = mmpcam_remove, .driver = { .name = "mmp-camera", .of_match_table = of_match_ptr(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 969516a940ba..0051f372a66c 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1004,8 +1004,8 @@ static void mtk_jpegenc_worker(struct work_struct *work) retry_select: hw_id = mtk_jpegenc_get_hw(ctx); if (hw_id < 0) { - ret = wait_event_interruptible(jpeg->enc_hw_wq, - atomic_read(&jpeg->enchw_rdy) > 0); + ret = wait_event_interruptible(jpeg->hw_wq, + atomic_read(&jpeg->hw_rdy) > 0); if (ret != 0 || (i++ > MTK_JPEG_MAX_RETRY_TIME)) { dev_err(jpeg->dev, "%s : %d, all HW are busy\n", __func__, __LINE__); @@ -1016,7 +1016,7 @@ retry_select: goto retry_select; } - atomic_dec(&jpeg->enchw_rdy); + atomic_dec(&jpeg->hw_rdy); src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); if (!src_buf) goto getbuf_fail; @@ -1025,9 +1025,6 @@ retry_select: if (!dst_buf) goto getbuf_fail; - v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); mtk_jpegenc_set_hw_param(ctx, hw_id, src_buf, dst_buf); @@ -1045,6 +1042,9 @@ retry_select: goto enc_end; } + v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + schedule_delayed_work(&comp_jpeg[hw_id]->job_timeout_work, msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); @@ -1073,7 +1073,7 @@ enc_end: v4l2_m2m_buf_done(src_buf, buf_state); v4l2_m2m_buf_done(dst_buf, buf_state); getbuf_fail: - atomic_inc(&jpeg->enchw_rdy); + atomic_inc(&jpeg->hw_rdy); mtk_jpegenc_put_hw(jpeg, hw_id); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } @@ -1198,8 +1198,8 @@ static void mtk_jpegdec_worker(struct work_struct *work) retry_select: hw_id = mtk_jpegdec_get_hw(ctx); if (hw_id < 0) { - ret = wait_event_interruptible_timeout(jpeg->dec_hw_wq, - atomic_read(&jpeg->dechw_rdy) > 0, + ret = wait_event_interruptible_timeout(jpeg->hw_wq, + atomic_read(&jpeg->hw_rdy) > 0, MTK_JPEG_HW_TIMEOUT_MSEC); if (ret != 0 || (i++ > MTK_JPEG_MAX_RETRY_TIME)) { dev_err(jpeg->dev, "%s : %d, all HW are busy\n", @@ -1211,7 +1211,7 @@ retry_select: goto retry_select; } - atomic_dec(&jpeg->dechw_rdy); + atomic_dec(&jpeg->hw_rdy); src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); if (!src_buf) goto getbuf_fail; @@ -1220,9 +1220,6 @@ retry_select: if (!dst_buf) goto getbuf_fail; - v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); jpeg_dst_buf = mtk_jpeg_vb2_to_srcbuf(&dst_buf->vb2_buf); @@ -1231,7 +1228,7 @@ retry_select: &jpeg_src_buf->dec_param)) { mtk_jpeg_queue_src_chg_event(ctx); ctx->state = MTK_JPEG_SOURCE_CHANGE; - goto dec_end; + goto getbuf_fail; } jpeg_src_buf->curr_ctx = ctx; @@ -1254,6 +1251,9 @@ retry_select: goto clk_end; } + v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + schedule_delayed_work(&comp_jpeg[hw_id]->job_timeout_work, msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); @@ -1290,7 +1290,7 @@ dec_end: v4l2_m2m_buf_done(src_buf, buf_state); v4l2_m2m_buf_done(dst_buf, buf_state); getbuf_fail: - atomic_inc(&jpeg->dechw_rdy); + atomic_inc(&jpeg->hw_rdy); mtk_jpegdec_put_hw(jpeg, hw_id); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } @@ -1575,12 +1575,7 @@ static int mtk_jpeg_open(struct file *file) goto free; } - if (jpeg->is_jpgenc_multihw) - INIT_WORK(&ctx->jpeg_work, mtk_jpegenc_worker); - - if (jpeg->is_jpgdec_multihw) - INIT_WORK(&ctx->jpeg_work, mtk_jpegdec_worker); - + INIT_WORK(&ctx->jpeg_work, jpeg->variant->jpeg_worker); INIT_LIST_HEAD(&ctx->dst_done_queue); spin_lock_init(&ctx->done_queue_lock); v4l2_fh_init(&ctx->fh, vfd); @@ -1671,10 +1666,52 @@ static void mtk_jpeg_job_timeout_work(struct work_struct *work) v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } +static int mtk_jpeg_single_core_init(struct platform_device *pdev, + struct mtk_jpeg_dev *jpeg_dev) +{ + struct mtk_jpeg_dev *jpeg = jpeg_dev; + int jpeg_irq, ret; + + INIT_DELAYED_WORK(&jpeg->job_timeout_work, + mtk_jpeg_job_timeout_work); + + jpeg->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(jpeg->reg_base)) { + ret = PTR_ERR(jpeg->reg_base); + return ret; + } + + jpeg_irq = platform_get_irq(pdev, 0); + if (jpeg_irq < 0) + return jpeg_irq; + + ret = devm_request_irq(&pdev->dev, + jpeg_irq, + jpeg->variant->irq_handler, + 0, + pdev->name, jpeg); + if (ret) { + dev_err(&pdev->dev, "Failed to request jpeg_irq %d (%d)\n", + jpeg_irq, ret); + return ret; + } + + ret = devm_clk_bulk_get(jpeg->dev, + jpeg->variant->num_clks, + jpeg->variant->clks); + if (ret) { + dev_err(&pdev->dev, "Failed to init clk\n"); + return ret; + } + + return 0; +} + static int mtk_jpeg_probe(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg; - int jpeg_irq; + struct device_node *child; + int num_child = 0; int ret; jpeg = devm_kzalloc(&pdev->dev, sizeof(*jpeg), GFP_KERNEL); @@ -1692,38 +1729,26 @@ static int mtk_jpeg_probe(struct platform_device *pdev) return -EINVAL; } - if (list_empty(&pdev->dev.devres_head)) { - INIT_DELAYED_WORK(&jpeg->job_timeout_work, - mtk_jpeg_job_timeout_work); - - jpeg->reg_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(jpeg->reg_base)) { - ret = PTR_ERR(jpeg->reg_base); - return ret; + if (!jpeg->variant->multi_core) { + ret = mtk_jpeg_single_core_init(pdev, jpeg); + if (ret) { + v4l2_err(&jpeg->v4l2_dev, "mtk_jpeg_single_core_init failed."); + return -EINVAL; } + } else { + init_waitqueue_head(&jpeg->hw_wq); - jpeg_irq = platform_get_irq(pdev, 0); - if (jpeg_irq < 0) - return jpeg_irq; + for_each_child_of_node(pdev->dev.of_node, child) + num_child++; - ret = devm_request_irq(&pdev->dev, - jpeg_irq, - jpeg->variant->irq_handler, - 0, - pdev->name, jpeg); - if (ret) { - dev_err(&pdev->dev, "Failed to request jpeg_irq %d (%d)\n", - jpeg_irq, ret); - return ret; - } + atomic_set(&jpeg->hw_rdy, num_child); + atomic_set(&jpeg->hw_index, 0); - ret = devm_clk_bulk_get(jpeg->dev, - jpeg->variant->num_clks, - jpeg->variant->clks); - if (ret) { - dev_err(&pdev->dev, "Failed to init clk\n"); - return ret; - } + jpeg->workqueue = alloc_ordered_workqueue(MTK_JPEG_NAME, + WQ_MEM_RECLAIM + | WQ_FREEZABLE); + if (!jpeg->workqueue) + return -EINVAL; } ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); @@ -1757,9 +1782,6 @@ static int mtk_jpeg_probe(struct platform_device *pdev) jpeg->vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - ret = video_register_device(jpeg->vdev, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); @@ -1790,7 +1812,7 @@ err_m2m_init: return ret; } -static int mtk_jpeg_remove(struct platform_device *pdev) +static void mtk_jpeg_remove(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev); @@ -1798,8 +1820,6 @@ static int mtk_jpeg_remove(struct platform_device *pdev) video_unregister_device(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); - - return 0; } static __maybe_unused int mtk_jpeg_pm_suspend(struct device *dev) @@ -1846,6 +1866,7 @@ static const struct dev_pm_ops mtk_jpeg_pm_ops = { SET_RUNTIME_PM_OPS(mtk_jpeg_pm_suspend, mtk_jpeg_pm_resume, NULL) }; +#if defined(CONFIG_OF) static const struct mtk_jpeg_variant mt8173_jpeg_drvdata = { .clks = mt8173_jpeg_dec_clocks, .num_clks = ARRAY_SIZE(mt8173_jpeg_dec_clocks), @@ -1874,6 +1895,7 @@ static const struct mtk_jpeg_variant mtk_jpeg_drvdata = { .ioctl_ops = &mtk_jpeg_enc_ioctl_ops, .out_q_default_fourcc = V4L2_PIX_FMT_YUYV, .cap_q_default_fourcc = V4L2_PIX_FMT_JPEG, + .multi_core = false, }; static struct mtk_jpeg_variant mtk8195_jpegenc_drvdata = { @@ -1885,6 +1907,8 @@ static struct mtk_jpeg_variant mtk8195_jpegenc_drvdata = { .ioctl_ops = &mtk_jpeg_enc_ioctl_ops, .out_q_default_fourcc = V4L2_PIX_FMT_YUYV, .cap_q_default_fourcc = V4L2_PIX_FMT_JPEG, + .multi_core = true, + .jpeg_worker = mtk_jpegenc_worker, }; static const struct mtk_jpeg_variant mtk8195_jpegdec_drvdata = { @@ -1896,9 +1920,10 @@ static const struct mtk_jpeg_variant mtk8195_jpegdec_drvdata = { .ioctl_ops = &mtk_jpeg_dec_ioctl_ops, .out_q_default_fourcc = V4L2_PIX_FMT_JPEG, .cap_q_default_fourcc = V4L2_PIX_FMT_YUV420M, + .multi_core = true, + .jpeg_worker = mtk_jpegdec_worker, }; -#if defined(CONFIG_OF) static const struct of_device_id mtk_jpeg_match[] = { { .compatible = "mediatek,mt8173-jpgdec", @@ -1928,7 +1953,7 @@ MODULE_DEVICE_TABLE(of, mtk_jpeg_match); static struct platform_driver mtk_jpeg_driver = { .probe = mtk_jpeg_probe, - .remove = mtk_jpeg_remove, + .remove_new = mtk_jpeg_remove, .driver = { .name = MTK_JPEG_NAME, .of_match_table = of_match_ptr(mtk_jpeg_match), diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h index b9126476be8f..8ba6e757e11a 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h @@ -60,6 +60,8 @@ enum mtk_jpeg_ctx_state { * @ioctl_ops: the callback of jpeg v4l2_ioctl_ops * @out_q_default_fourcc: output queue default fourcc * @cap_q_default_fourcc: capture queue default fourcc + * @multi_core: mark jpeg hw is multi_core or not + * @jpeg_worker: jpeg dec or enc worker */ struct mtk_jpeg_variant { struct clk_bulk_data *clks; @@ -74,6 +76,8 @@ struct mtk_jpeg_variant { const struct v4l2_ioctl_ops *ioctl_ops; u32 out_q_default_fourcc; u32 cap_q_default_fourcc; + bool multi_core; + void (*jpeg_worker)(struct work_struct *work); }; struct mtk_jpeg_src_buf { @@ -199,16 +203,12 @@ struct mtk_jpegdec_comp_dev { * @job_timeout_work: IRQ timeout structure * @variant: driver variant to be used * @reg_encbase: jpg encode register base addr - * @enc_hw_dev: jpg encode hardware device - * @is_jpgenc_multihw: the flag of multi-hw core - * @enc_hw_wq: jpg encode wait queue - * @enchw_rdy: jpg encode hw ready flag + * @enc_hw_dev: jpg encode hardware device + * @hw_wq: jpg wait queue + * @hw_rdy: jpg hw ready flag * @reg_decbase: jpg decode register base addr - * @dec_hw_dev: jpg decode hardware device - * @is_jpgdec_multihw: the flag of dec multi-hw core - * @dec_hw_wq: jpg decode wait queue - * @dec_workqueue: jpg decode work queue - * @dechw_rdy: jpg decode hw ready flag + * @dec_hw_dev: jpg decode hardware device + * @hw_index: jpg hw index */ struct mtk_jpeg_dev { struct mutex lock; @@ -225,16 +225,12 @@ struct mtk_jpeg_dev { void __iomem *reg_encbase[MTK_JPEGENC_HW_MAX]; struct mtk_jpegenc_comp_dev *enc_hw_dev[MTK_JPEGENC_HW_MAX]; - bool is_jpgenc_multihw; - wait_queue_head_t enc_hw_wq; - atomic_t enchw_rdy; + wait_queue_head_t hw_wq; + atomic_t hw_rdy; void __iomem *reg_decbase[MTK_JPEGDEC_HW_MAX]; struct mtk_jpegdec_comp_dev *dec_hw_dev[MTK_JPEGDEC_HW_MAX]; - bool is_jpgdec_multihw; - wait_queue_head_t dec_hw_wq; - struct workqueue_struct *dec_workqueue; - atomic_t dechw_rdy; + atomic_t hw_index; }; /** 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 8c07fa02fd9a..869068fac5e2 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c @@ -503,8 +503,8 @@ static void mtk_jpegdec_timeout_work(struct work_struct *work) clk_disable_unprepare(cjpeg->jdec_clk.clks->clk); pm_runtime_put(cjpeg->dev); cjpeg->hw_state = MTK_JPEG_HW_IDLE; - atomic_inc(&master_jpeg->dechw_rdy); - wake_up(&master_jpeg->dec_hw_wq); + atomic_inc(&master_jpeg->hw_rdy); + wake_up(&master_jpeg->hw_wq); v4l2_m2m_buf_done(src_buf, buf_state); mtk_jpegdec_put_buf(cjpeg); } @@ -551,8 +551,8 @@ static irqreturn_t mtk_jpegdec_hw_irq_handler(int irq, void *priv) clk_disable_unprepare(jpeg->jdec_clk.clks->clk); jpeg->hw_state = MTK_JPEG_HW_IDLE; - wake_up(&master_jpeg->dec_hw_wq); - atomic_inc(&master_jpeg->dechw_rdy); + wake_up(&master_jpeg->hw_wq); + atomic_inc(&master_jpeg->hw_rdy); return IRQ_HANDLED; } @@ -608,25 +608,12 @@ static int mtk_jpegdec_hw_probe(struct platform_device *pdev) dev->plat_dev = pdev; dev->dev = &pdev->dev; - if (!master_dev->is_jpgdec_multihw) { - master_dev->is_jpgdec_multihw = true; - for (i = 0; i < MTK_JPEGDEC_HW_MAX; i++) - master_dev->dec_hw_dev[i] = NULL; - - init_waitqueue_head(&master_dev->dec_hw_wq); - master_dev->workqueue = alloc_ordered_workqueue(MTK_JPEG_NAME, - WQ_MEM_RECLAIM - | WQ_FREEZABLE); - if (!master_dev->workqueue) - return -EINVAL; - - ret = devm_add_action_or_reset(&pdev->dev, mtk_jpegdec_destroy_workqueue, - master_dev->workqueue); - if (ret) - return ret; - } + ret = devm_add_action_or_reset(&pdev->dev, + mtk_jpegdec_destroy_workqueue, + master_dev->workqueue); + if (ret) + return ret; - atomic_set(&master_dev->dechw_rdy, MTK_JPEGDEC_HW_MAX); spin_lock_init(&dev->hw_lock); dev->hw_state = MTK_JPEG_HW_IDLE; @@ -651,14 +638,10 @@ static int mtk_jpegdec_hw_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to register JPEGDEC irq handler.\n"); - for (i = 0; i < MTK_JPEGDEC_HW_MAX; i++) { - if (master_dev->dec_hw_dev[i]) - continue; - - master_dev->dec_hw_dev[i] = dev; - master_dev->reg_decbase[i] = dev->reg_base; - dev->master_dev = master_dev; - } + i = atomic_add_return(1, &master_dev->hw_index) - 1; + master_dev->dec_hw_dev[i] = dev; + master_dev->reg_decbase[i] = dev->reg_base; + dev->master_dev = master_dev; platform_set_drvdata(pdev, dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c index 1bbb712d78d0..71e85b4bbf12 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c @@ -248,8 +248,8 @@ static void mtk_jpegenc_timeout_work(struct work_struct *work) clk_disable_unprepare(cjpeg->venc_clk.clks->clk); pm_runtime_put(cjpeg->dev); cjpeg->hw_state = MTK_JPEG_HW_IDLE; - atomic_inc(&master_jpeg->enchw_rdy); - wake_up(&master_jpeg->enc_hw_wq); + atomic_inc(&master_jpeg->hw_rdy); + wake_up(&master_jpeg->hw_wq); v4l2_m2m_buf_done(src_buf, buf_state); mtk_jpegenc_put_buf(cjpeg); } @@ -286,14 +286,10 @@ static irqreturn_t mtk_jpegenc_hw_irq_handler(int irq, void *priv) mtk_jpegenc_put_buf(jpeg); pm_runtime_put(ctx->jpeg->dev); clk_disable_unprepare(jpeg->venc_clk.clks->clk); - if (!list_empty(&ctx->fh.m2m_ctx->out_q_ctx.rdy_queue) || - !list_empty(&ctx->fh.m2m_ctx->cap_q_ctx.rdy_queue)) { - queue_work(master_jpeg->workqueue, &ctx->jpeg_work); - } jpeg->hw_state = MTK_JPEG_HW_IDLE; - wake_up(&master_jpeg->enc_hw_wq); - atomic_inc(&master_jpeg->enchw_rdy); + wake_up(&master_jpeg->hw_wq); + atomic_inc(&master_jpeg->hw_rdy); return IRQ_HANDLED; } @@ -344,20 +340,6 @@ static int mtk_jpegenc_hw_probe(struct platform_device *pdev) dev->plat_dev = pdev; dev->dev = &pdev->dev; - if (!master_dev->is_jpgenc_multihw) { - master_dev->is_jpgenc_multihw = true; - for (i = 0; i < MTK_JPEGENC_HW_MAX; i++) - master_dev->enc_hw_dev[i] = NULL; - - init_waitqueue_head(&master_dev->enc_hw_wq); - master_dev->workqueue = alloc_ordered_workqueue(MTK_JPEG_NAME, - WQ_MEM_RECLAIM - | WQ_FREEZABLE); - if (!master_dev->workqueue) - return -EINVAL; - } - - atomic_set(&master_dev->enchw_rdy, MTK_JPEGENC_HW_MAX); spin_lock_init(&dev->hw_lock); dev->hw_state = MTK_JPEG_HW_IDLE; @@ -380,14 +362,10 @@ static int mtk_jpegenc_hw_probe(struct platform_device *pdev) if (ret) return ret; - for (i = 0; i < MTK_JPEGENC_HW_MAX; i++) { - if (master_dev->enc_hw_dev[i]) - continue; - - master_dev->enc_hw_dev[i] = dev; - master_dev->reg_encbase[i] = dev->reg_base; - dev->master_dev = master_dev; - } + i = atomic_add_return(1, &master_dev->hw_index) - 1; + master_dev->enc_hw_dev[i] = dev; + master_dev->reg_encbase[i] = dev->reg_base; + dev->master_dev = master_dev; platform_set_drvdata(pdev, dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c index d83c4964eaf9..77e310e588e5 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c @@ -235,7 +235,7 @@ err_comp: return ret; } -static int mtk_mdp_remove(struct platform_device *pdev) +static void mtk_mdp_remove(struct platform_device *pdev) { struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev); struct mtk_mdp_comp *comp, *comp_temp; @@ -255,7 +255,6 @@ static int mtk_mdp_remove(struct platform_device *pdev) } dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); - return 0; } static int __maybe_unused mtk_mdp_pm_suspend(struct device *dev) @@ -299,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 = mtk_mdp_remove, + .remove_new = mtk_mdp_remove, .driver = { .name = MTK_MDP_MODULE_NAME, .pm = &mtk_mdp_pm_ops, diff --git a/drivers/media/platform/mediatek/mdp3/Makefile b/drivers/media/platform/mediatek/mdp3/Makefile index 63e6c87e480b..2ee24195a2dd 100644 --- a/drivers/media/platform/mediatek/mdp3/Makefile +++ b/drivers/media/platform/mediatek/mdp3/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -mtk-mdp3-y += mtk-mdp3-core.o mtk-mdp3-vpu.o mtk-mdp3-regs.o +mtk-mdp3-y += mdp_cfg_data.o mtk-mdp3-core.o mtk-mdp3-vpu.o mtk-mdp3-regs.o mtk-mdp3-y += mtk-mdp3-m2m.o mtk-mdp3-y += mtk-mdp3-comp.o mtk-mdp3-cmdq.o diff --git a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c new file mode 100644 index 000000000000..502eeae0bfdc --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c @@ -0,0 +1,453 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com> + */ + +#include "mtk-img-ipi.h" +#include "mtk-mdp3-cfg.h" +#include "mtk-mdp3-core.h" +#include "mtk-mdp3-comp.h" +#include "mtk-mdp3-regs.h" + +enum mt8183_mdp_comp_id { + /* ISP */ + MT8183_MDP_COMP_WPEI = 0, + MT8183_MDP_COMP_WPEO, /* 1 */ + MT8183_MDP_COMP_WPEI2, /* 2 */ + MT8183_MDP_COMP_WPEO2, /* 3 */ + MT8183_MDP_COMP_ISP_IMGI, /* 4 */ + MT8183_MDP_COMP_ISP_IMGO, /* 5 */ + MT8183_MDP_COMP_ISP_IMG2O, /* 6 */ + + /* IPU */ + MT8183_MDP_COMP_IPUI, /* 7 */ + MT8183_MDP_COMP_IPUO, /* 8 */ + + /* MDP */ + MT8183_MDP_COMP_CAMIN, /* 9 */ + MT8183_MDP_COMP_CAMIN2, /* 10 */ + MT8183_MDP_COMP_RDMA0, /* 11 */ + MT8183_MDP_COMP_AAL0, /* 12 */ + MT8183_MDP_COMP_CCORR0, /* 13 */ + MT8183_MDP_COMP_RSZ0, /* 14 */ + MT8183_MDP_COMP_RSZ1, /* 15 */ + MT8183_MDP_COMP_TDSHP0, /* 16 */ + MT8183_MDP_COMP_COLOR0, /* 17 */ + MT8183_MDP_COMP_PATH0_SOUT, /* 18 */ + MT8183_MDP_COMP_PATH1_SOUT, /* 19 */ + MT8183_MDP_COMP_WROT0, /* 20 */ + MT8183_MDP_COMP_WDMA, /* 21 */ + + /* Dummy Engine */ + MT8183_MDP_COMP_RDMA1, /* 22 */ + MT8183_MDP_COMP_RSZ2, /* 23 */ + MT8183_MDP_COMP_TDSHP1, /* 24 */ + MT8183_MDP_COMP_WROT1, /* 25 */ +}; + +static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = { + [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8183-mmsys" }, + [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8183-disp-mutex" }, + [MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" } +}; + +static const struct mdp_platform_config mt8183_plat_cfg = { + .rdma_support_10bit = true, + .rdma_rsz1_sram_sharing = true, + .rdma_upsample_repeat_only = true, + .rsz_disable_dcm_small_sample = false, + .wrot_filter_constraint = false, +}; + +static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, + [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0, + [MDP_COMP_RSZ1] = MUTEX_MOD_IDX_MDP_RSZ1, + [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0, + [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0, + [MDP_COMP_WDMA] = MUTEX_MOD_IDX_MDP_WDMA, + [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0, + [MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0, +}; + +static const struct mdp_comp_data mt8183_mdp_comp_data[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_WPEI] = { + {MDP_COMP_TYPE_WPEI, 0, MT8183_MDP_COMP_WPEI}, + {0, 0, 0} + }, + [MDP_COMP_WPEO] = { + {MDP_COMP_TYPE_EXTO, 2, MT8183_MDP_COMP_WPEO}, + {0, 0, 0} + }, + [MDP_COMP_WPEI2] = { + {MDP_COMP_TYPE_WPEI, 1, MT8183_MDP_COMP_WPEI2}, + {0, 0, 0} + }, + [MDP_COMP_WPEO2] = { + {MDP_COMP_TYPE_EXTO, 3, MT8183_MDP_COMP_WPEO2}, + {0, 0, 0} + }, + [MDP_COMP_ISP_IMGI] = { + {MDP_COMP_TYPE_IMGI, 0, MT8183_MDP_COMP_ISP_IMGI}, + {0, 0, 4} + }, + [MDP_COMP_ISP_IMGO] = { + {MDP_COMP_TYPE_EXTO, 0, MT8183_MDP_COMP_ISP_IMGO}, + {0, 0, 4} + }, + [MDP_COMP_ISP_IMG2O] = { + {MDP_COMP_TYPE_EXTO, 1, MT8183_MDP_COMP_ISP_IMG2O}, + {0, 0, 0} + }, + [MDP_COMP_CAMIN] = { + {MDP_COMP_TYPE_DL_PATH, 0, MT8183_MDP_COMP_CAMIN}, + {2, 2, 1} + }, + [MDP_COMP_CAMIN2] = { + {MDP_COMP_TYPE_DL_PATH, 1, MT8183_MDP_COMP_CAMIN2}, + {2, 4, 1} + }, + [MDP_COMP_RDMA0] = { + {MDP_COMP_TYPE_RDMA, 0, MT8183_MDP_COMP_RDMA0}, + {2, 0, 0} + }, + [MDP_COMP_CCORR0] = { + {MDP_COMP_TYPE_CCORR, 0, MT8183_MDP_COMP_CCORR0}, + {1, 0, 0} + }, + [MDP_COMP_RSZ0] = { + {MDP_COMP_TYPE_RSZ, 0, MT8183_MDP_COMP_RSZ0}, + {1, 0, 0} + }, + [MDP_COMP_RSZ1] = { + {MDP_COMP_TYPE_RSZ, 1, MT8183_MDP_COMP_RSZ1}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP0] = { + {MDP_COMP_TYPE_TDSHP, 0, MT8183_MDP_COMP_TDSHP0}, + {0, 0, 0} + }, + [MDP_COMP_PATH0_SOUT] = { + {MDP_COMP_TYPE_PATH, 0, MT8183_MDP_COMP_PATH0_SOUT}, + {0, 0, 0} + }, + [MDP_COMP_PATH1_SOUT] = { + {MDP_COMP_TYPE_PATH, 1, MT8183_MDP_COMP_PATH1_SOUT}, + {0, 0, 0} + }, + [MDP_COMP_WROT0] = { + {MDP_COMP_TYPE_WROT, 0, MT8183_MDP_COMP_WROT0}, + {1, 0, 0} + }, + [MDP_COMP_WDMA] = { + {MDP_COMP_TYPE_WDMA, 0, MT8183_MDP_COMP_WDMA}, + {1, 0, 0} + }, +}; + +static const struct of_device_id mt8183_sub_comp_dt_ids[] = { + { + .compatible = "mediatek,mt8183-mdp3-wdma", + .data = (void *)MDP_COMP_TYPE_PATH, + }, { + .compatible = "mediatek,mt8183-mdp3-wrot", + .data = (void *)MDP_COMP_TYPE_PATH, + }, + {} +}; + +/* + * All 10-bit related formats are not added in the basic format list, + * please add the corresponding format settings before use. + */ +static const struct mdp_format mt8183_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_GREY, + .mdp_color = MDP_COLOR_GREY, + .depth = { 8 }, + .row_depth = { 8 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_RGB565X, + .mdp_color = MDP_COLOR_BGR565, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_RGB565, + .mdp_color = MDP_COLOR_RGB565, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_RGB24, + .mdp_color = MDP_COLOR_RGB888, + .depth = { 24 }, + .row_depth = { 24 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_BGR24, + .mdp_color = MDP_COLOR_BGR888, + .depth = { 24 }, + .row_depth = { 24 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_ABGR32, + .mdp_color = MDP_COLOR_BGRA8888, + .depth = { 32 }, + .row_depth = { 32 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_ARGB32, + .mdp_color = MDP_COLOR_ARGB8888, + .depth = { 32 }, + .row_depth = { 32 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_UYVY, + .mdp_color = MDP_COLOR_UYVY, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_VYUY, + .mdp_color = MDP_COLOR_VYUY, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YUYV, + .mdp_color = MDP_COLOR_YUYV, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YVYU, + .mdp_color = MDP_COLOR_YVYU, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420, + .mdp_color = MDP_COLOR_I420, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420, + .mdp_color = MDP_COLOR_YV12, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV12, + .mdp_color = MDP_COLOR_NV12, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV21, + .mdp_color = MDP_COLOR_NV21, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV16, + .mdp_color = MDP_COLOR_NV16, + .depth = { 16 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV61, + .mdp_color = MDP_COLOR_NV61, + .depth = { 16 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV24, + .mdp_color = MDP_COLOR_NV24, + .depth = { 24 }, + .row_depth = { 8 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV42, + .mdp_color = MDP_COLOR_NV42, + .depth = { 24 }, + .row_depth = { 8 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_MT21C, + .mdp_color = MDP_COLOR_420_BLK_UFO, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 4, + .halign = 5, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_MM21, + .mdp_color = MDP_COLOR_420_BLK, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 4, + .halign = 5, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV12M, + .mdp_color = MDP_COLOR_NV12, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV21M, + .mdp_color = MDP_COLOR_NV21, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV16M, + .mdp_color = MDP_COLOR_NV16, + .depth = { 8, 8 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV61M, + .mdp_color = MDP_COLOR_NV61, + .depth = { 8, 8 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420M, + .mdp_color = MDP_COLOR_I420, + .depth = { 8, 2, 2 }, + .row_depth = { 8, 4, 4 }, + .num_planes = 3, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420M, + .mdp_color = MDP_COLOR_YV12, + .depth = { 8, 2, 2 }, + .row_depth = { 8, 4, 4 }, + .num_planes = 3, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + } +}; + +static const struct mdp_limit mt8183_mdp_def_limit = { + .out_limit = { + .wmin = 16, + .hmin = 16, + .wmax = 8176, + .hmax = 8176, + }, + .cap_limit = { + .wmin = 2, + .hmin = 2, + .wmax = 8176, + .hmax = 8176, + }, + .h_scale_up_max = 32, + .v_scale_up_max = 32, + .h_scale_down_max = 20, + .v_scale_down_max = 128, +}; + +static const struct mdp_pipe_info mt8183_pipe_info[] = { + [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, 0}, + [MDP_PIPE_WPEI2] = {MDP_PIPE_WPEI2, 1}, + [MDP_PIPE_IMGI] = {MDP_PIPE_IMGI, 2}, + [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, 3} +}; + +const struct mtk_mdp_driver_data mt8183_mdp_driver_data = { + .mdp_plat_id = MT8183, + .mdp_probe_infra = mt8183_mdp_probe_infra, + .mdp_cfg = &mt8183_plat_cfg, + .mdp_mutex_table_idx = mt8183_mutex_idx, + .comp_data = mt8183_mdp_comp_data, + .comp_data_len = ARRAY_SIZE(mt8183_mdp_comp_data), + .mdp_sub_comp_dt_ids = mt8183_sub_comp_dt_ids, + .format = mt8183_formats, + .format_len = ARRAY_SIZE(mt8183_formats), + .def_limit = &mt8183_mdp_def_limit, + .pipe_info = mt8183_pipe_info, + .pipe_info_len = ARRAY_SIZE(mt8183_pipe_info), +}; + +s32 mdp_cfg_get_id_inner(struct mdp_dev *mdp_dev, enum mtk_mdp_comp_id id) +{ + if (!mdp_dev) + return MDP_COMP_NONE; + if (id <= MDP_COMP_NONE || id >= MDP_MAX_COMP_COUNT) + return MDP_COMP_NONE; + + return mdp_dev->mdp_data->comp_data[id].match.inner_id; +} + +enum mtk_mdp_comp_id mdp_cfg_get_id_public(struct mdp_dev *mdp_dev, s32 inner_id) +{ + enum mtk_mdp_comp_id public_id = MDP_COMP_NONE; + u32 i; + + if (IS_ERR(mdp_dev) || !inner_id) + goto err_public_id; + + for (i = 0; i < MDP_MAX_COMP_COUNT; i++) { + if (mdp_dev->mdp_data->comp_data[i].match.inner_id == inner_id) { + public_id = i; + return public_id; + } + } + +err_public_id: + return public_id; +} diff --git a/drivers/media/platform/mediatek/mdp3/mdp_sm_mt8183.h b/drivers/media/platform/mediatek/mdp3/mdp_sm_mt8183.h new file mode 100644 index 000000000000..85637084520f --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_sm_mt8183.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com> + */ + +#ifndef __MDP_SM_MT8183_H__ +#define __MDP_SM_MT8183_H__ + +#include "mtk-mdp3-type.h" + +/* + * ISP-MDP generic output information + * MD5 of the target SCP prebuild: + * 2d995ddb5c3b0cf26e96d6a823481886 + */ + +#define IMG_MAX_SUBFRAMES_8183 14 + +struct img_comp_frame_8183 { + u32 output_disable:1; + u32 bypass:1; + u16 in_width; + u16 in_height; + u16 out_width; + u16 out_height; + struct img_crop crop; + u16 in_total_width; + u16 out_total_width; +} __packed; + +struct img_comp_subfrm_8183 { + u32 tile_disable:1; + struct img_region in; + struct img_region out; + struct img_offset luma; + struct img_offset chroma; + s16 out_vertical; /* Output vertical index */ + s16 out_horizontal; /* Output horizontal index */ +} __packed; + +struct mdp_rdma_subfrm_8183 { + u32 offset[IMG_MAX_PLANES]; + u32 offset_0_p; + u32 src; + u32 clip; + u32 clip_ofst; +} __packed; + +struct mdp_rdma_data_8183 { + u32 src_ctrl; + u32 control; + u32 iova[IMG_MAX_PLANES]; + u32 iova_end[IMG_MAX_PLANES]; + u32 mf_bkgd; + u32 mf_bkgd_in_pxl; + u32 sf_bkgd; + u32 ufo_dec_y; + u32 ufo_dec_c; + u32 transform; + struct mdp_rdma_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183]; +} __packed; + +struct mdp_rsz_subfrm_8183 { + u32 control2; + u32 src; + u32 clip; +} __packed; + +struct mdp_rsz_data_8183 { + u32 coeff_step_x; + u32 coeff_step_y; + u32 control1; + u32 control2; + struct mdp_rsz_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183]; +} __packed; + +struct mdp_wrot_subfrm_8183 { + u32 offset[IMG_MAX_PLANES]; + u32 src; + u32 clip; + u32 clip_ofst; + u32 main_buf; +} __packed; + +struct mdp_wrot_data_8183 { + u32 iova[IMG_MAX_PLANES]; + u32 control; + u32 stride[IMG_MAX_PLANES]; + u32 mat_ctrl; + u32 fifo_test; + u32 filter; + struct mdp_wrot_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183]; +} __packed; + +struct mdp_wdma_subfrm_8183 { + u32 offset[IMG_MAX_PLANES]; + u32 src; + u32 clip; + u32 clip_ofst; +} __packed; + +struct mdp_wdma_data_8183 { + u32 wdma_cfg; + u32 iova[IMG_MAX_PLANES]; + u32 w_in_byte; + u32 uv_stride; + struct mdp_wdma_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183]; +} __packed; + +struct isp_data_8183 { + u64 dl_flags; /* 1 << (enum mdp_comp_type) */ + u32 smxi_iova[4]; + u32 cq_idx; + u32 cq_iova; + u32 tpipe_iova[IMG_MAX_SUBFRAMES_8183]; +} __packed; + +struct img_compparam_8183 { + u16 type; /* enum mdp_comp_id */ + u16 id; /* engine alias_id */ + u32 input; + u32 outputs[IMG_MAX_HW_OUTPUTS]; + u32 num_outputs; + struct img_comp_frame_8183 frame; + struct img_comp_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183]; + u32 num_subfrms; + union { + struct mdp_rdma_data_8183 rdma; + struct mdp_rsz_data_8183 rsz; + struct mdp_wrot_data_8183 wrot; + struct mdp_wdma_data_8183 wdma; + struct isp_data_8183 isp; + }; +} __packed; + +struct img_config_8183 { + struct img_compparam_8183 components[IMG_MAX_COMPONENTS]; + u32 num_components; + struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES_8183]; + u32 num_subfrms; +} __packed; + +#endif /* __MDP_SM_MT8183_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h index c7f231f8ea3e..22b8b9a10ef7 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h @@ -8,19 +8,11 @@ #ifndef __MTK_IMG_IPI_H__ #define __MTK_IMG_IPI_H__ -#include <linux/types.h> +#include <linux/err.h> +#include "mdp_sm_mt8183.h" +#include "mtk-mdp3-type.h" -/* - * ISP-MDP generic input information - * MD5 of the target SCP blob: - * 6da52bdcf4bf76a0983b313e1d4745d6 - */ - -#define IMG_MAX_HW_INPUTS 3 - -#define IMG_MAX_HW_OUTPUTS 4 - -#define IMG_MAX_PLANES 3 +/* ISP-MDP generic input information */ #define IMG_IPI_INIT 1 #define IMG_IPI_DEINIT 2 @@ -71,17 +63,6 @@ struct img_image_buffer { #define IMG_SUBPIXEL_SHIFT 20 -struct img_crop { - s32 left; - s32 top; - u32 width; - u32 height; - u32 left_subpix; - u32 top_subpix; - u32 width_subpix; - u32 height_subpix; -} __packed; - #define IMG_CTRL_FLAG_HFLIP BIT(0) #define IMG_CTRL_FLAG_DITHER BIT(1) #define IMG_CTRL_FLAG_SHARPNESS BIT(4) @@ -132,159 +113,37 @@ struct img_frameparam { struct img_ipi_frameparam frameparam; } __packed; -/* ISP-MDP generic output information */ +/* Platform config indicator */ +#define MT8183 8183 -struct img_comp_frame { - u32 output_disable; - u32 bypass; - u32 in_width; - u32 in_height; - u32 out_width; - u32 out_height; - struct img_crop crop; - u32 in_total_width; - u32 out_total_width; -} __packed; +#define CFG_CHECK(plat, p_id) ((plat) == (p_id)) -struct img_region { - s32 left; - s32 right; - s32 top; - s32 bottom; -} __packed; +#define _CFG_OFST(plat, cfg, ofst) ((void *)(&((cfg)->config_##plat) + (ofst))) +#define CFG_OFST(plat, cfg, ofst) \ + (IS_ERR_OR_NULL(cfg) ? NULL : _CFG_OFST(plat, cfg, ofst)) -struct img_offset { - s32 left; - s32 top; - u32 left_subpix; - u32 top_subpix; -} __packed; - -struct img_comp_subfrm { - u32 tile_disable; - struct img_region in; - struct img_region out; - struct img_offset luma; - struct img_offset chroma; - s32 out_vertical; /* Output vertical index */ - s32 out_horizontal; /* Output horizontal index */ -} __packed; +#define _CFG_ADDR(plat, cfg, mem) (&((cfg)->config_##plat.mem)) +#define CFG_ADDR(plat, cfg, mem) \ + (IS_ERR_OR_NULL(cfg) ? NULL : _CFG_ADDR(plat, cfg, mem)) -#define IMG_MAX_SUBFRAMES 14 +#define _CFG_GET(plat, cfg, mem) ((cfg)->config_##plat.mem) +#define CFG_GET(plat, cfg, mem) \ + (IS_ERR_OR_NULL(cfg) ? 0 : _CFG_GET(plat, cfg, mem)) -struct mdp_rdma_subfrm { - u32 offset[IMG_MAX_PLANES]; - u32 offset_0_p; - u32 src; - u32 clip; - u32 clip_ofst; -} __packed; - -struct mdp_rdma_data { - u32 src_ctrl; - u32 control; - u32 iova[IMG_MAX_PLANES]; - u32 iova_end[IMG_MAX_PLANES]; - u32 mf_bkgd; - u32 mf_bkgd_in_pxl; - u32 sf_bkgd; - u32 ufo_dec_y; - u32 ufo_dec_c; - u32 transform; - struct mdp_rdma_subfrm subfrms[IMG_MAX_SUBFRAMES]; -} __packed; - -struct mdp_rsz_subfrm { - u32 control2; - u32 src; - u32 clip; -} __packed; - -struct mdp_rsz_data { - u32 coeff_step_x; - u32 coeff_step_y; - u32 control1; - u32 control2; - struct mdp_rsz_subfrm subfrms[IMG_MAX_SUBFRAMES]; -} __packed; +#define _CFG_COMP(plat, comp, mem) ((comp)->comp_##plat.mem) +#define CFG_COMP(plat, comp, mem) \ + (IS_ERR_OR_NULL(comp) ? 0 : _CFG_COMP(plat, comp, mem)) -struct mdp_wrot_subfrm { - u32 offset[IMG_MAX_PLANES]; - u32 src; - u32 clip; - u32 clip_ofst; - u32 main_buf; -} __packed; - -struct mdp_wrot_data { - u32 iova[IMG_MAX_PLANES]; - u32 control; - u32 stride[IMG_MAX_PLANES]; - u32 mat_ctrl; - u32 fifo_test; - u32 filter; - struct mdp_wrot_subfrm subfrms[IMG_MAX_SUBFRAMES]; -} __packed; - -struct mdp_wdma_subfrm { - u32 offset[IMG_MAX_PLANES]; - u32 src; - u32 clip; - u32 clip_ofst; -} __packed; - -struct mdp_wdma_data { - u32 wdma_cfg; - u32 iova[IMG_MAX_PLANES]; - u32 w_in_byte; - u32 uv_stride; - struct mdp_wdma_subfrm subfrms[IMG_MAX_SUBFRAMES]; -} __packed; - -struct isp_data { - u64 dl_flags; /* 1 << (enum mdp_comp_type) */ - u32 smxi_iova[4]; - u32 cq_idx; - u32 cq_iova; - u32 tpipe_iova[IMG_MAX_SUBFRAMES]; +struct img_config { + union { + struct img_config_8183 config_8183; + }; } __packed; struct img_compparam { - u32 type; /* enum mdp_comp_id */ - u32 id; /* engine alias_id */ - u32 input; - u32 outputs[IMG_MAX_HW_OUTPUTS]; - u32 num_outputs; - struct img_comp_frame frame; - struct img_comp_subfrm subfrms[IMG_MAX_SUBFRAMES]; - u32 num_subfrms; union { - struct mdp_rdma_data rdma; - struct mdp_rsz_data rsz; - struct mdp_wrot_data wrot; - struct mdp_wdma_data wdma; - struct isp_data isp; + struct img_compparam_8183 comp_8183; }; } __packed; -#define IMG_MAX_COMPONENTS 20 - -struct img_mux { - u32 reg; - u32 value; - u32 subsys_id; -} __packed; - -struct img_mmsys_ctrl { - struct img_mux sets[IMG_MAX_COMPONENTS * 2]; - u32 num_sets; -} __packed; - -struct img_config { - struct img_compparam components[IMG_MAX_COMPONENTS]; - u32 num_components; - struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES]; - u32 num_subfrms; -} __packed; - #endif /* __MTK_IMG_IPI_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h new file mode 100644 index 000000000000..dee57cc4a954 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com> + */ + +#ifndef __MTK_MDP3_CFG_H__ +#define __MTK_MDP3_CFG_H__ + +#include <linux/types.h> + +extern const struct mtk_mdp_driver_data mt8183_mdp_driver_data; + +struct mdp_dev; +enum mtk_mdp_comp_id; + +s32 mdp_cfg_get_id_inner(struct mdp_dev *mdp_dev, enum mtk_mdp_comp_id id); +enum mtk_mdp_comp_id mdp_cfg_get_id_public(struct mdp_dev *mdp_dev, s32 id); + +#endif /* __MTK_MDP3_CFG_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c index 124c1b96e96b..3177592490be 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c @@ -10,6 +10,7 @@ #include "mtk-mdp3-comp.h" #include "mtk-mdp3-core.h" #include "mtk-mdp3-m2m.h" +#include "mtk-img-ipi.h" #define MDP_PATH_MAX_COMPS IMG_MAX_COMPONENTS @@ -28,50 +29,62 @@ struct mdp_path { #define call_op(ctx, op, ...) \ (has_op(ctx, op) ? (ctx)->comp->ops->op(ctx, ##__VA_ARGS__) : 0) -static bool is_output_disabled(const struct img_compparam *param, u32 count) +static bool is_output_disabled(int p_id, const struct img_compparam *param, u32 count) { - return (count < param->num_subfrms) ? - (param->frame.output_disable || - param->subfrms[count].tile_disable) : - true; + u32 num = 0; + bool dis_output = false; + bool dis_tile = false; + + if (CFG_CHECK(MT8183, p_id)) { + num = CFG_COMP(MT8183, param, num_subfrms); + dis_output = CFG_COMP(MT8183, param, frame.output_disable); + dis_tile = CFG_COMP(MT8183, param, frame.output_disable); + } + + return (count < num) ? (dis_output || dis_tile) : true; } static int mdp_path_subfrm_require(const struct mdp_path *path, struct mdp_cmdq_cmd *cmd, s32 *mutex_id, u32 count) { - const struct img_config *config = path->config; + const int p_id = path->mdp_dev->mdp_data->mdp_plat_id; const struct mdp_comp_ctx *ctx; const struct mtk_mdp_driver_data *data = path->mdp_dev->mdp_data; struct device *dev = &path->mdp_dev->pdev->dev; struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex; int id, index; + u32 num_comp = 0; + + if (CFG_CHECK(MT8183, p_id)) + num_comp = CFG_GET(MT8183, path->config, num_components); /* Decide which mutex to use based on the current pipeline */ - switch (path->comps[0].comp->id) { + switch (path->comps[0].comp->public_id) { case MDP_COMP_RDMA0: - *mutex_id = MDP_PIPE_RDMA0; + index = MDP_PIPE_RDMA0; break; case MDP_COMP_ISP_IMGI: - *mutex_id = MDP_PIPE_IMGI; + index = MDP_PIPE_IMGI; break; case MDP_COMP_WPEI: - *mutex_id = MDP_PIPE_WPEI; + index = MDP_PIPE_WPEI; break; case MDP_COMP_WPEI2: - *mutex_id = MDP_PIPE_WPEI2; + index = MDP_PIPE_WPEI2; break; default: dev_err(dev, "Unknown pipeline and no mutex is assigned"); return -EINVAL; } + *mutex_id = data->pipe_info[index].mutex_id; /* Set mutex mod */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; - if (is_output_disabled(ctx->param, count)) + if (is_output_disabled(p_id, ctx->param, count)) continue; - id = ctx->comp->id; + id = ctx->comp->public_id; mtk_mutex_write_mod(mutex[*mutex_id], data->mdp_mutex_table_idx[id], false); } @@ -86,11 +99,12 @@ static int mdp_path_subfrm_run(const struct mdp_path *path, struct mdp_cmdq_cmd *cmd, s32 *mutex_id, u32 count) { - const struct img_config *config = path->config; + const int p_id = path->mdp_dev->mdp_data->mdp_plat_id; const struct mdp_comp_ctx *ctx; struct device *dev = &path->mdp_dev->pdev->dev; struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex; int index; + u32 num_comp = 0; s32 event; if (-1 == *mutex_id) { @@ -98,11 +112,14 @@ static int mdp_path_subfrm_run(const struct mdp_path *path, return -EINVAL; } + if (CFG_CHECK(MT8183, p_id)) + num_comp = CFG_GET(MT8183, path->config, num_components); + /* Wait WROT SRAM shared to DISP RDMA */ /* Clear SOF event for each engine */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; - if (is_output_disabled(ctx->param, count)) + if (is_output_disabled(p_id, ctx->param, count)) continue; event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF]; if (event != MDP_GCE_NO_EVENT) @@ -113,9 +130,9 @@ static int mdp_path_subfrm_run(const struct mdp_path *path, mtk_mutex_enable_by_cmdq(mutex[*mutex_id], (void *)&cmd->pkt); /* Wait SOF events and clear mutex modules (optional) */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; - if (is_output_disabled(ctx->param, count)) + if (is_output_disabled(p_id, ctx->param, count)) continue; event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF]; if (event != MDP_GCE_NO_EVENT) @@ -127,16 +144,22 @@ static int mdp_path_subfrm_run(const struct mdp_path *path, static int mdp_path_ctx_init(struct mdp_dev *mdp, struct mdp_path *path) { - const struct img_config *config = path->config; + const int p_id = mdp->mdp_data->mdp_plat_id; + void *param = NULL; int index, ret; + u32 num_comp = 0; - if (config->num_components < 1) + if (CFG_CHECK(MT8183, p_id)) + num_comp = CFG_GET(MT8183, path->config, num_components); + + if (num_comp < 1) return -EINVAL; - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { + if (CFG_CHECK(MT8183, p_id)) + param = (void *)CFG_ADDR(MT8183, path->config, components[index]); ret = mdp_comp_ctx_config(mdp, &path->comps[index], - &config->components[index], - path->param); + param, path->param); if (ret) return ret; } @@ -147,12 +170,19 @@ static int mdp_path_ctx_init(struct mdp_dev *mdp, struct mdp_path *path) static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, struct mdp_path *path, u32 count) { - const struct img_config *config = path->config; - const struct img_mmsys_ctrl *ctrl = &config->ctrls[count]; + const int p_id = path->mdp_dev->mdp_data->mdp_plat_id; + const struct img_mmsys_ctrl *ctrl = NULL; const struct img_mux *set; struct mdp_comp_ctx *ctx; s32 mutex_id; int index, ret; + u32 num_comp = 0; + + if (CFG_CHECK(MT8183, p_id)) + num_comp = CFG_GET(MT8183, path->config, num_components); + + if (CFG_CHECK(MT8183, p_id)) + ctrl = CFG_ADDR(MT8183, path->config, ctrls[count]); /* Acquire components */ ret = mdp_path_subfrm_require(path, cmd, &mutex_id, count); @@ -165,9 +195,9 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, set->value, 0xFFFFFFFF); } /* Config sub-frame information */ - for (index = (config->num_components - 1); index >= 0; index--) { + for (index = (num_comp - 1); index >= 0; index--) { ctx = &path->comps[index]; - if (is_output_disabled(ctx->param, count)) + if (is_output_disabled(p_id, ctx->param, count)) continue; ret = call_op(ctx, config_subfrm, cmd, count); if (ret) @@ -178,16 +208,16 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, if (ret) return ret; /* Wait components done */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; - if (is_output_disabled(ctx->param, count)) + if (is_output_disabled(p_id, ctx->param, count)) continue; ret = call_op(ctx, wait_comp_event, cmd); if (ret) return ret; } /* Advance to the next sub-frame */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; ret = call_op(ctx, advance_subfrm, cmd, count); if (ret) @@ -206,23 +236,35 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd, struct mdp_path *path) { - const struct img_config *config = path->config; + const int p_id = mdp->mdp_data->mdp_plat_id; struct mdp_comp_ctx *ctx; int index, count, ret; + u32 num_comp = 0; + u32 num_sub = 0; + + if (CFG_CHECK(MT8183, p_id)) + num_comp = CFG_GET(MT8183, path->config, num_components); + + if (CFG_CHECK(MT8183, p_id)) + num_sub = CFG_GET(MT8183, path->config, num_subfrms); /* Config path frame */ /* Reset components */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; ret = call_op(ctx, init_comp, cmd); if (ret) return ret; } /* Config frame mode */ - for (index = 0; index < config->num_components; index++) { - const struct v4l2_rect *compose = - path->composes[ctx->param->outputs[0]]; + for (index = 0; index < num_comp; index++) { + const struct v4l2_rect *compose; + u32 out = 0; + + if (CFG_CHECK(MT8183, p_id)) + out = CFG_COMP(MT8183, ctx->param, outputs[0]); + compose = path->composes[out]; ctx = &path->comps[index]; ret = call_op(ctx, config_frame, cmd, compose); if (ret) @@ -230,13 +272,13 @@ static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd, } /* Config path sub-frames */ - for (count = 0; count < config->num_subfrms; count++) { + for (count = 0; count < num_sub; count++) { ret = mdp_path_config_subfrm(cmd, path, count); if (ret) return ret; } /* Post processing information */ - for (index = 0; index < config->num_components; index++) { + for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; ret = call_op(ctx, post_process, cmd); if (ret) @@ -286,11 +328,13 @@ static void mdp_auto_release_work(struct work_struct *work) { struct mdp_cmdq_cmd *cmd; struct mdp_dev *mdp; + int id; cmd = container_of(work, struct mdp_cmdq_cmd, auto_release_work); mdp = cmd->mdp; - mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + id = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; + mtk_mutex_unprepare(mdp->mdp_mutex[id]); mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); @@ -310,6 +354,7 @@ static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg) struct cmdq_cb_data *data; struct mdp_dev *mdp; struct device *dev; + int id; if (!mssg) { pr_info("%s:no callback data\n", __func__); @@ -335,7 +380,8 @@ static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg) INIT_WORK(&cmd->auto_release_work, mdp_auto_release_work); if (!queue_work(mdp->clock_wq, &cmd->auto_release_work)) { dev_err(dev, "%s:queue_work fail!\n", __func__); - mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + id = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; + mtk_mutex_unprepare(mdp->mdp_mutex[id]); mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); @@ -356,7 +402,9 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) struct mdp_cmdq_cmd *cmd = NULL; struct mdp_comp *comps = NULL; struct device *dev = &mdp->pdev->dev; + const int p_id = mdp->mdp_data->mdp_plat_id; int i, ret; + u32 num_comp = 0; atomic_inc(&mdp->job_count); if (atomic_read(&mdp->suspended)) { @@ -374,8 +422,13 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) if (ret) goto err_free_cmd; - comps = kcalloc(param->config->num_components, sizeof(*comps), - GFP_KERNEL); + if (CFG_CHECK(MT8183, p_id)) { + num_comp = CFG_GET(MT8183, param->config, num_components); + } else { + ret = -EINVAL; + goto err_destroy_pkt; + } + comps = kcalloc(num_comp, sizeof(*comps), GFP_KERNEL); if (!comps) { ret = -ENOMEM; goto err_destroy_pkt; @@ -387,7 +440,8 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) goto err_free_comps; } - ret = mtk_mutex_prepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + i = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; + ret = mtk_mutex_prepare(mdp->mdp_mutex[i]); if (ret) { dev_err(dev, "Fail to enable mutex clk\n"); goto err_free_path; @@ -406,7 +460,6 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) path->composes[i] = param->composes[i] ? param->composes[i] : &path->bounds[i]; } - ret = mdp_path_ctx_init(mdp, path); if (ret) { dev_err(dev, "mdp_path_ctx_init error\n"); @@ -420,7 +473,7 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) } cmdq_pkt_finalize(&cmd->pkt); - for (i = 0; i < param->config->num_components; i++) + for (i = 0; i < num_comp; i++) memcpy(&comps[i], path->comps[i].comp, sizeof(struct mdp_comp)); @@ -429,7 +482,7 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) cmd->user_cmdq_cb = param->cmdq_cb; cmd->user_cb_data = param->cb_data; cmd->comps = comps; - cmd->num_comps = param->config->num_components; + cmd->num_comps = num_comp; cmd->mdp_ctx = param->mdp_ctx; ret = mdp_comp_clocks_on(&mdp->pdev->dev, cmd->comps, cmd->num_comps); @@ -453,7 +506,8 @@ err_clock_off: mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); err_free_path: - mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + i = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; + mtk_mutex_unprepare(mdp->mdp_mutex[i]); kfree(path); err_free_comps: kfree(comps); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c index 091a68685590..19a4a085f73a 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c @@ -8,6 +8,7 @@ #include <linux/of_platform.h> #include <linux/of_address.h> #include <linux/pm_runtime.h> +#include "mtk-mdp3-cfg.h" #include "mtk-mdp3-comp.h" #include "mtk-mdp3-core.h" #include "mtk-mdp3-regs.h" @@ -19,6 +20,7 @@ #include "mdp_reg_wdma.h" static u32 mdp_comp_alias_id[MDP_COMP_TYPE_COUNT]; +static int p_id; static inline const struct mdp_platform_config * __get_plat_cfg(const struct mdp_comp_ctx *ctx) @@ -32,12 +34,18 @@ __get_plat_cfg(const struct mdp_comp_ctx *ctx) static s64 get_comp_flag(const struct mdp_comp_ctx *ctx) { const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + u32 rdma0, rsz1; + + rdma0 = mdp_cfg_get_id_inner(ctx->comp->mdp_dev, MDP_COMP_RDMA0); + rsz1 = mdp_cfg_get_id_inner(ctx->comp->mdp_dev, MDP_COMP_RSZ1); + if (!rdma0 || !rsz1) + return MDP_COMP_NONE; if (mdp_cfg && mdp_cfg->rdma_rsz1_sram_sharing) - if (ctx->comp->id == MDP_COMP_RDMA0) - return BIT(MDP_COMP_RDMA0) | BIT(MDP_COMP_RSZ1); + if (ctx->comp->inner_id == rdma0) + return BIT(rdma0) | BIT(rsz1); - return BIT(ctx->comp->id); + return BIT(ctx->comp->inner_id); } static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) @@ -45,12 +53,17 @@ static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + s32 rdma0; + + rdma0 = mdp_cfg_get_id_inner(ctx->comp->mdp_dev, MDP_COMP_RDMA0); + if (!rdma0) + return -EINVAL; if (mdp_cfg && mdp_cfg->rdma_support_10bit) { struct mdp_comp *prz1 = ctx->comp->mdp_dev->comp[MDP_COMP_RSZ1]; /* Disable RSZ1 */ - if (ctx->comp->id == MDP_COMP_RDMA0 && prz1) + if (ctx->comp->inner_id == rdma0 && prz1) MM_REG_WRITE(cmd, subsys_id, prz1->reg_base, PRZ_ENABLE, 0x0, BIT(0)); } @@ -66,13 +79,13 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, const struct v4l2_rect *compose) { - const struct mdp_rdma_data *rdma = &ctx->param->rdma; const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); u32 colorformat = ctx->input->buffer.format.colorformat; bool block10bit = MDP_COLOR_IS_10BIT_PACKED(colorformat); bool en_ufo = MDP_COLOR_IS_UFP(colorformat); phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 reg = 0; if (mdp_cfg && mdp_cfg->rdma_support_10bit) { if (block10bit) @@ -90,49 +103,78 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, 0x00030071); /* Setup source frame info */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, rdma->src_ctrl, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.src_ctrl); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg, 0x03C8FE0F); if (mdp_cfg) if (mdp_cfg->rdma_support_10bit && en_ufo) { /* Setup source buffer base */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_y); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, - rdma->ufo_dec_y, 0xFFFFFFFF); + reg, 0xFFFFFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_c); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_UFO_DEC_LENGTH_BASE_C, - rdma->ufo_dec_c, 0xFFFFFFFF); + reg, 0xFFFFFFFF); /* Set 10bit source frame pitch */ - if (block10bit) + if (block10bit) { + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd_in_pxl); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_PXL, - rdma->mf_bkgd_in_pxl, 0x001FFFFF); + reg, 0x001FFFFF); + } } - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, rdma->control, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.control); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, reg, 0x1110); /* Setup source buffer base */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, rdma->iova[0], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, 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_1, rdma->iova[1], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, 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_2, rdma->iova[2], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.iova[2]); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg, 0xFFFFFFFF); /* Setup source buffer end */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[0]); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, - rdma->iova_end[0], 0xFFFFFFFF); + reg, 0xFFFFFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[1]); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, - rdma->iova_end[1], 0xFFFFFFFF); + reg, 0xFFFFFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[2]); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, - rdma->iova_end[2], 0xFFFFFFFF); + reg, 0xFFFFFFFF); /* Setup source frame pitch */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, - rdma->mf_bkgd, 0x001FFFFF); + reg, 0x001FFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.sf_bkgd); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE, - rdma->sf_bkgd, 0x001FFFFF); + reg, 0x001FFFFF); /* Setup color transform */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.transform); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0, - rdma->transform, 0x0F110000); + reg, 0x0F110000); return 0; } @@ -140,47 +182,67 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, u32 index) { - const struct mdp_rdma_subfrm *subfrm = &ctx->param->rdma.subfrms[index]; - const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); u32 colorformat = ctx->input->buffer.format.colorformat; bool block10bit = MDP_COLOR_IS_10BIT_PACKED(colorformat); bool en_ufo = MDP_COLOR_IS_UFP(colorformat); phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 csf_l = 0, csf_r = 0; + u32 reg = 0; /* Enable RDMA */ MM_REG_WRITE(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]); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, - subfrm->offset[0], 0xFFFFFFFF); + reg, 0xFFFFFFFF); /* Set 10bit UFO mode */ - if (mdp_cfg) - if (mdp_cfg->rdma_support_10bit && block10bit && en_ufo) + if (mdp_cfg) { + if (mdp_cfg->rdma_support_10bit && block10bit && en_ufo) { + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset_0_p); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0_P, - subfrm->offset_0_p, 0xFFFFFFFF); + reg, 0xFFFFFFFF); + } + } /* Set U pixel offset */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[1]); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, - subfrm->offset[1], 0xFFFFFFFF); + reg, 0xFFFFFFFF); /* Set V pixel offset */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[2]); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, - subfrm->offset[2], 0xFFFFFFFF); + reg, 0xFFFFFFFF); /* Set source size */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, subfrm->src, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].src); + MM_REG_WRITE(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); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE, - subfrm->clip, 0x1FFF1FFF); + reg, 0x1FFF1FFF); /* Set crop offset */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip_ofst); MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1, - subfrm->clip_ofst, 0x003F001F); + reg, 0x003F001F); + if (CFG_CHECK(MT8183, p_id)) { + csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); + csf_r = CFG_COMP(MT8183, ctx->param, subfrms[index].in.right); + } if (mdp_cfg && mdp_cfg->rdma_upsample_repeat_only) - if ((csf->in.right - csf->in.left + 1) > 320) + if ((csf_r - csf_l + 1) > 320) MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2)); @@ -228,63 +290,97 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, const struct v4l2_rect *compose) { - const struct mdp_rsz_data *rsz = &ctx->param->rsz; phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + bool bypass = FALSE; + u32 reg = 0; - if (ctx->param->frame.bypass) { + if (CFG_CHECK(MT8183, p_id)) + bypass = CFG_COMP(MT8183, ctx->param, frame.bypass); + + if (bypass) { /* Disable RSZ */ MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0)); return 0; } - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, rsz->control1, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rsz.control1); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, reg, 0x03FFFDF3); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, rsz->control2, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rsz.control2); + MM_REG_WRITE(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); MM_REG_WRITE(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, - rsz->coeff_step_x, 0x007FFFFF); + reg, 0x007FFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_y); MM_REG_WRITE(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, - rsz->coeff_step_y, 0x007FFFFF); + reg, 0x007FFFFF); return 0; } static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, u32 index) { - const struct mdp_rsz_subfrm *subfrm = &ctx->param->rsz.subfrms[index]; - const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 csf_l = 0, csf_r = 0; + u32 reg = 0; - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, subfrm->control2, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].control2); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x00003800); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, subfrm->src, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].src); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg, 0xFFFFFFFF); + if (CFG_CHECK(MT8183, p_id)) { + csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); + csf_r = CFG_COMP(MT8183, ctx->param, subfrms[index].in.right); + } if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample) - if ((csf->in.right - csf->in.left + 1) <= 16) + if ((csf_r - csf_l + 1) <= 16) MM_REG_WRITE(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); MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET, - csf->luma.left, 0xFFFF); + reg, 0xFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left_subpix); MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, - csf->luma.left_subpix, 0x1FFFFF); + reg, 0x1FFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top); MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET, - csf->luma.top, 0xFFFF); + reg, 0xFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top_subpix); MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, - csf->luma.top_subpix, 0x1FFFFF); + reg, 0x1FFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left); MM_REG_WRITE(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET, - csf->chroma.left, 0xFFFF); + reg, 0xFFFF); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left_subpix); MM_REG_WRITE(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET, - csf->chroma.left_subpix, 0x1FFFFF); + reg, 0x1FFFFF); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, subfrm->clip, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].clip); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg, 0xFFFFFFFF); return 0; @@ -296,11 +392,16 @@ static int advance_rsz_subfrm(struct mdp_comp_ctx *ctx, const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample) { - const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 csf_l = 0, csf_r = 0; + + if (CFG_CHECK(MT8183, p_id)) { + csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); + csf_r = CFG_COMP(MT8183, ctx->param, subfrms[index].in.right); + } - if ((csf->in.right - csf->in.left + 1) <= 16) + if ((csf_r - csf_l + 1) <= 16) MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0, BIT(27)); } @@ -333,31 +434,47 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, const struct v4l2_rect *compose) { - const struct mdp_wrot_data *wrot = &ctx->param->wrot; const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 reg = 0; /* Write frame base address */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, wrot->iova[0], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, 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_C, wrot->iova[1], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, 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_V, wrot->iova[2], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.iova[2]); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg, 0xFFFFFFFF); /* Write frame related registers */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, wrot->control, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.control); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, reg, 0xF131510F); /* Write frame Y pitch */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, wrot->stride[0], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.stride[0]); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, reg, 0x0000FFFF); /* Write frame UV pitch */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, wrot->stride[1], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.stride[1]); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, reg, 0xFFFF); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, wrot->stride[2], + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.stride[2]); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, reg, 0xFFFF); /* Write matrix control */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAT_CTRL, wrot->mat_ctrl, 0xF3); + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.mat_ctrl); + MM_REG_WRITE(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, @@ -365,13 +482,18 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, /* Set VIDO_EOL_SEL */ MM_REG_WRITE(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31)); /* Set VIDO_FIFO_TEST */ - if (wrot->fifo_test != 0) + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.fifo_test); + if (reg != 0) MM_REG_WRITE(cmd, subsys_id, base, VIDO_FIFO_TEST, - wrot->fifo_test, 0xFFF); + reg, 0xFFF); /* Filter enable */ - if (mdp_cfg && mdp_cfg->wrot_filter_constraint) + if (mdp_cfg && mdp_cfg->wrot_filter_constraint) { + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.filter); MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, - wrot->filter, 0x77); + reg, 0x77); + } return 0; } @@ -379,30 +501,44 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, static int config_wrot_subfrm(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, u32 index) { - const struct mdp_wrot_subfrm *subfrm = &ctx->param->wrot.subfrms[index]; phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 reg = 0; /* Write Y pixel offset */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[0]); MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR, - subfrm->offset[0], 0x0FFFFFFF); + reg, 0x0FFFFFFF); /* Write U pixel offset */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[1]); MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_C, - subfrm->offset[1], 0x0FFFFFFF); + reg, 0x0FFFFFFF); /* Write V pixel offset */ + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[2]); MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_V, - subfrm->offset[2], 0x0FFFFFFF); + reg, 0x0FFFFFFF); /* Write source size */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, subfrm->src, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].src); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, reg, 0x1FFF1FFF); /* Write target size */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, subfrm->clip, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, reg, 0x1FFF1FFF); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CROP_OFST, subfrm->clip_ofst, + if (CFG_CHECK(MT8183, p_id)) + reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip_ofst); + MM_REG_WRITE(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); MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, - subfrm->main_buf, 0x1FFF7F00); + reg, 0x1FFF7F00); /* Enable WROT */ MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0)); @@ -456,29 +592,41 @@ static int config_wdma_frame(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, const struct v4l2_rect *compose) { - const struct mdp_wdma_data *wdma = &ctx->param->wdma; phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 reg = 0; MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050, 0xFFFFFFFF); /* Setup frame information */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CFG, wdma->wdma_cfg, + 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); /* Setup frame base address */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, wdma->iova[0], + 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_U_ADDR, wdma->iova[1], + 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_V_ADDR, wdma->iova[2], + 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); /* 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, - wdma->w_in_byte, 0x0000FFFF); + 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, - wdma->uv_stride, 0x0000FFFF); + reg, 0x0000FFFF); /* Set the fixed ALPHA as 0xFF */ MM_REG_WRITE(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF, 0x800000FF); @@ -489,27 +637,39 @@ static int config_wdma_frame(struct mdp_comp_ctx *ctx, static int config_wdma_subfrm(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, u32 index) { - const struct mdp_wdma_subfrm *subfrm = &ctx->param->wdma.subfrms[index]; phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 reg = 0; /* 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, - subfrm->offset[0], 0x0FFFFFFF); + 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, - subfrm->offset[1], 0x0FFFFFFF); + 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, - subfrm->offset[2], 0x0FFFFFFF); + reg, 0x0FFFFFFF); /* Write source size */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_SRC_SIZE, subfrm->src, + 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); /* Write target size */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_SIZE, subfrm->clip, + 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); /* Write clip offset */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_COORD, subfrm->clip_ofst, + 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); /* Enable WDMA */ @@ -552,13 +712,21 @@ static int init_ccorr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) static int config_ccorr_subfrm(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, u32 index) { - const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; phys_addr_t base = ctx->comp->reg_base; u8 subsys_id = ctx->comp->subsys_id; + u32 csf_l = 0, csf_r = 0; + u32 csf_t = 0, csf_b = 0; u32 hsize, vsize; - hsize = csf->in.right - csf->in.left + 1; - vsize = csf->in.bottom - csf->in.top + 1; + if (CFG_CHECK(MT8183, p_id)) { + csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); + csf_r = CFG_COMP(MT8183, ctx->param, subfrms[index].in.right); + csf_t = CFG_COMP(MT8183, ctx->param, subfrms[index].in.top); + csf_b = CFG_COMP(MT8183, ctx->param, subfrms[index].in.bottom); + } + + 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); return 0; @@ -578,32 +746,6 @@ static const struct mdp_comp_ops *mdp_comp_ops[MDP_COMP_TYPE_COUNT] = { [MDP_COMP_TYPE_CCORR] = &ccorr_ops, }; -struct mdp_comp_match { - enum mdp_comp_type type; - u32 alias_id; -}; - -static const struct mdp_comp_match mdp_comp_matches[MDP_MAX_COMP_COUNT] = { - [MDP_COMP_WPEI] = { MDP_COMP_TYPE_WPEI, 0 }, - [MDP_COMP_WPEO] = { MDP_COMP_TYPE_EXTO, 2 }, - [MDP_COMP_WPEI2] = { MDP_COMP_TYPE_WPEI, 1 }, - [MDP_COMP_WPEO2] = { MDP_COMP_TYPE_EXTO, 3 }, - [MDP_COMP_ISP_IMGI] = { MDP_COMP_TYPE_IMGI, 0 }, - [MDP_COMP_ISP_IMGO] = { MDP_COMP_TYPE_EXTO, 0 }, - [MDP_COMP_ISP_IMG2O] = { MDP_COMP_TYPE_EXTO, 1 }, - - [MDP_COMP_CAMIN] = { MDP_COMP_TYPE_DL_PATH, 0 }, - [MDP_COMP_CAMIN2] = { MDP_COMP_TYPE_DL_PATH, 1 }, - [MDP_COMP_RDMA0] = { MDP_COMP_TYPE_RDMA, 0 }, - [MDP_COMP_CCORR0] = { MDP_COMP_TYPE_CCORR, 0 }, - [MDP_COMP_RSZ0] = { MDP_COMP_TYPE_RSZ, 0 }, - [MDP_COMP_RSZ1] = { MDP_COMP_TYPE_RSZ, 1 }, - [MDP_COMP_PATH0_SOUT] = { MDP_COMP_TYPE_PATH, 0 }, - [MDP_COMP_PATH1_SOUT] = { MDP_COMP_TYPE_PATH, 1 }, - [MDP_COMP_WROT0] = { MDP_COMP_TYPE_WROT, 0 }, - [MDP_COMP_WDMA] = { MDP_COMP_TYPE_WDMA, 0 }, -}; - static const struct of_device_id mdp_comp_dt_ids[] = { { .compatible = "mediatek,mt8183-mdp3-rdma", @@ -624,32 +766,6 @@ static const struct of_device_id mdp_comp_dt_ids[] = { {} }; -static const struct of_device_id mdp_sub_comp_dt_ids[] = { - { - .compatible = "mediatek,mt8183-mdp3-wdma", - .data = (void *)MDP_COMP_TYPE_PATH, - }, { - .compatible = "mediatek,mt8183-mdp3-wrot", - .data = (void *)MDP_COMP_TYPE_PATH, - }, - {} -}; - -/* Used to describe the item order in MDP property */ -struct mdp_comp_info { - u32 clk_num; - u32 clk_ofst; - u32 dts_reg_ofst; -}; - -static const struct mdp_comp_info mdp_comp_dt_info[MDP_MAX_COMP_COUNT] = { - [MDP_COMP_RDMA0] = {2, 0, 0}, - [MDP_COMP_RSZ0] = {1, 0, 0}, - [MDP_COMP_WROT0] = {1, 0, 0}, - [MDP_COMP_WDMA] = {1, 0, 0}, - [MDP_COMP_CCORR0] = {1, 0, 0}, -}; - static inline bool is_dma_capable(const enum mdp_comp_type type) { return (type == MDP_COMP_TYPE_RDMA || @@ -666,13 +782,13 @@ static inline bool is_bypass_gce_event(const enum mdp_comp_type type) return (type == MDP_COMP_TYPE_PATH); } -static int mdp_comp_get_id(enum mdp_comp_type type, int alias_id) +static int mdp_comp_get_id(struct mdp_dev *mdp, enum mdp_comp_type type, u32 alias_id) { int i; - for (i = 0; i < ARRAY_SIZE(mdp_comp_matches); i++) - if (mdp_comp_matches[i].type == type && - mdp_comp_matches[i].alias_id == alias_id) + for (i = 0; i < mdp->mdp_data->comp_data_len; i++) + if (mdp->mdp_data->comp_data[i].match.type == type && + mdp->mdp_data->comp_data[i].match.alias_id == alias_id) return i; return -ENODEV; } @@ -681,24 +797,25 @@ int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp) { int i, ret; - if (comp->comp_dev) { + /* Only DMA capable components need the pm control */ + if (comp->comp_dev && is_dma_capable(comp->type)) { ret = pm_runtime_resume_and_get(comp->comp_dev); if (ret < 0) { dev_err(dev, "Failed to get power, err %d. type:%d id:%d\n", - ret, comp->type, comp->id); + ret, comp->type, comp->inner_id); return ret; } } - for (i = 0; i < ARRAY_SIZE(comp->clks); i++) { + for (i = 0; i < comp->clk_num; i++) { if (IS_ERR_OR_NULL(comp->clks[i])) continue; ret = clk_prepare_enable(comp->clks[i]); if (ret) { dev_err(dev, "Failed to enable clk %d. type:%d id:%d\n", - i, comp->type, comp->id); + i, comp->type, comp->inner_id); goto err_revert; } } @@ -711,7 +828,7 @@ err_revert: continue; clk_disable_unprepare(comp->clks[i]); } - if (comp->comp_dev) + if (comp->comp_dev && is_dma_capable(comp->type)) pm_runtime_put_sync(comp->comp_dev); return ret; @@ -721,13 +838,13 @@ void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp) { int i; - for (i = 0; i < ARRAY_SIZE(comp->clks); i++) { + for (i = 0; i < comp->clk_num; i++) { if (IS_ERR_OR_NULL(comp->clks[i])) continue; clk_disable_unprepare(comp->clks[i]); } - if (comp->comp_dev) + if (comp->comp_dev && is_dma_capable(comp->type)) pm_runtime_put(comp->comp_dev); } @@ -752,8 +869,8 @@ void mdp_comp_clocks_off(struct device *dev, struct mdp_comp *comps, int num) mdp_comp_clock_off(dev, &comps[i]); } -static int mdp_get_subsys_id(struct device *dev, struct device_node *node, - struct mdp_comp *comp) +static int mdp_get_subsys_id(struct mdp_dev *mdp, struct device *dev, + struct device_node *node, struct mdp_comp *comp) { struct platform_device *comp_pdev; struct cmdq_client_reg cmdq_reg; @@ -766,12 +883,12 @@ static int mdp_get_subsys_id(struct device *dev, struct device_node *node, comp_pdev = of_find_device_by_node(node); if (!comp_pdev) { - dev_err(dev, "get comp_pdev fail! comp id=%d type=%d\n", - comp->id, comp->type); + dev_err(dev, "get comp_pdev fail! comp public id=%d, inner id=%d, type=%d\n", + comp->public_id, comp->inner_id, comp->type); return -ENODEV; } - index = mdp_comp_dt_info[comp->id].dts_reg_ofst; + index = mdp->mdp_data->comp_data[comp->public_id].info.dts_reg_ofst; ret = cmdq_dev_get_client_reg(&comp_pdev->dev, &cmdq_reg, index); if (ret != 0) { dev_err(&comp_pdev->dev, "cmdq_dev_get_subsys fail!\n"); @@ -789,8 +906,9 @@ static void __mdp_comp_init(struct mdp_dev *mdp, struct device_node *node, { struct resource res; phys_addr_t base; - int index = mdp_comp_dt_info[comp->id].dts_reg_ofst; + int index; + index = mdp->mdp_data->comp_data[comp->public_id].info.dts_reg_ofst; if (of_address_to_resource(node, index, &res) < 0) base = 0L; else @@ -805,7 +923,7 @@ static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node, struct mdp_comp *comp, enum mtk_mdp_comp_id id) { struct device *dev = &mdp->pdev->dev; - int clk_num; + struct platform_device *pdev_c; int clk_ofst; int i; s32 event; @@ -815,22 +933,36 @@ static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node, return -EINVAL; } - comp->id = id; - comp->type = mdp_comp_matches[id].type; - comp->alias_id = mdp_comp_matches[id].alias_id; + pdev_c = of_find_device_by_node(node); + if (!pdev_c) { + dev_warn(dev, "can't find platform device of node:%s\n", + node->name); + return -ENODEV; + } + + comp->comp_dev = &pdev_c->dev; + comp->public_id = id; + comp->type = mdp->mdp_data->comp_data[id].match.type; + comp->inner_id = mdp->mdp_data->comp_data[id].match.inner_id; + comp->alias_id = mdp->mdp_data->comp_data[id].match.alias_id; comp->ops = mdp_comp_ops[comp->type]; __mdp_comp_init(mdp, node, comp); - clk_num = mdp_comp_dt_info[id].clk_num; - clk_ofst = mdp_comp_dt_info[id].clk_ofst; + comp->clk_num = mdp->mdp_data->comp_data[id].info.clk_num; + comp->clks = devm_kzalloc(dev, sizeof(struct clk *) * comp->clk_num, + GFP_KERNEL); + if (!comp->clks) + return -ENOMEM; - for (i = 0; i < clk_num; i++) { + clk_ofst = mdp->mdp_data->comp_data[id].info.clk_ofst; + + for (i = 0; i < comp->clk_num; i++) { comp->clks[i] = of_clk_get(node, i + clk_ofst); if (IS_ERR(comp->clks[i])) break; } - mdp_get_subsys_id(dev, node, comp); + mdp_get_subsys_id(mdp, dev, node, comp); /* Set GCE SOF event */ if (is_bypass_gce_event(comp->type) || @@ -861,6 +993,11 @@ static void mdp_comp_deinit(struct mdp_comp *comp) if (!comp) return; + if (comp->comp_dev && comp->clks) { + devm_kfree(&comp->mdp_dev->pdev->dev, comp->clks); + comp->clks = NULL; + } + if (comp->regs) iounmap(comp->regs); } @@ -888,8 +1025,8 @@ static struct mdp_comp *mdp_comp_create(struct mdp_dev *mdp, mdp->comp[id] = comp; mdp->comp[id]->mdp_dev = mdp; - dev_dbg(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n", - dev->of_node->name, comp->type, comp->alias_id, id, + dev_dbg(dev, "%s type:%d alias:%d public id:%d inner id:%d base:%#x regs:%p\n", + dev->of_node->name, comp->type, comp->alias_id, id, comp->inner_id, (u32)comp->reg_base, comp->regs); return comp; } @@ -907,7 +1044,7 @@ static int mdp_comp_sub_create(struct mdp_dev *mdp) int id, alias_id; struct mdp_comp *comp; - of_id = of_match_node(mdp_sub_comp_dt_ids, node); + of_id = of_match_node(mdp->mdp_data->mdp_sub_comp_dt_ids, node); if (!of_id) continue; if (!of_device_is_available(node)) { @@ -918,7 +1055,7 @@ static int mdp_comp_sub_create(struct mdp_dev *mdp) type = (enum mdp_comp_type)(uintptr_t)of_id->data; alias_id = mdp_comp_alias_id[type]; - id = mdp_comp_get_id(type, alias_id); + id = mdp_comp_get_id(mdp, type, alias_id); if (id < 0) { dev_err(dev, "Fail to get sub comp. id: type %d alias %d\n", @@ -941,7 +1078,8 @@ void mdp_comp_destroy(struct mdp_dev *mdp) for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) { if (mdp->comp[i]) { - pm_runtime_disable(mdp->comp[i]->comp_dev); + if (is_dma_capable(mdp->comp[i]->type)) + pm_runtime_disable(mdp->comp[i]->comp_dev); mdp_comp_deinit(mdp->comp[i]); devm_kfree(mdp->comp[i]->comp_dev, mdp->comp[i]); mdp->comp[i] = NULL; @@ -953,10 +1091,10 @@ int mdp_comp_config(struct mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; struct device_node *node, *parent; - struct platform_device *pdev; int ret; memset(mdp_comp_alias_id, 0, sizeof(mdp_comp_alias_id)); + p_id = mdp->mdp_data->mdp_plat_id; parent = dev->of_node->parent; /* Iterate over sibling MDP function blocks */ @@ -978,7 +1116,7 @@ int mdp_comp_config(struct mdp_dev *mdp) type = (enum mdp_comp_type)(uintptr_t)of_id->data; alias_id = mdp_comp_alias_id[type]; - id = mdp_comp_get_id(type, alias_id); + id = mdp_comp_get_id(mdp, type, alias_id); if (id < 0) { dev_err(dev, "Fail to get component id: type %d alias %d\n", @@ -994,19 +1132,8 @@ int mdp_comp_config(struct mdp_dev *mdp) } /* Only DMA capable components need the pm control */ - comp->comp_dev = NULL; if (!is_dma_capable(comp->type)) continue; - - pdev = of_find_device_by_node(node); - if (!pdev) { - dev_warn(dev, "can't find platform device of node:%s\n", - node->name); - ret = -ENODEV; - goto err_init_comps; - } - - comp->comp_dev = &pdev->dev; pm_runtime_enable(comp->comp_dev); } @@ -1026,22 +1153,47 @@ int mdp_comp_ctx_config(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx, const struct img_ipi_frameparam *frame) { struct device *dev = &mdp->pdev->dev; - int i; + enum mtk_mdp_comp_id public_id = MDP_COMP_NONE; + u32 arg; + int i, idx; - if (param->type < 0 || param->type >= MDP_MAX_COMP_COUNT) { - dev_err(dev, "Invalid component id %d", param->type); + if (!param) { + dev_err(dev, "Invalid component param"); return -EINVAL; } - ctx->comp = mdp->comp[param->type]; + if (CFG_CHECK(MT8183, p_id)) + arg = CFG_COMP(MT8183, param, type); + else + return -EINVAL; + public_id = mdp_cfg_get_id_public(mdp, arg); + if (public_id < 0) { + dev_err(dev, "Invalid component id %d", public_id); + return -EINVAL; + } + + ctx->comp = mdp->comp[public_id]; if (!ctx->comp) { - dev_err(dev, "Uninit component id %d", param->type); + dev_err(dev, "Uninit component inner id %d", arg); return -EINVAL; } ctx->param = param; - ctx->input = &frame->inputs[param->input]; - for (i = 0; i < param->num_outputs; i++) - ctx->outputs[i] = &frame->outputs[param->outputs[i]]; + if (CFG_CHECK(MT8183, p_id)) + arg = CFG_COMP(MT8183, param, input); + else + return -EINVAL; + ctx->input = &frame->inputs[arg]; + if (CFG_CHECK(MT8183, p_id)) + idx = CFG_COMP(MT8183, param, num_outputs); + else + return -EINVAL; + for (i = 0; i < idx; i++) { + if (CFG_CHECK(MT8183, p_id)) + arg = CFG_COMP(MT8183, param, outputs[i]); + else + return -EINVAL; + ctx->outputs[i] = &frame->outputs[arg]; + } 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 dc48f55ac4f7..20d2bcb77ef9 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h @@ -134,6 +134,24 @@ enum { MDP_GCE_EVENT_MAX, }; +struct mdp_comp_match { + enum mdp_comp_type type; + u32 alias_id; + s32 inner_id; +}; + +/* Used to describe the item order in MDP property */ +struct mdp_comp_info { + u32 clk_num; + u32 clk_ofst; + u32 dts_reg_ofst; +}; + +struct mdp_comp_data { + struct mdp_comp_match match; + struct mdp_comp_info info; +}; + struct mdp_comp_ops; struct mdp_comp { @@ -141,10 +159,12 @@ struct mdp_comp { void __iomem *regs; phys_addr_t reg_base; u8 subsys_id; - struct clk *clks[6]; + u8 clk_num; + struct clk **clks; struct device *comp_dev; enum mdp_comp_type type; - enum mtk_mdp_comp_id id; + enum mtk_mdp_comp_id public_id; + s32 inner_id; u32 alias_id; s32 gce_event[MDP_GCE_EVENT_MAX]; const struct mdp_comp_ops *ops; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 97edcd9d1c81..aa6c225302f0 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -12,40 +12,11 @@ #include <linux/remoteproc.h> #include <linux/remoteproc/mtk_scp.h> #include <media/videobuf2-dma-contig.h> + #include "mtk-mdp3-core.h" +#include "mtk-mdp3-cfg.h" #include "mtk-mdp3-m2m.h" -static const struct mdp_platform_config mt8183_plat_cfg = { - .rdma_support_10bit = true, - .rdma_rsz1_sram_sharing = true, - .rdma_upsample_repeat_only = true, - .rsz_disable_dcm_small_sample = false, - .wrot_filter_constraint = false, -}; - -static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = { - [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8183-mmsys" }, - [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8183-disp-mutex" }, - [MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" } -}; - -static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = { - [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, - [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0, - [MDP_COMP_RSZ1] = MUTEX_MOD_IDX_MDP_RSZ1, - [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0, - [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0, - [MDP_COMP_WDMA] = MUTEX_MOD_IDX_MDP_WDMA, - [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0, - [MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0, -}; - -static const struct mtk_mdp_driver_data mt8183_mdp_driver_data = { - .mdp_probe_infra = mt8183_mdp_probe_infra, - .mdp_cfg = &mt8183_plat_cfg, - .mdp_mutex_table_idx = mt8183_mutex_idx, -}; - static const struct of_device_id mdp_of_ids[] = { { .compatible = "mediatek,mt8183-mdp3-rdma", .data = &mt8183_mdp_driver_data, @@ -182,7 +153,7 @@ static int mdp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mdp_dev *mdp; struct platform_device *mm_pdev; - int ret, i; + int ret, i, mutex_id; mdp = kzalloc(sizeof(*mdp), GFP_KERNEL); if (!mdp) { @@ -205,10 +176,13 @@ static int mdp_probe(struct platform_device *pdev) ret = -ENODEV; goto err_destroy_device; } - for (i = 0; i < MDP_PIPE_MAX; i++) { - mdp->mdp_mutex[i] = mtk_mutex_get(&mm_pdev->dev); - if (IS_ERR(mdp->mdp_mutex[i])) { - ret = PTR_ERR(mdp->mdp_mutex[i]); + for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) { + mutex_id = mdp->mdp_data->pipe_info[i].mutex_id; + if (!IS_ERR_OR_NULL(mdp->mdp_mutex[mutex_id])) + continue; + mdp->mdp_mutex[mutex_id] = mtk_mutex_get(&mm_pdev->dev); + if (IS_ERR(mdp->mdp_mutex[mutex_id])) { + ret = PTR_ERR(mdp->mdp_mutex[mutex_id]); goto err_free_mutex; } } @@ -288,7 +262,7 @@ err_destroy_job_wq: err_deinit_comp: mdp_comp_destroy(mdp); err_free_mutex: - for (i = 0; i < MDP_PIPE_MAX; i++) + for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) if (!IS_ERR_OR_NULL(mdp->mdp_mutex[i])) mtk_mutex_put(mdp->mdp_mutex[i]); err_destroy_device: @@ -298,14 +272,13 @@ err_return: return ret; } -static int mdp_remove(struct platform_device *pdev) +static void mdp_remove(struct platform_device *pdev) { struct mdp_dev *mdp = platform_get_drvdata(pdev); v4l2_device_unregister(&mdp->v4l2_dev); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); - return 0; } static int __maybe_unused mdp_suspend(struct device *dev) @@ -345,7 +318,7 @@ static const struct dev_pm_ops mdp_pm_ops = { static struct platform_driver mdp_driver = { .probe = mdp_probe, - .remove = mdp_remove, + .remove_new = 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 2ef5fbc4f25a..7e21d226ceb8 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h @@ -43,17 +43,26 @@ struct mdp_platform_config { /* indicate which mutex is used by each pipepline */ enum mdp_pipe_id { - MDP_PIPE_RDMA0, - MDP_PIPE_IMGI, MDP_PIPE_WPEI, MDP_PIPE_WPEI2, + MDP_PIPE_IMGI, + MDP_PIPE_RDMA0, MDP_PIPE_MAX }; struct mtk_mdp_driver_data { + const int mdp_plat_id; const struct of_device_id *mdp_probe_infra; const struct mdp_platform_config *mdp_cfg; const u32 *mdp_mutex_table_idx; + const struct mdp_comp_data *comp_data; + unsigned int comp_data_len; + const struct of_device_id *mdp_sub_comp_dt_ids; + const struct mdp_format *format; + unsigned int format_len; + const struct mdp_limit *def_limit; + const struct mdp_pipe_info *pipe_info; + unsigned int pipe_info_len; }; struct mdp_dev { @@ -85,6 +94,11 @@ struct mdp_dev { atomic_t job_count; }; +struct mdp_pipe_info { + enum mdp_pipe_id pipe_id; + u32 mutex_id; +}; + int mdp_vpu_get_locked(struct mdp_dev *mdp); void mdp_vpu_put_locked(struct mdp_dev *mdp); int mdp_vpu_register(struct mdp_dev *mdp); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c index 5f74ea3b7a52..a298c1b15b9e 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c @@ -87,14 +87,14 @@ static void mdp_m2m_device_run(void *priv) dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); mdp_set_dst_config(¶m.outputs[0], frame, &dst_vb->vb2_buf); - ret = mdp_vpu_process(&ctx->vpu, ¶m); + ret = mdp_vpu_process(&ctx->mdp_dev->vpu, ¶m); if (ret) { dev_err(&ctx->mdp_dev->pdev->dev, "VPU MDP process failed: %d\n", ret); goto worker_end; } - task.config = ctx->vpu.config; + task.config = ctx->mdp_dev->vpu.config; task.param = ¶m; task.composes[0] = &frame->compose; task.cmdq_cb = NULL; @@ -150,11 +150,6 @@ static int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count) if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) { ret = mdp_vpu_get_locked(ctx->mdp_dev); - if (ret) - return ret; - - ret = mdp_vpu_ctx_init(&ctx->vpu, &ctx->mdp_dev->vpu, - MDP_DEV_M2M); if (ret) { dev_err(&ctx->mdp_dev->pdev->dev, "VPU init failed %d\n", ret); @@ -277,7 +272,9 @@ static int mdp_m2m_querycap(struct file *file, void *fh, static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh, struct v4l2_fmtdesc *f) { - return mdp_enum_fmt_mplane(f); + struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); + + return mdp_enum_fmt_mplane(ctx->mdp_dev, f); } static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh, @@ -307,7 +304,7 @@ static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh, const struct mdp_format *fmt; struct vb2_queue *vq; - fmt = mdp_try_fmt_mplane(f, &ctx->curr_param, ctx->id); + fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id); if (!fmt) return -EINVAL; @@ -346,7 +343,7 @@ static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh, { struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); - if (!mdp_try_fmt_mplane(f, &ctx->curr_param, ctx->id)) + if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id)) return -EINVAL; return 0; @@ -556,6 +553,7 @@ static int mdp_m2m_open(struct file *file) struct device *dev = &mdp->pdev->dev; int ret; struct v4l2_format default_format = {}; + const struct mdp_limit *limit = mdp->mdp_data->def_limit; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -566,7 +564,11 @@ static int mdp_m2m_open(struct file *file) goto err_free_ctx; } - ctx->id = ida_alloc(&mdp->mdp_ida, GFP_KERNEL); + ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL); + if (ret < 0) + goto err_unlock_mutex; + ctx->id = ret; + ctx->mdp_dev = mdp; v4l2_fh_init(&ctx->fh, vdev); @@ -589,7 +591,7 @@ static int mdp_m2m_open(struct file *file) ctx->fh.m2m_ctx = ctx->m2m_ctx; ctx->curr_param.ctx = ctx; - ret = mdp_frameparam_init(&ctx->curr_param); + ret = mdp_frameparam_init(mdp, &ctx->curr_param); if (ret) { dev_err(dev, "Failed to initialize mdp parameter\n"); goto err_release_m2m_ctx; @@ -599,8 +601,8 @@ static int mdp_m2m_open(struct file *file) /* Default format */ default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - default_format.fmt.pix_mp.width = 32; - default_format.fmt.pix_mp.height = 32; + default_format.fmt.pix_mp.width = limit->out_limit.wmin; + default_format.fmt.pix_mp.height = limit->out_limit.hmin; default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -617,6 +619,8 @@ err_release_handler: v4l2_fh_del(&ctx->fh); err_exit_fh: v4l2_fh_exit(&ctx->fh); + ida_free(&mdp->mdp_ida, ctx->id); +err_unlock_mutex: mutex_unlock(&mdp->m2m_lock); err_free_ctx: kfree(ctx); @@ -632,10 +636,8 @@ static int mdp_m2m_release(struct file *file) mutex_lock(&mdp->m2m_lock); v4l2_m2m_ctx_release(ctx->m2m_ctx); - if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) { - mdp_vpu_ctx_deinit(&ctx->vpu); + if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) mdp_vpu_put_locked(mdp); - } v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h index 61ddbaf1bf13..dfc59e5b3b87 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h @@ -33,7 +33,6 @@ struct mdp_m2m_ctx { struct v4l2_ctrl_handler ctrl_handler; struct mdp_m2m_ctrls ctrls; struct v4l2_m2m_ctx *m2m_ctx; - struct mdp_vpu_ctx vpu; u32 frame_count[MDP_M2M_MAX]; struct mdp_frameparam curr_param; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c index 4e84a37ecdfc..9b436b911d92 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c @@ -4,6 +4,7 @@ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com> */ +#include <linux/math64.h> #include <media/v4l2-common.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> @@ -11,276 +12,34 @@ #include "mtk-mdp3-regs.h" #include "mtk-mdp3-m2m.h" -/* - * All 10-bit related formats are not added in the basic format list, - * please add the corresponding format settings before use. - */ -static const struct mdp_format mdp_formats[] = { - { - .pixelformat = V4L2_PIX_FMT_GREY, - .mdp_color = MDP_COLOR_GREY, - .depth = { 8 }, - .row_depth = { 8 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_RGB565X, - .mdp_color = MDP_COLOR_BGR565, - .depth = { 16 }, - .row_depth = { 16 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_RGB565, - .mdp_color = MDP_COLOR_RGB565, - .depth = { 16 }, - .row_depth = { 16 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_RGB24, - .mdp_color = MDP_COLOR_RGB888, - .depth = { 24 }, - .row_depth = { 24 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_BGR24, - .mdp_color = MDP_COLOR_BGR888, - .depth = { 24 }, - .row_depth = { 24 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_ABGR32, - .mdp_color = MDP_COLOR_BGRA8888, - .depth = { 32 }, - .row_depth = { 32 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_ARGB32, - .mdp_color = MDP_COLOR_ARGB8888, - .depth = { 32 }, - .row_depth = { 32 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_UYVY, - .mdp_color = MDP_COLOR_UYVY, - .depth = { 16 }, - .row_depth = { 16 }, - .num_planes = 1, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_VYUY, - .mdp_color = MDP_COLOR_VYUY, - .depth = { 16 }, - .row_depth = { 16 }, - .num_planes = 1, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_YUYV, - .mdp_color = MDP_COLOR_YUYV, - .depth = { 16 }, - .row_depth = { 16 }, - .num_planes = 1, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_YVYU, - .mdp_color = MDP_COLOR_YVYU, - .depth = { 16 }, - .row_depth = { 16 }, - .num_planes = 1, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_YUV420, - .mdp_color = MDP_COLOR_I420, - .depth = { 12 }, - .row_depth = { 8 }, - .num_planes = 1, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_YVU420, - .mdp_color = MDP_COLOR_YV12, - .depth = { 12 }, - .row_depth = { 8 }, - .num_planes = 1, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_NV12, - .mdp_color = MDP_COLOR_NV12, - .depth = { 12 }, - .row_depth = { 8 }, - .num_planes = 1, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_NV21, - .mdp_color = MDP_COLOR_NV21, - .depth = { 12 }, - .row_depth = { 8 }, - .num_planes = 1, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_NV16, - .mdp_color = MDP_COLOR_NV16, - .depth = { 16 }, - .row_depth = { 8 }, - .num_planes = 1, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_NV61, - .mdp_color = MDP_COLOR_NV61, - .depth = { 16 }, - .row_depth = { 8 }, - .num_planes = 1, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_NV24, - .mdp_color = MDP_COLOR_NV24, - .depth = { 24 }, - .row_depth = { 8 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_NV42, - .mdp_color = MDP_COLOR_NV42, - .depth = { 24 }, - .row_depth = { 8 }, - .num_planes = 1, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_MT21C, - .mdp_color = MDP_COLOR_420_BLK_UFO, - .depth = { 8, 4 }, - .row_depth = { 8, 8 }, - .num_planes = 2, - .walign = 4, - .halign = 5, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_MM21, - .mdp_color = MDP_COLOR_420_BLK, - .depth = { 8, 4 }, - .row_depth = { 8, 8 }, - .num_planes = 2, - .walign = 4, - .halign = 5, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_NV12M, - .mdp_color = MDP_COLOR_NV12, - .depth = { 8, 4 }, - .row_depth = { 8, 8 }, - .num_planes = 2, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_NV21M, - .mdp_color = MDP_COLOR_NV21, - .depth = { 8, 4 }, - .row_depth = { 8, 8 }, - .num_planes = 2, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_NV16M, - .mdp_color = MDP_COLOR_NV16, - .depth = { 8, 8 }, - .row_depth = { 8, 8 }, - .num_planes = 2, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_NV61M, - .mdp_color = MDP_COLOR_NV61, - .depth = { 8, 8 }, - .row_depth = { 8, 8 }, - .num_planes = 2, - .walign = 1, - .flags = MDP_FMT_FLAG_OUTPUT, - }, { - .pixelformat = V4L2_PIX_FMT_YUV420M, - .mdp_color = MDP_COLOR_I420, - .depth = { 8, 2, 2 }, - .row_depth = { 8, 4, 4 }, - .num_planes = 3, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - }, { - .pixelformat = V4L2_PIX_FMT_YVU420M, - .mdp_color = MDP_COLOR_YV12, - .depth = { 8, 2, 2 }, - .row_depth = { 8, 4, 4 }, - .num_planes = 3, - .walign = 1, - .halign = 1, - .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, - } -}; - -static const struct mdp_limit mdp_def_limit = { - .out_limit = { - .wmin = 16, - .hmin = 16, - .wmax = 8176, - .hmax = 8176, - }, - .cap_limit = { - .wmin = 2, - .hmin = 2, - .wmax = 8176, - .hmax = 8176, - }, - .h_scale_up_max = 32, - .v_scale_up_max = 32, - .h_scale_down_max = 20, - .v_scale_down_max = 128, -}; - -static const struct mdp_format *mdp_find_fmt(u32 pixelformat, u32 type) +static const struct mdp_format *mdp_find_fmt(const struct mtk_mdp_driver_data *mdp_data, + u32 pixelformat, u32 type) { u32 i, flag; flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT : MDP_FMT_FLAG_CAPTURE; - for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) { - if (!(mdp_formats[i].flags & flag)) + for (i = 0; i < mdp_data->format_len; ++i) { + if (!(mdp_data->format[i].flags & flag)) continue; - if (mdp_formats[i].pixelformat == pixelformat) - return &mdp_formats[i]; + if (mdp_data->format[i].pixelformat == pixelformat) + return &mdp_data->format[i]; } return NULL; } -static const struct mdp_format *mdp_find_fmt_by_index(u32 index, u32 type) +static const struct mdp_format *mdp_find_fmt_by_index(const struct mtk_mdp_driver_data *mdp_data, + u32 index, u32 type) { u32 i, flag, num = 0; flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT : MDP_FMT_FLAG_CAPTURE; - for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) { - if (!(mdp_formats[i].flags & flag)) + for (i = 0; i < mdp_data->format_len; ++i) { + if (!(mdp_data->format[i].flags & flag)) continue; if (index == num) - return &mdp_formats[i]; + return &mdp_data->format[i]; num++; } return NULL; @@ -354,11 +113,11 @@ static int mdp_clamp_align(s32 *x, int min, int max, unsigned int align) return 0; } -int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f) +int mdp_enum_fmt_mplane(struct mdp_dev *mdp, struct v4l2_fmtdesc *f) { const struct mdp_format *fmt; - fmt = mdp_find_fmt_by_index(f->index, f->type); + fmt = mdp_find_fmt_by_index(mdp->mdp_data, f->index, f->type); if (!fmt) return -EINVAL; @@ -366,7 +125,8 @@ int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f) return 0; } -const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, +const struct mdp_format *mdp_try_fmt_mplane(struct mdp_dev *mdp, + struct v4l2_format *f, struct mdp_frameparam *param, u32 ctx_id) { @@ -378,9 +138,9 @@ const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, u32 org_w, org_h; unsigned int i; - fmt = mdp_find_fmt(pix_mp->pixelformat, f->type); + fmt = mdp_find_fmt(mdp->mdp_data, pix_mp->pixelformat, f->type); if (!fmt) { - fmt = mdp_find_fmt_by_index(0, f->type); + fmt = mdp_find_fmt_by_index(mdp->mdp_data, 0, f->type); if (!fmt) { dev_dbg(dev, "%d: pixelformat %c%c%c%c invalid", ctx_id, (pix_mp->pixelformat & 0xff), @@ -428,14 +188,15 @@ const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, u32 bpl = pix_mp->plane_fmt[i].bytesperline; u32 min_si, max_si; u32 si = pix_mp->plane_fmt[i].sizeimage; + u64 di; bpl = clamp(bpl, min_bpl, max_bpl); pix_mp->plane_fmt[i].bytesperline = bpl; - min_si = (bpl * pix_mp->height * fmt->depth[i]) / - fmt->row_depth[i]; - max_si = (bpl * s.max_height * fmt->depth[i]) / - fmt->row_depth[i]; + di = (u64)bpl * pix_mp->height * fmt->depth[i]; + min_si = (u32)div_u64(di, fmt->row_depth[i]); + di = (u64)bpl * s.max_height * fmt->depth[i]; + max_si = (u32)div_u64(di, fmt->row_depth[i]); si = clamp(si, min_si, max_si); pix_mp->plane_fmt[i].sizeimage = si; @@ -699,7 +460,7 @@ void mdp_set_dst_config(struct img_output *out, mdp_set_orientation(out, frame->rotation, frame->hflip, frame->vflip); } -int mdp_frameparam_init(struct mdp_frameparam *param) +int mdp_frameparam_init(struct mdp_dev *mdp, struct mdp_frameparam *param) { struct mdp_frame *frame; @@ -707,12 +468,12 @@ int mdp_frameparam_init(struct mdp_frameparam *param) return -EINVAL; INIT_LIST_HEAD(¶m->list); - param->limit = &mdp_def_limit; + param->limit = mdp->mdp_data->def_limit; param->type = MDP_STREAM_TYPE_BITBLT; frame = ¶m->output; frame->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format, param, 0); + frame->mdp_fmt = mdp_try_fmt_mplane(mdp, &frame->format, param, 0); frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(&frame->format, frame->mdp_fmt->mdp_color); @@ -721,7 +482,7 @@ int mdp_frameparam_init(struct mdp_frameparam *param) param->num_captures = 1; frame = ¶m->captures[0]; frame->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format, param, 0); + frame->mdp_fmt = mdp_try_fmt_mplane(mdp, &frame->format, param, 0); frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(&frame->format, frame->mdp_fmt->mdp_color); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h index f995e536d45f..e9ab8ac2c0e8 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h @@ -18,11 +18,12 @@ * V-subsample: 0, 1 * Color group: 0-RGB, 1-YUV, 2-raw */ -#define MDP_COLOR(PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\ - (((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\ +#define MDP_COLOR(COMPRESS, PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\ + (((COMPRESS) << 29) | ((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\ ((PLANE) << 21) | ((HF) << 19) | ((VF) << 18) | ((BITS) << 8) |\ ((GROUP) << 6) | ((SWAP) << 5) | ((ID) << 0)) +#define MDP_COLOR_IS_COMPRESS(c) ((0x20000000 & (c)) >> 29) #define MDP_COLOR_IS_10BIT_PACKED(c) ((0x08000000 & (c)) >> 27) #define MDP_COLOR_IS_10BIT_LOOSE(c) (((0x0c000000 & (c)) >> 26) == 1) #define MDP_COLOR_IS_10BIT_TILE(c) (((0x0c000000 & (c)) >> 26) == 3) @@ -44,144 +45,144 @@ enum mdp_color { MDP_COLOR_UNKNOWN = 0, - //MDP_COLOR_FULLG8, - MDP_COLOR_FULLG8_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 21), - MDP_COLOR_FULLG8_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 21), - MDP_COLOR_FULLG8_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 21), - MDP_COLOR_FULLG8_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 21), + /* MDP_COLOR_FULLG8 */ + MDP_COLOR_FULLG8_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 21), + MDP_COLOR_FULLG8_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 8, 2, 0, 21), + MDP_COLOR_FULLG8_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 8, 2, 0, 21), + MDP_COLOR_FULLG8_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 8, 2, 0, 21), MDP_COLOR_FULLG8 = MDP_COLOR_FULLG8_BGGR, - //MDP_COLOR_FULLG10, - MDP_COLOR_FULLG10_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 21), - MDP_COLOR_FULLG10_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 21), - MDP_COLOR_FULLG10_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 21), - MDP_COLOR_FULLG10_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 21), + /* MDP_COLOR_FULLG10 */ + MDP_COLOR_FULLG10_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 21), + MDP_COLOR_FULLG10_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 10, 2, 0, 21), + MDP_COLOR_FULLG10_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 10, 2, 0, 21), + MDP_COLOR_FULLG10_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 10, 2, 0, 21), MDP_COLOR_FULLG10 = MDP_COLOR_FULLG10_BGGR, - //MDP_COLOR_FULLG12, - MDP_COLOR_FULLG12_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 21), - MDP_COLOR_FULLG12_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 21), - MDP_COLOR_FULLG12_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 21), - MDP_COLOR_FULLG12_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 21), + /* MDP_COLOR_FULLG12 */ + MDP_COLOR_FULLG12_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 12, 2, 0, 21), + MDP_COLOR_FULLG12_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 12, 2, 0, 21), + MDP_COLOR_FULLG12_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 12, 2, 0, 21), + MDP_COLOR_FULLG12_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 12, 2, 0, 21), MDP_COLOR_FULLG12 = MDP_COLOR_FULLG12_BGGR, - //MDP_COLOR_FULLG14, - MDP_COLOR_FULLG14_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 21), - MDP_COLOR_FULLG14_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 21), - MDP_COLOR_FULLG14_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 21), - MDP_COLOR_FULLG14_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 21), + /* MDP_COLOR_FULLG14 */ + MDP_COLOR_FULLG14_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 14, 2, 0, 21), + MDP_COLOR_FULLG14_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 14, 2, 0, 21), + MDP_COLOR_FULLG14_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 14, 2, 0, 21), + MDP_COLOR_FULLG14_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 14, 2, 0, 21), MDP_COLOR_FULLG14 = MDP_COLOR_FULLG14_BGGR, - MDP_COLOR_UFO10 = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 24), + MDP_COLOR_UFO10 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 24), - //MDP_COLOR_BAYER8, - MDP_COLOR_BAYER8_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 20), - MDP_COLOR_BAYER8_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 20), - MDP_COLOR_BAYER8_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 20), - MDP_COLOR_BAYER8_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 20), + /* MDP_COLOR_BAYER8 */ + MDP_COLOR_BAYER8_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 20), + MDP_COLOR_BAYER8_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 8, 2, 0, 20), + MDP_COLOR_BAYER8_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 8, 2, 0, 20), + MDP_COLOR_BAYER8_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 8, 2, 0, 20), MDP_COLOR_BAYER8 = MDP_COLOR_BAYER8_BGGR, - //MDP_COLOR_BAYER10, - MDP_COLOR_BAYER10_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 20), - MDP_COLOR_BAYER10_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 20), - MDP_COLOR_BAYER10_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 20), - MDP_COLOR_BAYER10_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 20), + /* MDP_COLOR_BAYER10 */ + MDP_COLOR_BAYER10_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 20), + MDP_COLOR_BAYER10_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 10, 2, 0, 20), + MDP_COLOR_BAYER10_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 10, 2, 0, 20), + MDP_COLOR_BAYER10_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 10, 2, 0, 20), MDP_COLOR_BAYER10 = MDP_COLOR_BAYER10_BGGR, - //MDP_COLOR_BAYER12, - MDP_COLOR_BAYER12_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 20), - MDP_COLOR_BAYER12_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 20), - MDP_COLOR_BAYER12_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 20), - MDP_COLOR_BAYER12_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 20), + /* MDP_COLOR_BAYER12 */ + MDP_COLOR_BAYER12_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 12, 2, 0, 20), + MDP_COLOR_BAYER12_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 12, 2, 0, 20), + MDP_COLOR_BAYER12_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 12, 2, 0, 20), + MDP_COLOR_BAYER12_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 12, 2, 0, 20), MDP_COLOR_BAYER12 = MDP_COLOR_BAYER12_BGGR, - //MDP_COLOR_BAYER14, - MDP_COLOR_BAYER14_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 20), - MDP_COLOR_BAYER14_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 20), - MDP_COLOR_BAYER14_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 20), - MDP_COLOR_BAYER14_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 20), + /* MDP_COLOR_BAYER14 */ + MDP_COLOR_BAYER14_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 14, 2, 0, 20), + MDP_COLOR_BAYER14_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 14, 2, 0, 20), + MDP_COLOR_BAYER14_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 14, 2, 0, 20), + MDP_COLOR_BAYER14_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 14, 2, 0, 20), MDP_COLOR_BAYER14 = MDP_COLOR_BAYER14_BGGR, - MDP_COLOR_RGB48 = MDP_COLOR(0, 0, 0, 1, 0, 0, 48, 0, 0, 23), + MDP_COLOR_RGB48 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 48, 0, 0, 23), /* For bayer+mono raw-16 */ - MDP_COLOR_RGB565_RAW = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 2, 0, 0), + MDP_COLOR_RGB565_RAW = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 16, 2, 0, 0), - MDP_COLOR_BAYER8_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 22), - MDP_COLOR_BAYER10_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 22), - MDP_COLOR_BAYER12_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 22), - MDP_COLOR_BAYER14_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 22), + MDP_COLOR_BAYER8_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 22), + MDP_COLOR_BAYER10_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 22), + MDP_COLOR_BAYER12_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 12, 2, 0, 22), + MDP_COLOR_BAYER14_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 14, 2, 0, 22), /* Unified formats */ - MDP_COLOR_GREY = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 1, 0, 7), - - MDP_COLOR_RGB565 = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0, 0, 0), - MDP_COLOR_BGR565 = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0, 1, 0), - MDP_COLOR_RGB888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0, 1, 1), - MDP_COLOR_BGR888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0, 0, 1), - MDP_COLOR_RGBA8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 1, 2), - MDP_COLOR_BGRA8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 0, 2), - MDP_COLOR_ARGB8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 1, 3), - MDP_COLOR_ABGR8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 0, 3), - - MDP_COLOR_UYVY = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 4), - MDP_COLOR_VYUY = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 4), - MDP_COLOR_YUYV = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 5), - MDP_COLOR_YVYU = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 5), - - MDP_COLOR_I420 = MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 0, 8), - MDP_COLOR_YV12 = MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 1, 8), - MDP_COLOR_I422 = MDP_COLOR(0, 0, 0, 3, 1, 0, 8, 1, 0, 9), - MDP_COLOR_YV16 = MDP_COLOR(0, 0, 0, 3, 1, 0, 8, 1, 1, 9), - MDP_COLOR_I444 = MDP_COLOR(0, 0, 0, 3, 0, 0, 8, 1, 0, 10), - MDP_COLOR_YV24 = MDP_COLOR(0, 0, 0, 3, 0, 0, 8, 1, 1, 10), - - MDP_COLOR_NV12 = MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 0, 12), - MDP_COLOR_NV21 = MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 1, 12), - MDP_COLOR_NV16 = MDP_COLOR(0, 0, 0, 2, 1, 0, 8, 1, 0, 13), - MDP_COLOR_NV61 = MDP_COLOR(0, 0, 0, 2, 1, 0, 8, 1, 1, 13), - MDP_COLOR_NV24 = MDP_COLOR(0, 0, 0, 2, 0, 0, 8, 1, 0, 14), - MDP_COLOR_NV42 = MDP_COLOR(0, 0, 0, 2, 0, 0, 8, 1, 1, 14), + MDP_COLOR_GREY = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 1, 0, 7), + + MDP_COLOR_RGB565 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 16, 0, 0, 0), + MDP_COLOR_BGR565 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 16, 0, 1, 0), + MDP_COLOR_RGB888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 0, 1, 1), + MDP_COLOR_BGR888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 0, 0, 1), + MDP_COLOR_RGBA8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 1, 2), + MDP_COLOR_BGRA8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 0, 2), + MDP_COLOR_ARGB8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 1, 3), + MDP_COLOR_ABGR8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 0, 3), + + MDP_COLOR_UYVY = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 0, 4), + MDP_COLOR_VYUY = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 1, 4), + MDP_COLOR_YUYV = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 0, 5), + MDP_COLOR_YVYU = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 1, 5), + + MDP_COLOR_I420 = MDP_COLOR(0, 0, 0, 0, 3, 1, 1, 8, 1, 0, 8), + MDP_COLOR_YV12 = MDP_COLOR(0, 0, 0, 0, 3, 1, 1, 8, 1, 1, 8), + MDP_COLOR_I422 = MDP_COLOR(0, 0, 0, 0, 3, 1, 0, 8, 1, 0, 9), + MDP_COLOR_YV16 = MDP_COLOR(0, 0, 0, 0, 3, 1, 0, 8, 1, 1, 9), + MDP_COLOR_I444 = MDP_COLOR(0, 0, 0, 0, 3, 0, 0, 8, 1, 0, 10), + MDP_COLOR_YV24 = MDP_COLOR(0, 0, 0, 0, 3, 0, 0, 8, 1, 1, 10), + + MDP_COLOR_NV12 = MDP_COLOR(0, 0, 0, 0, 2, 1, 1, 8, 1, 0, 12), + MDP_COLOR_NV21 = MDP_COLOR(0, 0, 0, 0, 2, 1, 1, 8, 1, 1, 12), + MDP_COLOR_NV16 = MDP_COLOR(0, 0, 0, 0, 2, 1, 0, 8, 1, 0, 13), + MDP_COLOR_NV61 = MDP_COLOR(0, 0, 0, 0, 2, 1, 0, 8, 1, 1, 13), + MDP_COLOR_NV24 = MDP_COLOR(0, 0, 0, 0, 2, 0, 0, 8, 1, 0, 14), + MDP_COLOR_NV42 = MDP_COLOR(0, 0, 0, 0, 2, 0, 0, 8, 1, 1, 14), /* MediaTek proprietary formats */ /* UFO encoded block mode */ - MDP_COLOR_420_BLK_UFO = MDP_COLOR(0, 0, 5, 2, 1, 1, 256, 1, 0, 12), + MDP_COLOR_420_BLK_UFO = MDP_COLOR(0, 0, 0, 5, 2, 1, 1, 256, 1, 0, 12), /* Block mode */ - MDP_COLOR_420_BLK = MDP_COLOR(0, 0, 1, 2, 1, 1, 256, 1, 0, 12), + MDP_COLOR_420_BLK = MDP_COLOR(0, 0, 0, 1, 2, 1, 1, 256, 1, 0, 12), /* Block mode + field mode */ - MDP_COLOR_420_BLKI = MDP_COLOR(0, 0, 3, 2, 1, 1, 256, 1, 0, 12), + MDP_COLOR_420_BLKI = MDP_COLOR(0, 0, 0, 3, 2, 1, 1, 256, 1, 0, 12), /* Block mode */ - MDP_COLOR_422_BLK = MDP_COLOR(0, 0, 1, 1, 1, 0, 512, 1, 0, 4), + MDP_COLOR_422_BLK = MDP_COLOR(0, 0, 0, 1, 1, 1, 0, 512, 1, 0, 4), - MDP_COLOR_IYU2 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 1, 0, 25), - MDP_COLOR_YUV444 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 1, 0, 30), + MDP_COLOR_IYU2 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 1, 0, 25), + MDP_COLOR_YUV444 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 1, 0, 30), /* Packed 10-bit formats */ - MDP_COLOR_RGBA1010102 = MDP_COLOR(1, 0, 0, 1, 0, 0, 32, 0, 1, 2), - MDP_COLOR_BGRA1010102 = MDP_COLOR(1, 0, 0, 1, 0, 0, 32, 0, 0, 2), + MDP_COLOR_RGBA1010102 = MDP_COLOR(0, 1, 0, 0, 1, 0, 0, 32, 0, 1, 2), + MDP_COLOR_BGRA1010102 = MDP_COLOR(0, 1, 0, 0, 1, 0, 0, 32, 0, 0, 2), /* Packed 10-bit UYVY */ - MDP_COLOR_UYVY_10P = MDP_COLOR(1, 0, 0, 1, 1, 0, 20, 1, 0, 4), + MDP_COLOR_UYVY_10P = MDP_COLOR(0, 1, 0, 0, 1, 1, 0, 20, 1, 0, 4), /* Packed 10-bit NV21 */ - MDP_COLOR_NV21_10P = MDP_COLOR(1, 0, 0, 2, 1, 1, 10, 1, 1, 12), + MDP_COLOR_NV21_10P = MDP_COLOR(0, 1, 0, 0, 2, 1, 1, 10, 1, 1, 12), /* 10-bit block mode */ - MDP_COLOR_420_BLK_10_H = MDP_COLOR(1, 0, 1, 2, 1, 1, 320, 1, 0, 12), + MDP_COLOR_420_BLK_10_H = MDP_COLOR(0, 1, 0, 1, 2, 1, 1, 320, 1, 0, 12), /* 10-bit HEVC tile mode */ - MDP_COLOR_420_BLK_10_V = MDP_COLOR(1, 1, 1, 2, 1, 1, 320, 1, 0, 12), + MDP_COLOR_420_BLK_10_V = MDP_COLOR(0, 1, 1, 1, 2, 1, 1, 320, 1, 0, 12), /* UFO encoded 10-bit block mode */ - MDP_COLOR_420_BLK_U10_H = MDP_COLOR(1, 0, 5, 2, 1, 1, 320, 1, 0, 12), + MDP_COLOR_420_BLK_U10_H = MDP_COLOR(0, 1, 0, 5, 2, 1, 1, 320, 1, 0, 12), /* UFO encoded 10-bit HEVC tile mode */ - MDP_COLOR_420_BLK_U10_V = MDP_COLOR(1, 1, 5, 2, 1, 1, 320, 1, 0, 12), + MDP_COLOR_420_BLK_U10_V = MDP_COLOR(0, 1, 1, 5, 2, 1, 1, 320, 1, 0, 12), /* Loose 10-bit formats */ - MDP_COLOR_UYVY_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 0, 4), - MDP_COLOR_VYUY_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 1, 4), - MDP_COLOR_YUYV_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 0, 5), - MDP_COLOR_YVYU_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 1, 5), - MDP_COLOR_NV12_10L = MDP_COLOR(0, 1, 0, 2, 1, 1, 10, 1, 0, 12), - MDP_COLOR_NV21_10L = MDP_COLOR(0, 1, 0, 2, 1, 1, 10, 1, 1, 12), - MDP_COLOR_NV16_10L = MDP_COLOR(0, 1, 0, 2, 1, 0, 10, 1, 0, 13), - MDP_COLOR_NV61_10L = MDP_COLOR(0, 1, 0, 2, 1, 0, 10, 1, 1, 13), - MDP_COLOR_YV12_10L = MDP_COLOR(0, 1, 0, 3, 1, 1, 10, 1, 1, 8), - MDP_COLOR_I420_10L = MDP_COLOR(0, 1, 0, 3, 1, 1, 10, 1, 0, 8), + MDP_COLOR_UYVY_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 0, 4), + MDP_COLOR_VYUY_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 1, 4), + MDP_COLOR_YUYV_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 0, 5), + MDP_COLOR_YVYU_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 1, 5), + MDP_COLOR_NV12_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 1, 10, 1, 0, 12), + MDP_COLOR_NV21_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 1, 10, 1, 1, 12), + MDP_COLOR_NV16_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 0, 10, 1, 0, 13), + MDP_COLOR_NV61_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 0, 10, 1, 1, 13), + MDP_COLOR_YV12_10L = MDP_COLOR(0, 0, 1, 0, 3, 1, 1, 10, 1, 1, 8), + MDP_COLOR_I420_10L = MDP_COLOR(0, 0, 1, 0, 3, 1, 1, 10, 1, 0, 8), }; static inline bool MDP_COLOR_IS_UV_COPLANE(enum mdp_color c) @@ -353,8 +354,11 @@ struct mdp_frameparam { enum v4l2_quantization quant; }; -int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f); -const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, +struct mdp_dev; + +int mdp_enum_fmt_mplane(struct mdp_dev *mdp, struct v4l2_fmtdesc *f); +const struct mdp_format *mdp_try_fmt_mplane(struct mdp_dev *mdp, + struct v4l2_format *f, struct mdp_frameparam *param, u32 ctx_id); enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f, @@ -368,6 +372,6 @@ void mdp_set_src_config(struct img_input *in, struct mdp_frame *frame, struct vb2_buffer *vb); void mdp_set_dst_config(struct img_output *out, struct mdp_frame *frame, struct vb2_buffer *vb); -int mdp_frameparam_init(struct mdp_frameparam *param); +int mdp_frameparam_init(struct mdp_dev *mdp, struct mdp_frameparam *param); #endif /* __MTK_MDP3_REGS_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-type.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-type.h new file mode 100644 index 000000000000..ae0396806152 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-type.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com> + */ + +#ifndef __MTK_MDP3_TYPE_H__ +#define __MTK_MDP3_TYPE_H__ + +#include <linux/types.h> + +#define IMG_MAX_HW_INPUTS 3 +#define IMG_MAX_HW_OUTPUTS 4 +#define IMG_MAX_PLANES 3 +#define IMG_MAX_COMPONENTS 20 + +struct img_crop { + s32 left; + s32 top; + u32 width; + u32 height; + u32 left_subpix; + u32 top_subpix; + u32 width_subpix; + u32 height_subpix; +} __packed; + +struct img_region { + s32 left; + s32 right; + s32 top; + s32 bottom; +} __packed; + +struct img_offset { + s32 left; + s32 top; + u32 left_subpix; + u32 top_subpix; +} __packed; + +struct img_mux { + u32 reg; + u32 value; + u32 subsys_id; +} __packed; + +struct img_mmsys_ctrl { + struct img_mux sets[IMG_MAX_COMPONENTS * 2]; + u32 num_sets; +} __packed; + +#endif /* __MTK_MDP3_TYPE_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c index a72bed927bb6..49fc2e9d45dd 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c @@ -10,7 +10,6 @@ #include "mtk-mdp3-core.h" #define MDP_VPU_MESSAGE_TIMEOUT 500U -#define vpu_alloc_size 0x600000 static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu) { @@ -19,23 +18,63 @@ static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu) static int mdp_vpu_shared_mem_alloc(struct mdp_vpu_dev *vpu) { - if (vpu->work && vpu->work_addr) - return 0; + struct device *dev; - vpu->work = dma_alloc_coherent(scp_get_device(vpu->scp), vpu_alloc_size, - &vpu->work_addr, GFP_KERNEL); + if (IS_ERR_OR_NULL(vpu)) + goto err_return; - if (!vpu->work) - return -ENOMEM; - else - return 0; + dev = scp_get_device(vpu->scp); + + if (!vpu->param) { + vpu->param = dma_alloc_wc(dev, vpu->param_size, + &vpu->param_addr, GFP_KERNEL); + if (!vpu->param) + goto err_return; + } + + if (!vpu->work) { + vpu->work = dma_alloc_wc(dev, vpu->work_size, + &vpu->work_addr, GFP_KERNEL); + if (!vpu->work) + goto err_free_param; + } + + if (!vpu->config) { + vpu->config = dma_alloc_wc(dev, vpu->config_size, + &vpu->config_addr, GFP_KERNEL); + if (!vpu->config) + goto err_free_work; + } + + return 0; + +err_free_work: + dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr); + vpu->work = NULL; +err_free_param: + dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr); + vpu->param = NULL; +err_return: + return -ENOMEM; } void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu) { + struct device *dev; + + if (IS_ERR_OR_NULL(vpu)) + return; + + dev = scp_get_device(vpu->scp); + + if (vpu->param && vpu->param_addr) + dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr); + if (vpu->work && vpu->work_addr) - dma_free_coherent(scp_get_device(vpu->scp), vpu_alloc_size, - vpu->work, vpu->work_addr); + dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr); + + if (vpu->config && vpu->config_addr) + dma_free_wc(dev, vpu->config_size, vpu->config, vpu->config_addr); } static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len, @@ -69,16 +108,16 @@ static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len, struct img_sw_addr *addr = (struct img_sw_addr *)data; struct img_ipi_frameparam *param = (struct img_ipi_frameparam *)(unsigned long)addr->va; - struct mdp_vpu_ctx *ctx = - (struct mdp_vpu_ctx *)(unsigned long)param->drv_data; + struct mdp_vpu_dev *vpu = + (struct mdp_vpu_dev *)(unsigned long)param->drv_data; if (param->state) { - struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev); + struct mdp_dev *mdp = vpu_to_mdp(vpu); dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state); } - ctx->vpu_dev->status = param->state; - complete(&ctx->vpu_dev->ipi_acked); + vpu->status = param->state; + complete(&vpu->ipi_acked); } int mdp_vpu_register(struct mdp_dev *mdp) @@ -157,9 +196,6 @@ int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp, struct mdp_ipi_init_msg msg = { .drv_data = (unsigned long)vpu, }; - size_t mem_size; - phys_addr_t pool; - const size_t pool_size = sizeof(struct mdp_config_pool); struct mdp_dev *mdp = vpu_to_mdp(vpu); int err; @@ -172,34 +208,29 @@ int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp, goto err_work_size; /* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */ - mem_size = vpu_alloc_size; + mutex_lock(vpu->lock); + vpu->work_size = ALIGN(vpu->work_size, 64); + vpu->param_size = ALIGN(sizeof(struct img_ipi_frameparam), 64); + vpu->config_size = ALIGN(sizeof(struct img_config), 64); err = mdp_vpu_shared_mem_alloc(vpu); + mutex_unlock(vpu->lock); if (err) { dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); goto err_mem_alloc; } - pool = ALIGN((uintptr_t)vpu->work + vpu->work_size, 8); - if (pool + pool_size - (uintptr_t)vpu->work > mem_size) { - dev_err(&mdp->pdev->dev, - "VPU memory insufficient: %zx + %zx > %zx", - vpu->work_size, pool_size, mem_size); - err = -ENOMEM; - goto err_mem_size; - } - dev_dbg(&mdp->pdev->dev, - "VPU work:%pK pa:%pad sz:%zx pool:%pa sz:%zx (mem sz:%zx)", + "VPU param:%pK pa:%pad sz:%zx, work:%pK pa:%pad sz:%zx, config:%pK pa:%pad sz:%zx", + vpu->param, &vpu->param_addr, vpu->param_size, vpu->work, &vpu->work_addr, vpu->work_size, - &pool, pool_size, mem_size); - vpu->pool = (struct mdp_config_pool *)(uintptr_t)pool; + vpu->config, &vpu->config_addr, vpu->config_size); + msg.work_addr = vpu->work_addr; msg.work_size = vpu->work_size; err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); if (err) goto err_work_size; - memset(vpu->pool, 0, sizeof(*vpu->pool)); return 0; err_work_size: @@ -212,7 +243,6 @@ err_work_size: break; } return err; -err_mem_size: err_mem_alloc: return err; } @@ -227,88 +257,31 @@ int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu) return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg)); } -static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu, - enum mdp_config_id id, uint32_t *addr) +int mdp_vpu_process(struct mdp_vpu_dev *vpu, struct img_ipi_frameparam *param) { - struct img_config *config; - - if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) - return ERR_PTR(-EINVAL); + struct mdp_dev *mdp = vpu_to_mdp(vpu); + struct img_sw_addr addr; mutex_lock(vpu->lock); - vpu->pool->cfg_count[id]++; - config = &vpu->pool->configs[id]; - *addr = vpu->work_addr + ((uintptr_t)config - (uintptr_t)vpu->work); - mutex_unlock(vpu->lock); - - return config; -} - -static int mdp_config_put(struct mdp_vpu_dev *vpu, - enum mdp_config_id id, - const struct img_config *config) -{ - int err = 0; - - if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) - return -EINVAL; - if (vpu->lock) - mutex_lock(vpu->lock); - if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id]) - err = -EINVAL; - else - vpu->pool->cfg_count[id]--; - if (vpu->lock) + if (mdp_vpu_shared_mem_alloc(vpu)) { + dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); mutex_unlock(vpu->lock); - return err; -} - -int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, - enum mdp_config_id id) -{ - ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr); - if (IS_ERR(ctx->config)) { - int err = PTR_ERR(ctx->config); - - ctx->config = NULL; - return err; + return -ENOMEM; } - ctx->config_id = id; - ctx->vpu_dev = vpu; - return 0; -} -int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx) -{ - int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config); + memset(vpu->param, 0, vpu->param_size); + memset(vpu->work, 0, vpu->work_size); + memset(vpu->config, 0, vpu->config_size); - ctx->config_id = 0; - ctx->config = NULL; - ctx->inst_addr = 0; - return err; -} + param->self_data.va = (unsigned long)vpu->work; + param->self_data.pa = vpu->work_addr; + param->config_data.va = (unsigned long)vpu->config; + param->config_data.pa = vpu->config_addr; + param->drv_data = (unsigned long)vpu; + memcpy(vpu->param, param, sizeof(*param)); -int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param) -{ - struct mdp_vpu_dev *vpu = ctx->vpu_dev; - struct mdp_dev *mdp = vpu_to_mdp(vpu); - struct img_sw_addr addr; - - if (!ctx->vpu_dev->work || !ctx->vpu_dev->work_addr) { - if (mdp_vpu_shared_mem_alloc(vpu)) { - dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); - return -ENOMEM; - } - } - memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size); - memset(ctx->config, 0, sizeof(*ctx->config)); - param->config_data.va = (unsigned long)ctx->config; - param->config_data.pa = ctx->inst_addr; - param->drv_data = (unsigned long)ctx; - - memcpy((void *)ctx->vpu_dev->work, param, sizeof(*param)); - addr.pa = ctx->vpu_dev->work_addr; - addr.va = (uintptr_t)ctx->vpu_dev->work; - return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME, - &addr, sizeof(addr)); + addr.pa = vpu->param_addr; + addr.va = (unsigned long)vpu->param; + mutex_unlock(vpu->lock); + return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_FRAME, &addr, sizeof(addr)); } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h index 244b3a32d689..ad3551bc0730 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h @@ -37,42 +37,27 @@ struct mdp_ipi_deinit_msg { u32 work_addr; } __packed; -enum mdp_config_id { - MDP_DEV_M2M = 0, - MDP_CONFIG_POOL_SIZE /* ALWAYS keep at the end */ -}; - -struct mdp_config_pool { - u64 cfg_count[MDP_CONFIG_POOL_SIZE]; - struct img_config configs[MDP_CONFIG_POOL_SIZE]; -}; - struct mdp_vpu_dev { /* synchronization protect for accessing vpu working buffer info */ struct mutex *lock; struct mtk_scp *scp; struct completion ipi_acked; + void *param; + dma_addr_t param_addr; + size_t param_size; void *work; dma_addr_t work_addr; size_t work_size; - struct mdp_config_pool *pool; + void *config; + dma_addr_t config_addr; + size_t config_size; u32 status; }; -struct mdp_vpu_ctx { - struct mdp_vpu_dev *vpu_dev; - u32 config_id; - struct img_config *config; - u32 inst_addr; -}; - void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu); int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp, struct mutex *lock /* for sync */); int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu); -int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, - enum mdp_config_id id); -int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx); -int mdp_vpu_process(struct mdp_vpu_ctx *vpu, struct img_ipi_frameparam *param); +int mdp_vpu_process(struct mdp_vpu_dev *vpu, struct img_ipi_frameparam *param); #endif /* __MTK_MDP3_VPU_H__ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c index 641f533c417f..93fcea821001 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c @@ -39,10 +39,9 @@ static bool mtk_vdec_get_cap_fmt(struct mtk_vcodec_ctx *ctx, int format_index) { const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; const struct mtk_video_fmt *fmt; - struct mtk_q_data *q_data; int num_frame_count = 0, i; - bool ret = true; + fmt = &dec_pdata->vdec_formats[format_index]; for (i = 0; i < *dec_pdata->num_formats; i++) { if (dec_pdata->vdec_formats[i].type != MTK_FMT_FRAME) continue; @@ -50,27 +49,10 @@ static bool mtk_vdec_get_cap_fmt(struct mtk_vcodec_ctx *ctx, int format_index) num_frame_count++; } - if (num_frame_count == 1) + if (num_frame_count == 1 || fmt->fourcc == V4L2_PIX_FMT_MM21) return true; - fmt = &dec_pdata->vdec_formats[format_index]; - q_data = &ctx->q_data[MTK_Q_DATA_SRC]; - switch (q_data->fmt->fourcc) { - case V4L2_PIX_FMT_VP8_FRAME: - if (fmt->fourcc == V4L2_PIX_FMT_MM21) - ret = true; - break; - case V4L2_PIX_FMT_H264_SLICE: - case V4L2_PIX_FMT_VP9_FRAME: - if (fmt->fourcc == V4L2_PIX_FMT_MM21) - ret = false; - break; - default: - ret = true; - break; - } - - return ret; + return false; } static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_ctx *ctx, @@ -753,6 +735,13 @@ int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, } if (*nplanes) { + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (*nplanes != q_data->fmt->num_planes) + return -EINVAL; + } else { + if (*nplanes != 1) + return -EINVAL; + } for (i = 0; i < *nplanes; i++) { if (sizes[i] < q_data->sizeimage[i]) return -EINVAL; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c index 174a6eec2f54..9c652beb3f19 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c @@ -321,14 +321,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) } } - if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) { - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - if (ret) { - mtk_v4l2_err("Failed to set mask"); - goto err_core_workq; - } - } - for (i = 0; i < MTK_VDEC_HW_MAX; i++) mutex_init(&dev->dec_mutex[i]); mutex_init(&dev->dev_mutex); @@ -451,7 +443,8 @@ err_core_workq: if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) destroy_workqueue(dev->core_workqueue); err_res: - pm_runtime_disable(dev->pm.dev); + if (!dev->vdec_pdata->is_subdev_supported) + pm_runtime_disable(dev->pm.dev); err_dec_pm: mtk_vcodec_fw_release(dev->fw_handler); return ret; @@ -487,7 +480,7 @@ static const struct of_device_id mtk_vcodec_match[] = { MODULE_DEVICE_TABLE(of, mtk_vcodec_match); -static int mtk_vcodec_dec_remove(struct platform_device *pdev) +static void mtk_vcodec_dec_remove(struct platform_device *pdev) { struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); @@ -509,12 +502,11 @@ static int mtk_vcodec_dec_remove(struct platform_device *pdev) if (!dev->vdec_pdata->is_subdev_supported) pm_runtime_disable(dev->pm.dev); mtk_vcodec_fw_release(dev->fw_handler); - return 0; } static struct platform_driver mtk_vcodec_dec_driver = { .probe = mtk_vcodec_probe, - .remove = mtk_vcodec_dec_remove, + .remove_new = mtk_vcodec_dec_remove, .driver = { .name = MTK_VCODEC_DEC_NAME, .of_match_table = mtk_vcodec_match, diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c index 376db0e433d7..b753bf54ebd9 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c @@ -193,8 +193,16 @@ err: return ret; } +static int mtk_vdec_hw_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + static struct platform_driver mtk_vdec_driver = { .probe = mtk_vdec_hw_probe, + .remove = mtk_vdec_hw_remove, .driver = { .name = "mtk-vdec-comp", .of_match_table = mtk_vdec_hw_match, diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c index 035c86e7809f..29991551cf61 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c @@ -11,7 +11,7 @@ #include "mtk_vcodec_dec_pm.h" #include "vdec_drv_if.h" -static const struct mtk_video_fmt mtk_video_formats[] = { +static struct mtk_video_fmt mtk_video_formats[] = { { .fourcc = V4L2_PIX_FMT_H264, .type = MTK_FMT_DEC, @@ -580,6 +580,16 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) { + unsigned int i; + + if (!(ctx->dev->dec_capability & VCODEC_CAPABILITY_4K_DISABLED)) { + for (i = 0; i < num_supported_formats; i++) { + mtk_video_formats[i].frmsize.max_width = + VCODEC_DEC_4K_CODED_WIDTH; + mtk_video_formats[i].frmsize.max_height = + VCODEC_DEC_4K_CODED_HEIGHT; + } + } } static struct vb2_ops mtk_vdec_frame_vb2_ops = { diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c index ffbcee04dc26..3000db975e5f 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c @@ -258,8 +258,10 @@ static void mtk_vdec_worker(struct work_struct *work) if (src_buf_req) v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl); } else { - v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - v4l2_m2m_buf_done(vb2_v4l2_src, state); + if (ret != -EAGAIN) { + v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(vb2_v4l2_src, state); + } v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); } } @@ -390,14 +392,14 @@ static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_ctx *ctx) if (num_formats) return; - if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_MM21) { - mtk_vcodec_add_formats(V4L2_PIX_FMT_MM21, ctx); - cap_format_count++; - } if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_MT21C) { mtk_vcodec_add_formats(V4L2_PIX_FMT_MT21C, ctx); cap_format_count++; } + if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_MM21) { + mtk_vcodec_add_formats(V4L2_PIX_FMT_MM21, ctx); + cap_format_count++; + } if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_H264_SLICE) { mtk_vcodec_add_formats(V4L2_PIX_FMT_H264_SLICE, ctx); out_format_count++; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index d65800a3b89d..db65e77bd373 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -943,7 +943,7 @@ err_start_stream: * FIXME: This check is not needed as only active buffers * can be marked as done. */ - if (buf->state == VB2_BUF_STATE_ACTIVE) { + if (buf && buf->state == VB2_BUF_STATE_ACTIVE) { mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", ctx->id, i, q->type, (int)buf->state); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c index 9095186d5495..168004a08888 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c @@ -89,16 +89,24 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) struct mtk_vcodec_ctx *ctx; unsigned long flags; void __iomem *addr; + int core_id; spin_lock_irqsave(&dev->irqlock, flags); ctx = dev->curr_ctx; spin_unlock_irqrestore(&dev->irqlock, flags); - mtk_v4l2_debug(1, "id=%d coreid:%d", ctx->id, dev->venc_pdata->core_id); - addr = dev->reg_base[dev->venc_pdata->core_id] + - MTK_VENC_IRQ_ACK_OFFSET; + core_id = dev->venc_pdata->core_id; + if (core_id < 0 || core_id >= NUM_MAX_VCODEC_REG_BASE) { + mtk_v4l2_err("Invalid core id: %d, ctx id: %d", + core_id, ctx->id); + return IRQ_HANDLED; + } + + mtk_v4l2_debug(1, "id: %d, core id: %d", ctx->id, core_id); + + addr = dev->reg_base[core_id] + MTK_VENC_IRQ_ACK_OFFSET; - ctx->irq_status = readl(dev->reg_base[dev->venc_pdata->core_id] + + ctx->irq_status = readl(dev->reg_base[core_id] + (MTK_VENC_IRQ_STATUS_OFFSET)); clean_irq_status(ctx->irq_status, addr); @@ -344,9 +352,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_event_workq; } - if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, -1); if (ret) { mtk_v4l2_err("Failed to register video device"); @@ -451,7 +456,7 @@ static const struct of_device_id mtk_vcodec_enc_match[] = { }; MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); -static int mtk_vcodec_enc_remove(struct platform_device *pdev) +static void mtk_vcodec_enc_remove(struct platform_device *pdev) { struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); @@ -466,12 +471,11 @@ static int mtk_vcodec_enc_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); pm_runtime_disable(dev->pm.dev); mtk_vcodec_fw_release(dev->fw_handler); - return 0; } static struct platform_driver mtk_vcodec_enc_driver = { .probe = mtk_vcodec_probe, - .remove = mtk_vcodec_enc_remove, + .remove_new = 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/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c index 955b2d0c8f53..999ce7ee5fdc 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c @@ -597,7 +597,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx); if (!lat_buf) { mtk_vcodec_err(inst, "failed to get lat buffer"); - return -EINVAL; + return -EAGAIN; } share_info = lat_buf->private_data; src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c index cbb6728b8a40..cf16cf2807f0 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c @@ -2070,7 +2070,7 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&instance->ctx->msg_queue.lat_ctx); if (!lat_buf) { mtk_vcodec_err(instance, "Failed to get VP9 lat buf\n"); - return -EBUSY; + return -EAGAIN; } pfc = (struct vdec_vp9_slice_pfc *)lat_buf->private_data; if (!pfc) { diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c index dc2004790a47..f3073d1e7f42 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c @@ -52,9 +52,26 @@ static struct list_head *vdec_get_buf_list(int hardware_index, struct vdec_lat_b } } +static void vdec_msg_queue_inc(struct vdec_msg_queue *msg_queue, int hardware_index) +{ + if (hardware_index == MTK_VDEC_CORE) + atomic_inc(&msg_queue->core_list_cnt); + else + atomic_inc(&msg_queue->lat_list_cnt); +} + +static void vdec_msg_queue_dec(struct vdec_msg_queue *msg_queue, int hardware_index) +{ + if (hardware_index == MTK_VDEC_CORE) + atomic_dec(&msg_queue->core_list_cnt); + else + atomic_dec(&msg_queue->lat_list_cnt); +} + int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf) { struct list_head *head; + int status; head = vdec_get_buf_list(msg_ctx->hardware_index, buf); if (!head) { @@ -66,11 +83,18 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf list_add_tail(head, &msg_ctx->ready_queue); msg_ctx->ready_num++; - if (msg_ctx->hardware_index != MTK_VDEC_CORE) + vdec_msg_queue_inc(&buf->ctx->msg_queue, msg_ctx->hardware_index); + if (msg_ctx->hardware_index != MTK_VDEC_CORE) { wake_up_all(&msg_ctx->ready_to_use); - else - queue_work(buf->ctx->dev->core_workqueue, - &buf->ctx->msg_queue.core_work); + } else { + if (buf->ctx->msg_queue.core_work_cnt < + atomic_read(&buf->ctx->msg_queue.core_list_cnt)) { + status = queue_work(buf->ctx->dev->core_workqueue, + &buf->ctx->msg_queue.core_work); + if (status) + buf->ctx->msg_queue.core_work_cnt++; + } + } mtk_v4l2_debug(3, "enqueue buf type: %d addr: 0x%p num: %d", msg_ctx->hardware_index, buf, msg_ctx->ready_num); @@ -127,6 +151,7 @@ struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx) return NULL; } list_del(head); + vdec_msg_queue_dec(&buf->ctx->msg_queue, msg_ctx->hardware_index); msg_ctx->ready_num--; mtk_v4l2_debug(3, "dqueue buf type:%d addr: 0x%p num: %d", @@ -156,11 +181,29 @@ void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t u bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) { + struct vdec_lat_buf *buf, *tmp; + struct list_head *list_core[3]; + struct vdec_msg_queue_ctx *core_ctx; + int ret, i, in_core_count = 0, count = 0; long timeout_jiff; - int ret; + + core_ctx = &msg_queue->ctx->dev->msg_queue_core_ctx; + spin_lock(&core_ctx->ready_lock); + list_for_each_entry_safe(buf, tmp, &core_ctx->ready_queue, core_list) { + if (buf && buf->ctx == msg_queue->ctx) { + list_core[in_core_count++] = &buf->core_list; + list_del(&buf->core_list); + } + } + + for (i = 0; i < in_core_count; i++) { + list_add(list_core[in_core_count - (1 + i)], &core_ctx->ready_queue); + queue_work(msg_queue->ctx->dev->core_workqueue, &msg_queue->core_work); + } + spin_unlock(&core_ctx->ready_lock); timeout_jiff = msecs_to_jiffies(1000 * (NUM_BUFFER_COUNT + 2)); - ret = wait_event_timeout(msg_queue->lat_ctx.ready_to_use, + ret = wait_event_timeout(msg_queue->ctx->msg_queue.core_dec_done, msg_queue->lat_ctx.ready_num == NUM_BUFFER_COUNT, timeout_jiff); if (ret) { @@ -168,8 +211,20 @@ bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) msg_queue->lat_ctx.ready_num); return true; } - mtk_v4l2_err("failed with lat buf isn't full: %d", - msg_queue->lat_ctx.ready_num); + + spin_lock(&core_ctx->ready_lock); + list_for_each_entry_safe(buf, tmp, &core_ctx->ready_queue, core_list) { + if (buf && buf->ctx == msg_queue->ctx) { + count++; + list_del(&buf->core_list); + } + } + spin_unlock(&core_ctx->ready_lock); + + mtk_v4l2_err("failed with lat buf isn't full: list(%d %d) count:%d", + atomic_read(&msg_queue->lat_list_cnt), + atomic_read(&msg_queue->core_list_cnt), count); + return false; } @@ -206,6 +261,7 @@ static void vdec_msg_queue_core_work(struct work_struct *work) container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); struct mtk_vcodec_dev *dev = ctx->dev; struct vdec_lat_buf *lat_buf; + int status; lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx); if (!lat_buf) @@ -221,11 +277,18 @@ static void vdec_msg_queue_core_work(struct work_struct *work) mtk_vcodec_dec_disable_hardware(ctx, MTK_VDEC_CORE); vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); - if (!list_empty(&dev->msg_queue_core_ctx.ready_queue)) { - mtk_v4l2_debug(3, "re-schedule to decode for core: %d", - dev->msg_queue_core_ctx.ready_num); - queue_work(dev->core_workqueue, &msg_queue->core_work); + wake_up_all(&ctx->msg_queue.core_dec_done); + spin_lock(&dev->msg_queue_core_ctx.ready_lock); + lat_buf->ctx->msg_queue.core_work_cnt--; + + if (lat_buf->ctx->msg_queue.core_work_cnt < + atomic_read(&lat_buf->ctx->msg_queue.core_list_cnt)) { + status = queue_work(lat_buf->ctx->dev->core_workqueue, + &lat_buf->ctx->msg_queue.core_work); + if (status) + lat_buf->ctx->msg_queue.core_work_cnt++; } + spin_unlock(&dev->msg_queue_core_ctx.ready_lock); } int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, @@ -239,12 +302,18 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, if (msg_queue->wdma_addr.size) return 0; + msg_queue->ctx = ctx; + msg_queue->core_work_cnt = 0; vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0); INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work); + + atomic_set(&msg_queue->lat_list_cnt, 0); + atomic_set(&msg_queue->core_list_cnt, 0); + init_waitqueue_head(&msg_queue->core_dec_done); + msg_queue->wdma_addr.size = vde_msg_queue_get_trans_size(ctx->picinfo.buf_w, ctx->picinfo.buf_h); - err = mtk_vcodec_mem_alloc(ctx, &msg_queue->wdma_addr); if (err) { mtk_v4l2_err("failed to allocate wdma_addr buf"); diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h index c43d427f5f54..a5d44bc97c16 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h @@ -72,6 +72,12 @@ struct vdec_lat_buf { * @wdma_wptr_addr: ube write point * @core_work: core hardware work * @lat_ctx: used to store lat buffer list + * @ctx: point to mtk_vcodec_ctx + * + * @lat_list_cnt: used to record each instance lat list count + * @core_list_cnt: used to record each instance core list count + * @core_dec_done: core work queue decode done event + * @core_work_cnt: the number of core work in work queue */ struct vdec_msg_queue { struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; @@ -82,6 +88,12 @@ struct vdec_msg_queue { struct work_struct core_work; struct vdec_msg_queue_ctx lat_ctx; + struct mtk_vcodec_ctx *ctx; + + atomic_t lat_list_cnt; + atomic_t core_list_cnt; + wait_queue_head_t core_dec_done; + int core_work_cnt; }; /** diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c index 47b684b92f81..5e2bc286f168 100644 --- a/drivers/media/platform/mediatek/vpu/mtk_vpu.c +++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c @@ -953,7 +953,7 @@ static const struct of_device_id mtk_vpu_match[] = { }; MODULE_DEVICE_TABLE(of, mtk_vpu_match); -static int mtk_vpu_remove(struct platform_device *pdev) +static void mtk_vpu_remove(struct platform_device *pdev) { struct mtk_vpu *vpu = platform_get_drvdata(pdev); @@ -966,8 +966,6 @@ static int mtk_vpu_remove(struct platform_device *pdev) vpu_free_ext_mem(vpu, D_FW); mutex_destroy(&vpu->vpu_mutex); clk_unprepare(vpu->clk); - - return 0; } static int mtk_vpu_suspend(struct device *dev) @@ -1040,7 +1038,7 @@ static const struct dev_pm_ops mtk_vpu_pm = { static struct platform_driver mtk_vpu_driver = { .probe = mtk_vpu_probe, - .remove = mtk_vpu_remove, + .remove_new = 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 d5b359f607ae..bfb3edcf018a 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -741,7 +741,7 @@ csi2dc_probe_cleanup_entity: return ret; } -static int csi2dc_remove(struct platform_device *pdev) +static void csi2dc_remove(struct platform_device *pdev) { struct csi2dc_device *csi2dc = platform_get_drvdata(pdev); @@ -751,8 +751,6 @@ static int csi2dc_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&csi2dc->notifier); v4l2_async_nf_cleanup(&csi2dc->notifier); media_entity_cleanup(&csi2dc->csi2dc_sd.entity); - - return 0; } static int __maybe_unused csi2dc_runtime_suspend(struct device *dev) @@ -782,7 +780,7 @@ MODULE_DEVICE_TABLE(of, csi2dc_of_match); static struct platform_driver csi2dc_driver = { .probe = csi2dc_probe, - .remove = csi2dc_remove, + .remove_new = 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 71758ee8474b..4e657fad33d0 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -858,8 +858,10 @@ static int isc_try_configure_pipeline(struct isc_device *isc) static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; int ret; - struct v4l2_subdev_frame_size_enum fse = {}; /* * If we do not know yet which format the subdev is using, we cannot @@ -869,7 +871,6 @@ static void isc_try_fse(struct isc_device *isc, return; fse.code = isc->try_config.sd_format->mbus_code; - fse.which = V4L2_SUBDEV_FORMAT_TRY; ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, sd_state, &fse); diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c index ac4715d91de6..746f4a2fa9f6 100644 --- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c +++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c @@ -608,7 +608,7 @@ unprepare_hclk: return ret; } -static int microchip_isc_remove(struct platform_device *pdev) +static void microchip_isc_remove(struct platform_device *pdev) { struct isc_device *isc = platform_get_drvdata(pdev); @@ -624,8 +624,6 @@ static int microchip_isc_remove(struct platform_device *pdev) clk_disable_unprepare(isc->hclock); microchip_isc_clk_cleanup(isc); - - return 0; } static int __maybe_unused isc_runtime_suspend(struct device *dev) @@ -668,7 +666,7 @@ MODULE_DEVICE_TABLE(of, microchip_isc_of_match); static struct platform_driver microchip_isc_driver = { .probe = microchip_isc_probe, - .remove = microchip_isc_remove, + .remove_new = 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 d583eafe5cc1..79ae696764d0 100644 --- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c +++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c @@ -577,7 +577,7 @@ unprepare_hclk: return ret; } -static int microchip_xisc_remove(struct platform_device *pdev) +static void microchip_xisc_remove(struct platform_device *pdev) { struct isc_device *isc = platform_get_drvdata(pdev); @@ -592,8 +592,6 @@ static int microchip_xisc_remove(struct platform_device *pdev) clk_disable_unprepare(isc->hclock); microchip_isc_clk_cleanup(isc); - - return 0; } static int __maybe_unused xisc_runtime_suspend(struct device *dev) @@ -631,7 +629,7 @@ MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); static struct platform_driver microchip_xisc_driver = { .probe = microchip_xisc_probe, - .remove = microchip_xisc_remove, + .remove_new = microchip_xisc_remove, .driver = { .name = "microchip-sama7g5-xisc", .pm = µchip_xisc_dev_pm_ops, diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.c b/drivers/media/platform/nvidia/tegra-vde/vde.c index f3e863a94c5a..7157734a1550 100644 --- a/drivers/media/platform/nvidia/tegra-vde/vde.c +++ b/drivers/media/platform/nvidia/tegra-vde/vde.c @@ -378,7 +378,7 @@ err_gen_free: return err; } -static int tegra_vde_remove(struct platform_device *pdev) +static void tegra_vde_remove(struct platform_device *pdev) { struct tegra_vde *vde = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -407,8 +407,6 @@ static int tegra_vde_remove(struct platform_device *pdev) gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, gen_pool_size(vde->iram_pool)); - - return 0; } static void tegra_vde_shutdown(struct platform_device *pdev) @@ -536,7 +534,7 @@ MODULE_DEVICE_TABLE(of, tegra_vde_of_match); static struct platform_driver tegra_vde_driver = { .probe = tegra_vde_probe, - .remove = tegra_vde_remove, + .remove_new = tegra_vde_remove, .shutdown = tegra_vde_shutdown, .driver = { .name = "tegra-vde", diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig index 730f39971e1c..a0ca6b297fb8 100644 --- a/drivers/media/platform/nxp/Kconfig +++ b/drivers/media/platform/nxp/Kconfig @@ -28,6 +28,8 @@ config VIDEO_IMX_MIPI_CSIS Video4Linux2 sub-device driver for the MIPI CSI-2 CSIS receiver v3.3/v3.6.3 found on some i.MX7 and i.MX8 SoCs. +source "drivers/media/platform/nxp/imx8-isi/Kconfig" + # mem2mem drivers config VIDEO_IMX_PXP diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile index 1a129b58d99e..b8e672b75fed 100644 --- a/drivers/media/platform/nxp/Makefile +++ b/drivers/media/platform/nxp/Makefile @@ -2,6 +2,7 @@ obj-y += dw100/ obj-y += imx-jpeg/ +obj-y += imx8-isi/ obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c index 189d60cd5ed1..0024d6175ad9 100644 --- a/drivers/media/platform/nxp/dw100/dw100.c +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -1532,7 +1532,6 @@ static int dw100_probe(struct platform_device *pdev) { struct dw100_device *dw_dev; struct video_device *vfd; - struct resource *res; int ret, irq; dw_dev = devm_kzalloc(&pdev->dev, sizeof(*dw_dev), GFP_KERNEL); @@ -1547,8 +1546,7 @@ static int dw100_probe(struct platform_device *pdev) } dw_dev->num_clks = ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dw_dev->mmio = devm_ioremap_resource(&pdev->dev, res); + dw_dev->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(dw_dev->mmio)) return PTR_ERR(dw_dev->mmio); @@ -1633,7 +1631,7 @@ err_pm: return ret; } -static int dw100_remove(struct platform_device *pdev) +static void dw100_remove(struct platform_device *pdev) { struct dw100_device *dw_dev = platform_get_drvdata(pdev); @@ -1649,8 +1647,6 @@ static int dw100_remove(struct platform_device *pdev) mutex_destroy(dw_dev->vfd.lock); v4l2_m2m_release(dw_dev->m2m_dev); v4l2_device_unregister(&dw_dev->v4l2_dev); - - return 0; } static int __maybe_unused dw100_runtime_suspend(struct device *dev) @@ -1692,7 +1688,7 @@ MODULE_DEVICE_TABLE(of, dw100_dt_ids); static struct platform_driver dw100_driver = { .probe = dw100_probe, - .remove = dw100_remove, + .remove_new = dw100_remove, .driver = { .name = DRV_NAME, .pm = &dw100_pm, diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c index ef28122a5ed4..9a6e8b332e12 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c @@ -97,25 +97,31 @@ void mxc_jpeg_sw_reset(void __iomem *reg) writel(GLB_CTRL_SFT_RST, reg + GLB_CTRL); } -void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg) +void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg, u8 extseq) { dev_dbg(dev, "CAST Encoder CONFIG...\n"); /* * "Config_Mode" enabled, "Config_Mode auto clear enabled", */ - writel(0xa0, reg + CAST_MODE); + if (extseq) + writel(0xb0, reg + CAST_MODE); + else + writel(0xa0, reg + CAST_MODE); /* all markers and segments */ writel(0x3ff, reg + CAST_CFG_MODE); } -void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg) +void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg, u8 extseq) { dev_dbg(dev, "CAST Encoder GO...\n"); /* * "GO" enabled, "GO bit auto clear" enabled */ - writel(0x140, reg + CAST_MODE); + if (extseq) + writel(0x150, reg + CAST_MODE); + else + writel(0x140, reg + CAST_MODE); } void mxc_jpeg_enc_set_quality(struct device *dev, void __iomem *reg, u8 quality) @@ -178,3 +184,8 @@ void mxc_jpeg_set_desc(u32 desc, void __iomem *reg, int slot) writel(desc | MXC_NXT_DESCPT_EN, reg + MXC_SLOT_OFFSET(slot, SLOT_NXT_DESCPT_PTR)); } + +void mxc_jpeg_clr_desc(void __iomem *reg, int slot) +{ + writel(0, reg + MXC_SLOT_OFFSET(slot, SLOT_NXT_DESCPT_PTR)); +} diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h index ecf3b6562ba2..ed15ea348f97 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h @@ -117,8 +117,8 @@ void print_wrapper_info(struct device *dev, void __iomem *reg); void mxc_jpeg_sw_reset(void __iomem *reg); int mxc_jpeg_enable(void __iomem *reg); void wait_frmdone(struct device *dev, void __iomem *reg); -void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg); -void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg); +void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg, u8 extseq); +void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg, u8 extseq); void mxc_jpeg_enc_set_quality(struct device *dev, void __iomem *reg, u8 quality); void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg); int mxc_jpeg_get_slot(void __iomem *reg); @@ -137,6 +137,7 @@ void mxc_jpeg_set_bufsize(struct mxc_jpeg_desc *desc, u32 bufsize); void mxc_jpeg_set_res(struct mxc_jpeg_desc *desc, u16 w, u16 h); void mxc_jpeg_set_line_pitch(struct mxc_jpeg_desc *desc, u32 line_pitch); void mxc_jpeg_set_desc(u32 desc, void __iomem *reg, int slot); +void mxc_jpeg_clr_desc(void __iomem *reg, int slot); void mxc_jpeg_set_regs_from_desc(struct mxc_jpeg_desc *desc, void __iomem *reg); #endif diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index f085f14d676a..c0e49be42450 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -88,6 +88,20 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { .is_rgb = 1, }, { + .name = "BGR 12bit", /*12-bit BGR packed format*/ + .fourcc = V4L2_PIX_FMT_BGR48_12, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + .nc = 3, + .depth = 36, + .mem_planes = 1, + .comp_planes = 1, + .h_align = 3, + .v_align = 3, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + .is_rgb = 1, + }, + { .name = "ABGR", /* ABGR packed format */ .fourcc = V4L2_PIX_FMT_ABGR32, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, @@ -102,6 +116,20 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { .is_rgb = 1, }, { + .name = "ABGR 12bit", /* 12-bit ABGR packed format */ + .fourcc = V4L2_PIX_FMT_ABGR64_12, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + .nc = 4, + .depth = 48, + .mem_planes = 1, + .comp_planes = 1, + .h_align = 3, + .v_align = 3, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + .is_rgb = 1, + }, + { .name = "YUV420", /* 1st plane = Y, 2nd plane = UV */ .fourcc = V4L2_PIX_FMT_NV12M, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, @@ -128,6 +156,32 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { .precision = 8, }, { + .name = "YUV420 12bit", /* 1st plane = Y, 2nd plane = UV */ + .fourcc = V4L2_PIX_FMT_P012M, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .nc = 3, + .depth = 18, /* 6 x 12 bits (4Y + UV) for 4 pixels */ + .mem_planes = 2, + .comp_planes = 2, /* 1 plane Y, 1 plane UV interleaved */ + .h_align = 4, + .v_align = 4, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + }, + { + .name = "YUV420 12bit", /* 1st plane = Y, 2nd plane = UV */ + .fourcc = V4L2_PIX_FMT_P012, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .nc = 3, + .depth = 18, /* 6 x 12 bits (4Y + UV) for 4 pixels */ + .mem_planes = 1, + .comp_planes = 2, /* 1 plane Y, 1 plane UV interleaved */ + .h_align = 4, + .v_align = 4, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + }, + { .name = "YUV422", /* YUYV */ .fourcc = V4L2_PIX_FMT_YUYV, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, @@ -141,6 +195,19 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { .precision = 8, }, { + .name = "YUV422 12bit", /* YUYV */ + .fourcc = V4L2_PIX_FMT_Y212, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .nc = 3, + .depth = 24, + .mem_planes = 1, + .comp_planes = 1, + .h_align = 4, + .v_align = 3, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + }, + { .name = "YUV444", /* YUVYUV */ .fourcc = V4L2_PIX_FMT_YUV24, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, @@ -154,6 +221,19 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { .precision = 8, }, { + .name = "YUV444 12bit", /* YUVYUV */ + .fourcc = V4L2_PIX_FMT_YUV48_12, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + .nc = 3, + .depth = 36, + .mem_planes = 1, + .comp_planes = 1, + .h_align = 3, + .v_align = 3, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + }, + { .name = "Gray", /* Gray (Y8/Y12) or Single Comp */ .fourcc = V4L2_PIX_FMT_GREY, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, @@ -166,6 +246,19 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { .flags = MXC_JPEG_FMT_TYPE_RAW, .precision = 8, }, + { + .name = "Gray 12bit", /* Gray (Y8/Y12) or Single Comp */ + .fourcc = V4L2_PIX_FMT_Y012, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, + .nc = 1, + .depth = 12, + .mem_planes = 1, + .comp_planes = 1, + .h_align = 3, + .v_align = 3, + .flags = MXC_JPEG_FMT_TYPE_RAW, + .precision = 12, + }, }; #define MXC_JPEG_NUM_FORMATS ARRAY_SIZE(mxc_formats) @@ -229,6 +322,45 @@ static const unsigned char jpeg_dqt[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 }; +static const unsigned char jpeg_dqt_extseq[] = { + 0xFF, 0xDB, + 0x01, 0x04, + 0x10, + 0x00, 0x80, 0x00, 0x58, 0x00, 0x60, 0x00, 0x70, + 0x00, 0x60, 0x00, 0x50, 0x00, 0x80, 0x00, 0x70, + 0x00, 0x68, 0x00, 0x70, 0x00, 0x90, 0x00, 0x88, + 0x00, 0x80, 0x00, 0x98, 0x00, 0xC0, 0x01, 0x40, + 0x00, 0xD0, 0x00, 0xC0, 0x00, 0xB0, 0x00, 0xB0, + 0x00, 0xC0, 0x01, 0x88, 0x01, 0x18, 0x01, 0x28, + 0x00, 0xE8, 0x01, 0x40, 0x01, 0xD0, 0x01, 0x98, + 0x01, 0xE8, 0x01, 0xE0, 0x01, 0xC8, 0x01, 0x98, + 0x01, 0xC0, 0x01, 0xB8, 0x02, 0x00, 0x02, 0x40, + 0x02, 0xE0, 0x02, 0x70, 0x02, 0x00, 0x02, 0x20, + 0x02, 0xB8, 0x02, 0x28, 0x01, 0xB8, 0x01, 0xC0, + 0x02, 0x80, 0x03, 0x68, 0x02, 0x88, 0x02, 0xB8, + 0x02, 0xF8, 0x03, 0x10, 0x03, 0x38, 0x03, 0x40, + 0x03, 0x38, 0x01, 0xF0, 0x02, 0x68, 0x03, 0x88, + 0x03, 0xC8, 0x03, 0x80, 0x03, 0x20, 0x03, 0xC0, + 0x02, 0xE0, 0x03, 0x28, 0x03, 0x38, 0x03, 0x18, + 0x11, + 0x00, 0x88, 0x00, 0x90, 0x00, 0x90, 0x00, 0xC0, + 0x00, 0xA8, 0x00, 0xC0, 0x01, 0x78, 0x00, 0xD0, + 0x00, 0xD0, 0x01, 0x78, 0x03, 0x18, 0x02, 0x10, + 0x01, 0xC0, 0x02, 0x10, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, +}; + static const unsigned char jpeg_sof_maximal[] = { 0xFF, 0xC0, 0x00, 0x14, 0x08, 0x00, 0x40, 0x00, 0x40, @@ -236,6 +368,13 @@ static const unsigned char jpeg_sof_maximal[] = { 0x03, 0x11, 0x01, 0x04, 0x11, 0x01 }; +static const unsigned char jpeg_sof_extseq[] = { + 0xFF, 0xC1, + 0x00, 0x14, 0x08, 0x00, 0x40, 0x00, 0x40, + 0x04, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, + 0x03, 0x11, 0x01, 0x04, 0x11, 0x01 +}; + static const unsigned char jpeg_dht[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, @@ -300,6 +439,90 @@ static const unsigned char jpeg_dht[] = { 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; +static const unsigned char jpeg_dht_extseq[] = { + 0xFF, 0xC4, + 0x02, 0x2a, 0x00, 0x00, 0x01, 0x05, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x02, 0x03, 0x02, + 0x00, 0x00, 0xbf, 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, 0x0b, 0x0c, 0x0d, + 0x0e, 0x1b, 0x1c, 0x1d, 0x1e, 0x2b, 0x2c, + 0x2d, 0x2e, 0x3b, 0x3c, 0x3d, 0x3e, 0x4b, + 0x4c, 0x4d, 0x4e, 0x5b, 0x5c, 0x5d, 0x5e, + 0x6b, 0x6c, 0x6d, 0x6e, 0x7b, 0x7c, 0x7d, + 0x7e, 0x8b, 0x8c, 0x8d, 0x8e, 0x9b, 0x9c, + 0x9d, 0x9e, 0xab, 0xac, 0xad, 0xae, 0xbb, + 0xbc, 0xbd, 0xbe, 0xcb, 0xcc, 0xcd, 0xce, + 0xdb, 0xdc, 0xdd, 0xde, 0xeb, 0xec, 0xed, + 0xee, 0xfb, 0xfc, 0xfd, 0xfe, 0x01, 0x00, + 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x11, 0x00, 0x02, 0x01, + 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, + 0x02, 0x03, 0x02, 0x00, 0x00, 0xbf, 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, + 0x0b, 0x0c, 0x0d, 0x0e, 0x1b, 0x1c, 0x1d, + 0x1e, 0x2b, 0x2c, 0x2d, 0x2e, 0x3b, 0x3c, + 0x3d, 0x3e, 0x4b, 0x4c, 0x4d, 0x4e, 0x5b, + 0x5c, 0x5d, 0x5e, 0x6b, 0x6c, 0x6d, 0x6e, + 0x7b, 0x7c, 0x7d, 0x7e, 0x8b, 0x8c, 0x8d, + 0x8e, 0x9b, 0x9c, 0x9d, 0x9e, 0xab, 0xac, + 0xad, 0xae, 0xbb, 0xbc, 0xbd, 0xbe, 0xcb, + 0xcc, 0xcd, 0xce, 0xdb, 0xdc, 0xdd, 0xde, + 0xeb, 0xec, 0xed, 0xee, 0xfb, 0xfc, 0xfd, + 0xfe, +}; + static const unsigned char jpeg_dri[] = { 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x20 @@ -420,8 +643,7 @@ static int enum_fmt(const struct mxc_jpeg_fmt *mxc_formats, int n, return 0; } -static const struct mxc_jpeg_fmt *mxc_jpeg_find_format(struct mxc_jpeg_ctx *ctx, - u32 pixelformat) +static const struct mxc_jpeg_fmt *mxc_jpeg_find_format(u32 pixelformat) { unsigned int k; @@ -438,17 +660,24 @@ static enum mxc_jpeg_image_format mxc_jpeg_fourcc_to_imgfmt(u32 fourcc) { switch (fourcc) { case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y012: return MXC_JPEG_GRAY; case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_Y212: return MXC_JPEG_YUV422; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_P012: + case V4L2_PIX_FMT_P012M: return MXC_JPEG_YUV420; case V4L2_PIX_FMT_YUV24: + case V4L2_PIX_FMT_YUV48_12: return MXC_JPEG_YUV444; case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_BGR48_12: return MXC_JPEG_BGR; case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_ABGR64_12: return MXC_JPEG_ABGR; default: return MXC_JPEG_INVALID; @@ -484,6 +713,17 @@ static void mxc_jpeg_addrs(struct mxc_jpeg_desc *desc, offset; } +static bool mxc_jpeg_is_extended_sequential(const struct mxc_jpeg_fmt *fmt) +{ + if (!fmt || !(fmt->flags & MXC_JPEG_FMT_TYPE_RAW)) + return false; + + if (fmt->precision > 8) + return true; + + return false; +} + static void notify_eos(struct mxc_jpeg_ctx *ctx) { const struct v4l2_event ev = { @@ -497,10 +737,9 @@ static void notify_eos(struct mxc_jpeg_ctx *ctx) static void notify_src_chg(struct mxc_jpeg_ctx *ctx) { const struct v4l2_event ev = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = - V4L2_EVENT_SRC_CH_RESOLUTION, - }; + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; dev_dbg(ctx->mxc_jpeg->dev, "Notify app event SRC_CH_RESOLUTION"); v4l2_event_queue_fh(&ctx->fh, &ev); @@ -637,6 +876,11 @@ static u32 mxc_jpeg_get_plane_size(struct mxc_jpeg_q_data *q_data, u32 plane_no) return q_data->sizeimage[plane_no]; size = q_data->sizeimage[fmt->mem_planes - 1]; + + /* Should be impossible given mxc_formats. */ + if (WARN_ON_ONCE(fmt->comp_planes > ARRAY_SIZE(q_data->sizeimage))) + return size; + for (i = fmt->mem_planes; i < fmt->comp_planes; i++) size += q_data->sizeimage[i]; @@ -692,7 +936,9 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) if (dec_ret & SLOT_STATUS_ENC_CONFIG_ERR) { u32 ret = readl(reg + CAST_STATUS12); - dev_err(dev, "Encoder/decoder error, status=0x%08x", ret); + dev_err(dev, "Encoder/decoder error, dec_ret = 0x%08x, status=0x%08x", + dec_ret, ret); + mxc_jpeg_clr_desc(reg, slot); mxc_jpeg_sw_reset(reg); buf_state = VB2_BUF_STATE_ERROR; goto buffers_done; @@ -703,10 +949,11 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) if (jpeg->mode == MXC_JPEG_ENCODE && ctx->enc_state == MXC_JPEG_ENC_CONF) { + q_data = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); ctx->enc_state = MXC_JPEG_ENCODING; dev_dbg(dev, "Encoder config finished. Start encoding...\n"); mxc_jpeg_enc_set_quality(dev, reg, ctx->jpeg_quality); - mxc_jpeg_enc_mode_go(dev, reg); + mxc_jpeg_enc_mode_go(dev, reg, mxc_jpeg_is_extended_sequential(q_data->fmt)); goto job_unlock; } if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed) { @@ -757,8 +1004,12 @@ static int mxc_jpeg_fixup_sof(struct mxc_jpeg_sof *sof, u16 w, u16 h) { int sof_length; + const struct mxc_jpeg_fmt *fmt = mxc_jpeg_find_format(fourcc); - sof->precision = 8; /* TODO allow 8/12 bit precision*/ + if (fmt) + sof->precision = fmt->precision; + else + sof->precision = 8; /* TODO allow 8/12 bit precision*/ sof->height = h; _bswap16(&sof->height); sof->width = w; @@ -767,24 +1018,31 @@ static int mxc_jpeg_fixup_sof(struct mxc_jpeg_sof *sof, switch (fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_P012: + case V4L2_PIX_FMT_P012M: sof->components_no = 3; sof->comp[0].v = 0x2; sof->comp[0].h = 0x2; break; case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_Y212: sof->components_no = 3; sof->comp[0].v = 0x1; sof->comp[0].h = 0x2; break; case V4L2_PIX_FMT_YUV24: + case V4L2_PIX_FMT_YUV48_12: case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_BGR48_12: default: sof->components_no = 3; break; case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_ABGR64_12: sof->components_no = 4; break; case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y012: sof->components_no = 1; break; } @@ -804,20 +1062,27 @@ static int mxc_jpeg_fixup_sos(struct mxc_jpeg_sos *sos, switch (fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_P012: + case V4L2_PIX_FMT_P012M: sos->components_no = 3; break; case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_Y212: sos->components_no = 3; break; case V4L2_PIX_FMT_YUV24: + case V4L2_PIX_FMT_YUV48_12: case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_BGR48_12: default: sos->components_no = 3; break; case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_ABGR64_12: sos->components_no = 4; break; case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y012: sos->components_no = 1; break; } @@ -847,12 +1112,15 @@ static unsigned int mxc_jpeg_setup_cfg_stream(void *cfg_stream_vaddr, u8 *cfg = (u8 *)cfg_stream_vaddr; struct mxc_jpeg_sof *sof; struct mxc_jpeg_sos *sos; + const struct mxc_jpeg_fmt *fmt = mxc_jpeg_find_format(fourcc); + + if (!fmt) + return 0; memcpy(cfg + offset, jpeg_soi, ARRAY_SIZE(jpeg_soi)); offset += ARRAY_SIZE(jpeg_soi); - if (fourcc == V4L2_PIX_FMT_BGR24 || - fourcc == V4L2_PIX_FMT_ABGR32) { + if (fmt->is_rgb) { memcpy(cfg + offset, jpeg_app14, sizeof(jpeg_app14)); offset += sizeof(jpeg_app14); } else { @@ -860,16 +1128,28 @@ static unsigned int mxc_jpeg_setup_cfg_stream(void *cfg_stream_vaddr, offset += sizeof(jpeg_app0); } - memcpy(cfg + offset, jpeg_dqt, sizeof(jpeg_dqt)); - offset += sizeof(jpeg_dqt); + if (mxc_jpeg_is_extended_sequential(fmt)) { + memcpy(cfg + offset, jpeg_dqt_extseq, sizeof(jpeg_dqt_extseq)); + offset += sizeof(jpeg_dqt_extseq); - memcpy(cfg + offset, jpeg_sof_maximal, sizeof(jpeg_sof_maximal)); + memcpy(cfg + offset, jpeg_sof_extseq, sizeof(jpeg_sof_extseq)); + } else { + memcpy(cfg + offset, jpeg_dqt, sizeof(jpeg_dqt)); + offset += sizeof(jpeg_dqt); + + memcpy(cfg + offset, jpeg_sof_maximal, sizeof(jpeg_sof_maximal)); + } offset += 2; /* skip marker ID */ sof = (struct mxc_jpeg_sof *)(cfg + offset); offset += mxc_jpeg_fixup_sof(sof, fourcc, w, h); - memcpy(cfg + offset, jpeg_dht, sizeof(jpeg_dht)); - offset += sizeof(jpeg_dht); + if (mxc_jpeg_is_extended_sequential(fmt)) { + memcpy(cfg + offset, jpeg_dht_extseq, sizeof(jpeg_dht_extseq)); + offset += sizeof(jpeg_dht_extseq); + } else { + memcpy(cfg + offset, jpeg_dht, sizeof(jpeg_dht)); + offset += sizeof(jpeg_dht); + } memcpy(cfg + offset, jpeg_dri, sizeof(jpeg_dri)); offset += sizeof(jpeg_dri); @@ -918,6 +1198,10 @@ static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf, desc->stm_ctrl &= ~STM_CTRL_IMAGE_FORMAT(0xF); /* clear image format */ desc->stm_ctrl |= STM_CTRL_IMAGE_FORMAT(img_fmt); desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1); + if (mxc_jpeg_is_extended_sequential(jpeg_src_buf->fmt)) + desc->stm_ctrl |= STM_CTRL_PIXEL_PRECISION; + else + desc->stm_ctrl &= ~STM_CTRL_PIXEL_PRECISION; desc->line_pitch = q_data_cap->bytesperline[0]; mxc_jpeg_addrs(desc, dst_buf, src_buf, 0); mxc_jpeg_set_bufsize(desc, ALIGN(vb2_plane_size(src_buf, 0), 1024)); @@ -1006,6 +1290,10 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf, desc->stm_ctrl = STM_CTRL_CONFIG_MOD(0) | STM_CTRL_IMAGE_FORMAT(img_fmt); desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1); + if (mxc_jpeg_is_extended_sequential(q_data->fmt)) + desc->stm_ctrl |= STM_CTRL_PIXEL_PRECISION; + else + desc->stm_ctrl &= ~STM_CTRL_PIXEL_PRECISION; mxc_jpeg_addrs(desc, src_buf, dst_buf, 0); dev_dbg(jpeg->dev, "cfg_desc:\n"); print_descriptor_info(jpeg->dev, cfg_desc); @@ -1206,7 +1494,9 @@ static void mxc_jpeg_device_run(void *priv) ctx->enc_state = MXC_JPEG_ENC_CONF; mxc_jpeg_config_enc_desc(&dst_buf->vb2_buf, ctx, &src_buf->vb2_buf, &dst_buf->vb2_buf); - mxc_jpeg_enc_mode_conf(dev, reg); /* start config phase */ + /* start config phase */ + mxc_jpeg_enc_mode_conf(dev, reg, + mxc_jpeg_is_extended_sequential(q_data_out->fmt)); } else { dev_dbg(dev, "Decoding on slot %d\n", ctx->slot); print_mxc_buf(jpeg, &src_buf->vb2_buf, 0); @@ -1562,7 +1852,7 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb) if (fourcc == 0) return -EINVAL; - jpeg_src_buf->fmt = mxc_jpeg_find_format(ctx, fourcc); + jpeg_src_buf->fmt = mxc_jpeg_find_format(fourcc); jpeg_src_buf->w = header.frame.width; jpeg_src_buf->h = header.frame.height; ctx->header_parsed = true; @@ -1702,11 +1992,11 @@ static void mxc_jpeg_set_default_params(struct mxc_jpeg_ctx *ctx) int i; if (ctx->mxc_jpeg->mode == MXC_JPEG_ENCODE) { - out_q->fmt = mxc_jpeg_find_format(ctx, MXC_JPEG_DEFAULT_PFMT); - cap_q->fmt = mxc_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG); + out_q->fmt = mxc_jpeg_find_format(MXC_JPEG_DEFAULT_PFMT); + cap_q->fmt = mxc_jpeg_find_format(V4L2_PIX_FMT_JPEG); } else { - out_q->fmt = mxc_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG); - cap_q->fmt = mxc_jpeg_find_format(ctx, MXC_JPEG_DEFAULT_PFMT); + out_q->fmt = mxc_jpeg_find_format(V4L2_PIX_FMT_JPEG); + cap_q->fmt = mxc_jpeg_find_format(MXC_JPEG_DEFAULT_PFMT); } for (i = 0; i < 2; i++) { @@ -1950,7 +2240,7 @@ static int mxc_jpeg_try_fmt(struct v4l2_format *f, pix_mp->height : MXC_JPEG_MAX_HEIGHT; int i; - fmt = mxc_jpeg_find_format(ctx, fourcc); + fmt = mxc_jpeg_find_format(fourcc); if (!fmt || fmt->flags != mxc_jpeg_get_fmt_type(ctx, f->type)) { dev_warn(ctx->mxc_jpeg->dev, "Format not supported: %c%c%c%c, use the default.\n", (fourcc & 0xff), @@ -1958,7 +2248,7 @@ static int mxc_jpeg_try_fmt(struct v4l2_format *f, (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff); fourcc = mxc_jpeg_get_default_fourcc(ctx, f->type); - fmt = mxc_jpeg_find_format(ctx, fourcc); + fmt = mxc_jpeg_find_format(fourcc); if (!fmt) return -EINVAL; f->fmt.pix_mp.pixelformat = fourcc; @@ -2622,7 +2912,7 @@ static const struct dev_pm_ops mxc_jpeg_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mxc_jpeg_suspend, mxc_jpeg_resume) }; -static int mxc_jpeg_remove(struct platform_device *pdev) +static void mxc_jpeg_remove(struct platform_device *pdev) { unsigned int slot; struct mxc_jpeg_dev *jpeg = platform_get_drvdata(pdev); @@ -2635,15 +2925,13 @@ static int mxc_jpeg_remove(struct platform_device *pdev) v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); mxc_jpeg_detach_pm_domains(jpeg); - - return 0; } MODULE_DEVICE_TABLE(of, mxc_jpeg_match); static struct platform_driver mxc_jpeg_driver = { .probe = mxc_jpeg_probe, - .remove = mxc_jpeg_remove, + .remove_new = mxc_jpeg_remove, .driver = { .name = "mxc-jpeg", .of_match_table = mxc_jpeg_match, diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index be2768a47995..05d52762e792 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1503,7 +1503,7 @@ err_disable_clock: return ret; } -static int mipi_csis_remove(struct platform_device *pdev) +static void mipi_csis_remove(struct platform_device *pdev) { struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); @@ -1520,8 +1520,6 @@ static int mipi_csis_remove(struct platform_device *pdev) media_entity_cleanup(&csis->sd.entity); fwnode_handle_put(csis->sd.fwnode); pm_runtime_set_suspended(&pdev->dev); - - return 0; } static const struct of_device_id mipi_csis_of_match[] = { @@ -1544,7 +1542,7 @@ MODULE_DEVICE_TABLE(of, mipi_csis_of_match); static struct platform_driver mipi_csis_driver = { .probe = mipi_csis_probe, - .remove = mipi_csis_remove, + .remove_new = mipi_csis_remove, .driver = { .of_match_table = mipi_csis_of_match, .name = CSIS_DRIVER_NAME, diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index fde3c36e5e1d..90f319857c23 100644 --- a/drivers/media/platform/nxp/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c @@ -1904,7 +1904,7 @@ err_clk: return ret; } -static int pxp_remove(struct platform_device *pdev) +static void pxp_remove(struct platform_device *pdev) { struct pxp_dev *dev = platform_get_drvdata(pdev); @@ -1922,8 +1922,6 @@ static int pxp_remove(struct platform_device *pdev) video_unregister_device(&dev->vfd); v4l2_m2m_release(dev->m2m_dev); v4l2_device_unregister(&dev->v4l2_dev); - - return 0; } static const struct pxp_pdata pxp_imx6ull_pdata = { @@ -1943,7 +1941,7 @@ MODULE_DEVICE_TABLE(of, pxp_dt_ids); static struct platform_driver pxp_driver = { .probe = pxp_probe, - .remove = pxp_remove, + .remove_new = pxp_remove, .driver = { .name = MEM2MEM_NAME, .of_match_table = pxp_dt_ids, diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index c22bf5c827e7..b701e823436a 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -1214,6 +1214,9 @@ static int imx7_csi_video_g_selection(struct file *file, void *fh, { struct imx7_csi *csi = video_drvdata(file); + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + switch (s->target) { case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: @@ -1610,6 +1613,7 @@ static int imx7_csi_video_init_format(struct imx7_csi *csi) format.code = IMX7_CSI_DEF_MBUS_CODE; format.width = IMX7_CSI_DEF_PIX_WIDTH; format.height = IMX7_CSI_DEF_PIX_HEIGHT; + format.field = V4L2_FIELD_NONE; imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &format, NULL); csi->vdev_compose.width = format.width; @@ -2107,18 +2111,21 @@ static int imx7_csi_async_register(struct imx7_csi *csi) ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); - if (ep) { - asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, - struct v4l2_async_subdev); + if (!ep) { + ret = dev_err_probe(csi->dev, -ENOTCONN, + "Failed to get remote endpoint\n"); + goto error; + } - fwnode_handle_put(ep); + asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, + struct v4l2_async_subdev); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - /* OK if asd already exists */ - if (ret != -EEXIST) - goto error; - } + fwnode_handle_put(ep); + + if (IS_ERR(asd)) { + ret = dev_err_probe(csi->dev, PTR_ERR(asd), + "Failed to add remote subdev to notifier\n"); + goto error; } csi->notifier.ops = &imx7_csi_notify_ops; @@ -2278,7 +2285,7 @@ err_media_cleanup: return ret; } -static int imx7_csi_remove(struct platform_device *pdev) +static void imx7_csi_remove(struct platform_device *pdev) { struct imx7_csi *csi = platform_get_drvdata(pdev); @@ -2287,8 +2294,6 @@ static int imx7_csi_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&csi->notifier); v4l2_async_nf_cleanup(&csi->notifier); v4l2_async_unregister_subdev(&csi->sd); - - return 0; } static const struct of_device_id imx7_csi_of_match[] = { @@ -2301,7 +2306,7 @@ MODULE_DEVICE_TABLE(of, imx7_csi_of_match); static struct platform_driver imx7_csi_driver = { .probe = imx7_csi_probe, - .remove = imx7_csi_remove, + .remove_new = imx7_csi_remove, .driver = { .of_match_table = imx7_csi_of_match, .name = "imx7-csi", diff --git a/drivers/media/platform/nxp/imx8-isi/Kconfig b/drivers/media/platform/nxp/imx8-isi/Kconfig new file mode 100644 index 000000000000..fcff33fc2630 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_IMX8_ISI + tristate "i.MX8 Image Sensor Interface (ISI) driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_DMA && PM + depends on VIDEO_DEV + select MEDIA_CONTROLLER + select V4L2_FWNODE + select V4L2_MEM2MEM_DEV if VIDEO_IMX8_ISI_M2M + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + help + V4L2 driver for the Image Sensor Interface (ISI) found in various + i.MX8 SoCs. + +config VIDEO_IMX8_ISI_M2M + bool "i.MX8 Image Sensor Interface (ISI) memory-to-memory support" + depends on VIDEO_IMX8_ISI + help + Select 'yes' here to enable support for memory-to-memory processing + in the ISI driver. diff --git a/drivers/media/platform/nxp/imx8-isi/Makefile b/drivers/media/platform/nxp/imx8-isi/Makefile new file mode 100644 index 000000000000..9bff9297686d --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +imx8-isi-y := imx8-isi-core.o imx8-isi-crossbar.o imx8-isi-hw.o \ + imx8-isi-pipe.o imx8-isi-video.o +imx8-isi-$(CONFIG_DEBUG_FS) += imx8-isi-debug.o +imx8-isi-$(CONFIG_VIDEO_IMX8_ISI_M2M) += imx8-isi-m2m.o + +obj-$(CONFIG_VIDEO_IMX8_ISI) += imx8-isi.o diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c new file mode 100644 index 000000000000..253e77189b69 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019-2020 NXP + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mc.h> + +#include "imx8-isi-core.h" + +/* ----------------------------------------------------------------------------- + * V4L2 async subdevs + */ + +struct mxc_isi_async_subdev { + struct v4l2_async_subdev asd; + unsigned int port; +}; + +static inline struct mxc_isi_async_subdev * +asd_to_mxc_isi_async_subdev(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct mxc_isi_async_subdev, asd); +}; + +static inline struct mxc_isi_dev * +notifier_to_mxc_isi_dev(struct v4l2_async_notifier *n) +{ + return container_of(n, struct mxc_isi_dev, notifier); +}; + +static int mxc_isi_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + struct mxc_isi_dev *isi = notifier_to_mxc_isi_dev(notifier); + struct mxc_isi_async_subdev *masd = asd_to_mxc_isi_async_subdev(asd); + struct media_pad *pad = &isi->crossbar.pads[masd->port]; + struct device_link *link; + + dev_dbg(isi->dev, "Bound subdev %s to crossbar input %u\n", sd->name, + masd->port); + + /* + * Enforce suspend/resume ordering between the source (supplier) and + * the ISI (consumer). The source will be suspended before and resume + * after the ISI. + */ + link = device_link_add(isi->dev, sd->dev, DL_FLAG_STATELESS); + if (!link) { + dev_err(isi->dev, + "Failed to create device link to source %s\n", sd->name); + return -EINVAL; + } + + return v4l2_create_fwnode_links_to_pad(sd, pad, link_flags); +} + +static int mxc_isi_async_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct mxc_isi_dev *isi = notifier_to_mxc_isi_dev(notifier); + int ret; + + dev_dbg(isi->dev, "All subdevs bound\n"); + + ret = v4l2_device_register_subdev_nodes(&isi->v4l2_dev); + if (ret < 0) { + dev_err(isi->dev, + "Failed to register subdev nodes: %d\n", ret); + return ret; + } + + return media_device_register(&isi->media_dev); +} + +static const struct v4l2_async_notifier_operations mxc_isi_async_notifier_ops = { + .bound = mxc_isi_async_notifier_bound, + .complete = mxc_isi_async_notifier_complete, +}; + +static int mxc_isi_pipe_register(struct mxc_isi_pipe *pipe) +{ + int ret; + + ret = v4l2_device_register_subdev(&pipe->isi->v4l2_dev, &pipe->sd); + if (ret < 0) + return ret; + + return mxc_isi_video_register(pipe, &pipe->isi->v4l2_dev); +} + +static void mxc_isi_pipe_unregister(struct mxc_isi_pipe *pipe) +{ + mxc_isi_video_unregister(pipe); +} + +static int mxc_isi_v4l2_init(struct mxc_isi_dev *isi) +{ + struct fwnode_handle *node = dev_fwnode(isi->dev); + struct media_device *media_dev = &isi->media_dev; + struct v4l2_device *v4l2_dev = &isi->v4l2_dev; + unsigned int i; + int ret; + + /* Initialize the media device. */ + strscpy(media_dev->model, "FSL Capture Media Device", + sizeof(media_dev->model)); + media_dev->dev = isi->dev; + + media_device_init(media_dev); + + /* Initialize and register the V4L2 device. */ + v4l2_dev->mdev = media_dev; + strscpy(v4l2_dev->name, "mx8-img-md", sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(isi->dev, v4l2_dev); + if (ret < 0) { + dev_err(isi->dev, + "Failed to register V4L2 device: %d\n", ret); + goto err_media; + } + + /* Register the crossbar switch subdev. */ + ret = mxc_isi_crossbar_register(&isi->crossbar); + if (ret < 0) { + dev_err(isi->dev, "Failed to register crossbar: %d\n", ret); + goto err_v4l2; + } + + /* Register the pipeline subdevs and link them to the crossbar switch. */ + for (i = 0; i < isi->pdata->num_channels; ++i) { + struct mxc_isi_pipe *pipe = &isi->pipes[i]; + + ret = mxc_isi_pipe_register(pipe); + if (ret < 0) { + dev_err(isi->dev, "Failed to register pipe%u: %d\n", i, + ret); + goto err_v4l2; + } + + ret = media_create_pad_link(&isi->crossbar.sd.entity, + isi->crossbar.num_sinks + i, + &pipe->sd.entity, + MXC_ISI_PIPE_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + goto err_v4l2; + } + + /* Register the M2M device. */ + ret = mxc_isi_m2m_register(isi, v4l2_dev); + if (ret < 0) { + dev_err(isi->dev, "Failed to register M2M device: %d\n", ret); + goto err_v4l2; + } + + /* Initialize, fill and register the async notifier. */ + v4l2_async_nf_init(&isi->notifier); + isi->notifier.ops = &mxc_isi_async_notifier_ops; + + for (i = 0; i < isi->pdata->num_ports; ++i) { + struct mxc_isi_async_subdev *masd; + struct fwnode_handle *ep; + + ep = fwnode_graph_get_endpoint_by_id(node, i, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + + if (!ep) + continue; + + masd = v4l2_async_nf_add_fwnode_remote(&isi->notifier, ep, + struct mxc_isi_async_subdev); + fwnode_handle_put(ep); + + if (IS_ERR(masd)) { + ret = PTR_ERR(masd); + goto err_m2m; + } + + masd->port = i; + } + + ret = v4l2_async_nf_register(v4l2_dev, &isi->notifier); + if (ret < 0) { + dev_err(isi->dev, + "Failed to register async notifier: %d\n", ret); + goto err_m2m; + } + + return 0; + +err_m2m: + mxc_isi_m2m_unregister(isi); + v4l2_async_nf_cleanup(&isi->notifier); +err_v4l2: + v4l2_device_unregister(v4l2_dev); +err_media: + media_device_cleanup(media_dev); + return ret; +} + +static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi) +{ + unsigned int i; + + v4l2_async_nf_unregister(&isi->notifier); + v4l2_async_nf_cleanup(&isi->notifier); + + v4l2_device_unregister(&isi->v4l2_dev); + media_device_unregister(&isi->media_dev); + + mxc_isi_m2m_unregister(isi); + + for (i = 0; i < isi->pdata->num_channels; ++i) + mxc_isi_pipe_unregister(&isi->pipes[i]); + + mxc_isi_crossbar_unregister(&isi->crossbar); + + media_device_cleanup(&isi->media_dev); +} + +/* ----------------------------------------------------------------------------- + * Device information + */ + +/* Panic will assert when the buffers are 50% full */ + +/* For i.MX8QXP C0 and i.MX8MN ISI IER version */ +static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v1 = { + .oflw_y_buf_en = { .offset = 19, .mask = 0x80000 }, + .oflw_u_buf_en = { .offset = 21, .mask = 0x200000 }, + .oflw_v_buf_en = { .offset = 23, .mask = 0x800000 }, + + .panic_y_buf_en = {.offset = 20, .mask = 0x100000 }, + .panic_u_buf_en = {.offset = 22, .mask = 0x400000 }, + .panic_v_buf_en = {.offset = 24, .mask = 0x1000000 }, +}; + +/* For i.MX8MP ISI IER version */ +static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v2 = { + .oflw_y_buf_en = { .offset = 18, .mask = 0x40000 }, + .oflw_u_buf_en = { .offset = 20, .mask = 0x100000 }, + .oflw_v_buf_en = { .offset = 22, .mask = 0x400000 }, + + .panic_y_buf_en = {.offset = 19, .mask = 0x80000 }, + .panic_u_buf_en = {.offset = 21, .mask = 0x200000 }, + .panic_v_buf_en = {.offset = 23, .mask = 0x800000 }, +}; + +/* Panic will assert when the buffers are 50% full */ +static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1 = { + .panic_set_thd_y = { .mask = 0x0000f, .offset = 0, .threshold = 0x7 }, + .panic_set_thd_u = { .mask = 0x00f00, .offset = 8, .threshold = 0x7 }, + .panic_set_thd_v = { .mask = 0xf0000, .offset = 16, .threshold = 0x7 }, +}; + +static const struct clk_bulk_data mxc_imx8mn_clks[] = { + { .id = "axi" }, + { .id = "apb" }, +}; + +static const struct mxc_isi_plat_data mxc_imx8mn_data = { + .model = MXC_ISI_IMX8MN, + .num_ports = 1, + .num_channels = 1, + .reg_offset = 0, + .ier_reg = &mxc_imx8_isi_ier_v1, + .set_thd = &mxc_imx8_isi_thd_v1, + .clks = mxc_imx8mn_clks, + .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), + .buf_active_reverse = false, + .has_gasket = true, + .has_36bit_dma = false, +}; + +static const struct mxc_isi_plat_data mxc_imx8mp_data = { + .model = MXC_ISI_IMX8MP, + .num_ports = 2, + .num_channels = 2, + .reg_offset = 0x2000, + .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_gasket = true, + .has_36bit_dma = true, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int mxc_isi_pm_suspend(struct device *dev) +{ + struct mxc_isi_dev *isi = dev_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < isi->pdata->num_channels; ++i) { + struct mxc_isi_pipe *pipe = &isi->pipes[i]; + + mxc_isi_video_suspend(pipe); + } + + return pm_runtime_force_suspend(dev); +} + +static int mxc_isi_pm_resume(struct device *dev) +{ + struct mxc_isi_dev *isi = dev_get_drvdata(dev); + unsigned int i; + int err = 0; + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + + for (i = 0; i < isi->pdata->num_channels; ++i) { + struct mxc_isi_pipe *pipe = &isi->pipes[i]; + + ret = mxc_isi_video_resume(pipe); + if (ret) { + dev_err(dev, "Failed to resume pipeline %u (%d)\n", i, + ret); + /* + * Record the last error as it's as meaningful as any, + * and continue resuming the other pipelines. + */ + err = ret; + } + } + + return err; +} + +static int mxc_isi_runtime_suspend(struct device *dev) +{ + struct mxc_isi_dev *isi = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(isi->pdata->num_clks, isi->clks); + + return 0; +} + +static int mxc_isi_runtime_resume(struct device *dev) +{ + struct mxc_isi_dev *isi = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(isi->pdata->num_clks, isi->clks); + if (ret) { + dev_err(dev, "Failed to enable clocks (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops mxc_isi_pm_ops = { + SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend, mxc_isi_pm_resume) + RUNTIME_PM_OPS(mxc_isi_runtime_suspend, mxc_isi_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe, remove & driver + */ + +static int mxc_isi_clk_get(struct mxc_isi_dev *isi) +{ + unsigned int size = isi->pdata->num_clks + * sizeof(*isi->clks); + int ret; + + isi->clks = devm_kmalloc(isi->dev, size, GFP_KERNEL); + if (!isi->clks) + return -ENOMEM; + + memcpy(isi->clks, isi->pdata->clks, size); + + ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks, + isi->clks); + if (ret < 0) { + dev_err(isi->dev, "Failed to acquire clocks: %d\n", + ret); + return ret; + } + + return 0; +} + +static int mxc_isi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mxc_isi_dev *isi; + unsigned int dma_size; + unsigned int i; + int ret = 0; + + isi = devm_kzalloc(dev, sizeof(*isi), GFP_KERNEL); + if (!isi) + return -ENOMEM; + + isi->dev = dev; + platform_set_drvdata(pdev, isi); + + isi->pdata = of_device_get_match_data(dev); + + isi->pipes = kcalloc(isi->pdata->num_channels, sizeof(isi->pipes[0]), + GFP_KERNEL); + if (!isi->pipes) + return -ENOMEM; + + ret = mxc_isi_clk_get(isi); + if (ret < 0) { + dev_err(dev, "Failed to get clocks\n"); + return ret; + } + + isi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(isi->regs)) { + dev_err(dev, "Failed to get ISI register map\n"); + return PTR_ERR(isi->regs); + } + + if (isi->pdata->has_gasket) { + isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node, + "fsl,blk-ctrl"); + if (IS_ERR(isi->gasket)) { + ret = PTR_ERR(isi->gasket); + dev_err(dev, "failed to get gasket: %d\n", ret); + return ret; + } + } + + dma_size = isi->pdata->has_36bit_dma ? 36 : 32; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); + if (ret) { + dev_err(dev, "failed to set DMA mask\n"); + return ret; + } + + pm_runtime_enable(dev); + + ret = mxc_isi_crossbar_init(isi); + if (ret) { + dev_err(dev, "Failed to initialize crossbar: %d\n", ret); + goto err_pm; + } + + for (i = 0; i < isi->pdata->num_channels; ++i) { + ret = mxc_isi_pipe_init(isi, i); + if (ret < 0) { + dev_err(dev, "Failed to initialize pipe%u: %d\n", i, + ret); + goto err_xbar; + } + } + + ret = mxc_isi_v4l2_init(isi); + if (ret < 0) { + dev_err(dev, "Failed to initialize V4L2: %d\n", ret); + goto err_xbar; + } + + mxc_isi_debug_init(isi); + + return 0; + +err_xbar: + mxc_isi_crossbar_cleanup(&isi->crossbar); +err_pm: + pm_runtime_disable(isi->dev); + return ret; +} + +static int mxc_isi_remove(struct platform_device *pdev) +{ + struct mxc_isi_dev *isi = platform_get_drvdata(pdev); + unsigned int i; + + mxc_isi_debug_cleanup(isi); + + for (i = 0; i < isi->pdata->num_channels; ++i) { + struct mxc_isi_pipe *pipe = &isi->pipes[i]; + + mxc_isi_pipe_cleanup(pipe); + } + + mxc_isi_crossbar_cleanup(&isi->crossbar); + mxc_isi_v4l2_cleanup(isi); + + pm_runtime_disable(isi->dev); + + return 0; +} + +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 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mxc_isi_of_match); + +static struct platform_driver mxc_isi_driver = { + .probe = mxc_isi_probe, + .remove = mxc_isi_remove, + .driver = { + .of_match_table = mxc_isi_of_match, + .name = MXC_ISI_DRIVER_NAME, + .pm = pm_ptr(&mxc_isi_pm_ops), + } +}; +module_platform_driver(mxc_isi_driver); + +MODULE_ALIAS("ISI"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h new file mode 100644 index 000000000000..e469788a9e6c --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * V4L2 Capture ISI subdev for i.MX8QXP/QM platform + * + * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which + * used to process image from camera sensor to memory or DC + * Copyright 2019-2020 NXP + */ + +#ifndef __MXC_ISI_CORE_H__ +#define __MXC_ISI_CORE_H__ + +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + +struct clk_bulk_data; +struct dentry; +struct device; +struct media_intf_devnode; +struct regmap; +struct v4l2_m2m_dev; + +/* Pipeline pads */ +#define MXC_ISI_PIPE_PAD_SINK 0 +#define MXC_ISI_PIPE_PAD_SOURCE 1 +#define MXC_ISI_PIPE_PADS_NUM 2 + +#define MXC_ISI_MIN_WIDTH 1U +#define MXC_ISI_MIN_HEIGHT 1U +#define MXC_ISI_MAX_WIDTH_UNCHAINED 2048U +#define MXC_ISI_MAX_WIDTH_CHAINED 4096U +#define MXC_ISI_MAX_HEIGHT 8191U + +#define MXC_ISI_DEF_WIDTH 1920U +#define MXC_ISI_DEF_HEIGHT 1080U +#define MXC_ISI_DEF_MBUS_CODE_SINK MEDIA_BUS_FMT_UYVY8_1X16 +#define MXC_ISI_DEF_MBUS_CODE_SOURCE MEDIA_BUS_FMT_YUV8_1X24 +#define MXC_ISI_DEF_PIXEL_FORMAT V4L2_PIX_FMT_YUYV +#define MXC_ISI_DEF_COLOR_SPACE V4L2_COLORSPACE_SRGB +#define MXC_ISI_DEF_YCBCR_ENC V4L2_YCBCR_ENC_601 +#define MXC_ISI_DEF_QUANTIZATION V4L2_QUANTIZATION_LIM_RANGE +#define MXC_ISI_DEF_XFER_FUNC V4L2_XFER_FUNC_SRGB + +#define MXC_ISI_DRIVER_NAME "mxc-isi" +#define MXC_ISI_CAPTURE "mxc-isi-cap" +#define MXC_ISI_M2M "mxc-isi-m2m" +#define MXC_MAX_PLANES 3 + +struct mxc_isi_dev; +struct mxc_isi_m2m_ctx; + +enum mxc_isi_buf_id { + MXC_ISI_BUF1 = 0x0, + MXC_ISI_BUF2, +}; + +enum mxc_isi_encoding { + MXC_ISI_ENC_RAW, + MXC_ISI_ENC_RGB, + MXC_ISI_ENC_YUV, +}; + +enum mxc_isi_input_id { + /* Inputs from the crossbar switch range from 0 to 15 */ + MXC_ISI_INPUT_MEM = 16, +}; + +enum mxc_isi_video_type { + MXC_ISI_VIDEO_CAP = BIT(0), + MXC_ISI_VIDEO_M2M_OUT = BIT(1), + MXC_ISI_VIDEO_M2M_CAP = BIT(2), +}; + +struct mxc_isi_format_info { + u32 mbus_code; + u32 fourcc; + enum mxc_isi_video_type type; + u32 isi_in_format; + u32 isi_out_format; + u8 mem_planes; + u8 color_planes; + u8 depth[MXC_MAX_PLANES]; + u8 hsub; + u8 vsub; + enum mxc_isi_encoding encoding; +}; + +struct mxc_isi_bus_format_info { + u32 mbus_code; + u32 output; + u32 pads; + enum mxc_isi_encoding encoding; +}; + +struct mxc_isi_buffer { + struct vb2_v4l2_buffer v4l2_buf; + struct list_head list; + dma_addr_t dma_addrs[3]; + enum mxc_isi_buf_id id; + bool discard; +}; + +struct mxc_isi_reg { + u32 offset; + u32 mask; +}; + +struct mxc_isi_ier_reg { + /* Overflow Y/U/V trigger enable*/ + struct mxc_isi_reg oflw_y_buf_en; + struct mxc_isi_reg oflw_u_buf_en; + struct mxc_isi_reg oflw_v_buf_en; + + /* Excess overflow Y/U/V trigger enable*/ + struct mxc_isi_reg excs_oflw_y_buf_en; + struct mxc_isi_reg excs_oflw_u_buf_en; + struct mxc_isi_reg excs_oflw_v_buf_en; + + /* Panic Y/U/V trigger enable*/ + struct mxc_isi_reg panic_y_buf_en; + struct mxc_isi_reg panic_v_buf_en; + struct mxc_isi_reg panic_u_buf_en; +}; + +struct mxc_isi_panic_thd { + u32 mask; + u32 offset; + u32 threshold; +}; + +struct mxc_isi_set_thd { + struct mxc_isi_panic_thd panic_set_thd_y; + struct mxc_isi_panic_thd panic_set_thd_u; + struct mxc_isi_panic_thd panic_set_thd_v; +}; + +enum model { + MXC_ISI_IMX8MN, + MXC_ISI_IMX8MP, +}; + +struct mxc_isi_plat_data { + enum model model; + unsigned int num_ports; + unsigned int num_channels; + unsigned int reg_offset; + const struct mxc_isi_ier_reg *ier_reg; + const struct mxc_isi_set_thd *set_thd; + const struct clk_bulk_data *clks; + unsigned int num_clks; + bool buf_active_reverse; + bool has_gasket; + bool has_36bit_dma; +}; + +struct mxc_isi_dma_buffer { + size_t size; + void *addr; + dma_addr_t dma; +}; + +struct mxc_isi_input { + unsigned int enable_count; +}; + +struct mxc_isi_crossbar { + struct mxc_isi_dev *isi; + + unsigned int num_sinks; + unsigned int num_sources; + struct mxc_isi_input *inputs; + + struct v4l2_subdev sd; + struct media_pad *pads; +}; + +struct mxc_isi_video { + struct mxc_isi_pipe *pipe; + + struct video_device vdev; + struct media_pad pad; + + /* Protects is_streaming, and the vdev and vb2_q operations */ + struct mutex lock; + bool is_streaming; + + struct v4l2_pix_format_mplane pix; + const struct mxc_isi_format_info *fmtinfo; + + struct { + struct v4l2_ctrl_handler handler; + unsigned int alpha; + bool hflip; + bool vflip; + } ctrls; + + struct vb2_queue vb2_q; + struct mxc_isi_buffer buf_discard[3]; + struct list_head out_pending; + struct list_head out_active; + struct list_head out_discard; + u32 frame_count; + /* Protects out_pending, out_active, out_discard and frame_count */ + spinlock_t buf_lock; + + struct mxc_isi_dma_buffer discard_buffer[MXC_MAX_PLANES]; +}; + +typedef void(*mxc_isi_pipe_irq_t)(struct mxc_isi_pipe *, u32); + +struct mxc_isi_pipe { + struct mxc_isi_dev *isi; + u32 id; + void __iomem *regs; + + struct media_pipeline pipe; + + struct v4l2_subdev sd; + struct media_pad pads[MXC_ISI_PIPE_PADS_NUM]; + + struct mxc_isi_video video; + + /* + * Protects use_count, irq_handler, res_available, res_acquired, + * chained_res, and the CHNL_CTRL register. + */ + struct mutex lock; + unsigned int use_count; + mxc_isi_pipe_irq_t irq_handler; + +#define MXC_ISI_CHANNEL_RES_LINE_BUF BIT(0) +#define MXC_ISI_CHANNEL_RES_OUTPUT_BUF BIT(1) + u8 available_res; + u8 acquired_res; + u8 chained_res; + bool chained; +}; + +struct mxc_isi_m2m { + struct mxc_isi_dev *isi; + struct mxc_isi_pipe *pipe; + + struct media_pad pad; + struct video_device vdev; + struct media_intf_devnode *intf; + struct v4l2_m2m_dev *m2m_dev; + + /* Protects last_ctx, usage_count and chained_count */ + struct mutex lock; + + struct mxc_isi_m2m_ctx *last_ctx; + int usage_count; + int chained_count; +}; + +struct mxc_isi_dev { + struct device *dev; + + const struct mxc_isi_plat_data *pdata; + + void __iomem *regs; + struct clk_bulk_data *clks; + struct regmap *gasket; + + struct mxc_isi_crossbar crossbar; + struct mxc_isi_pipe *pipes; + struct mxc_isi_m2m m2m; + + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + + struct dentry *debugfs_root; +}; + +int mxc_isi_crossbar_init(struct mxc_isi_dev *isi); +void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar); +int mxc_isi_crossbar_register(struct mxc_isi_crossbar *xbar); +void mxc_isi_crossbar_unregister(struct mxc_isi_crossbar *xbar); + +const struct mxc_isi_bus_format_info * +mxc_isi_bus_format_by_code(u32 code, unsigned int pad); +const struct mxc_isi_bus_format_info * +mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad); +const struct mxc_isi_format_info * +mxc_isi_format_by_fourcc(u32 fourcc, enum mxc_isi_video_type type); +const struct mxc_isi_format_info * +mxc_isi_format_enum(unsigned int index, enum mxc_isi_video_type type); +const struct mxc_isi_format_info * +mxc_isi_format_try(struct mxc_isi_pipe *pipe, struct v4l2_pix_format_mplane *pix, + enum mxc_isi_video_type type); + +int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id); +void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe); +int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, + mxc_isi_pipe_irq_t irq_handler); +void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe); +int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe); +void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe); + +int mxc_isi_video_register(struct mxc_isi_pipe *pipe, + struct v4l2_device *v4l2_dev); +void mxc_isi_video_unregister(struct mxc_isi_pipe *pipe); +void mxc_isi_video_suspend(struct mxc_isi_pipe *pipe); +int mxc_isi_video_resume(struct mxc_isi_pipe *pipe); +int mxc_isi_video_queue_setup(const struct v4l2_pix_format_mplane *format, + const struct mxc_isi_format_info *info, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[]); +void mxc_isi_video_buffer_init(struct vb2_buffer *vb2, dma_addr_t dma_addrs[3], + const struct mxc_isi_format_info *info, + const struct v4l2_pix_format_mplane *pix); +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); + +#ifdef CONFIG_VIDEO_IMX8_ISI_M2M +int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev); +int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi); +#else +static inline int mxc_isi_m2m_register(struct mxc_isi_dev *isi, + struct v4l2_device *v4l2_dev) +{ + return 0; +} +static inline int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi) +{ + return 0; +} +#endif + +int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe, + mxc_isi_pipe_irq_t irq_handler, bool bypass); +void mxc_isi_channel_release(struct mxc_isi_pipe *pipe); +void mxc_isi_channel_get(struct mxc_isi_pipe *pipe); +void mxc_isi_channel_put(struct mxc_isi_pipe *pipe); +void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe); +void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe); +int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass); +void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe); + +void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, + enum mxc_isi_input_id input, + const struct v4l2_area *in_size, + const struct v4l2_area *scale, + const struct v4l2_rect *crop, + enum mxc_isi_encoding in_encoding, + enum mxc_isi_encoding out_encoding); + +void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe, + const struct mxc_isi_format_info *info, + const struct v4l2_pix_format_mplane *format); +void mxc_isi_channel_set_output_format(struct mxc_isi_pipe *pipe, + const struct mxc_isi_format_info *info, + struct v4l2_pix_format_mplane *format); +void mxc_isi_channel_m2m_start(struct mxc_isi_pipe *pipe); + +void mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha); +void mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip); + +void mxc_isi_channel_set_inbuf(struct mxc_isi_pipe *pipe, dma_addr_t dma_addr); +void mxc_isi_channel_set_outbuf(struct mxc_isi_pipe *pipe, + const dma_addr_t dma_addrs[3], + enum mxc_isi_buf_id buf_id); + +u32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear); +void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe); + +#if IS_ENABLED(CONFIG_DEBUG_FS) +void mxc_isi_debug_init(struct mxc_isi_dev *isi); +void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi); +#else +static inline void mxc_isi_debug_init(struct mxc_isi_dev *isi) +{ +} +static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi) +{ +} +#endif + +#endif /* __MXC_ISI_CORE_H__ */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c new file mode 100644 index 000000000000..b5ffde46f31b --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i.MX8 ISI - Input crossbar switch + * + * Copyright (c) 2022 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/minmax.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <media/media-entity.h> +#include <media/mipi-csi2.h> +#include <media/v4l2-subdev.h> + +#include "imx8-isi-core.h" + +static inline struct mxc_isi_crossbar *to_isi_crossbar(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mxc_isi_crossbar, sd); +} + +/* ----------------------------------------------------------------------------- + * Media block control (i.MX8MN and i.MX8MP only) + */ +#define GASKET_BASE(n) (0x0060 + (n) * 0x30) + +#define GASKET_CTRL 0x0000 +#define GASKET_CTRL_DATA_TYPE(dt) ((dt) << 8) +#define GASKET_CTRL_DATA_TYPE_MASK (0x3f << 8) +#define GASKET_CTRL_DUAL_COMP_ENABLE BIT(1) +#define GASKET_CTRL_ENABLE BIT(0) + +#define GASKET_HSIZE 0x0004 +#define GASKET_VSIZE 0x0008 + +static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, + struct v4l2_subdev_state *state, + struct v4l2_subdev *remote_sd, + u32 remote_pad, unsigned int port) +{ + struct mxc_isi_dev *isi = xbar->isi; + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_mbus_frame_desc fd; + u32 val; + int ret; + + if (!isi->pdata->has_gasket) + return 0; + + /* + * Configure and enable the gasket with the frame size and CSI-2 data + * type. For YUV422 8-bit, enable dual component mode unconditionally, + * to match the configuration of the CSIS. + */ + + ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc, remote_pad, &fd); + if (ret) { + dev_err(isi->dev, + "failed to get frame descriptor from '%s':%u: %d\n", + remote_sd->name, remote_pad, ret); + return ret; + } + + if (fd.num_entries != 1) { + dev_err(isi->dev, "invalid frame descriptor for '%s':%u\n", + remote_sd->name, remote_pad); + return -EINVAL; + } + + fmt = v4l2_subdev_state_get_stream_format(state, port, 0); + if (!fmt) + return -EINVAL; + + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_HSIZE, fmt->width); + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_VSIZE, fmt->height); + + val = GASKET_CTRL_DATA_TYPE(fd.entry[0].bus.csi2.dt) + | GASKET_CTRL_ENABLE; + + if (fd.entry[0].bus.csi2.dt == MIPI_CSI2_DT_YUV422_8B) + val |= GASKET_CTRL_DUAL_COMP_ENABLE; + + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_CTRL, val); + + return 0; +} + +static void mxc_isi_crossbar_gasket_disable(struct mxc_isi_crossbar *xbar, + unsigned int port) +{ + struct mxc_isi_dev *isi = xbar->isi; + + if (!isi->pdata->has_gasket) + return; + + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_CTRL, 0); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static const struct v4l2_mbus_framefmt mxc_isi_crossbar_default_format = { + .code = MXC_ISI_DEF_MBUS_CODE_SINK, + .width = MXC_ISI_DEF_WIDTH, + .height = MXC_ISI_DEF_HEIGHT, + .field = V4L2_FIELD_NONE, + .colorspace = MXC_ISI_DEF_COLOR_SPACE, + .ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC, + .quantization = MXC_ISI_DEF_QUANTIZATION, + .xfer_func = MXC_ISI_DEF_XFER_FUNC, +}; + +static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct v4l2_subdev_route *route; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_NO_N_TO_1); + if (ret) + return ret; + + /* The memory input can be routed to the first pipeline only. */ + for_each_active_route(&state->routing, route) { + if (route->sink_pad == xbar->num_sinks - 1 && + route->source_pad != xbar->num_sinks) { + dev_dbg(xbar->isi->dev, + "invalid route from memory input (%u) to pipe %u\n", + route->sink_pad, + route->source_pad - xbar->num_sinks); + return -EINVAL; + } + } + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, + &mxc_isi_crossbar_default_format); +} + +static struct v4l2_subdev * +mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, + struct v4l2_subdev_state *state, + u32 source_pad, u64 source_streams, + u32 *__sink_pad, u64 *__sink_streams, + u32 *remote_pad) +{ + struct v4l2_subdev_route *route; + struct v4l2_subdev *sd; + struct media_pad *pad; + u64 sink_streams = 0; + int sink_pad = -1; + + /* + * Translate the source pad and streams to the sink side. The routing + * validation forbids stream merging, so all matching entries in the + * routing table are guaranteed to have the same sink pad. + * + * TODO: This is likely worth a helper function, it could perhaps be + * supported by v4l2_subdev_state_xlate_streams() with pad1 set to -1. + */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != source_pad || + !(source_streams & BIT(route->source_stream))) + continue; + + sink_streams |= BIT(route->sink_stream); + sink_pad = route->sink_pad; + } + + if (sink_pad < 0) { + dev_dbg(xbar->isi->dev, + "no stream connected to pipeline %u\n", + source_pad - xbar->num_sinks); + return ERR_PTR(-EPIPE); + } + + pad = media_pad_remote_pad_first(&xbar->pads[sink_pad]); + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (!sd) { + dev_dbg(xbar->isi->dev, + "no entity connected to crossbar input %u\n", + sink_pad); + return ERR_PTR(-EPIPE); + } + + *__sink_pad = sink_pad; + *__sink_streams = sink_streams; + *remote_pad = pad->index; + + return sd; +} + +static int mxc_isi_crossbar_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct v4l2_subdev_krouting routing = { }; + struct v4l2_subdev_route *routes; + unsigned int i; + int ret; + + /* + * Create a 1:1 mapping between pixel link inputs and outputs to + * pipelines by default. + */ + routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL); + if (!routes) + return -ENOMEM; + + for (i = 0; i < xbar->num_sources; ++i) { + struct v4l2_subdev_route *route = &routes[i]; + + route->sink_pad = i; + route->source_pad = i + xbar->num_sinks; + route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE; + }; + + routing.num_routes = xbar->num_sources; + routing.routes = routes; + + ret = __mxc_isi_crossbar_set_routing(sd, state, &routing); + + kfree(routes); + + return ret; +} + +static int mxc_isi_crossbar_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + const struct mxc_isi_bus_format_info *info; + + if (code->pad >= xbar->num_sinks) { + const struct v4l2_mbus_framefmt *format; + + /* + * The media bus code on source pads is identical to the + * connected sink pad. + */ + if (code->index > 0) + return -EINVAL; + + format = v4l2_subdev_state_get_opposite_stream_format(state, + code->pad, + code->stream); + if (!format) + return -EINVAL; + + code->code = format->code; + + return 0; + } + + info = mxc_isi_bus_format_by_index(code->index, MXC_ISI_PIPE_PAD_SINK); + if (!info) + return -EINVAL; + + code->code = info->mbus_code; + + return 0; +} + +static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_subdev_route *route; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && + media_pad_is_streaming(&xbar->pads[fmt->pad])) + return -EBUSY; + + /* + * The source pad format is always identical to the sink pad format and + * can't be modified. + */ + if (fmt->pad >= xbar->num_sinks) + return v4l2_subdev_get_fmt(sd, state, fmt); + + /* Validate the requested format. */ + if (!mxc_isi_bus_format_by_code(fmt->format.code, MXC_ISI_PIPE_PAD_SINK)) + fmt->format.code = MXC_ISI_DEF_MBUS_CODE_SINK; + + fmt->format.width = clamp_t(unsigned int, fmt->format.width, + MXC_ISI_MIN_WIDTH, MXC_ISI_MAX_WIDTH_CHAINED); + fmt->format.height = clamp_t(unsigned int, fmt->format.height, + MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT); + fmt->format.field = V4L2_FIELD_NONE; + + /* + * Set the format on the sink stream and propagate it to the source + * streams. + */ + sink_fmt = v4l2_subdev_state_get_stream_format(state, fmt->pad, + fmt->stream); + if (!sink_fmt) + return -EINVAL; + + *sink_fmt = fmt->format; + + /* TODO: A format propagation helper would be useful. */ + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_framefmt *source_fmt; + + if (route->sink_pad != fmt->pad || + route->sink_stream != fmt->stream) + continue; + + source_fmt = v4l2_subdev_state_get_stream_format(state, route->source_pad, + route->source_stream); + if (!source_fmt) + return -EINVAL; + + *source_fmt = fmt->format; + } + + return 0; +} + +static int mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && + media_entity_is_streaming(&sd->entity)) + return -EBUSY; + + return __mxc_isi_crossbar_set_routing(sd, state, routing); +} + +static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct v4l2_subdev *remote_sd; + struct mxc_isi_input *input; + u64 sink_streams; + u32 sink_pad; + u32 remote_pad; + int ret; + + remote_sd = mxc_isi_crossbar_xlate_streams(xbar, state, pad, streams_mask, + &sink_pad, &sink_streams, + &remote_pad); + if (IS_ERR(remote_sd)) + return PTR_ERR(remote_sd); + + input = &xbar->inputs[sink_pad]; + + /* + * TODO: Track per-stream enable counts to support multiplexed + * streams. + */ + if (!input->enable_count) { + ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd, + remote_pad, sink_pad); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad, + sink_streams); + if (ret) { + dev_err(xbar->isi->dev, + "failed to %s streams 0x%llx on '%s':%u: %d\n", + "enable", sink_streams, remote_sd->name, + remote_pad, ret); + mxc_isi_crossbar_gasket_disable(xbar, sink_pad); + return ret; + } + } + + input->enable_count++; + + return 0; +} + +static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct v4l2_subdev *remote_sd; + struct mxc_isi_input *input; + u64 sink_streams; + u32 sink_pad; + u32 remote_pad; + int ret = 0; + + remote_sd = mxc_isi_crossbar_xlate_streams(xbar, state, pad, streams_mask, + &sink_pad, &sink_streams, + &remote_pad); + if (IS_ERR(remote_sd)) + return PTR_ERR(remote_sd); + + input = &xbar->inputs[sink_pad]; + + input->enable_count--; + + if (!input->enable_count) { + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad, + sink_streams); + if (ret) + dev_err(xbar->isi->dev, + "failed to %s streams 0x%llx on '%s':%u: %d\n", + "disable", sink_streams, remote_sd->name, + remote_pad, ret); + + mxc_isi_crossbar_gasket_disable(xbar, sink_pad); + } + + return ret; +} + +static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = { + .init_cfg = mxc_isi_crossbar_init_cfg, + .enum_mbus_code = mxc_isi_crossbar_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mxc_isi_crossbar_set_fmt, + .set_routing = mxc_isi_crossbar_set_routing, + .enable_streams = mxc_isi_crossbar_enable_streams, + .disable_streams = mxc_isi_crossbar_disable_streams, +}; + +static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = { + .pad = &mxc_isi_crossbar_subdev_pad_ops, +}; + +static const struct media_entity_operations mxc_isi_cross_entity_ops = { + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +/* ----------------------------------------------------------------------------- + * Init & cleanup + */ + +int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) +{ + struct mxc_isi_crossbar *xbar = &isi->crossbar; + struct v4l2_subdev *sd = &xbar->sd; + unsigned int num_pads; + unsigned int i; + int ret; + + xbar->isi = isi; + + v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + strscpy(sd->name, "crossbar", sizeof(sd->name)); + sd->dev = isi->dev; + + sd->entity.function = MEDIA_ENT_F_VID_MUX; + sd->entity.ops = &mxc_isi_cross_entity_ops; + + /* + * The subdev has one sink and one source per port, plus one sink for + * the memory input. + */ + xbar->num_sinks = isi->pdata->num_ports + 1; + xbar->num_sources = isi->pdata->num_ports; + num_pads = xbar->num_sinks + xbar->num_sources; + + xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL); + if (!xbar->pads) + return -ENOMEM; + + xbar->inputs = kcalloc(xbar->num_sinks, sizeof(*xbar->inputs), + GFP_KERNEL); + if (!xbar->pads) { + ret = -ENOMEM; + goto err_free; + } + + for (i = 0; i < xbar->num_sinks; ++i) + xbar->pads[i].flags = MEDIA_PAD_FL_SINK; + for (i = 0; i < xbar->num_sources; ++i) + xbar->pads[i + xbar->num_sinks].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, num_pads, xbar->pads); + if (ret) + goto err_free; + + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) + goto err_entity; + + return 0; + +err_entity: + media_entity_cleanup(&sd->entity); +err_free: + kfree(xbar->pads); + kfree(xbar->inputs); + + return ret; +} + +void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar) +{ + media_entity_cleanup(&xbar->sd.entity); + kfree(xbar->pads); + kfree(xbar->inputs); +} + +int mxc_isi_crossbar_register(struct mxc_isi_crossbar *xbar) +{ + return v4l2_device_register_subdev(&xbar->isi->v4l2_dev, &xbar->sd); +} + +void mxc_isi_crossbar_unregister(struct mxc_isi_crossbar *xbar) +{ +} diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c new file mode 100644 index 000000000000..6709ab7ea1f3 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019-2020 NXP + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/pm_runtime.h> +#include <linux/seq_file.h> +#include <linux/types.h> + +#include "imx8-isi-core.h" +#include "imx8-isi-regs.h" + +static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg) +{ + return readl(pipe->regs + reg); +} + +static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) +{ +#define MXC_ISI_DEBUG_REG(name) { name, #name } + static const struct { + u32 offset; + const char * const name; + } registers[] = { + MXC_ISI_DEBUG_REG(CHNL_CTRL), + MXC_ISI_DEBUG_REG(CHNL_IMG_CTRL), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF_CTRL), + MXC_ISI_DEBUG_REG(CHNL_IMG_CFG), + MXC_ISI_DEBUG_REG(CHNL_IER), + MXC_ISI_DEBUG_REG(CHNL_STS), + MXC_ISI_DEBUG_REG(CHNL_SCALE_FACTOR), + MXC_ISI_DEBUG_REG(CHNL_SCALE_OFFSET), + MXC_ISI_DEBUG_REG(CHNL_CROP_ULC), + MXC_ISI_DEBUG_REG(CHNL_CROP_LRC), + MXC_ISI_DEBUG_REG(CHNL_CSC_COEFF0), + MXC_ISI_DEBUG_REG(CHNL_CSC_COEFF1), + MXC_ISI_DEBUG_REG(CHNL_CSC_COEFF2), + MXC_ISI_DEBUG_REG(CHNL_CSC_COEFF3), + MXC_ISI_DEBUG_REG(CHNL_CSC_COEFF4), + MXC_ISI_DEBUG_REG(CHNL_CSC_COEFF5), + MXC_ISI_DEBUG_REG(CHNL_ROI_0_ALPHA), + MXC_ISI_DEBUG_REG(CHNL_ROI_0_ULC), + MXC_ISI_DEBUG_REG(CHNL_ROI_0_LRC), + MXC_ISI_DEBUG_REG(CHNL_ROI_1_ALPHA), + MXC_ISI_DEBUG_REG(CHNL_ROI_1_ULC), + MXC_ISI_DEBUG_REG(CHNL_ROI_1_LRC), + MXC_ISI_DEBUG_REG(CHNL_ROI_2_ALPHA), + MXC_ISI_DEBUG_REG(CHNL_ROI_2_ULC), + MXC_ISI_DEBUG_REG(CHNL_ROI_2_LRC), + MXC_ISI_DEBUG_REG(CHNL_ROI_3_ALPHA), + MXC_ISI_DEBUG_REG(CHNL_ROI_3_ULC), + MXC_ISI_DEBUG_REG(CHNL_ROI_3_LRC), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF1_ADDR_Y), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF1_ADDR_U), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF1_ADDR_V), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF_PITCH), + MXC_ISI_DEBUG_REG(CHNL_IN_BUF_ADDR), + MXC_ISI_DEBUG_REG(CHNL_IN_BUF_PITCH), + MXC_ISI_DEBUG_REG(CHNL_MEM_RD_CTRL), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF2_ADDR_Y), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF2_ADDR_U), + MXC_ISI_DEBUG_REG(CHNL_OUT_BUF2_ADDR_V), + MXC_ISI_DEBUG_REG(CHNL_SCL_IMG_CFG), + MXC_ISI_DEBUG_REG(CHNL_FLOW_CTRL), + }; + + struct mxc_isi_pipe *pipe = m->private; + unsigned int i; + + if (!pm_runtime_get_if_in_use(pipe->isi->dev)) + return 0; + + seq_printf(m, "--- ISI pipe %u registers ---\n", pipe->id); + + for (i = 0; i < ARRAY_SIZE(registers); ++i) + seq_printf(m, "%20s[0x%02x]: 0x%08x\n", + registers[i].name, registers[i].offset, + mxc_isi_read(pipe, registers[i].offset)); + + pm_runtime_put(pipe->isi->dev); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(mxc_isi_debug_dump_regs); + +void mxc_isi_debug_init(struct mxc_isi_dev *isi) +{ + unsigned int i; + + isi->debugfs_root = debugfs_create_dir(dev_name(isi->dev), NULL); + + for (i = 0; i < isi->pdata->num_channels; ++i) { + struct mxc_isi_pipe *pipe = &isi->pipes[i]; + char name[8]; + + sprintf(name, "pipe%u", pipe->id); + debugfs_create_file(name, 0444, isi->debugfs_root, pipe, + &mxc_isi_debug_dump_regs_fops); + } +} + +void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi) +{ + debugfs_remove_recursive(isi->debugfs_root); +} diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c new file mode 100644 index 000000000000..19e80b95ffea --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019-2020 NXP + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/types.h> + +#include "imx8-isi-core.h" +#include "imx8-isi-regs.h" + +#define ISI_DOWNSCALE_THRESHOLD 0x4000 + +static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg) +{ + return readl(pipe->regs + reg); +} + +static inline void mxc_isi_write(struct mxc_isi_pipe *pipe, u32 reg, u32 val) +{ + writel(val, pipe->regs + reg); +} + +/* ----------------------------------------------------------------------------- + * Buffers & M2M operation + */ + +void mxc_isi_channel_set_inbuf(struct mxc_isi_pipe *pipe, dma_addr_t dma_addr) +{ + mxc_isi_write(pipe, CHNL_IN_BUF_ADDR, lower_32_bits(dma_addr)); + if (pipe->isi->pdata->has_36bit_dma) + mxc_isi_write(pipe, CHNL_IN_BUF_XTND_ADDR, + upper_32_bits(dma_addr)); +} + +void mxc_isi_channel_set_outbuf(struct mxc_isi_pipe *pipe, + const dma_addr_t dma_addrs[3], + enum mxc_isi_buf_id buf_id) +{ + int val; + + val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL); + + if (buf_id == MXC_ISI_BUF1) { + mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_Y, + lower_32_bits(dma_addrs[0])); + mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_U, + lower_32_bits(dma_addrs[1])); + mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_V, + lower_32_bits(dma_addrs[2])); + if (pipe->isi->pdata->has_36bit_dma) { + mxc_isi_write(pipe, CHNL_Y_BUF1_XTND_ADDR, + upper_32_bits(dma_addrs[0])); + mxc_isi_write(pipe, CHNL_U_BUF1_XTND_ADDR, + upper_32_bits(dma_addrs[1])); + mxc_isi_write(pipe, CHNL_V_BUF1_XTND_ADDR, + upper_32_bits(dma_addrs[2])); + } + val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR; + } else { + mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_Y, + lower_32_bits(dma_addrs[0])); + mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_U, + lower_32_bits(dma_addrs[1])); + mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_V, + lower_32_bits(dma_addrs[2])); + if (pipe->isi->pdata->has_36bit_dma) { + mxc_isi_write(pipe, CHNL_Y_BUF2_XTND_ADDR, + upper_32_bits(dma_addrs[0])); + mxc_isi_write(pipe, CHNL_U_BUF2_XTND_ADDR, + upper_32_bits(dma_addrs[1])); + mxc_isi_write(pipe, CHNL_V_BUF2_XTND_ADDR, + upper_32_bits(dma_addrs[2])); + } + val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR; + } + + mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val); +} + +void mxc_isi_channel_m2m_start(struct mxc_isi_pipe *pipe) +{ + u32 val; + + val = mxc_isi_read(pipe, CHNL_MEM_RD_CTRL); + val &= ~CHNL_MEM_RD_CTRL_READ_MEM; + mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val); + + fsleep(300); + + val |= CHNL_MEM_RD_CTRL_READ_MEM; + mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val); +} + +/* ----------------------------------------------------------------------------- + * Pipeline configuration + */ + +static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to, + u32 *dec) +{ + unsigned int ratio = from / to; + + if (ratio < 2) + *dec = 1; + else if (ratio < 4) + *dec = 2; + else if (ratio < 8) + *dec = 4; + else + *dec = 8; + + return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD); +} + +static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe, + enum mxc_isi_encoding encoding, + const struct v4l2_area *in_size, + const struct v4l2_area *out_size, + bool *bypass) +{ + u32 xscale, yscale; + u32 decx, decy; + u32 val; + + dev_dbg(pipe->isi->dev, "input %ux%u, output %ux%u\n", + in_size->width, in_size->height, + out_size->width, out_size->height); + + xscale = mxc_isi_channel_scaling_ratio(in_size->width, out_size->width, + &decx); + yscale = mxc_isi_channel_scaling_ratio(in_size->height, out_size->height, + &decy); + + val = mxc_isi_read(pipe, CHNL_IMG_CTRL); + val &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK | + CHNL_IMG_CTRL_YCBCR_MODE); + + val |= CHNL_IMG_CTRL_DEC_X(ilog2(decx)) + | CHNL_IMG_CTRL_DEC_Y(ilog2(decy)); + + /* + * Contrary to what the documentation states, YCBCR_MODE does not + * control conversion between YCbCr and RGB, but whether the scaler + * operates in YUV mode or in RGB mode. It must be set when the scaler + * input is YUV. + */ + if (encoding == MXC_ISI_ENC_YUV) + val |= CHNL_IMG_CTRL_YCBCR_MODE; + + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); + + mxc_isi_write(pipe, CHNL_SCALE_FACTOR, + CHNL_SCALE_FACTOR_Y_SCALE(yscale) | + CHNL_SCALE_FACTOR_X_SCALE(xscale)); + + mxc_isi_write(pipe, CHNL_SCALE_OFFSET, 0); + + mxc_isi_write(pipe, CHNL_SCL_IMG_CFG, + CHNL_SCL_IMG_CFG_HEIGHT(out_size->height) | + CHNL_SCL_IMG_CFG_WIDTH(out_size->width)); + + *bypass = in_size->height == out_size->height && + in_size->width == out_size->width; +} + +static void mxc_isi_channel_set_crop(struct mxc_isi_pipe *pipe, + const struct v4l2_area *src, + const struct v4l2_rect *dst) +{ + u32 val, val0, val1; + + val = mxc_isi_read(pipe, CHNL_IMG_CTRL); + val &= ~CHNL_IMG_CTRL_CROP_EN; + + if (src->height == dst->height && src->width == dst->width) { + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); + return; + } + + val |= CHNL_IMG_CTRL_CROP_EN; + val0 = CHNL_CROP_ULC_X(dst->left) | CHNL_CROP_ULC_Y(dst->top); + val1 = CHNL_CROP_LRC_X(dst->width) | CHNL_CROP_LRC_Y(dst->height); + + mxc_isi_write(pipe, CHNL_CROP_ULC, val0); + mxc_isi_write(pipe, CHNL_CROP_LRC, val1 + val0); + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); +} + +/* + * A2,A1, B1, A3, B3, B2, + * C2, C1, D1, C3, D3, D2 + */ +static const u32 mxc_isi_yuv2rgb_coeffs[6] = { + /* YUV -> RGB */ + 0x0000012a, 0x012a0198, 0x0730079c, + 0x0204012a, 0x01f00000, 0x01800180 +}; + +static const u32 mxc_isi_rgb2yuv_coeffs[6] = { + /* RGB->YUV */ + 0x00810041, 0x07db0019, 0x007007b6, + 0x07a20070, 0x001007ee, 0x00800080 +}; + +static void mxc_isi_channel_set_csc(struct mxc_isi_pipe *pipe, + enum mxc_isi_encoding in_encoding, + enum mxc_isi_encoding out_encoding, + bool *bypass) +{ + static const char * const encodings[] = { + [MXC_ISI_ENC_RAW] = "RAW", + [MXC_ISI_ENC_RGB] = "RGB", + [MXC_ISI_ENC_YUV] = "YUV", + }; + const u32 *coeffs; + bool cscen = true; + u32 val; + + val = mxc_isi_read(pipe, CHNL_IMG_CTRL); + val &= ~(CHNL_IMG_CTRL_CSC_BYPASS | CHNL_IMG_CTRL_CSC_MODE_MASK); + + if (in_encoding == MXC_ISI_ENC_YUV && + out_encoding == MXC_ISI_ENC_RGB) { + /* YUV2RGB */ + coeffs = mxc_isi_yuv2rgb_coeffs; + /* YCbCr enable??? */ + val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB); + } else if (in_encoding == MXC_ISI_ENC_RGB && + out_encoding == MXC_ISI_ENC_YUV) { + /* RGB2YUV */ + coeffs = mxc_isi_rgb2yuv_coeffs; + val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR); + } else { + /* Bypass CSC */ + cscen = false; + val |= CHNL_IMG_CTRL_CSC_BYPASS; + } + + dev_dbg(pipe->isi->dev, "CSC: %s -> %s\n", + encodings[in_encoding], encodings[out_encoding]); + + if (cscen) { + mxc_isi_write(pipe, CHNL_CSC_COEFF0, coeffs[0]); + mxc_isi_write(pipe, CHNL_CSC_COEFF1, coeffs[1]); + mxc_isi_write(pipe, CHNL_CSC_COEFF2, coeffs[2]); + mxc_isi_write(pipe, CHNL_CSC_COEFF3, coeffs[3]); + mxc_isi_write(pipe, CHNL_CSC_COEFF4, coeffs[4]); + mxc_isi_write(pipe, CHNL_CSC_COEFF5, coeffs[5]); + } + + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); + + *bypass = !cscen; +} + +void mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha) +{ + u32 val; + + val = mxc_isi_read(pipe, CHNL_IMG_CTRL); + val &= ~CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK; + val |= CHNL_IMG_CTRL_GBL_ALPHA_VAL(alpha) | + CHNL_IMG_CTRL_GBL_ALPHA_EN; + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); +} + +void mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip) +{ + u32 val; + + val = mxc_isi_read(pipe, CHNL_IMG_CTRL); + val &= ~(CHNL_IMG_CTRL_VFLIP_EN | CHNL_IMG_CTRL_HFLIP_EN); + + if (vflip) + val |= CHNL_IMG_CTRL_VFLIP_EN; + if (hflip) + val |= CHNL_IMG_CTRL_HFLIP_EN; + + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); +} + +static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe) +{ + const struct mxc_isi_set_thd *set_thd = pipe->isi->pdata->set_thd; + u32 val; + + val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL); + + val &= ~(set_thd->panic_set_thd_y.mask); + val |= set_thd->panic_set_thd_y.threshold << set_thd->panic_set_thd_y.offset; + + val &= ~(set_thd->panic_set_thd_u.mask); + val |= set_thd->panic_set_thd_u.threshold << set_thd->panic_set_thd_u.offset; + + val &= ~(set_thd->panic_set_thd_v.mask); + val |= set_thd->panic_set_thd_v.threshold << set_thd->panic_set_thd_v.offset; + + mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val); +} + +static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, + enum mxc_isi_input_id input, + bool bypass) +{ + u32 val; + + mutex_lock(&pipe->lock); + + val = mxc_isi_read(pipe, CHNL_CTRL); + val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK | + CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK | + CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK); + + /* + * If no scaling or color space conversion is needed, bypass the + * channel. + */ + if (bypass) + val |= CHNL_CTRL_CHNL_BYPASS; + + /* Chain line buffers if needed. */ + if (pipe->chained) + val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN); + + val |= CHNL_CTRL_BLANK_PXL(0xff); + + /* Input source (including VC configuration for CSI-2) */ + if (input == MXC_ISI_INPUT_MEM) { + /* + * The memory input is connected to the last port of the + * crossbar switch, after all pixel link inputs. The SRC_INPUT + * field controls the input selection and must be set + * accordingly, despite being documented as ignored when using + * the memory input in the i.MX8MP reference manual, and + * reserved in the i.MX8MN reference manual. + */ + val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_MEMORY); + val |= CHNL_CTRL_SRC_INPUT(pipe->isi->pdata->num_ports); + } else { + val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE); + val |= CHNL_CTRL_SRC_INPUT(input); + val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */ + } + + mxc_isi_write(pipe, CHNL_CTRL, val); + + mutex_unlock(&pipe->lock); +} + +void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, + enum mxc_isi_input_id input, + const struct v4l2_area *in_size, + const struct v4l2_area *scale, + const struct v4l2_rect *crop, + enum mxc_isi_encoding in_encoding, + enum mxc_isi_encoding out_encoding) +{ + bool csc_bypass; + bool scaler_bypass; + + /* Input frame size */ + mxc_isi_write(pipe, CHNL_IMG_CFG, + CHNL_IMG_CFG_HEIGHT(in_size->height) | + CHNL_IMG_CFG_WIDTH(in_size->width)); + + /* Scaling */ + mxc_isi_channel_set_scaling(pipe, in_encoding, in_size, scale, + &scaler_bypass); + mxc_isi_channel_set_crop(pipe, scale, crop); + + /* CSC */ + mxc_isi_channel_set_csc(pipe, in_encoding, out_encoding, &csc_bypass); + + /* Output buffer management */ + mxc_isi_channel_set_panic_threshold(pipe); + + /* Channel control */ + mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass); +} + +void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe, + const struct mxc_isi_format_info *info, + const struct v4l2_pix_format_mplane *format) +{ + unsigned int bpl = format->plane_fmt[0].bytesperline; + + mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, + CHNL_MEM_RD_CTRL_IMG_TYPE(info->isi_in_format)); + mxc_isi_write(pipe, CHNL_IN_BUF_PITCH, + CHNL_IN_BUF_PITCH_LINE_PITCH(bpl)); +} + +void mxc_isi_channel_set_output_format(struct mxc_isi_pipe *pipe, + const struct mxc_isi_format_info *info, + struct v4l2_pix_format_mplane *format) +{ + u32 val; + + /* set outbuf format */ + dev_dbg(pipe->isi->dev, "output format %p4cc", &format->pixelformat); + + val = mxc_isi_read(pipe, CHNL_IMG_CTRL); + val &= ~CHNL_IMG_CTRL_FORMAT_MASK; + val |= CHNL_IMG_CTRL_FORMAT(info->isi_out_format); + mxc_isi_write(pipe, CHNL_IMG_CTRL, val); + + /* line pitch */ + mxc_isi_write(pipe, CHNL_OUT_BUF_PITCH, + format->plane_fmt[0].bytesperline); +} + +/* ----------------------------------------------------------------------------- + * IRQ + */ + +u32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear) +{ + u32 status; + + status = mxc_isi_read(pipe, CHNL_STS); + if (clear) + mxc_isi_write(pipe, CHNL_STS, status); + + return status; +} + +void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe) +{ + mxc_isi_write(pipe, CHNL_STS, 0xffffffff); +} + +static void mxc_isi_channel_irq_enable(struct mxc_isi_pipe *pipe) +{ + const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg; + u32 val; + + val = CHNL_IER_FRM_RCVD_EN | + CHNL_IER_AXI_WR_ERR_U_EN | + CHNL_IER_AXI_WR_ERR_V_EN | + CHNL_IER_AXI_WR_ERR_Y_EN; + + /* Y/U/V overflow enable */ + val |= ier_reg->oflw_y_buf_en.mask | + ier_reg->oflw_u_buf_en.mask | + ier_reg->oflw_v_buf_en.mask; + + /* Y/U/V excess overflow enable */ + val |= ier_reg->excs_oflw_y_buf_en.mask | + ier_reg->excs_oflw_u_buf_en.mask | + ier_reg->excs_oflw_v_buf_en.mask; + + /* Y/U/V panic enable */ + val |= ier_reg->panic_y_buf_en.mask | + ier_reg->panic_u_buf_en.mask | + ier_reg->panic_v_buf_en.mask; + + mxc_isi_channel_irq_clear(pipe); + mxc_isi_write(pipe, CHNL_IER, val); +} + +static void mxc_isi_channel_irq_disable(struct mxc_isi_pipe *pipe) +{ + mxc_isi_write(pipe, CHNL_IER, 0); +} + +/* ----------------------------------------------------------------------------- + * Init, deinit, enable, disable + */ + +static void mxc_isi_channel_sw_reset(struct mxc_isi_pipe *pipe, bool enable_clk) +{ + mxc_isi_write(pipe, CHNL_CTRL, CHNL_CTRL_SW_RST); + mdelay(5); + mxc_isi_write(pipe, CHNL_CTRL, enable_clk ? CHNL_CTRL_CLK_EN : 0); +} + +static void __mxc_isi_channel_get(struct mxc_isi_pipe *pipe) +{ + if (!pipe->use_count++) + mxc_isi_channel_sw_reset(pipe, true); +} + +void mxc_isi_channel_get(struct mxc_isi_pipe *pipe) +{ + mutex_lock(&pipe->lock); + __mxc_isi_channel_get(pipe); + mutex_unlock(&pipe->lock); +} + +static void __mxc_isi_channel_put(struct mxc_isi_pipe *pipe) +{ + if (!--pipe->use_count) + mxc_isi_channel_sw_reset(pipe, false); +} + +void mxc_isi_channel_put(struct mxc_isi_pipe *pipe) +{ + mutex_lock(&pipe->lock); + __mxc_isi_channel_put(pipe); + mutex_unlock(&pipe->lock); +} + +void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe) +{ + u32 val; + + mxc_isi_channel_irq_enable(pipe); + + mutex_lock(&pipe->lock); + + val = mxc_isi_read(pipe, CHNL_CTRL); + val |= CHNL_CTRL_CHNL_EN; + mxc_isi_write(pipe, CHNL_CTRL, val); + + mutex_unlock(&pipe->lock); +} + +void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe) +{ + u32 val; + + mxc_isi_channel_irq_disable(pipe); + + mutex_lock(&pipe->lock); + + val = mxc_isi_read(pipe, CHNL_CTRL); + val &= ~CHNL_CTRL_CHNL_EN; + mxc_isi_write(pipe, CHNL_CTRL, val); + + mutex_unlock(&pipe->lock); +} + +/* ----------------------------------------------------------------------------- + * Resource management & chaining + */ +int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe, + mxc_isi_pipe_irq_t irq_handler, bool bypass) +{ + u8 resources; + int ret = 0; + + mutex_lock(&pipe->lock); + + if (pipe->irq_handler) { + ret = -EBUSY; + goto unlock; + } + + /* + * Make sure the resources we need are available. The output buffer is + * always needed to operate the channel, the line buffer is needed only + * when the channel isn't in bypass mode. + */ + resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF + | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0); + if ((pipe->available_res & resources) != resources) { + ret = -EBUSY; + goto unlock; + } + + /* Acquire the channel resources. */ + pipe->acquired_res = resources; + pipe->available_res &= ~resources; + pipe->irq_handler = irq_handler; + +unlock: + mutex_unlock(&pipe->lock); + + return ret; +} + +void mxc_isi_channel_release(struct mxc_isi_pipe *pipe) +{ + mutex_lock(&pipe->lock); + + pipe->irq_handler = NULL; + pipe->available_res |= pipe->acquired_res; + pipe->acquired_res = 0; + + mutex_unlock(&pipe->lock); +} + +/* + * We currently support line buffer chaining only, for handling images with a + * width larger than 2048 pixels. + * + * TODO: Support secondary line buffer for downscaling YUV420 images. + */ +int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass) +{ + /* Channel chaining requires both line and output buffer. */ + const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF + | MXC_ISI_CHANNEL_RES_LINE_BUF; + struct mxc_isi_pipe *chained_pipe = pipe + 1; + int ret = 0; + + /* + * If buffer chaining is required, make sure this channel is not the + * last one, otherwise there's no 'next' channel to chain with. This + * should be prevented by checks in the set format handlers, but let's + * be defensive. + */ + if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1)) + return -EINVAL; + + mutex_lock(&chained_pipe->lock); + + /* Safety checks. */ + if (WARN_ON(pipe->chained || chained_pipe->chained_res)) { + ret = -EINVAL; + goto unlock; + } + + if ((chained_pipe->available_res & resources) != resources) { + ret = -EBUSY; + goto unlock; + } + + pipe->chained = true; + chained_pipe->chained_res |= resources; + chained_pipe->available_res &= ~resources; + + __mxc_isi_channel_get(chained_pipe); + +unlock: + mutex_unlock(&chained_pipe->lock); + + return ret; +} + +void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_pipe *chained_pipe = pipe + 1; + + if (!pipe->chained) + return; + + pipe->chained = false; + + mutex_lock(&chained_pipe->lock); + + chained_pipe->available_res |= chained_pipe->chained_res; + chained_pipe->chained_res = 0; + + __mxc_isi_channel_put(chained_pipe); + + mutex_unlock(&chained_pipe->lock); +} diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c new file mode 100644 index 000000000000..9745d6219a16 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -0,0 +1,858 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ISI V4L2 memory to memory driver for i.MX8QXP/QM platform + * + * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which + * used to process image from camera sensor or memory to memory or DC + * + * Copyright (c) 2019 NXP Semiconductor + */ + +#include <linux/container_of.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/limits.h> +#include <linux/minmax.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "imx8-isi-core.h" + +struct mxc_isi_m2m_buffer { + struct v4l2_m2m_buffer buf; + dma_addr_t dma_addrs[3]; +}; + +struct mxc_isi_m2m_ctx_queue_data { + struct v4l2_pix_format_mplane format; + const struct mxc_isi_format_info *info; + u32 sequence; +}; + +struct mxc_isi_m2m_ctx { + struct v4l2_fh fh; + struct mxc_isi_m2m *m2m; + + /* Protects the m2m vb2 queues */ + struct mutex vb2_lock; + + struct { + struct mxc_isi_m2m_ctx_queue_data out; + struct mxc_isi_m2m_ctx_queue_data cap; + } queues; + + struct { + struct v4l2_ctrl_handler handler; + unsigned int alpha; + bool hflip; + bool vflip; + } ctrls; + + bool chained; +}; + +static inline struct mxc_isi_m2m_buffer * +to_isi_m2m_buffer(struct vb2_v4l2_buffer *buf) +{ + return container_of(buf, struct mxc_isi_m2m_buffer, buf.vb); +} + +static inline struct mxc_isi_m2m_ctx *to_isi_m2m_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mxc_isi_m2m_ctx, fh); +} + +static inline struct mxc_isi_m2m_ctx_queue_data * +mxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx *ctx, enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->queues.out; + else + return &ctx->queues.cap; +} + +/* ----------------------------------------------------------------------------- + * V4L2 M2M device operations + */ + +static void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status) +{ + struct mxc_isi_m2m *m2m = &pipe->isi->m2m; + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; + struct mxc_isi_m2m_ctx *ctx; + + ctx = v4l2_m2m_get_curr_priv(m2m->m2m_dev); + if (!ctx) { + dev_err(m2m->isi->dev, + "Instance released before the end of transaction\n"); + return; + } + + src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, false); + + src_vbuf->sequence = ctx->queues.out.sequence++; + dst_vbuf->sequence = ctx->queues.cap.sequence++; + + v4l2_m2m_buf_done(src_vbuf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vbuf, VB2_BUF_STATE_DONE); + + v4l2_m2m_job_finish(m2m->m2m_dev, ctx->fh.m2m_ctx); +} + +static void mxc_isi_m2m_device_run(void *priv) +{ + struct mxc_isi_m2m_ctx *ctx = priv; + struct mxc_isi_m2m *m2m = ctx->m2m; + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; + struct mxc_isi_m2m_buffer *src_buf, *dst_buf; + + mxc_isi_channel_disable(m2m->pipe); + + mutex_lock(&m2m->lock); + + /* If the context has changed, reconfigure the channel. */ + if (m2m->last_ctx != ctx) { + const struct v4l2_area in_size = { + .width = ctx->queues.out.format.width, + .height = ctx->queues.out.format.height, + }; + const struct v4l2_area scale = { + .width = ctx->queues.cap.format.width, + .height = ctx->queues.cap.format.height, + }; + const struct v4l2_rect crop = { + .width = ctx->queues.cap.format.width, + .height = ctx->queues.cap.format.height, + }; + + mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM, + &in_size, &scale, &crop, + ctx->queues.out.info->encoding, + ctx->queues.cap.info->encoding); + mxc_isi_channel_set_input_format(m2m->pipe, + ctx->queues.out.info, + &ctx->queues.out.format); + mxc_isi_channel_set_output_format(m2m->pipe, + ctx->queues.cap.info, + &ctx->queues.cap.format); + + m2m->last_ctx = ctx; + } + + mutex_unlock(&m2m->lock); + + mutex_lock(ctx->ctrls.handler.lock); + mxc_isi_channel_set_alpha(m2m->pipe, ctx->ctrls.alpha); + mxc_isi_channel_set_flip(m2m->pipe, ctx->ctrls.hflip, ctx->ctrls.vflip); + mutex_unlock(ctx->ctrls.handler.lock); + + src_vbuf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_vbuf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + src_buf = to_isi_m2m_buffer(src_vbuf); + dst_buf = to_isi_m2m_buffer(dst_vbuf); + + mxc_isi_channel_set_inbuf(m2m->pipe, src_buf->dma_addrs[0]); + mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF1); + mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF2); + + mxc_isi_channel_enable(m2m->pipe); + + mxc_isi_channel_m2m_start(m2m->pipe); +} + +static const struct v4l2_m2m_ops mxc_isi_m2m_ops = { + .device_run = mxc_isi_m2m_device_run, +}; + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations + */ + +static int mxc_isi_m2m_vb2_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + const struct mxc_isi_m2m_ctx_queue_data *qdata = + mxc_isi_m2m_ctx_qdata(ctx, q->type); + + return mxc_isi_video_queue_setup(&qdata->format, qdata->info, + num_buffers, num_planes, sizes); +} + +static int mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer *vb2) +{ + struct vb2_queue *vq = vb2->vb2_queue; + struct mxc_isi_m2m_buffer *buf = to_isi_m2m_buffer(to_vb2_v4l2_buffer(vb2)); + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue); + const struct mxc_isi_m2m_ctx_queue_data *qdata = + mxc_isi_m2m_ctx_qdata(ctx, vq->type); + + mxc_isi_video_buffer_init(vb2, buf->dma_addrs, qdata->info, + &qdata->format); + + return 0; +} + +static int mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer *vb2) +{ + struct vb2_queue *vq = vb2->vb2_queue; + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vq); + const struct mxc_isi_m2m_ctx_queue_data *qdata = + mxc_isi_m2m_ctx_qdata(ctx, vq->type); + + return mxc_isi_video_buffer_prepare(ctx->m2m->isi, vb2, qdata->info, + &qdata->format); +} + +static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct mxc_isi_m2m_ctx_queue_data *qdata = + mxc_isi_m2m_ctx_qdata(ctx, q->type); + + qdata->sequence = 0; + + return 0; +} + +static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + break; + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops mxc_isi_m2m_vb2_qops = { + .queue_setup = mxc_isi_m2m_vb2_queue_setup, + .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, +}; + +static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct mxc_isi_m2m_ctx *ctx = priv; + struct mxc_isi_m2m *m2m = ctx->m2m; + int ret; + + 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 mxc_isi_m2m_buffer); + src_vq->ops = &mxc_isi_m2m_vb2_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->vb2_lock; + src_vq->dev = m2m->isi->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer); + dst_vq->ops = &mxc_isi_m2m_vb2_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->vb2_lock; + dst_vq->dev = m2m->isi->dev; + + return vb2_queue_init(dst_vq); +} + +/* ----------------------------------------------------------------------------- + * V4L2 controls + */ + +static inline struct mxc_isi_m2m_ctx * +ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mxc_isi_m2m_ctx, ctrls.handler); +} + +static int mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mxc_isi_m2m_ctx *ctx = ctrl_to_mxc_isi_m2m_ctx(ctrl); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctx->ctrls.hflip = ctrl->val; + break; + + case V4L2_CID_VFLIP: + ctx->ctrls.vflip = ctrl->val; + break; + + case V4L2_CID_ALPHA_COMPONENT: + ctx->ctrls.alpha = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = { + .s_ctrl = mxc_isi_m2m_ctx_s_ctrl, +}; + +static int mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx *ctx) +{ + struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler; + int ret; + + v4l2_ctrl_handler_init(handler, 3); + + v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); + v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + v4l2_ctrl_handler_free(handler); + return ret; + } + + ctx->fh.ctrl_handler = handler; + + return 0; +} + +static void mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx *ctx) +{ + v4l2_ctrl_handler_free(&ctx->ctrls.handler); +} + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int mxc_isi_m2m_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, MXC_ISI_M2M, sizeof(cap->card)); + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int mxc_isi_m2m_enum_fmt_vid(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + const enum mxc_isi_video_type type = + f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; + const struct mxc_isi_format_info *info; + + info = mxc_isi_format_enum(f->index, type); + if (!info) + return -EINVAL; + + f->pixelformat = info->fourcc; + f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC + | V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC; + + return 0; +} + +static const struct mxc_isi_format_info * +__mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, + struct v4l2_pix_format_mplane *pix, + const enum mxc_isi_video_type type) +{ + if (type == MXC_ISI_VIDEO_M2M_CAP) { + /* Downscaling only */ + pix->width = min(pix->width, ctx->queues.out.format.width); + pix->height = min(pix->height, ctx->queues.out.format.height); + } + + return mxc_isi_format_try(ctx->m2m->pipe, pix, type); +} + +static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh, + struct v4l2_format *f) +{ + const enum mxc_isi_video_type type = + f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; + struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + + __mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type); + + return 0; +} + +static int mxc_isi_m2m_g_fmt_vid(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + const struct mxc_isi_m2m_ctx_queue_data *qdata = + mxc_isi_m2m_ctx_qdata(ctx, f->type); + + f->fmt.pix_mp = qdata->format; + + return 0; +} + +static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, + struct v4l2_format *f) +{ + const enum mxc_isi_video_type type = + f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; + struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + const struct mxc_isi_format_info *info; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) + return -EBUSY; + + info = __mxc_isi_m2m_try_fmt_vid(ctx, pix, type); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ctx->queues.out.format = *pix; + ctx->queues.out.info = info; + } + + /* + * Always set the format on the capture side, due to either format + * propagation or direct setting. + */ + ctx->queues.cap.format = *pix; + ctx->queues.cap.info = info; + + return 0; +} + +static int mxc_isi_m2m_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format; + const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format; + const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; + const struct mxc_isi_format_info *out_info = ctx->queues.out.info; + struct mxc_isi_m2m *m2m = ctx->m2m; + bool bypass; + + int ret; + + mutex_lock(&m2m->lock); + + if (m2m->usage_count == INT_MAX) { + ret = -EOVERFLOW; + goto unlock; + } + + bypass = cap_pix->width == out_pix->width && + cap_pix->height == out_pix->height && + cap_info->encoding == out_info->encoding; + + /* + * Acquire the pipe and initialize the channel with the first user of + * the M2M device. + */ + if (m2m->usage_count == 0) { + ret = mxc_isi_channel_acquire(m2m->pipe, + &mxc_isi_m2m_frame_write_done, + bypass); + if (ret) + goto unlock; + + mxc_isi_channel_get(m2m->pipe); + } + + m2m->usage_count++; + + /* + * Allocate resources for the channel, counting how many users require + * buffer chaining. + */ + if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { + ret = mxc_isi_channel_chain(m2m->pipe, bypass); + if (ret) + goto deinit; + + m2m->chained_count++; + ctx->chained = true; + } + + /* + * Drop the lock to start the stream, as the .device_run() operation + * needs to acquire it. + */ + mutex_unlock(&m2m->lock); + ret = v4l2_m2m_ioctl_streamon(file, fh, type); + if (ret) { + /* Reacquire the lock for the cleanup path. */ + mutex_lock(&m2m->lock); + goto unchain; + } + + return 0; + +unchain: + if (ctx->chained && --m2m->chained_count == 0) + mxc_isi_channel_unchain(m2m->pipe); + ctx->chained = false; + +deinit: + if (--m2m->usage_count == 0) { + mxc_isi_channel_put(m2m->pipe); + mxc_isi_channel_release(m2m->pipe); + } + +unlock: + mutex_unlock(&m2m->lock); + return ret; +} + +static int mxc_isi_m2m_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + struct mxc_isi_m2m *m2m = ctx->m2m; + + v4l2_m2m_ioctl_streamoff(file, fh, type); + + mutex_lock(&m2m->lock); + + /* + * If the last context is this one, reset it to make sure the device + * will be reconfigured when streaming is restarted. + */ + if (m2m->last_ctx == ctx) + m2m->last_ctx = NULL; + + /* Free the channel resources if this is the last chained context. */ + if (ctx->chained && --m2m->chained_count == 0) + mxc_isi_channel_unchain(m2m->pipe); + ctx->chained = false; + + /* Turn off the light with the last user. */ + if (--m2m->usage_count == 0) { + mxc_isi_channel_disable(m2m->pipe); + mxc_isi_channel_put(m2m->pipe); + mxc_isi_channel_release(m2m->pipe); + } + + WARN_ON(m2m->usage_count < 0); + + mutex_unlock(&m2m->lock); + + return 0; +} + +static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { + .vidioc_querycap = mxc_isi_m2m_querycap, + + .vidioc_enum_fmt_vid_cap = mxc_isi_m2m_enum_fmt_vid, + .vidioc_enum_fmt_vid_out = mxc_isi_m2m_enum_fmt_vid, + .vidioc_g_fmt_vid_cap_mplane = mxc_isi_m2m_g_fmt_vid, + .vidioc_g_fmt_vid_out_mplane = mxc_isi_m2m_g_fmt_vid, + .vidioc_s_fmt_vid_cap_mplane = mxc_isi_m2m_s_fmt_vid, + .vidioc_s_fmt_vid_out_mplane = mxc_isi_m2m_s_fmt_vid, + .vidioc_try_fmt_vid_cap_mplane = mxc_isi_m2m_try_fmt_vid, + .vidioc_try_fmt_vid_out_mplane = mxc_isi_m2m_try_fmt_vid, + + .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_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + + .vidioc_streamon = mxc_isi_m2m_streamon, + .vidioc_streamoff = mxc_isi_m2m_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- + * Video device file operations + */ + +static void mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx *ctx, + struct mxc_isi_m2m_ctx_queue_data *qdata, + enum mxc_isi_video_type type) +{ + qdata->format.width = MXC_ISI_DEF_WIDTH; + qdata->format.height = MXC_ISI_DEF_HEIGHT; + qdata->format.pixelformat = MXC_ISI_DEF_PIXEL_FORMAT; + + qdata->info = mxc_isi_format_try(ctx->m2m->pipe, &qdata->format, type); +} + +static int mxc_isi_m2m_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct mxc_isi_m2m *m2m = video_drvdata(file); + struct mxc_isi_m2m_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->m2m = m2m; + mutex_init(&ctx->vb2_lock); + + v4l2_fh_init(&ctx->fh, vdev); + file->private_data = &ctx->fh; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, ctx, + &mxc_isi_m2m_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + ctx->fh.m2m_ctx = NULL; + goto err_fh; + } + + mxc_isi_m2m_init_format(ctx, &ctx->queues.out, MXC_ISI_VIDEO_M2M_OUT); + mxc_isi_m2m_init_format(ctx, &ctx->queues.cap, MXC_ISI_VIDEO_M2M_CAP); + + ret = mxc_isi_m2m_ctx_ctrls_create(ctx); + if (ret) + goto err_ctx; + + ret = pm_runtime_resume_and_get(m2m->isi->dev); + if (ret) + goto err_ctrls; + + v4l2_fh_add(&ctx->fh); + + return 0; + +err_ctrls: + mxc_isi_m2m_ctx_ctrls_delete(ctx); +err_ctx: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +err_fh: + v4l2_fh_exit(&ctx->fh); + mutex_destroy(&ctx->vb2_lock); + kfree(ctx); + return ret; +} + +static int mxc_isi_m2m_release(struct file *file) +{ + struct mxc_isi_m2m *m2m = video_drvdata(file); + struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(file->private_data); + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + mxc_isi_m2m_ctx_ctrls_delete(ctx); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + + mutex_destroy(&ctx->vb2_lock); + kfree(ctx); + + pm_runtime_put(m2m->isi->dev); + + return 0; +} + +static const struct v4l2_file_operations mxc_isi_m2m_fops = { + .owner = THIS_MODULE, + .open = mxc_isi_m2m_open, + .release = mxc_isi_m2m_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +/* ----------------------------------------------------------------------------- + * Registration + */ + +int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev) +{ + struct mxc_isi_m2m *m2m = &isi->m2m; + struct video_device *vdev = &m2m->vdev; + struct media_link *link; + int ret; + + m2m->isi = isi; + m2m->pipe = &isi->pipes[0]; + + mutex_init(&m2m->lock); + + /* Initialize the video device and create controls. */ + snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.m2m"); + + vdev->fops = &mxc_isi_m2m_fops; + vdev->ioctl_ops = &mxc_isi_m2m_ioctl_ops; + vdev->v4l2_dev = v4l2_dev; + vdev->minor = -1; + vdev->release = video_device_release_empty; + vdev->vfl_dir = VFL_DIR_M2M; + + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + video_set_drvdata(vdev, m2m); + + /* Create the M2M device. */ + m2m->m2m_dev = v4l2_m2m_init(&mxc_isi_m2m_ops); + if (IS_ERR(m2m->m2m_dev)) { + dev_err(isi->dev, "failed to initialize m2m device\n"); + ret = PTR_ERR(m2m->m2m_dev); + goto err_mutex; + } + + /* Register the video device. */ + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(isi->dev, "failed to register m2m device\n"); + goto err_m2m; + } + + /* + * Populate the media graph. We can't use the mem2mem helper + * v4l2_m2m_register_media_controller() as the M2M interface needs to + * be connected to the existing entities in the graph, so we have to + * wire things up manually: + * + * - The entity in the video_device, which isn't touched by the V4L2 + * core for M2M devices, is used as the source I/O entity in the + * graph, connected to the crossbar switch. + * + * - The video device at the end of the pipeline provides the sink + * entity, and is already wired up in the graph. + * + * - A new interface is created, pointing at both entities. The sink + * entity will thus have two interfaces pointing to it. + */ + m2m->pad.flags = MEDIA_PAD_FL_SOURCE; + vdev->entity.name = "mxc_isi.output"; + vdev->entity.function = MEDIA_ENT_F_IO_V4L; + ret = media_entity_pads_init(&vdev->entity, 1, &m2m->pad); + if (ret) + goto err_video; + + ret = media_device_register_entity(v4l2_dev->mdev, &vdev->entity); + if (ret) + goto err_entity_cleanup; + + ret = media_create_pad_link(&vdev->entity, 0, + &m2m->isi->crossbar.sd.entity, + m2m->isi->crossbar.num_sinks - 1, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_entity_unreg; + + m2m->intf = media_devnode_create(v4l2_dev->mdev, MEDIA_INTF_T_V4L_VIDEO, + 0, VIDEO_MAJOR, vdev->minor); + if (!m2m->intf) { + ret = -ENOMEM; + goto err_entity_unreg; + } + + link = media_create_intf_link(&vdev->entity, &m2m->intf->intf, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_devnode; + } + + link = media_create_intf_link(&m2m->pipe->video.vdev.entity, + &m2m->intf->intf, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_devnode; + } + + return 0; + +err_devnode: + media_devnode_remove(m2m->intf); +err_entity_unreg: + media_device_unregister_entity(&vdev->entity); +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_video: + video_unregister_device(vdev); +err_m2m: + v4l2_m2m_release(m2m->m2m_dev); +err_mutex: + mutex_destroy(&m2m->lock); + return ret; +} + +int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi) +{ + struct mxc_isi_m2m *m2m = &isi->m2m; + struct video_device *vdev = &m2m->vdev; + + video_unregister_device(vdev); + + v4l2_m2m_release(m2m->m2m_dev); + media_devnode_remove(m2m->intf); + media_entity_cleanup(&vdev->entity); + mutex_destroy(&m2m->lock); + + return 0; +} diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c new file mode 100644 index 000000000000..c4454aa1cb34 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform + * + * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which + * used to process image from camera sensor to memory or DC + * + * Copyright (c) 2019 NXP Semiconductor + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/minmax.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-v4l2.h> + +#include "imx8-isi-core.h" +#include "imx8-isi-regs.h" + +/* + * While the ISI receives data from the gasket on a 3x12-bit bus, the pipeline + * subdev conceptually includes the gasket in order to avoid exposing an extra + * subdev between the CSIS and the ISI. We thus need to expose media bus codes + * corresponding to the CSIS output, which is narrower. + */ +static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = { + /* YUV formats */ + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .output = MEDIA_BUS_FMT_YUV8_1X24, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK), + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .output = MEDIA_BUS_FMT_YUV8_1X24, + .pads = BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_YUV, + }, + /* RGB formats */ + { + .mbus_code = MEDIA_BUS_FMT_RGB565_1X16, + .output = MEDIA_BUS_FMT_RGB888_1X24, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK), + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .output = MEDIA_BUS_FMT_RGB888_1X24, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RGB, + }, + /* RAW formats */ + { + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .output = MEDIA_BUS_FMT_Y8_1X8, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .output = MEDIA_BUS_FMT_Y10_1X10, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y12_1X12, + .output = MEDIA_BUS_FMT_Y12_1X12, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y14_1X14, + .output = MEDIA_BUS_FMT_Y14_1X14, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .output = MEDIA_BUS_FMT_SBGGR8_1X8, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .output = MEDIA_BUS_FMT_SGBRG8_1X8, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .output = MEDIA_BUS_FMT_SGRBG8_1X8, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .output = MEDIA_BUS_FMT_SRGGB8_1X8, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .output = MEDIA_BUS_FMT_SBGGR10_1X10, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .output = MEDIA_BUS_FMT_SGBRG10_1X10, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .output = MEDIA_BUS_FMT_SGRBG10_1X10, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .output = MEDIA_BUS_FMT_SRGGB10_1X10, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .output = MEDIA_BUS_FMT_SBGGR12_1X12, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .output = MEDIA_BUS_FMT_SGBRG12_1X12, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .output = MEDIA_BUS_FMT_SGRBG12_1X12, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .output = MEDIA_BUS_FMT_SRGGB12_1X12, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14, + .output = MEDIA_BUS_FMT_SBGGR14_1X14, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG14_1X14, + .output = MEDIA_BUS_FMT_SGBRG14_1X14, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG14_1X14, + .output = MEDIA_BUS_FMT_SGRBG14_1X14, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB14_1X14, + .output = MEDIA_BUS_FMT_SRGGB14_1X14, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, + /* JPEG */ + { + .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, + .output = MEDIA_BUS_FMT_JPEG_1X8, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + } +}; + +const struct mxc_isi_bus_format_info * +mxc_isi_bus_format_by_code(u32 code, unsigned int pad) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) { + const struct mxc_isi_bus_format_info *info = + &mxc_isi_bus_formats[i]; + + if (info->mbus_code == code && info->pads & BIT(pad)) + return info; + } + + return NULL; +} + +const struct mxc_isi_bus_format_info * +mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) { + const struct mxc_isi_bus_format_info *info = + &mxc_isi_bus_formats[i]; + + if (!(info->pads & BIT(pad))) + continue; + + if (!index) + return info; + + index--; + } + + return NULL; +} + +static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mxc_isi_pipe, sd); +} + +int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; + const struct mxc_isi_bus_format_info *sink_info; + const struct mxc_isi_bus_format_info *src_info; + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt; + const struct v4l2_rect *compose; + struct v4l2_subdev_state *state; + struct v4l2_subdev *sd = &pipe->sd; + struct v4l2_area in_size, scale; + struct v4l2_rect crop; + u32 input; + int ret; + + /* + * Find the connected input by inspecting the crossbar switch routing + * table. + */ + state = v4l2_subdev_lock_and_get_active_state(&xbar->sd); + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + xbar->num_sinks + pipe->id, + 0, &input, NULL); + v4l2_subdev_unlock_state(state); + + if (ret) + return -EPIPE; + + /* Configure the pipeline. */ + state = v4l2_subdev_lock_and_get_active_state(sd); + + sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); + crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + + sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, + MXC_ISI_PIPE_PAD_SINK); + src_info = mxc_isi_bus_format_by_code(src_fmt->code, + MXC_ISI_PIPE_PAD_SOURCE); + + in_size.width = sink_fmt->width; + in_size.height = sink_fmt->height; + scale.width = compose->width; + scale.height = compose->height; + + v4l2_subdev_unlock_state(state); + + /* Configure the ISI channel. */ + mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop, + sink_info->encoding, src_info->encoding); + + mxc_isi_channel_enable(pipe); + + /* Enable streams on the crossbar switch. */ + ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id, + BIT(0)); + if (ret) { + mxc_isi_channel_disable(pipe); + dev_err(pipe->isi->dev, "Failed to enable pipe %u\n", + pipe->id); + return ret; + } + + return 0; +} + +void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; + int ret; + + ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id, + BIT(0)); + if (ret) + dev_err(pipe->isi->dev, "Failed to disable pipe %u\n", + pipe->id); + + mxc_isi_channel_disable(pipe); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static struct v4l2_mbus_framefmt * +mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, + struct v4l2_subdev_state *state, + unsigned int pad) +{ + return v4l2_subdev_get_try_format(&pipe->sd, state, pad); +} + +static struct v4l2_rect * +mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, + struct v4l2_subdev_state *state, + unsigned int pad) +{ + return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); +} + +static struct v4l2_rect * +mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, + struct v4l2_subdev_state *state, + unsigned int pad) +{ + return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); +} + +static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct mxc_isi_pipe *pipe = to_isi_pipe(sd); + struct v4l2_mbus_framefmt *fmt_source; + struct v4l2_mbus_framefmt *fmt_sink; + struct v4l2_rect *compose; + struct v4l2_rect *crop; + + fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + fmt_source = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + + fmt_sink->width = MXC_ISI_DEF_WIDTH; + fmt_sink->height = MXC_ISI_DEF_HEIGHT; + fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK; + fmt_sink->field = V4L2_FIELD_NONE; + fmt_sink->colorspace = V4L2_COLORSPACE_JPEG; + fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); + fmt_sink->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, + fmt_sink->ycbcr_enc); + fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); + + *fmt_source = *fmt_sink; + fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE; + + compose = mxc_isi_pipe_get_pad_compose(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE); + + compose->left = 0; + compose->top = 0; + compose->width = MXC_ISI_DEF_WIDTH; + compose->height = MXC_ISI_DEF_HEIGHT; + + *crop = *compose; + + return 0; +} + +static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const u32 output_codes[] = { + MEDIA_BUS_FMT_YUV8_1X24, + MEDIA_BUS_FMT_RGB888_1X24, + }; + struct mxc_isi_pipe *pipe = to_isi_pipe(sd); + const struct mxc_isi_bus_format_info *info; + unsigned int index; + unsigned int i; + + if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) { + const struct v4l2_mbus_framefmt *format; + + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + info = mxc_isi_bus_format_by_code(format->code, + MXC_ISI_PIPE_PAD_SINK); + + if (info->encoding == MXC_ISI_ENC_RAW) { + /* + * For RAW formats, the sink and source media bus codes + * must match. + */ + if (code->index) + return -EINVAL; + + code->code = info->output; + } else { + /* + * For RGB or YUV formats, the ISI supports format + * conversion. Either of the two output formats can be + * used regardless of the input. + */ + if (code->index > 1) + return -EINVAL; + + code->code = output_codes[code->index]; + } + + return 0; + } + + index = code->index; + + for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) { + info = &mxc_isi_bus_formats[i]; + + if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK))) + continue; + + if (index == 0) { + code->code = info->mbus_code; + return 0; + } + + index--; + } + + return -EINVAL; +} + +static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mxc_isi_pipe *pipe = to_isi_pipe(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + const struct mxc_isi_bus_format_info *info; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *rect; + + if (vb2_is_busy(&pipe->video.vb2_q)) + return -EBUSY; + + if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) { + unsigned int max_width; + + info = mxc_isi_bus_format_by_code(mf->code, + MXC_ISI_PIPE_PAD_SINK); + if (!info) + info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK, + MXC_ISI_PIPE_PAD_SINK); + + /* + * Limit the max line length if there's no adjacent pipe to + * chain with. + */ + max_width = pipe->id == pipe->isi->pdata->num_channels - 1 + ? MXC_ISI_MAX_WIDTH_UNCHAINED + : MXC_ISI_MAX_WIDTH_CHAINED; + + mf->code = info->mbus_code; + mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH, max_width); + mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT, + MXC_ISI_MAX_HEIGHT); + + /* Propagate the format to the source pad. */ + rect = mxc_isi_pipe_get_pad_compose(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + rect->width = mf->width; + rect->height = mf->height; + + rect = mxc_isi_pipe_get_pad_crop(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + rect->left = 0; + rect->top = 0; + rect->width = mf->width; + rect->height = mf->height; + + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + format->code = info->output; + format->width = mf->width; + format->height = mf->height; + } else { + /* + * For RGB or YUV formats, the ISI supports RGB <-> YUV format + * conversion. For RAW formats, the sink and source media bus + * codes must match. + */ + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + info = mxc_isi_bus_format_by_code(format->code, + MXC_ISI_PIPE_PAD_SINK); + + if (info->encoding != MXC_ISI_ENC_RAW) { + if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 && + mf->code != MEDIA_BUS_FMT_RGB888_1X24) + mf->code = info->output; + + info = mxc_isi_bus_format_by_code(mf->code, + MXC_ISI_PIPE_PAD_SOURCE); + } + + mf->code = info->output; + + /* + * The width and height on the source can't be changed, they + * must match the crop rectangle size. + */ + rect = mxc_isi_pipe_get_pad_crop(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + + mf->width = rect->width; + mf->height = rect->height; + } + + format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad); + *format = *mf; + + dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u", + fmt->pad, mf->code, mf->width, mf->height); + + return 0; +} + +static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct mxc_isi_pipe *pipe = to_isi_pipe(sd); + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *rect; + + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad != MXC_ISI_PIPE_PAD_SINK) + /* No compose rectangle on source pad. */ + return -EINVAL; + + /* The sink compose is bound by the sink format. */ + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; + break; + + case V4L2_SEL_TGT_CROP_BOUNDS: + if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) + /* No crop rectangle on sink pad. */ + return -EINVAL; + + /* The source crop is bound by the sink compose. */ + rect = mxc_isi_pipe_get_pad_compose(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + sel->r = *rect; + break; + + case V4L2_SEL_TGT_CROP: + if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) + /* No crop rectangle on sink pad. */ + return -EINVAL; + + rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad); + sel->r = *rect; + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != MXC_ISI_PIPE_PAD_SINK) + /* No compose rectangle on source pad. */ + return -EINVAL; + + rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad); + sel->r = *rect; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct mxc_isi_pipe *pipe = to_isi_pipe(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *rect; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) + /* The pipeline support cropping on the source only. */ + return -EINVAL; + + /* The source crop is bound by the sink compose. */ + rect = mxc_isi_pipe_get_pad_compose(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + sel->r.left = clamp_t(s32, sel->r.left, 0, rect->width - 1); + sel->r.top = clamp_t(s32, sel->r.top, 0, rect->height - 1); + sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, + rect->width - sel->r.left); + sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, + rect->height - sel->r.top); + + rect = mxc_isi_pipe_get_pad_crop(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + *rect = sel->r; + + /* Propagate the crop rectangle to the source pad. */ + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + format->width = sel->r.width; + format->height = sel->r.height; + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != MXC_ISI_PIPE_PAD_SINK) + /* Composing is supported on the sink only. */ + return -EINVAL; + + /* The sink crop is bound by the sink format downscaling only). */ + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, + format->width); + sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, + format->height); + + rect = mxc_isi_pipe_get_pad_compose(pipe, state, + MXC_ISI_PIPE_PAD_SINK); + *rect = sel->r; + + /* Propagate the compose rectangle to the source pad. */ + rect = mxc_isi_pipe_get_pad_crop(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + rect->left = 0; + rect->top = 0; + rect->width = sel->r.width; + rect->height = sel->r.height; + + format = mxc_isi_pipe_get_pad_format(pipe, state, + MXC_ISI_PIPE_PAD_SOURCE); + format->width = sel->r.width; + format->height = sel->r.height; + break; + + default: + return -EINVAL; + } + + dev_dbg(pipe->isi->dev, "%s, target %#x: (%d,%d)/%dx%d", __func__, + sel->target, sel->r.left, sel->r.top, sel->r.width, + sel->r.height); + + return 0; +} + +static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { + .init_cfg = mxc_isi_pipe_init_cfg, + .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mxc_isi_pipe_set_fmt, + .get_selection = mxc_isi_pipe_get_selection, + .set_selection = mxc_isi_pipe_set_selection, +}; + +static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { + .pad = &mxc_isi_pipe_subdev_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * IRQ handling + */ + +static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv) +{ + struct mxc_isi_pipe *pipe = priv; + const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg; + u32 status; + + status = mxc_isi_channel_irq_status(pipe, true); + + if (status & CHNL_STS_FRM_STRD) { + if (!WARN_ON(!pipe->irq_handler)) + pipe->irq_handler(pipe, status); + } + + if (status & (CHNL_STS_AXI_WR_ERR_Y | + CHNL_STS_AXI_WR_ERR_U | + CHNL_STS_AXI_WR_ERR_V)) + dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n", + __func__, status); + + if (status & (ier_reg->panic_y_buf_en.mask | + ier_reg->panic_u_buf_en.mask | + ier_reg->panic_v_buf_en.mask)) + dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n", + __func__, status); + + if (status & (ier_reg->oflw_y_buf_en.mask | + ier_reg->oflw_u_buf_en.mask | + ier_reg->oflw_v_buf_en.mask)) + dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n", + __func__, status); + + if (status & (ier_reg->excs_oflw_y_buf_en.mask | + ier_reg->excs_oflw_u_buf_en.mask | + ier_reg->excs_oflw_v_buf_en.mask)) + dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n", + __func__, status); + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * Init & cleanup + */ + +static const struct media_entity_operations mxc_isi_pipe_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) +{ + struct mxc_isi_pipe *pipe = &isi->pipes[id]; + struct v4l2_subdev *sd; + int irq; + int ret; + + pipe->id = id; + pipe->isi = isi; + pipe->regs = isi->regs + id * isi->pdata->reg_offset; + + mutex_init(&pipe->lock); + + pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF + | MXC_ISI_CHANNEL_RES_OUTPUT_BUF; + pipe->acquired_res = 0; + pipe->chained_res = 0; + pipe->chained = false; + + sd = &pipe->sd; + v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); + sd->dev = isi->dev; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &mxc_isi_pipe_entity_ops; + + pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM, + pipe->pads); + if (ret) + goto error; + + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) + goto error; + + /* Register IRQ handler. */ + mxc_isi_channel_irq_clear(pipe); + + irq = platform_get_irq(to_platform_device(isi->dev), id); + if (irq < 0) { + dev_err(pipe->isi->dev, "Failed to get IRQ (%d)\n", irq); + ret = irq; + goto error; + } + + ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, + 0, dev_name(isi->dev), pipe); + if (ret < 0) { + dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&sd->entity); + mutex_destroy(&pipe->lock); + + return ret; +} + +void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) +{ + struct v4l2_subdev *sd = &pipe->sd; + + media_entity_cleanup(&sd->entity); + mutex_destroy(&pipe->lock); +} + +int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, + mxc_isi_pipe_irq_t irq_handler) +{ + const struct mxc_isi_bus_format_info *sink_info; + const struct mxc_isi_bus_format_info *src_info; + struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_subdev *sd = &pipe->sd; + struct v4l2_subdev_state *state; + bool bypass; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(sd); + sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + v4l2_subdev_unlock_state(state); + + sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, + MXC_ISI_PIPE_PAD_SINK); + src_info = mxc_isi_bus_format_by_code(src_fmt->code, + MXC_ISI_PIPE_PAD_SOURCE); + + bypass = sink_fmt->width == src_fmt->width && + sink_fmt->height == src_fmt->height && + sink_info->encoding == src_info->encoding; + + ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass); + if (ret) + return ret; + + /* Chain the channel if needed for wide resolutions. */ + if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { + ret = mxc_isi_channel_chain(pipe, bypass); + if (ret) + mxc_isi_channel_release(pipe); + } + + return ret; +} + +void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe) +{ + mxc_isi_channel_release(pipe); + mxc_isi_channel_unchain(pipe); +} diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h new file mode 100644 index 000000000000..1b65eccdf0da --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h @@ -0,0 +1,418 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019-2020 NXP + */ + +#ifndef __IMX8_ISI_REGS_H__ +#define __IMX8_ISI_REGS_H__ + +#include <linux/bits.h> + +/* ISI Registers Define */ +/* Channel Control Register */ +#define CHNL_CTRL 0x0000 +#define CHNL_CTRL_CHNL_EN BIT(31) +#define CHNL_CTRL_CLK_EN BIT(30) +#define CHNL_CTRL_CHNL_BYPASS BIT(29) +#define CHNL_CTRL_CHAIN_BUF(n) ((n) << 25) +#define CHNL_CTRL_CHAIN_BUF_MASK GENMASK(26, 25) +#define CHNL_CTRL_CHAIN_BUF_NO_CHAIN 0 +#define CHNL_CTRL_CHAIN_BUF_2_CHAIN 1 +#define CHNL_CTRL_SW_RST BIT(24) +#define CHNL_CTRL_BLANK_PXL(n) ((n) << 16) +#define CHNL_CTRL_BLANK_PXL_MASK GENMASK(23, 16) +#define CHNL_CTRL_MIPI_VC_ID(n) ((n) << 6) +#define CHNL_CTRL_MIPI_VC_ID_MASK GENMASK(7, 6) +#define CHNL_CTRL_SRC_TYPE(n) ((n) << 4) +#define CHNL_CTRL_SRC_TYPE_MASK BIT(4) +#define CHNL_CTRL_SRC_TYPE_DEVICE 0 +#define CHNL_CTRL_SRC_TYPE_MEMORY 1 +#define CHNL_CTRL_SRC_INPUT(n) ((n) << 0) +#define CHNL_CTRL_SRC_INPUT_MASK GENMASK(2, 0) + +/* Channel Image Control Register */ +#define CHNL_IMG_CTRL 0x0004 +#define CHNL_IMG_CTRL_FORMAT(n) ((n) << 24) +#define CHNL_IMG_CTRL_FORMAT_MASK GENMASK(29, 24) +#define CHNL_IMG_CTRL_FORMAT_RGBA8888 0x00 +#define CHNL_IMG_CTRL_FORMAT_ABGR8888 0x01 +#define CHNL_IMG_CTRL_FORMAT_ARGB8888 0x02 +#define CHNL_IMG_CTRL_FORMAT_RGBX888 0x03 +#define CHNL_IMG_CTRL_FORMAT_XBGR888 0x04 +#define CHNL_IMG_CTRL_FORMAT_XRGB888 0x05 +#define CHNL_IMG_CTRL_FORMAT_RGB888P 0x06 +#define CHNL_IMG_CTRL_FORMAT_BGR888P 0x07 +#define CHNL_IMG_CTRL_FORMAT_A2BGR10 0x08 +#define CHNL_IMG_CTRL_FORMAT_A2RGB10 0x09 +#define CHNL_IMG_CTRL_FORMAT_RGB565 0x0a +#define CHNL_IMG_CTRL_FORMAT_RAW8 0x0b +#define CHNL_IMG_CTRL_FORMAT_RAW10 0x0c +#define CHNL_IMG_CTRL_FORMAT_RAW10P 0x0d +#define CHNL_IMG_CTRL_FORMAT_RAW12 0x0e +#define CHNL_IMG_CTRL_FORMAT_RAW16 0x0f +#define CHNL_IMG_CTRL_FORMAT_YUV444_1P8P 0x10 +#define CHNL_IMG_CTRL_FORMAT_YUV444_2P8P 0x11 +#define CHNL_IMG_CTRL_FORMAT_YUV444_3P8P 0x12 +#define CHNL_IMG_CTRL_FORMAT_YUV444_1P8 0x13 +#define CHNL_IMG_CTRL_FORMAT_YUV444_1P10 0x14 +#define CHNL_IMG_CTRL_FORMAT_YUV444_2P10 0x15 +#define CHNL_IMG_CTRL_FORMAT_YUV444_3P10 0x16 +#define CHNL_IMG_CTRL_FORMAT_YUV444_1P10P 0x18 +#define CHNL_IMG_CTRL_FORMAT_YUV444_2P10P 0x19 +#define CHNL_IMG_CTRL_FORMAT_YUV444_3P10P 0x1a +#define CHNL_IMG_CTRL_FORMAT_YUV444_1P12 0x1c +#define CHNL_IMG_CTRL_FORMAT_YUV444_2P12 0x1d +#define CHNL_IMG_CTRL_FORMAT_YUV444_3P12 0x1e +#define CHNL_IMG_CTRL_FORMAT_YUV422_1P8P 0x20 +#define CHNL_IMG_CTRL_FORMAT_YUV422_2P8P 0x21 +#define CHNL_IMG_CTRL_FORMAT_YUV422_3P8P 0x22 +#define CHNL_IMG_CTRL_FORMAT_YUV422_1P10 0x24 +#define CHNL_IMG_CTRL_FORMAT_YUV422_2P10 0x25 +#define CHNL_IMG_CTRL_FORMAT_YUV422_3P10 0x26 +#define CHNL_IMG_CTRL_FORMAT_YUV422_1P10P 0x28 +#define CHNL_IMG_CTRL_FORMAT_YUV422_2P10P 0x29 +#define CHNL_IMG_CTRL_FORMAT_YUV422_3P10P 0x2a +#define CHNL_IMG_CTRL_FORMAT_YUV422_1P12 0x2c +#define CHNL_IMG_CTRL_FORMAT_YUV422_2P12 0x2d +#define CHNL_IMG_CTRL_FORMAT_YUV422_3P12 0x2e +#define CHNL_IMG_CTRL_FORMAT_YUV420_2P8P 0x31 +#define CHNL_IMG_CTRL_FORMAT_YUV420_3P8P 0x32 +#define CHNL_IMG_CTRL_FORMAT_YUV420_2P10 0x35 +#define CHNL_IMG_CTRL_FORMAT_YUV420_3P10 0x36 +#define CHNL_IMG_CTRL_FORMAT_YUV420_2P10P 0x39 +#define CHNL_IMG_CTRL_FORMAT_YUV420_3P10P 0x3a +#define CHNL_IMG_CTRL_FORMAT_YUV420_2P12 0x3d +#define CHNL_IMG_CTRL_FORMAT_YUV420_3P12 0x3e +#define CHNL_IMG_CTRL_GBL_ALPHA_VAL(n) ((n) << 16) +#define CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK GENMASK(23, 16) +#define CHNL_IMG_CTRL_GBL_ALPHA_EN BIT(15) +#define CHNL_IMG_CTRL_DEINT(n) ((n) << 12) +#define CHNL_IMG_CTRL_DEINT_MASK GENMASK(14, 12) +#define CHNL_IMG_CTRL_DEINT_WEAVE_ODD_EVEN 2 +#define CHNL_IMG_CTRL_DEINT_WEAVE_EVEN_ODD 3 +#define CHNL_IMG_CTRL_DEINT_BLEND_ODD_EVEN 4 +#define CHNL_IMG_CTRL_DEINT_BLEND_EVEN_ODD 5 +#define CHNL_IMG_CTRL_DEINT_LDOUBLE_ODD_EVEN 6 +#define CHNL_IMG_CTRL_DEINT_LDOUBLE_EVEN_ODD 7 +#define CHNL_IMG_CTRL_DEC_X(n) ((n) << 10) +#define CHNL_IMG_CTRL_DEC_X_MASK GENMASK(11, 10) +#define CHNL_IMG_CTRL_DEC_Y(n) ((n) << 8) +#define CHNL_IMG_CTRL_DEC_Y_MASK GENMASK(9, 8) +#define CHNL_IMG_CTRL_CROP_EN BIT(7) +#define CHNL_IMG_CTRL_VFLIP_EN BIT(6) +#define CHNL_IMG_CTRL_HFLIP_EN BIT(5) +#define CHNL_IMG_CTRL_YCBCR_MODE BIT(3) +#define CHNL_IMG_CTRL_CSC_MODE(n) ((n) << 1) +#define CHNL_IMG_CTRL_CSC_MODE_MASK GENMASK(2, 1) +#define CHNL_IMG_CTRL_CSC_MODE_YUV2RGB 0 +#define CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB 1 +#define CHNL_IMG_CTRL_CSC_MODE_RGB2YUV 2 +#define CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR 3 +#define CHNL_IMG_CTRL_CSC_BYPASS BIT(0) + +/* Channel Output Buffer Control Register */ +#define CHNL_OUT_BUF_CTRL 0x0008 +#define CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR BIT(15) +#define CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR BIT(14) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V(n) ((n) << 6) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_MASK GENMASK(7, 6) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_NO_PANIC 0 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_PANIC_25 1 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_PANIC_50 2 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_PANIC_75 3 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U(n) ((n) << 3) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_MASK GENMASK(4, 3) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_NO_PANIC 0 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_PANIC_25 1 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_PANIC_50 2 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_PANIC_75 3 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y(n) ((n) << 0) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_MASK GENMASK(1, 0) +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_NO_PANIC 0 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_PANIC_25 1 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_PANIC_50 2 +#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_PANIC_75 3 + +/* Channel Image Configuration */ +#define CHNL_IMG_CFG 0x000c +#define CHNL_IMG_CFG_HEIGHT(n) ((n) << 16) +#define CHNL_IMG_CFG_HEIGHT_MASK GENMASK(28, 16) +#define CHNL_IMG_CFG_WIDTH(n) ((n) << 0) +#define CHNL_IMG_CFG_WIDTH_MASK GENMASK(12, 0) + +/* Channel Interrupt Enable Register */ +#define CHNL_IER 0x0010 +#define CHNL_IER_MEM_RD_DONE_EN BIT(31) +#define CHNL_IER_LINE_RCVD_EN BIT(30) +#define CHNL_IER_FRM_RCVD_EN BIT(29) +#define CHNL_IER_AXI_WR_ERR_V_EN BIT(28) +#define CHNL_IER_AXI_WR_ERR_U_EN BIT(27) +#define CHNL_IER_AXI_WR_ERR_Y_EN BIT(26) +#define CHNL_IER_AXI_RD_ERR_EN BIT(25) + +/* Channel Status Register */ +#define CHNL_STS 0x0014 +#define CHNL_STS_MEM_RD_DONE BIT(31) +#define CHNL_STS_LINE_STRD BIT(30) +#define CHNL_STS_FRM_STRD BIT(29) +#define CHNL_STS_AXI_WR_ERR_V BIT(28) +#define CHNL_STS_AXI_WR_ERR_U BIT(27) +#define CHNL_STS_AXI_WR_ERR_Y BIT(26) +#define CHNL_STS_AXI_RD_ERR BIT(25) +#define CHNL_STS_OFLW_PANIC_V_BUF BIT(24) +#define CHNL_STS_EXCS_OFLW_V_BUF BIT(23) +#define CHNL_STS_OFLW_V_BUF BIT(22) +#define CHNL_STS_OFLW_PANIC_U_BUF BIT(21) +#define CHNL_STS_EXCS_OFLW_U_BUF BIT(20) +#define CHNL_STS_OFLW_U_BUF BIT(19) +#define CHNL_STS_OFLW_PANIC_Y_BUF BIT(18) +#define CHNL_STS_EXCS_OFLW_Y_BUF BIT(17) +#define CHNL_STS_OFLW_Y_BUF BIT(16) +#define CHNL_STS_EARLY_VSYNC_ERR BIT(15) +#define CHNL_STS_LATE_VSYNC_ERR BIT(14) +#define CHNL_STS_MEM_RD_OFLOW BIT(10) +#define CHNL_STS_BUF2_ACTIVE BIT(9) +#define CHNL_STS_BUF1_ACTIVE BIT(8) +#define CHNL_STS_OFLW_BYTES(n) ((n) << 0) +#define CHNL_STS_OFLW_BYTES_MASK GENMASK(7, 0) + +/* Channel Scale Factor Register */ +#define CHNL_SCALE_FACTOR 0x0018 +#define CHNL_SCALE_FACTOR_Y_SCALE(n) ((n) << 16) +#define CHNL_SCALE_FACTOR_Y_SCALE_MASK GENMASK(29, 16) +#define CHNL_SCALE_FACTOR_X_SCALE(n) ((n) << 0) +#define CHNL_SCALE_FACTOR_X_SCALE_MASK GENMASK(13, 0) + +/* Channel Scale Offset Register */ +#define CHNL_SCALE_OFFSET 0x001c +#define CHNL_SCALE_OFFSET_Y_SCALE(n) ((n) << 16) +#define CHNL_SCALE_OFFSET_Y_SCALE_MASK GENMASK(27, 16) +#define CHNL_SCALE_OFFSET_X_SCALE(n) ((n) << 0) +#define CHNL_SCALE_OFFSET_X_SCALE_MASK GENMASK(11, 0) + +/* Channel Crop Upper Left Corner Coordinate Register */ +#define CHNL_CROP_ULC 0x0020 +#define CHNL_CROP_ULC_X(n) ((n) << 16) +#define CHNL_CROP_ULC_X_MASK GENMASK(27, 16) +#define CHNL_CROP_ULC_Y(n) ((n) << 0) +#define CHNL_CROP_ULC_Y_MASK GENMASK(11, 0) + +/* Channel Crop Lower Right Corner Coordinate Register */ +#define CHNL_CROP_LRC 0x0024 +#define CHNL_CROP_LRC_X(n) ((n) << 16) +#define CHNL_CROP_LRC_X_MASK GENMASK(27, 16) +#define CHNL_CROP_LRC_Y(n) ((n) << 0) +#define CHNL_CROP_LRC_Y_MASK GENMASK(11, 0) + +/* Channel Color Space Conversion Coefficient Register 0 */ +#define CHNL_CSC_COEFF0 0x0028 +#define CHNL_CSC_COEFF0_A2(n) ((n) << 16) +#define CHNL_CSC_COEFF0_A2_MASK GENMASK(26, 16) +#define CHNL_CSC_COEFF0_A1(n) ((n) << 0) +#define CHNL_CSC_COEFF0_A1_MASK GENMASK(10, 0) + +/* Channel Color Space Conversion Coefficient Register 1 */ +#define CHNL_CSC_COEFF1 0x002c +#define CHNL_CSC_COEFF1_B1(n) ((n) << 16) +#define CHNL_CSC_COEFF1_B1_MASK GENMASK(26, 16) +#define CHNL_CSC_COEFF1_A3(n) ((n) << 0) +#define CHNL_CSC_COEFF1_A3_MASK GENMASK(10, 0) + +/* Channel Color Space Conversion Coefficient Register 2 */ +#define CHNL_CSC_COEFF2 0x0030 +#define CHNL_CSC_COEFF2_B3(n) ((n) << 16) +#define CHNL_CSC_COEFF2_B3_MASK GENMASK(26, 16) +#define CHNL_CSC_COEFF2_B2(n) ((n) << 0) +#define CHNL_CSC_COEFF2_B2_MASK GENMASK(10, 0) + +/* Channel Color Space Conversion Coefficient Register 3 */ +#define CHNL_CSC_COEFF3 0x0034 +#define CHNL_CSC_COEFF3_C2(n) ((n) << 16) +#define CHNL_CSC_COEFF3_C2_MASK GENMASK(26, 16) +#define CHNL_CSC_COEFF3_C1(n) ((n) << 0) +#define CHNL_CSC_COEFF3_C1_MASK GENMASK(10, 0) + +/* Channel Color Space Conversion Coefficient Register 4 */ +#define CHNL_CSC_COEFF4 0x0038 +#define CHNL_CSC_COEFF4_D1(n) ((n) << 16) +#define CHNL_CSC_COEFF4_D1_MASK GENMASK(24, 16) +#define CHNL_CSC_COEFF4_C3(n) ((n) << 0) +#define CHNL_CSC_COEFF4_C3_MASK GENMASK(10, 0) + +/* Channel Color Space Conversion Coefficient Register 5 */ +#define CHNL_CSC_COEFF5 0x003c +#define CHNL_CSC_COEFF5_D3(n) ((n) << 16) +#define CHNL_CSC_COEFF5_D3_MASK GENMASK(24, 16) +#define CHNL_CSC_COEFF5_D2(n) ((n) << 0) +#define CHNL_CSC_COEFF5_D2_MASK GENMASK(8, 0) + +/* Channel Alpha Value Register for ROI 0 */ +#define CHNL_ROI_0_ALPHA 0x0040 +#define CHNL_ROI_0_ALPHA_VAL(n) ((n) << 24) +#define CHNL_ROI_0_ALPHA_MASK GENMASK(31, 24) +#define CHNL_ROI_0_ALPHA_EN BIT(16) + +/* Channel Upper Left Coordinate Register for ROI 0 */ +#define CHNL_ROI_0_ULC 0x0044 +#define CHNL_ROI_0_ULC_X(n) ((n) << 16) +#define CHNL_ROI_0_ULC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_0_ULC_Y(n) ((n) << 0) +#define CHNL_ROI_0_ULC_Y_MASK GENMASK(11, 0) + +/* Channel Lower Right Coordinate Register for ROI 0 */ +#define CHNL_ROI_0_LRC 0x0048 +#define CHNL_ROI_0_LRC_X(n) ((n) << 16) +#define CHNL_ROI_0_LRC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_0_LRC_Y(n) ((n) << 0) +#define CHNL_ROI_0_LRC_Y_MASK GENMASK(11, 0) + +/* Channel Alpha Value Register for ROI 1 */ +#define CHNL_ROI_1_ALPHA 0x004c +#define CHNL_ROI_1_ALPHA_VAL(n) ((n) << 24) +#define CHNL_ROI_1_ALPHA_MASK GENMASK(31, 24) +#define CHNL_ROI_1_ALPHA_EN BIT(16) + +/* Channel Upper Left Coordinate Register for ROI 1 */ +#define CHNL_ROI_1_ULC 0x0050 +#define CHNL_ROI_1_ULC_X(n) ((n) << 16) +#define CHNL_ROI_1_ULC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_1_ULC_Y(n) ((n) << 0) +#define CHNL_ROI_1_ULC_Y_MASK GENMASK(11, 0) + +/* Channel Lower Right Coordinate Register for ROI 1 */ +#define CHNL_ROI_1_LRC 0x0054 +#define CHNL_ROI_1_LRC_X(n) ((n) << 16) +#define CHNL_ROI_1_LRC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_1_LRC_Y(n) ((n) << 0) +#define CHNL_ROI_1_LRC_Y_MASK GENMASK(11, 0) + +/* Channel Alpha Value Register for ROI 2 */ +#define CHNL_ROI_2_ALPHA 0x0058 +#define CHNL_ROI_2_ALPHA_VAL(n) ((n) << 24) +#define CHNL_ROI_2_ALPHA_MASK GENMASK(31, 24) +#define CHNL_ROI_2_ALPHA_EN BIT(16) + +/* Channel Upper Left Coordinate Register for ROI 2 */ +#define CHNL_ROI_2_ULC 0x005c +#define CHNL_ROI_2_ULC_X(n) ((n) << 16) +#define CHNL_ROI_2_ULC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_2_ULC_Y(n) ((n) << 0) +#define CHNL_ROI_2_ULC_Y_MASK GENMASK(11, 0) + +/* Channel Lower Right Coordinate Register for ROI 2 */ +#define CHNL_ROI_2_LRC 0x0060 +#define CHNL_ROI_2_LRC_X(n) ((n) << 16) +#define CHNL_ROI_2_LRC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_2_LRC_Y(n) ((n) << 0) +#define CHNL_ROI_2_LRC_Y_MASK GENMASK(11, 0) + +/* Channel Alpha Value Register for ROI 3 */ +#define CHNL_ROI_3_ALPHA 0x0064 +#define CHNL_ROI_3_ALPHA_VAL(n) ((n) << 24) +#define CHNL_ROI_3_ALPHA_MASK GENMASK(31, 24) +#define CHNL_ROI_3_ALPHA_EN BIT(16) + +/* Channel Upper Left Coordinate Register for ROI 3 */ +#define CHNL_ROI_3_ULC 0x0068 +#define CHNL_ROI_3_ULC_X(n) ((n) << 16) +#define CHNL_ROI_3_ULC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_3_ULC_Y(n) ((n) << 0) +#define CHNL_ROI_3_ULC_Y_MASK GENMASK(11, 0) + +/* Channel Lower Right Coordinate Register for ROI 3 */ +#define CHNL_ROI_3_LRC 0x006c +#define CHNL_ROI_3_LRC_X(n) ((n) << 16) +#define CHNL_ROI_3_LRC_X_MASK GENMASK(27, 16) +#define CHNL_ROI_3_LRC_Y(n) ((n) << 0) +#define CHNL_ROI_3_LRC_Y_MASK GENMASK(11, 0) +/* Channel RGB or Luma (Y) Output Buffer 1 Address */ +#define CHNL_OUT_BUF1_ADDR_Y 0x0070 + +/* Channel Chroma (U/Cb/UV/CbCr) Output Buffer 1 Address */ +#define CHNL_OUT_BUF1_ADDR_U 0x0074 + +/* Channel Chroma (V/Cr) Output Buffer 1 Address */ +#define CHNL_OUT_BUF1_ADDR_V 0x0078 + +/* Channel Output Buffer Pitch */ +#define CHNL_OUT_BUF_PITCH 0x007c +#define CHNL_OUT_BUF_PITCH_LINE_PITCH(n) ((n) << 0) +#define CHNL_OUT_BUF_PITCH_LINE_PITCH_MASK GENMASK(15, 0) + +/* Channel Input Buffer Address */ +#define CHNL_IN_BUF_ADDR 0x0080 + +/* Channel Input Buffer Pitch */ +#define CHNL_IN_BUF_PITCH 0x0084 +#define CHNL_IN_BUF_PITCH_FRM_PITCH(n) ((n) << 16) +#define CHNL_IN_BUF_PITCH_FRM_PITCH_MASK GENMASK(31, 16) +#define CHNL_IN_BUF_PITCH_LINE_PITCH(n) ((n) << 0) +#define CHNL_IN_BUF_PITCH_LINE_PITCH_MASK GENMASK(15, 0) + +/* Channel Memory Read Control */ +#define CHNL_MEM_RD_CTRL 0x0088 +#define CHNL_MEM_RD_CTRL_IMG_TYPE(n) ((n) << 28) +#define CHNL_MEM_RD_CTRL_IMG_TYPE_MASK GENMASK(31, 28) +#define CHNL_MEM_RD_CTRL_IMG_TYPE_BGR8P 0x00 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_RGB8P 0x01 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8 0x02 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_RGBX8 0x03 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8 0x04 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_RGB565 0x05 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_A2BGR10 0x06 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_A2RGB10 0x07 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV444_1P8P 0x08 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV444_1P10 0x09 +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV444_1P10P 0x0a +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV444_1P12 0x0b +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV444_1P8 0x0c +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV422_1P8P 0x0d +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV422_1P10 0x0e +#define CHNL_MEM_RD_CTRL_IMG_TYPE_YUV422_1P12 0x0f +#define CHNL_MEM_RD_CTRL_READ_MEM BIT(0) + +/* Channel RGB or Luma (Y) Output Buffer 2 Address */ +#define CHNL_OUT_BUF2_ADDR_Y 0x008c + +/* Channel Chroma (U/Cb/UV/CbCr) Output Buffer 2 Address */ +#define CHNL_OUT_BUF2_ADDR_U 0x0090 + +/* Channel Chroma (V/Cr) Output Buffer 2 Address */ +#define CHNL_OUT_BUF2_ADDR_V 0x0094 + +/* Channel scale image config */ +#define CHNL_SCL_IMG_CFG 0x0098 +#define CHNL_SCL_IMG_CFG_HEIGHT(n) ((n) << 16) +#define CHNL_SCL_IMG_CFG_HEIGHT_MASK GENMASK(28, 16) +#define CHNL_SCL_IMG_CFG_WIDTH(n) ((n) << 0) +#define CHNL_SCL_IMG_CFG_WIDTH_MASK GENMASK(12, 0) + +/* Channel Flow Control Register */ +#define CHNL_FLOW_CTRL 0x009c +#define CHNL_FLOW_CTRL_FC_DENOM_MASK GENMASK(7, 0) +#define CHNL_FLOW_CTRL_FC_DENOM(n) ((n) << 0) +#define CHNL_FLOW_CTRL_FC_NUMER_MASK GENMASK(23, 16) +#define CHNL_FLOW_CTRL_FC_NUMER(n) ((n) << 0) + +/* Channel Output Y-Buffer 1 Extended Address Bits */ +#define CHNL_Y_BUF1_XTND_ADDR 0x00a0 + +/* Channel Output U-Buffer 1 Extended Address Bits */ +#define CHNL_U_BUF1_XTND_ADDR 0x00a4 + +/* Channel Output V-Buffer 1 Extended Address Bits */ +#define CHNL_V_BUF1_XTND_ADDR 0x00a8 + +/* Channel Output Y-Buffer 2 Extended Address Bits */ +#define CHNL_Y_BUF2_XTND_ADDR 0x00ac + +/* Channel Output U-Buffer 2 Extended Address Bits */ +#define CHNL_U_BUF2_XTND_ADDR 0x00b0 + +/* Channel Output V-Buffer 2 Extended Address Bits */ +#define CHNL_V_BUF2_XTND_ADDR 0x00b4 + +/* Channel Input Buffer Extended Address Bits */ +#define CHNL_IN_BUF_XTND_ADDR 0x00b8 + +#endif /* __IMX8_ISI_REGS_H__ */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c new file mode 100644 index 000000000000..10840c9a0912 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -0,0 +1,1512 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform + * + * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which + * used to process image from camera sensor to memory or DC + * + * Copyright (c) 2019 NXP Semiconductor + */ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/media-bus-format.h> +#include <linux/minmax.h> +#include <linux/pm_runtime.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> + +#include "imx8-isi-core.h" +#include "imx8-isi-regs.h" + +/* Keep the first entry matching MXC_ISI_DEF_PIXEL_FORMAT */ +static const struct mxc_isi_format_info mxc_isi_formats[] = { + /* YUV formats */ + { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_YUYV, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_YUV422_1P8P, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV422_1P8P, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_YUVA32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV444_1P8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_NV12, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV420_2P8P, + .color_planes = 2, + .mem_planes = 1, + .depth = { 8, 16 }, + .hsub = 2, + .vsub = 2, + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_NV12M, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV420_2P8P, + .mem_planes = 2, + .color_planes = 2, + .depth = { 8, 16 }, + .hsub = 2, + .vsub = 2, + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_NV16, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV422_2P8P, + .color_planes = 2, + .mem_planes = 1, + .depth = { 8, 16 }, + .hsub = 2, + .vsub = 1, + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_NV16M, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV422_2P8P, + .mem_planes = 2, + .color_planes = 2, + .depth = { 8, 16 }, + .hsub = 2, + .vsub = 1, + .encoding = MXC_ISI_ENC_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, + .fourcc = V4L2_PIX_FMT_YUV444M, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV444_3P8P, + .mem_planes = 3, + .color_planes = 3, + .depth = { 8, 8, 8 }, + .hsub = 1, + .vsub = 1, + .encoding = MXC_ISI_ENC_YUV, + }, + /* RGB formats */ + { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_RGB565, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGB565, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGB565, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_RGB24, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_BGR8P, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_BGR888P, + .mem_planes = 1, + .color_planes = 1, + .depth = { 24 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_BGR24, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGB8P, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGB888P, + .mem_planes = 1, + .color_planes = 1, + .depth = { 24 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_XBGR32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_ABGR32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_ARGB8888, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, + }, + /* + * RAW formats + * + * The ISI shifts the 10-bit and 12-bit formats left by 6 and 4 bits + * when using CHNL_IMG_CTRL_FORMAT_RAW10 or MXC_ISI_OUT_FMT_RAW12 + * respectively, to align the bits to the left and pad with zeros in + * the LSBs. The corresponding V4L2 formats are however right-aligned, + * we have to use CHNL_IMG_CTRL_FORMAT_RAW16 to avoid the left shift. + */ + { + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .fourcc = V4L2_PIX_FMT_GREY, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 8 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .fourcc = V4L2_PIX_FMT_Y10, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y12_1X12, + .fourcc = V4L2_PIX_FMT_Y12, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y14_1X14, + .fourcc = V4L2_PIX_FMT_Y14, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 8 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .fourcc = V4L2_PIX_FMT_SGBRG8, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 8 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fourcc = V4L2_PIX_FMT_SGRBG8, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 8 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .fourcc = V4L2_PIX_FMT_SRGGB8, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 8 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fourcc = V4L2_PIX_FMT_SBGGR10, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fourcc = V4L2_PIX_FMT_SGBRG10, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fourcc = V4L2_PIX_FMT_SGRBG10, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fourcc = V4L2_PIX_FMT_SRGGB10, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fourcc = V4L2_PIX_FMT_SBGGR12, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fourcc = V4L2_PIX_FMT_SGBRG12, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fourcc = V4L2_PIX_FMT_SGRBG12, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fourcc = V4L2_PIX_FMT_SRGGB12, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14, + .fourcc = V4L2_PIX_FMT_SBGGR14, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG14_1X14, + .fourcc = V4L2_PIX_FMT_SGBRG14, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG14_1X14, + .fourcc = V4L2_PIX_FMT_SGRBG14, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB14_1X14, + .fourcc = V4L2_PIX_FMT_SRGGB14, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, + /* JPEG */ + { + .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, + .fourcc = V4L2_PIX_FMT_MJPEG, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8, + .mem_planes = 1, + .color_planes = 1, + .depth = { 8 }, + .encoding = MXC_ISI_ENC_RAW, + } +}; + +const struct mxc_isi_format_info * +mxc_isi_format_by_fourcc(u32 fourcc, enum mxc_isi_video_type type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) { + const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i]; + + if (fmt->fourcc == fourcc && fmt->type & type) + return fmt; + } + + return NULL; +} + +const struct mxc_isi_format_info * +mxc_isi_format_enum(unsigned int index, enum mxc_isi_video_type type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) { + const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i]; + + if (!(fmt->type & type)) + continue; + + if (!index) + return fmt; + + index--; + } + + return NULL; +} + +const struct mxc_isi_format_info * +mxc_isi_format_try(struct mxc_isi_pipe *pipe, struct v4l2_pix_format_mplane *pix, + enum mxc_isi_video_type type) +{ + const struct mxc_isi_format_info *fmt; + unsigned int max_width; + unsigned int i; + + max_width = pipe->id == pipe->isi->pdata->num_channels - 1 + ? MXC_ISI_MAX_WIDTH_UNCHAINED + : MXC_ISI_MAX_WIDTH_CHAINED; + + fmt = mxc_isi_format_by_fourcc(pix->pixelformat, type); + if (!fmt) + fmt = &mxc_isi_formats[0]; + + pix->width = clamp(pix->width, MXC_ISI_MIN_WIDTH, max_width); + pix->height = clamp(pix->height, MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT); + pix->pixelformat = fmt->fourcc; + pix->field = V4L2_FIELD_NONE; + + if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) { + pix->colorspace = MXC_ISI_DEF_COLOR_SPACE; + pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC; + pix->quantization = MXC_ISI_DEF_QUANTIZATION; + pix->xfer_func = MXC_ISI_DEF_XFER_FUNC; + } + + if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + if (pix->quantization == V4L2_QUANTIZATION_DEFAULT) { + bool is_rgb = fmt->encoding == MXC_ISI_ENC_RGB; + + pix->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, pix->colorspace, + pix->ycbcr_enc); + } + if (pix->xfer_func == V4L2_XFER_FUNC_DEFAULT) + pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); + + pix->num_planes = fmt->mem_planes; + + for (i = 0; i < fmt->color_planes; ++i) { + struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i]; + unsigned int bpl; + + /* The pitch must be identical for all planes. */ + if (i == 0) + bpl = clamp(plane->bytesperline, + pix->width * fmt->depth[0] / 8, + 65535U); + else + bpl = pix->plane_fmt[0].bytesperline; + + plane->bytesperline = bpl; + + plane->sizeimage = plane->bytesperline * pix->height; + if (i >= 1) + plane->sizeimage /= fmt->vsub; + } + + /* + * For single-planar pixel formats with multiple color planes, + * concatenate the size of all planes and clear all planes but the + * first one. + */ + if (fmt->color_planes != fmt->mem_planes) { + for (i = 1; i < fmt->color_planes; ++i) { + struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i]; + + pix->plane_fmt[0].sizeimage += plane->sizeimage; + plane->bytesperline = 0; + plane->sizeimage = 0; + } + } + + return fmt; +} + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations + */ + +static void mxc_isi_video_frame_write_done(struct mxc_isi_pipe *pipe, + u32 status) +{ + struct mxc_isi_video *video = &pipe->video; + struct device *dev = pipe->isi->dev; + struct mxc_isi_buffer *next_buf; + struct mxc_isi_buffer *buf; + enum mxc_isi_buf_id buf_id; + + spin_lock(&video->buf_lock); + + /* + * The ISI hardware handles buffers using a ping-pong mechanism with + * two sets of destination addresses (with shadow registers to allow + * programming addresses for all planes atomically) named BUF1 and + * BUF2. Addresses can be loaded and copied to shadow registers at any + * at any time. + * + * The hardware keeps track of which buffer is being written to and + * automatically switches to the other buffer at frame end, copying the + * corresponding address to another set of shadow registers that track + * the address being written to. The active buffer tracking bits are + * accessible through the CHNL_STS register. + * + * BUF1 BUF2 | Event | Action + * | | + * | | Program initial buffers + * | | B0 in BUF1, B1 in BUF2 + * | Start ISI | + * +----+ | | + * | B0 | | | + * +----+ | | + * +----+ | FRM IRQ 0 | B0 complete, BUF2 now active + * | B1 | | | Program B2 in BUF1 + * +----+ | | + * +----+ | FRM IRQ 1 | B1 complete, BUF1 now active + * | B2 | | | Program B3 in BUF2 + * +----+ | | + * +----+ | FRM IRQ 2 | B2 complete, BUF2 now active + * | B3 | | | Program B4 in BUF1 + * +----+ | | + * +----+ | FRM IRQ 3 | B3 complete, BUF1 now active + * | B4 | | | Program B5 in BUF2 + * +----+ | | + * ... | | + * + * Races between address programming and buffer switching can be + * detected by checking if a frame end interrupt occurred after + * programming the addresses. + * + * As none of the shadow registers are accessible, races can occur + * between address programming and buffer switching. It is possible to + * detect the race condition by checking if a frame end interrupt + * occurred after programming the addresses, but impossible to + * determine if the race has been won or lost. + * + * In addition to this, we need to use discard buffers if no pending + * buffers are available. To simplify handling of discard buffer, we + * need to allocate three of them, as two can be active concurrently + * and we need to still be able to get hold of a next buffer. The logic + * could be improved to use two buffers only, but as all discard + * buffers share the same memory, an additional buffer is cheap. + */ + + /* Check which buffer has just completed. */ + buf_id = pipe->isi->pdata->buf_active_reverse + ? (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF2 : MXC_ISI_BUF1) + : (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF1 : MXC_ISI_BUF2); + + buf = list_first_entry_or_null(&video->out_active, + struct mxc_isi_buffer, list); + + /* Safety check, this should really never happen. */ + if (!buf) { + dev_warn(dev, "trying to access empty active list\n"); + goto done; + } + + /* + * If the buffer that has completed doesn't match the buffer on the + * front of the active list, it means we have lost one frame end + * interrupt (or possibly a large odd number of interrupts, although + * quite unlikely). + * + * For instance, if IRQ1 is lost and we handle IRQ2, both B1 and B2 + * have been completed, but B3 hasn't been programmed, BUF2 still + * addresses B1 and the ISI is now writing in B1 instead of B3. We + * can't complete B2 as that would result in out-of-order completion. + * + * The only option is to ignore this interrupt and try again. When IRQ3 + * will be handled, we will complete B1 and be in sync again. + */ + if (buf->id != buf_id) { + dev_dbg(dev, "buffer ID mismatch (expected %u, got %u), skipping\n", + buf->id, buf_id); + + /* + * Increment the frame count by two to account for the missed + * and the ignored interrupts. + */ + video->frame_count += 2; + goto done; + } + + /* Pick the next buffer and queue it to the hardware. */ + next_buf = list_first_entry_or_null(&video->out_pending, + struct mxc_isi_buffer, list); + if (!next_buf) { + next_buf = list_first_entry_or_null(&video->out_discard, + struct mxc_isi_buffer, list); + + /* Safety check, this should never happen. */ + if (!next_buf) { + dev_warn(dev, "trying to access empty discard list\n"); + goto done; + } + } + + mxc_isi_channel_set_outbuf(pipe, next_buf->dma_addrs, buf_id); + next_buf->id = buf_id; + + /* + * Check if we have raced with the end of frame interrupt. If so, we + * can't tell if the ISI has recorded the new address, or is still + * using the previous buffer. We must assume the latter as that is the + * worst case. + * + * For instance, if we are handling IRQ1 and now detect the FRM + * interrupt, assume B2 has completed and the ISI has switched to BUF2 + * using B1 just before we programmed B3. Unlike in the previous race + * condition, B3 has been programmed and will be written to the next + * time the ISI switches to BUF2. We can however handle this exactly as + * the first race condition, as we'll program B3 (still at the head of + * the pending list) when handling IRQ3. + */ + status = mxc_isi_channel_irq_status(pipe, false); + if (status & CHNL_STS_FRM_STRD) { + dev_dbg(dev, "raced with frame end interrupt\n"); + video->frame_count += 2; + goto done; + } + + /* + * The next buffer has been queued successfully, move it to the active + * list, and complete the current buffer. + */ + list_move_tail(&next_buf->list, &video->out_active); + + if (!buf->discard) { + list_del_init(&buf->list); + buf->v4l2_buf.sequence = video->frame_count; + buf->v4l2_buf.vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&buf->v4l2_buf.vb2_buf, VB2_BUF_STATE_DONE); + } else { + list_move_tail(&buf->list, &video->out_discard); + } + + video->frame_count++; + +done: + spin_unlock(&video->buf_lock); +} + +static void mxc_isi_video_free_discard_buffers(struct mxc_isi_video *video) +{ + unsigned int i; + + for (i = 0; i < video->pix.num_planes; i++) { + struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i]; + + if (!buf->addr) + continue; + + dma_free_coherent(video->pipe->isi->dev, buf->size, buf->addr, + buf->dma); + buf->addr = NULL; + } +} + +static int mxc_isi_video_alloc_discard_buffers(struct mxc_isi_video *video) +{ + unsigned int i, j; + + /* Allocate memory for each plane. */ + for (i = 0; i < video->pix.num_planes; i++) { + struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i]; + + buf->size = PAGE_ALIGN(video->pix.plane_fmt[i].sizeimage); + buf->addr = dma_alloc_coherent(video->pipe->isi->dev, buf->size, + &buf->dma, GFP_DMA | GFP_KERNEL); + if (!buf->addr) { + mxc_isi_video_free_discard_buffers(video); + return -ENOMEM; + } + + dev_dbg(video->pipe->isi->dev, + "discard buffer plane %u: %zu bytes @%pad (CPU address %p)\n", + i, buf->size, &buf->dma, buf->addr); + } + + /* Fill the DMA addresses in the discard buffers. */ + for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) { + struct mxc_isi_buffer *buf = &video->buf_discard[i]; + + buf->discard = true; + + for (j = 0; j < video->pix.num_planes; ++j) + buf->dma_addrs[j] = video->discard_buffer[j].dma; + } + + return 0; +} + +static int mxc_isi_video_validate_format(struct mxc_isi_video *video) +{ + const struct v4l2_mbus_framefmt *format; + const struct mxc_isi_format_info *info; + struct v4l2_subdev_state *state; + struct v4l2_subdev *sd = &video->pipe->sd; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + info = mxc_isi_format_by_fourcc(video->pix.pixelformat, + MXC_ISI_VIDEO_CAP); + format = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + + if (format->code != info->mbus_code || + format->width != video->pix.width || + format->height != video->pix.height) { + dev_dbg(video->pipe->isi->dev, + "%s: configuration mismatch, 0x%04x/%ux%u != 0x%04x/%ux%u\n", + __func__, format->code, format->width, format->height, + info->mbus_code, video->pix.width, video->pix.height); + ret = -EINVAL; + } + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static void mxc_isi_video_return_buffers(struct mxc_isi_video *video, + enum vb2_buffer_state state) +{ + struct mxc_isi_buffer *buf; + + spin_lock_irq(&video->buf_lock); + + while (!list_empty(&video->out_active)) { + buf = list_first_entry(&video->out_active, + struct mxc_isi_buffer, list); + list_del_init(&buf->list); + if (buf->discard) + continue; + + vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state); + } + + while (!list_empty(&video->out_pending)) { + buf = list_first_entry(&video->out_pending, + struct mxc_isi_buffer, list); + list_del_init(&buf->list); + vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state); + } + + while (!list_empty(&video->out_discard)) { + buf = list_first_entry(&video->out_discard, + struct mxc_isi_buffer, list); + list_del_init(&buf->list); + } + + INIT_LIST_HEAD(&video->out_active); + INIT_LIST_HEAD(&video->out_pending); + INIT_LIST_HEAD(&video->out_discard); + + spin_unlock_irq(&video->buf_lock); +} + +static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video) +{ + unsigned int discard; + unsigned int i; + + lockdep_assert_held(&video->buf_lock); + + /* + * Queue two ISI channel output buffers. We are not guaranteed to have + * any buffer in the pending list when this function is called from the + * system resume handler. Use pending buffers as much as possible, and + * use discard buffers to fill the remaining slots. + */ + + /* How many discard buffers do we need to queue first ? */ + discard = list_empty(&video->out_pending) ? 2 + : list_is_singular(&video->out_pending) ? 1 + : 0; + + for (i = 0; i < 2; ++i) { + enum mxc_isi_buf_id buf_id = i == 0 ? MXC_ISI_BUF1 + : MXC_ISI_BUF2; + struct mxc_isi_buffer *buf; + struct list_head *list; + + list = i < discard ? &video->out_discard : &video->out_pending; + buf = list_first_entry(list, struct mxc_isi_buffer, list); + + mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id); + buf->id = buf_id; + list_move_tail(&buf->list, &video->out_active); + } +} + +static inline struct mxc_isi_buffer *to_isi_buffer(struct vb2_v4l2_buffer *v4l2_buf) +{ + return container_of(v4l2_buf, struct mxc_isi_buffer, v4l2_buf); +} + +int mxc_isi_video_queue_setup(const struct v4l2_pix_format_mplane *format, + const struct mxc_isi_format_info *info, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[]) +{ + unsigned int i; + + if (*num_planes) { + if (*num_planes != info->mem_planes) + return -EINVAL; + + for (i = 0; i < info->mem_planes; ++i) { + if (sizes[i] < format->plane_fmt[i].sizeimage) + return -EINVAL; + } + + return 0; + } + + *num_planes = info->mem_planes; + + for (i = 0; i < info->mem_planes; ++i) + sizes[i] = format->plane_fmt[i].sizeimage; + + return 0; +} + +void mxc_isi_video_buffer_init(struct vb2_buffer *vb2, dma_addr_t dma_addrs[3], + const struct mxc_isi_format_info *info, + const struct v4l2_pix_format_mplane *pix) +{ + unsigned int i; + + for (i = 0; i < info->mem_planes; ++i) + dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb2, i); + + /* + * For single-planar pixel formats with multiple color planes, split + * the buffer into color planes. + */ + if (info->color_planes != info->mem_planes) { + unsigned int size = pix->plane_fmt[0].bytesperline * pix->height; + + for (i = 1; i < info->color_planes; ++i) { + unsigned int vsub = i > 1 ? info->vsub : 1; + + dma_addrs[i] = dma_addrs[i - 1] + size / vsub; + } + } +} + +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) +{ + unsigned int i; + + for (i = 0; i < info->mem_planes; i++) { + unsigned long size = pix->plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb2, i) < size) { + dev_err(isi->dev, "User buffer too small (%ld < %ld)\n", + vb2_plane_size(vb2, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb2, i, size); + } + + return 0; +} + +static int mxc_isi_vb2_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mxc_isi_video *video = vb2_get_drv_priv(q); + + return mxc_isi_video_queue_setup(&video->pix, video->fmtinfo, + num_buffers, num_planes, sizes); +} + +static int mxc_isi_vb2_buffer_init(struct vb2_buffer *vb2) +{ + struct mxc_isi_buffer *buf = to_isi_buffer(to_vb2_v4l2_buffer(vb2)); + struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue); + + mxc_isi_video_buffer_init(vb2, buf->dma_addrs, video->fmtinfo, + &video->pix); + + return 0; +} + +static int mxc_isi_vb2_buffer_prepare(struct vb2_buffer *vb2) +{ + struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue); + + return mxc_isi_video_buffer_prepare(video->pipe->isi, vb2, + video->fmtinfo, &video->pix); +} + +static void mxc_isi_vb2_buffer_queue(struct vb2_buffer *vb2) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2); + struct mxc_isi_buffer *buf = to_isi_buffer(v4l2_buf); + struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue); + + spin_lock_irq(&video->buf_lock); + list_add_tail(&buf->list, &video->out_pending); + spin_unlock_irq(&video->buf_lock); +} + +static void mxc_isi_video_init_channel(struct mxc_isi_video *video) +{ + struct mxc_isi_pipe *pipe = video->pipe; + + mxc_isi_channel_get(pipe); + + mutex_lock(video->ctrls.handler.lock); + mxc_isi_channel_set_alpha(pipe, video->ctrls.alpha); + mxc_isi_channel_set_flip(pipe, video->ctrls.hflip, video->ctrls.vflip); + mutex_unlock(video->ctrls.handler.lock); + + mxc_isi_channel_set_output_format(pipe, video->fmtinfo, &video->pix); +} + +static int mxc_isi_vb2_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mxc_isi_video *video = vb2_get_drv_priv(q); + unsigned int i; + int ret; + + /* Initialize the ISI channel. */ + mxc_isi_video_init_channel(video); + + spin_lock_irq(&video->buf_lock); + + /* Add the discard buffers to the out_discard list. */ + for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) { + struct mxc_isi_buffer *buf = &video->buf_discard[i]; + + list_add_tail(&buf->list, &video->out_discard); + } + + /* Queue the first buffers. */ + mxc_isi_video_queue_first_buffers(video); + + /* Clear frame count */ + video->frame_count = 0; + + spin_unlock_irq(&video->buf_lock); + + ret = mxc_isi_pipe_enable(video->pipe); + if (ret) + goto error; + + return 0; + +error: + mxc_isi_channel_put(video->pipe); + mxc_isi_video_return_buffers(video, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void mxc_isi_vb2_stop_streaming(struct vb2_queue *q) +{ + struct mxc_isi_video *video = vb2_get_drv_priv(q); + + mxc_isi_pipe_disable(video->pipe); + mxc_isi_channel_put(video->pipe); + + mxc_isi_video_return_buffers(video, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops mxc_isi_vb2_qops = { + .queue_setup = mxc_isi_vb2_queue_setup, + .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, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 controls + */ + +static inline struct mxc_isi_video *ctrl_to_isi_video(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mxc_isi_video, ctrls.handler); +} + +static int mxc_isi_video_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mxc_isi_video *video = ctrl_to_isi_video(ctrl); + + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + video->ctrls.alpha = ctrl->val; + break; + case V4L2_CID_VFLIP: + video->ctrls.vflip = ctrl->val; + break; + case V4L2_CID_HFLIP: + video->ctrls.hflip = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mxc_isi_video_ctrl_ops = { + .s_ctrl = mxc_isi_video_s_ctrl, +}; + +static int mxc_isi_video_ctrls_create(struct mxc_isi_video *video) +{ + struct v4l2_ctrl_handler *handler = &video->ctrls.handler; + int ret; + + v4l2_ctrl_handler_init(handler, 3); + + v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); + + v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + v4l2_ctrl_handler_free(handler); + return ret; + } + + video->vdev.ctrl_handler = handler; + + return 0; +} + +static void mxc_isi_video_ctrls_delete(struct mxc_isi_video *video) +{ + v4l2_ctrl_handler_free(&video->ctrls.handler); +} + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int mxc_isi_video_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, MXC_ISI_CAPTURE, sizeof(cap->card)); + + return 0; +} + +static int mxc_isi_video_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct mxc_isi_format_info *fmt; + unsigned int index = f->index; + unsigned int i; + + if (f->mbus_code) { + /* + * If a media bus code is specified, only enumerate formats + * compatible with it. + */ + for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) { + fmt = &mxc_isi_formats[i]; + if (fmt->mbus_code != f->mbus_code) + continue; + + if (index == 0) + break; + + index--; + } + + if (i == ARRAY_SIZE(mxc_isi_formats)) + return -EINVAL; + } else { + /* Otherwise, enumerate all formatS. */ + if (f->index >= ARRAY_SIZE(mxc_isi_formats)) + return -EINVAL; + + fmt = &mxc_isi_formats[f->index]; + } + + f->pixelformat = fmt->fourcc; + f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC + | V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC; + + return 0; +} + +static int mxc_isi_video_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_isi_video *video = video_drvdata(file); + + f->fmt.pix_mp = video->pix; + + return 0; +} + +static int mxc_isi_video_try_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_isi_video *video = video_drvdata(file); + + mxc_isi_format_try(video->pipe, &f->fmt.pix_mp, MXC_ISI_VIDEO_CAP); + return 0; +} + +static int mxc_isi_video_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mxc_isi_video *video = video_drvdata(file); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + + if (vb2_is_busy(&video->vb2_q)) + return -EBUSY; + + video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP); + video->pix = *pix; + + return 0; +} + +static int mxc_isi_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct mxc_isi_video *video = video_drvdata(file); + struct media_device *mdev = &video->pipe->isi->media_dev; + struct media_pipeline *pipe; + int ret; + + if (vb2_queue_is_busy(&video->vb2_q, file)) + return -EBUSY; + + /* + * Get a pipeline for the video node and start it. This must be done + * here and not in the queue .start_streaming() handler, so that + * pipeline start errors can be reported from VIDIOC_STREAMON and not + * delayed until subsequent VIDIOC_QBUF calls. + */ + mutex_lock(&mdev->graph_mutex); + + ret = mxc_isi_pipe_acquire(video->pipe, &mxc_isi_video_frame_write_done); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + + pipe = media_entity_pipeline(&video->vdev.entity) ? : &video->pipe->pipe; + + ret = __video_device_pipeline_start(&video->vdev, pipe); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + goto err_release; + } + + mutex_unlock(&mdev->graph_mutex); + + /* Verify that the video format matches the output of the subdev. */ + ret = mxc_isi_video_validate_format(video); + if (ret) + goto err_stop; + + /* Allocate buffers for discard operation. */ + ret = mxc_isi_video_alloc_discard_buffers(video); + if (ret) + goto err_stop; + + ret = vb2_streamon(&video->vb2_q, type); + if (ret) + goto err_free; + + video->is_streaming = true; + + return 0; + +err_free: + mxc_isi_video_free_discard_buffers(video); +err_stop: + video_device_pipeline_stop(&video->vdev); +err_release: + mxc_isi_pipe_release(video->pipe); + return ret; +} + +static void mxc_isi_video_cleanup_streaming(struct mxc_isi_video *video) +{ + lockdep_assert_held(&video->lock); + + if (!video->is_streaming) + return; + + mxc_isi_video_free_discard_buffers(video); + video_device_pipeline_stop(&video->vdev); + mxc_isi_pipe_release(video->pipe); + + video->is_streaming = false; +} + +static int mxc_isi_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct mxc_isi_video *video = video_drvdata(file); + int ret; + + ret = vb2_ioctl_streamoff(file, priv, type); + if (ret) + return ret; + + mxc_isi_video_cleanup_streaming(video); + + return 0; +} + +static int mxc_isi_video_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct mxc_isi_video *video = video_drvdata(file); + const struct mxc_isi_format_info *info; + unsigned int max_width; + unsigned int h_align; + unsigned int v_align; + + if (fsize->index) + return -EINVAL; + + info = mxc_isi_format_by_fourcc(fsize->pixel_format, MXC_ISI_VIDEO_CAP); + if (!info) + return -EINVAL; + + h_align = max_t(unsigned int, info->hsub, 1); + v_align = max_t(unsigned int, info->vsub, 1); + + max_width = video->pipe->id == video->pipe->isi->pdata->num_channels - 1 + ? MXC_ISI_MAX_WIDTH_UNCHAINED + : MXC_ISI_MAX_WIDTH_CHAINED; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = ALIGN(MXC_ISI_MIN_WIDTH, h_align); + fsize->stepwise.min_height = ALIGN(MXC_ISI_MIN_HEIGHT, v_align); + fsize->stepwise.max_width = ALIGN_DOWN(max_width, h_align); + fsize->stepwise.max_height = ALIGN_DOWN(MXC_ISI_MAX_HEIGHT, v_align); + fsize->stepwise.step_width = h_align; + fsize->stepwise.step_height = v_align; + + /* + * The width can be further restricted due to line buffer sharing + * between pipelines when scaling, but we have no way to know here if + * the scaler will be used. + */ + + return 0; +} + +static const struct v4l2_ioctl_ops mxc_isi_video_ioctl_ops = { + .vidioc_querycap = mxc_isi_video_querycap, + + .vidioc_enum_fmt_vid_cap = mxc_isi_video_enum_fmt, + .vidioc_try_fmt_vid_cap_mplane = mxc_isi_video_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = mxc_isi_video_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = mxc_isi_video_g_fmt, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + + .vidioc_streamon = mxc_isi_video_streamon, + .vidioc_streamoff = mxc_isi_video_streamoff, + + .vidioc_enum_framesizes = mxc_isi_video_enum_framesizes, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- + * Video device file operations + */ + +static int mxc_isi_video_open(struct file *file) +{ + struct mxc_isi_video *video = video_drvdata(file); + int ret; + + ret = v4l2_fh_open(file); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(video->pipe->isi->dev); + if (ret) { + v4l2_fh_release(file); + return ret; + } + + return 0; +} + +static int mxc_isi_video_release(struct file *file) +{ + struct mxc_isi_video *video = video_drvdata(file); + int ret; + + ret = vb2_fop_release(file); + if (ret) + dev_err(video->pipe->isi->dev, "%s fail\n", __func__); + + mutex_lock(&video->lock); + mxc_isi_video_cleanup_streaming(video); + mutex_unlock(&video->lock); + + pm_runtime_put(video->pipe->isi->dev); + return ret; +} + +static const struct v4l2_file_operations mxc_isi_video_fops = { + .owner = THIS_MODULE, + .open = mxc_isi_video_open, + .release = mxc_isi_video_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +/* ----------------------------------------------------------------------------- + * Suspend & resume + */ + +void mxc_isi_video_suspend(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_video *video = &pipe->video; + + if (!video->is_streaming) + return; + + mxc_isi_pipe_disable(pipe); + mxc_isi_channel_put(pipe); + + spin_lock_irq(&video->buf_lock); + + /* + * Move the active buffers back to the pending or discard list. We must + * iterate the active list backward and move the buffers to the head of + * the pending list to preserve the buffer queueing order. + */ + while (!list_empty(&video->out_active)) { + struct mxc_isi_buffer *buf = + list_last_entry(&video->out_active, + struct mxc_isi_buffer, list); + + if (buf->discard) + list_move(&buf->list, &video->out_discard); + else + list_move(&buf->list, &video->out_pending); + } + + spin_unlock_irq(&video->buf_lock); +} + +int mxc_isi_video_resume(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_video *video = &pipe->video; + + if (!video->is_streaming) + return 0; + + mxc_isi_video_init_channel(video); + + spin_lock_irq(&video->buf_lock); + mxc_isi_video_queue_first_buffers(video); + spin_unlock_irq(&video->buf_lock); + + return mxc_isi_pipe_enable(pipe); +} + +/* ----------------------------------------------------------------------------- + * Registration + */ + +int mxc_isi_video_register(struct mxc_isi_pipe *pipe, + struct v4l2_device *v4l2_dev) +{ + struct mxc_isi_video *video = &pipe->video; + struct v4l2_pix_format_mplane *pix = &video->pix; + struct video_device *vdev = &video->vdev; + struct vb2_queue *q = &video->vb2_q; + int ret = -ENOMEM; + + video->pipe = pipe; + + mutex_init(&video->lock); + spin_lock_init(&video->buf_lock); + + pix->width = MXC_ISI_DEF_WIDTH; + pix->height = MXC_ISI_DEF_HEIGHT; + pix->pixelformat = MXC_ISI_DEF_PIXEL_FORMAT; + pix->colorspace = MXC_ISI_DEF_COLOR_SPACE; + pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC; + pix->quantization = MXC_ISI_DEF_QUANTIZATION; + pix->xfer_func = MXC_ISI_DEF_XFER_FUNC; + video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP); + + memset(vdev, 0, sizeof(*vdev)); + snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.%d.capture", pipe->id); + + vdev->fops = &mxc_isi_video_fops; + vdev->ioctl_ops = &mxc_isi_video_ioctl_ops; + vdev->v4l2_dev = v4l2_dev; + vdev->minor = -1; + vdev->release = video_device_release_empty; + vdev->queue = q; + vdev->lock = &video->lock; + + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_IO_MC; + video_set_drvdata(vdev, video); + + INIT_LIST_HEAD(&video->out_pending); + INIT_LIST_HEAD(&video->out_active); + INIT_LIST_HEAD(&video->out_discard); + + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = video; + q->ops = &mxc_isi_vb2_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct mxc_isi_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &video->lock; + q->dev = pipe->isi->dev; + + ret = vb2_queue_init(q); + if (ret) + goto err_free_ctx; + + video->pad.flags = MEDIA_PAD_FL_SINK; + vdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + ret = media_entity_pads_init(&vdev->entity, 1, &video->pad); + if (ret) + goto err_free_ctx; + + ret = mxc_isi_video_ctrls_create(video); + if (ret) + goto err_me_cleanup; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + goto err_ctrl_free; + + ret = media_create_pad_link(&pipe->sd.entity, + MXC_ISI_PIPE_PAD_SOURCE, + &vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_video_unreg; + + return 0; + +err_video_unreg: + video_unregister_device(vdev); +err_ctrl_free: + mxc_isi_video_ctrls_delete(video); +err_me_cleanup: + media_entity_cleanup(&vdev->entity); +err_free_ctx: + return ret; +} + +void mxc_isi_video_unregister(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_video *video = &pipe->video; + struct video_device *vdev = &video->vdev; + + mutex_lock(&video->lock); + + if (video_is_registered(vdev)) { + video_unregister_device(vdev); + mxc_isi_video_ctrls_delete(video); + media_entity_cleanup(&vdev->entity); + } + + mutex_unlock(&video->lock); +} diff --git a/drivers/media/platform/nxp/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c index 3ce84d0f078c..023ed40c6b08 100644 --- a/drivers/media/platform/nxp/mx2_emmaprp.c +++ b/drivers/media/platform/nxp/mx2_emmaprp.c @@ -888,7 +888,7 @@ unreg_dev: return ret; } -static int emmaprp_remove(struct platform_device *pdev) +static void emmaprp_remove(struct platform_device *pdev) { struct emmaprp_dev *pcdev = platform_get_drvdata(pdev); @@ -898,13 +898,11 @@ static int emmaprp_remove(struct platform_device *pdev) v4l2_m2m_release(pcdev->m2m_dev); v4l2_device_unregister(&pcdev->v4l2_dev); mutex_destroy(&pcdev->dev_mutex); - - return 0; } static struct platform_driver emmaprp_pdrv = { .probe = emmaprp_probe, - .remove = emmaprp_remove, + .remove_new = emmaprp_remove, .driver = { .name = MEM2MEM_NAME, }, diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 2031bde13a93..0f8ac29d038d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -334,13 +334,14 @@ static const struct csid_format csid_formats[] = { }, }; -static void csid_configure_stream(struct csid_device *csid, u8 enable) +static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) { struct csid_testgen_config *tg = &csid->testgen; u32 val; u32 phy_sel = 0; u8 lane_cnt = csid->phy.lane_cnt; - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_SRC]; + /* 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); @@ -351,8 +352,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) phy_sel = csid->phy.csiphy_id; if (enable) { - u8 vc = 0; /* Virtual Channel 0 */ - u8 dt_id = vc * 4; + u8 dt_id = vc; if (tg->enabled) { /* Config Test Generator */ @@ -395,42 +395,42 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) 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(0)); + 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(0)); + writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(0)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(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(0)); + 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(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(0)); + 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(0)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(0)); + writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(0)); + val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); val |= 1 << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(0)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); } if (tg->enabled) { @@ -456,7 +456,16 @@ static void csid_configure_stream(struct csid_device *csid, u8 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(0)); + writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); +} + +static void csid_configure_stream(struct csid_device *csid, u8 enable) +{ + 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); } static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) @@ -502,6 +511,7 @@ static irqreturn_t csid_isr(int irq, void *dev) struct csid_device *csid = dev; u32 val; u8 reset_done; + int i; val = readl_relaxed(csid->base + CSID_TOP_IRQ_STATUS); writel_relaxed(val, csid->base + CSID_TOP_IRQ_CLEAR); @@ -510,8 +520,12 @@ static irqreturn_t csid_isr(int irq, void *dev) val = readl_relaxed(csid->base + CSID_CSI2_RX_IRQ_STATUS); writel_relaxed(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR); - val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(0)); - writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(0)); + /* Read and clear IRQ status for each enabled RDI channel */ + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) + if (csid->phy.en_vc & BIT(i)) { + val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i)); + writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i)); + } val = 1 << IRQ_CMD_CLEAR; writel_relaxed(val, csid->base + CSID_IRQ_CMD); diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 88f188e0f750..6360314f04a6 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -196,6 +196,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) return ret; } + csid->phy.need_vc_update = true; + enable_irq(csid->irq); ret = csid->ops->reset(csid); @@ -249,7 +251,10 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) return -ENOLINK; } - csid->ops->configure_stream(csid, enable); + if (csid->phy.need_vc_update) { + csid->ops->configure_stream(csid, enable); + csid->phy.need_vc_update = false; + } return 0; } @@ -460,6 +465,7 @@ static int csid_set_format(struct v4l2_subdev *sd, { struct csid_device *csid = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; + int i; format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which); if (format == NULL) @@ -468,14 +474,14 @@ static int csid_set_format(struct v4l2_subdev *sd, csid_try_format(csid, sd_state, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; - /* Propagate the format from sink to source */ + /* Propagate the format from sink to source pads */ if (fmt->pad == MSM_CSID_PAD_SINK) { - format = __csid_get_format(csid, sd_state, MSM_CSID_PAD_SRC, - fmt->which); + for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) { + format = __csid_get_format(csid, sd_state, i, fmt->which); - *format = fmt->format; - csid_try_format(csid, sd_state, MSM_CSID_PAD_SRC, format, - fmt->which); + *format = fmt->format; + csid_try_format(csid, sd_state, i, format, fmt->which); + } } return 0; @@ -738,7 +744,6 @@ static int csid_link_setup(struct media_entity *entity, struct csid_device *csid; struct csiphy_device *csiphy; struct csiphy_lanes_cfg *lane_cfg; - struct v4l2_subdev_format format = { 0 }; sd = media_entity_to_v4l2_subdev(entity); csid = v4l2_get_subdevdata(sd); @@ -761,11 +766,22 @@ static int csid_link_setup(struct media_entity *entity, lane_cfg = &csiphy->cfg.csi2->lane_cfg; csid->phy.lane_cnt = lane_cfg->num_data; csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); + } + /* Decide which virtual channels to enable based on which source pads are enabled */ + if (local->flags & MEDIA_PAD_FL_SOURCE) { + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct device *dev = csid->camss->dev; + + if (flags & MEDIA_LNK_FL_ENABLED) + csid->phy.en_vc |= BIT(local->index - 1); + else + csid->phy.en_vc &= ~BIT(local->index - 1); - /* Reset format on source pad to sink pad format */ - format.pad = MSM_CSID_PAD_SRC; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - csid_set_format(&csid->subdev, NULL, &format); + csid->phy.need_vc_update = true; + + dev_dbg(dev, "%s: Enabled CSID virtual channels mask 0x%x\n", + __func__, csid->phy.en_vc); } return 0; @@ -816,6 +832,7 @@ int msm_csid_register_entity(struct csid_device *csid, struct v4l2_subdev *sd = &csid->subdev; struct media_pad *pads = csid->pads; struct device *dev = csid->camss->dev; + int i; int ret; v4l2_subdev_init(sd, &csid_v4l2_ops); @@ -852,7 +869,8 @@ int msm_csid_register_entity(struct csid_device *csid, } pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) + pads[i].flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csid_media_ops; diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index f06040e44c51..d4b48432a097 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -19,8 +19,13 @@ #include <media/v4l2-subdev.h> #define MSM_CSID_PAD_SINK 0 -#define MSM_CSID_PAD_SRC 1 -#define MSM_CSID_PADS_NUM 2 +#define MSM_CSID_PAD_FIRST_SRC 1 +#define MSM_CSID_PADS_NUM 5 + +#define MSM_CSID_PAD_SRC (MSM_CSID_PAD_FIRST_SRC) + +/* CSID hardware can demultiplex up to 4 outputs */ +#define MSM_CSID_MAX_SRC_STREAMS 4 #define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 #define DATA_TYPE_YUV420_8BIT 0x18 @@ -81,6 +86,8 @@ struct csid_phy_config { u8 csiphy_id; u8 lane_cnt; u32 lane_assign; + u32 en_vc; + u8 need_vc_update; }; struct csid_device; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c index 8e506a805d11..02494c89da91 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c @@ -409,7 +409,7 @@ static int vfe_get_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); output = &line->output; - if (output->state != VFE_OUTPUT_OFF) { + if (output->state > VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is running\n"); goto error; } @@ -462,7 +462,7 @@ static int vfe_enable_output(struct vfe_line *line) ops->reg_update_clear(vfe, line->id); - if (output->state != VFE_OUTPUT_OFF) { + if (output->state > VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state); spin_unlock_irqrestore(&vfe->output_lock, flags); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c index 3aa962b5663b..f70aad2e8c23 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-480.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c @@ -94,6 +94,8 @@ static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) #define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n)) #define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n)) +#define MAX_VFE_OUTPUT_LINES 4 + static u32 vfe_hw_version(struct vfe_device *vfe) { u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION); @@ -171,12 +173,26 @@ static inline void vfe_reg_update_clear(struct vfe_device *vfe, static void vfe_enable_irq_common(struct vfe_device *vfe) { - /* enable only the IRQs used: rup and comp_done irqs for RDI0 */ + /* enable reset ack IRQ and top BUS status IRQ */ writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ, vfe->base + VFE_IRQ_MASK(0)); - writel_relaxed(BUS_IRQ_MASK_0_RDI_RUP(vfe, 0) | - BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(0)), - vfe->base + VFE_BUS_IRQ_MASK(0)); +} + +static void vfe_enable_lines_irq(struct vfe_device *vfe) +{ + int i; + u32 bus_irq_mask = 0; + + for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) { + /* Enable IRQ for newly added lines, but also keep already running lines's IRQ */ + if (vfe->line[i].output.state == VFE_OUTPUT_RESERVED || + vfe->line[i].output.state == VFE_OUTPUT_ON) { + bus_irq_mask |= BUS_IRQ_MASK_0_RDI_RUP(vfe, i) + | BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)); + } + } + + writel_relaxed(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0)); } static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id); @@ -193,6 +209,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) { struct vfe_device *vfe = dev; u32 status; + int i; status = readl_relaxed(vfe->base + VFE_IRQ_STATUS(0)); writel_relaxed(status, vfe->base + VFE_IRQ_CLEAR(0)); @@ -207,11 +224,14 @@ static irqreturn_t vfe_isr(int irq, void *dev) writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0)); writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL); - if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, 0)) - vfe_isr_reg_update(vfe, 0); + /* Loop through all WMs IRQs */ + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) { + if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, i)) + vfe_isr_reg_update(vfe, i); - if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(0))) - vfe_isr_wm_done(vfe, 0); + if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i))) + vfe_isr_wm_done(vfe, i); + } } return IRQ_HANDLED; @@ -234,24 +254,23 @@ static int vfe_get_output(struct vfe_line *line) struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; unsigned long flags; - int wm_idx; spin_lock_irqsave(&vfe->output_lock, flags); output = &line->output; - if (output->state != VFE_OUTPUT_OFF) { + if (output->state > VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is running\n"); goto error; } output->wm_num = 1; - wm_idx = vfe_reserve_wm(vfe, line->id); - if (wm_idx < 0) { - dev_err(vfe->camss->dev, "Can not reserve wm\n"); - goto error_get_wm; - } - output->wm_idx[0] = wm_idx; + /* Correspondence between VFE line number and WM number. + * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3 + * Note this 1:1 mapping will not work for PIX streams. + */ + output->wm_idx[0] = line->id; + vfe->wm_output_map[line->id] = line->id; output->drop_update_idx = 0; @@ -259,11 +278,9 @@ static int vfe_get_output(struct vfe_line *line) return 0; -error_get_wm: - vfe_release_wm(vfe, output->wm_idx[0]); - output->state = VFE_OUTPUT_OFF; error: spin_unlock_irqrestore(&vfe->output_lock, flags); + output->state = VFE_OUTPUT_OFF; return -EINVAL; } @@ -279,7 +296,7 @@ static int vfe_enable_output(struct vfe_line *line) vfe_reg_update_clear(vfe, line->id); - if (output->state != VFE_OUTPUT_OFF) { + if (output->state > VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state); spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -360,6 +377,8 @@ static int vfe_enable(struct vfe_line *line) vfe->stream_count++; + vfe_enable_lines_irq(vfe); + mutex_unlock(&vfe->stream_lock); ret = vfe_get_output(line); @@ -566,7 +585,7 @@ static const struct camss_video_ops vfe_video_ops_480 = { static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) { vfe->video_ops = vfe_video_ops_480; - vfe->line_num = 1; + vfe->line_num = MAX_VFE_OUTPUT_LINES; } const struct vfe_hw_ops vfe_ops_480 = { diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c index 4fd265d01883..239d3d4ac666 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c @@ -194,7 +194,7 @@ static int vfe_enable_output(struct vfe_line *line) ops->reg_update_clear(vfe, line->id); - if (output->state != VFE_OUTPUT_RESERVED) { + if (output->state > VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state); spin_unlock_irqrestore(&vfe->output_lock, flags); return -EINVAL; @@ -289,7 +289,7 @@ static int vfe_get_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); output = &line->output; - if (output->state != VFE_OUTPUT_OFF) { + if (output->state > VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is running\n"); goto error; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index a26e4a5d87b6..e0832f3f4f25 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -740,6 +740,7 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) int ret; if (enable) { + line->output.state = VFE_OUTPUT_RESERVED; ret = vfe->ops->vfe_enable(line); if (ret < 0) dev_err(vfe->camss->dev, diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 41deda232e4a..898f32177b12 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -342,7 +342,9 @@ static struct v4l2_subdev *video_remote_subdev(struct camss_video *video, static int video_get_subdev_format(struct camss_video *video, struct v4l2_format *format) { - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; u32 pad; int ret; @@ -351,8 +353,8 @@ static int video_get_subdev_format(struct camss_video *video, if (subdev == NULL) return -EPIPE; + memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); if (ret) @@ -493,9 +495,11 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) struct v4l2_subdev *subdev; int ret; - ret = video_device_pipeline_start(vdev, &video->pipe); - if (ret < 0) + ret = video_device_pipeline_alloc_start(vdev); + if (ret < 0) { + dev_err(video->camss->dev, "Failed to start media pipeline: %d\n", ret); goto flush_buffers; + } ret = video_check_format(video); if (ret < 0) @@ -537,6 +541,7 @@ static void video_stop_streaming(struct vb2_queue *q) struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; + int ret; entity = &vdev->entity; while (1) { @@ -551,7 +556,18 @@ static void video_stop_streaming(struct vb2_queue *q) entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); - v4l2_subdev_call(subdev, video, s_stream, 0); + 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; + } } video_device_pipeline_stop(vdev); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 9cda284f1e71..1ef26aea3eae 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1320,7 +1320,7 @@ static int camss_register_entities(struct camss *camss) struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; ret = media_create_pad_link(&csid->entity, - MSM_CSID_PAD_SRC, + MSM_CSID_PAD_FIRST_SRC + j, &vfe->entity, MSM_VFE_PAD_SINK, 0); @@ -1725,7 +1725,7 @@ void camss_delete(struct camss *camss) * * Always returns 0. */ -static int camss_remove(struct platform_device *pdev) +static void camss_remove(struct platform_device *pdev) { struct camss *camss = platform_get_drvdata(pdev); @@ -1735,8 +1735,6 @@ static int camss_remove(struct platform_device *pdev) if (atomic_read(&camss->ref_count) == 0) camss_delete(camss); - - return 0; } static const struct of_device_id camss_dt_match[] = { @@ -1798,7 +1796,7 @@ static const struct dev_pm_ops camss_pm_ops = { static struct platform_driver qcom_camss_driver = { .probe = camss_probe, - .remove = camss_remove, + .remove_new = camss_remove, .driver = { .name = "qcom-camss", .of_match_table = camss_dt_match, diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 990a1519f968..2ae867cb4c48 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -416,7 +416,7 @@ err_core_put: return ret; } -static int venus_remove(struct platform_device *pdev) +static void venus_remove(struct platform_device *pdev) { struct venus_core *core = platform_get_drvdata(pdev); const struct venus_pm_ops *pm_ops = core->pm_ops; @@ -447,8 +447,6 @@ static int venus_remove(struct platform_device *pdev) mutex_destroy(&core->pm_lock); mutex_destroy(&core->lock); venus_dbgfs_deinit(core); - - return ret; } static void venus_core_shutdown(struct platform_device *pdev) @@ -891,7 +889,7 @@ MODULE_DEVICE_TABLE(of, venus_dt_match); static struct platform_driver qcom_venus_driver = { .probe = venus_probe, - .remove = venus_remove, + .remove_new = 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 32551c2602a9..4f81669986ba 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -107,7 +107,6 @@ 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 - * @opp_table: an device OPP table handle * @has_opp_table: does OPP table exist * @pmdomains: an array of pmdomains struct device pointers * @opp_dl_venus: an device-link for device OPP @@ -317,6 +316,14 @@ enum venus_dec_state { VENUS_DEC_STATE_DRC = 7, }; +enum venus_enc_state { + VENUS_ENC_STATE_DEINIT = 0, + VENUS_ENC_STATE_INIT = 1, + VENUS_ENC_STATE_ENCODING = 2, + VENUS_ENC_STATE_STOPPED = 3, + VENUS_ENC_STATE_DRAIN = 4, +}; + struct venus_ts_metadata { bool used; u64 ts_ns; @@ -428,6 +435,7 @@ struct venus_inst { u8 quantization; u8 xfer_func; enum venus_dec_state codec_state; + enum venus_enc_state enc_state; wait_queue_head_t reconf_wait; unsigned int subscriptions; int buf_count; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 61ff20a7e935..cfb11c551167 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -38,8 +38,8 @@ static void venus_reset_cpu(struct venus_core *core) writel(fw_size, wrapper_base + WRAPPER_FW_END_ADDR); writel(0, wrapper_base + WRAPPER_CPA_START_ADDR); writel(fw_size, wrapper_base + WRAPPER_CPA_END_ADDR); - writel(0, wrapper_base + WRAPPER_NONPIX_START_ADDR); - writel(0, wrapper_base + WRAPPER_NONPIX_END_ADDR); + writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR); + writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR); if (IS_V6(core)) { /* Bring XTSS out of reset */ diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index ab6a29ffc81e..a2ceab7f9ddb 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -988,8 +988,8 @@ static u32 get_framesize_raw_p010(u32 width, u32 height) { u32 y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; - y_stride = ALIGN(width * 2, 256); - uv_stride = ALIGN(width * 2, 256); + y_stride = ALIGN(width * 2, 128); + uv_stride = ALIGN(width * 2, 128); y_sclines = ALIGN(height, 32); uv_sclines = ALIGN((height + 1) >> 1, 16); y_plane = y_stride * y_sclines; diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 930b743f225e..bc3f8ff05840 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1257,7 +1257,30 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, pkt->shdr.hdr.size += sizeof(u32) + sizeof(*tm); break; } + case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2: { + struct hfi_quantization_range_v2 *in = pdata, *range = prop_data; + u32 min_qp, max_qp; + + min_qp = in->min_qp.qp_packed; + max_qp = in->max_qp.qp_packed; + /* We'll be packing in the qp, so make sure we + * won't be losing data when masking + */ + if (min_qp > 0xff || max_qp > 0xff) + return -ERANGE; + + range->min_qp.layer_id = 0xFF; + range->max_qp.layer_id = 0xFF; + range->min_qp.qp_packed = (min_qp & 0xFF) | ((min_qp & 0xFF) << 8) | + ((min_qp & 0xFF) << 16); + range->max_qp.qp_packed = (max_qp & 0xFF) | ((max_qp & 0xFF) << 8) | + ((max_qp & 0xFF) << 16); + range->min_qp.enable = 7; + range->max_qp.enable = 7; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*range); + break; + } case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index d2d6719a2ba4..105792a68060 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -487,6 +487,11 @@ #define HFI_PROPERTY_PARAM_VENC_SESSION_QP 0x2005006 #define HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION 0x2005007 #define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE 0x2005008 +/* + * Note: HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 is + * specific to HFI_VERSION_6XX and HFI_VERSION_4XX only + */ +#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 0x2005009 #define HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION 0x2005009 #define HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER 0x200500a #define HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION 0x200500b @@ -827,6 +832,19 @@ struct hfi_quantization_range { u32 layer_id; }; +struct hfi_quantization_v2 { + u32 qp_packed; + u32 layer_id; + u32 enable; + u32 reserved[3]; +}; + +struct hfi_quantization_range_v2 { + struct hfi_quantization_v2 min_qp; + struct hfi_quantization_v2 max_qp; + u32 reserved[4]; +}; + #define HFI_LTR_MODE_DISABLE 0x0 #define HFI_LTR_MODE_MANUAL 0x1 #define HFI_LTR_MODE_PERIODIC 0x2 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 ea25c451222b..a9be31ec6927 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -93,7 +93,7 @@ #define LCU_MIN_SIZE_PELS 16 #define SIZE_SEI_USERDATA 4096 -#define H265D_MAX_SLICE 600 +#define H265D_MAX_SLICE 3600 #define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T #define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32)) #define SIZE_H265D_VPP_CMD_PER_BUF 256 @@ -1021,7 +1021,7 @@ static u32 h264d_persist1_size(void) static u32 h265d_persist1_size(void) { return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + H265_NUM_TILE - * sizeof(u32)), HFI_DMA_ALIGNMENT); + * sizeof(u32) + NUM_HW_PIC_BUF * SIZE_SEI_USERDATA), HFI_DMA_ALIGNMENT); } static u32 vp8d_persist1_size(void) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 4ceaba37e2e5..51a53bf82bd3 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -31,15 +31,15 @@ */ static const struct venus_format vdec_formats[] = { { - .pixfmt = V4L2_PIX_FMT_QC08C, + .pixfmt = V4L2_PIX_FMT_NV12, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, }, { - .pixfmt = V4L2_PIX_FMT_QC10C, + .pixfmt = V4L2_PIX_FMT_QC08C, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - },{ - .pixfmt = V4L2_PIX_FMT_NV12, + }, { + .pixfmt = V4L2_PIX_FMT_QC10C, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, }, { @@ -204,8 +204,13 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->height); if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + unsigned int stride = pixmp->width; + + if (pixmp->pixelformat == V4L2_PIX_FMT_P010) + stride *= 2; + pfmt[0].sizeimage = szimage; - pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + pfmt[0].bytesperline = ALIGN(stride, 128); } else { pfmt[0].sizeimage = clamp_t(u32, pfmt[0].sizeimage, 0, SZ_8M); pfmt[0].sizeimage = max(pfmt[0].sizeimage, szimage); @@ -526,6 +531,7 @@ static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) { struct venus_inst *inst = to_inst(file); + struct vb2_queue *dst_vq; struct hfi_frame_data fdata = {0}; int ret; @@ -556,6 +562,13 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) inst->codec_state = VENUS_DEC_STATE_DRAIN; inst->drain_active = true; } + } else if (cmd->cmd == V4L2_DEC_CMD_START && + inst->codec_state == VENUS_DEC_STATE_STOPPED) { + dst_vq = v4l2_m2m_get_vq(inst->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + vb2_clear_last_buffer_dequeued(dst_vq); + + inst->codec_state = VENUS_DEC_STATE_DECODING; } unlock: @@ -1774,7 +1787,7 @@ err_vdev_release: return ret; } -static int vdec_remove(struct platform_device *pdev) +static void vdec_remove(struct platform_device *pdev) { struct venus_core *core = dev_get_drvdata(pdev->dev.parent); @@ -1783,8 +1796,6 @@ static int vdec_remove(struct platform_device *pdev) if (core->pm_ops->vdec_put) core->pm_ops->vdec_put(core->dev_dec); - - return 0; } static __maybe_unused int vdec_runtime_suspend(struct device *dev) @@ -1825,7 +1836,7 @@ MODULE_DEVICE_TABLE(of, vdec_dt_match); static struct platform_driver qcom_venus_dec_driver = { .probe = vdec_probe, - .remove = vdec_remove, + .remove_new = vdec_remove, .driver = { .name = "qcom-venus-decoder", .of_match_table = vdec_dt_match, diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index cdb12546c4fa..4666f42feea3 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -520,6 +520,51 @@ static int venc_subscribe_event(struct v4l2_fh *fh, } } +static int +venc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd) +{ + struct venus_inst *inst = to_inst(file); + struct hfi_frame_data fdata = {0}; + int ret = 0; + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); + if (ret) + return ret; + + mutex_lock(&inst->lock); + + if (cmd->cmd == V4L2_ENC_CMD_STOP && + inst->enc_state == VENUS_ENC_STATE_ENCODING) { + /* + * Implement V4L2_ENC_CMD_STOP by enqueue an empty buffer on + * encoder input to signal EOS. + */ + if (!(inst->streamon_out && inst->streamon_cap)) + goto unlock; + + fdata.buffer_type = HFI_BUFFER_INPUT; + fdata.flags |= HFI_BUFFERFLAG_EOS; + fdata.device_addr = 0xdeadb000; + + ret = hfi_session_process_buf(inst, &fdata); + + inst->enc_state = VENUS_ENC_STATE_DRAIN; + } else if (cmd->cmd == V4L2_ENC_CMD_START) { + if (inst->enc_state == VENUS_ENC_STATE_DRAIN) { + ret = -EBUSY; + goto unlock; + } + if (inst->enc_state == VENUS_ENC_STATE_STOPPED) { + vb2_clear_last_buffer_dequeued(&inst->fh.m2m_ctx->cap_q_ctx.q); + inst->enc_state = VENUS_ENC_STATE_ENCODING; + } + } + +unlock: + mutex_unlock(&inst->lock); + return ret; +} + static const struct v4l2_ioctl_ops venc_ioctl_ops = { .vidioc_querycap = venc_querycap, .vidioc_enum_fmt_vid_cap = venc_enum_fmt, @@ -548,6 +593,7 @@ static const struct v4l2_ioctl_ops venc_ioctl_ops = { .vidioc_subscribe_event = venc_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = venc_encoder_cmd, }; static int venc_pm_get(struct venus_inst *inst) @@ -617,6 +663,7 @@ static int venc_set_properties(struct venus_inst *inst) struct hfi_idr_period idrp; struct hfi_quantization quant; struct hfi_quantization_range quant_range; + struct hfi_quantization_range_v2 quant_range_v2; struct hfi_enable en; struct hfi_ltr_mode ltr_mode; struct hfi_intra_refresh intra_refresh = {}; @@ -825,16 +872,40 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; - ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; - if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { - quant_range.min_qp = ctr->hevc_min_qp; - quant_range.max_qp = ctr->hevc_max_qp; + if (inst->core->res->hfi_version == HFI_VERSION_4XX || + inst->core->res->hfi_version == HFI_VERSION_6XX) { + ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2; + + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + quant_range_v2.min_qp.qp_packed = ctr->hevc_min_qp; + quant_range_v2.max_qp.qp_packed = ctr->hevc_max_qp; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { + quant_range_v2.min_qp.qp_packed = ctr->vp8_min_qp; + quant_range_v2.max_qp.qp_packed = ctr->vp8_max_qp; + } else { + quant_range_v2.min_qp.qp_packed = ctr->h264_min_qp; + quant_range_v2.max_qp.qp_packed = ctr->h264_max_qp; + } + + ret = hfi_session_set_property(inst, ptype, &quant_range_v2); } else { - quant_range.min_qp = ctr->h264_min_qp; - quant_range.max_qp = ctr->h264_max_qp; + ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; + + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + quant_range.min_qp = ctr->hevc_min_qp; + quant_range.max_qp = ctr->hevc_max_qp; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { + quant_range.min_qp = ctr->vp8_min_qp; + quant_range.max_qp = ctr->vp8_max_qp; + } else { + quant_range.min_qp = ctr->h264_min_qp; + quant_range.max_qp = ctr->h264_max_qp; + } + + quant_range.layer_id = 0; + ret = hfi_session_set_property(inst, ptype, &quant_range); } - quant_range.layer_id = 0; - ret = hfi_session_set_property(inst, ptype, &quant_range); + if (ret) return ret; @@ -1196,6 +1267,8 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto error; + inst->enc_state = VENUS_ENC_STATE_ENCODING; + mutex_unlock(&inst->lock); return 0; @@ -1215,10 +1288,21 @@ error: static void venc_vb2_buf_queue(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); venc_pm_get_put(inst); mutex_lock(&inst->lock); + + if (inst->enc_state == VENUS_ENC_STATE_STOPPED) { + vbuf->sequence = inst->sequence_cap++; + vbuf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, 0); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + mutex_unlock(&inst->lock); + return; + } + venus_helper_vb2_buf_queue(vb); mutex_unlock(&inst->lock); } @@ -1260,6 +1344,10 @@ static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type, vb->planes[0].data_offset = data_offset; vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; + if ((vbuf->flags & V4L2_BUF_FLAG_LAST) && + inst->enc_state == VENUS_ENC_STATE_DRAIN) { + inst->enc_state = VENUS_ENC_STATE_STOPPED; + } } else { vbuf->sequence = inst->sequence_out++; } @@ -1362,6 +1450,9 @@ static int venc_open(struct file *file) inst->core_acquired = false; inst->nonblock = file->f_flags & O_NONBLOCK; + if (inst->enc_state == VENUS_ENC_STATE_DEINIT) + inst->enc_state = VENUS_ENC_STATE_INIT; + venus_helper_init_instance(inst); ret = venc_ctrl_init(inst); @@ -1424,6 +1515,8 @@ static int venc_close(struct file *file) v4l2_fh_del(&inst->fh); v4l2_fh_exit(&inst->fh); + inst->enc_state = VENUS_ENC_STATE_DEINIT; + venc_pm_put(inst, false); kfree(inst); @@ -1492,7 +1585,7 @@ err_vdev_release: return ret; } -static int venc_remove(struct platform_device *pdev) +static void venc_remove(struct platform_device *pdev) { struct venus_core *core = dev_get_drvdata(pdev->dev.parent); @@ -1501,8 +1594,6 @@ static int venc_remove(struct platform_device *pdev) if (core->pm_ops->venc_put) core->pm_ops->venc_put(core->dev_enc); - - return 0; } static __maybe_unused int venc_runtime_suspend(struct device *dev) @@ -1543,7 +1634,7 @@ MODULE_DEVICE_TABLE(of, venc_dt_match); static struct platform_driver qcom_venus_enc_driver = { .probe = venc_probe, - .remove = venc_remove, + .remove_new = venc_remove, .driver = { .name = "qcom-venus-encoder", .of_match_table = venc_dt_match, diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c index eb59a3ba6d0f..bcef7b87da7c 100644 --- a/drivers/media/platform/renesas/rcar-fcp.c +++ b/drivers/media/platform/renesas/rcar-fcp.c @@ -144,7 +144,7 @@ static int rcar_fcp_probe(struct platform_device *pdev) return 0; } -static int rcar_fcp_remove(struct platform_device *pdev) +static void rcar_fcp_remove(struct platform_device *pdev) { struct rcar_fcp_device *fcp = platform_get_drvdata(pdev); @@ -153,8 +153,6 @@ static int rcar_fcp_remove(struct platform_device *pdev) mutex_unlock(&fcp_lock); pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id rcar_fcp_of_match[] = { @@ -166,7 +164,7 @@ MODULE_DEVICE_TABLE(of, rcar_fcp_of_match); static struct platform_driver rcar_fcp_platform_driver = { .probe = rcar_fcp_probe, - .remove = rcar_fcp_remove, + .remove_new = 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 10b3474f93a4..f666b621338d 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -419,10 +419,7 @@ static const struct media_entity_operations risp_entity_ops = { static int risp_probe_resources(struct rcar_isp *isp, struct platform_device *pdev) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - isp->base = devm_ioremap_resource(&pdev->dev, res); + isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(isp->base)) return PTR_ERR(isp->base); @@ -503,7 +500,7 @@ error_mutex: return ret; } -static int risp_remove(struct platform_device *pdev) +static void risp_remove(struct platform_device *pdev) { struct rcar_isp *isp = platform_get_drvdata(pdev); @@ -515,8 +512,6 @@ static int risp_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mutex_destroy(&isp->lock); - - return 0; } static struct platform_driver rcar_isp_driver = { @@ -525,7 +520,7 @@ static struct platform_driver rcar_isp_driver = { .of_match_table = risp_of_id_table, }, .probe = risp_probe, - .remove = risp_remove, + .remove_new = 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 5e53d6b7036c..ff4bde9cc0e3 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -17,7 +17,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> -#include <linux/sys_soc.h> #include <media/v4l2-async.h> #include <media/v4l2-fwnode.h> @@ -1183,24 +1182,6 @@ static const struct rvin_info rcar_info_r8a7795 = { .scaler = rvin_scaler_gen3, }; -static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = { - { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, - { .master = 0, .csi = RVIN_CSI21, .chsel = 0x05 }, - { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, - { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, - { .master = 4, .csi = RVIN_CSI21, .chsel = 0x05 }, - { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 }, - { /* Sentinel */ } -}; - -static const struct rvin_info rcar_info_r8a7795es1 = { - .model = RCAR_GEN3, - .use_mc = true, - .max_width = 4096, - .max_height = 4096, - .routes = rcar_info_r8a7795es1_routes, -}; - static const struct rvin_group_route rcar_info_r8a7796_routes[] = { { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, @@ -1372,17 +1353,8 @@ static const struct of_device_id rvin_of_id_table[] = { }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); -static const struct soc_device_attribute r8a7795es1[] = { - { - .soc_id = "r8a7795", .revision = "ES1.*", - .data = &rcar_info_r8a7795es1, - }, - { /* Sentinel */ } -}; - static int rcar_vin_probe(struct platform_device *pdev) { - const struct soc_device_attribute *attr; struct rvin_dev *vin; int irq, ret; @@ -1394,14 +1366,6 @@ static int rcar_vin_probe(struct platform_device *pdev) vin->info = of_device_get_match_data(&pdev->dev); vin->alpha = 0xff; - /* - * Special care is needed on r8a7795 ES1.x since it - * uses different routing than r8a7795 ES2.0. - */ - attr = soc_device_match(r8a7795es1); - if (attr) - vin->info = attr->data; - vin->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vin->base)) return PTR_ERR(vin->base); @@ -1442,7 +1406,7 @@ static int rcar_vin_probe(struct platform_device *pdev) return 0; } -static int rcar_vin_remove(struct platform_device *pdev) +static void rcar_vin_remove(struct platform_device *pdev) { struct rvin_dev *vin = platform_get_drvdata(pdev); @@ -1458,8 +1422,6 @@ static int rcar_vin_remove(struct platform_device *pdev) rvin_parallel_cleanup(vin); rvin_dma_unregister(vin); - - return 0; } static SIMPLE_DEV_PM_OPS(rvin_pm_ops, rvin_suspend, rvin_resume); @@ -1472,7 +1434,7 @@ static struct platform_driver rcar_vin_driver = { .of_match_table = rvin_of_id_table, }, .probe = rcar_vin_probe, - .remove = rcar_vin_remove, + .remove_new = rcar_vin_remove, }; module_platform_driver(rcar_vin_driver); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 174aa6176f54..e34060c2b039 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -345,7 +345,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg hsfreqrange_m3w_h3es1[] = { +static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -1369,11 +1369,6 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { .clear_ulps = true, }; -static const struct rcar_csi2_info rcar_csi2_info_r8a7795es1 = { - .hsfreqrange = hsfreqrange_m3w_h3es1, - .num_channels = 4, -}; - static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { .init_phtw = rcsi2_init_phtw_h3es2, .hsfreqrange = hsfreqrange_h3_v3h_m3n, @@ -1383,12 +1378,12 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { - .hsfreqrange = hsfreqrange_m3w_h3es1, + .hsfreqrange = hsfreqrange_m3w, .num_channels = 4, }; static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = { - .hsfreqrange = hsfreqrange_m3w_h3es1, + .hsfreqrange = hsfreqrange_m3w, .num_channels = 4, }; @@ -1482,10 +1477,6 @@ MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); static const struct soc_device_attribute r8a7795[] = { { - .soc_id = "r8a7795", .revision = "ES1.*", - .data = &rcar_csi2_info_r8a7795es1, - }, - { .soc_id = "r8a7795", .revision = "ES2.*", .data = &rcar_csi2_info_r8a7795es2, }, @@ -1574,7 +1565,7 @@ error_mutex: return ret; } -static int rcsi2_remove(struct platform_device *pdev) +static void rcsi2_remove(struct platform_device *pdev) { struct rcar_csi2 *priv = platform_get_drvdata(pdev); @@ -1585,12 +1576,10 @@ static int rcsi2_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mutex_destroy(&priv->lock); - - return 0; } static struct platform_driver rcar_csi2_pdrv = { - .remove = rcsi2_remove, + .remove_new = rcsi2_remove, .probe = rcsi2_probe, .driver = { .name = "rcar-csi2", diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 98bfd445a649..2a77353f10b5 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -728,11 +728,9 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_SEQ_TB: case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_NONE: - vnmc = VNMC_IM_ODD_EVEN; - progressive = true; - break; case V4L2_FIELD_ALTERNATE: vnmc = VNMC_IM_ODD_EVEN; + progressive = true; break; default: vnmc = VNMC_IM_ODD; @@ -1312,12 +1310,23 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, } if (rvin_scaler_needed(vin)) { + /* Gen3 can't scale NV12 */ + if (vin->info->model == RCAR_GEN3 && + vin->format.pixelformat == V4L2_PIX_FMT_NV12) + return -EPIPE; + if (!vin->scaler) return -EPIPE; } else { - if (fmt.format.width != vin->format.width || - fmt.format.height != vin->format.height) - return -EPIPE; + if (vin->format.pixelformat == V4L2_PIX_FMT_NV12) { + if (ALIGN(fmt.format.width, 32) != vin->format.width || + ALIGN(fmt.format.height, 32) != vin->format.height) + return -EPIPE; + } else { + if (fmt.format.width != vin->format.width || + fmt.format.height != vin->format.height) + return -EPIPE; + } } if (fmt.format.code != vin->mbus_code) diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 3fec41f6e964..3a92f4535c18 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -1433,19 +1433,17 @@ static int rcar_drif_probe(struct platform_device *pdev) } /* DRIF channel remove */ -static int rcar_drif_remove(struct platform_device *pdev) +static void rcar_drif_remove(struct platform_device *pdev) { struct rcar_drif *ch = platform_get_drvdata(pdev); struct rcar_drif_sdr *sdr = ch->sdr; /* Channel 0 will be the SDR instance */ if (ch->num) - return 0; + return; /* SDR instance */ rcar_drif_sdr_remove(sdr); - - return 0; } /* FIXME: Implement suspend/resume support */ @@ -1476,7 +1474,7 @@ static struct platform_driver rcar_drif_driver = { .pm = &rcar_drif_pm_ops, }, .probe = rcar_drif_probe, - .remove = rcar_drif_remove, + .remove_new = 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 37ecf489d112..f43e458590b8 100644 --- a/drivers/media/platform/renesas/rcar_fdp1.c +++ b/drivers/media/platform/renesas/rcar_fdp1.c @@ -254,7 +254,6 @@ MODULE_PARM_DESC(debug, "activate debug info"); /* Internal Data (HW Version) */ #define FD1_IP_INTDATA 0x0800 -#define FD1_IP_H3_ES1 0x02010101 #define FD1_IP_M3W 0x02010202 #define FD1_IP_H3 0x02010203 #define FD1_IP_M3N 0x02010204 @@ -2313,8 +2312,10 @@ static int fdp1_probe(struct platform_device *pdev) /* Determine our clock rate */ clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto put_dev; + } fdp1->clk_rate = clk_get_rate(clk); clk_put(clk); @@ -2323,7 +2324,7 @@ static int fdp1_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &fdp1->v4l2_dev); if (ret) { v4l2_err(&fdp1->v4l2_dev, "Failed to register video device\n"); - return ret; + goto put_dev; } /* M2M registration */ @@ -2359,9 +2360,6 @@ static int fdp1_probe(struct platform_device *pdev) hw_version = fdp1_read(fdp1, FD1_IP_INTDATA); switch (hw_version) { - case FD1_IP_H3_ES1: - dprintk(fdp1, "FDP1 Version R-Car H3 ES1\n"); - break; case FD1_IP_M3W: dprintk(fdp1, "FDP1 Version R-Car M3-W\n"); break; @@ -2393,10 +2391,12 @@ release_m2m: unreg_dev: v4l2_device_unregister(&fdp1->v4l2_dev); +put_dev: + rcar_fcp_put(fdp1->fcp); return ret; } -static int fdp1_remove(struct platform_device *pdev) +static void fdp1_remove(struct platform_device *pdev) { struct fdp1_dev *fdp1 = platform_get_drvdata(pdev); @@ -2404,8 +2404,7 @@ static int fdp1_remove(struct platform_device *pdev) video_unregister_device(&fdp1->vfd); v4l2_device_unregister(&fdp1->v4l2_dev); pm_runtime_disable(&pdev->dev); - - return 0; + rcar_fcp_put(fdp1->fcp); } static int __maybe_unused fdp1_pm_runtime_suspend(struct device *dev) @@ -2441,7 +2440,7 @@ MODULE_DEVICE_TABLE(of, fdp1_dt_ids); static struct platform_driver fdp1_pdrv = { .probe = fdp1_probe, - .remove = fdp1_remove, + .remove_new = 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 2f4377cfbb42..e7f604807825 100644 --- a/drivers/media/platform/renesas/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -1702,7 +1702,7 @@ device_register_rollback: return ret; } -static int jpu_remove(struct platform_device *pdev) +static void jpu_remove(struct platform_device *pdev) { struct jpu *jpu = platform_get_drvdata(pdev); @@ -1710,8 +1710,6 @@ static int jpu_remove(struct platform_device *pdev) video_unregister_device(&jpu->vfd_encoder); v4l2_m2m_release(jpu->m2m_dev); v4l2_device_unregister(&jpu->v4l2_dev); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1746,7 +1744,7 @@ static const struct dev_pm_ops jpu_pm_ops = { static struct platform_driver jpu_driver = { .probe = jpu_probe, - .remove = jpu_remove, + .remove_new = 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 f70f91b006b7..56b9c59cfda8 100644 --- a/drivers/media/platform/renesas/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c @@ -795,8 +795,8 @@ static int __ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; struct v4l2_subdev_pad_config pad_cfg; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; + .pads = &pad_cfg, + }; const struct ceu_fmt *ceu_fmt; u32 mbus_code_old; u32 mbus_code; @@ -1709,7 +1709,7 @@ error_free_ceudev: return ret; } -static int ceu_remove(struct platform_device *pdev) +static void ceu_remove(struct platform_device *pdev) { struct ceu_device *ceudev = platform_get_drvdata(pdev); @@ -1722,8 +1722,6 @@ static int ceu_remove(struct platform_device *pdev) v4l2_device_unregister(&ceudev->v4l2_dev); video_unregister_device(&ceudev->vdev); - - return 0; } static const struct dev_pm_ops ceu_pm_ops = { @@ -1739,7 +1737,7 @@ static struct platform_driver ceu_driver = { .of_match_table = of_match_ptr(ceu_of_match), }, .probe = ceu_probe, - .remove = ceu_remove, + .remove_new = 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 5939f5165a5e..7a71370fcc32 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -298,7 +298,7 @@ error_dma_unregister: return ret; } -static int rzg2l_cru_remove(struct platform_device *pdev) +static void rzg2l_cru_remove(struct platform_device *pdev) { struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev); @@ -312,8 +312,6 @@ static int rzg2l_cru_remove(struct platform_device *pdev) mutex_destroy(&cru->mdev_lock); rzg2l_cru_dma_unregister(cru); - - return 0; } static const struct of_device_id rzg2l_cru_of_id_table[] = { @@ -328,7 +326,7 @@ static struct platform_driver rzg2l_cru_driver = { .of_match_table = rzg2l_cru_of_id_table, }, .probe = rzg2l_cru_probe, - .remove = rzg2l_cru_remove, + .remove_new = rzg2l_cru_remove, }; module_platform_driver(rzg2l_cru_driver); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 384fb54e219a..30dad7383654 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -819,7 +819,7 @@ error_pm: return ret; } -static int rzg2l_csi2_remove(struct platform_device *pdev) +static void rzg2l_csi2_remove(struct platform_device *pdev) { struct rzg2l_csi2 *csi2 = platform_get_drvdata(pdev); @@ -829,8 +829,6 @@ static int rzg2l_csi2_remove(struct platform_device *pdev) v4l2_subdev_cleanup(&csi2->subdev); media_entity_cleanup(&csi2->subdev.entity); pm_runtime_disable(&pdev->dev); - - return 0; } static int __maybe_unused rzg2l_csi2_pm_runtime_suspend(struct device *dev) @@ -859,7 +857,7 @@ static const struct of_device_id rzg2l_csi2_of_table[] = { }; static struct platform_driver rzg2l_csi2_pdrv = { - .remove = rzg2l_csi2_remove, + .remove_new = rzg2l_csi2_remove, .probe = rzg2l_csi2_probe, .driver = { .name = "rzg2l-csi2", diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index ca4310e26c49..8fe3272a541f 100644 --- a/drivers/media/platform/renesas/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c @@ -1343,7 +1343,7 @@ ei2cgadap: return ret; } -static int sh_vou_remove(struct platform_device *pdev) +static void sh_vou_remove(struct platform_device *pdev) { struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); struct sh_vou_device *vou_dev = container_of(v4l2_dev, @@ -1356,11 +1356,10 @@ static int sh_vou_remove(struct platform_device *pdev) video_unregister_device(&vou_dev->vdev); i2c_put_adapter(client->adapter); v4l2_device_unregister(&vou_dev->v4l2_dev); - return 0; } static struct platform_driver sh_vou = { - .remove = sh_vou_remove, + .remove_new = sh_vou_remove, .driver = { .name = "sh-vou", }, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index c6f25200982c..9b087bd8df7d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -66,7 +66,9 @@ static int vsp1_du_insert_uif(struct vsp1_device *vsp1, struct vsp1_entity *prev, unsigned int prev_pad, struct vsp1_entity *next, unsigned int next_pad) { - struct v4l2_subdev_format format; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; if (!uif) { @@ -82,8 +84,6 @@ static int vsp1_du_insert_uif(struct vsp1_device *vsp1, prev->sink = uif; prev->sink_pad = UIF_PAD_SINK; - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; format.pad = prev_pad; ret = v4l2_subdev_call(&prev->subdev, pad, get_fmt, NULL, &format); @@ -118,8 +118,12 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, struct vsp1_entity *uif, unsigned int brx_input) { - struct v4l2_subdev_selection sel; - struct v4l2_subdev_format format; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; const struct v4l2_rect *crop; int ret; @@ -129,8 +133,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, */ crop = &vsp1->drm->inputs[rpf->entity.index].crop; - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; format.pad = RWPF_PAD_SINK; format.format.width = crop->width + crop->left; format.format.height = crop->height + crop->top; @@ -147,8 +149,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, __func__, format.format.width, format.format.height, format.format.code, rpf->entity.index); - memset(&sel, 0, sizeof(sel)); - sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.pad = RWPF_PAD_SINK; sel.target = V4L2_SEL_TGT_CROP; sel.r = *crop; @@ -481,10 +481,11 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe) { struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); - struct v4l2_subdev_format format = { 0, }; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; format.pad = RWPF_PAD_SINK; format.format.width = drm_pipe->width; format.format.height = drm_pipe->height; @@ -710,6 +711,9 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, return 0; } + /* Reset the underrun counter */ + pipe->underrun_count = 0; + drm_pipe->width = cfg->width; drm_pipe->height = cfg->height; pipe->interlaced = cfg->interlaced; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 5710152d6511..a9db84be4822 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -45,7 +45,8 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) { - u32 mask = VI6_WPF_IRQ_STA_DFE | VI6_WPF_IRQ_STA_FRE; + u32 mask = VI6_WPF_IRQ_STA_DFE | VI6_WPF_IRQ_STA_FRE | + VI6_WPF_IRQ_STA_UND; struct vsp1_device *vsp1 = data; irqreturn_t ret = IRQ_NONE; unsigned int i; @@ -60,6 +61,14 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i)); vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); + if ((status & VI6_WPF_IRQ_STA_UND) && wpf->entity.pipe) { + wpf->entity.pipe->underrun_count++; + + dev_warn_ratelimited(vsp1->dev, + "Underrun occurred at WPF%u (total underruns %u)\n", + i, wpf->entity.pipe->underrun_count); + } + if (status & VI6_WPF_IRQ_STA_DFE) { vsp1_pipeline_frame_end(wpf->entity.pipe); ret = IRQ_HANDLED; @@ -977,7 +986,7 @@ done: return ret; } -static int vsp1_remove(struct platform_device *pdev) +static void vsp1_remove(struct platform_device *pdev) { struct vsp1_device *vsp1 = platform_get_drvdata(pdev); @@ -985,8 +994,6 @@ static int vsp1_remove(struct platform_device *pdev) rcar_fcp_put(vsp1->fcp); pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id vsp1_of_match[] = { @@ -999,7 +1006,7 @@ MODULE_DEVICE_TABLE(of, vsp1_of_match); static struct platform_driver vsp1_platform_driver = { .probe = vsp1_probe, - .remove = vsp1_remove, + .remove_new = 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 4c3bd2b1ca28..c31f05a80bb5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -184,15 +184,14 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state) { - struct v4l2_subdev_format format; unsigned int pad; for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { - memset(&format, 0, sizeof(format)); - - format.pad = pad; - format.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE; + struct v4l2_subdev_format format = { + .pad = pad, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index f22724439cdc..17f98a6a972e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -130,8 +130,6 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const struct v4l2_subdev_ops *ops, u32 function); void vsp1_entity_destroy(struct vsp1_entity *entity); -extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; - int vsp1_entity_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index ae646c9ef337..674b5748d929 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -148,6 +148,8 @@ struct vsp1_pipeline { unsigned int partitions; struct vsp1_partition *partition; struct vsp1_partition *part_table; + + u32 underrun_count; }; void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index d94343ae57a1..7eca82e0ba7e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h @@ -32,10 +32,12 @@ #define VI6_STATUS_SYS_ACT(n) BIT((n) + 8) #define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12) +#define VI6_WPF_IRQ_ENB_UNDE BIT(16) #define VI6_WPF_IRQ_ENB_DFEE BIT(1) #define VI6_WPF_IRQ_ENB_FREE BIT(0) #define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12) +#define VI6_WPF_IRQ_STA_UND BIT(16) #define VI6_WPF_IRQ_STA_DFE BIT(1) #define VI6_WPF_IRQ_STA_FRE BIT(0) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 544012fd1fe9..e9d5027761bb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -62,7 +62,9 @@ vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) static int vsp1_video_verify_format(struct vsp1_video *video) { - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; int ret; @@ -70,7 +72,6 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (subdev == NULL) return -EINVAL; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret == -ENOIOCTLCMD ? -EINVAL : ret; @@ -776,7 +777,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; - if (vb2_is_streaming(&video->queue) && + if (vb2_start_streaming_called(&video->queue) && vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 61b25fcf826e..67dcf22e5ba3 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -930,7 +930,7 @@ err_put_clk: return ret; } -static int rga_remove(struct platform_device *pdev) +static void rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); @@ -947,8 +947,6 @@ static int rga_remove(struct platform_device *pdev) v4l2_device_unregister(&rga->v4l2_dev); pm_runtime_disable(rga->dev); - - return 0; } static int __maybe_unused rga_runtime_suspend(struct device *dev) @@ -986,7 +984,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rga_match); static struct platform_driver rga_pdrv = { .probe = rga_probe, - .remove = rga_remove, + .remove_new = rga_remove, .driver = { .name = RGA_NAME, .pm = &rga_pm, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index d1d1fdce03e3..8f3cba319762 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -111,6 +111,16 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_mp_fmts[] = { .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA, .mbus = MEDIA_BUS_FMT_YUYV8_2X8, }, { + .fourcc = V4L2_PIX_FMT_NV16M, + .uv_swap = 0, + .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA, + .mbus = MEDIA_BUS_FMT_YUYV8_2X8, + }, { + .fourcc = V4L2_PIX_FMT_NV61M, + .uv_swap = 1, + .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA, + .mbus = MEDIA_BUS_FMT_YUYV8_2X8, + }, { .fourcc = V4L2_PIX_FMT_YVU422M, .uv_swap = 1, .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, @@ -238,6 +248,18 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = { .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422, .mbus = MEDIA_BUS_FMT_YUYV8_2X8, }, { + .fourcc = V4L2_PIX_FMT_NV16M, + .uv_swap = 0, + .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA, + .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422, + .mbus = MEDIA_BUS_FMT_YUYV8_2X8, + }, { + .fourcc = V4L2_PIX_FMT_NV61M, + .uv_swap = 1, + .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA, + .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422, + .mbus = MEDIA_BUS_FMT_YUYV8_2X8, + }, { .fourcc = V4L2_PIX_FMT_YVU422M, .uv_swap = 1, .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, @@ -1212,6 +1234,35 @@ static int rkisp1_enum_fmt_vid_cap_mplane(struct file *file, void *priv, return -EINVAL; } +static int rkisp1_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + static const unsigned int max_widths[] = { + RKISP1_RSZ_MP_SRC_MAX_WIDTH, + RKISP1_RSZ_SP_SRC_MAX_WIDTH, + }; + static const unsigned int max_heights[] = { + RKISP1_RSZ_MP_SRC_MAX_HEIGHT, + RKISP1_RSZ_SP_SRC_MAX_HEIGHT, + }; + struct rkisp1_capture *cap = video_drvdata(file); + + if (fsize->index != 0) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fsize->stepwise.min_width = RKISP1_RSZ_SRC_MIN_WIDTH; + fsize->stepwise.max_width = max_widths[cap->id]; + fsize->stepwise.step_width = 2; + + fsize->stepwise.min_height = RKISP1_RSZ_SRC_MIN_HEIGHT; + fsize->stepwise.max_height = max_heights[cap->id]; + fsize->stepwise.step_height = 2; + + return 0; +} + static int rkisp1_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { @@ -1261,6 +1312,7 @@ static const struct v4l2_ioctl_ops rkisp1_v4l2_ioctl_ops = { .vidioc_s_fmt_vid_cap_mplane = rkisp1_s_fmt_vid_cap_mplane, .vidioc_g_fmt_vid_cap_mplane = rkisp1_g_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_cap = rkisp1_enum_fmt_vid_cap_mplane, + .vidioc_enum_framesizes = rkisp1_enum_framesizes, .vidioc_querycap = rkisp1_querycap, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index f2475c6235ea..4762cb32353d 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -622,7 +622,7 @@ err_pm_runtime_disable: return ret; } -static int rkisp1_remove(struct platform_device *pdev) +static void rkisp1_remove(struct platform_device *pdev) { struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev); @@ -638,8 +638,6 @@ static int rkisp1_remove(struct platform_device *pdev) v4l2_device_unregister(&rkisp1->v4l2_dev); pm_runtime_disable(&pdev->dev); - - return 0; } static struct platform_driver rkisp1_drv = { @@ -649,7 +647,7 @@ static struct platform_driver rkisp1_drv = { .pm = &rkisp1_pm_ops, }, .probe = rkisp1_probe, - .remove = rkisp1_remove, + .remove_new = rkisp1_remove, }; module_platform_driver(rkisp1_drv); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index f76afd8112b2..c15ae0218118 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -123,8 +123,8 @@ rkisp1_rsz_get_pad_fmt(struct rkisp1_resizer *rsz, unsigned int pad, u32 which) { struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg - }; + .pads = rsz->pad_cfg, + }; if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_format(&rsz->sd, sd_state, pad); else @@ -137,8 +137,8 @@ rkisp1_rsz_get_pad_crop(struct rkisp1_resizer *rsz, unsigned int pad, u32 which) { struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg - }; + .pads = rsz->pad_cfg, + }; if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_crop(&rsz->sd, sd_state, pad); else @@ -366,7 +366,7 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config dummy_cfg; struct v4l2_subdev_state pad_state = { .pads = &dummy_cfg - }; + }; u32 pad = code->pad; int ret; @@ -733,8 +733,8 @@ static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) { struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg - }; + .pads = rsz->pad_cfg, + }; static const char * const dev_names[] = { RKISP1_RSZ_MP_DEV_NAME, RKISP1_RSZ_SP_DEV_NAME diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c index b147c645ae0b..1fb34de70649 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -1201,7 +1201,7 @@ err_clk: return ret; } -static int gsc_remove(struct platform_device *pdev) +static void gsc_remove(struct platform_device *pdev) { struct gsc_dev *gsc = platform_get_drvdata(pdev); int i; @@ -1220,7 +1220,6 @@ static int gsc_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); - return 0; } #ifdef CONFIG_PM @@ -1311,7 +1310,7 @@ static const struct dev_pm_ops gsc_pm_ops = { static struct platform_driver gsc_driver = { .probe = gsc_probe, - .remove = gsc_remove, + .remove_new = gsc_remove, .driver = { .name = GSC_MODULE_NAME, .pm = &gsc_pm_ops, diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index e3b95a2b7e04..a0d43bf892e6 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -763,7 +763,10 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_pipeline *p = to_fimc_pipeline(fimc->vid_cap.ve.pipe); struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR]; - struct v4l2_subdev_format sfmt; + struct v4l2_subdev_format sfmt = { + .which = set ? V4L2_SUBDEV_FORMAT_ACTIVE + : V4L2_SUBDEV_FORMAT_TRY, + }; struct v4l2_mbus_framefmt *mf = &sfmt.format; struct media_entity *me; struct fimc_fmt *ffmt; @@ -774,9 +777,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, if (WARN_ON(!sd || !tfmt)) return -EINVAL; - memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; - sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; me = fimc_pipeline_get_head(&sd->entity); @@ -854,7 +855,7 @@ static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor, struct v4l2_plane_pix_format *plane_fmt, unsigned int num_planes, bool try) { - struct v4l2_mbus_frame_desc fd; + struct v4l2_mbus_frame_desc fd = { }; int i, ret; int pad; @@ -1095,7 +1096,12 @@ static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i) */ static int fimc_pipeline_validate(struct fimc_dev *fimc) { - struct v4l2_subdev_format sink_fmt, src_fmt; + struct v4l2_subdev_format sink_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_format src_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct fimc_vid_cap *vc = &fimc->vid_cap; struct v4l2_subdev *sd = &vc->subdev; struct fimc_pipeline *p = to_fimc_pipeline(vc->ve.pipe); @@ -1132,7 +1138,6 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; } else { sink_fmt.pad = sink_pad->index; - sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; @@ -1141,7 +1146,6 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) /* Retrieve format at the source pad */ sd = media_entity_to_v4l2_subdev(src_pad->entity); src_fmt.pad = src_pad->index; - src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index 1791100b6935..a2034ade8b9e 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -1092,7 +1092,7 @@ static int fimc_suspend(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static int fimc_remove(struct platform_device *pdev) +static void fimc_remove(struct platform_device *pdev) { struct fimc_dev *fimc = platform_get_drvdata(pdev); @@ -1108,7 +1108,6 @@ static int fimc_remove(struct platform_device *pdev) fimc_clk_put(fimc); dev_info(&pdev->dev, "driver unloaded\n"); - return 0; } /* S5PV210, S5PC110 */ @@ -1160,7 +1159,7 @@ static const struct dev_pm_ops fimc_pm_ops = { static struct platform_driver fimc_driver = { .probe = fimc_probe, - .remove = fimc_remove, + .remove_new = 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 5d9f4c1cdc5e..7a48fad1df16 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c @@ -54,7 +54,7 @@ const char *fimc_is_param_strerr(unsigned int error) 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 palne (DRC: 3, FD: 1, 2, 3)"; + 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: 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 9dcbb9853ac0..809e117331c0 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h @@ -156,7 +156,7 @@ enum fimc_is_error { ERROR_DMA_INPUT_BIT_WIDTH = 34, /* invalid order(DRC: YYCbCrorYCbYCr, FD:NO,YYCbCr,YCbYCr,CbCr,CrCb) */ ERROR_DMA_INPUT_ORDER = 35, - /* invalid palne (DRC: 3, FD: 1, 2, 3) */ + /* invalid plane (DRC: 3, FD: 1, 2, 3) */ ERROR_DMA_INPUT_PLANE = 36, ERROR_OTF_OUTPUT_NONE = ERROR_COMMON_NONE, 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 83a28ef8e099..bef6e9b4a25e 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c @@ -82,14 +82,12 @@ err_pm_dis: return ret; } -static int fimc_is_i2c_remove(struct platform_device *pdev) +static void fimc_is_i2c_remove(struct platform_device *pdev) { struct fimc_is_i2c *isp_i2c = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); i2c_del_adapter(&isp_i2c->adapter); - - return 0; } #ifdef CONFIG_PM @@ -140,7 +138,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 = fimc_is_i2c_remove, + .remove_new = 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.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index a7704ff069d6..530a148fe4d3 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -915,7 +915,7 @@ static int fimc_is_suspend(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static int fimc_is_remove(struct platform_device *pdev) +static void fimc_is_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fimc_is *is = dev_get_drvdata(dev); @@ -932,8 +932,6 @@ static int fimc_is_remove(struct platform_device *pdev) fimc_is_debugfs_remove(is); release_firmware(is->fw.f_w); fimc_is_free_cpu_memory(is); - - return 0; } static const struct of_device_id fimc_is_of_match[] = { @@ -950,7 +948,7 @@ static const struct dev_pm_ops fimc_is_pm_ops = { static struct platform_driver fimc_is_driver = { .probe = fimc_is_probe, - .remove = fimc_is_remove, + .remove_new = fimc_is_remove, .driver = { .of_match_table = fimc_is_of_match, .name = FIMC_IS_DRV_NAME, 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 f6a302fa8d37..8fa26969c411 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c @@ -449,17 +449,22 @@ static int isp_video_s_fmt_mplane(struct file *file, void *priv, static int isp_video_pipeline_validate(struct fimc_isp *isp) { struct v4l2_subdev *sd = &isp->subdev; - struct v4l2_subdev_format sink_fmt, src_fmt; struct media_pad *pad; int ret; while (1) { + struct v4l2_subdev_format sink_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_format src_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + /* Retrieve format at the sink pad */ pad = &sd->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; sink_fmt.pad = pad->index; - sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; @@ -471,7 +476,6 @@ static int isp_video_pipeline_validate(struct fimc_isp *isp) sd = media_entity_to_v4l2_subdev(pad->entity); src_fmt.pad = pad->index; - src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index e185a40305a8..24b3dda26714 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -765,7 +765,12 @@ static int fimc_lite_s_fmt_mplane(struct file *file, void *priv, static int fimc_pipeline_validate(struct fimc_lite *fimc) { struct v4l2_subdev *sd = &fimc->subdev; - struct v4l2_subdev_format sink_fmt, src_fmt; + struct v4l2_subdev_format sink_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_format src_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct media_pad *pad; int ret; @@ -782,7 +787,6 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code; } else { sink_fmt.pad = pad->index; - sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) @@ -795,7 +799,6 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) sd = media_entity_to_v4l2_subdev(pad->entity); src_fmt.pad = pad->index; - src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; @@ -1595,7 +1598,7 @@ static int fimc_lite_suspend(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static int fimc_lite_remove(struct platform_device *pdev) +static void fimc_lite_remove(struct platform_device *pdev) { struct fimc_lite *fimc = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -1610,7 +1613,6 @@ static int fimc_lite_remove(struct platform_device *pdev) fimc_lite_clk_put(fimc); dev_info(dev, "Driver unloaded\n"); - return 0; } static const struct dev_pm_ops fimc_lite_pm_ops = { @@ -1656,7 +1658,7 @@ MODULE_DEVICE_TABLE(of, flite_of_match); static struct platform_driver fimc_lite_driver = { .probe = fimc_lite_probe, - .remove = fimc_lite_remove, + .remove_new = fimc_lite_remove, .driver = { .of_match_table = flite_of_match, .name = FIMC_LITE_DRV_NAME, diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index 98a60f01129d..c9cb9a216fae 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c @@ -1440,6 +1440,10 @@ static int fimc_md_probe(struct platform_device *pdev) if (!fmd) return -ENOMEM; + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret < 0) + return -ENOMEM; + spin_lock_init(&fmd->slock); INIT_LIST_HEAD(&fmd->pipelines); fmd->pdev = pdev; @@ -1470,10 +1474,8 @@ static int fimc_md_probe(struct platform_device *pdev) goto err_v4l2dev; pinctrl = devm_pinctrl_get(dev); - if (IS_ERR(pinctrl)) { - ret = dev_err_probe(dev, PTR_ERR(pinctrl), "Failed to get pinctrl\n"); - goto err_clk; - } + if (IS_ERR(pinctrl)) + dev_dbg(dev, "Failed to get pinctrl: %pe\n", pinctrl); platform_set_drvdata(pdev, fmd); @@ -1530,12 +1532,12 @@ err_md: return ret; } -static int fimc_md_remove(struct platform_device *pdev) +static void fimc_md_remove(struct platform_device *pdev) { struct fimc_md *fmd = platform_get_drvdata(pdev); if (!fmd) - return 0; + return; fimc_md_unregister_clk_provider(fmd); v4l2_async_nf_unregister(&fmd->subdev_notifier); @@ -1548,8 +1550,6 @@ static int fimc_md_remove(struct platform_device *pdev) media_device_unregister(&fmd->media_dev); media_device_cleanup(&fmd->media_dev); fimc_md_put_clocks(fmd); - - return 0; } static const struct platform_device_id fimc_driver_ids[] __always_unused = { @@ -1566,7 +1566,7 @@ MODULE_DEVICE_TABLE(of, fimc_md_of_match); static struct platform_driver fimc_md_driver = { .probe = fimc_md_probe, - .remove = fimc_md_remove, + .remove_new = 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/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 6a0d35f33e8c..686ca8753ba2 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -975,7 +975,7 @@ static int s5pcsis_runtime_resume(struct device *dev) } #endif -static int s5pcsis_remove(struct platform_device *pdev) +static void s5pcsis_remove(struct platform_device *pdev) { struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct csis_state *state = sd_to_csis_state(sd); @@ -987,8 +987,6 @@ static int s5pcsis_remove(struct platform_device *pdev) s5pcsis_clk_put(state); media_entity_cleanup(&state->sd.entity); - - return 0; } static const struct dev_pm_ops s5pcsis_pm_ops = { @@ -1022,7 +1020,7 @@ MODULE_DEVICE_TABLE(of, s5pcsis_of_match); static struct platform_driver s5pcsis_driver = { .probe = s5pcsis_probe, - .remove = s5pcsis_remove, + .remove_new = 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 db106ebdf870..76634d242b10 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -806,7 +806,9 @@ static int s3c_camif_vidioc_s_fmt(struct file *file, void *priv, /* Only check pixel formats at the sensor and the camif subdev pads */ static int camif_pipeline_validate(struct camif_dev *camif) { - struct v4l2_subdev_format src_fmt; + struct v4l2_subdev_format src_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct media_pad *pad; int ret; @@ -816,7 +818,6 @@ static int camif_pipeline_validate(struct camif_dev *camif) return -EPIPE; src_fmt.pad = pad->index; - src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(camif->sensor.sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index 6e8ef86566b7..afe1fcc37354 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -190,7 +190,9 @@ static int camif_register_sensor(struct camif_dev *camif) struct s3c_camif_sensor_info *sensor = &camif->pdata.sensor; struct v4l2_device *v4l2_dev = &camif->v4l2_dev; struct i2c_adapter *adapter; - struct v4l2_subdev_format format; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *sd; int ret; @@ -220,7 +222,6 @@ static int camif_register_sensor(struct camif_dev *camif) /* Get initial pixel format and set it at the camif sink pad */ format.pad = 0; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format); if (ret < 0) @@ -507,7 +508,7 @@ err_sd: return ret; } -static int s3c_camif_remove(struct platform_device *pdev) +static void s3c_camif_remove(struct platform_device *pdev) { struct camif_dev *camif = platform_get_drvdata(pdev); struct s3c_camif_plat_data *pdata = &camif->pdata; @@ -521,8 +522,6 @@ static int s3c_camif_remove(struct platform_device *pdev) camif_clk_put(camif); s3c_camif_unregister_subdev(camif); pdata->gpio_put(); - - return 0; } static int s3c_camif_runtime_resume(struct device *dev) @@ -623,7 +622,7 @@ static const struct dev_pm_ops s3c_camif_pm_ops = { static struct platform_driver s3c_camif_driver = { .probe = s3c_camif_probe, - .remove = s3c_camif_remove, + .remove_new = 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 dd8864779a7c..89aeba47ed07 100644 --- a/drivers/media/platform/samsung/s5p-g2d/g2d.c +++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c @@ -740,7 +740,7 @@ put_clk: return ret; } -static int g2d_remove(struct platform_device *pdev) +static void g2d_remove(struct platform_device *pdev) { struct g2d_dev *dev = platform_get_drvdata(pdev); @@ -753,7 +753,6 @@ static int g2d_remove(struct platform_device *pdev) clk_put(dev->gate); clk_unprepare(dev->clk); clk_put(dev->clk); - return 0; } static struct g2d_variant g2d_drvdata_v3x = { @@ -778,7 +777,7 @@ MODULE_DEVICE_TABLE(of, exynos_g2d_match); static struct platform_driver g2d_pdrv = { .probe = g2d_probe, - .remove = g2d_remove, + .remove_new = 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 55814041b8d8..c3c7e48f1b6e 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -2991,7 +2991,7 @@ device_register_rollback: return ret; } -static int s5p_jpeg_remove(struct platform_device *pdev) +static void s5p_jpeg_remove(struct platform_device *pdev) { struct s5p_jpeg *jpeg = platform_get_drvdata(pdev); int i; @@ -3008,8 +3008,6 @@ static int s5p_jpeg_remove(struct platform_device *pdev) for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) clk_disable_unprepare(jpeg->clocks[i]); } - - return 0; } #ifdef CONFIG_PM @@ -3164,7 +3162,7 @@ static void *jpeg_get_drv_data(struct device *dev) static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, - .remove = s5p_jpeg_remove, + .remove_new = s5p_jpeg_remove, .driver = { .of_match_table = of_match_ptr(samsung_jpeg_match), .name = S5P_JPEG_M2M_NAME, diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index 9d2cce124a34..e30e54935d79 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -1431,7 +1431,7 @@ err_dma: } /* Remove the driver */ -static int s5p_mfc_remove(struct platform_device *pdev) +static void s5p_mfc_remove(struct platform_device *pdev) { struct s5p_mfc_dev *dev = platform_get_drvdata(pdev); struct s5p_mfc_ctx *ctx; @@ -1463,7 +1463,6 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_unconfigure_dma_memory(dev); s5p_mfc_final_pm(dev); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1690,7 +1689,7 @@ MODULE_DEVICE_TABLE(of, exynos_mfc_match); static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, - .remove = s5p_mfc_remove, + .remove_new = s5p_mfc_remove, .driver = { .name = S5P_MFC_NAME, .pm = &s5p_mfc_pm_ops, diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c index dd74cc43920d..1328b4eb6b9f 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c @@ -1257,7 +1257,7 @@ static const struct dev_pm_ops bdisp_pm_ops = { .runtime_resume = bdisp_runtime_resume, }; -static int bdisp_remove(struct platform_device *pdev) +static void bdisp_remove(struct platform_device *pdev) { struct bdisp_dev *bdisp = platform_get_drvdata(pdev); @@ -1277,8 +1277,6 @@ static int bdisp_remove(struct platform_device *pdev) destroy_workqueue(bdisp->work_queue); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); - - return 0; } static int bdisp_probe(struct platform_device *pdev) @@ -1309,6 +1307,8 @@ static int bdisp_probe(struct platform_device *pdev) init_waitqueue_head(&bdisp->irq_queue); INIT_DELAYED_WORK(&bdisp->timeout_work, bdisp_irq_timeout); bdisp->work_queue = create_workqueue(BDISP_NAME); + if (!bdisp->work_queue) + return -ENOMEM; spin_lock_init(&bdisp->slock); mutex_init(&bdisp->lock); @@ -1411,7 +1411,7 @@ MODULE_DEVICE_TABLE(of, bdisp_match_types); static struct platform_driver bdisp_driver = { .probe = bdisp_probe, - .remove = bdisp_remove, + .remove_new = bdisp_remove, .driver = { .name = BDISP_NAME, .of_match_table = bdisp_match_types, diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index c38b62d4f1ae..45ade7210d26 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -878,7 +878,7 @@ err_clk_disable: return ret; } -static int c8sectpfe_remove(struct platform_device *pdev) +static void c8sectpfe_remove(struct platform_device *pdev) { struct c8sectpfei *fei = platform_get_drvdata(pdev); struct channel_info *channel; @@ -910,8 +910,6 @@ static int c8sectpfe_remove(struct platform_device *pdev) writel(0, fei->io + SYS_OTHER_CLKEN); clk_disable_unprepare(fei->c8sectpfeclk); - - return 0; } @@ -1178,7 +1176,7 @@ static struct platform_driver c8sectpfe_driver = { .of_match_table = of_match_ptr(c8sectpfe_match), }, .probe = c8sectpfe_probe, - .remove = c8sectpfe_remove, + .remove_new = c8sectpfe_remove, }; module_platform_driver(c8sectpfe_driver); diff --git a/drivers/media/platform/st/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c index 03eaee6d15da..da402d1e9171 100644 --- a/drivers/media/platform/st/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c @@ -1900,7 +1900,7 @@ err: return ret; } -static int delta_remove(struct platform_device *pdev) +static void delta_remove(struct platform_device *pdev) { struct delta_dev *delta = platform_get_drvdata(pdev); @@ -1914,8 +1914,6 @@ static int delta_remove(struct platform_device *pdev) pm_runtime_disable(delta->dev); v4l2_device_unregister(&delta->v4l2_dev); - - return 0; } static int delta_runtime_suspend(struct device *dev) @@ -1956,7 +1954,7 @@ MODULE_DEVICE_TABLE(of, delta_match_types); static struct platform_driver delta_driver = { .probe = delta_probe, - .remove = delta_remove, + .remove_new = delta_remove, .driver = { .name = DELTA_NAME, .of_match_table = delta_match_types, diff --git a/drivers/media/platform/st/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c index bb34d6997d99..3a848ca32a0e 100644 --- a/drivers/media/platform/st/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c @@ -1422,7 +1422,7 @@ err: return ret; } -static int hva_remove(struct platform_device *pdev) +static void hva_remove(struct platform_device *pdev) { struct hva_dev *hva = platform_get_drvdata(pdev); struct device *dev = hva_to_dev(hva); @@ -1440,8 +1440,6 @@ static int hva_remove(struct platform_device *pdev) v4l2_device_unregister(&hva->v4l2_dev); dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name); - - return 0; } /* PM ops */ @@ -1461,7 +1459,7 @@ MODULE_DEVICE_TABLE(of, hva_match_types); static struct platform_driver hva_driver = { .probe = hva_probe, - .remove = hva_remove, + .remove_new = hva_remove, .driver = { .name = HVA_NAME, .of_match_table = hva_match_types, diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c index 9706aa41b5d2..92f1edee58f8 100644 --- a/drivers/media/platform/st/stm32/dma2d/dma2d.c +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c @@ -603,7 +603,6 @@ static int dma2d_probe(struct platform_device *pdev) { struct dma2d_dev *dev; struct video_device *vfd; - struct resource *res; int ret = 0; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -614,9 +613,7 @@ static int dma2d_probe(struct platform_device *pdev) mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - dev->regs = devm_ioremap_resource(&pdev->dev, res); + dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(dev->regs)) return PTR_ERR(dev->regs); @@ -696,7 +693,7 @@ put_clk_gate: return ret; } -static int dma2d_remove(struct platform_device *pdev) +static void dma2d_remove(struct platform_device *pdev) { struct dma2d_dev *dev = platform_get_drvdata(pdev); @@ -707,8 +704,6 @@ static int dma2d_remove(struct platform_device *pdev) vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_unprepare(dev->gate); clk_put(dev->gate); - - return 0; } static const struct of_device_id stm32_dma2d_match[] = { @@ -722,7 +717,7 @@ MODULE_DEVICE_TABLE(of, stm32_dma2d_match); static struct platform_driver dma2d_pdrv = { .probe = dma2d_probe, - .remove = dma2d_remove, + .remove_new = dma2d_remove, .driver = { .name = DMA2D_NAME, .of_match_table = stm32_dma2d_match, diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.h b/drivers/media/platform/st/stm32/dma2d/dma2d.h index 3f03a7ca9ee3..af12739fc774 100644 --- a/drivers/media/platform/st/stm32/dma2d/dma2d.h +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.h @@ -90,8 +90,6 @@ struct dma2d_ctx { struct dma2d_frame cap; struct dma2d_frame out; struct dma2d_frame bg; - /* fb_buf always point to bg address */ - struct v4l2_framebuffer fb_buf; /* * MODE[17:16] of DMA2D_CR */ diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index ad8e9742e1ae..dad6e22e4ce4 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2084,6 +2084,7 @@ static int dcmi_probe(struct platform_device *pdev) q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 2; + q->allow_cache_hints = 1; q->dev = &pdev->dev; ret = vb2_queue_init(q); @@ -2134,7 +2135,7 @@ err_media_device_cleanup: return ret; } -static int dcmi_remove(struct platform_device *pdev) +static void dcmi_remove(struct platform_device *pdev) { struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); @@ -2147,8 +2148,6 @@ static int dcmi_remove(struct platform_device *pdev) media_device_cleanup(&dcmi->mdev); dma_release_channel(dcmi->dma_chan); - - return 0; } static __maybe_unused int dcmi_runtime_suspend(struct device *dev) @@ -2202,7 +2201,7 @@ static const struct dev_pm_ops dcmi_pm_ops = { static struct platform_driver stm32_dcmi_driver = { .probe = dcmi_probe, - .remove = dcmi_remove, + .remove_new = dcmi_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(stm32_dcmi_of_match), diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index 86c5235a0c7a..d6e7d1b36083 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -260,7 +260,7 @@ err_clean_pad: return ret; } -static int sun4i_csi_remove(struct platform_device *pdev) +static void sun4i_csi_remove(struct platform_device *pdev) { struct sun4i_csi *csi = platform_get_drvdata(pdev); @@ -271,8 +271,6 @@ static int sun4i_csi_remove(struct platform_device *pdev) media_device_unregister(&csi->mdev); sun4i_csi_dma_unregister(csi); media_device_cleanup(&csi->mdev); - - return 0; } static const struct sun4i_csi_traits sun4i_a10_csi1_traits = { @@ -330,7 +328,7 @@ static const struct dev_pm_ops sun4i_csi_pm_ops = { static struct platform_driver sun4i_csi_driver = { .probe = sun4i_csi_probe, - .remove = sun4i_csi_remove, + .remove_new = sun4i_csi_remove, .driver = { .name = "sun4i-csi", .of_match_table = sun4i_csi_of_match, diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index e3e6650181c8..e2723cfa4515 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -375,7 +375,7 @@ error_resources: return ret; } -static int sun6i_csi_remove(struct platform_device *pdev) +static void sun6i_csi_remove(struct platform_device *pdev) { struct sun6i_csi_device *csi_dev = platform_get_drvdata(pdev); @@ -386,8 +386,6 @@ static int sun6i_csi_remove(struct platform_device *pdev) sun6i_csi_v4l2_cleanup(csi_dev); sun6i_csi_resources_cleanup(csi_dev); - - return 0; } static const struct sun6i_csi_variant sun6i_a31_csi_variant = { @@ -426,7 +424,7 @@ MODULE_DEVICE_TABLE(of, sun6i_csi_of_match); static struct platform_driver sun6i_csi_platform_driver = { .probe = sun6i_csi_probe, - .remove = sun6i_csi_remove, + .remove_new = sun6i_csi_remove, .driver = { .name = SUN6I_CSI_NAME, .of_match_table = of_match_ptr(sun6i_csi_of_match), 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 a220ce849b41..dce130b4c9f6 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 @@ -737,15 +737,13 @@ error_resources: return ret; } -static int sun6i_mipi_csi2_remove(struct platform_device *platform_dev) +static void sun6i_mipi_csi2_remove(struct platform_device *platform_dev) { struct sun6i_mipi_csi2_device *csi2_dev = platform_get_drvdata(platform_dev); sun6i_mipi_csi2_bridge_cleanup(csi2_dev); sun6i_mipi_csi2_resources_cleanup(csi2_dev); - - return 0; } static const struct of_device_id sun6i_mipi_csi2_of_match[] = { @@ -756,7 +754,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 = sun6i_mipi_csi2_remove, + .remove_new = sun6i_mipi_csi2_remove, .driver = { .name = SUN6I_MIPI_CSI2_NAME, .of_match_table = of_match_ptr(sun6i_mipi_csi2_of_match), 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 cd2e92ae2293..23d32e198aaa 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 @@ -804,15 +804,13 @@ error_resources: return ret; } -static int sun8i_a83t_mipi_csi2_remove(struct platform_device *platform_dev) +static void sun8i_a83t_mipi_csi2_remove(struct platform_device *platform_dev) { struct sun8i_a83t_mipi_csi2_device *csi2_dev = platform_get_drvdata(platform_dev); sun8i_a83t_mipi_csi2_bridge_cleanup(csi2_dev); sun8i_a83t_mipi_csi2_resources_cleanup(csi2_dev); - - return 0; } static const struct of_device_id sun8i_a83t_mipi_csi2_of_match[] = { @@ -823,7 +821,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 = sun8i_a83t_mipi_csi2_remove, + .remove_new = sun8i_a83t_mipi_csi2_remove, .driver = { .name = SUN8I_A83T_MIPI_CSI2_NAME, .of_match_table = of_match_ptr(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 aa65d70b6270..e4b0fd793f55 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -906,7 +906,7 @@ err_v4l2: return ret; } -static int deinterlace_remove(struct platform_device *pdev) +static void deinterlace_remove(struct platform_device *pdev) { struct deinterlace_dev *dev = platform_get_drvdata(pdev); @@ -915,8 +915,6 @@ static int deinterlace_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); pm_runtime_force_suspend(&pdev->dev); - - return 0; } static int deinterlace_runtime_resume(struct device *device) @@ -1002,7 +1000,7 @@ static const struct dev_pm_ops deinterlace_pm_ops = { static struct platform_driver deinterlace_driver = { .probe = deinterlace_probe, - .remove = deinterlace_remove, + .remove_new = 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 fbcca59a0517..bd0c4257bbff 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -833,7 +833,7 @@ err_v4l2: return ret; } -static int rotate_remove(struct platform_device *pdev) +static void rotate_remove(struct platform_device *pdev) { struct rotate_dev *dev = platform_get_drvdata(pdev); @@ -842,8 +842,6 @@ static int rotate_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); pm_runtime_force_suspend(&pdev->dev); - - return 0; } static int rotate_runtime_resume(struct device *device) @@ -907,7 +905,7 @@ static const struct dev_pm_ops rotate_pm_ops = { static struct platform_driver rotate_driver = { .probe = rotate_probe, - .remove = rotate_remove, + .remove_new = 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 2dfae9bc0bba..ffe1887cc429 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -1285,13 +1285,13 @@ static int __subdev_get_format(struct vpfe_device *vpfe, struct v4l2_mbus_framefmt *fmt) { struct v4l2_subdev *sd = vpfe->current_subdev->sd; - struct v4l2_subdev_format sd_fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -1309,12 +1309,13 @@ static int __subdev_set_format(struct vpfe_device *vpfe, struct v4l2_mbus_framefmt *fmt) { struct v4l2_subdev *sd = vpfe->current_subdev->sd; - struct v4l2_subdev_format sd_fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; *mbus_fmt = *fmt; ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt); @@ -1393,7 +1394,9 @@ static int vpfe_try_fmt(struct file *file, void *priv, struct vpfe_device *vpfe = video_drvdata(file); struct v4l2_subdev *sd = vpfe->current_subdev->sd; const struct vpfe_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret, found; fmt = find_format_by_pix(vpfe, f->fmt.pix.pixelformat); @@ -1412,7 +1415,6 @@ static int vpfe_try_fmt(struct file *file, void *priv, found = false; fse.pad = 0; fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); @@ -1499,7 +1501,9 @@ static int vpfe_enum_size(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { struct vpfe_device *vpfe = video_drvdata(file); - struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *sd = vpfe->current_subdev->sd; struct vpfe_fmt *fmt; int ret; @@ -1514,11 +1518,9 @@ static int vpfe_enum_size(struct file *file, void *priv, memset(fsize->reserved, 0x0, sizeof(fsize->reserved)); - memset(&fse, 0x0, sizeof(fse)); fse.index = fsize->index; fse.pad = 0; fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -2146,7 +2148,6 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, { struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, struct vpfe_device, v4l2_dev); - struct v4l2_subdev_mbus_code_enum mbus_code; struct vpfe_subdev_info *sdinfo; struct vpfe_fmt *fmt; int ret = 0; @@ -2173,9 +2174,11 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, vpfe->num_active_fmt = 0; for (j = 0, i = 0; (ret != -EINVAL); ++j) { - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = j, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &mbus_code); if (ret) @@ -2483,7 +2486,7 @@ probe_out_cleanup: /* * vpfe_remove : It un-register device from V4L2 driver */ -static int vpfe_remove(struct platform_device *pdev) +static void vpfe_remove(struct platform_device *pdev) { struct vpfe_device *vpfe = platform_get_drvdata(pdev); @@ -2493,8 +2496,6 @@ static int vpfe_remove(struct platform_device *pdev) v4l2_async_nf_cleanup(&vpfe->notifier); v4l2_device_unregister(&vpfe->v4l2_dev); video_unregister_device(&vpfe->video_dev); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -2625,7 +2626,7 @@ MODULE_DEVICE_TABLE(of, vpfe_of_match); static struct platform_driver vpfe_driver = { .probe = vpfe_probe, - .remove = vpfe_remove, + .remove_new = vpfe_remove, .driver = { .name = VPFE_MODULE_NAME, .pm = &vpfe_pm_ops, diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 4eade409d5d3..ca906a9e4222 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -117,13 +117,13 @@ static int cal_legacy_enum_fmt_vid_cap(struct file *file, void *priv, static int __subdev_get_format(struct cal_ctx *ctx, struct v4l2_mbus_framefmt *fmt) { - struct v4l2_subdev_format sd_fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -139,12 +139,13 @@ static int __subdev_get_format(struct cal_ctx *ctx, static int __subdev_set_format(struct cal_ctx *ctx, struct v4l2_mbus_framefmt *fmt) { - struct v4l2_subdev_format sd_fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; *mbus_fmt = *fmt; ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt); @@ -190,7 +191,9 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, { struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; - struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int found; fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); @@ -209,7 +212,6 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, found = false; fse.pad = 0; fse.code = fmtinfo->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { int ret; @@ -302,7 +304,11 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh, { struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; - struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .pad = 0, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; /* check for valid format */ @@ -313,10 +319,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh, return -EINVAL; } - fse.index = fsize->index; - fse.pad = 0; fse.code = fmtinfo->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, &fse); @@ -811,7 +814,6 @@ static const struct v4l2_file_operations cal_fops = { static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { - struct v4l2_subdev_mbus_code_enum mbus_code; struct v4l2_mbus_framefmt mbus_fmt; const struct cal_format_info *fmtinfo; unsigned int i, j, k; @@ -826,10 +828,11 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) ctx->num_active_fmt = 0; for (j = 0, i = 0; ; ++j) { + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = j, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code, NULL, &mbus_code); if (ret == -EINVAL) diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 1236215ec70e..9c5105223d6b 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -1293,7 +1293,7 @@ error_pm_runtime: return ret; } -static int cal_remove(struct platform_device *pdev) +static void cal_remove(struct platform_device *pdev) { struct cal_dev *cal = platform_get_drvdata(pdev); unsigned int i; @@ -1319,8 +1319,6 @@ static int cal_remove(struct platform_device *pdev) if (ret >= 0) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } static int cal_runtime_resume(struct device *dev) @@ -1364,7 +1362,7 @@ static const struct dev_pm_ops cal_pm_ops = { static struct platform_driver cal_pdrv = { .probe = cal_probe, - .remove = cal_remove, + .remove_new = 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 832489822706..63cdfed37bc9 100644 --- a/drivers/media/platform/ti/davinci/vpif.c +++ b/drivers/media/platform/ti/davinci/vpif.c @@ -538,7 +538,7 @@ err_put_rpm: return ret; } -static int vpif_remove(struct platform_device *pdev) +static void vpif_remove(struct platform_device *pdev) { struct vpif_data *data = platform_get_drvdata(pdev); @@ -551,8 +551,6 @@ static int vpif_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); kfree(data); - - return 0; } #ifdef CONFIG_PM @@ -592,7 +590,7 @@ static struct platform_driver vpif_driver = { .name = VPIF_DRIVER_NAME, .pm = vpif_pm_ops, }, - .remove = vpif_remove, + .remove_new = 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 580723333fcc..44d269d6038c 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1714,7 +1714,7 @@ cleanup: * * The vidoe device is unregistered */ -static int vpif_remove(struct platform_device *device) +static void vpif_remove(struct platform_device *device) { struct channel_obj *ch; int i; @@ -1732,7 +1732,6 @@ static int vpif_remove(struct platform_device *device) video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); } - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1815,7 +1814,7 @@ static __refdata struct platform_driver vpif_driver = { .pm = &vpif_pm_ops, }, .probe = vpif_probe, - .remove = vpif_remove, + .remove_new = 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 b2df81603f62..f8ec2991c667 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -1305,7 +1305,7 @@ vpif_free: /* * vpif_remove: It un-register channels from V4L2 driver */ -static int vpif_remove(struct platform_device *device) +static void vpif_remove(struct platform_device *device) { struct channel_obj *ch; int i; @@ -1321,8 +1321,6 @@ static int vpif_remove(struct platform_device *device) video_unregister_device(&ch->video_dev); } free_vpif_objs(); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1399,7 +1397,7 @@ static __refdata struct platform_driver vpif_driver = { .pm = &vpif_pm_ops, }, .probe = vpif_probe, - .remove = vpif_remove, + .remove_new = vpif_remove, }; module_platform_driver(vpif_driver); diff --git a/drivers/media/platform/ti/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c index 3e0d9af7ffec..4143274089c3 100644 --- a/drivers/media/platform/ti/omap/omap_vout.c +++ b/drivers/media/platform/ti/omap/omap_vout.c @@ -1569,7 +1569,7 @@ static void omap_vout_cleanup_device(struct omap_vout_device *vout) kfree(vout); } -static int omap_vout_remove(struct platform_device *pdev) +static void omap_vout_remove(struct platform_device *pdev) { int k; struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); @@ -1587,7 +1587,6 @@ static int omap_vout_remove(struct platform_device *pdev) omap_dss_put_device(vid_dev->displays[k]); } kfree(vid_dev); - return 0; } static int __init omap_vout_probe(struct platform_device *pdev) @@ -1721,7 +1720,7 @@ static struct platform_driver omap_vout_driver = { .driver = { .name = VOUT_NAME, }, - .remove = omap_vout_remove, + .remove_new = omap_vout_remove, }; static int __init omap_vout_init(void) diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index e7327e38482d..f3aaa9e76492 100644 --- a/drivers/media/platform/ti/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c @@ -1997,7 +1997,7 @@ error: * * Always returns 0. */ -static int isp_remove(struct platform_device *pdev) +static void isp_remove(struct platform_device *pdev) { struct isp_device *isp = platform_get_drvdata(pdev); @@ -2014,8 +2014,6 @@ static int isp_remove(struct platform_device *pdev) v4l2_async_nf_cleanup(&isp->notifier); kfree(isp); - - return 0; } enum isp_of_phy { @@ -2476,7 +2474,7 @@ MODULE_DEVICE_TABLE(of, omap3isp_of_table); static struct platform_driver omap3isp_driver = { .probe = isp_probe, - .remove = isp_remove, + .remove_new = isp_remove, .id_table = omap3isp_id_table, .driver = { .name = "omap3isp", diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 11afb8aec292..fdcdffe5fecb 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1118,7 +1118,9 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; const struct isp_format_info *fmt_info; - struct v4l2_subdev_format fmt_src; + struct v4l2_subdev_format fmt_src = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; unsigned int depth_out; unsigned int depth_in = 0; struct media_pad *pad; @@ -1150,7 +1152,6 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) * input format is a non-BT.656 YUV variant. */ fmt_src.pad = pad->index; - fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) { fmt_info = omap3isp_video_format_info(fmt_src.format.code); depth_in = fmt_info->width; diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index ddc7d08d4f96..daca689dc082 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -268,7 +268,9 @@ static int isp_video_get_graph_data(struct isp_video *video, static int __isp_video_get_format(struct isp_video *video, struct v4l2_format *format) { - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; u32 pad; int ret; @@ -278,7 +280,6 @@ __isp_video_get_format(struct isp_video *video, struct v4l2_format *format) return -EINVAL; fmt.pad = pad; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; mutex_lock(&video->mutex); ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); @@ -731,7 +732,9 @@ static int isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) { struct isp_video *video = video_drvdata(file); - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; u32 pad; int ret; @@ -746,7 +749,6 @@ isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); fmt.pad = pad; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); if (ret) return ret == -ENOIOCTLCMD ? -ENOTTY : ret; @@ -759,7 +761,9 @@ static int isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct isp_video *video = video_drvdata(file); - struct v4l2_subdev_format format; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; struct v4l2_subdev_selection sdsel = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -799,7 +803,6 @@ isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) return ret; format.pad = pad; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); if (ret < 0) return ret == -ENOIOCTLCMD ? -ENOTTY : ret; @@ -957,7 +960,9 @@ static int isp_video_check_external_subdevs(struct isp_video *video, struct media_pad *source_pad; struct media_entity *source = NULL; struct media_entity *sink; - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_ext_controls ctrls; struct v4l2_ext_control ctrl; unsigned int i; @@ -993,7 +998,6 @@ static int isp_video_check_external_subdevs(struct isp_video *video, pipe->external = media_entity_to_v4l2_subdev(source); fmt.pad = source_pad->index; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink), pad, get_fmt, NULL, &fmt); if (unlikely(ret < 0)) { diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index 5b1c5d96a407..6848cbc82f52 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -2622,7 +2622,7 @@ v4l2_dev_unreg: return ret; } -static int vpe_remove(struct platform_device *pdev) +static void vpe_remove(struct platform_device *pdev) { struct vpe_dev *dev = platform_get_drvdata(pdev); @@ -2635,8 +2635,6 @@ static int vpe_remove(struct platform_device *pdev) vpe_set_clock_enable(dev, 0); vpe_runtime_put(pdev); pm_runtime_disable(&pdev->dev); - - return 0; } #if defined(CONFIG_OF) @@ -2651,7 +2649,7 @@ MODULE_DEVICE_TABLE(of, vpe_of_match); static struct platform_driver vpe_pdrv = { .probe = vpe_probe, - .remove = vpe_remove, + .remove_new = vpe_remove, .driver = { .name = VPE_MODULE_NAME, .of_match_table = of_match_ptr(vpe_of_match), diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index b0aeedae7b65..09c74a573ddb 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -251,11 +251,6 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) { - struct hantro_ctx *ctx; - - ctx = container_of(ctrl->handler, - struct hantro_ctx, ctrl_handler); - if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; @@ -274,8 +269,6 @@ static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) /* Only 8-bit and 10-bit are supported */ return -EINVAL; - - ctx->bit_depth = sps->bit_depth_luma_minus8 + 8; } else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) { const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame; @@ -314,9 +307,38 @@ static int hantro_vp9_s_ctrl(struct v4l2_ctrl *ctrl) struct hantro_ctx, ctrl_handler); switch (ctrl->id) { - case V4L2_CID_STATELESS_VP9_FRAME: - ctx->bit_depth = ctrl->p_new.p_vp9_frame->bit_depth; - break; + case V4L2_CID_STATELESS_VP9_FRAME: { + int bit_depth = ctrl->p_new.p_vp9_frame->bit_depth; + + if (ctx->bit_depth == bit_depth) + return 0; + + return hantro_reset_raw_fmt(ctx, bit_depth); + } + default: + return -EINVAL; + } + + return 0; +} + +static int hantro_hevc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hantro_ctx *ctx; + + ctx = container_of(ctrl->handler, + struct hantro_ctx, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_STATELESS_HEVC_SPS: { + const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; + int bit_depth = sps->bit_depth_luma_minus8 + 8; + + if (ctx->bit_depth == bit_depth) + return 0; + + return hantro_reset_raw_fmt(ctx, bit_depth); + } default: return -EINVAL; } @@ -336,6 +358,11 @@ static const struct v4l2_ctrl_ops hantro_vp9_ctrl_ops = { .s_ctrl = hantro_vp9_s_ctrl, }; +static const struct v4l2_ctrl_ops hantro_hevc_ctrl_ops = { + .try_ctrl = hantro_try_ctrl, + .s_ctrl = hantro_hevc_s_ctrl, +}; + #define HANTRO_JPEG_ACTIVE_MARKERS (V4L2_JPEG_ACTIVE_MARKER_APP0 | \ V4L2_JPEG_ACTIVE_MARKER_COM | \ V4L2_JPEG_ACTIVE_MARKER_DQT | \ @@ -470,7 +497,7 @@ static const struct hantro_ctrl controls[] = { .codec = HANTRO_HEVC_DECODER, .cfg = { .id = V4L2_CID_STATELESS_HEVC_SPS, - .ops = &hantro_ctrl_ops, + .ops = &hantro_hevc_ctrl_ops, }, }, { .codec = HANTRO_HEVC_DECODER, @@ -945,7 +972,7 @@ static int hantro_probe(struct platform_device *pdev) return PTR_ERR(vpu->clocks[0].clk); } - vpu->resets = devm_reset_control_array_get(&pdev->dev, false, true); + vpu->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev); if (IS_ERR(vpu->resets)) return PTR_ERR(vpu->resets); @@ -1093,7 +1120,7 @@ err_pm_disable: return ret; } -static int hantro_remove(struct platform_device *pdev) +static void hantro_remove(struct platform_device *pdev) { struct hantro_dev *vpu = platform_get_drvdata(pdev); @@ -1109,7 +1136,6 @@ static int hantro_remove(struct platform_device *pdev) reset_control_assert(vpu->resets); pm_runtime_dont_use_autosuspend(vpu->dev); pm_runtime_disable(vpu->dev); - return 0; } #ifdef CONFIG_PM @@ -1132,7 +1158,7 @@ static const struct dev_pm_ops hantro_pm_ops = { static struct platform_driver hantro_driver = { .probe = hantro_probe, - .remove = hantro_remove, + .remove_new = hantro_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(of_hantro_match), diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 09d8cf942689..6437423ccf3a 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -197,7 +197,7 @@ int hantro_postproc_alloc(struct hantro_ctx *ctx) unsigned int i, buf_size; /* this should always pick native format */ - fmt = hantro_get_default_fmt(ctx, false); + fmt = hantro_get_default_fmt(ctx, false, ctx->bit_depth); if (!fmt) return -EINVAL; v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index c0d427956210..835518534e3b 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -28,6 +28,8 @@ #include "hantro_hw.h" #include "hantro_v4l2.h" +#define HANTRO_DEFAULT_BIT_DEPTH 8 + static int hantro_set_fmt_out(struct hantro_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp); static int hantro_set_fmt_cap(struct hantro_ctx *ctx, @@ -76,18 +78,13 @@ int hantro_get_format_depth(u32 fourcc) } static bool -hantro_check_depth_match(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt) +hantro_check_depth_match(const struct hantro_fmt *fmt, int bit_depth) { - int fmt_depth, ctx_depth = 8; + int fmt_depth; if (!fmt->match_depth && !fmt->postprocessed) return true; - /* 0 means default depth, which is 8 */ - if (ctx->bit_depth) - ctx_depth = ctx->bit_depth; - fmt_depth = hantro_get_format_depth(fmt->fourcc); /* @@ -95,9 +92,9 @@ hantro_check_depth_match(const struct hantro_ctx *ctx, * It may be possible to relax that on some HW. */ if (!fmt->match_depth) - return fmt_depth <= ctx_depth; + return fmt_depth <= bit_depth; - return fmt_depth == ctx_depth; + return fmt_depth == bit_depth; } static const struct hantro_fmt * @@ -119,7 +116,7 @@ hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc) } const struct hantro_fmt * -hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream) +hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream, int bit_depth) { const struct hantro_fmt *formats; unsigned int i, num_fmts; @@ -128,7 +125,7 @@ hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream) for (i = 0; i < num_fmts; i++) { if (bitstream == (formats[i].codec_mode != HANTRO_MODE_NONE) && - hantro_check_depth_match(ctx, &formats[i])) + hantro_check_depth_match(&formats[i], bit_depth)) return &formats[i]; } return NULL; @@ -204,7 +201,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, if (skip_mode_none == mode_none) continue; - if (!hantro_check_depth_match(ctx, fmt)) + if (!hantro_check_depth_match(fmt, ctx->bit_depth)) continue; if (j == f->index) { f->pixelformat = fmt->fourcc; @@ -224,7 +221,7 @@ 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(ctx, fmt)) + if (!hantro_check_depth_match(fmt, ctx->bit_depth)) continue; if (j == f->index) { f->pixelformat = fmt->fourcc; @@ -278,7 +275,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp, enum v4l2_buf_type type) { - const struct hantro_fmt *fmt, *vpu_fmt; + const struct hantro_fmt *fmt; bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; @@ -292,17 +289,13 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, fmt = hantro_find_format(ctx, pix_mp->pixelformat); if (!fmt) { - fmt = hantro_get_default_fmt(ctx, coded); + fmt = hantro_get_default_fmt(ctx, coded, HANTRO_DEFAULT_BIT_DEPTH); pix_mp->pixelformat = fmt->fourcc; } if (coded) { pix_mp->num_planes = 1; - vpu_fmt = fmt; - } else if (ctx->is_encoder) { - vpu_fmt = ctx->vpu_dst_fmt; - } else { - vpu_fmt = fmt; + } else if (!ctx->is_encoder) { /* * Width/height on the CAPTURE end of a decoder are ignored and * replaced by the OUTPUT ones. @@ -314,7 +307,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, pix_mp->field = V4L2_FIELD_NONE; v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, - &vpu_fmt->frmsize); + &fmt->frmsize); if (!coded) { /* Fill remaining fields */ @@ -378,58 +371,55 @@ static void hantro_reset_encoded_fmt(struct hantro_ctx *ctx) { const struct hantro_fmt *vpu_fmt; - struct v4l2_pix_format_mplane *fmt; - - vpu_fmt = hantro_get_default_fmt(ctx, true); + struct v4l2_pix_format_mplane fmt; - if (ctx->is_encoder) { - ctx->vpu_dst_fmt = vpu_fmt; - fmt = &ctx->dst_fmt; - } else { - ctx->vpu_src_fmt = vpu_fmt; - fmt = &ctx->src_fmt; - } + vpu_fmt = hantro_get_default_fmt(ctx, true, HANTRO_DEFAULT_BIT_DEPTH); + if (!vpu_fmt) + return; - hantro_reset_fmt(fmt, vpu_fmt); - fmt->width = vpu_fmt->frmsize.min_width; - fmt->height = vpu_fmt->frmsize.min_height; + hantro_reset_fmt(&fmt, vpu_fmt); + fmt.width = vpu_fmt->frmsize.min_width; + fmt.height = vpu_fmt->frmsize.min_height; if (ctx->is_encoder) - hantro_set_fmt_cap(ctx, fmt); + hantro_set_fmt_cap(ctx, &fmt); else - hantro_set_fmt_out(ctx, fmt); + hantro_set_fmt_out(ctx, &fmt); } -static void -hantro_reset_raw_fmt(struct hantro_ctx *ctx) +int +hantro_reset_raw_fmt(struct hantro_ctx *ctx, int bit_depth) { const struct hantro_fmt *raw_vpu_fmt; - struct v4l2_pix_format_mplane *raw_fmt, *encoded_fmt; + struct v4l2_pix_format_mplane raw_fmt, *encoded_fmt; + int ret; - raw_vpu_fmt = hantro_get_default_fmt(ctx, false); + raw_vpu_fmt = hantro_get_default_fmt(ctx, false, bit_depth); + if (!raw_vpu_fmt) + return -EINVAL; - if (ctx->is_encoder) { - ctx->vpu_src_fmt = raw_vpu_fmt; - raw_fmt = &ctx->src_fmt; + if (ctx->is_encoder) encoded_fmt = &ctx->dst_fmt; - } else { - ctx->vpu_dst_fmt = raw_vpu_fmt; - raw_fmt = &ctx->dst_fmt; + else encoded_fmt = &ctx->src_fmt; - } - hantro_reset_fmt(raw_fmt, raw_vpu_fmt); - raw_fmt->width = encoded_fmt->width; - raw_fmt->height = encoded_fmt->height; + hantro_reset_fmt(&raw_fmt, raw_vpu_fmt); + raw_fmt.width = encoded_fmt->width; + raw_fmt.height = encoded_fmt->height; if (ctx->is_encoder) - hantro_set_fmt_out(ctx, raw_fmt); + ret = hantro_set_fmt_out(ctx, &raw_fmt); else - hantro_set_fmt_cap(ctx, raw_fmt); + ret = hantro_set_fmt_cap(ctx, &raw_fmt); + + if (!ret) + ctx->bit_depth = bit_depth; + + return ret; } void hantro_reset_fmts(struct hantro_ctx *ctx) { hantro_reset_encoded_fmt(ctx); - hantro_reset_raw_fmt(ctx); + hantro_reset_raw_fmt(ctx, HANTRO_DEFAULT_BIT_DEPTH); } static void @@ -529,7 +519,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, * changes to the raw format. */ if (!ctx->is_encoder) - hantro_reset_raw_fmt(ctx); + hantro_reset_raw_fmt(ctx, hantro_get_format_depth(pix_mp->pixelformat)); /* Colorimetry information are always propagated. */ ctx->dst_fmt.colorspace = pix_mp->colorspace; @@ -592,7 +582,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, * changes to the raw format. */ if (ctx->is_encoder) - hantro_reset_raw_fmt(ctx); + hantro_reset_raw_fmt(ctx, HANTRO_DEFAULT_BIT_DEPTH); /* Colorimetry information are always propagated. */ ctx->src_fmt.colorspace = pix_mp->colorspace; diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.h b/drivers/media/platform/verisilicon/hantro_v4l2.h index 64f6f57e9d7a..9ea2fef57dcd 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.h +++ b/drivers/media/platform/verisilicon/hantro_v4l2.h @@ -21,9 +21,10 @@ extern const struct v4l2_ioctl_ops hantro_ioctl_ops; extern const struct vb2_ops hantro_queue_ops; +int hantro_reset_raw_fmt(struct hantro_ctx *ctx, int bit_depth); void hantro_reset_fmts(struct hantro_ctx *ctx); int hantro_get_format_depth(u32 fourcc); const struct hantro_fmt * -hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream); +hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream, int bit_depth); #endif /* HANTRO_V4L2_H_ */ diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c index b390228fd3b4..f850d8bddef6 100644 --- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c +++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c @@ -152,6 +152,7 @@ static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV12, .codec_mode = HANTRO_MODE_NONE, + .match_depth = true, .postprocessed = true, .frmsize = { .min_width = FMT_MIN_WIDTH, @@ -165,6 +166,7 @@ static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = { { .fourcc = V4L2_PIX_FMT_P010, .codec_mode = HANTRO_MODE_NONE, + .match_depth = true, .postprocessed = true, .frmsize = { .min_width = FMT_MIN_WIDTH, diff --git a/drivers/media/platform/via/via-camera.c b/drivers/media/platform/via/via-camera.c index 95483c84c3f2..450254403fa8 100644 --- a/drivers/media/platform/via/via-camera.c +++ b/drivers/media/platform/via/via-camera.c @@ -845,8 +845,8 @@ static int viacam_do_try_fmt(struct via_camera *cam, int ret; struct v4l2_subdev_pad_config pad_cfg; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; + .pads = &pad_cfg, + }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1208,7 +1208,9 @@ static int viacam_probe(struct platform_device *pdev) * Convince the system that we can do DMA. */ pdev->dev.dma_mask = &viadev->pdev->dma_mask; - dma_set_mask(&pdev->dev, 0xffffffff); + ret = dma_set_mask(&pdev->dev, 0xffffffff); + if (ret) + goto out_ctrl_hdl_free; /* * Fire up the capture port. The write to 0x78 looks purely * OLPCish; any system will need to tweak 0x1e. @@ -1294,7 +1296,7 @@ out_free: return ret; } -static int viacam_remove(struct platform_device *pdev) +static void viacam_remove(struct platform_device *pdev) { struct via_camera *cam = via_cam_info; struct viafb_dev *viadev = pdev->dev.platform_data; @@ -1309,7 +1311,6 @@ static int viacam_remove(struct platform_device *pdev) v4l2_ctrl_handler_free(&cam->ctrl_handler); kfree(cam); via_cam_info = NULL; - return 0; } static struct platform_driver viacam_driver = { @@ -1317,7 +1318,7 @@ static struct platform_driver viacam_driver = { .name = "viafb-camera", }, .probe = viacam_probe, - .remove = viacam_remove, + .remove_new = viacam_remove, }; module_platform_driver(viacam_driver); diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 71d97042a470..1d9f32e5a917 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -481,7 +481,7 @@ static int video_mux_probe(struct platform_device *pdev) return ret; } -static int video_mux_remove(struct platform_device *pdev) +static void video_mux_remove(struct platform_device *pdev) { struct video_mux *vmux = platform_get_drvdata(pdev); struct v4l2_subdev *sd = &vmux->subdev; @@ -490,8 +490,6 @@ static int video_mux_remove(struct platform_device *pdev) v4l2_async_nf_cleanup(&vmux->notifier); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); - - return 0; } static const struct of_device_id video_mux_dt_ids[] = { @@ -502,7 +500,7 @@ MODULE_DEVICE_TABLE(of, video_mux_dt_ids); static struct platform_driver video_mux_driver = { .probe = video_mux_probe, - .remove = video_mux_remove, + .remove_new = 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 d8a23f18cfbc..5b53745fe44e 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -1059,7 +1059,7 @@ err_clk_put: return ret; } -static int xcsi2rxss_remove(struct platform_device *pdev) +static void xcsi2rxss_remove(struct platform_device *pdev) { struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev); struct v4l2_subdev *subdev = &xcsi2rxss->subdev; @@ -1070,8 +1070,6 @@ static int xcsi2rxss_remove(struct platform_device *pdev) mutex_destroy(&xcsi2rxss->lock); clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); clk_bulk_put(num_clks, xcsi2rxss->clks); - - return 0; } static const struct of_device_id xcsi2rxss_of_id_table[] = { @@ -1086,7 +1084,7 @@ static struct platform_driver xcsi2rxss_driver = { .of_match_table = xcsi2rxss_of_id_table, }, .probe = xcsi2rxss_probe, - .remove = xcsi2rxss_remove, + .remove_new = 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 fee02c8c85fd..80d6f5b072ea 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -56,7 +56,9 @@ xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) static int xvip_dma_verify_format(struct xvip_dma *dma) { - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; int ret; @@ -64,7 +66,6 @@ static int xvip_dma_verify_format(struct xvip_dma *dma) if (subdev == NULL) return -EPIPE; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret == -ENOIOCTLCMD ? -EINVAL : ret; diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 0f2d5a0edf0c..80353ca44402 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -894,7 +894,7 @@ error_resource: return ret; } -static int xtpg_remove(struct platform_device *pdev) +static void xtpg_remove(struct platform_device *pdev) { struct xtpg_device *xtpg = platform_get_drvdata(pdev); struct v4l2_subdev *subdev = &xtpg->xvip.subdev; @@ -904,8 +904,6 @@ static int xtpg_remove(struct platform_device *pdev) media_entity_cleanup(&subdev->entity); xvip_cleanup_resources(&xtpg->xvip); - - return 0; } static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); @@ -923,7 +921,7 @@ static struct platform_driver xtpg_driver = { .of_match_table = xtpg_of_id_table, }, .probe = xtpg_probe, - .remove = xtpg_remove, + .remove_new = 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 0a16c218a50a..3123216b3f70 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -617,14 +617,12 @@ error: return ret; } -static int xvip_composite_remove(struct platform_device *pdev) +static void xvip_composite_remove(struct platform_device *pdev) { struct xvip_composite_device *xdev = platform_get_drvdata(pdev); xvip_graph_cleanup(xdev); xvip_composite_v4l2_cleanup(xdev); - - return 0; } static const struct of_device_id xvip_composite_of_id_table[] = { @@ -639,7 +637,7 @@ static struct platform_driver xvip_composite_driver = { .of_match_table = xvip_composite_of_id_table, }, .probe = xvip_composite_probe, - .remove = xvip_composite_remove, + .remove_new = 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 0ae0208d7529..dda70719f004 100644 --- a/drivers/media/platform/xilinx/xilinx-vtc.c +++ b/drivers/media/platform/xilinx/xilinx-vtc.c @@ -254,7 +254,7 @@ struct xvtc_device *xvtc_of_get(struct device_node *np) struct xvtc_device *found = NULL; struct xvtc_device *xvtc; - if (!of_find_property(np, "xlnx,vtc", NULL)) + if (!of_property_present(np, "xlnx,vtc")) return NULL; xvtc_node = of_parse_phandle(np, "xlnx,vtc", 0); @@ -344,15 +344,13 @@ static int xvtc_probe(struct platform_device *pdev) return 0; } -static int xvtc_remove(struct platform_device *pdev) +static void xvtc_remove(struct platform_device *pdev) { struct xvtc_device *xvtc = platform_get_drvdata(pdev); xvtc_unregister_device(xvtc); xvip_cleanup_resources(&xvtc->xvip); - - return 0; } static const struct of_device_id xvtc_of_id_table[] = { @@ -367,7 +365,7 @@ static struct platform_driver xvtc_driver = { .of_match_table = xvtc_of_id_table, }, .probe = xvtc_probe, - .remove = xvtc_remove, + .remove_new = xvtc_remove, }; module_platform_driver(xvtc_driver); |