1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) 4 * Author: Yong Deng <yong.deng@magewell.com> 5 * Copyright 2021-2022 Bootlin 6 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/err.h> 11 #include <linux/interrupt.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/pm_runtime.h> 16 #include <linux/regmap.h> 17 #include <linux/reset.h> 18 #include <media/v4l2-device.h> 19 #include <media/v4l2-mc.h> 20 21 #include "sun6i_csi.h" 22 #include "sun6i_csi_bridge.h" 23 #include "sun6i_csi_capture.h" 24 #include "sun6i_csi_reg.h" 25 26 /* ISP */ 27 28 int sun6i_csi_isp_complete(struct sun6i_csi_device *csi_dev, 29 struct v4l2_device *v4l2_dev) 30 { 31 if (csi_dev->v4l2_dev && csi_dev->v4l2_dev != v4l2_dev) 32 return -EINVAL; 33 34 csi_dev->v4l2_dev = v4l2_dev; 35 csi_dev->media_dev = v4l2_dev->mdev; 36 37 return sun6i_csi_capture_setup(csi_dev); 38 } 39 40 static int sun6i_csi_isp_detect(struct sun6i_csi_device *csi_dev) 41 { 42 struct device *dev = csi_dev->dev; 43 struct fwnode_handle *handle; 44 45 /* 46 * ISP is not available if not connected via fwnode graph. 47 * This will also check that the remote parent node is available. 48 */ 49 handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 50 SUN6I_CSI_PORT_ISP, 0, 51 FWNODE_GRAPH_ENDPOINT_NEXT); 52 if (!handle) 53 return 0; 54 55 fwnode_handle_put(handle); 56 57 if (!IS_ENABLED(CONFIG_VIDEO_SUN6I_ISP)) { 58 dev_warn(dev, 59 "ISP link is detected but not enabled in kernel config!"); 60 return 0; 61 } 62 63 csi_dev->isp_available = true; 64 65 return 0; 66 } 67 68 /* Media */ 69 70 static const struct media_device_ops sun6i_csi_media_ops = { 71 .link_notify = v4l2_pipeline_link_notify, 72 }; 73 74 /* V4L2 */ 75 76 static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev) 77 { 78 struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; 79 struct media_device *media_dev = &v4l2->media_dev; 80 struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; 81 struct device *dev = csi_dev->dev; 82 int ret; 83 84 /* Media Device */ 85 86 strscpy(media_dev->model, SUN6I_CSI_DESCRIPTION, 87 sizeof(media_dev->model)); 88 media_dev->hw_revision = 0; 89 media_dev->ops = &sun6i_csi_media_ops; 90 media_dev->dev = dev; 91 92 media_device_init(media_dev); 93 94 ret = media_device_register(media_dev); 95 if (ret) { 96 dev_err(dev, "failed to register media device: %d\n", ret); 97 goto error_media; 98 } 99 100 /* V4L2 Device */ 101 102 v4l2_dev->mdev = media_dev; 103 104 ret = v4l2_device_register(dev, v4l2_dev); 105 if (ret) { 106 dev_err(dev, "failed to register v4l2 device: %d\n", ret); 107 goto error_media; 108 } 109 110 csi_dev->v4l2_dev = v4l2_dev; 111 csi_dev->media_dev = media_dev; 112 113 return 0; 114 115 error_media: 116 media_device_unregister(media_dev); 117 media_device_cleanup(media_dev); 118 119 return ret; 120 } 121 122 static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev) 123 { 124 struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; 125 126 media_device_unregister(&v4l2->media_dev); 127 v4l2_device_unregister(&v4l2->v4l2_dev); 128 media_device_cleanup(&v4l2->media_dev); 129 } 130 131 /* Platform */ 132 133 static irqreturn_t sun6i_csi_interrupt(int irq, void *private) 134 { 135 struct sun6i_csi_device *csi_dev = private; 136 bool capture_streaming = csi_dev->capture.state.streaming; 137 struct regmap *regmap = csi_dev->regmap; 138 u32 status = 0, enable = 0; 139 140 regmap_read(regmap, SUN6I_CSI_CH_INT_STA_REG, &status); 141 regmap_read(regmap, SUN6I_CSI_CH_INT_EN_REG, &enable); 142 143 if (!status) 144 return IRQ_NONE; 145 else if (!(status & enable) || !capture_streaming) 146 goto complete; 147 148 if ((status & SUN6I_CSI_CH_INT_STA_FIFO0_OF) || 149 (status & SUN6I_CSI_CH_INT_STA_FIFO1_OF) || 150 (status & SUN6I_CSI_CH_INT_STA_FIFO2_OF) || 151 (status & SUN6I_CSI_CH_INT_STA_HB_OF)) { 152 regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status); 153 154 regmap_update_bits(regmap, SUN6I_CSI_EN_REG, 155 SUN6I_CSI_EN_CSI_EN, 0); 156 regmap_update_bits(regmap, SUN6I_CSI_EN_REG, 157 SUN6I_CSI_EN_CSI_EN, SUN6I_CSI_EN_CSI_EN); 158 return IRQ_HANDLED; 159 } 160 161 if (status & SUN6I_CSI_CH_INT_STA_FD) 162 sun6i_csi_capture_frame_done(csi_dev); 163 164 if (status & SUN6I_CSI_CH_INT_STA_VS) 165 sun6i_csi_capture_sync(csi_dev); 166 167 complete: 168 regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status); 169 170 return IRQ_HANDLED; 171 } 172 173 static int sun6i_csi_suspend(struct device *dev) 174 { 175 struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev); 176 177 reset_control_assert(csi_dev->reset); 178 clk_disable_unprepare(csi_dev->clock_ram); 179 clk_disable_unprepare(csi_dev->clock_mod); 180 181 return 0; 182 } 183 184 static int sun6i_csi_resume(struct device *dev) 185 { 186 struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev); 187 int ret; 188 189 ret = reset_control_deassert(csi_dev->reset); 190 if (ret) { 191 dev_err(dev, "failed to deassert reset\n"); 192 return ret; 193 } 194 195 ret = clk_prepare_enable(csi_dev->clock_mod); 196 if (ret) { 197 dev_err(dev, "failed to enable module clock\n"); 198 goto error_reset; 199 } 200 201 ret = clk_prepare_enable(csi_dev->clock_ram); 202 if (ret) { 203 dev_err(dev, "failed to enable ram clock\n"); 204 goto error_clock_mod; 205 } 206 207 return 0; 208 209 error_clock_mod: 210 clk_disable_unprepare(csi_dev->clock_mod); 211 212 error_reset: 213 reset_control_assert(csi_dev->reset); 214 215 return ret; 216 } 217 218 static const struct dev_pm_ops sun6i_csi_pm_ops = { 219 .runtime_suspend = sun6i_csi_suspend, 220 .runtime_resume = sun6i_csi_resume, 221 }; 222 223 static const struct regmap_config sun6i_csi_regmap_config = { 224 .reg_bits = 32, 225 .reg_stride = 4, 226 .val_bits = 32, 227 .max_register = 0x9c, 228 }; 229 230 static int sun6i_csi_resources_setup(struct sun6i_csi_device *csi_dev, 231 struct platform_device *platform_dev) 232 { 233 struct device *dev = csi_dev->dev; 234 const struct sun6i_csi_variant *variant; 235 void __iomem *io_base; 236 int ret; 237 int irq; 238 239 variant = of_device_get_match_data(dev); 240 if (!variant) 241 return -EINVAL; 242 243 /* Registers */ 244 245 io_base = devm_platform_ioremap_resource(platform_dev, 0); 246 if (IS_ERR(io_base)) 247 return PTR_ERR(io_base); 248 249 csi_dev->regmap = devm_regmap_init_mmio_clk(dev, "bus", io_base, 250 &sun6i_csi_regmap_config); 251 if (IS_ERR(csi_dev->regmap)) { 252 dev_err(dev, "failed to init register map\n"); 253 return PTR_ERR(csi_dev->regmap); 254 } 255 256 /* Clocks */ 257 258 csi_dev->clock_mod = devm_clk_get(dev, "mod"); 259 if (IS_ERR(csi_dev->clock_mod)) { 260 dev_err(dev, "failed to acquire module clock\n"); 261 return PTR_ERR(csi_dev->clock_mod); 262 } 263 264 csi_dev->clock_ram = devm_clk_get(dev, "ram"); 265 if (IS_ERR(csi_dev->clock_ram)) { 266 dev_err(dev, "failed to acquire ram clock\n"); 267 return PTR_ERR(csi_dev->clock_ram); 268 } 269 270 ret = clk_set_rate_exclusive(csi_dev->clock_mod, 271 variant->clock_mod_rate); 272 if (ret) { 273 dev_err(dev, "failed to set mod clock rate\n"); 274 return ret; 275 } 276 277 /* Reset */ 278 279 csi_dev->reset = devm_reset_control_get_shared(dev, NULL); 280 if (IS_ERR(csi_dev->reset)) { 281 dev_err(dev, "failed to acquire reset\n"); 282 ret = PTR_ERR(csi_dev->reset); 283 goto error_clock_rate_exclusive; 284 } 285 286 /* Interrupt */ 287 288 irq = platform_get_irq(platform_dev, 0); 289 if (irq < 0) { 290 ret = -ENXIO; 291 goto error_clock_rate_exclusive; 292 } 293 294 ret = devm_request_irq(dev, irq, sun6i_csi_interrupt, IRQF_SHARED, 295 SUN6I_CSI_NAME, csi_dev); 296 if (ret) { 297 dev_err(dev, "failed to request interrupt\n"); 298 goto error_clock_rate_exclusive; 299 } 300 301 /* Runtime PM */ 302 303 pm_runtime_enable(dev); 304 305 return 0; 306 307 error_clock_rate_exclusive: 308 clk_rate_exclusive_put(csi_dev->clock_mod); 309 310 return ret; 311 } 312 313 static void sun6i_csi_resources_cleanup(struct sun6i_csi_device *csi_dev) 314 { 315 pm_runtime_disable(csi_dev->dev); 316 clk_rate_exclusive_put(csi_dev->clock_mod); 317 } 318 319 static int sun6i_csi_probe(struct platform_device *platform_dev) 320 { 321 struct sun6i_csi_device *csi_dev; 322 struct device *dev = &platform_dev->dev; 323 int ret; 324 325 csi_dev = devm_kzalloc(dev, sizeof(*csi_dev), GFP_KERNEL); 326 if (!csi_dev) 327 return -ENOMEM; 328 329 csi_dev->dev = &platform_dev->dev; 330 platform_set_drvdata(platform_dev, csi_dev); 331 332 ret = sun6i_csi_resources_setup(csi_dev, platform_dev); 333 if (ret) 334 return ret; 335 336 ret = sun6i_csi_isp_detect(csi_dev); 337 if (ret) 338 goto error_resources; 339 340 /* 341 * Register our own v4l2 and media devices when there is no ISP around. 342 * Otherwise the ISP will use async subdev registration with our bridge, 343 * which will provide v4l2 and media devices that are used to register 344 * the video interface. 345 */ 346 if (!csi_dev->isp_available) { 347 ret = sun6i_csi_v4l2_setup(csi_dev); 348 if (ret) 349 goto error_resources; 350 } 351 352 ret = sun6i_csi_bridge_setup(csi_dev); 353 if (ret) 354 goto error_v4l2; 355 356 if (!csi_dev->isp_available) { 357 ret = sun6i_csi_capture_setup(csi_dev); 358 if (ret) 359 goto error_bridge; 360 } 361 362 return 0; 363 364 error_bridge: 365 sun6i_csi_bridge_cleanup(csi_dev); 366 367 error_v4l2: 368 if (!csi_dev->isp_available) 369 sun6i_csi_v4l2_cleanup(csi_dev); 370 371 error_resources: 372 sun6i_csi_resources_cleanup(csi_dev); 373 374 return ret; 375 } 376 377 static void sun6i_csi_remove(struct platform_device *pdev) 378 { 379 struct sun6i_csi_device *csi_dev = platform_get_drvdata(pdev); 380 381 sun6i_csi_capture_cleanup(csi_dev); 382 sun6i_csi_bridge_cleanup(csi_dev); 383 384 if (!csi_dev->isp_available) 385 sun6i_csi_v4l2_cleanup(csi_dev); 386 387 sun6i_csi_resources_cleanup(csi_dev); 388 } 389 390 static const struct sun6i_csi_variant sun6i_a31_csi_variant = { 391 .clock_mod_rate = 297000000, 392 }; 393 394 static const struct sun6i_csi_variant sun50i_a64_csi_variant = { 395 .clock_mod_rate = 300000000, 396 }; 397 398 static const struct of_device_id sun6i_csi_of_match[] = { 399 { 400 .compatible = "allwinner,sun6i-a31-csi", 401 .data = &sun6i_a31_csi_variant, 402 }, 403 { 404 .compatible = "allwinner,sun8i-a83t-csi", 405 .data = &sun6i_a31_csi_variant, 406 }, 407 { 408 .compatible = "allwinner,sun8i-h3-csi", 409 .data = &sun6i_a31_csi_variant, 410 }, 411 { 412 .compatible = "allwinner,sun8i-v3s-csi", 413 .data = &sun6i_a31_csi_variant, 414 }, 415 { 416 .compatible = "allwinner,sun50i-a64-csi", 417 .data = &sun50i_a64_csi_variant, 418 }, 419 {}, 420 }; 421 422 MODULE_DEVICE_TABLE(of, sun6i_csi_of_match); 423 424 static struct platform_driver sun6i_csi_platform_driver = { 425 .probe = sun6i_csi_probe, 426 .remove_new = sun6i_csi_remove, 427 .driver = { 428 .name = SUN6I_CSI_NAME, 429 .of_match_table = sun6i_csi_of_match, 430 .pm = &sun6i_csi_pm_ops, 431 }, 432 }; 433 434 module_platform_driver(sun6i_csi_platform_driver); 435 436 MODULE_DESCRIPTION("Allwinner A31 Camera Sensor Interface driver"); 437 MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>"); 438 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 439 MODULE_LICENSE("GPL"); 440