FILE
rockchip_drm_drv.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) Rockchip Electronics Co., Ltd. 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 * 6 * based on exynos_drm_drv.c 7 */ 8 9#include <linux/aperture.h> 10#include <linux/dma-mapping.h> 11#include <linux/platform_device.h> 12#include <linux/pm_runtime.h> 13#include <linux/module.h> 14#include <linux/of_graph.h> 15#include <linux/of_platform.h> 16#include <linux/component.h> 17#include <linux/console.h> 18#include <linux/iommu.h> 19 20#include <drm/clients/drm_client_setup.h> 21#include <drm/drm_drv.h> 22#include <drm/drm_fbdev_dma.h> 23#include <drm/drm_gem_dma_helper.h> 24#include <drm/drm_of.h> 25#include <drm/drm_print.h> 26#include <drm/drm_probe_helper.h> 27#include <drm/drm_vblank.h> 28 29#if defined(CONFIG_ARM_DMA_USE_IOMMU) 30#include <asm/dma-iommu.h> 31#else 32#define arm_iommu_detach_device(...) ({ }) 33#define arm_iommu_release_mapping(...) ({ }) 34#define to_dma_iommu_mapping(dev) NULL 35#endif 36 37#include "rockchip_drm_drv.h" 38#include "rockchip_drm_fb.h" 39#include "rockchip_drm_gem.h" 40 41#define DRIVER_NAME "rockchip" 42#define DRIVER_DESC "RockChip Soc DRM" 43#define DRIVER_MAJOR 1 44#define DRIVER_MINOR 0 45 46static const struct drm_driver rockchip_drm_driver; 47 48/* 49 * Attach a (component) device to the shared drm dma mapping from master drm 50 * device. This is used by the VOPs to map GEM buffers to a common DMA 51 * mapping. 52 */ 53int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, 54 struct device *dev) 55{ 56 struct rockchip_drm_private *private = drm_dev->dev_private; 57 int ret; 58 59 if (!private->domain) 60 return 0; 61 62 if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) { 63 struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); 64 65 if (mapping) { 66 arm_iommu_detach_device(dev); 67 arm_iommu_release_mapping(mapping); 68 } 69 } 70 71 ret = iommu_attach_device(private->domain, dev); 72 if (ret) { 73 DRM_DEV_ERROR(dev, "Failed to attach iommu device\n"); 74 return ret; 75 } 76 77 return 0; 78} 79 80void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, 81 struct device *dev) 82{ 83 struct rockchip_drm_private *private = drm_dev->dev_private; 84 85 if (!private->domain) 86 return; 87 88 iommu_detach_device(private->domain, dev); 89} 90 91void rockchip_drm_dma_init_device(struct drm_device *drm_dev, 92 struct device *dev) 93{ 94 struct rockchip_drm_private *private = drm_dev->dev_private; 95 96 if (!device_iommu_mapped(dev)) 97 private->iommu_dev = ERR_PTR(-ENODEV); 98 else if (!private->iommu_dev) 99 private->iommu_dev = dev; 100 101 if (!IS_ERR(private->iommu_dev)) 102 drm_dev_set_dma_dev(drm_dev, private->iommu_dev); 103} 104 105static int rockchip_drm_init_iommu(struct drm_device *drm_dev) 106{ 107 struct rockchip_drm_private *private = drm_dev->dev_private; 108 struct iommu_domain_geometry *geometry; 109 u64 start, end; 110 int ret; 111 112 if (IS_ERR_OR_NULL(private->iommu_dev)) 113 return 0; 114 115 private->domain = iommu_paging_domain_alloc(private->iommu_dev); 116 if (IS_ERR(private->domain)) { 117 ret = PTR_ERR(private->domain); 118 private->domain = NULL; 119 return ret; 120 } 121 122 geometry = &private->domain->geometry; 123 start = geometry->aperture_start; 124 end = geometry->aperture_end; 125 126 DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n", 127 start, end); 128 drm_mm_init(&private->mm, start, end - start + 1); 129 mutex_init(&private->mm_lock); 130 131 return 0; 132} 133 134static void rockchip_iommu_cleanup(struct drm_device *drm_dev) 135{ 136 struct rockchip_drm_private *private = drm_dev->dev_private; 137 138 if (!private->domain) 139 return; 140 141 drm_mm_takedown(&private->mm); 142 iommu_domain_free(private->domain); 143} 144 145static int rockchip_drm_bind(struct device *dev) 146{ 147 struct drm_device *drm_dev; 148 struct rockchip_drm_private *private; 149 int ret; 150 151 /* Remove existing drivers that may own the framebuffer memory. */ 152 ret = aperture_remove_all_conflicting_devices(rockchip_drm_driver.name); 153 if (ret) { 154 DRM_DEV_ERROR(dev, 155 "Failed to remove existing framebuffers - %d.\n", 156 ret); 157 return ret; 158 } 159 160 drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); 161 if (IS_ERR(drm_dev)) 162 return PTR_ERR(drm_dev); 163 164 dev_set_drvdata(dev, drm_dev); 165 166 private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); 167 if (!private) { 168 ret = -ENOMEM; 169 goto err_free; 170 } 171 172 drm_dev->dev_private = private; 173 174 ret = drmm_mode_config_init(drm_dev); 175 if (ret) 176 goto err_free; 177 178 rockchip_drm_mode_config_init(drm_dev); 179 180 /* Try to bind all sub drivers. */ 181 ret = component_bind_all(dev, drm_dev); 182 if (ret) 183 goto err_free; 184 185 ret = rockchip_drm_init_iommu(drm_dev); 186 if (ret) 187 goto err_unbind_all; 188 189 ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); 190 if (ret) 191 goto err_iommu_cleanup; 192 193 drm_mode_config_reset(drm_dev); 194 195 /* init kms poll for handling hpd */ 196 drm_kms_helper_poll_init(drm_dev); 197 198 ret = drm_dev_register(drm_dev, 0); 199 if (ret) 200 goto err_kms_helper_poll_fini; 201 202 drm_client_setup(drm_dev, NULL); 203 204 return 0; 205err_kms_helper_poll_fini: 206 drm_kms_helper_poll_fini(drm_dev); 207err_iommu_cleanup: 208 rockchip_iommu_cleanup(drm_dev); 209err_unbind_all: 210 component_unbind_all(dev, drm_dev); 211err_free: 212 drm_dev_put(drm_dev); 213 return ret; 214} 215 216static void rockchip_drm_unbind(struct device *dev) 217{ 218 struct drm_device *drm_dev = dev_get_drvdata(dev); 219 220 drm_dev_unregister(drm_dev); 221 222 drm_kms_helper_poll_fini(drm_dev); 223 224 drm_atomic_helper_shutdown(drm_dev); 225 component_unbind_all(dev, drm_dev); 226 rockchip_iommu_cleanup(drm_dev); 227 228 drm_dev_put(drm_dev); 229} 230 231DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops); 232 233static const struct drm_driver rockchip_drm_driver = { 234 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 235 .dumb_create = rockchip_gem_dumb_create, 236 .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, 237 DRM_FBDEV_DMA_DRIVER_OPS, 238 .fops = &rockchip_drm_driver_fops, 239 .name = DRIVER_NAME, 240 .desc = DRIVER_DESC, 241 .major = DRIVER_MAJOR, 242 .minor = DRIVER_MINOR, 243}; 244 245#ifdef CONFIG_PM_SLEEP 246static int rockchip_drm_sys_suspend(struct device *dev) 247{ 248 struct drm_device *drm = dev_get_drvdata(dev); 249 250 return drm_mode_config_helper_suspend(drm); 251} 252 253static int rockchip_drm_sys_resume(struct device *dev) 254{ 255 struct drm_device *drm = dev_get_drvdata(dev); 256 257 return drm_mode_config_helper_resume(drm); 258} 259#endif 260 261static const struct dev_pm_ops rockchip_drm_pm_ops = { 262 SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend, 263 rockchip_drm_sys_resume) 264}; 265 266#define MAX_ROCKCHIP_SUB_DRIVERS 16 267static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; 268static int num_rockchip_sub_drivers; 269 270/* 271 * Get the endpoint id of the remote endpoint of the given encoder. This 272 * information is used by the VOP2 driver to identify the encoder. 273 * 274 * @rkencoder: The encoder to get the remote endpoint id from 275 * @np: The encoder device node 276 * @port: The number of the port leading to the VOP2 277 * @reg: The endpoint number leading to the VOP2 278 */ 279int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rkencoder, 280 struct device_node *np, int port, int reg) 281{ 282 struct of_endpoint ep; 283 struct device_node *en, *ren; 284 int ret; 285 286 en = of_graph_get_endpoint_by_regs(np, port, reg); 287 if (!en) 288 return -ENOENT; 289 290 ren = of_graph_get_remote_endpoint(en); 291 if (!ren) 292 return -ENOENT; 293 294 ret = of_graph_parse_endpoint(ren, &ep); 295 if (ret) 296 return ret; 297 298 rkencoder->crtc_endpoint_id = ep.id; 299 300 return 0; 301} 302 303/* 304 * Check if a vop endpoint is leading to a rockchip subdriver or bridge. 305 * Should be called from the component bind stage of the drivers 306 * to ensure that all subdrivers are probed. 307 * 308 * @ep: endpoint of a rockchip vop 309 * 310 * returns true if subdriver, false if external bridge and -ENODEV 311 * if remote port does not contain a device. 312 */ 313int rockchip_drm_endpoint_is_subdriver(struct device_node *ep) 314{ 315 struct device_node *node = of_graph_get_remote_port_parent(ep); 316 struct platform_device *pdev; 317 struct device_driver *drv; 318 int i; 319 320 if (!node) 321 return -ENODEV; 322 323 /* status disabled will prevent creation of platform-devices */ 324 if (!of_device_is_available(node)) { 325 of_node_put(node); 326 return -ENODEV; 327 } 328 329 pdev = of_find_device_by_node(node); 330 of_node_put(node); 331 332 /* enabled non-platform-devices can immediately return here */ 333 if (!pdev) 334 return false; 335 336 /* 337 * All rockchip subdrivers have probed at this point, so 338 * any device not having a driver now is an external bridge. 339 */ 340 drv = pdev->dev.driver; 341 if (!drv) { 342 platform_device_put(pdev); 343 return false; 344 } 345 346 for (i = 0; i < num_rockchip_sub_drivers; i++) { 347 if (rockchip_sub_drivers[i] == to_platform_driver(drv)) { 348 platform_device_put(pdev); 349 return true; 350 } 351 } 352 353 platform_device_put(pdev); 354 return false; 355} 356 357static void rockchip_drm_match_remove(struct device *dev) 358{ 359 struct device_link *link; 360 361 list_for_each_entry(link, &dev->links.consumers, s_node) 362 device_link_del(link); 363} 364 365/* list of preferred vop devices */ 366static const char *const rockchip_drm_match_preferred[] = { 367 "rockchip,rk3399-vop-big", 368 NULL, 369}; 370 371static struct component_match *rockchip_drm_match_add(struct device *dev) 372{ 373 struct component_match *match = NULL; 374 struct device_node *port; 375 int i; 376 377 /* add preferred vop device match before adding driver device matches */ 378 for (i = 0; ; i++) { 379 port = of_parse_phandle(dev->of_node, "ports", i); 380 if (!port) 381 break; 382 383 if (of_device_is_available(port->parent) && 384 of_device_compatible_match(port->parent, 385 rockchip_drm_match_preferred)) 386 drm_of_component_match_add(dev, &match, 387 component_compare_of, 388 port->parent); 389 390 of_node_put(port); 391 } 392 393 for (i = 0; i < num_rockchip_sub_drivers; i++) { 394 struct platform_driver *drv = rockchip_sub_drivers[i]; 395 struct device *p = NULL, *d; 396 397 do { 398 d = platform_find_device_by_driver(p, &drv->driver); 399 put_device(p); 400 p = d; 401 402 if (!d) 403 break; 404 405 device_link_add(dev, d, DL_FLAG_STATELESS); 406 component_match_add(dev, &match, component_compare_dev, d); 407 } while (true); 408 } 409 410 if (IS_ERR(match)) 411 rockchip_drm_match_remove(dev); 412 413 return match ?: ERR_PTR(-ENODEV); 414} 415 416static const struct component_master_ops rockchip_drm_ops = { 417 .bind = rockchip_drm_bind, 418 .unbind = rockchip_drm_unbind, 419}; 420 421static int rockchip_drm_platform_of_probe(struct device *dev) 422{ 423 struct device_node *np = dev->of_node; 424 struct device_node *port; 425 bool found = false; 426 int i; 427 428 if (!np) 429 return -ENODEV; 430 431 for (i = 0;; i++) { 432 port = of_parse_phandle(np, "ports", i); 433 if (!port) 434 break; 435 436 if (!of_device_is_available(port->parent)) { 437 of_node_put(port); 438 continue; 439 } 440 441 found = true; 442 of_node_put(port); 443 } 444 445 if (i == 0) { 446 DRM_DEV_ERROR(dev, "missing 'ports' property\n"); 447 return -ENODEV; 448 } 449 450 if (!found) { 451 DRM_DEV_ERROR(dev, 452 "No available vop found for display-subsystem.\n"); 453 return -ENODEV; 454 } 455 456 return 0; 457} 458 459static int rockchip_drm_platform_probe(struct platform_device *pdev) 460{ 461 struct device *dev = &pdev->dev; 462 struct component_match *match = NULL; 463 int ret; 464 465 ret = rockchip_drm_platform_of_probe(dev); 466 if (ret) 467 return ret; 468 469 match = rockchip_drm_match_add(dev); 470 if (IS_ERR(match)) 471 return PTR_ERR(match); 472 473 ret = component_master_add_with_match(dev, &rockchip_drm_ops, match); 474 if (ret < 0) { 475 rockchip_drm_match_remove(dev); 476 return ret; 477 } 478 479 return 0; 480} 481 482static void rockchip_drm_platform_remove(struct platform_device *pdev) 483{ 484 component_master_del(&pdev->dev, &rockchip_drm_ops); 485 486 rockchip_drm_match_remove(&pdev->dev); 487} 488 489static void rockchip_drm_platform_shutdown(struct platform_device *pdev) 490{ 491 if (component_master_is_bound(&pdev->dev, &rockchip_drm_ops)) { 492 struct drm_device *drm = platform_get_drvdata(pdev); 493 494 drm_atomic_helper_shutdown(drm); 495 } 496} 497 498static const struct of_device_id rockchip_drm_dt_ids[] = { 499 { .compatible = "rockchip,display-subsystem", }, 500 { /* sentinel */ }, 501}; 502MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); 503 504static struct platform_driver rockchip_drm_platform_driver = { 505 .probe = rockchip_drm_platform_probe, 506 .remove = rockchip_drm_platform_remove, 507 .shutdown = rockchip_drm_platform_shutdown, 508 .driver = { 509 .name = "rockchip-drm", 510 .of_match_table = rockchip_drm_dt_ids, 511 .pm = &rockchip_drm_pm_ops, 512 }, 513}; 514 515#define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \ 516 if (IS_ENABLED(cond) && \ 517 !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \ 518 rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \ 519} 520 521static int __init rockchip_drm_init(void) 522{ 523 int ret; 524 525 if (drm_firmware_drivers_only()) 526 return -ENODEV; 527 528 num_rockchip_sub_drivers = 0; 529 ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP); 530 ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2); 531 ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, 532 CONFIG_ROCKCHIP_LVDS); 533 ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, 534 CONFIG_ROCKCHIP_ANALOGIX_DP); 535 ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); 536 ADD_ROCKCHIP_SUB_DRIVER(dw_dp_driver, CONFIG_ROCKCHIP_DW_DP); 537 ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, 538 CONFIG_ROCKCHIP_DW_HDMI); 539 ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver, 540 CONFIG_ROCKCHIP_DW_HDMI_QP); 541 ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, 542 CONFIG_ROCKCHIP_DW_MIPI_DSI); 543 ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver, 544 CONFIG_ROCKCHIP_DW_MIPI_DSI2); 545 ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); 546 ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, 547 CONFIG_ROCKCHIP_RK3066_HDMI); 548 549 ret = platform_register_drivers(rockchip_sub_drivers, 550 num_rockchip_sub_drivers); 551 if (ret) 552 return ret; 553 554 ret = platform_driver_register(&rockchip_drm_platform_driver); 555 if (ret) 556 goto err_unreg_drivers; 557 558 return 0; 559 560err_unreg_drivers: 561 platform_unregister_drivers(rockchip_sub_drivers, 562 num_rockchip_sub_drivers); 563 return ret; 564} 565 566static void __exit rockchip_drm_fini(void) 567{ 568 platform_driver_unregister(&rockchip_drm_platform_driver); 569 570 platform_unregister_drivers(rockchip_sub_drivers, 571 num_rockchip_sub_drivers); 572} 573 574module_init(rockchip_drm_init); 575module_exit(rockchip_drm_fini); 576 577MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); 578MODULE_DESCRIPTION("ROCKCHIP DRM Driver"); 579MODULE_LICENSE("GPL v2");
~/
Read-only file preview.