19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22048e328SMark Yao /* 32048e328SMark Yao * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 42048e328SMark Yao * Author:Mark Yao <mark.yao@rock-chips.com> 52048e328SMark Yao * 62048e328SMark Yao * based on exynos_drm_drv.c 72048e328SMark Yao */ 82048e328SMark Yao 9*634bd298SThomas Zimmermann #include <linux/aperture.h> 102048e328SMark Yao #include <linux/dma-mapping.h> 11722d4f06SRob Herring #include <linux/platform_device.h> 122048e328SMark Yao #include <linux/pm_runtime.h> 1300fe6148SPaul Gortmaker #include <linux/module.h> 142048e328SMark Yao #include <linux/of_graph.h> 153880f62eSHeiko Stuebner #include <linux/of_platform.h> 162048e328SMark Yao #include <linux/component.h> 175a587383STomeu Vizoso #include <linux/console.h> 181aa5ca6eSShunqian Zheng #include <linux/iommu.h> 192048e328SMark Yao 20905df8f1SThomas Zimmermann #include <drm/drm_client_setup.h> 21c2156ccdSSam Ravnborg #include <drm/drm_drv.h> 22b4c3fe53SThomas Zimmermann #include <drm/drm_fbdev_dma.h> 234a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h> 24c2156ccdSSam Ravnborg #include <drm/drm_of.h> 25c2156ccdSSam Ravnborg #include <drm/drm_probe_helper.h> 26c2156ccdSSam Ravnborg #include <drm/drm_vblank.h> 27c2156ccdSSam Ravnborg 288490cad4SSteven Price #if defined(CONFIG_ARM_DMA_USE_IOMMU) 298490cad4SSteven Price #include <asm/dma-iommu.h> 308490cad4SSteven Price #else 318490cad4SSteven Price #define arm_iommu_detach_device(...) ({ }) 328490cad4SSteven Price #define arm_iommu_release_mapping(...) ({ }) 338490cad4SSteven Price #define to_dma_iommu_mapping(dev) NULL 348490cad4SSteven Price #endif 358490cad4SSteven Price 362048e328SMark Yao #include "rockchip_drm_drv.h" 372048e328SMark Yao #include "rockchip_drm_fb.h" 382048e328SMark Yao #include "rockchip_drm_gem.h" 392048e328SMark Yao 402048e328SMark Yao #define DRIVER_NAME "rockchip" 412048e328SMark Yao #define DRIVER_DESC "RockChip Soc DRM" 422048e328SMark Yao #define DRIVER_DATE "20140818" 432048e328SMark Yao #define DRIVER_MAJOR 1 442048e328SMark Yao #define DRIVER_MINOR 0 452048e328SMark Yao 4670a59dd8SDaniel Vetter static const struct drm_driver rockchip_drm_driver; 472d90d477SMark Yao 482048e328SMark Yao /* 492048e328SMark Yao * Attach a (component) device to the shared drm dma mapping from master drm 502048e328SMark Yao * device. This is used by the VOPs to map GEM buffers to a common DMA 512048e328SMark Yao * mapping. 522048e328SMark Yao */ 532048e328SMark Yao int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, 542048e328SMark Yao struct device *dev) 552048e328SMark Yao { 561aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private; 572048e328SMark Yao int ret; 582048e328SMark Yao 59421be3eeSRobin Murphy if (!private->domain) 602d90d477SMark Yao return 0; 612d90d477SMark Yao 628490cad4SSteven Price if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) { 638490cad4SSteven Price struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); 648490cad4SSteven Price 658490cad4SSteven Price if (mapping) { 668490cad4SSteven Price arm_iommu_detach_device(dev); 678490cad4SSteven Price arm_iommu_release_mapping(mapping); 688490cad4SSteven Price } 698490cad4SSteven Price } 708490cad4SSteven Price 711aa5ca6eSShunqian Zheng ret = iommu_attach_device(private->domain, dev); 721aa5ca6eSShunqian Zheng if (ret) { 73d8dd6804SHaneen Mohammed DRM_DEV_ERROR(dev, "Failed to attach iommu device\n"); 742048e328SMark Yao return ret; 751aa5ca6eSShunqian Zheng } 762048e328SMark Yao 771aa5ca6eSShunqian Zheng return 0; 782048e328SMark Yao } 792048e328SMark Yao 802048e328SMark Yao void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, 812048e328SMark Yao struct device *dev) 822048e328SMark Yao { 831aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private; 841aa5ca6eSShunqian Zheng 85421be3eeSRobin Murphy if (!private->domain) 862d90d477SMark Yao return; 872d90d477SMark Yao 88421be3eeSRobin Murphy iommu_detach_device(private->domain, dev); 89421be3eeSRobin Murphy } 90421be3eeSRobin Murphy 91421be3eeSRobin Murphy void rockchip_drm_dma_init_device(struct drm_device *drm_dev, 92421be3eeSRobin Murphy struct device *dev) 93421be3eeSRobin Murphy { 94421be3eeSRobin Murphy struct rockchip_drm_private *private = drm_dev->dev_private; 95421be3eeSRobin Murphy 96421be3eeSRobin Murphy if (!device_iommu_mapped(dev)) 97421be3eeSRobin Murphy private->iommu_dev = ERR_PTR(-ENODEV); 98421be3eeSRobin Murphy else if (!private->iommu_dev) 99421be3eeSRobin Murphy private->iommu_dev = dev; 1002048e328SMark Yao } 1012048e328SMark Yao 1021aa5ca6eSShunqian Zheng static int rockchip_drm_init_iommu(struct drm_device *drm_dev) 1031aa5ca6eSShunqian Zheng { 1041aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private; 1051aa5ca6eSShunqian Zheng struct iommu_domain_geometry *geometry; 1061aa5ca6eSShunqian Zheng u64 start, end; 107d8c07beeSLu Baolu int ret; 1081aa5ca6eSShunqian Zheng 109421be3eeSRobin Murphy if (IS_ERR_OR_NULL(private->iommu_dev)) 1101aa5ca6eSShunqian Zheng return 0; 1111aa5ca6eSShunqian Zheng 112d8c07beeSLu Baolu private->domain = iommu_paging_domain_alloc(private->iommu_dev); 113d8c07beeSLu Baolu if (IS_ERR(private->domain)) { 114d8c07beeSLu Baolu ret = PTR_ERR(private->domain); 115d8c07beeSLu Baolu private->domain = NULL; 116d8c07beeSLu Baolu return ret; 117d8c07beeSLu Baolu } 1181aa5ca6eSShunqian Zheng 1191aa5ca6eSShunqian Zheng geometry = &private->domain->geometry; 1201aa5ca6eSShunqian Zheng start = geometry->aperture_start; 1211aa5ca6eSShunqian Zheng end = geometry->aperture_end; 1221aa5ca6eSShunqian Zheng 1231aa5ca6eSShunqian Zheng DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n", 1241aa5ca6eSShunqian Zheng start, end); 1251aa5ca6eSShunqian Zheng drm_mm_init(&private->mm, start, end - start + 1); 1261aa5ca6eSShunqian Zheng mutex_init(&private->mm_lock); 1271aa5ca6eSShunqian Zheng 1281aa5ca6eSShunqian Zheng return 0; 1291aa5ca6eSShunqian Zheng } 1301aa5ca6eSShunqian Zheng 1311aa5ca6eSShunqian Zheng static void rockchip_iommu_cleanup(struct drm_device *drm_dev) 1321aa5ca6eSShunqian Zheng { 1331aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private; 1341aa5ca6eSShunqian Zheng 135421be3eeSRobin Murphy if (!private->domain) 1361aa5ca6eSShunqian Zheng return; 1371aa5ca6eSShunqian Zheng 1381aa5ca6eSShunqian Zheng drm_mm_takedown(&private->mm); 1391aa5ca6eSShunqian Zheng iommu_domain_free(private->domain); 1402048e328SMark Yao } 1412048e328SMark Yao 142f706974aSTomeu Vizoso static int rockchip_drm_bind(struct device *dev) 1432048e328SMark Yao { 144f706974aSTomeu Vizoso struct drm_device *drm_dev; 1452048e328SMark Yao struct rockchip_drm_private *private; 1462048e328SMark Yao int ret; 1472048e328SMark Yao 1483c1ed51aSJavier Martinez Canillas /* Remove existing drivers that may own the framebuffer memory. */ 149*634bd298SThomas Zimmermann ret = aperture_remove_all_conflicting_devices(rockchip_drm_driver.name); 1503c1ed51aSJavier Martinez Canillas if (ret) { 1513c1ed51aSJavier Martinez Canillas DRM_DEV_ERROR(dev, 1523c1ed51aSJavier Martinez Canillas "Failed to remove existing framebuffers - %d.\n", 1533c1ed51aSJavier Martinez Canillas ret); 1543c1ed51aSJavier Martinez Canillas return ret; 1553c1ed51aSJavier Martinez Canillas } 1563c1ed51aSJavier Martinez Canillas 157f706974aSTomeu Vizoso drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); 1580f288605STom Gundersen if (IS_ERR(drm_dev)) 1590f288605STom Gundersen return PTR_ERR(drm_dev); 1602048e328SMark Yao 161f706974aSTomeu Vizoso dev_set_drvdata(dev, drm_dev); 162f706974aSTomeu Vizoso 163f706974aSTomeu Vizoso private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); 164f706974aSTomeu Vizoso if (!private) { 165f706974aSTomeu Vizoso ret = -ENOMEM; 1669127f99cSTomasz Figa goto err_free; 167f706974aSTomeu Vizoso } 168f706974aSTomeu Vizoso 1692048e328SMark Yao drm_dev->dev_private = private; 1702048e328SMark Yao 17156e35f85SDaniel Vetter ret = drmm_mode_config_init(drm_dev); 17256e35f85SDaniel Vetter if (ret) 173421be3eeSRobin Murphy goto err_free; 1742048e328SMark Yao 1752048e328SMark Yao rockchip_drm_mode_config_init(drm_dev); 1762048e328SMark Yao 1772048e328SMark Yao /* Try to bind all sub drivers. */ 1782048e328SMark Yao ret = component_bind_all(dev, drm_dev); 1792048e328SMark Yao if (ret) 180421be3eeSRobin Murphy goto err_free; 181421be3eeSRobin Murphy 182421be3eeSRobin Murphy ret = rockchip_drm_init_iommu(drm_dev); 183421be3eeSRobin Murphy if (ret) 184421be3eeSRobin Murphy goto err_unbind_all; 1852048e328SMark Yao 186ccea9199SJeffy Chen ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); 187ccea9199SJeffy Chen if (ret) 188421be3eeSRobin Murphy goto err_iommu_cleanup; 189ccea9199SJeffy Chen 190ccea9199SJeffy Chen drm_mode_config_reset(drm_dev); 1912048e328SMark Yao 192ccea9199SJeffy Chen /* init kms poll for handling hpd */ 193ccea9199SJeffy Chen drm_kms_helper_poll_init(drm_dev); 19463ebb9faSMark Yao 1958415ab56SMark yao ret = drm_dev_register(drm_dev, 0); 1962048e328SMark Yao if (ret) 197ccea9199SJeffy Chen goto err_kms_helper_poll_fini; 1982048e328SMark Yao 199905df8f1SThomas Zimmermann drm_client_setup(drm_dev, NULL); 20024af7c34SJohn Keeping 2012048e328SMark Yao return 0; 2022048e328SMark Yao err_kms_helper_poll_fini: 2032048e328SMark Yao drm_kms_helper_poll_fini(drm_dev); 20456e35f85SDaniel Vetter err_iommu_cleanup: 205ccea9199SJeffy Chen rockchip_iommu_cleanup(drm_dev); 206421be3eeSRobin Murphy err_unbind_all: 207421be3eeSRobin Murphy component_unbind_all(dev, drm_dev); 208f706974aSTomeu Vizoso err_free: 209574e0fbfSThomas Zimmermann drm_dev_put(drm_dev); 2102048e328SMark Yao return ret; 2112048e328SMark Yao } 2122048e328SMark Yao 213f706974aSTomeu Vizoso static void rockchip_drm_unbind(struct device *dev) 2142048e328SMark Yao { 215f706974aSTomeu Vizoso struct drm_device *drm_dev = dev_get_drvdata(dev); 2162048e328SMark Yao 217f706974aSTomeu Vizoso drm_dev_unregister(drm_dev); 218ccea9199SJeffy Chen 219ccea9199SJeffy Chen drm_kms_helper_poll_fini(drm_dev); 220ccea9199SJeffy Chen 221c1bb8188SJeffy Chen drm_atomic_helper_shutdown(drm_dev); 222ccea9199SJeffy Chen component_unbind_all(dev, drm_dev); 223ccea9199SJeffy Chen rockchip_iommu_cleanup(drm_dev); 224ccea9199SJeffy Chen 225574e0fbfSThomas Zimmermann drm_dev_put(drm_dev); 2262048e328SMark Yao } 2272048e328SMark Yao 228f8b53070SThomas Zimmermann DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops); 2292048e328SMark Yao 23070a59dd8SDaniel Vetter static const struct drm_driver rockchip_drm_driver = { 2310424fdafSDaniel Vetter .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 2322048e328SMark Yao .dumb_create = rockchip_gem_dumb_create, 2336fd0bfe2SHaixia Shi .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, 234905df8f1SThomas Zimmermann DRM_FBDEV_DMA_DRIVER_OPS, 2352048e328SMark Yao .fops = &rockchip_drm_driver_fops, 2362048e328SMark Yao .name = DRIVER_NAME, 2372048e328SMark Yao .desc = DRIVER_DESC, 2382048e328SMark Yao .date = DRIVER_DATE, 2392048e328SMark Yao .major = DRIVER_MAJOR, 2402048e328SMark Yao .minor = DRIVER_MINOR, 2412048e328SMark Yao }; 2422048e328SMark Yao 2432048e328SMark Yao #ifdef CONFIG_PM_SLEEP 2442048e328SMark Yao static int rockchip_drm_sys_suspend(struct device *dev) 2452048e328SMark Yao { 2462048e328SMark Yao struct drm_device *drm = dev_get_drvdata(dev); 2470fa375e6SJeffy Chen 248e7941cc2SSouptick Joarder return drm_mode_config_helper_suspend(drm); 2492048e328SMark Yao } 2502048e328SMark Yao 2512048e328SMark Yao static int rockchip_drm_sys_resume(struct device *dev) 2522048e328SMark Yao { 2532048e328SMark Yao struct drm_device *drm = dev_get_drvdata(dev); 2542048e328SMark Yao 255e7941cc2SSouptick Joarder return drm_mode_config_helper_resume(drm); 2562048e328SMark Yao } 2572048e328SMark Yao #endif 2582048e328SMark Yao 2592048e328SMark Yao static const struct dev_pm_ops rockchip_drm_pm_ops = { 2602048e328SMark Yao SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend, 2612048e328SMark Yao rockchip_drm_sys_resume) 2622048e328SMark Yao }; 2632048e328SMark Yao 2648820b68bSJeffy Chen #define MAX_ROCKCHIP_SUB_DRIVERS 16 2658820b68bSJeffy Chen static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; 2668820b68bSJeffy Chen static int num_rockchip_sub_drivers; 2678820b68bSJeffy Chen 2683880f62eSHeiko Stuebner /* 269cf544c6aSSascha Hauer * Get the endpoint id of the remote endpoint of the given encoder. This 270cf544c6aSSascha Hauer * information is used by the VOP2 driver to identify the encoder. 271cf544c6aSSascha Hauer * 272cf544c6aSSascha Hauer * @rkencoder: The encoder to get the remote endpoint id from 273cf544c6aSSascha Hauer * @np: The encoder device node 274cf544c6aSSascha Hauer * @port: The number of the port leading to the VOP2 275cf544c6aSSascha Hauer * @reg: The endpoint number leading to the VOP2 276cf544c6aSSascha Hauer */ 277cf544c6aSSascha Hauer int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rkencoder, 278cf544c6aSSascha Hauer struct device_node *np, int port, int reg) 279cf544c6aSSascha Hauer { 280cf544c6aSSascha Hauer struct of_endpoint ep; 281cf544c6aSSascha Hauer struct device_node *en, *ren; 282cf544c6aSSascha Hauer int ret; 283cf544c6aSSascha Hauer 284cf544c6aSSascha Hauer en = of_graph_get_endpoint_by_regs(np, port, reg); 285cf544c6aSSascha Hauer if (!en) 286cf544c6aSSascha Hauer return -ENOENT; 287cf544c6aSSascha Hauer 288cf544c6aSSascha Hauer ren = of_graph_get_remote_endpoint(en); 289cf544c6aSSascha Hauer if (!ren) 290cf544c6aSSascha Hauer return -ENOENT; 291cf544c6aSSascha Hauer 292cf544c6aSSascha Hauer ret = of_graph_parse_endpoint(ren, &ep); 293cf544c6aSSascha Hauer if (ret) 294cf544c6aSSascha Hauer return ret; 295cf544c6aSSascha Hauer 296cf544c6aSSascha Hauer rkencoder->crtc_endpoint_id = ep.id; 297cf544c6aSSascha Hauer 298cf544c6aSSascha Hauer return 0; 299cf544c6aSSascha Hauer } 300cf544c6aSSascha Hauer 301cf544c6aSSascha Hauer /* 3023880f62eSHeiko Stuebner * Check if a vop endpoint is leading to a rockchip subdriver or bridge. 3033880f62eSHeiko Stuebner * Should be called from the component bind stage of the drivers 3043880f62eSHeiko Stuebner * to ensure that all subdrivers are probed. 3053880f62eSHeiko Stuebner * 3063880f62eSHeiko Stuebner * @ep: endpoint of a rockchip vop 3073880f62eSHeiko Stuebner * 3083880f62eSHeiko Stuebner * returns true if subdriver, false if external bridge and -ENODEV 3093880f62eSHeiko Stuebner * if remote port does not contain a device. 3103880f62eSHeiko Stuebner */ 3113880f62eSHeiko Stuebner int rockchip_drm_endpoint_is_subdriver(struct device_node *ep) 3123880f62eSHeiko Stuebner { 3133880f62eSHeiko Stuebner struct device_node *node = of_graph_get_remote_port_parent(ep); 3143880f62eSHeiko Stuebner struct platform_device *pdev; 3153880f62eSHeiko Stuebner struct device_driver *drv; 3163880f62eSHeiko Stuebner int i; 3173880f62eSHeiko Stuebner 3183880f62eSHeiko Stuebner if (!node) 3193880f62eSHeiko Stuebner return -ENODEV; 3203880f62eSHeiko Stuebner 3213880f62eSHeiko Stuebner /* status disabled will prevent creation of platform-devices */ 32237825e07SAlex Bee if (!of_device_is_available(node)) { 32337825e07SAlex Bee of_node_put(node); 32437825e07SAlex Bee return -ENODEV; 32537825e07SAlex Bee } 32637825e07SAlex Bee 3273880f62eSHeiko Stuebner pdev = of_find_device_by_node(node); 3283880f62eSHeiko Stuebner of_node_put(node); 32937825e07SAlex Bee 33037825e07SAlex Bee /* enabled non-platform-devices can immediately return here */ 3313880f62eSHeiko Stuebner if (!pdev) 33237825e07SAlex Bee return false; 3333880f62eSHeiko Stuebner 3343880f62eSHeiko Stuebner /* 3353880f62eSHeiko Stuebner * All rockchip subdrivers have probed at this point, so 3363880f62eSHeiko Stuebner * any device not having a driver now is an external bridge. 3373880f62eSHeiko Stuebner */ 3383880f62eSHeiko Stuebner drv = pdev->dev.driver; 3393880f62eSHeiko Stuebner if (!drv) { 3403880f62eSHeiko Stuebner platform_device_put(pdev); 3413880f62eSHeiko Stuebner return false; 3423880f62eSHeiko Stuebner } 3433880f62eSHeiko Stuebner 3443880f62eSHeiko Stuebner for (i = 0; i < num_rockchip_sub_drivers; i++) { 3453880f62eSHeiko Stuebner if (rockchip_sub_drivers[i] == to_platform_driver(drv)) { 3463880f62eSHeiko Stuebner platform_device_put(pdev); 3473880f62eSHeiko Stuebner return true; 3483880f62eSHeiko Stuebner } 3493880f62eSHeiko Stuebner } 3503880f62eSHeiko Stuebner 3513880f62eSHeiko Stuebner platform_device_put(pdev); 3523880f62eSHeiko Stuebner return false; 3533880f62eSHeiko Stuebner } 3543880f62eSHeiko Stuebner 3557d4e981dSJeffy Chen static void rockchip_drm_match_remove(struct device *dev) 3567d4e981dSJeffy Chen { 3577d4e981dSJeffy Chen struct device_link *link; 3587d4e981dSJeffy Chen 3597d4e981dSJeffy Chen list_for_each_entry(link, &dev->links.consumers, s_node) 3607d4e981dSJeffy Chen device_link_del(link); 3617d4e981dSJeffy Chen } 3627d4e981dSJeffy Chen 3630c4558a1SJonas Karlman /* list of preferred vop devices */ 3640c4558a1SJonas Karlman static const char *const rockchip_drm_match_preferred[] = { 3650c4558a1SJonas Karlman "rockchip,rk3399-vop-big", 3660c4558a1SJonas Karlman NULL, 3670c4558a1SJonas Karlman }; 3680c4558a1SJonas Karlman 3698820b68bSJeffy Chen static struct component_match *rockchip_drm_match_add(struct device *dev) 3705bad7d29SMark Yao { 3718820b68bSJeffy Chen struct component_match *match = NULL; 3720c4558a1SJonas Karlman struct device_node *port; 3738820b68bSJeffy Chen int i; 3745bad7d29SMark Yao 3750c4558a1SJonas Karlman /* add preferred vop device match before adding driver device matches */ 3760c4558a1SJonas Karlman for (i = 0; ; i++) { 3770c4558a1SJonas Karlman port = of_parse_phandle(dev->of_node, "ports", i); 3780c4558a1SJonas Karlman if (!port) 3790c4558a1SJonas Karlman break; 3800c4558a1SJonas Karlman 3810c4558a1SJonas Karlman if (of_device_is_available(port->parent) && 3820c4558a1SJonas Karlman of_device_compatible_match(port->parent, 3830c4558a1SJonas Karlman rockchip_drm_match_preferred)) 3840c4558a1SJonas Karlman drm_of_component_match_add(dev, &match, 3850c4558a1SJonas Karlman component_compare_of, 3860c4558a1SJonas Karlman port->parent); 3870c4558a1SJonas Karlman 3880c4558a1SJonas Karlman of_node_put(port); 3890c4558a1SJonas Karlman } 3900c4558a1SJonas Karlman 3918820b68bSJeffy Chen for (i = 0; i < num_rockchip_sub_drivers; i++) { 3928820b68bSJeffy Chen struct platform_driver *drv = rockchip_sub_drivers[i]; 3938820b68bSJeffy Chen struct device *p = NULL, *d; 3948820b68bSJeffy Chen 3958820b68bSJeffy Chen do { 39636f3313dSSuzuki K Poulose d = platform_find_device_by_driver(p, &drv->driver); 3978820b68bSJeffy Chen put_device(p); 3988820b68bSJeffy Chen p = d; 3998820b68bSJeffy Chen 4008820b68bSJeffy Chen if (!d) 4018820b68bSJeffy Chen break; 4027d4e981dSJeffy Chen 4037d4e981dSJeffy Chen device_link_add(dev, d, DL_FLAG_STATELESS); 404f798aa41SYong Wu component_match_add(dev, &match, component_compare_dev, d); 4058820b68bSJeffy Chen } while (true); 4065bad7d29SMark Yao } 4075bad7d29SMark Yao 4087d4e981dSJeffy Chen if (IS_ERR(match)) 4097d4e981dSJeffy Chen rockchip_drm_match_remove(dev); 4107d4e981dSJeffy Chen 4118820b68bSJeffy Chen return match ?: ERR_PTR(-ENODEV); 4125bad7d29SMark Yao } 4135bad7d29SMark Yao 4142048e328SMark Yao static const struct component_master_ops rockchip_drm_ops = { 4152048e328SMark Yao .bind = rockchip_drm_bind, 4162048e328SMark Yao .unbind = rockchip_drm_unbind, 4172048e328SMark Yao }; 4182048e328SMark Yao 4198820b68bSJeffy Chen static int rockchip_drm_platform_of_probe(struct device *dev) 4202048e328SMark Yao { 4215bad7d29SMark Yao struct device_node *np = dev->of_node; 4225bad7d29SMark Yao struct device_node *port; 4238820b68bSJeffy Chen bool found = false; 4245bad7d29SMark Yao int i; 4252048e328SMark Yao 4265bad7d29SMark Yao if (!np) 4272048e328SMark Yao return -ENODEV; 4288820b68bSJeffy Chen 4295bad7d29SMark Yao for (i = 0;; i++) { 4305bad7d29SMark Yao port = of_parse_phandle(np, "ports", i); 4315bad7d29SMark Yao if (!port) 4325bad7d29SMark Yao break; 4332048e328SMark Yao 4345bad7d29SMark Yao if (!of_device_is_available(port->parent)) { 4355bad7d29SMark Yao of_node_put(port); 4365bad7d29SMark Yao continue; 4375bad7d29SMark Yao } 4385bad7d29SMark Yao 4398820b68bSJeffy Chen found = true; 4405bad7d29SMark Yao of_node_put(port); 4415bad7d29SMark Yao } 4425bad7d29SMark Yao 4435bad7d29SMark Yao if (i == 0) { 444d8dd6804SHaneen Mohammed DRM_DEV_ERROR(dev, "missing 'ports' property\n"); 4455bad7d29SMark Yao return -ENODEV; 4465bad7d29SMark Yao } 4475bad7d29SMark Yao 4488820b68bSJeffy Chen if (!found) { 449d8dd6804SHaneen Mohammed DRM_DEV_ERROR(dev, 450d8dd6804SHaneen Mohammed "No available vop found for display-subsystem.\n"); 4515bad7d29SMark Yao return -ENODEV; 4525bad7d29SMark Yao } 4535bad7d29SMark Yao 4548820b68bSJeffy Chen return 0; 4555bad7d29SMark Yao } 4565bad7d29SMark Yao 4578820b68bSJeffy Chen static int rockchip_drm_platform_probe(struct platform_device *pdev) 4588820b68bSJeffy Chen { 4598820b68bSJeffy Chen struct device *dev = &pdev->dev; 4608820b68bSJeffy Chen struct component_match *match = NULL; 4618820b68bSJeffy Chen int ret; 4628820b68bSJeffy Chen 4638820b68bSJeffy Chen ret = rockchip_drm_platform_of_probe(dev); 4648820b68bSJeffy Chen if (ret) 4658820b68bSJeffy Chen return ret; 4668820b68bSJeffy Chen 4678820b68bSJeffy Chen match = rockchip_drm_match_add(dev); 4688820b68bSJeffy Chen if (IS_ERR(match)) 4698820b68bSJeffy Chen return PTR_ERR(match); 4705bad7d29SMark Yao 4717d4e981dSJeffy Chen ret = component_master_add_with_match(dev, &rockchip_drm_ops, match); 4727d4e981dSJeffy Chen if (ret < 0) { 4737d4e981dSJeffy Chen rockchip_drm_match_remove(dev); 4747d4e981dSJeffy Chen return ret; 4757d4e981dSJeffy Chen } 4767d4e981dSJeffy Chen 4777d4e981dSJeffy Chen return 0; 4782048e328SMark Yao } 4792048e328SMark Yao 4803c855610SUwe Kleine-König static void rockchip_drm_platform_remove(struct platform_device *pdev) 4812048e328SMark Yao { 4822048e328SMark Yao component_master_del(&pdev->dev, &rockchip_drm_ops); 4832048e328SMark Yao 4847d4e981dSJeffy Chen rockchip_drm_match_remove(&pdev->dev); 4852048e328SMark Yao } 4862048e328SMark Yao 487b8f9d7f3SVicente Bergas static void rockchip_drm_platform_shutdown(struct platform_device *pdev) 488b8f9d7f3SVicente Bergas { 489b8f9d7f3SVicente Bergas struct drm_device *drm = platform_get_drvdata(pdev); 490b8f9d7f3SVicente Bergas 491b8f9d7f3SVicente Bergas if (drm) 492b8f9d7f3SVicente Bergas drm_atomic_helper_shutdown(drm); 493b8f9d7f3SVicente Bergas } 494b8f9d7f3SVicente Bergas 4952048e328SMark Yao static const struct of_device_id rockchip_drm_dt_ids[] = { 4962048e328SMark Yao { .compatible = "rockchip,display-subsystem", }, 4972048e328SMark Yao { /* sentinel */ }, 4982048e328SMark Yao }; 4992048e328SMark Yao MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); 5002048e328SMark Yao 5012048e328SMark Yao static struct platform_driver rockchip_drm_platform_driver = { 5022048e328SMark Yao .probe = rockchip_drm_platform_probe, 5033c855610SUwe Kleine-König .remove_new = rockchip_drm_platform_remove, 504b8f9d7f3SVicente Bergas .shutdown = rockchip_drm_platform_shutdown, 5052048e328SMark Yao .driver = { 5062048e328SMark Yao .name = "rockchip-drm", 5072048e328SMark Yao .of_match_table = rockchip_drm_dt_ids, 5082048e328SMark Yao .pm = &rockchip_drm_pm_ops, 5092048e328SMark Yao }, 5102048e328SMark Yao }; 5112048e328SMark Yao 5128820b68bSJeffy Chen #define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \ 5138820b68bSJeffy Chen if (IS_ENABLED(cond) && \ 5148820b68bSJeffy Chen !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \ 5158820b68bSJeffy Chen rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \ 5168820b68bSJeffy Chen } 5178820b68bSJeffy Chen 5188820b68bSJeffy Chen static int __init rockchip_drm_init(void) 5198820b68bSJeffy Chen { 5208820b68bSJeffy Chen int ret; 5218820b68bSJeffy Chen 52209037781SJavier Martinez Canillas if (drm_firmware_drivers_only()) 52309037781SJavier Martinez Canillas return -ENODEV; 52409037781SJavier Martinez Canillas 5258820b68bSJeffy Chen num_rockchip_sub_drivers = 0; 526b382406aSSascha Hauer ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP); 527604be855SAndy Yan ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2); 52834cc0aa2SSandy Huang ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, 52934cc0aa2SSandy Huang CONFIG_ROCKCHIP_LVDS); 5308820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, 5318820b68bSJeffy Chen CONFIG_ROCKCHIP_ANALOGIX_DP); 5328820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); 5338820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, 5348820b68bSJeffy Chen CONFIG_ROCKCHIP_DW_HDMI); 5352d4f7bdaSNickey Yang ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, 5368820b68bSJeffy Chen CONFIG_ROCKCHIP_DW_MIPI_DSI); 5378820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); 538f84d3d37SZheng Yang ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, 539f84d3d37SZheng Yang CONFIG_ROCKCHIP_RK3066_HDMI); 5408820b68bSJeffy Chen 5418820b68bSJeffy Chen ret = platform_register_drivers(rockchip_sub_drivers, 5428820b68bSJeffy Chen num_rockchip_sub_drivers); 5438820b68bSJeffy Chen if (ret) 5448820b68bSJeffy Chen return ret; 5458820b68bSJeffy Chen 5468820b68bSJeffy Chen ret = platform_driver_register(&rockchip_drm_platform_driver); 5478820b68bSJeffy Chen if (ret) 5488820b68bSJeffy Chen goto err_unreg_drivers; 5498820b68bSJeffy Chen 5508820b68bSJeffy Chen return 0; 5518820b68bSJeffy Chen 5528820b68bSJeffy Chen err_unreg_drivers: 5538820b68bSJeffy Chen platform_unregister_drivers(rockchip_sub_drivers, 5548820b68bSJeffy Chen num_rockchip_sub_drivers); 5558820b68bSJeffy Chen return ret; 5568820b68bSJeffy Chen } 5578820b68bSJeffy Chen 5588820b68bSJeffy Chen static void __exit rockchip_drm_fini(void) 5598820b68bSJeffy Chen { 5608820b68bSJeffy Chen platform_driver_unregister(&rockchip_drm_platform_driver); 5618820b68bSJeffy Chen 5628820b68bSJeffy Chen platform_unregister_drivers(rockchip_sub_drivers, 5638820b68bSJeffy Chen num_rockchip_sub_drivers); 5648820b68bSJeffy Chen } 5658820b68bSJeffy Chen 5668820b68bSJeffy Chen module_init(rockchip_drm_init); 5678820b68bSJeffy Chen module_exit(rockchip_drm_fini); 5682048e328SMark Yao 5692048e328SMark Yao MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); 5702048e328SMark Yao MODULE_DESCRIPTION("ROCKCHIP DRM Driver"); 5712048e328SMark Yao MODULE_LICENSE("GPL v2"); 572