1f53fb31aSMichael Riesch // SPDX-License-Identifier: GPL-2.0 2f53fb31aSMichael Riesch /* 3f53fb31aSMichael Riesch * Rockchip Camera Interface (CIF) Driver 4f53fb31aSMichael Riesch * 5f53fb31aSMichael Riesch * Copyright (C) 2018 Rockchip Electronics Co., Ltd. 6f53fb31aSMichael Riesch * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com> 7f53fb31aSMichael Riesch * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com> 8f53fb31aSMichael Riesch * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> 9f53fb31aSMichael Riesch * Copyright (C) 2025 Collabora, Ltd. 10f53fb31aSMichael Riesch */ 11f53fb31aSMichael Riesch 12f53fb31aSMichael Riesch #include <linux/clk.h> 13f53fb31aSMichael Riesch #include <linux/delay.h> 14f53fb31aSMichael Riesch #include <linux/interrupt.h> 15f53fb31aSMichael Riesch #include <linux/mfd/syscon.h> 16f53fb31aSMichael Riesch #include <linux/module.h> 17f53fb31aSMichael Riesch #include <linux/of.h> 18f53fb31aSMichael Riesch #include <linux/of_graph.h> 19f53fb31aSMichael Riesch #include <linux/of_platform.h> 20f53fb31aSMichael Riesch #include <linux/pm_runtime.h> 21f53fb31aSMichael Riesch #include <linux/reset.h> 22f53fb31aSMichael Riesch 23f53fb31aSMichael Riesch #include <media/v4l2-fwnode.h> 24f53fb31aSMichael Riesch #include <media/v4l2-mc.h> 25f53fb31aSMichael Riesch 2675e28594SMichael Riesch #include "rkcif-capture-dvp.h" 27*1f2353f5SMichael Riesch #include "rkcif-capture-mipi.h" 28f53fb31aSMichael Riesch #include "rkcif-common.h" 29f53fb31aSMichael Riesch 30f53fb31aSMichael Riesch static const char *const px30_vip_clks[] = { 31f53fb31aSMichael Riesch "aclk", 32f53fb31aSMichael Riesch "hclk", 33f53fb31aSMichael Riesch "pclk", 34f53fb31aSMichael Riesch }; 35f53fb31aSMichael Riesch 36f53fb31aSMichael Riesch static const struct rkcif_match_data px30_vip_match_data = { 37f53fb31aSMichael Riesch .clks = px30_vip_clks, 38f53fb31aSMichael Riesch .clks_num = ARRAY_SIZE(px30_vip_clks), 3975e28594SMichael Riesch .dvp = &rkcif_px30_vip_dvp_match_data, 40f53fb31aSMichael Riesch }; 41f53fb31aSMichael Riesch 42f53fb31aSMichael Riesch static const char *const rk3568_vicap_clks[] = { 43f53fb31aSMichael Riesch "aclk", 44f53fb31aSMichael Riesch "hclk", 45f53fb31aSMichael Riesch "dclk", 46f53fb31aSMichael Riesch "iclk", 47f53fb31aSMichael Riesch }; 48f53fb31aSMichael Riesch 49f53fb31aSMichael Riesch static const struct rkcif_match_data rk3568_vicap_match_data = { 50f53fb31aSMichael Riesch .clks = rk3568_vicap_clks, 51f53fb31aSMichael Riesch .clks_num = ARRAY_SIZE(rk3568_vicap_clks), 52c348d671SMichael Riesch .dvp = &rkcif_rk3568_vicap_dvp_match_data, 53*1f2353f5SMichael Riesch .mipi = &rkcif_rk3568_vicap_mipi_match_data, 54f53fb31aSMichael Riesch }; 55f53fb31aSMichael Riesch 56f53fb31aSMichael Riesch static const struct of_device_id rkcif_plat_of_match[] = { 57f53fb31aSMichael Riesch { 58f53fb31aSMichael Riesch .compatible = "rockchip,px30-vip", 59f53fb31aSMichael Riesch .data = &px30_vip_match_data, 60f53fb31aSMichael Riesch }, 61f53fb31aSMichael Riesch { 62f53fb31aSMichael Riesch .compatible = "rockchip,rk3568-vicap", 63f53fb31aSMichael Riesch .data = &rk3568_vicap_match_data, 64f53fb31aSMichael Riesch }, 65f53fb31aSMichael Riesch {} 66f53fb31aSMichael Riesch }; 67f53fb31aSMichael Riesch MODULE_DEVICE_TABLE(of, rkcif_plat_of_match); 68f53fb31aSMichael Riesch 69f53fb31aSMichael Riesch static int rkcif_register(struct rkcif_device *rkcif) 70f53fb31aSMichael Riesch { 7175e28594SMichael Riesch int ret; 7275e28594SMichael Riesch 7375e28594SMichael Riesch ret = rkcif_dvp_register(rkcif); 7475e28594SMichael Riesch if (ret && ret != -ENODEV) 7575e28594SMichael Riesch goto err; 7675e28594SMichael Riesch 77*1f2353f5SMichael Riesch ret = rkcif_mipi_register(rkcif); 78*1f2353f5SMichael Riesch if (ret && ret != -ENODEV) 79*1f2353f5SMichael Riesch goto err_dvp_unregister; 80*1f2353f5SMichael Riesch 81f53fb31aSMichael Riesch return 0; 8275e28594SMichael Riesch 83*1f2353f5SMichael Riesch err_dvp_unregister: 84*1f2353f5SMichael Riesch rkcif_dvp_unregister(rkcif); 8575e28594SMichael Riesch err: 8675e28594SMichael Riesch return ret; 87f53fb31aSMichael Riesch } 88f53fb31aSMichael Riesch 89f53fb31aSMichael Riesch static void rkcif_unregister(struct rkcif_device *rkcif) 90f53fb31aSMichael Riesch { 91*1f2353f5SMichael Riesch rkcif_mipi_unregister(rkcif); 9275e28594SMichael Riesch rkcif_dvp_unregister(rkcif); 93f53fb31aSMichael Riesch } 94f53fb31aSMichael Riesch 95f53fb31aSMichael Riesch static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier, 96f53fb31aSMichael Riesch struct v4l2_subdev *sd, 97f53fb31aSMichael Riesch struct v4l2_async_connection *asd) 98f53fb31aSMichael Riesch { 9985411d17SMichael Riesch struct rkcif_device *rkcif = 10085411d17SMichael Riesch container_of(notifier, struct rkcif_device, notifier); 101f53fb31aSMichael Riesch struct rkcif_remote *remote = 102f53fb31aSMichael Riesch container_of(asd, struct rkcif_remote, async_conn); 10385411d17SMichael Riesch struct media_pad *sink_pad = 10485411d17SMichael Riesch &remote->interface->pads[RKCIF_IF_PAD_SINK]; 10585411d17SMichael Riesch int ret; 10685411d17SMichael Riesch 10785411d17SMichael Riesch ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad, 10885411d17SMichael Riesch MEDIA_LNK_FL_ENABLED); 10985411d17SMichael Riesch if (ret) { 11085411d17SMichael Riesch dev_err(rkcif->dev, "failed to link source pad of %s\n", 11185411d17SMichael Riesch sd->name); 11285411d17SMichael Riesch return ret; 11385411d17SMichael Riesch } 114f53fb31aSMichael Riesch 115f53fb31aSMichael Riesch remote->sd = sd; 116f53fb31aSMichael Riesch 117f53fb31aSMichael Riesch return 0; 118f53fb31aSMichael Riesch } 119f53fb31aSMichael Riesch 120f53fb31aSMichael Riesch static int rkcif_notifier_complete(struct v4l2_async_notifier *notifier) 121f53fb31aSMichael Riesch { 122f53fb31aSMichael Riesch struct rkcif_device *rkcif = 123f53fb31aSMichael Riesch container_of(notifier, struct rkcif_device, notifier); 124f53fb31aSMichael Riesch 125f53fb31aSMichael Riesch return v4l2_device_register_subdev_nodes(&rkcif->v4l2_dev); 126f53fb31aSMichael Riesch } 127f53fb31aSMichael Riesch 128f53fb31aSMichael Riesch static const struct v4l2_async_notifier_operations rkcif_notifier_ops = { 129f53fb31aSMichael Riesch .bound = rkcif_notifier_bound, 130f53fb31aSMichael Riesch .complete = rkcif_notifier_complete, 131f53fb31aSMichael Riesch }; 132f53fb31aSMichael Riesch 133f53fb31aSMichael Riesch static irqreturn_t rkcif_isr(int irq, void *ctx) 134f53fb31aSMichael Riesch { 135f53fb31aSMichael Riesch irqreturn_t ret = IRQ_NONE; 136f53fb31aSMichael Riesch 13775e28594SMichael Riesch if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED) 13875e28594SMichael Riesch ret = IRQ_HANDLED; 13975e28594SMichael Riesch 140*1f2353f5SMichael Riesch if (rkcif_mipi_isr(irq, ctx) == IRQ_HANDLED) 141*1f2353f5SMichael Riesch ret = IRQ_HANDLED; 142*1f2353f5SMichael Riesch 143f53fb31aSMichael Riesch return ret; 144f53fb31aSMichael Riesch } 145f53fb31aSMichael Riesch 146f53fb31aSMichael Riesch static int rkcif_probe(struct platform_device *pdev) 147f53fb31aSMichael Riesch { 148f53fb31aSMichael Riesch struct device *dev = &pdev->dev; 149f53fb31aSMichael Riesch struct rkcif_device *rkcif; 150f53fb31aSMichael Riesch int ret, irq; 151f53fb31aSMichael Riesch 152f53fb31aSMichael Riesch rkcif = devm_kzalloc(dev, sizeof(*rkcif), GFP_KERNEL); 153f53fb31aSMichael Riesch if (!rkcif) 154f53fb31aSMichael Riesch return -ENOMEM; 155f53fb31aSMichael Riesch 156f53fb31aSMichael Riesch rkcif->match_data = of_device_get_match_data(dev); 157f53fb31aSMichael Riesch if (!rkcif->match_data) 158f53fb31aSMichael Riesch return -ENODEV; 159f53fb31aSMichael Riesch 160f53fb31aSMichael Riesch dev_set_drvdata(dev, rkcif); 161f53fb31aSMichael Riesch rkcif->dev = dev; 162f53fb31aSMichael Riesch 163f53fb31aSMichael Riesch rkcif->base_addr = devm_platform_ioremap_resource(pdev, 0); 164f53fb31aSMichael Riesch if (IS_ERR(rkcif->base_addr)) 165f53fb31aSMichael Riesch return PTR_ERR(rkcif->base_addr); 166f53fb31aSMichael Riesch 167f53fb31aSMichael Riesch irq = platform_get_irq(pdev, 0); 168f53fb31aSMichael Riesch if (irq < 0) 169f53fb31aSMichael Riesch return irq; 170f53fb31aSMichael Riesch 171f53fb31aSMichael Riesch ret = devm_request_irq(dev, irq, rkcif_isr, IRQF_SHARED, 172f53fb31aSMichael Riesch dev_driver_string(dev), dev); 173f53fb31aSMichael Riesch if (ret) 174f53fb31aSMichael Riesch return dev_err_probe(dev, ret, "failed to request irq\n"); 175f53fb31aSMichael Riesch 176f53fb31aSMichael Riesch if (rkcif->match_data->clks_num > RKCIF_CLK_MAX) 177f53fb31aSMichael Riesch return dev_err_probe(dev, -EINVAL, "invalid number of clocks\n"); 178f53fb31aSMichael Riesch 179f53fb31aSMichael Riesch rkcif->clks_num = rkcif->match_data->clks_num; 180f53fb31aSMichael Riesch for (unsigned int i = 0; i < rkcif->clks_num; i++) 181f53fb31aSMichael Riesch rkcif->clks[i].id = rkcif->match_data->clks[i]; 182f53fb31aSMichael Riesch ret = devm_clk_bulk_get(dev, rkcif->clks_num, rkcif->clks); 183f53fb31aSMichael Riesch if (ret) 184f53fb31aSMichael Riesch return dev_err_probe(dev, ret, "failed to get clocks\n"); 185f53fb31aSMichael Riesch 186f53fb31aSMichael Riesch rkcif->reset = devm_reset_control_array_get_exclusive(dev); 187f53fb31aSMichael Riesch if (IS_ERR(rkcif->reset)) 188f53fb31aSMichael Riesch return PTR_ERR(rkcif->reset); 189f53fb31aSMichael Riesch 190f53fb31aSMichael Riesch rkcif->grf = syscon_regmap_lookup_by_phandle(dev->of_node, 191f53fb31aSMichael Riesch "rockchip,grf"); 192f53fb31aSMichael Riesch if (IS_ERR(rkcif->grf)) 193f53fb31aSMichael Riesch rkcif->grf = NULL; 194f53fb31aSMichael Riesch 195f53fb31aSMichael Riesch pm_runtime_enable(&pdev->dev); 196f53fb31aSMichael Riesch 197f53fb31aSMichael Riesch rkcif->media_dev.dev = dev; 198f53fb31aSMichael Riesch strscpy(rkcif->media_dev.model, RKCIF_DRIVER_NAME, 199f53fb31aSMichael Riesch sizeof(rkcif->media_dev.model)); 200f53fb31aSMichael Riesch media_device_init(&rkcif->media_dev); 201f53fb31aSMichael Riesch 202f53fb31aSMichael Riesch rkcif->v4l2_dev.mdev = &rkcif->media_dev; 203f53fb31aSMichael Riesch ret = v4l2_device_register(dev, &rkcif->v4l2_dev); 204f53fb31aSMichael Riesch if (ret) 205f53fb31aSMichael Riesch goto err_media_dev_cleanup; 206f53fb31aSMichael Riesch 207f53fb31aSMichael Riesch ret = media_device_register(&rkcif->media_dev); 208f53fb31aSMichael Riesch if (ret < 0) { 209f53fb31aSMichael Riesch dev_err(dev, "failed to register media device: %d\n", ret); 210f53fb31aSMichael Riesch goto err_v4l2_dev_unregister; 211f53fb31aSMichael Riesch } 212f53fb31aSMichael Riesch 213f53fb31aSMichael Riesch v4l2_async_nf_init(&rkcif->notifier, &rkcif->v4l2_dev); 214f53fb31aSMichael Riesch rkcif->notifier.ops = &rkcif_notifier_ops; 215f53fb31aSMichael Riesch 216f53fb31aSMichael Riesch ret = rkcif_register(rkcif); 217f53fb31aSMichael Riesch if (ret) { 218f53fb31aSMichael Riesch dev_err(dev, "failed to register media entities: %d\n", ret); 219f53fb31aSMichael Riesch goto err_notifier_cleanup; 220f53fb31aSMichael Riesch } 221f53fb31aSMichael Riesch 222f53fb31aSMichael Riesch ret = v4l2_async_nf_register(&rkcif->notifier); 223f53fb31aSMichael Riesch if (ret) 224f53fb31aSMichael Riesch goto err_rkcif_unregister; 225f53fb31aSMichael Riesch 226f53fb31aSMichael Riesch return 0; 227f53fb31aSMichael Riesch 228f53fb31aSMichael Riesch err_rkcif_unregister: 229f53fb31aSMichael Riesch rkcif_unregister(rkcif); 230f53fb31aSMichael Riesch err_notifier_cleanup: 231f53fb31aSMichael Riesch v4l2_async_nf_cleanup(&rkcif->notifier); 232f53fb31aSMichael Riesch media_device_unregister(&rkcif->media_dev); 233f53fb31aSMichael Riesch err_v4l2_dev_unregister: 234f53fb31aSMichael Riesch v4l2_device_unregister(&rkcif->v4l2_dev); 235f53fb31aSMichael Riesch err_media_dev_cleanup: 236f53fb31aSMichael Riesch media_device_cleanup(&rkcif->media_dev); 237f53fb31aSMichael Riesch pm_runtime_disable(&pdev->dev); 238f53fb31aSMichael Riesch return ret; 239f53fb31aSMichael Riesch } 240f53fb31aSMichael Riesch 241f53fb31aSMichael Riesch static void rkcif_remove(struct platform_device *pdev) 242f53fb31aSMichael Riesch { 243f53fb31aSMichael Riesch struct rkcif_device *rkcif = platform_get_drvdata(pdev); 244f53fb31aSMichael Riesch 245f53fb31aSMichael Riesch v4l2_async_nf_unregister(&rkcif->notifier); 246f53fb31aSMichael Riesch rkcif_unregister(rkcif); 247f53fb31aSMichael Riesch v4l2_async_nf_cleanup(&rkcif->notifier); 248f53fb31aSMichael Riesch media_device_unregister(&rkcif->media_dev); 249f53fb31aSMichael Riesch v4l2_device_unregister(&rkcif->v4l2_dev); 250f53fb31aSMichael Riesch media_device_cleanup(&rkcif->media_dev); 251f53fb31aSMichael Riesch pm_runtime_disable(&pdev->dev); 252f53fb31aSMichael Riesch } 253f53fb31aSMichael Riesch 254f53fb31aSMichael Riesch static int rkcif_runtime_suspend(struct device *dev) 255f53fb31aSMichael Riesch { 256f53fb31aSMichael Riesch struct rkcif_device *rkcif = dev_get_drvdata(dev); 257f53fb31aSMichael Riesch 258f53fb31aSMichael Riesch /* 259f53fb31aSMichael Riesch * Reset CIF (CRU, DMA, FIFOs) to allow a clean resume. 260f53fb31aSMichael Riesch * Since this resets the IOMMU too, we cannot issue this reset when 261f53fb31aSMichael Riesch * resuming. 262f53fb31aSMichael Riesch */ 263f53fb31aSMichael Riesch reset_control_assert(rkcif->reset); 264f53fb31aSMichael Riesch udelay(5); 265f53fb31aSMichael Riesch reset_control_deassert(rkcif->reset); 266f53fb31aSMichael Riesch 267f53fb31aSMichael Riesch clk_bulk_disable_unprepare(rkcif->clks_num, rkcif->clks); 268f53fb31aSMichael Riesch 269f53fb31aSMichael Riesch return 0; 270f53fb31aSMichael Riesch } 271f53fb31aSMichael Riesch 272f53fb31aSMichael Riesch static int rkcif_runtime_resume(struct device *dev) 273f53fb31aSMichael Riesch { 274f53fb31aSMichael Riesch struct rkcif_device *rkcif = dev_get_drvdata(dev); 275f53fb31aSMichael Riesch int ret; 276f53fb31aSMichael Riesch 277f53fb31aSMichael Riesch ret = clk_bulk_prepare_enable(rkcif->clks_num, rkcif->clks); 278f53fb31aSMichael Riesch if (ret) { 279f53fb31aSMichael Riesch dev_err(dev, "failed to enable clocks\n"); 280f53fb31aSMichael Riesch return ret; 281f53fb31aSMichael Riesch } 282f53fb31aSMichael Riesch 283f53fb31aSMichael Riesch return 0; 284f53fb31aSMichael Riesch } 285f53fb31aSMichael Riesch 286f53fb31aSMichael Riesch static const struct dev_pm_ops rkcif_plat_pm_ops = { 287f53fb31aSMichael Riesch .runtime_suspend = rkcif_runtime_suspend, 288f53fb31aSMichael Riesch .runtime_resume = rkcif_runtime_resume, 289f53fb31aSMichael Riesch }; 290f53fb31aSMichael Riesch 291f53fb31aSMichael Riesch static struct platform_driver rkcif_plat_drv = { 292f53fb31aSMichael Riesch .driver = { 293f53fb31aSMichael Riesch .name = RKCIF_DRIVER_NAME, 294f53fb31aSMichael Riesch .of_match_table = rkcif_plat_of_match, 295f53fb31aSMichael Riesch .pm = &rkcif_plat_pm_ops, 296f53fb31aSMichael Riesch }, 297f53fb31aSMichael Riesch .probe = rkcif_probe, 298f53fb31aSMichael Riesch .remove = rkcif_remove, 299f53fb31aSMichael Riesch }; 300f53fb31aSMichael Riesch module_platform_driver(rkcif_plat_drv); 301f53fb31aSMichael Riesch 302f53fb31aSMichael Riesch MODULE_DESCRIPTION("Rockchip Camera Interface (CIF) platform driver"); 303f53fb31aSMichael Riesch MODULE_LICENSE("GPL"); 304