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