18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 48c2ecf20Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * based on exynos_drm_drv.c 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/component.h> 168c2ecf20Sopenharmony_ci#include <linux/console.h> 178c2ecf20Sopenharmony_ci#include <linux/iommu.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "rockchip_drm_drv.h" 278c2ecf20Sopenharmony_ci#include "rockchip_drm_fb.h" 288c2ecf20Sopenharmony_ci#include "rockchip_drm_fbdev.h" 298c2ecf20Sopenharmony_ci#include "rockchip_drm_gem.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DRIVER_NAME "rockchip" 328c2ecf20Sopenharmony_ci#define DRIVER_DESC "RockChip Soc DRM" 338c2ecf20Sopenharmony_ci#define DRIVER_DATE "20140818" 348c2ecf20Sopenharmony_ci#define DRIVER_MAJOR 1 358c2ecf20Sopenharmony_ci#define DRIVER_MINOR 0 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic bool is_support_iommu = true; 388c2ecf20Sopenharmony_cistatic struct drm_driver rockchip_drm_driver; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Attach a (component) device to the shared drm dma mapping from master drm 428c2ecf20Sopenharmony_ci * device. This is used by the VOPs to map GEM buffers to a common DMA 438c2ecf20Sopenharmony_ci * mapping. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ciint rockchip_drm_dma_attach_device(struct drm_device *drm_dev, 468c2ecf20Sopenharmony_ci struct device *dev) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = drm_dev->dev_private; 498c2ecf20Sopenharmony_ci int ret; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!is_support_iommu) 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ret = iommu_attach_device(private->domain, dev); 558c2ecf20Sopenharmony_ci if (ret) { 568c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to attach iommu device\n"); 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_civoid rockchip_drm_dma_detach_device(struct drm_device *drm_dev, 648c2ecf20Sopenharmony_ci struct device *dev) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = drm_dev->dev_private; 678c2ecf20Sopenharmony_ci struct iommu_domain *domain = private->domain; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!is_support_iommu) 708c2ecf20Sopenharmony_ci return; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci iommu_detach_device(domain, dev); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int rockchip_drm_init_iommu(struct drm_device *drm_dev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = drm_dev->dev_private; 788c2ecf20Sopenharmony_ci struct iommu_domain_geometry *geometry; 798c2ecf20Sopenharmony_ci u64 start, end; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (!is_support_iommu) 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci private->domain = iommu_domain_alloc(&platform_bus_type); 858c2ecf20Sopenharmony_ci if (!private->domain) 868c2ecf20Sopenharmony_ci return -ENOMEM; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci geometry = &private->domain->geometry; 898c2ecf20Sopenharmony_ci start = geometry->aperture_start; 908c2ecf20Sopenharmony_ci end = geometry->aperture_end; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n", 938c2ecf20Sopenharmony_ci start, end); 948c2ecf20Sopenharmony_ci drm_mm_init(&private->mm, start, end - start + 1); 958c2ecf20Sopenharmony_ci mutex_init(&private->mm_lock); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void rockchip_iommu_cleanup(struct drm_device *drm_dev) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = drm_dev->dev_private; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (!is_support_iommu) 1058c2ecf20Sopenharmony_ci return; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci drm_mm_takedown(&private->mm); 1088c2ecf20Sopenharmony_ci iommu_domain_free(private->domain); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int rockchip_drm_bind(struct device *dev) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct drm_device *drm_dev; 1148c2ecf20Sopenharmony_ci struct rockchip_drm_private *private; 1158c2ecf20Sopenharmony_ci int ret; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); 1188c2ecf20Sopenharmony_ci if (IS_ERR(drm_dev)) 1198c2ecf20Sopenharmony_ci return PTR_ERR(drm_dev); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci dev_set_drvdata(dev, drm_dev); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); 1248c2ecf20Sopenharmony_ci if (!private) { 1258c2ecf20Sopenharmony_ci ret = -ENOMEM; 1268c2ecf20Sopenharmony_ci goto err_free; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci drm_dev->dev_private = private; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&private->psr_list); 1328c2ecf20Sopenharmony_ci mutex_init(&private->psr_list_lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = rockchip_drm_init_iommu(drm_dev); 1358c2ecf20Sopenharmony_ci if (ret) 1368c2ecf20Sopenharmony_ci goto err_free; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ret = drmm_mode_config_init(drm_dev); 1398c2ecf20Sopenharmony_ci if (ret) 1408c2ecf20Sopenharmony_ci goto err_iommu_cleanup; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci rockchip_drm_mode_config_init(drm_dev); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Try to bind all sub drivers. */ 1458c2ecf20Sopenharmony_ci ret = component_bind_all(dev, drm_dev); 1468c2ecf20Sopenharmony_ci if (ret) 1478c2ecf20Sopenharmony_ci goto err_iommu_cleanup; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); 1508c2ecf20Sopenharmony_ci if (ret) 1518c2ecf20Sopenharmony_ci goto err_unbind_all; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci drm_mode_config_reset(drm_dev); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * enable drm irq mode. 1578c2ecf20Sopenharmony_ci * - with irq_enabled = true, we can use the vblank feature. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci drm_dev->irq_enabled = true; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = rockchip_drm_fbdev_init(drm_dev); 1628c2ecf20Sopenharmony_ci if (ret) 1638c2ecf20Sopenharmony_ci goto err_unbind_all; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* init kms poll for handling hpd */ 1668c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(drm_dev); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = drm_dev_register(drm_dev, 0); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci goto err_kms_helper_poll_fini; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_cierr_kms_helper_poll_fini: 1748c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm_dev); 1758c2ecf20Sopenharmony_ci rockchip_drm_fbdev_fini(drm_dev); 1768c2ecf20Sopenharmony_cierr_unbind_all: 1778c2ecf20Sopenharmony_ci component_unbind_all(dev, drm_dev); 1788c2ecf20Sopenharmony_cierr_iommu_cleanup: 1798c2ecf20Sopenharmony_ci rockchip_iommu_cleanup(drm_dev); 1808c2ecf20Sopenharmony_cierr_free: 1818c2ecf20Sopenharmony_ci drm_dev_put(drm_dev); 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void rockchip_drm_unbind(struct device *dev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci drm_dev_unregister(drm_dev); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci rockchip_drm_fbdev_fini(drm_dev); 1928c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm_dev); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm_dev); 1958c2ecf20Sopenharmony_ci component_unbind_all(dev, drm_dev); 1968c2ecf20Sopenharmony_ci rockchip_iommu_cleanup(drm_dev); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci drm_dev_put(drm_dev); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic const struct file_operations rockchip_drm_driver_fops = { 2028c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2038c2ecf20Sopenharmony_ci .open = drm_open, 2048c2ecf20Sopenharmony_ci .mmap = rockchip_gem_mmap, 2058c2ecf20Sopenharmony_ci .poll = drm_poll, 2068c2ecf20Sopenharmony_ci .read = drm_read, 2078c2ecf20Sopenharmony_ci .unlocked_ioctl = drm_ioctl, 2088c2ecf20Sopenharmony_ci .compat_ioctl = drm_compat_ioctl, 2098c2ecf20Sopenharmony_ci .release = drm_release, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic struct drm_driver rockchip_drm_driver = { 2138c2ecf20Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 2148c2ecf20Sopenharmony_ci .lastclose = drm_fb_helper_lastclose, 2158c2ecf20Sopenharmony_ci .gem_vm_ops = &drm_gem_cma_vm_ops, 2168c2ecf20Sopenharmony_ci .gem_free_object_unlocked = rockchip_gem_free_object, 2178c2ecf20Sopenharmony_ci .dumb_create = rockchip_gem_dumb_create, 2188c2ecf20Sopenharmony_ci .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 2198c2ecf20Sopenharmony_ci .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 2208c2ecf20Sopenharmony_ci .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table, 2218c2ecf20Sopenharmony_ci .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, 2228c2ecf20Sopenharmony_ci .gem_prime_vmap = rockchip_gem_prime_vmap, 2238c2ecf20Sopenharmony_ci .gem_prime_vunmap = rockchip_gem_prime_vunmap, 2248c2ecf20Sopenharmony_ci .gem_prime_mmap = rockchip_gem_mmap_buf, 2258c2ecf20Sopenharmony_ci .fops = &rockchip_drm_driver_fops, 2268c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 2278c2ecf20Sopenharmony_ci .desc = DRIVER_DESC, 2288c2ecf20Sopenharmony_ci .date = DRIVER_DATE, 2298c2ecf20Sopenharmony_ci .major = DRIVER_MAJOR, 2308c2ecf20Sopenharmony_ci .minor = DRIVER_MINOR, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2348c2ecf20Sopenharmony_cistatic int rockchip_drm_sys_suspend(struct device *dev) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return drm_mode_config_helper_suspend(drm); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int rockchip_drm_sys_resume(struct device *dev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return drm_mode_config_helper_resume(drm); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci#endif 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic const struct dev_pm_ops rockchip_drm_pm_ops = { 2508c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend, 2518c2ecf20Sopenharmony_ci rockchip_drm_sys_resume) 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define MAX_ROCKCHIP_SUB_DRIVERS 16 2558c2ecf20Sopenharmony_cistatic struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; 2568c2ecf20Sopenharmony_cistatic int num_rockchip_sub_drivers; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * Check if a vop endpoint is leading to a rockchip subdriver or bridge. 2608c2ecf20Sopenharmony_ci * Should be called from the component bind stage of the drivers 2618c2ecf20Sopenharmony_ci * to ensure that all subdrivers are probed. 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * @ep: endpoint of a rockchip vop 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * returns true if subdriver, false if external bridge and -ENODEV 2668c2ecf20Sopenharmony_ci * if remote port does not contain a device. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ciint rockchip_drm_endpoint_is_subdriver(struct device_node *ep) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct device_node *node = of_graph_get_remote_port_parent(ep); 2718c2ecf20Sopenharmony_ci struct platform_device *pdev; 2728c2ecf20Sopenharmony_ci struct device_driver *drv; 2738c2ecf20Sopenharmony_ci int i; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!node) 2768c2ecf20Sopenharmony_ci return -ENODEV; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* status disabled will prevent creation of platform-devices */ 2798c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(node); 2808c2ecf20Sopenharmony_ci of_node_put(node); 2818c2ecf20Sopenharmony_ci if (!pdev) 2828c2ecf20Sopenharmony_ci return -ENODEV; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * All rockchip subdrivers have probed at this point, so 2868c2ecf20Sopenharmony_ci * any device not having a driver now is an external bridge. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci drv = pdev->dev.driver; 2898c2ecf20Sopenharmony_ci if (!drv) { 2908c2ecf20Sopenharmony_ci platform_device_put(pdev); 2918c2ecf20Sopenharmony_ci return false; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci for (i = 0; i < num_rockchip_sub_drivers; i++) { 2958c2ecf20Sopenharmony_ci if (rockchip_sub_drivers[i] == to_platform_driver(drv)) { 2968c2ecf20Sopenharmony_ci platform_device_put(pdev); 2978c2ecf20Sopenharmony_ci return true; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci platform_device_put(pdev); 3028c2ecf20Sopenharmony_ci return false; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int compare_dev(struct device *dev, void *data) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci return dev == (struct device *)data; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void rockchip_drm_match_remove(struct device *dev) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct device_link *link; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci list_for_each_entry(link, &dev->links.consumers, s_node) 3158c2ecf20Sopenharmony_ci device_link_del(link); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic struct component_match *rockchip_drm_match_add(struct device *dev) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct component_match *match = NULL; 3218c2ecf20Sopenharmony_ci int i; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci for (i = 0; i < num_rockchip_sub_drivers; i++) { 3248c2ecf20Sopenharmony_ci struct platform_driver *drv = rockchip_sub_drivers[i]; 3258c2ecf20Sopenharmony_ci struct device *p = NULL, *d; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci do { 3288c2ecf20Sopenharmony_ci d = platform_find_device_by_driver(p, &drv->driver); 3298c2ecf20Sopenharmony_ci put_device(p); 3308c2ecf20Sopenharmony_ci p = d; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!d) 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci device_link_add(dev, d, DL_FLAG_STATELESS); 3368c2ecf20Sopenharmony_ci component_match_add(dev, &match, compare_dev, d); 3378c2ecf20Sopenharmony_ci } while (true); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (IS_ERR(match)) 3418c2ecf20Sopenharmony_ci rockchip_drm_match_remove(dev); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return match ?: ERR_PTR(-ENODEV); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic const struct component_master_ops rockchip_drm_ops = { 3478c2ecf20Sopenharmony_ci .bind = rockchip_drm_bind, 3488c2ecf20Sopenharmony_ci .unbind = rockchip_drm_unbind, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int rockchip_drm_platform_of_probe(struct device *dev) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 3548c2ecf20Sopenharmony_ci struct device_node *port; 3558c2ecf20Sopenharmony_ci bool found = false; 3568c2ecf20Sopenharmony_ci int i; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!np) 3598c2ecf20Sopenharmony_ci return -ENODEV; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for (i = 0;; i++) { 3628c2ecf20Sopenharmony_ci struct device_node *iommu; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci port = of_parse_phandle(np, "ports", i); 3658c2ecf20Sopenharmony_ci if (!port) 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!of_device_is_available(port->parent)) { 3698c2ecf20Sopenharmony_ci of_node_put(port); 3708c2ecf20Sopenharmony_ci continue; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci iommu = of_parse_phandle(port->parent, "iommus", 0); 3748c2ecf20Sopenharmony_ci if (!iommu || !of_device_is_available(iommu->parent)) { 3758c2ecf20Sopenharmony_ci DRM_DEV_DEBUG(dev, 3768c2ecf20Sopenharmony_ci "no iommu attached for %pOF, using non-iommu buffers\n", 3778c2ecf20Sopenharmony_ci port->parent); 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * if there is a crtc not support iommu, force set all 3808c2ecf20Sopenharmony_ci * crtc use non-iommu buffer. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci is_support_iommu = false; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci found = true; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci of_node_put(iommu); 3888c2ecf20Sopenharmony_ci of_node_put(port); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (i == 0) { 3928c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "missing 'ports' property\n"); 3938c2ecf20Sopenharmony_ci return -ENODEV; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!found) { 3978c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, 3988c2ecf20Sopenharmony_ci "No available vop found for display-subsystem.\n"); 3998c2ecf20Sopenharmony_ci return -ENODEV; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int rockchip_drm_platform_probe(struct platform_device *pdev) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4088c2ecf20Sopenharmony_ci struct component_match *match = NULL; 4098c2ecf20Sopenharmony_ci int ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = rockchip_drm_platform_of_probe(dev); 4128c2ecf20Sopenharmony_ci if (ret) 4138c2ecf20Sopenharmony_ci return ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci match = rockchip_drm_match_add(dev); 4168c2ecf20Sopenharmony_ci if (IS_ERR(match)) 4178c2ecf20Sopenharmony_ci return PTR_ERR(match); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = component_master_add_with_match(dev, &rockchip_drm_ops, match); 4208c2ecf20Sopenharmony_ci if (ret < 0) { 4218c2ecf20Sopenharmony_ci rockchip_drm_match_remove(dev); 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int rockchip_drm_platform_remove(struct platform_device *pdev) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci component_master_del(&pdev->dev, &rockchip_drm_ops); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci rockchip_drm_match_remove(&pdev->dev); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void rockchip_drm_platform_shutdown(struct platform_device *pdev) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct drm_device *drm = platform_get_drvdata(pdev); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (drm) 4428c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic const struct of_device_id rockchip_drm_dt_ids[] = { 4468c2ecf20Sopenharmony_ci { .compatible = "rockchip,display-subsystem", }, 4478c2ecf20Sopenharmony_ci { /* sentinel */ }, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic struct platform_driver rockchip_drm_platform_driver = { 4528c2ecf20Sopenharmony_ci .probe = rockchip_drm_platform_probe, 4538c2ecf20Sopenharmony_ci .remove = rockchip_drm_platform_remove, 4548c2ecf20Sopenharmony_ci .shutdown = rockchip_drm_platform_shutdown, 4558c2ecf20Sopenharmony_ci .driver = { 4568c2ecf20Sopenharmony_ci .name = "rockchip-drm", 4578c2ecf20Sopenharmony_ci .of_match_table = rockchip_drm_dt_ids, 4588c2ecf20Sopenharmony_ci .pm = &rockchip_drm_pm_ops, 4598c2ecf20Sopenharmony_ci }, 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \ 4638c2ecf20Sopenharmony_ci if (IS_ENABLED(cond) && \ 4648c2ecf20Sopenharmony_ci !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \ 4658c2ecf20Sopenharmony_ci rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \ 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int __init rockchip_drm_init(void) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci int ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci num_rockchip_sub_drivers = 0; 4738c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP); 4748c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, 4758c2ecf20Sopenharmony_ci CONFIG_ROCKCHIP_LVDS); 4768c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, 4778c2ecf20Sopenharmony_ci CONFIG_ROCKCHIP_ANALOGIX_DP); 4788c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); 4798c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, 4808c2ecf20Sopenharmony_ci CONFIG_ROCKCHIP_DW_HDMI); 4818c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, 4828c2ecf20Sopenharmony_ci CONFIG_ROCKCHIP_DW_MIPI_DSI); 4838c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); 4848c2ecf20Sopenharmony_ci ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, 4858c2ecf20Sopenharmony_ci CONFIG_ROCKCHIP_RK3066_HDMI); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ret = platform_register_drivers(rockchip_sub_drivers, 4888c2ecf20Sopenharmony_ci num_rockchip_sub_drivers); 4898c2ecf20Sopenharmony_ci if (ret) 4908c2ecf20Sopenharmony_ci return ret; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ret = platform_driver_register(&rockchip_drm_platform_driver); 4938c2ecf20Sopenharmony_ci if (ret) 4948c2ecf20Sopenharmony_ci goto err_unreg_drivers; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cierr_unreg_drivers: 4998c2ecf20Sopenharmony_ci platform_unregister_drivers(rockchip_sub_drivers, 5008c2ecf20Sopenharmony_ci num_rockchip_sub_drivers); 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void __exit rockchip_drm_fini(void) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci platform_driver_unregister(&rockchip_drm_platform_driver); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci platform_unregister_drivers(rockchip_sub_drivers, 5098c2ecf20Sopenharmony_ci num_rockchip_sub_drivers); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cimodule_init(rockchip_drm_init); 5138c2ecf20Sopenharmony_cimodule_exit(rockchip_drm_fini); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); 5168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ROCKCHIP DRM Driver"); 5178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 518