diff options
Diffstat (limited to 'drivers/media/platform/nxp/imx8-isi')
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 143 | ||||
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h | 20 | ||||
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 18 | ||||
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 2 | ||||
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 292 | ||||
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 2 | ||||
-rw-r--r-- | drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 156 |
7 files changed, 295 insertions, 338 deletions
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 1e79b1211b60..adc8d9960bf0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -3,6 +3,7 @@ * Copyright 2019-2020 NXP */ +#include <linux/bits.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/errno.h> @@ -245,26 +246,41 @@ static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi) /* Panic will assert when the buffers are 50% full */ -/* For i.MX8QXP C0 and i.MX8MN ISI IER version */ +/* For 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 }, + .oflw_y_buf_en = { .mask = BIT(19) }, + .oflw_u_buf_en = { .mask = BIT(21) }, + .oflw_v_buf_en = { .mask = BIT(23) }, - .panic_y_buf_en = {.offset = 20, .mask = 0x100000 }, - .panic_u_buf_en = {.offset = 22, .mask = 0x400000 }, - .panic_v_buf_en = {.offset = 24, .mask = 0x1000000 }, + .panic_y_buf_en = { .mask = BIT(20) }, + .panic_u_buf_en = { .mask = BIT(22) }, + .panic_v_buf_en = { .mask = BIT(24) }, }; -/* For i.MX8MP ISI IER version */ +/* For i.MX8QXP C0 and 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 }, + .oflw_y_buf_en = { .mask = BIT(18) }, + .oflw_u_buf_en = { .mask = BIT(20) }, + .oflw_v_buf_en = { .mask = BIT(22) }, - .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_y_buf_en = { .mask = BIT(19) }, + .panic_u_buf_en = { .mask = BIT(21) }, + .panic_v_buf_en = { .mask = BIT(23) }, +}; + +/* For i.MX8QM ISI IER version */ +static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_qm = { + .oflw_y_buf_en = { .mask = BIT(16) }, + .oflw_u_buf_en = { .mask = BIT(19) }, + .oflw_v_buf_en = { .mask = BIT(22) }, + + .excs_oflw_y_buf_en = { .mask = BIT(17) }, + .excs_oflw_u_buf_en = { .mask = BIT(20) }, + .excs_oflw_v_buf_en = { .mask = BIT(23) }, + + .panic_y_buf_en = { .mask = BIT(18) }, + .panic_u_buf_en = { .mask = BIT(21) }, + .panic_v_buf_en = { .mask = BIT(24) }, }; /* Panic will assert when the buffers are 50% full */ @@ -274,11 +290,6 @@ static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1 = { .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, @@ -286,8 +297,6 @@ static const struct mxc_isi_plat_data mxc_imx8mn_data = { .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, .gasket_ops = &mxc_imx8_gasket_ops, .has_36bit_dma = false, @@ -300,8 +309,6 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .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, .gasket_ops = &mxc_imx8_gasket_ops, .has_36bit_dma = true, @@ -314,8 +321,6 @@ static const struct mxc_isi_plat_data mxc_imx8ulp_data = { .reg_offset = 0x0, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, - .clks = mxc_imx8mn_clks, - .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = true, .has_36bit_dma = false, }; @@ -327,13 +332,33 @@ static const struct mxc_isi_plat_data mxc_imx93_data = { .reg_offset = 0, .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, .gasket_ops = &mxc_imx93_gasket_ops, .has_36bit_dma = false, }; +static const struct mxc_isi_plat_data mxc_imx8qm_data = { + .model = MXC_ISI_IMX8QM, + .num_ports = 5, + .num_channels = 8, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_qm, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + +static const struct mxc_isi_plat_data mxc_imx8qxp_data = { + .model = MXC_ISI_IMX8QXP, + .num_ports = 5, + .num_channels = 6, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + /* ----------------------------------------------------------------------------- * Power management */ @@ -349,6 +374,8 @@ static int mxc_isi_pm_suspend(struct device *dev) mxc_isi_video_suspend(pipe); } + mxc_isi_m2m_suspend(&isi->m2m); + return pm_runtime_force_suspend(dev); } @@ -378,6 +405,12 @@ static int mxc_isi_pm_resume(struct device *dev) } } + ret = mxc_isi_m2m_resume(&isi->m2m); + if (ret) { + dev_err(dev, "Failed to resume ISI (%d) for m2m\n", ret); + err = ret; + } + return err; } @@ -385,7 +418,7 @@ 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); + clk_bulk_disable_unprepare(isi->num_clks, isi->clks); return 0; } @@ -395,7 +428,7 @@ 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); + ret = clk_bulk_prepare_enable(isi->num_clks, isi->clks); if (ret) { dev_err(dev, "Failed to enable clocks (%d)\n", ret); return ret; @@ -413,27 +446,6 @@ static const struct dev_pm_ops mxc_isi_pm_ops = { * 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_kmemdup(isi->dev, isi->pdata->clks, size, GFP_KERNEL); - if (!isi->clks) - return -ENOMEM; - - 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; @@ -456,34 +468,25 @@ static int mxc_isi_probe(struct platform_device *pdev) 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->num_clks = devm_clk_bulk_get_all(dev, &isi->clks); + if (isi->num_clks < 0) + return dev_err_probe(dev, isi->num_clks, "Failed to get clocks\n"); 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 (IS_ERR(isi->regs)) + return dev_err_probe(dev, PTR_ERR(isi->regs), + "Failed to get ISI register map\n"); if (isi->pdata->gasket_ops) { 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; - } + if (IS_ERR(isi->gasket)) + return dev_err_probe(dev, PTR_ERR(isi->gasket), + "failed to get gasket\n"); } 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; - } + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); pm_runtime_enable(dev); @@ -541,6 +544,8 @@ static void mxc_isi_remove(struct platform_device *pdev) static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data }, { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data }, + { .compatible = "fsl,imx8qm-isi", .data = &mxc_imx8qm_data }, + { .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data }, { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data }, { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, { /* sentinel */ }, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 9c7fe9e5f941..e84af5127e4e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -114,7 +114,6 @@ struct mxc_isi_buffer { }; struct mxc_isi_reg { - u32 offset; u32 mask; }; @@ -158,6 +157,8 @@ struct mxc_gasket_ops { enum model { MXC_ISI_IMX8MN, MXC_ISI_IMX8MP, + MXC_ISI_IMX8QM, + MXC_ISI_IMX8QXP, MXC_ISI_IMX8ULP, MXC_ISI_IMX93, }; @@ -170,8 +171,6 @@ struct mxc_isi_plat_data { const struct mxc_isi_ier_reg *ier_reg; const struct mxc_isi_set_thd *set_thd; const struct mxc_gasket_ops *gasket_ops; - const struct clk_bulk_data *clks; - unsigned int num_clks; bool buf_active_reverse; bool has_36bit_dma; }; @@ -203,9 +202,8 @@ struct mxc_isi_video { struct video_device vdev; struct media_pad pad; - /* Protects is_streaming, and the vdev and vb2_q operations */ + /* Protects 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; @@ -283,6 +281,7 @@ struct mxc_isi_dev { void __iomem *regs; struct clk_bulk_data *clks; + int num_clks; struct regmap *gasket; struct mxc_isi_crossbar crossbar; @@ -343,6 +342,8 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2 #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); +void mxc_isi_m2m_suspend(struct mxc_isi_m2m *m2m); +int mxc_isi_m2m_resume(struct mxc_isi_m2m *m2m); #else static inline int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev) @@ -353,6 +354,13 @@ static inline int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi) { return 0; } +static inline void mxc_isi_m2m_suspend(struct mxc_isi_m2m *m2m) +{ +} +static inline int mxc_isi_m2m_resume(struct mxc_isi_m2m *m2m) +{ + return 0; +} #endif int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe, @@ -362,7 +370,7 @@ 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); +int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe); void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe); void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 93a55c97cd17..ede6cc74c023 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -188,11 +188,12 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, * Create a 1:1 mapping between pixel link inputs and outputs to * pipelines by default. */ - routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL); + routing.num_routes = min(xbar->num_sinks - 1, xbar->num_sources); + routes = kcalloc(routing.num_routes, sizeof(*routes), GFP_KERNEL); if (!routes) return -ENOMEM; - for (i = 0; i < xbar->num_sources; ++i) { + for (i = 0; i < routing.num_routes; ++i) { struct v4l2_subdev_route *route = &routes[i]; route->sink_pad = i; @@ -200,7 +201,6 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, 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); @@ -352,9 +352,8 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd, 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); + "failed to enable streams 0x%llx on '%s':%u: %d\n", + sink_streams, remote_sd->name, remote_pad, ret); mxc_isi_crossbar_gasket_disable(xbar, sink_pad); return ret; } @@ -392,9 +391,8 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, 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); + "failed to disable streams 0x%llx on '%s':%u: %d\n", + sink_streams, remote_sd->name, remote_pad, ret); mxc_isi_crossbar_gasket_disable(xbar, sink_pad); } @@ -453,7 +451,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) * the memory input. */ xbar->num_sinks = isi->pdata->num_ports + 1; - xbar->num_sources = isi->pdata->num_ports; + xbar->num_sources = isi->pdata->num_channels; num_pads = xbar->num_sinks + xbar->num_sources; xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 5623914f95e6..9225a7ac1c3e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -587,7 +587,7 @@ void mxc_isi_channel_release(struct mxc_isi_pipe *pipe) * * TODO: Support secondary line buffer for downscaling YUV420 images. */ -int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass) +int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe) { /* Channel chaining requires both line and output buffer. */ const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index 22e49d3a1287..00afcbfbdde4 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -43,7 +43,6 @@ struct mxc_isi_m2m_ctx_queue_data { struct v4l2_pix_format_mplane format; const struct mxc_isi_format_info *info; u32 sequence; - bool streaming; }; struct mxc_isi_m2m_ctx { @@ -74,9 +73,9 @@ 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) +static inline struct mxc_isi_m2m_ctx *file_to_isi_m2m_ctx(struct file *filp) { - return container_of(fh, struct mxc_isi_m2m_ctx, fh); + return container_of(file_to_v4l2_fh(filp), struct mxc_isi_m2m_ctx, fh); } static inline struct mxc_isi_m2m_ctx_queue_data * @@ -236,6 +235,70 @@ static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static int mxc_isi_m2m_vb2_prepare_streaming(struct vb2_queue *q) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + 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; + int ret; + + guard(mutex)(&m2m->lock); + + if (m2m->usage_count == INT_MAX) + return -EOVERFLOW; + + ret = pm_runtime_resume_and_get(m2m->isi->dev); + if (ret) + return ret; + + /* + * Acquire the pipe and initialize the channel with the first user of + * the M2M device. + */ + if (m2m->usage_count == 0) { + bool bypass = cap_pix->width == out_pix->width && + cap_pix->height == out_pix->height && + cap_info->encoding == out_info->encoding; + + ret = mxc_isi_channel_acquire(m2m->pipe, + &mxc_isi_m2m_frame_write_done, + bypass); + if (ret) + goto err_pm; + + 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); + if (ret) + goto err_deinit; + + m2m->chained_count++; + ctx->chained = true; + } + + return 0; + +err_deinit: + if (--m2m->usage_count == 0) { + mxc_isi_channel_put(m2m->pipe); + mxc_isi_channel_release(m2m->pipe); + } +err_pm: + pm_runtime_put(m2m->isi->dev); + return ret; +} + static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q, unsigned int count) { @@ -265,13 +328,46 @@ static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q) } } +static void mxc_isi_m2m_vb2_unprepare_streaming(struct vb2_queue *q) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct mxc_isi_m2m *m2m = ctx->m2m; + + guard(mutex)(&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); + + pm_runtime_put(m2m->isi->dev); +} + 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, + .prepare_streaming = mxc_isi_m2m_vb2_prepare_streaming, .start_streaming = mxc_isi_m2m_vb2_start_streaming, .stop_streaming = mxc_isi_m2m_vb2_stop_streaming, + .unprepare_streaming = mxc_isi_m2m_vb2_unprepare_streaming, }; static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq, @@ -427,7 +523,7 @@ static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh, 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 mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); __mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type); @@ -437,7 +533,7 @@ static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh, 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); + struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); const struct mxc_isi_m2m_ctx_queue_data *qdata = mxc_isi_m2m_ctx_qdata(ctx, f->type); @@ -452,7 +548,7 @@ static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, 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 mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; const struct mxc_isi_format_info *info; struct vb2_queue *vq; @@ -481,136 +577,6 @@ static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, 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); - struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type); - 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; - - if (q->streaming) - return 0; - - 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; - } - - q->streaming = true; - - 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_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type); - struct mxc_isi_m2m *m2m = ctx->m2m; - - v4l2_m2m_ioctl_streamoff(file, fh, type); - - if (!q->streaming) - return 0; - - 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); - - q->streaming = false; - - return 0; -} - static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { .vidioc_querycap = mxc_isi_m2m_querycap, @@ -631,8 +597,8 @@ static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { .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_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -668,7 +634,6 @@ static int mxc_isi_m2m_open(struct file *file) 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); @@ -685,16 +650,10 @@ static int mxc_isi_m2m_open(struct file *file) if (ret) goto err_ctx; - ret = pm_runtime_resume_and_get(m2m->isi->dev); - if (ret) - goto err_ctrls; - - v4l2_fh_add(&ctx->fh); + v4l2_fh_add(&ctx->fh, file); return 0; -err_ctrls: - mxc_isi_m2m_ctx_ctrls_delete(ctx); err_ctx: v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_fh: @@ -706,20 +665,17 @@ err_fh: 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); + struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); mxc_isi_m2m_ctx_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); mutex_destroy(&ctx->vb2_lock); kfree(ctx); - pm_runtime_put(m2m->isi->dev); - return 0; } @@ -733,6 +689,40 @@ static const struct v4l2_file_operations mxc_isi_m2m_fops = { }; /* ----------------------------------------------------------------------------- + * Suspend & resume + */ + +void mxc_isi_m2m_suspend(struct mxc_isi_m2m *m2m) +{ + if (m2m->usage_count == 0) + return; + + v4l2_m2m_suspend(m2m->m2m_dev); + + if (m2m->chained_count > 0) + mxc_isi_channel_unchain(m2m->pipe); + + mxc_isi_channel_disable(m2m->pipe); + mxc_isi_channel_put(m2m->pipe); +} + +int mxc_isi_m2m_resume(struct mxc_isi_m2m *m2m) +{ + if (m2m->usage_count == 0) + return 0; + + mxc_isi_channel_get(m2m->pipe); + + if (m2m->chained_count > 0) + mxc_isi_channel_chain(m2m->pipe); + + m2m->last_ctx = NULL; + v4l2_m2m_resume(m2m->m2m_dev); + + return 0; +} + +/* ----------------------------------------------------------------------------- * Registration */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index d76eb58deb09..a41c51dd9ce0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -855,7 +855,7 @@ int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, /* Chain the channel if needed for wide resolutions. */ if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { - ret = mxc_isi_channel_chain(pipe, bypass); + ret = mxc_isi_channel_chain(pipe); if (ret) mxc_isi_channel_release(pipe); } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 8654150728a8..13682bf6e9f8 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -937,6 +937,47 @@ static void mxc_isi_video_init_channel(struct mxc_isi_video *video) mxc_isi_channel_set_output_format(pipe, video->fmtinfo, &video->pix); } +static int mxc_isi_vb2_prepare_streaming(struct vb2_queue *q) +{ + struct mxc_isi_video *video = vb2_get_drv_priv(q); + struct media_device *mdev = &video->pipe->isi->media_dev; + struct media_pipeline *pipe; + int ret; + + /* Get a pipeline for the video node and start it. */ + scoped_guard(mutex, &mdev->graph_mutex) { + ret = mxc_isi_pipe_acquire(video->pipe, + &mxc_isi_video_frame_write_done); + if (ret) + return ret; + + pipe = media_entity_pipeline(&video->vdev.entity) + ? : &video->pipe->pipe; + + ret = __video_device_pipeline_start(&video->vdev, pipe); + if (ret) + goto err_release; + } + + /* 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; + + return 0; + +err_stop: + video_device_pipeline_stop(&video->vdev); +err_release: + mxc_isi_pipe_release(video->pipe); + return ret; +} + static int mxc_isi_vb2_start_streaming(struct vb2_queue *q, unsigned int count) { struct mxc_isi_video *video = vb2_get_drv_priv(q); @@ -985,13 +1026,24 @@ static void mxc_isi_vb2_stop_streaming(struct vb2_queue *q) mxc_isi_video_return_buffers(video, VB2_BUF_STATE_ERROR); } +static void mxc_isi_vb2_unprepare_streaming(struct vb2_queue *q) +{ + struct mxc_isi_video *video = vb2_get_drv_priv(q); + + mxc_isi_video_free_discard_buffers(video); + video_device_pipeline_stop(&video->vdev); + mxc_isi_pipe_release(video->pipe); +} + 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, + .prepare_streaming = mxc_isi_vb2_prepare_streaming, .start_streaming = mxc_isi_vb2_start_streaming, .stop_streaming = mxc_isi_vb2_stop_streaming, + .unprepare_streaming = mxc_isi_vb2_unprepare_streaming, }; /* ----------------------------------------------------------------------------- @@ -1145,97 +1197,6 @@ static int mxc_isi_video_s_fmt(struct file *file, void *priv, 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) { @@ -1291,9 +1252,8 @@ static const struct v4l2_ioctl_ops mxc_isi_video_ioctl_ops = { .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_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_enum_framesizes = mxc_isi_video_enum_framesizes, @@ -1332,10 +1292,6 @@ static int mxc_isi_video_release(struct file *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; } @@ -1357,7 +1313,7 @@ void mxc_isi_video_suspend(struct mxc_isi_pipe *pipe) { struct mxc_isi_video *video = &pipe->video; - if (!video->is_streaming) + if (!vb2_is_streaming(&video->vb2_q)) return; mxc_isi_pipe_disable(pipe); @@ -1388,7 +1344,7 @@ int mxc_isi_video_resume(struct mxc_isi_pipe *pipe) { struct mxc_isi_video *video = &pipe->video; - if (!video->is_streaming) + if (!vb2_is_streaming(&video->vb2_q)) return 0; mxc_isi_video_init_channel(video); |