1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2016 NextThing Co 4 * Copyright (C) 2016-2019 Bootlin 5 * 6 * Author: Maxime Ripard <maxime.ripard@bootlin.com> 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/dma-mapping.h> 11 #include <linux/interrupt.h> 12 #include <linux/module.h> 13 #include <linux/mutex.h> 14 #include <linux/of.h> 15 #include <linux/of_graph.h> 16 #include <linux/platform_device.h> 17 #include <linux/pm_runtime.h> 18 #include <linux/reset.h> 19 #include <linux/videodev2.h> 20 21 #include <media/v4l2-dev.h> 22 #include <media/v4l2-device.h> 23 #include <media/v4l2-fwnode.h> 24 #include <media/v4l2-ioctl.h> 25 #include <media/v4l2-mediabus.h> 26 27 #include <media/videobuf2-core.h> 28 #include <media/videobuf2-dma-contig.h> 29 30 #include "sun4i_csi.h" 31 32 struct sun4i_csi_traits { 33 unsigned int channels; 34 unsigned int max_width; 35 bool has_isp; 36 }; 37 38 static int sun4i_csi_video_link_validate(struct media_link *link) 39 { 40 dev_warn_once(link->graph_obj.mdev->dev, 41 "Driver bug: link validation not implemented\n"); 42 return 0; 43 } 44 45 static const struct media_entity_operations sun4i_csi_video_entity_ops = { 46 .link_validate = sun4i_csi_video_link_validate, 47 }; 48 49 static const struct media_entity_operations sun4i_csi_subdev_entity_ops = { 50 .link_validate = v4l2_subdev_link_validate, 51 }; 52 53 static int sun4i_csi_notify_bound(struct v4l2_async_notifier *notifier, 54 struct v4l2_subdev *subdev, 55 struct v4l2_async_connection *asd) 56 { 57 struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi, 58 notifier); 59 60 csi->src_subdev = subdev; 61 csi->src_pad = media_entity_get_fwnode_pad(&subdev->entity, 62 subdev->fwnode, 63 MEDIA_PAD_FL_SOURCE); 64 if (csi->src_pad < 0) { 65 dev_err(csi->dev, "Couldn't find output pad for subdev %s\n", 66 subdev->name); 67 return csi->src_pad; 68 } 69 70 dev_dbg(csi->dev, "Bound %s pad: %d\n", subdev->name, csi->src_pad); 71 return 0; 72 } 73 74 static int sun4i_csi_notify_complete(struct v4l2_async_notifier *notifier) 75 { 76 struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi, 77 notifier); 78 struct v4l2_subdev *subdev = &csi->subdev; 79 struct video_device *vdev = &csi->vdev; 80 int ret; 81 82 ret = v4l2_device_register_subdev(&csi->v4l, subdev); 83 if (ret < 0) 84 return ret; 85 86 ret = sun4i_csi_v4l2_register(csi); 87 if (ret < 0) 88 return ret; 89 90 ret = media_device_register(&csi->mdev); 91 if (ret) 92 return ret; 93 94 /* Create link from subdev to main device */ 95 ret = media_create_pad_link(&subdev->entity, CSI_SUBDEV_SOURCE, 96 &vdev->entity, 0, 97 MEDIA_LNK_FL_ENABLED | 98 MEDIA_LNK_FL_IMMUTABLE); 99 if (ret) 100 goto err_clean_media; 101 102 ret = media_create_pad_link(&csi->src_subdev->entity, csi->src_pad, 103 &subdev->entity, CSI_SUBDEV_SINK, 104 MEDIA_LNK_FL_ENABLED | 105 MEDIA_LNK_FL_IMMUTABLE); 106 if (ret) 107 goto err_clean_media; 108 109 ret = v4l2_device_register_subdev_nodes(&csi->v4l); 110 if (ret < 0) 111 goto err_clean_media; 112 113 return 0; 114 115 err_clean_media: 116 media_device_unregister(&csi->mdev); 117 118 return ret; 119 } 120 121 static const struct v4l2_async_notifier_operations sun4i_csi_notify_ops = { 122 .bound = sun4i_csi_notify_bound, 123 .complete = sun4i_csi_notify_complete, 124 }; 125 126 static int sun4i_csi_notifier_init(struct sun4i_csi *csi) 127 { 128 struct v4l2_fwnode_endpoint vep = { 129 .bus_type = V4L2_MBUS_PARALLEL, 130 }; 131 struct v4l2_async_connection *asd; 132 struct fwnode_handle *ep; 133 int ret; 134 135 v4l2_async_nf_init(&csi->notifier, &csi->v4l); 136 137 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, 138 FWNODE_GRAPH_ENDPOINT_NEXT); 139 if (!ep) 140 return -EINVAL; 141 142 ret = v4l2_fwnode_endpoint_parse(ep, &vep); 143 if (ret) 144 goto out; 145 146 csi->bus = vep.bus.parallel; 147 148 asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, 149 struct v4l2_async_connection); 150 if (IS_ERR(asd)) { 151 ret = PTR_ERR(asd); 152 goto out; 153 } 154 155 csi->notifier.ops = &sun4i_csi_notify_ops; 156 157 out: 158 fwnode_handle_put(ep); 159 return ret; 160 } 161 162 static int sun4i_csi_probe(struct platform_device *pdev) 163 { 164 struct v4l2_subdev *subdev; 165 struct video_device *vdev; 166 struct sun4i_csi *csi; 167 int ret; 168 int irq; 169 170 csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); 171 if (!csi) 172 return -ENOMEM; 173 platform_set_drvdata(pdev, csi); 174 csi->dev = &pdev->dev; 175 subdev = &csi->subdev; 176 vdev = &csi->vdev; 177 178 csi->traits = of_device_get_match_data(&pdev->dev); 179 if (!csi->traits) 180 return -EINVAL; 181 182 csi->mdev.dev = csi->dev; 183 strscpy(csi->mdev.model, "Allwinner Video Capture Device", 184 sizeof(csi->mdev.model)); 185 csi->mdev.hw_revision = 0; 186 media_device_init(&csi->mdev); 187 csi->v4l.mdev = &csi->mdev; 188 189 csi->regs = devm_platform_ioremap_resource(pdev, 0); 190 if (IS_ERR(csi->regs)) 191 return PTR_ERR(csi->regs); 192 193 irq = platform_get_irq(pdev, 0); 194 if (irq < 0) 195 return irq; 196 197 csi->bus_clk = devm_clk_get(&pdev->dev, "bus"); 198 if (IS_ERR(csi->bus_clk)) { 199 dev_err(&pdev->dev, "Couldn't get our bus clock\n"); 200 return PTR_ERR(csi->bus_clk); 201 } 202 203 if (csi->traits->has_isp) { 204 csi->isp_clk = devm_clk_get(&pdev->dev, "isp"); 205 if (IS_ERR(csi->isp_clk)) { 206 dev_err(&pdev->dev, "Couldn't get our ISP clock\n"); 207 return PTR_ERR(csi->isp_clk); 208 } 209 } 210 211 csi->ram_clk = devm_clk_get(&pdev->dev, "ram"); 212 if (IS_ERR(csi->ram_clk)) { 213 dev_err(&pdev->dev, "Couldn't get our ram clock\n"); 214 return PTR_ERR(csi->ram_clk); 215 } 216 217 csi->rst = devm_reset_control_get(&pdev->dev, NULL); 218 if (IS_ERR(csi->rst)) { 219 dev_err(&pdev->dev, "Couldn't get our reset line\n"); 220 return PTR_ERR(csi->rst); 221 } 222 223 /* Initialize subdev */ 224 v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops); 225 subdev->internal_ops = &sun4i_csi_subdev_internal_ops; 226 subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 227 subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 228 subdev->entity.ops = &sun4i_csi_subdev_entity_ops; 229 subdev->owner = THIS_MODULE; 230 snprintf(subdev->name, sizeof(subdev->name), "sun4i-csi-0"); 231 v4l2_set_subdevdata(subdev, csi); 232 233 csi->subdev_pads[CSI_SUBDEV_SINK].flags = 234 MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 235 csi->subdev_pads[CSI_SUBDEV_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 236 ret = media_entity_pads_init(&subdev->entity, CSI_SUBDEV_PADS, 237 csi->subdev_pads); 238 if (ret < 0) 239 return ret; 240 241 csi->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 242 vdev->entity.ops = &sun4i_csi_video_entity_ops; 243 ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad); 244 if (ret < 0) 245 return ret; 246 247 ret = sun4i_csi_dma_register(csi, irq); 248 if (ret) 249 goto err_clean_pad; 250 251 ret = sun4i_csi_notifier_init(csi); 252 if (ret) 253 goto err_unregister_media; 254 255 ret = v4l2_async_nf_register(&csi->notifier); 256 if (ret) { 257 dev_err(csi->dev, "Couldn't register our notifier.\n"); 258 goto err_unregister_media; 259 } 260 261 pm_runtime_enable(&pdev->dev); 262 263 return 0; 264 265 err_unregister_media: 266 media_device_unregister(&csi->mdev); 267 sun4i_csi_dma_unregister(csi); 268 269 err_clean_pad: 270 media_device_cleanup(&csi->mdev); 271 272 return ret; 273 } 274 275 static void sun4i_csi_remove(struct platform_device *pdev) 276 { 277 struct sun4i_csi *csi = platform_get_drvdata(pdev); 278 279 pm_runtime_disable(&pdev->dev); 280 v4l2_async_nf_unregister(&csi->notifier); 281 v4l2_async_nf_cleanup(&csi->notifier); 282 vb2_video_unregister_device(&csi->vdev); 283 media_device_unregister(&csi->mdev); 284 sun4i_csi_dma_unregister(csi); 285 media_device_cleanup(&csi->mdev); 286 } 287 288 static const struct sun4i_csi_traits sun4i_a10_csi1_traits = { 289 .channels = 1, 290 .max_width = 24, 291 .has_isp = false, 292 }; 293 294 static const struct sun4i_csi_traits sun7i_a20_csi0_traits = { 295 .channels = 4, 296 .max_width = 16, 297 .has_isp = true, 298 }; 299 300 static const struct of_device_id sun4i_csi_of_match[] = { 301 { .compatible = "allwinner,sun4i-a10-csi1", .data = &sun4i_a10_csi1_traits }, 302 { .compatible = "allwinner,sun7i-a20-csi0", .data = &sun7i_a20_csi0_traits }, 303 { /* Sentinel */ } 304 }; 305 MODULE_DEVICE_TABLE(of, sun4i_csi_of_match); 306 307 static int __maybe_unused sun4i_csi_runtime_resume(struct device *dev) 308 { 309 struct sun4i_csi *csi = dev_get_drvdata(dev); 310 311 reset_control_deassert(csi->rst); 312 clk_prepare_enable(csi->bus_clk); 313 clk_prepare_enable(csi->ram_clk); 314 clk_set_rate(csi->isp_clk, 80000000); 315 clk_prepare_enable(csi->isp_clk); 316 317 writel(1, csi->regs + CSI_EN_REG); 318 319 return 0; 320 } 321 322 static int __maybe_unused sun4i_csi_runtime_suspend(struct device *dev) 323 { 324 struct sun4i_csi *csi = dev_get_drvdata(dev); 325 326 clk_disable_unprepare(csi->isp_clk); 327 clk_disable_unprepare(csi->ram_clk); 328 clk_disable_unprepare(csi->bus_clk); 329 330 reset_control_assert(csi->rst); 331 332 return 0; 333 } 334 335 static const struct dev_pm_ops sun4i_csi_pm_ops = { 336 SET_RUNTIME_PM_OPS(sun4i_csi_runtime_suspend, 337 sun4i_csi_runtime_resume, 338 NULL) 339 }; 340 341 static struct platform_driver sun4i_csi_driver = { 342 .probe = sun4i_csi_probe, 343 .remove_new = sun4i_csi_remove, 344 .driver = { 345 .name = "sun4i-csi", 346 .of_match_table = sun4i_csi_of_match, 347 .pm = &sun4i_csi_pm_ops, 348 }, 349 }; 350 module_platform_driver(sun4i_csi_driver); 351 352 MODULE_DESCRIPTION("Allwinner A10 Camera Sensor Interface driver"); 353 MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); 354 MODULE_LICENSE("GPL"); 355