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()
298 src_pad = media_entity_remote_source_pad_unique(&sd_state->sd->entity); in imx8mq_mipi_csi_calc_hs_settle()
300 dev_err(state->dev, "can't get source pad of %s (%ld)\n", in imx8mq_mipi_csi_calc_hs_settle()
301 sd_state->sd->name, PTR_ERR(src_pad)); in imx8mq_mipi_csi_calc_hs_settle()
308 csi2_fmt = find_csi2_format(fmt->code); in imx8mq_mipi_csi_calc_hs_settle()
310 link_freq = v4l2_get_link_freq(src_pad, csi2_fmt->width, in imx8mq_mipi_csi_calc_hs_settle()
311 state->bus.num_data_lanes * 2); in imx8mq_mipi_csi_calc_hs_settle()
313 dev_err(state->dev, "Unable to obtain link frequency: %d\n", in imx8mq_mipi_csi_calc_hs_settle()
320 dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate); in imx8mq_mipi_csi_calc_hs_settle()
321 return -EINVAL; in imx8mq_mipi_csi_calc_hs_settle()
325 * The D-PHY specification requires Ths-settle to be in the range in imx8mq_mipi_csi_calc_hs_settle()
329 * The Ths-settle value is expressed in the hardware as a multiple of in imx8mq_mipi_csi_calc_hs_settle()
332 * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc in imx8mq_mipi_csi_calc_hs_settle()
338 esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk); in imx8mq_mipi_csi_calc_hs_settle()
340 dev_err(state->dev, "Could not get esc clock rate.\n"); in imx8mq_mipi_csi_calc_hs_settle()
341 return -EINVAL; in imx8mq_mipi_csi_calc_hs_settle()
344 dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate); in imx8mq_mipi_csi_calc_hs_settle()
351 *hs_settle = ths_settle_ns / esc_clk_period_ns - 1; in imx8mq_mipi_csi_calc_hs_settle()
353 dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n", in imx8mq_mipi_csi_calc_hs_settle()
374 regmap_update_bits(state->phy_gpr, in imx8mq_mipi_csi_start_stream()
375 state->phy_gpr_reg, in imx8mq_mipi_csi_start_stream()
391 /* -----------------------------------------------------------------------------
407 ret = pm_runtime_resume_and_get(state->dev); in imx8mq_mipi_csi_s_stream()
412 mutex_lock(&state->lock); in imx8mq_mipi_csi_s_stream()
415 if (state->state & ST_SUSPENDED) { in imx8mq_mipi_csi_s_stream()
416 ret = -EBUSY; in imx8mq_mipi_csi_s_stream()
427 ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1); in imx8mq_mipi_csi_s_stream()
431 state->state |= ST_STREAMING; in imx8mq_mipi_csi_s_stream()
433 v4l2_subdev_call(state->src_sd, video, s_stream, 0); in imx8mq_mipi_csi_s_stream()
435 state->state &= ~ST_STREAMING; in imx8mq_mipi_csi_s_stream()
439 mutex_unlock(&state->lock); in imx8mq_mipi_csi_s_stream()
442 pm_runtime_put(state->dev); in imx8mq_mipi_csi_s_stream()
457 fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; in imx8mq_mipi_csi_init_state()
458 fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; in imx8mq_mipi_csi_init_state()
459 fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT; in imx8mq_mipi_csi_init_state()
460 fmt_sink->field = V4L2_FIELD_NONE; in imx8mq_mipi_csi_init_state()
462 fmt_sink->colorspace = V4L2_COLORSPACE_RAW; in imx8mq_mipi_csi_init_state()
463 fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); in imx8mq_mipi_csi_init_state()
464 fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); in imx8mq_mipi_csi_init_state()
465 fmt_sink->quantization = in imx8mq_mipi_csi_init_state()
466 V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, in imx8mq_mipi_csi_init_state()
467 fmt_sink->ycbcr_enc); in imx8mq_mipi_csi_init_state()
482 if (code->pad == MIPI_CSI2_PAD_SOURCE) { in imx8mq_mipi_csi_enum_mbus_code()
485 if (code->index > 0) in imx8mq_mipi_csi_enum_mbus_code()
486 return -EINVAL; in imx8mq_mipi_csi_enum_mbus_code()
488 fmt = v4l2_subdev_state_get_format(sd_state, code->pad); in imx8mq_mipi_csi_enum_mbus_code()
489 code->code = fmt->code; in imx8mq_mipi_csi_enum_mbus_code()
493 if (code->pad != MIPI_CSI2_PAD_SINK) in imx8mq_mipi_csi_enum_mbus_code()
494 return -EINVAL; in imx8mq_mipi_csi_enum_mbus_code()
496 if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats)) in imx8mq_mipi_csi_enum_mbus_code()
497 return -EINVAL; in imx8mq_mipi_csi_enum_mbus_code()
499 code->code = imx8mq_mipi_csi_formats[code->index].code; in imx8mq_mipi_csi_enum_mbus_code()
512 * The device can't transcode in any way, the source format can't be in imx8mq_mipi_csi_set_fmt()
515 if (sdformat->pad == MIPI_CSI2_PAD_SOURCE) in imx8mq_mipi_csi_set_fmt()
518 if (sdformat->pad != MIPI_CSI2_PAD_SINK) in imx8mq_mipi_csi_set_fmt()
519 return -EINVAL; in imx8mq_mipi_csi_set_fmt()
521 csi2_fmt = find_csi2_format(sdformat->format.code); in imx8mq_mipi_csi_set_fmt()
525 fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); in imx8mq_mipi_csi_set_fmt()
527 fmt->code = csi2_fmt->code; in imx8mq_mipi_csi_set_fmt()
528 fmt->width = sdformat->format.width; in imx8mq_mipi_csi_set_fmt()
529 fmt->height = sdformat->format.height; in imx8mq_mipi_csi_set_fmt()
531 sdformat->format = *fmt; in imx8mq_mipi_csi_set_fmt()
535 *fmt = sdformat->format; in imx8mq_mipi_csi_set_fmt()
559 /* -----------------------------------------------------------------------------
568 /* -----------------------------------------------------------------------------
583 struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK]; in imx8mq_mipi_csi_notify_bound()
585 state->src_sd = sd; in imx8mq_mipi_csi_notify_bound()
605 v4l2_async_subdev_nf_init(&state->notifier, &state->sd); in imx8mq_mipi_csi_async_register()
607 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, in imx8mq_mipi_csi_async_register()
610 return -ENOTCONN; in imx8mq_mipi_csi_async_register()
618 dev_err(state->dev, in imx8mq_mipi_csi_async_register()
620 ret = -EINVAL; in imx8mq_mipi_csi_async_register()
625 state->bus = vep.bus.mipi_csi2; in imx8mq_mipi_csi_async_register()
627 dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n", in imx8mq_mipi_csi_async_register()
628 state->bus.num_data_lanes, in imx8mq_mipi_csi_async_register()
629 state->bus.flags); in imx8mq_mipi_csi_async_register()
631 asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep, in imx8mq_mipi_csi_async_register()
640 state->notifier.ops = &imx8mq_mipi_csi_notify_ops; in imx8mq_mipi_csi_async_register()
642 ret = v4l2_async_nf_register(&state->notifier); in imx8mq_mipi_csi_async_register()
646 return v4l2_async_register_subdev(&state->sd); in imx8mq_mipi_csi_async_register()
654 /* -----------------------------------------------------------------------------
658 static void imx8mq_mipi_csi_pm_suspend(struct device *dev) in imx8mq_mipi_csi_pm_suspend()
663 mutex_lock(&state->lock); in imx8mq_mipi_csi_pm_suspend()
665 if (state->state & ST_POWERED) { in imx8mq_mipi_csi_pm_suspend()
668 state->state &= ~ST_POWERED; in imx8mq_mipi_csi_pm_suspend()
671 mutex_unlock(&state->lock); in imx8mq_mipi_csi_pm_suspend()
674 static int imx8mq_mipi_csi_pm_resume(struct device *dev) in imx8mq_mipi_csi_pm_resume()
681 mutex_lock(&state->lock); in imx8mq_mipi_csi_pm_resume()
683 if (!(state->state & ST_POWERED)) { in imx8mq_mipi_csi_pm_resume()
684 state->state |= ST_POWERED; in imx8mq_mipi_csi_pm_resume()
687 if (state->state & ST_STREAMING) { in imx8mq_mipi_csi_pm_resume()
695 state->state &= ~ST_SUSPENDED; in imx8mq_mipi_csi_pm_resume()
698 mutex_unlock(&state->lock); in imx8mq_mipi_csi_pm_resume()
700 return ret ? -EAGAIN : 0; in imx8mq_mipi_csi_pm_resume()
703 static int imx8mq_mipi_csi_suspend(struct device *dev) in imx8mq_mipi_csi_suspend()
710 state->state |= ST_SUSPENDED; in imx8mq_mipi_csi_suspend()
715 static int imx8mq_mipi_csi_resume(struct device *dev) in imx8mq_mipi_csi_resume()
720 if (!(state->state & ST_SUSPENDED)) in imx8mq_mipi_csi_resume()
726 static int imx8mq_mipi_csi_runtime_suspend(struct device *dev) in imx8mq_mipi_csi_runtime_suspend()
734 ret = icc_set_bw(state->icc_path, 0, 0); in imx8mq_mipi_csi_runtime_suspend()
741 static int imx8mq_mipi_csi_runtime_resume(struct device *dev) in imx8mq_mipi_csi_runtime_resume()
747 ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); in imx8mq_mipi_csi_runtime_resume()
762 /* -----------------------------------------------------------------------------
768 struct v4l2_subdev *sd = &state->sd; in imx8mq_mipi_csi_subdev_init()
772 sd->internal_ops = &imx8mq_mipi_csi_internal_ops; in imx8mq_mipi_csi_subdev_init()
773 sd->owner = THIS_MODULE; in imx8mq_mipi_csi_subdev_init()
774 snprintf(sd->name, sizeof(sd->name), "%s %s", in imx8mq_mipi_csi_subdev_init()
775 MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); in imx8mq_mipi_csi_subdev_init()
777 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; in imx8mq_mipi_csi_subdev_init()
779 sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; in imx8mq_mipi_csi_subdev_init()
780 sd->entity.ops = &imx8mq_mipi_csi_entity_ops; in imx8mq_mipi_csi_subdev_init()
782 sd->dev = state->dev; in imx8mq_mipi_csi_subdev_init()
784 state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK in imx8mq_mipi_csi_subdev_init()
786 state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE in imx8mq_mipi_csi_subdev_init()
788 ret = media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM, in imx8mq_mipi_csi_subdev_init()
789 state->pads); in imx8mq_mipi_csi_subdev_init()
795 media_entity_cleanup(&sd->entity); in imx8mq_mipi_csi_subdev_init()
804 struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); in imx8mq_mipi_csi_release_icc()
807 icc_put(state->icc_path); in imx8mq_mipi_csi_release_icc()
812 struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); in imx8mq_mipi_csi_init_icc()
816 state->icc_path = of_icc_get(&pdev->dev, "dram"); in imx8mq_mipi_csi_init_icc()
817 if (IS_ERR_OR_NULL(state->icc_path)) in imx8mq_mipi_csi_init_icc()
818 return PTR_ERR_OR_ZERO(state->icc_path); in imx8mq_mipi_csi_init_icc()
820 state->icc_path_bw = MBps_to_icc(700); in imx8mq_mipi_csi_init_icc()
827 struct device *dev = state->dev; in imx8mq_mipi_csi_parse_dt()
828 struct device_node *np = state->dev->of_node; in imx8mq_mipi_csi_parse_dt()
834 state->rst = devm_reset_control_array_get_exclusive(dev); in imx8mq_mipi_csi_parse_dt()
835 if (IS_ERR(state->rst)) { in imx8mq_mipi_csi_parse_dt()
836 dev_err(dev, "Failed to get reset: %pe\n", state->rst); in imx8mq_mipi_csi_parse_dt()
837 return PTR_ERR(state->rst); in imx8mq_mipi_csi_parse_dt()
840 ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val, in imx8mq_mipi_csi_parse_dt()
843 dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret); in imx8mq_mipi_csi_parse_dt()
852 return -ENODEV; in imx8mq_mipi_csi_parse_dt()
854 state->phy_gpr = syscon_node_to_regmap(node); in imx8mq_mipi_csi_parse_dt()
856 if (IS_ERR(state->phy_gpr)) { in imx8mq_mipi_csi_parse_dt()
857 dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr); in imx8mq_mipi_csi_parse_dt()
858 return PTR_ERR(state->phy_gpr); in imx8mq_mipi_csi_parse_dt()
861 state->phy_gpr_reg = out_val[1]; in imx8mq_mipi_csi_parse_dt()
862 dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg); in imx8mq_mipi_csi_parse_dt()
869 struct device *dev = &pdev->dev; in imx8mq_mipi_csi_probe()
875 return -ENOMEM; in imx8mq_mipi_csi_probe()
877 state->dev = dev; in imx8mq_mipi_csi_probe()
881 dev_err(dev, "Failed to parse device tree: %d\n", ret); in imx8mq_mipi_csi_probe()
886 state->regs = devm_platform_ioremap_resource(pdev, 0); in imx8mq_mipi_csi_probe()
887 if (IS_ERR(state->regs)) in imx8mq_mipi_csi_probe()
888 return PTR_ERR(state->regs); in imx8mq_mipi_csi_probe()
894 platform_set_drvdata(pdev, &state->sd); in imx8mq_mipi_csi_probe()
896 mutex_init(&state->lock); in imx8mq_mipi_csi_probe()
921 pm_runtime_disable(&pdev->dev); in imx8mq_mipi_csi_probe()
922 imx8mq_mipi_csi_runtime_suspend(&pdev->dev); in imx8mq_mipi_csi_probe()
924 media_entity_cleanup(&state->sd.entity); in imx8mq_mipi_csi_probe()
925 v4l2_subdev_cleanup(&state->sd); in imx8mq_mipi_csi_probe()
926 v4l2_async_nf_unregister(&state->notifier); in imx8mq_mipi_csi_probe()
927 v4l2_async_nf_cleanup(&state->notifier); in imx8mq_mipi_csi_probe()
928 v4l2_async_unregister_subdev(&state->sd); in imx8mq_mipi_csi_probe()
932 mutex_destroy(&state->lock); in imx8mq_mipi_csi_probe()
942 v4l2_async_nf_unregister(&state->notifier); in imx8mq_mipi_csi_remove()
943 v4l2_async_nf_cleanup(&state->notifier); in imx8mq_mipi_csi_remove()
944 v4l2_async_unregister_subdev(&state->sd); in imx8mq_mipi_csi_remove()
946 pm_runtime_disable(&pdev->dev); in imx8mq_mipi_csi_remove()
947 imx8mq_mipi_csi_runtime_suspend(&pdev->dev); in imx8mq_mipi_csi_remove()
948 media_entity_cleanup(&state->sd.entity); in imx8mq_mipi_csi_remove()
949 v4l2_subdev_cleanup(&state->sd); in imx8mq_mipi_csi_remove()
950 mutex_destroy(&state->lock); in imx8mq_mipi_csi_remove()
951 pm_runtime_set_suspended(&pdev->dev); in imx8mq_mipi_csi_remove()
956 { .compatible = "fsl,imx8mq-mipi-csi2", },
973 MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
976 MODULE_ALIAS("platform:imx8mq-mipi-csi2");