Lines Matching +full:device +full:- +full:width

1 // SPDX-License-Identifier: GPL-2.0
3 * NXP i.MX8MQ SoC series MIPI-CSI2 receiver driver
9 #include <linux/clk-provider.h>
27 #include <media/v4l2-common.h>
28 #include <media/v4l2-device.h>
29 #include <media/v4l2-fwnode.h>
30 #include <media/v4l2-mc.h>
31 #include <media/v4l2-subdev.h>
33 #define MIPI_CSI2_DRIVER_NAME "imx8mq-mipi-csi2"
45 /* i.MX8MQ CSI-2 controller CSR */
96 * which the sensor transfers data to the CSI-2 Controller and the user
99 * The calculation is the classical rate-in rate-out type of problem: If the
108 struct device *dev;
131 /* -----------------------------------------------------------------------------
137 u8 width; member
144 .width = 8,
147 .width = 8,
150 .width = 8,
153 .width = 8,
156 .width = 8,
159 .width = 10,
162 .width = 10,
165 .width = 10,
168 .width = 10,
171 .width = 10,
174 .width = 12,
177 .width = 12,
180 .width = 12,
183 .width = 12,
186 .width = 12,
189 .width = 14,
192 .width = 14,
195 .width = 14,
198 .width = 14,
203 .width = 16,
206 .width = 16,
220 /* -----------------------------------------------------------------------------
226 writel(val, state->regs + reg); in imx8mq_mipi_csi_write()
234 * these are most likely self-clearing reset bits. to make it in imx8mq_mipi_csi_sw_reset()
235 * more clear, the reset-imx7 driver should implement the in imx8mq_mipi_csi_sw_reset()
238 ret = reset_control_assert(state->rst); in imx8mq_mipi_csi_sw_reset()
240 dev_err(state->dev, "Failed to assert resets: %d\n", ret); in imx8mq_mipi_csi_sw_reset()
249 int lanes = state->bus.num_data_lanes; in imx8mq_mipi_csi_set_params()
251 imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1); in imx8mq_mipi_csi_set_params()
268 return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks); in imx8mq_mipi_csi_clk_enable()
273 clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks); in imx8mq_mipi_csi_clk_disable()
281 state->clks[i].id = imx8mq_mipi_csi_clk_id[i]; in imx8mq_mipi_csi_clk_get()
283 return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks); in imx8mq_mipi_csi_clk_get()
299 fmt = v4l2_subdev_get_pad_format(&state->sd, sd_state, MIPI_CSI2_PAD_SINK); in imx8mq_mipi_csi_calc_hs_settle()
300 csi2_fmt = find_csi2_format(fmt->code); in imx8mq_mipi_csi_calc_hs_settle()
302 link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler, in imx8mq_mipi_csi_calc_hs_settle()
303 csi2_fmt->width, in imx8mq_mipi_csi_calc_hs_settle()
304 state->bus.num_data_lanes * 2); in imx8mq_mipi_csi_calc_hs_settle()
306 dev_err(state->dev, "Unable to obtain link frequency: %d\n", in imx8mq_mipi_csi_calc_hs_settle()
313 dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate); in imx8mq_mipi_csi_calc_hs_settle()
314 return -EINVAL; in imx8mq_mipi_csi_calc_hs_settle()
318 * The D-PHY specification requires Ths-settle to be in the range in imx8mq_mipi_csi_calc_hs_settle()
322 * The Ths-settle value is expressed in the hardware as a multiple of in imx8mq_mipi_csi_calc_hs_settle()
325 * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc in imx8mq_mipi_csi_calc_hs_settle()
331 esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk); in imx8mq_mipi_csi_calc_hs_settle()
333 dev_err(state->dev, "Could not get esc clock rate.\n"); in imx8mq_mipi_csi_calc_hs_settle()
334 return -EINVAL; in imx8mq_mipi_csi_calc_hs_settle()
337 dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate); in imx8mq_mipi_csi_calc_hs_settle()
344 *hs_settle = ths_settle_ns / esc_clk_period_ns - 1; in imx8mq_mipi_csi_calc_hs_settle()
346 dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n", in imx8mq_mipi_csi_calc_hs_settle()
367 regmap_update_bits(state->phy_gpr, in imx8mq_mipi_csi_start_stream()
368 state->phy_gpr_reg, in imx8mq_mipi_csi_start_stream()
384 /* -----------------------------------------------------------------------------
400 ret = pm_runtime_resume_and_get(state->dev); in imx8mq_mipi_csi_s_stream()
405 mutex_lock(&state->lock); in imx8mq_mipi_csi_s_stream()
408 if (state->state & ST_SUSPENDED) { in imx8mq_mipi_csi_s_stream()
409 ret = -EBUSY; in imx8mq_mipi_csi_s_stream()
420 ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1); in imx8mq_mipi_csi_s_stream()
424 state->state |= ST_STREAMING; in imx8mq_mipi_csi_s_stream()
426 v4l2_subdev_call(state->src_sd, video, s_stream, 0); in imx8mq_mipi_csi_s_stream()
428 state->state &= ~ST_STREAMING; in imx8mq_mipi_csi_s_stream()
432 mutex_unlock(&state->lock); in imx8mq_mipi_csi_s_stream()
435 pm_runtime_put(state->dev); in imx8mq_mipi_csi_s_stream()
449 fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; in imx8mq_mipi_csi_init_cfg()
450 fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; in imx8mq_mipi_csi_init_cfg()
451 fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT; in imx8mq_mipi_csi_init_cfg()
452 fmt_sink->field = V4L2_FIELD_NONE; in imx8mq_mipi_csi_init_cfg()
454 fmt_sink->colorspace = V4L2_COLORSPACE_RAW; in imx8mq_mipi_csi_init_cfg()
455 fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); in imx8mq_mipi_csi_init_cfg()
456 fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); in imx8mq_mipi_csi_init_cfg()
457 fmt_sink->quantization = in imx8mq_mipi_csi_init_cfg()
458 V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, in imx8mq_mipi_csi_init_cfg()
459 fmt_sink->ycbcr_enc); in imx8mq_mipi_csi_init_cfg()
474 if (code->pad == MIPI_CSI2_PAD_SOURCE) { in imx8mq_mipi_csi_enum_mbus_code()
477 if (code->index > 0) in imx8mq_mipi_csi_enum_mbus_code()
478 return -EINVAL; in imx8mq_mipi_csi_enum_mbus_code()
480 fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); in imx8mq_mipi_csi_enum_mbus_code()
481 code->code = fmt->code; in imx8mq_mipi_csi_enum_mbus_code()
485 if (code->pad != MIPI_CSI2_PAD_SINK) in imx8mq_mipi_csi_enum_mbus_code()
486 return -EINVAL; in imx8mq_mipi_csi_enum_mbus_code()
488 if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats)) in imx8mq_mipi_csi_enum_mbus_code()
489 return -EINVAL; in imx8mq_mipi_csi_enum_mbus_code()
491 code->code = imx8mq_mipi_csi_formats[code->index].code; in imx8mq_mipi_csi_enum_mbus_code()
504 * The device can't transcode in any way, the source format can't be in imx8mq_mipi_csi_set_fmt()
507 if (sdformat->pad == MIPI_CSI2_PAD_SOURCE) in imx8mq_mipi_csi_set_fmt()
510 if (sdformat->pad != MIPI_CSI2_PAD_SINK) in imx8mq_mipi_csi_set_fmt()
511 return -EINVAL; in imx8mq_mipi_csi_set_fmt()
513 csi2_fmt = find_csi2_format(sdformat->format.code); in imx8mq_mipi_csi_set_fmt()
517 fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); in imx8mq_mipi_csi_set_fmt()
519 fmt->code = csi2_fmt->code; in imx8mq_mipi_csi_set_fmt()
520 fmt->width = sdformat->format.width; in imx8mq_mipi_csi_set_fmt()
521 fmt->height = sdformat->format.height; in imx8mq_mipi_csi_set_fmt()
523 sdformat->format = *fmt; in imx8mq_mipi_csi_set_fmt()
527 *fmt = sdformat->format; in imx8mq_mipi_csi_set_fmt()
548 /* -----------------------------------------------------------------------------
557 /* -----------------------------------------------------------------------------
572 struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK]; in imx8mq_mipi_csi_notify_bound()
574 state->src_sd = sd; in imx8mq_mipi_csi_notify_bound()
594 v4l2_async_subdev_nf_init(&state->notifier, &state->sd); in imx8mq_mipi_csi_async_register()
596 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, in imx8mq_mipi_csi_async_register()
599 return -ENOTCONN; in imx8mq_mipi_csi_async_register()
607 dev_err(state->dev, in imx8mq_mipi_csi_async_register()
609 ret = -EINVAL; in imx8mq_mipi_csi_async_register()
614 state->bus = vep.bus.mipi_csi2; in imx8mq_mipi_csi_async_register()
616 dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n", in imx8mq_mipi_csi_async_register()
617 state->bus.num_data_lanes, in imx8mq_mipi_csi_async_register()
618 state->bus.flags); in imx8mq_mipi_csi_async_register()
620 asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep, in imx8mq_mipi_csi_async_register()
629 state->notifier.ops = &imx8mq_mipi_csi_notify_ops; in imx8mq_mipi_csi_async_register()
631 ret = v4l2_async_nf_register(&state->notifier); in imx8mq_mipi_csi_async_register()
635 return v4l2_async_register_subdev(&state->sd); in imx8mq_mipi_csi_async_register()
643 /* -----------------------------------------------------------------------------
647 static void imx8mq_mipi_csi_pm_suspend(struct device *dev) in imx8mq_mipi_csi_pm_suspend()
652 mutex_lock(&state->lock); in imx8mq_mipi_csi_pm_suspend()
654 if (state->state & ST_POWERED) { in imx8mq_mipi_csi_pm_suspend()
657 state->state &= ~ST_POWERED; in imx8mq_mipi_csi_pm_suspend()
660 mutex_unlock(&state->lock); in imx8mq_mipi_csi_pm_suspend()
663 static int imx8mq_mipi_csi_pm_resume(struct device *dev) in imx8mq_mipi_csi_pm_resume()
670 mutex_lock(&state->lock); in imx8mq_mipi_csi_pm_resume()
672 if (!(state->state & ST_POWERED)) { in imx8mq_mipi_csi_pm_resume()
673 state->state |= ST_POWERED; in imx8mq_mipi_csi_pm_resume()
676 if (state->state & ST_STREAMING) { in imx8mq_mipi_csi_pm_resume()
684 state->state &= ~ST_SUSPENDED; in imx8mq_mipi_csi_pm_resume()
687 mutex_unlock(&state->lock); in imx8mq_mipi_csi_pm_resume()
689 return ret ? -EAGAIN : 0; in imx8mq_mipi_csi_pm_resume()
692 static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) in imx8mq_mipi_csi_suspend()
699 state->state |= ST_SUSPENDED; in imx8mq_mipi_csi_suspend()
704 static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) in imx8mq_mipi_csi_resume()
709 if (!(state->state & ST_SUSPENDED)) in imx8mq_mipi_csi_resume()
715 static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) in imx8mq_mipi_csi_runtime_suspend()
723 ret = icc_set_bw(state->icc_path, 0, 0); in imx8mq_mipi_csi_runtime_suspend()
730 static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) in imx8mq_mipi_csi_runtime_resume()
736 ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); in imx8mq_mipi_csi_runtime_resume()
752 /* -----------------------------------------------------------------------------
758 struct v4l2_subdev *sd = &state->sd; in imx8mq_mipi_csi_subdev_init()
762 sd->owner = THIS_MODULE; in imx8mq_mipi_csi_subdev_init()
763 snprintf(sd->name, sizeof(sd->name), "%s %s", in imx8mq_mipi_csi_subdev_init()
764 MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); in imx8mq_mipi_csi_subdev_init()
766 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; in imx8mq_mipi_csi_subdev_init()
768 sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; in imx8mq_mipi_csi_subdev_init()
769 sd->entity.ops = &imx8mq_mipi_csi_entity_ops; in imx8mq_mipi_csi_subdev_init()
771 sd->dev = state->dev; in imx8mq_mipi_csi_subdev_init()
773 state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK in imx8mq_mipi_csi_subdev_init()
775 state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE in imx8mq_mipi_csi_subdev_init()
777 ret = media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM, in imx8mq_mipi_csi_subdev_init()
778 state->pads); in imx8mq_mipi_csi_subdev_init()
784 media_entity_cleanup(&sd->entity); in imx8mq_mipi_csi_subdev_init()
793 struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); in imx8mq_mipi_csi_release_icc()
796 icc_put(state->icc_path); in imx8mq_mipi_csi_release_icc()
801 struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); in imx8mq_mipi_csi_init_icc()
805 state->icc_path = of_icc_get(&pdev->dev, "dram"); in imx8mq_mipi_csi_init_icc()
806 if (IS_ERR_OR_NULL(state->icc_path)) in imx8mq_mipi_csi_init_icc()
807 return PTR_ERR_OR_ZERO(state->icc_path); in imx8mq_mipi_csi_init_icc()
809 state->icc_path_bw = MBps_to_icc(700); in imx8mq_mipi_csi_init_icc()
816 struct device *dev = state->dev; in imx8mq_mipi_csi_parse_dt()
817 struct device_node *np = state->dev->of_node; in imx8mq_mipi_csi_parse_dt()
823 state->rst = devm_reset_control_array_get_exclusive(dev); in imx8mq_mipi_csi_parse_dt()
824 if (IS_ERR(state->rst)) { in imx8mq_mipi_csi_parse_dt()
825 dev_err(dev, "Failed to get reset: %pe\n", state->rst); in imx8mq_mipi_csi_parse_dt()
826 return PTR_ERR(state->rst); in imx8mq_mipi_csi_parse_dt()
829 ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val, in imx8mq_mipi_csi_parse_dt()
832 dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret); in imx8mq_mipi_csi_parse_dt()
841 return -ENODEV; in imx8mq_mipi_csi_parse_dt()
843 state->phy_gpr = syscon_node_to_regmap(node); in imx8mq_mipi_csi_parse_dt()
845 if (IS_ERR(state->phy_gpr)) { in imx8mq_mipi_csi_parse_dt()
846 dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr); in imx8mq_mipi_csi_parse_dt()
847 return PTR_ERR(state->phy_gpr); in imx8mq_mipi_csi_parse_dt()
850 state->phy_gpr_reg = out_val[1]; in imx8mq_mipi_csi_parse_dt()
851 dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg); in imx8mq_mipi_csi_parse_dt()
858 struct device *dev = &pdev->dev; in imx8mq_mipi_csi_probe()
864 return -ENOMEM; in imx8mq_mipi_csi_probe()
866 state->dev = dev; in imx8mq_mipi_csi_probe()
870 dev_err(dev, "Failed to parse device tree: %d\n", ret); in imx8mq_mipi_csi_probe()
875 state->regs = devm_platform_ioremap_resource(pdev, 0); in imx8mq_mipi_csi_probe()
876 if (IS_ERR(state->regs)) in imx8mq_mipi_csi_probe()
877 return PTR_ERR(state->regs); in imx8mq_mipi_csi_probe()
883 platform_set_drvdata(pdev, &state->sd); in imx8mq_mipi_csi_probe()
885 mutex_init(&state->lock); in imx8mq_mipi_csi_probe()
910 pm_runtime_disable(&pdev->dev); in imx8mq_mipi_csi_probe()
911 imx8mq_mipi_csi_runtime_suspend(&pdev->dev); in imx8mq_mipi_csi_probe()
913 media_entity_cleanup(&state->sd.entity); in imx8mq_mipi_csi_probe()
914 v4l2_subdev_cleanup(&state->sd); in imx8mq_mipi_csi_probe()
915 v4l2_async_nf_unregister(&state->notifier); in imx8mq_mipi_csi_probe()
916 v4l2_async_nf_cleanup(&state->notifier); in imx8mq_mipi_csi_probe()
917 v4l2_async_unregister_subdev(&state->sd); in imx8mq_mipi_csi_probe()
921 mutex_destroy(&state->lock); in imx8mq_mipi_csi_probe()
931 v4l2_async_nf_unregister(&state->notifier); in imx8mq_mipi_csi_remove()
932 v4l2_async_nf_cleanup(&state->notifier); in imx8mq_mipi_csi_remove()
933 v4l2_async_unregister_subdev(&state->sd); in imx8mq_mipi_csi_remove()
935 pm_runtime_disable(&pdev->dev); in imx8mq_mipi_csi_remove()
936 imx8mq_mipi_csi_runtime_suspend(&pdev->dev); in imx8mq_mipi_csi_remove()
937 media_entity_cleanup(&state->sd.entity); in imx8mq_mipi_csi_remove()
938 v4l2_subdev_cleanup(&state->sd); in imx8mq_mipi_csi_remove()
939 mutex_destroy(&state->lock); in imx8mq_mipi_csi_remove()
940 pm_runtime_set_suspended(&pdev->dev); in imx8mq_mipi_csi_remove()
945 { .compatible = "fsl,imx8mq-mipi-csi2", },
962 MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
965 MODULE_ALIAS("platform:imx8mq-mipi-csi2");