FILE
rzg2l-core.c
File preview with syntax highlighting.
linux-6.19.14/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
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_graph.h> 18#include <linux/platform_device.h> 19#include <linux/pm_runtime.h> 20 21#include <media/v4l2-fwnode.h> 22#include <media/v4l2-mc.h> 23 24#include "rzg2l-cru.h" 25#include "rzg2l-cru-regs.h" 26 27static 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 32static 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 40static 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->ip.remote = cru->csi.subdev; 77 78 /* Create media device link between CRU IP <-> CRU OUTPUT */ 79 source = &cru->ip.subdev.entity; 80 sink = &cru->vdev.entity; 81 ret = media_create_pad_link(source, 1, sink, 0, 82 MEDIA_LNK_FL_ENABLED | 83 MEDIA_LNK_FL_IMMUTABLE); 84 if (ret) { 85 dev_err(cru->dev, "Error creating link from %s to %s\n", 86 source->name, sink->name); 87 return ret; 88 } 89 90 return 0; 91} 92 93static void rzg2l_cru_group_notify_unbind(struct v4l2_async_notifier *notifier, 94 struct v4l2_subdev *subdev, 95 struct v4l2_async_connection *asd) 96{ 97 struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); 98 99 rzg2l_cru_ip_subdev_unregister(cru); 100 101 mutex_lock(&cru->mdev_lock); 102 103 if (cru->csi.asd == asd) { 104 cru->csi.subdev = NULL; 105 dev_dbg(cru->dev, "Unbind CSI-2 %s\n", subdev->name); 106 } 107 108 mutex_unlock(&cru->mdev_lock); 109} 110 111static int rzg2l_cru_group_notify_bound(struct v4l2_async_notifier *notifier, 112 struct v4l2_subdev *subdev, 113 struct v4l2_async_connection *asd) 114{ 115 struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); 116 117 mutex_lock(&cru->mdev_lock); 118 119 if (cru->csi.asd == asd) { 120 cru->csi.subdev = subdev; 121 dev_dbg(cru->dev, "Bound CSI-2 %s\n", subdev->name); 122 } 123 124 mutex_unlock(&cru->mdev_lock); 125 126 return 0; 127} 128 129static const struct v4l2_async_notifier_operations rzg2l_cru_async_ops = { 130 .bound = rzg2l_cru_group_notify_bound, 131 .unbind = rzg2l_cru_group_notify_unbind, 132 .complete = rzg2l_cru_group_notify_complete, 133}; 134 135static int rzg2l_cru_mc_parse_of(struct rzg2l_cru_dev *cru) 136{ 137 struct v4l2_fwnode_endpoint vep = { 138 .bus_type = V4L2_MBUS_CSI2_DPHY, 139 }; 140 struct fwnode_handle *ep, *fwnode; 141 struct v4l2_async_connection *asd; 142 int ret; 143 144 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(cru->dev), 1, 0, 0); 145 if (!ep) 146 return 0; 147 148 fwnode = fwnode_graph_get_remote_endpoint(ep); 149 ret = v4l2_fwnode_endpoint_parse(ep, &vep); 150 fwnode_handle_put(ep); 151 if (ret) { 152 dev_err(cru->dev, "Failed to parse %pOF\n", to_of_node(fwnode)); 153 ret = -EINVAL; 154 goto out; 155 } 156 157 if (!of_device_is_available(to_of_node(fwnode))) { 158 dev_dbg(cru->dev, "OF device %pOF disabled, ignoring\n", 159 to_of_node(fwnode)); 160 ret = -ENOTCONN; 161 goto out; 162 } 163 164 asd = v4l2_async_nf_add_fwnode(&cru->notifier, fwnode, 165 struct v4l2_async_connection); 166 if (IS_ERR(asd)) { 167 ret = PTR_ERR(asd); 168 goto out; 169 } 170 171 cru->csi.asd = asd; 172 173 dev_dbg(cru->dev, "Added OF device %pOF to slot %u\n", 174 to_of_node(fwnode), vep.base.id); 175out: 176 fwnode_handle_put(fwnode); 177 178 return ret; 179} 180 181static int rzg2l_cru_mc_parse_of_graph(struct rzg2l_cru_dev *cru) 182{ 183 int ret; 184 185 v4l2_async_nf_init(&cru->notifier, &cru->v4l2_dev); 186 187 ret = rzg2l_cru_mc_parse_of(cru); 188 if (ret) 189 return ret; 190 191 cru->notifier.ops = &rzg2l_cru_async_ops; 192 193 if (list_empty(&cru->notifier.waiting_list)) 194 return 0; 195 196 ret = v4l2_async_nf_register(&cru->notifier); 197 if (ret < 0) { 198 dev_err(cru->dev, "Notifier registration failed\n"); 199 v4l2_async_nf_cleanup(&cru->notifier); 200 return ret; 201 } 202 203 return 0; 204} 205 206static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) 207{ 208 struct media_device *mdev = NULL; 209 const struct of_device_id *match; 210 int ret; 211 212 cru->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 213 ret = media_entity_pads_init(&cru->vdev.entity, 1, &cru->pad); 214 if (ret) 215 return ret; 216 217 mutex_init(&cru->mdev_lock); 218 mdev = &cru->mdev; 219 mdev->dev = cru->dev; 220 mdev->ops = &rzg2l_cru_media_ops; 221 222 match = of_match_node(cru->dev->driver->of_match_table, 223 cru->dev->of_node); 224 225 strscpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name)); 226 strscpy(mdev->model, match->compatible, sizeof(mdev->model)); 227 228 cru->v4l2_dev.mdev = &cru->mdev; 229 230 media_device_init(mdev); 231 232 ret = rzg2l_cru_mc_parse_of_graph(cru); 233 if (ret) { 234 mutex_lock(&cru->mdev_lock); 235 cru->v4l2_dev.mdev = NULL; 236 mutex_unlock(&cru->mdev_lock); 237 } 238 239 return 0; 240} 241 242static int rzg2l_cru_probe(struct platform_device *pdev) 243{ 244 struct device *dev = &pdev->dev; 245 struct rzg2l_cru_dev *cru; 246 int irq, ret; 247 248 cru = devm_kzalloc(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(dev, "presetn"); 257 if (IS_ERR(cru->presetn)) 258 return dev_err_probe(dev, PTR_ERR(cru->presetn), 259 "Failed to get cpg presetn\n"); 260 261 cru->aresetn = devm_reset_control_get_exclusive(dev, "aresetn"); 262 if (IS_ERR(cru->aresetn)) 263 return dev_err_probe(dev, PTR_ERR(cru->aresetn), 264 "Failed to get cpg aresetn\n"); 265 266 cru->vclk = devm_clk_get(dev, "video"); 267 if (IS_ERR(cru->vclk)) 268 return dev_err_probe(dev, PTR_ERR(cru->vclk), 269 "Failed to get video clock\n"); 270 271 cru->dev = dev; 272 cru->info = of_device_get_match_data(dev); 273 274 irq = platform_get_irq(pdev, 0); 275 if (irq < 0) 276 return irq; 277 278 ret = devm_request_irq(dev, irq, cru->info->irq_handler, 0, 279 KBUILD_MODNAME, cru); 280 if (ret) 281 return dev_err_probe(dev, ret, "failed to request irq\n"); 282 283 platform_set_drvdata(pdev, cru); 284 285 ret = rzg2l_cru_dma_register(cru); 286 if (ret) 287 return ret; 288 289 cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT; 290 pm_suspend_ignore_children(dev, true); 291 ret = devm_pm_runtime_enable(dev); 292 if (ret) 293 goto error_dma_unregister; 294 295 ret = rzg2l_cru_media_init(cru); 296 if (ret) 297 goto error_dma_unregister; 298 299 return 0; 300 301error_dma_unregister: 302 rzg2l_cru_dma_unregister(cru); 303 304 return ret; 305} 306 307static void rzg2l_cru_remove(struct platform_device *pdev) 308{ 309 struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev); 310 311 v4l2_async_nf_unregister(&cru->notifier); 312 v4l2_async_nf_cleanup(&cru->notifier); 313 314 rzg2l_cru_video_unregister(cru); 315 media_device_cleanup(&cru->mdev); 316 mutex_destroy(&cru->mdev_lock); 317 318 rzg2l_cru_dma_unregister(cru); 319} 320 321static const u16 rzg3e_cru_regs[] = { 322 [CRUnCTRL] = 0x0, 323 [CRUnIE] = 0x4, 324 [CRUnIE2] = 0x8, 325 [CRUnINTS] = 0xc, 326 [CRUnINTS2] = 0x10, 327 [CRUnRST] = 0x18, 328 [AMnMB1ADDRL] = 0x40, 329 [AMnMB1ADDRH] = 0x44, 330 [AMnMB2ADDRL] = 0x48, 331 [AMnMB2ADDRH] = 0x4c, 332 [AMnMB3ADDRL] = 0x50, 333 [AMnMB3ADDRH] = 0x54, 334 [AMnMB4ADDRL] = 0x58, 335 [AMnMB4ADDRH] = 0x5c, 336 [AMnMB5ADDRL] = 0x60, 337 [AMnMB5ADDRH] = 0x64, 338 [AMnMB6ADDRL] = 0x68, 339 [AMnMB6ADDRH] = 0x6c, 340 [AMnMB7ADDRL] = 0x70, 341 [AMnMB7ADDRH] = 0x74, 342 [AMnMB8ADDRL] = 0x78, 343 [AMnMB8ADDRH] = 0x7c, 344 [AMnMBVALID] = 0x88, 345 [AMnMADRSL] = 0x8c, 346 [AMnMADRSH] = 0x90, 347 [AMnAXIATTR] = 0xec, 348 [AMnFIFOPNTR] = 0xf8, 349 [AMnAXISTP] = 0x110, 350 [AMnAXISTPACK] = 0x114, 351 [AMnIS] = 0x128, 352 [ICnEN] = 0x1f0, 353 [ICnSVCNUM] = 0x1f8, 354 [ICnSVC] = 0x1fc, 355 [ICnIPMC_C0] = 0x200, 356 [ICnMS] = 0x2d8, 357 [ICnDMR] = 0x304, 358}; 359 360static const struct rzg2l_cru_info rzg3e_cru_info = { 361 .max_width = 4095, 362 .max_height = 4095, 363 .image_conv = ICnIPMC_C0, 364 .has_stride = true, 365 .regs = rzg3e_cru_regs, 366 .irq_handler = rzg3e_cru_irq, 367 .enable_interrupts = rzg3e_cru_enable_interrupts, 368 .disable_interrupts = rzg3e_cru_disable_interrupts, 369 .fifo_empty = rzg3e_fifo_empty, 370}; 371 372static const u16 rzg2l_cru_regs[] = { 373 [CRUnCTRL] = 0x0, 374 [CRUnIE] = 0x4, 375 [CRUnINTS] = 0x8, 376 [CRUnRST] = 0xc, 377 [AMnMB1ADDRL] = 0x100, 378 [AMnMB1ADDRH] = 0x104, 379 [AMnMB2ADDRL] = 0x108, 380 [AMnMB2ADDRH] = 0x10c, 381 [AMnMB3ADDRL] = 0x110, 382 [AMnMB3ADDRH] = 0x114, 383 [AMnMB4ADDRL] = 0x118, 384 [AMnMB4ADDRH] = 0x11c, 385 [AMnMB5ADDRL] = 0x120, 386 [AMnMB5ADDRH] = 0x124, 387 [AMnMB6ADDRL] = 0x128, 388 [AMnMB6ADDRH] = 0x12c, 389 [AMnMB7ADDRL] = 0x130, 390 [AMnMB7ADDRH] = 0x134, 391 [AMnMB8ADDRL] = 0x138, 392 [AMnMB8ADDRH] = 0x13c, 393 [AMnMBVALID] = 0x148, 394 [AMnMBS] = 0x14c, 395 [AMnAXIATTR] = 0x158, 396 [AMnFIFOPNTR] = 0x168, 397 [AMnAXISTP] = 0x174, 398 [AMnAXISTPACK] = 0x178, 399 [ICnEN] = 0x200, 400 [ICnMC] = 0x208, 401 [ICnMS] = 0x254, 402 [ICnDMR] = 0x26c, 403}; 404 405static const struct rzg2l_cru_info rzg2l_cru_info = { 406 .max_width = 2800, 407 .max_height = 4095, 408 .image_conv = ICnMC, 409 .regs = rzg2l_cru_regs, 410 .irq_handler = rzg2l_cru_irq, 411 .enable_interrupts = rzg2l_cru_enable_interrupts, 412 .disable_interrupts = rzg2l_cru_disable_interrupts, 413 .fifo_empty = rzg2l_fifo_empty, 414}; 415 416static const struct of_device_id rzg2l_cru_of_id_table[] = { 417 { 418 .compatible = "renesas,r9a09g047-cru", 419 .data = &rzg3e_cru_info, 420 }, 421 { 422 .compatible = "renesas,rzg2l-cru", 423 .data = &rzg2l_cru_info, 424 }, 425 { /* sentinel */ } 426}; 427MODULE_DEVICE_TABLE(of, rzg2l_cru_of_id_table); 428 429static struct platform_driver rzg2l_cru_driver = { 430 .driver = { 431 .name = "rzg2l-cru", 432 .of_match_table = rzg2l_cru_of_id_table, 433 }, 434 .probe = rzg2l_cru_probe, 435 .remove = rzg2l_cru_remove, 436}; 437 438module_platform_driver(rzg2l_cru_driver); 439 440MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); 441MODULE_DESCRIPTION("Renesas RZ/G2L CRU driver"); 442MODULE_LICENSE("GPL");
~/
Read-only file preview.