diff options
Diffstat (limited to 'drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c')
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 207 |
1 files changed, 177 insertions, 30 deletions
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index e68fcdaea207..9243306e2aa9 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -85,6 +85,15 @@ CSIDPHYSKW0_UTIL_DL2_SKW_ADJ(1) | \ CSIDPHYSKW0_UTIL_DL3_SKW_ADJ(1)) +/* DPHY registers on RZ/V2H(P) SoC */ +#define CRUm_S_TIMCTL 0x41c +#define CRUm_S_TIMCTL_S_HSSETTLECTL(x) ((x) << 8) + +#define CRUm_S_DPHYCTL_MSB 0x434 +#define CRUm_S_DPHYCTL_MSB_DESKEW BIT(1) + +#define CRUm_SWAPCTL 0x438 + #define VSRSTS_RETRIES 20 #define RZG2L_CSI2_MIN_WIDTH 320 @@ -107,6 +116,7 @@ struct rzg2l_csi2 { void __iomem *base; struct reset_control *presetn; struct reset_control *cmn_rstb; + const struct rzg2l_csi2_info *info; struct clk *sysclk; struct clk *vclk; unsigned long vclk_rate; @@ -123,6 +133,12 @@ struct rzg2l_csi2 { bool dphy_enabled; }; +struct rzg2l_csi2_info { + int (*dphy_enable)(struct rzg2l_csi2 *csi2); + int (*dphy_disable)(struct rzg2l_csi2 *csi2); + bool has_system_clk; +}; + struct rzg2l_csi2_timings { u32 t_init; u32 tclk_miss; @@ -133,6 +149,30 @@ struct rzg2l_csi2_timings { u32 max_hsfreq; }; +struct rzv2h_csi2_s_hssettlectl { + unsigned int hsfreq; + u16 s_hssettlectl; +}; + +static const struct rzv2h_csi2_s_hssettlectl rzv2h_s_hssettlectl[] = { + { 90, 1 }, { 130, 2 }, { 180, 3 }, + { 220, 4 }, { 270, 5 }, { 310, 6 }, + { 360, 7 }, { 400, 8 }, { 450, 9 }, + { 490, 10 }, { 540, 11 }, { 580, 12 }, + { 630, 13 }, { 670, 14 }, { 720, 15 }, + { 760, 16 }, { 810, 17 }, { 850, 18 }, + { 900, 19 }, { 940, 20 }, { 990, 21 }, + { 1030, 22 }, { 1080, 23 }, { 1120, 24 }, + { 1170, 25 }, { 1220, 26 }, { 1260, 27 }, + { 1310, 28 }, { 1350, 29 }, { 1400, 30 }, + { 1440, 31 }, { 1490, 32 }, { 1530, 33 }, + { 1580, 34 }, { 1620, 35 }, { 1670, 36 }, + { 1710, 37 }, { 1760, 38 }, { 1800, 39 }, + { 1850, 40 }, { 1890, 41 }, { 1940, 42 }, + { 1980, 43 }, { 2030, 44 }, { 2070, 45 }, + { 2100, 46 }, +}; + static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = { { .max_hsfreq = 80, @@ -183,12 +223,15 @@ static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = { struct rzg2l_csi2_format { u32 code; - unsigned int datatype; unsigned int bpp; }; static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = { - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, }; static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -352,14 +395,20 @@ static int rzg2l_csi2_dphy_enable(struct rzg2l_csi2 *csi2) return ret; } +static const struct rzg2l_csi2_info rzg2l_csi2_info = { + .dphy_enable = rzg2l_csi2_dphy_enable, + .dphy_disable = rzg2l_csi2_dphy_disable, + .has_system_clk = true, +}; + static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); if (on) - return rzg2l_csi2_dphy_enable(csi2); + return csi2->info->dphy_enable(csi2); - return rzg2l_csi2_dphy_disable(csi2); + return csi2->info->dphy_disable(csi2); } static int rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2) @@ -418,6 +467,64 @@ static int rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2) return 0; } +static int rzv2h_csi2_dphy_disable(struct rzg2l_csi2 *csi2) +{ + int ret; + + /* Reset the CRU (D-PHY) */ + ret = reset_control_assert(csi2->cmn_rstb); + if (ret) + return ret; + + csi2->dphy_enabled = false; + + return 0; +} + +static int rzv2h_csi2_dphy_enable(struct rzg2l_csi2 *csi2) +{ + unsigned int i; + u16 hssettle; + int mbps; + + mbps = rzg2l_csi2_calc_mbps(csi2); + if (mbps < 0) + return mbps; + + csi2->hsfreq = mbps; + + for (i = 0; i < ARRAY_SIZE(rzv2h_s_hssettlectl); i++) { + if (csi2->hsfreq <= rzv2h_s_hssettlectl[i].hsfreq) + break; + } + + if (i == ARRAY_SIZE(rzv2h_s_hssettlectl)) + return -EINVAL; + + rzg2l_csi2_write(csi2, CRUm_SWAPCTL, 0); + + hssettle = rzv2h_s_hssettlectl[i].s_hssettlectl; + rzg2l_csi2_write(csi2, CRUm_S_TIMCTL, + CRUm_S_TIMCTL_S_HSSETTLECTL(hssettle)); + + if (csi2->hsfreq > 1500) + rzg2l_csi2_set(csi2, CRUm_S_DPHYCTL_MSB, + CRUm_S_DPHYCTL_MSB_DESKEW); + else + rzg2l_csi2_clr(csi2, CRUm_S_DPHYCTL_MSB, + CRUm_S_DPHYCTL_MSB_DESKEW); + + csi2->dphy_enabled = true; + + return 0; +} + +static const struct rzg2l_csi2_info rzv2h_csi2_info = { + .dphy_enable = rzv2h_csi2_dphy_enable, + .dphy_disable = rzv2h_csi2_dphy_disable, + .has_system_clk = false, +}; + static int rzg2l_csi2_mipi_link_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); @@ -574,6 +681,9 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd, if (fse->index != 0) return -EINVAL; + if (!rzg2l_csi2_code_to_fmt(fse->code)) + return -EINVAL; + fse->min_width = RZG2L_CSI2_MIN_WIDTH; fse->min_height = RZG2L_CSI2_MIN_HEIGHT; fse->max_width = RZG2L_CSI2_MAX_WIDTH; @@ -582,6 +692,25 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd, return 0; } +static int rzg2l_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); + struct media_pad *remote_pad; + + if (!csi2->remote_source) + return -ENODEV; + + remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]); + if (IS_ERR(remote_pad)) { + dev_err(csi2->dev, "can't get source pad of %s (%ld)\n", + csi2->remote_source->name, PTR_ERR(remote_pad)); + return PTR_ERR(remote_pad); + } + return v4l2_subdev_call(csi2->remote_source, pad, get_frame_desc, + remote_pad->index, fd); +} + static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { .s_stream = rzg2l_csi2_s_stream, .pre_streamon = rzg2l_csi2_pre_streamon, @@ -593,6 +722,7 @@ static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, + .get_frame_desc = rzg2l_csi2_get_frame_desc, }; static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { @@ -738,39 +868,46 @@ static const struct media_entity_operations rzg2l_csi2_entity_ops = { static int rzg2l_csi2_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rzg2l_csi2 *csi2; int ret; - csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL); + csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL); if (!csi2) return -ENOMEM; + csi2->info = of_device_get_match_data(dev); + if (!csi2->info) + return dev_err_probe(dev, -EINVAL, "Failed to get OF match data\n"); + csi2->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi2->base)) return PTR_ERR(csi2->base); - csi2->cmn_rstb = devm_reset_control_get_exclusive(&pdev->dev, "cmn-rstb"); + csi2->cmn_rstb = devm_reset_control_get_exclusive(dev, "cmn-rstb"); if (IS_ERR(csi2->cmn_rstb)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->cmn_rstb), + return dev_err_probe(dev, PTR_ERR(csi2->cmn_rstb), "Failed to get cpg cmn-rstb\n"); - csi2->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn"); + csi2->presetn = devm_reset_control_get_shared(dev, "presetn"); if (IS_ERR(csi2->presetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->presetn), + return dev_err_probe(dev, PTR_ERR(csi2->presetn), "Failed to get cpg presetn\n"); - csi2->sysclk = devm_clk_get(&pdev->dev, "system"); - if (IS_ERR(csi2->sysclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->sysclk), - "Failed to get system clk\n"); + if (csi2->info->has_system_clk) { + csi2->sysclk = devm_clk_get(dev, "system"); + if (IS_ERR(csi2->sysclk)) + return dev_err_probe(dev, PTR_ERR(csi2->sysclk), + "Failed to get system clk\n"); + } - csi2->vclk = devm_clk_get(&pdev->dev, "video"); + csi2->vclk = devm_clk_get(dev, "video"); if (IS_ERR(csi2->vclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->vclk), + return dev_err_probe(dev, PTR_ERR(csi2->vclk), "Failed to get video clock\n"); csi2->vclk_rate = clk_get_rate(csi2->vclk); - csi2->dev = &pdev->dev; + csi2->dev = dev; platform_set_drvdata(pdev, csi2); @@ -778,33 +915,38 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) if (ret) return ret; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = rzg2l_validate_csi2_lanes(csi2); if (ret) - goto error_pm; + return ret; - csi2->subdev.dev = &pdev->dev; + csi2->subdev.dev = dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; - v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); + v4l2_set_subdevdata(&csi2->subdev, dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), - "csi-%s", dev_name(&pdev->dev)); + "csi-%s", dev_name(dev)); csi2->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; csi2->subdev.entity.ops = &rzg2l_csi2_entity_ops; - csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; + csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; /* * TODO: RZ/G2L CSI2 supports 4 virtual channels, as virtual * channels should be implemented by streams API which is under * development lets hardcode to VC0 for now. */ - csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&csi2->subdev.entity, 2, csi2->pads); + csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; + ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads), + csi2->pads); if (ret) - goto error_pm; + return ret; ret = v4l2_subdev_init_finalize(&csi2->subdev); if (ret < 0) @@ -822,8 +964,6 @@ error_async: v4l2_async_nf_unregister(&csi2->notifier); v4l2_async_nf_cleanup(&csi2->notifier); media_entity_cleanup(&csi2->subdev.entity); -error_pm: - pm_runtime_disable(&pdev->dev); return ret; } @@ -837,7 +977,6 @@ static void rzg2l_csi2_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&csi2->subdev); v4l2_subdev_cleanup(&csi2->subdev); media_entity_cleanup(&csi2->subdev.entity); - pm_runtime_disable(&pdev->dev); } static int rzg2l_csi2_pm_runtime_suspend(struct device *dev) @@ -862,12 +1001,20 @@ static const struct dev_pm_ops rzg2l_csi2_pm_ops = { }; static const struct of_device_id rzg2l_csi2_of_table[] = { - { .compatible = "renesas,rzg2l-csi2", }, + { + .compatible = "renesas,r9a09g057-csi2", + .data = &rzv2h_csi2_info, + }, + { + .compatible = "renesas,rzg2l-csi2", + .data = &rzg2l_csi2_info, + }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table); static struct platform_driver rzg2l_csi2_pdrv = { - .remove_new = rzg2l_csi2_remove, + .remove = rzg2l_csi2_remove, .probe = rzg2l_csi2_probe, .driver = { .name = "rzg2l-csi2", |