Lines Matching +full:jh7110 +full:- +full:csi2rx
1 // SPDX-License-Identifier: GPL-2.0+
3 * Driver for Cadence MIPI-CSI2 RX Controller v1.3
20 #include <media/v4l2-ctrls.h>
21 #include <media/v4l2-device.h>
22 #include <media/v4l2-fwnode.h>
23 #include <media/v4l2-subdev.h>
144 static void csi2rx_reset(struct csi2rx_priv *csi2rx) in csi2rx_reset() argument
150 csi2rx->base + CSI2RX_SOFT_RESET_REG); in csi2rx_reset()
152 for (i = 0; i < csi2rx->max_streams; i++) { in csi2rx_reset()
154 csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); in csi2rx_reset()
160 writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); in csi2rx_reset()
161 for (i = 0; i < csi2rx->max_streams; i++) in csi2rx_reset()
162 writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); in csi2rx_reset()
165 static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) in csi2rx_configure_ext_dphy() argument
177 ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt, in csi2rx_configure_ext_dphy()
184 link_freq = v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler, in csi2rx_configure_ext_dphy()
185 fmt->bpp, 2 * csi2rx->num_lanes); in csi2rx_configure_ext_dphy()
190 csi2rx->num_lanes, cfg); in csi2rx_configure_ext_dphy()
194 ret = phy_power_on(csi2rx->dphy); in csi2rx_configure_ext_dphy()
198 ret = phy_configure(csi2rx->dphy, &opts); in csi2rx_configure_ext_dphy()
200 phy_power_off(csi2rx->dphy); in csi2rx_configure_ext_dphy()
207 static int csi2rx_start(struct csi2rx_priv *csi2rx) in csi2rx_start() argument
214 ret = clk_prepare_enable(csi2rx->p_clk); in csi2rx_start()
218 reset_control_deassert(csi2rx->p_rst); in csi2rx_start()
219 csi2rx_reset(csi2rx); in csi2rx_start()
221 reg = csi2rx->num_lanes << 8; in csi2rx_start()
222 for (i = 0; i < csi2rx->num_lanes; i++) { in csi2rx_start()
223 reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]); in csi2rx_start()
224 set_bit(csi2rx->lanes[i], &lanes_used); in csi2rx_start()
233 for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) { in csi2rx_start()
235 csi2rx->max_lanes); in csi2rx_start()
240 writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); in csi2rx_start()
243 if (csi2rx->dphy) { in csi2rx_start()
245 for (i = 0; i < csi2rx->num_lanes; i++) { in csi2rx_start()
246 reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); in csi2rx_start()
247 reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); in csi2rx_start()
250 writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); in csi2rx_start()
252 ret = csi2rx_configure_ext_dphy(csi2rx); in csi2rx_start()
254 dev_err(csi2rx->dev, in csi2rx_start()
270 for (i = 0; i < csi2rx->max_streams; i++) { in csi2rx_start()
271 ret = clk_prepare_enable(csi2rx->pixel_clk[i]); in csi2rx_start()
275 reset_control_deassert(csi2rx->pixel_rst[i]); in csi2rx_start()
278 csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); in csi2rx_start()
285 csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); in csi2rx_start()
288 csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); in csi2rx_start()
291 ret = clk_prepare_enable(csi2rx->sys_clk); in csi2rx_start()
295 reset_control_deassert(csi2rx->sys_rst); in csi2rx_start()
297 ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); in csi2rx_start()
301 clk_disable_unprepare(csi2rx->p_clk); in csi2rx_start()
306 clk_disable_unprepare(csi2rx->sys_clk); in csi2rx_start()
308 for (; i > 0; i--) { in csi2rx_start()
309 reset_control_assert(csi2rx->pixel_rst[i - 1]); in csi2rx_start()
310 clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); in csi2rx_start()
313 if (csi2rx->dphy) { in csi2rx_start()
314 writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); in csi2rx_start()
315 phy_power_off(csi2rx->dphy); in csi2rx_start()
318 clk_disable_unprepare(csi2rx->p_clk); in csi2rx_start()
323 static void csi2rx_stop(struct csi2rx_priv *csi2rx) in csi2rx_stop() argument
329 clk_prepare_enable(csi2rx->p_clk); in csi2rx_stop()
330 reset_control_assert(csi2rx->sys_rst); in csi2rx_stop()
331 clk_disable_unprepare(csi2rx->sys_clk); in csi2rx_stop()
333 for (i = 0; i < csi2rx->max_streams; i++) { in csi2rx_stop()
335 csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); in csi2rx_stop()
337 ret = readl_relaxed_poll_timeout(csi2rx->base + in csi2rx_stop()
343 dev_warn(csi2rx->dev, in csi2rx_stop()
346 reset_control_assert(csi2rx->pixel_rst[i]); in csi2rx_stop()
347 clk_disable_unprepare(csi2rx->pixel_clk[i]); in csi2rx_stop()
350 reset_control_assert(csi2rx->p_rst); in csi2rx_stop()
351 clk_disable_unprepare(csi2rx->p_clk); in csi2rx_stop()
353 if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) in csi2rx_stop()
354 dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); in csi2rx_stop()
356 if (csi2rx->dphy) { in csi2rx_stop()
357 writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); in csi2rx_stop()
359 if (phy_power_off(csi2rx->dphy)) in csi2rx_stop()
360 dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); in csi2rx_stop()
366 struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); in csi2rx_s_stream() local
369 mutex_lock(&csi2rx->lock); in csi2rx_s_stream()
376 if (!csi2rx->count) { in csi2rx_s_stream()
377 ret = csi2rx_start(csi2rx); in csi2rx_s_stream()
382 csi2rx->count++; in csi2rx_s_stream()
384 csi2rx->count--; in csi2rx_s_stream()
389 if (!csi2rx->count) in csi2rx_s_stream()
390 csi2rx_stop(csi2rx); in csi2rx_s_stream()
394 mutex_unlock(&csi2rx->lock); in csi2rx_s_stream()
402 if (code_enum->index >= ARRAY_SIZE(formats)) in csi2rx_enum_mbus_code()
403 return -EINVAL; in csi2rx_enum_mbus_code()
405 code_enum->code = formats[code_enum->index].code; in csi2rx_enum_mbus_code()
418 if (format->pad != CSI2RX_PAD_SINK) in csi2rx_set_fmt()
421 if (!csi2rx_get_fmt_by_code(format->format.code)) in csi2rx_set_fmt()
422 format->format.code = formats[0].code; in csi2rx_set_fmt()
424 format->format.field = V4L2_FIELD_NONE; in csi2rx_set_fmt()
427 fmt = v4l2_subdev_state_get_format(state, format->pad); in csi2rx_set_fmt()
428 *fmt = format->format; in csi2rx_set_fmt()
433 *fmt = format->format; in csi2rx_set_fmt()
486 struct v4l2_subdev *subdev = notifier->sd; in csi2rx_async_bound()
487 struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); in csi2rx_async_bound() local
489 csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, in csi2rx_async_bound()
490 asd->match.fwnode, in csi2rx_async_bound()
492 if (csi2rx->source_pad < 0) { in csi2rx_async_bound()
493 dev_err(csi2rx->dev, "Couldn't find output pad for subdev %s\n", in csi2rx_async_bound()
494 s_subdev->name); in csi2rx_async_bound()
495 return csi2rx->source_pad; in csi2rx_async_bound()
498 csi2rx->source_subdev = s_subdev; in csi2rx_async_bound()
500 dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name, in csi2rx_async_bound()
501 csi2rx->source_pad); in csi2rx_async_bound()
503 return media_create_pad_link(&csi2rx->source_subdev->entity, in csi2rx_async_bound()
504 csi2rx->source_pad, in csi2rx_async_bound()
505 &csi2rx->subdev.entity, 0, in csi2rx_async_bound()
514 static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, in csi2rx_get_resources() argument
521 csi2rx->base = devm_platform_ioremap_resource(pdev, 0); in csi2rx_get_resources()
522 if (IS_ERR(csi2rx->base)) in csi2rx_get_resources()
523 return PTR_ERR(csi2rx->base); in csi2rx_get_resources()
525 csi2rx->sys_clk = devm_clk_get(&pdev->dev, "sys_clk"); in csi2rx_get_resources()
526 if (IS_ERR(csi2rx->sys_clk)) { in csi2rx_get_resources()
527 dev_err(&pdev->dev, "Couldn't get sys clock\n"); in csi2rx_get_resources()
528 return PTR_ERR(csi2rx->sys_clk); in csi2rx_get_resources()
531 csi2rx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); in csi2rx_get_resources()
532 if (IS_ERR(csi2rx->p_clk)) { in csi2rx_get_resources()
533 dev_err(&pdev->dev, "Couldn't get P clock\n"); in csi2rx_get_resources()
534 return PTR_ERR(csi2rx->p_clk); in csi2rx_get_resources()
537 csi2rx->sys_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, in csi2rx_get_resources()
539 if (IS_ERR(csi2rx->sys_rst)) in csi2rx_get_resources()
540 return PTR_ERR(csi2rx->sys_rst); in csi2rx_get_resources()
542 csi2rx->p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, in csi2rx_get_resources()
544 if (IS_ERR(csi2rx->p_rst)) in csi2rx_get_resources()
545 return PTR_ERR(csi2rx->p_rst); in csi2rx_get_resources()
547 csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); in csi2rx_get_resources()
548 if (IS_ERR(csi2rx->dphy)) { in csi2rx_get_resources()
549 dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); in csi2rx_get_resources()
550 return PTR_ERR(csi2rx->dphy); in csi2rx_get_resources()
553 ret = clk_prepare_enable(csi2rx->p_clk); in csi2rx_get_resources()
555 dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); in csi2rx_get_resources()
559 dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG); in csi2rx_get_resources()
560 clk_disable_unprepare(csi2rx->p_clk); in csi2rx_get_resources()
562 csi2rx->max_lanes = dev_cfg & 7; in csi2rx_get_resources()
563 if (csi2rx->max_lanes > CSI2RX_LANES_MAX) { in csi2rx_get_resources()
564 dev_err(&pdev->dev, "Invalid number of lanes: %u\n", in csi2rx_get_resources()
565 csi2rx->max_lanes); in csi2rx_get_resources()
566 return -EINVAL; in csi2rx_get_resources()
569 csi2rx->max_streams = (dev_cfg >> 4) & 7; in csi2rx_get_resources()
570 if (csi2rx->max_streams > CSI2RX_STREAMS_MAX) { in csi2rx_get_resources()
571 dev_err(&pdev->dev, "Invalid number of streams: %u\n", in csi2rx_get_resources()
572 csi2rx->max_streams); in csi2rx_get_resources()
573 return -EINVAL; in csi2rx_get_resources()
576 csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false; in csi2rx_get_resources()
579 * FIXME: Once we'll have internal D-PHY support, the check in csi2rx_get_resources()
582 if (!csi2rx->dphy && csi2rx->has_internal_dphy) { in csi2rx_get_resources()
583 dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); in csi2rx_get_resources()
584 return -EINVAL; in csi2rx_get_resources()
587 for (i = 0; i < csi2rx->max_streams; i++) { in csi2rx_get_resources()
591 csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, name); in csi2rx_get_resources()
592 if (IS_ERR(csi2rx->pixel_clk[i])) { in csi2rx_get_resources()
593 dev_err(&pdev->dev, "Couldn't get clock %s\n", name); in csi2rx_get_resources()
594 return PTR_ERR(csi2rx->pixel_clk[i]); in csi2rx_get_resources()
598 csi2rx->pixel_rst[i] = in csi2rx_get_resources()
599 devm_reset_control_get_optional_exclusive(&pdev->dev, in csi2rx_get_resources()
601 if (IS_ERR(csi2rx->pixel_rst[i])) in csi2rx_get_resources()
602 return PTR_ERR(csi2rx->pixel_rst[i]); in csi2rx_get_resources()
608 static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) in csi2rx_parse_dt() argument
616 ep = of_graph_get_endpoint_by_regs(csi2rx->dev->of_node, 0, 0); in csi2rx_parse_dt()
618 return -EINVAL; in csi2rx_parse_dt()
623 dev_err(csi2rx->dev, "Could not parse v4l2 endpoint\n"); in csi2rx_parse_dt()
629 dev_err(csi2rx->dev, "Unsupported media bus type: 0x%x\n", in csi2rx_parse_dt()
632 return -EINVAL; in csi2rx_parse_dt()
635 memcpy(csi2rx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, in csi2rx_parse_dt()
636 sizeof(csi2rx->lanes)); in csi2rx_parse_dt()
637 csi2rx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; in csi2rx_parse_dt()
638 if (csi2rx->num_lanes > csi2rx->max_lanes) { in csi2rx_parse_dt()
639 dev_err(csi2rx->dev, "Unsupported number of data-lanes: %d\n", in csi2rx_parse_dt()
640 csi2rx->num_lanes); in csi2rx_parse_dt()
642 return -EINVAL; in csi2rx_parse_dt()
645 v4l2_async_subdev_nf_init(&csi2rx->notifier, &csi2rx->subdev); in csi2rx_parse_dt()
647 asd = v4l2_async_nf_add_fwnode_remote(&csi2rx->notifier, fwh, in csi2rx_parse_dt()
651 v4l2_async_nf_cleanup(&csi2rx->notifier); in csi2rx_parse_dt()
655 csi2rx->notifier.ops = &csi2rx_notifier_ops; in csi2rx_parse_dt()
657 ret = v4l2_async_nf_register(&csi2rx->notifier); in csi2rx_parse_dt()
659 v4l2_async_nf_cleanup(&csi2rx->notifier); in csi2rx_parse_dt()
666 struct csi2rx_priv *csi2rx; in csi2rx_probe() local
670 csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL); in csi2rx_probe()
671 if (!csi2rx) in csi2rx_probe()
672 return -ENOMEM; in csi2rx_probe()
673 platform_set_drvdata(pdev, csi2rx); in csi2rx_probe()
674 csi2rx->dev = &pdev->dev; in csi2rx_probe()
675 mutex_init(&csi2rx->lock); in csi2rx_probe()
677 ret = csi2rx_get_resources(csi2rx, pdev); in csi2rx_probe()
681 ret = csi2rx_parse_dt(csi2rx); in csi2rx_probe()
685 csi2rx->subdev.owner = THIS_MODULE; in csi2rx_probe()
686 csi2rx->subdev.dev = &pdev->dev; in csi2rx_probe()
687 v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); in csi2rx_probe()
688 csi2rx->subdev.internal_ops = &csi2rx_internal_ops; in csi2rx_probe()
689 v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); in csi2rx_probe()
690 snprintf(csi2rx->subdev.name, sizeof(csi2rx->subdev.name), in csi2rx_probe()
691 "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); in csi2rx_probe()
694 csi2rx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; in csi2rx_probe()
695 csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; in csi2rx_probe()
697 csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; in csi2rx_probe()
698 csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; in csi2rx_probe()
699 csi2rx->subdev.entity.ops = &csi2rx_media_ops; in csi2rx_probe()
701 ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, in csi2rx_probe()
702 csi2rx->pads); in csi2rx_probe()
706 ret = v4l2_subdev_init_finalize(&csi2rx->subdev); in csi2rx_probe()
710 ret = v4l2_async_register_subdev(&csi2rx->subdev); in csi2rx_probe()
714 dev_info(&pdev->dev, in csi2rx_probe()
715 "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", in csi2rx_probe()
716 csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, in csi2rx_probe()
717 csi2rx->dphy ? "external" : in csi2rx_probe()
718 csi2rx->has_internal_dphy ? "internal" : "no"); in csi2rx_probe()
723 v4l2_subdev_cleanup(&csi2rx->subdev); in csi2rx_probe()
725 v4l2_async_nf_unregister(&csi2rx->notifier); in csi2rx_probe()
726 v4l2_async_nf_cleanup(&csi2rx->notifier); in csi2rx_probe()
727 media_entity_cleanup(&csi2rx->subdev.entity); in csi2rx_probe()
729 kfree(csi2rx); in csi2rx_probe()
735 struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); in csi2rx_remove() local
737 v4l2_async_nf_unregister(&csi2rx->notifier); in csi2rx_remove()
738 v4l2_async_nf_cleanup(&csi2rx->notifier); in csi2rx_remove()
739 v4l2_async_unregister_subdev(&csi2rx->subdev); in csi2rx_remove()
740 v4l2_subdev_cleanup(&csi2rx->subdev); in csi2rx_remove()
741 media_entity_cleanup(&csi2rx->subdev.entity); in csi2rx_remove()
742 kfree(csi2rx); in csi2rx_remove()
746 { .compatible = "starfive,jh7110-csi2rx" },
747 { .compatible = "cdns,csi2rx" },
757 .name = "cdns-csi2rx",
763 MODULE_DESCRIPTION("Cadence CSI2-RX controller");