1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Driver for Renesas RZ/G2L CRU 4 * 5 * Copyright (C) 2022 Renesas Electronics Corp. 6 * 7 * Based on Renesas R-Car VIN 8 * Copyright (C) 2011-2013 Renesas Solutions Corp. 9 * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> 10 * Copyright (C) 2008 Magnus Damm 11 */ 12 13 #include <linux/clk.h> 14 #include <linux/module.h> 15 #include <linux/mod_devicetable.h> 16 #include <linux/of.h> 17 #include <linux/of_device.h> 18 #include <linux/of_graph.h> 19 #include <linux/platform_device.h> 20 #include <linux/pm_runtime.h> 21 22 #include <media/v4l2-fwnode.h> 23 #include <media/v4l2-mc.h> 24 25 #include "rzg2l-cru.h" 26 27 static inline struct rzg2l_cru_dev *notifier_to_cru(struct v4l2_async_notifier *n) 28 { 29 return container_of(n, struct rzg2l_cru_dev, notifier); 30 } 31 32 static const struct media_device_ops rzg2l_cru_media_ops = { 33 .link_notify = v4l2_pipeline_link_notify, 34 }; 35 36 /* ----------------------------------------------------------------------------- 37 * Group async notifier 38 */ 39 40 static int rzg2l_cru_group_notify_complete(struct v4l2_async_notifier *notifier) 41 { 42 struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); 43 struct media_entity *source, *sink; 44 int ret; 45 46 ret = rzg2l_cru_ip_subdev_register(cru); 47 if (ret) 48 return ret; 49 50 ret = v4l2_device_register_subdev_nodes(&cru->v4l2_dev); 51 if (ret) { 52 dev_err(cru->dev, "Failed to register subdev nodes\n"); 53 return ret; 54 } 55 56 ret = rzg2l_cru_video_register(cru); 57 if (ret) 58 return ret; 59 60 /* 61 * CRU can be connected either to CSI2 or PARALLEL device 62 * For now we are only supporting CSI2 63 * 64 * Create media device link between CSI-2 <-> CRU IP 65 */ 66 source = &cru->csi.subdev->entity; 67 sink = &cru->ip.subdev.entity; 68 ret = media_create_pad_link(source, 1, sink, 0, 69 MEDIA_LNK_FL_ENABLED | 70 MEDIA_LNK_FL_IMMUTABLE); 71 if (ret) { 72 dev_err(cru->dev, "Error creating link from %s to %s\n", 73 source->name, sink->name); 74 return ret; 75 } 76 cru->csi.channel = 0; 77 cru->ip.remote = cru->csi.subdev; 78 79 /* Create media device link between CRU IP <-> CRU OUTPUT */ 80 source = &cru->ip.subdev.entity; 81 sink = &cru->vdev.entity; 82 ret = media_create_pad_link(source, 1, sink, 0, 83 MEDIA_LNK_FL_ENABLED | 84 MEDIA_LNK_FL_IMMUTABLE); 85 if (ret) { 86 dev_err(cru->dev, "Error creating link from %s to %s\n", 87 source->name, sink->name); 88 return ret; 89 } 90 91 return 0; 92 } 93 94 static void rzg2l_cru_group_notify_unbind(struct v4l2_async_notifier *notifier, 95 struct v4l2_subdev *subdev, 96 struct v4l2_async_subdev *asd) 97 { 98 struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); 99 100 rzg2l_cru_ip_subdev_unregister(cru); 101 102 mutex_lock(&cru->mdev_lock); 103 104 if (cru->csi.asd == asd) { 105 cru->csi.subdev = NULL; 106 dev_dbg(cru->dev, "Unbind CSI-2 %s\n", subdev->name); 107 } 108 109 mutex_unlock(&cru->mdev_lock); 110 } 111 112 static int rzg2l_cru_group_notify_bound(struct v4l2_async_notifier *notifier, 113 struct v4l2_subdev *subdev, 114 struct v4l2_async_subdev *asd) 115 { 116 struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); 117 118 mutex_lock(&cru->mdev_lock); 119 120 if (cru->csi.asd == asd) { 121 cru->csi.subdev = subdev; 122 dev_dbg(cru->dev, "Bound CSI-2 %s\n", subdev->name); 123 } 124 125 mutex_unlock(&cru->mdev_lock); 126 127 return 0; 128 } 129 130 static const struct v4l2_async_notifier_operations rzg2l_cru_async_ops = { 131 .bound = rzg2l_cru_group_notify_bound, 132 .unbind = rzg2l_cru_group_notify_unbind, 133 .complete = rzg2l_cru_group_notify_complete, 134 }; 135 136 static int rzg2l_cru_mc_parse_of(struct rzg2l_cru_dev *cru) 137 { 138 struct v4l2_fwnode_endpoint vep = { 139 .bus_type = V4L2_MBUS_CSI2_DPHY, 140 }; 141 struct fwnode_handle *ep, *fwnode; 142 struct v4l2_async_subdev *asd; 143 int ret; 144 145 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(cru->dev), 1, 0, 0); 146 if (!ep) 147 return 0; 148 149 fwnode = fwnode_graph_get_remote_endpoint(ep); 150 ret = v4l2_fwnode_endpoint_parse(ep, &vep); 151 fwnode_handle_put(ep); 152 if (ret) { 153 dev_err(cru->dev, "Failed to parse %pOF\n", to_of_node(fwnode)); 154 ret = -EINVAL; 155 goto out; 156 } 157 158 if (!of_device_is_available(to_of_node(fwnode))) { 159 dev_dbg(cru->dev, "OF device %pOF disabled, ignoring\n", 160 to_of_node(fwnode)); 161 ret = -ENOTCONN; 162 goto out; 163 } 164 165 asd = v4l2_async_nf_add_fwnode(&cru->notifier, fwnode, 166 struct v4l2_async_subdev); 167 if (IS_ERR(asd)) { 168 ret = PTR_ERR(asd); 169 goto out; 170 } 171 172 cru->csi.asd = asd; 173 174 dev_dbg(cru->dev, "Added OF device %pOF to slot %u\n", 175 to_of_node(fwnode), vep.base.id); 176 out: 177 fwnode_handle_put(fwnode); 178 179 return ret; 180 } 181 182 static int rzg2l_cru_mc_parse_of_graph(struct rzg2l_cru_dev *cru) 183 { 184 int ret; 185 186 v4l2_async_nf_init(&cru->notifier); 187 188 ret = rzg2l_cru_mc_parse_of(cru); 189 if (ret) 190 return ret; 191 192 cru->notifier.ops = &rzg2l_cru_async_ops; 193 194 if (list_empty(&cru->notifier.asd_list)) 195 return 0; 196 197 ret = v4l2_async_nf_register(&cru->v4l2_dev, &cru->notifier); 198 if (ret < 0) { 199 dev_err(cru->dev, "Notifier registration failed\n"); 200 v4l2_async_nf_cleanup(&cru->notifier); 201 return ret; 202 } 203 204 return 0; 205 } 206 207 static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) 208 { 209 struct media_device *mdev = NULL; 210 const struct of_device_id *match; 211 int ret; 212 213 cru->pad.flags = MEDIA_PAD_FL_SINK; 214 ret = media_entity_pads_init(&cru->vdev.entity, 1, &cru->pad); 215 if (ret) 216 return ret; 217 218 mutex_init(&cru->mdev_lock); 219 mdev = &cru->mdev; 220 mdev->dev = cru->dev; 221 mdev->ops = &rzg2l_cru_media_ops; 222 223 match = of_match_node(cru->dev->driver->of_match_table, 224 cru->dev->of_node); 225 226 strscpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name)); 227 strscpy(mdev->model, match->compatible, sizeof(mdev->model)); 228 229 cru->v4l2_dev.mdev = &cru->mdev; 230 231 media_device_init(mdev); 232 233 ret = rzg2l_cru_mc_parse_of_graph(cru); 234 if (ret) { 235 mutex_lock(&cru->mdev_lock); 236 cru->v4l2_dev.mdev = NULL; 237 mutex_unlock(&cru->mdev_lock); 238 } 239 240 return 0; 241 } 242 243 static int rzg2l_cru_probe(struct platform_device *pdev) 244 { 245 struct rzg2l_cru_dev *cru; 246 int ret; 247 248 cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL); 249 if (!cru) 250 return -ENOMEM; 251 252 cru->base = devm_platform_ioremap_resource(pdev, 0); 253 if (IS_ERR(cru->base)) 254 return PTR_ERR(cru->base); 255 256 cru->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn"); 257 if (IS_ERR(cru->presetn)) 258 return dev_err_probe(&pdev->dev, PTR_ERR(cru->presetn), 259 "Failed to get cpg presetn\n"); 260 261 cru->aresetn = devm_reset_control_get_exclusive(&pdev->dev, "aresetn"); 262 if (IS_ERR(cru->aresetn)) 263 return dev_err_probe(&pdev->dev, PTR_ERR(cru->aresetn), 264 "Failed to get cpg aresetn\n"); 265 266 cru->vclk = devm_clk_get(&pdev->dev, "video"); 267 if (IS_ERR(cru->vclk)) 268 return dev_err_probe(&pdev->dev, PTR_ERR(cru->vclk), 269 "Failed to get video clock\n"); 270 271 cru->dev = &pdev->dev; 272 cru->info = of_device_get_match_data(&pdev->dev); 273 274 cru->image_conv_irq = platform_get_irq(pdev, 0); 275 if (cru->image_conv_irq < 0) 276 return cru->image_conv_irq; 277 278 platform_set_drvdata(pdev, cru); 279 280 ret = rzg2l_cru_dma_register(cru); 281 if (ret) 282 return ret; 283 284 cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT; 285 pm_suspend_ignore_children(&pdev->dev, true); 286 pm_runtime_enable(&pdev->dev); 287 288 ret = rzg2l_cru_media_init(cru); 289 if (ret) 290 goto error_dma_unregister; 291 292 return 0; 293 294 error_dma_unregister: 295 rzg2l_cru_dma_unregister(cru); 296 pm_runtime_disable(&pdev->dev); 297 298 return ret; 299 } 300 301 static void rzg2l_cru_remove(struct platform_device *pdev) 302 { 303 struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev); 304 305 pm_runtime_disable(&pdev->dev); 306 307 v4l2_async_nf_unregister(&cru->notifier); 308 v4l2_async_nf_cleanup(&cru->notifier); 309 310 rzg2l_cru_video_unregister(cru); 311 media_device_cleanup(&cru->mdev); 312 mutex_destroy(&cru->mdev_lock); 313 314 rzg2l_cru_dma_unregister(cru); 315 } 316 317 static const struct of_device_id rzg2l_cru_of_id_table[] = { 318 { .compatible = "renesas,rzg2l-cru", }, 319 { /* sentinel */ } 320 }; 321 MODULE_DEVICE_TABLE(of, rzg2l_cru_of_id_table); 322 323 static struct platform_driver rzg2l_cru_driver = { 324 .driver = { 325 .name = "rzg2l-cru", 326 .of_match_table = rzg2l_cru_of_id_table, 327 }, 328 .probe = rzg2l_cru_probe, 329 .remove_new = rzg2l_cru_remove, 330 }; 331 332 module_platform_driver(rzg2l_cru_driver); 333 334 MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); 335 MODULE_DESCRIPTION("Renesas RZ/G2L CRU driver"); 336 MODULE_LICENSE("GPL"); 337