xref: /openbmc/linux/drivers/gpu/drm/rockchip/rockchip_drm_drv.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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 
92048e328SMark Yao #include <linux/dma-mapping.h>
10*722d4f06SRob Herring #include <linux/platform_device.h>
112048e328SMark Yao #include <linux/pm_runtime.h>
1200fe6148SPaul Gortmaker #include <linux/module.h>
132048e328SMark Yao #include <linux/of_graph.h>
143880f62eSHeiko Stuebner #include <linux/of_platform.h>
152048e328SMark Yao #include <linux/component.h>
165a587383STomeu Vizoso #include <linux/console.h>
171aa5ca6eSShunqian Zheng #include <linux/iommu.h>
182048e328SMark Yao 
193c1ed51aSJavier Martinez Canillas #include <drm/drm_aperture.h>
20c2156ccdSSam Ravnborg #include <drm/drm_drv.h>
218ab59da2SThomas Zimmermann #include <drm/drm_fbdev_generic.h>
224a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
23c2156ccdSSam Ravnborg #include <drm/drm_of.h>
24c2156ccdSSam Ravnborg #include <drm/drm_probe_helper.h>
25c2156ccdSSam Ravnborg #include <drm/drm_vblank.h>
26c2156ccdSSam Ravnborg 
278490cad4SSteven Price #if defined(CONFIG_ARM_DMA_USE_IOMMU)
288490cad4SSteven Price #include <asm/dma-iommu.h>
298490cad4SSteven Price #else
308490cad4SSteven Price #define arm_iommu_detach_device(...)	({ })
318490cad4SSteven Price #define arm_iommu_release_mapping(...)	({ })
328490cad4SSteven Price #define to_dma_iommu_mapping(dev) NULL
338490cad4SSteven Price #endif
348490cad4SSteven Price 
352048e328SMark Yao #include "rockchip_drm_drv.h"
362048e328SMark Yao #include "rockchip_drm_fb.h"
372048e328SMark Yao #include "rockchip_drm_gem.h"
382048e328SMark Yao 
392048e328SMark Yao #define DRIVER_NAME	"rockchip"
402048e328SMark Yao #define DRIVER_DESC	"RockChip Soc DRM"
412048e328SMark Yao #define DRIVER_DATE	"20140818"
422048e328SMark Yao #define DRIVER_MAJOR	1
432048e328SMark Yao #define DRIVER_MINOR	0
442048e328SMark Yao 
4570a59dd8SDaniel Vetter static const struct drm_driver rockchip_drm_driver;
462d90d477SMark Yao 
472048e328SMark Yao /*
482048e328SMark Yao  * Attach a (component) device to the shared drm dma mapping from master drm
492048e328SMark Yao  * device.  This is used by the VOPs to map GEM buffers to a common DMA
502048e328SMark Yao  * mapping.
512048e328SMark Yao  */
rockchip_drm_dma_attach_device(struct drm_device * drm_dev,struct device * dev)522048e328SMark Yao int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
532048e328SMark Yao 				   struct device *dev)
542048e328SMark Yao {
551aa5ca6eSShunqian Zheng 	struct rockchip_drm_private *private = drm_dev->dev_private;
562048e328SMark Yao 	int ret;
572048e328SMark Yao 
58421be3eeSRobin Murphy 	if (!private->domain)
592d90d477SMark Yao 		return 0;
602d90d477SMark Yao 
618490cad4SSteven Price 	if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
628490cad4SSteven Price 		struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
638490cad4SSteven Price 
648490cad4SSteven Price 		if (mapping) {
658490cad4SSteven Price 			arm_iommu_detach_device(dev);
668490cad4SSteven Price 			arm_iommu_release_mapping(mapping);
678490cad4SSteven Price 		}
688490cad4SSteven Price 	}
698490cad4SSteven Price 
701aa5ca6eSShunqian Zheng 	ret = iommu_attach_device(private->domain, dev);
711aa5ca6eSShunqian Zheng 	if (ret) {
72d8dd6804SHaneen Mohammed 		DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
732048e328SMark Yao 		return ret;
741aa5ca6eSShunqian Zheng 	}
752048e328SMark Yao 
761aa5ca6eSShunqian Zheng 	return 0;
772048e328SMark Yao }
782048e328SMark Yao 
rockchip_drm_dma_detach_device(struct drm_device * drm_dev,struct device * dev)792048e328SMark Yao void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
802048e328SMark Yao 				    struct device *dev)
812048e328SMark Yao {
821aa5ca6eSShunqian Zheng 	struct rockchip_drm_private *private = drm_dev->dev_private;
831aa5ca6eSShunqian Zheng 
84421be3eeSRobin Murphy 	if (!private->domain)
852d90d477SMark Yao 		return;
862d90d477SMark Yao 
87421be3eeSRobin Murphy 	iommu_detach_device(private->domain, dev);
88421be3eeSRobin Murphy }
89421be3eeSRobin Murphy 
rockchip_drm_dma_init_device(struct drm_device * drm_dev,struct device * dev)90421be3eeSRobin Murphy void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
91421be3eeSRobin Murphy 				  struct device *dev)
92421be3eeSRobin Murphy {
93421be3eeSRobin Murphy 	struct rockchip_drm_private *private = drm_dev->dev_private;
94421be3eeSRobin Murphy 
95421be3eeSRobin Murphy 	if (!device_iommu_mapped(dev))
96421be3eeSRobin Murphy 		private->iommu_dev = ERR_PTR(-ENODEV);
97421be3eeSRobin Murphy 	else if (!private->iommu_dev)
98421be3eeSRobin Murphy 		private->iommu_dev = dev;
992048e328SMark Yao }
1002048e328SMark Yao 
rockchip_drm_init_iommu(struct drm_device * drm_dev)1011aa5ca6eSShunqian Zheng static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
1021aa5ca6eSShunqian Zheng {
1031aa5ca6eSShunqian Zheng 	struct rockchip_drm_private *private = drm_dev->dev_private;
1041aa5ca6eSShunqian Zheng 	struct iommu_domain_geometry *geometry;
1051aa5ca6eSShunqian Zheng 	u64 start, end;
1061aa5ca6eSShunqian Zheng 
107421be3eeSRobin Murphy 	if (IS_ERR_OR_NULL(private->iommu_dev))
1081aa5ca6eSShunqian Zheng 		return 0;
1091aa5ca6eSShunqian Zheng 
110421be3eeSRobin Murphy 	private->domain = iommu_domain_alloc(private->iommu_dev->bus);
1111aa5ca6eSShunqian Zheng 	if (!private->domain)
1121aa5ca6eSShunqian Zheng 		return -ENOMEM;
1131aa5ca6eSShunqian Zheng 
1141aa5ca6eSShunqian Zheng 	geometry = &private->domain->geometry;
1151aa5ca6eSShunqian Zheng 	start = geometry->aperture_start;
1161aa5ca6eSShunqian Zheng 	end = geometry->aperture_end;
1171aa5ca6eSShunqian Zheng 
1181aa5ca6eSShunqian Zheng 	DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n",
1191aa5ca6eSShunqian Zheng 		  start, end);
1201aa5ca6eSShunqian Zheng 	drm_mm_init(&private->mm, start, end - start + 1);
1211aa5ca6eSShunqian Zheng 	mutex_init(&private->mm_lock);
1221aa5ca6eSShunqian Zheng 
1231aa5ca6eSShunqian Zheng 	return 0;
1241aa5ca6eSShunqian Zheng }
1251aa5ca6eSShunqian Zheng 
rockchip_iommu_cleanup(struct drm_device * drm_dev)1261aa5ca6eSShunqian Zheng static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
1271aa5ca6eSShunqian Zheng {
1281aa5ca6eSShunqian Zheng 	struct rockchip_drm_private *private = drm_dev->dev_private;
1291aa5ca6eSShunqian Zheng 
130421be3eeSRobin Murphy 	if (!private->domain)
1311aa5ca6eSShunqian Zheng 		return;
1321aa5ca6eSShunqian Zheng 
1331aa5ca6eSShunqian Zheng 	drm_mm_takedown(&private->mm);
1341aa5ca6eSShunqian Zheng 	iommu_domain_free(private->domain);
1352048e328SMark Yao }
1362048e328SMark Yao 
rockchip_drm_bind(struct device * dev)137f706974aSTomeu Vizoso static int rockchip_drm_bind(struct device *dev)
1382048e328SMark Yao {
139f706974aSTomeu Vizoso 	struct drm_device *drm_dev;
1402048e328SMark Yao 	struct rockchip_drm_private *private;
1412048e328SMark Yao 	int ret;
1422048e328SMark Yao 
1433c1ed51aSJavier Martinez Canillas 	/* Remove existing drivers that may own the framebuffer memory. */
14462aeaeaaSDaniel Vetter 	ret = drm_aperture_remove_framebuffers(&rockchip_drm_driver);
1453c1ed51aSJavier Martinez Canillas 	if (ret) {
1463c1ed51aSJavier Martinez Canillas 		DRM_DEV_ERROR(dev,
1473c1ed51aSJavier Martinez Canillas 			      "Failed to remove existing framebuffers - %d.\n",
1483c1ed51aSJavier Martinez Canillas 			      ret);
1493c1ed51aSJavier Martinez Canillas 		return ret;
1503c1ed51aSJavier Martinez Canillas 	}
1513c1ed51aSJavier Martinez Canillas 
152f706974aSTomeu Vizoso 	drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
1530f288605STom Gundersen 	if (IS_ERR(drm_dev))
1540f288605STom Gundersen 		return PTR_ERR(drm_dev);
1552048e328SMark Yao 
156f706974aSTomeu Vizoso 	dev_set_drvdata(dev, drm_dev);
157f706974aSTomeu Vizoso 
158f706974aSTomeu Vizoso 	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
159f706974aSTomeu Vizoso 	if (!private) {
160f706974aSTomeu Vizoso 		ret = -ENOMEM;
1619127f99cSTomasz Figa 		goto err_free;
162f706974aSTomeu Vizoso 	}
163f706974aSTomeu Vizoso 
1642048e328SMark Yao 	drm_dev->dev_private = private;
1652048e328SMark Yao 
16656e35f85SDaniel Vetter 	ret = drmm_mode_config_init(drm_dev);
16756e35f85SDaniel Vetter 	if (ret)
168421be3eeSRobin Murphy 		goto err_free;
1692048e328SMark Yao 
1702048e328SMark Yao 	rockchip_drm_mode_config_init(drm_dev);
1712048e328SMark Yao 
1722048e328SMark Yao 	/* Try to bind all sub drivers. */
1732048e328SMark Yao 	ret = component_bind_all(dev, drm_dev);
1742048e328SMark Yao 	if (ret)
175421be3eeSRobin Murphy 		goto err_free;
176421be3eeSRobin Murphy 
177421be3eeSRobin Murphy 	ret = rockchip_drm_init_iommu(drm_dev);
178421be3eeSRobin Murphy 	if (ret)
179421be3eeSRobin Murphy 		goto err_unbind_all;
1802048e328SMark Yao 
181ccea9199SJeffy Chen 	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
182ccea9199SJeffy Chen 	if (ret)
183421be3eeSRobin Murphy 		goto err_iommu_cleanup;
184ccea9199SJeffy Chen 
185ccea9199SJeffy Chen 	drm_mode_config_reset(drm_dev);
1862048e328SMark Yao 
187ccea9199SJeffy Chen 	/* init kms poll for handling hpd */
188ccea9199SJeffy Chen 	drm_kms_helper_poll_init(drm_dev);
18963ebb9faSMark Yao 
1908415ab56SMark yao 	ret = drm_dev_register(drm_dev, 0);
1912048e328SMark Yao 	if (ret)
192ccea9199SJeffy Chen 		goto err_kms_helper_poll_fini;
1932048e328SMark Yao 
194f2ed93a4SJohn Keeping 	drm_fbdev_generic_setup(drm_dev, 0);
19524af7c34SJohn Keeping 
1962048e328SMark Yao 	return 0;
1972048e328SMark Yao err_kms_helper_poll_fini:
1982048e328SMark Yao 	drm_kms_helper_poll_fini(drm_dev);
19956e35f85SDaniel Vetter err_iommu_cleanup:
200ccea9199SJeffy Chen 	rockchip_iommu_cleanup(drm_dev);
201421be3eeSRobin Murphy err_unbind_all:
202421be3eeSRobin Murphy 	component_unbind_all(dev, drm_dev);
203f706974aSTomeu Vizoso err_free:
204574e0fbfSThomas Zimmermann 	drm_dev_put(drm_dev);
2052048e328SMark Yao 	return ret;
2062048e328SMark Yao }
2072048e328SMark Yao 
rockchip_drm_unbind(struct device * dev)208f706974aSTomeu Vizoso static void rockchip_drm_unbind(struct device *dev)
2092048e328SMark Yao {
210f706974aSTomeu Vizoso 	struct drm_device *drm_dev = dev_get_drvdata(dev);
2112048e328SMark Yao 
212f706974aSTomeu Vizoso 	drm_dev_unregister(drm_dev);
213ccea9199SJeffy Chen 
214ccea9199SJeffy Chen 	drm_kms_helper_poll_fini(drm_dev);
215ccea9199SJeffy Chen 
216c1bb8188SJeffy Chen 	drm_atomic_helper_shutdown(drm_dev);
217ccea9199SJeffy Chen 	component_unbind_all(dev, drm_dev);
218ccea9199SJeffy Chen 	rockchip_iommu_cleanup(drm_dev);
219ccea9199SJeffy Chen 
220574e0fbfSThomas Zimmermann 	drm_dev_put(drm_dev);
2212048e328SMark Yao }
2222048e328SMark Yao 
223f8b53070SThomas Zimmermann DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops);
2242048e328SMark Yao 
22570a59dd8SDaniel Vetter static const struct drm_driver rockchip_drm_driver = {
2260424fdafSDaniel Vetter 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
2272048e328SMark Yao 	.dumb_create		= rockchip_gem_dumb_create,
2286fd0bfe2SHaixia Shi 	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
2292048e328SMark Yao 	.fops			= &rockchip_drm_driver_fops,
2302048e328SMark Yao 	.name	= DRIVER_NAME,
2312048e328SMark Yao 	.desc	= DRIVER_DESC,
2322048e328SMark Yao 	.date	= DRIVER_DATE,
2332048e328SMark Yao 	.major	= DRIVER_MAJOR,
2342048e328SMark Yao 	.minor	= DRIVER_MINOR,
2352048e328SMark Yao };
2362048e328SMark Yao 
2372048e328SMark Yao #ifdef CONFIG_PM_SLEEP
rockchip_drm_sys_suspend(struct device * dev)2382048e328SMark Yao static int rockchip_drm_sys_suspend(struct device *dev)
2392048e328SMark Yao {
2402048e328SMark Yao 	struct drm_device *drm = dev_get_drvdata(dev);
2410fa375e6SJeffy Chen 
242e7941cc2SSouptick Joarder 	return drm_mode_config_helper_suspend(drm);
2432048e328SMark Yao }
2442048e328SMark Yao 
rockchip_drm_sys_resume(struct device * dev)2452048e328SMark Yao static int rockchip_drm_sys_resume(struct device *dev)
2462048e328SMark Yao {
2472048e328SMark Yao 	struct drm_device *drm = dev_get_drvdata(dev);
2482048e328SMark Yao 
249e7941cc2SSouptick Joarder 	return drm_mode_config_helper_resume(drm);
2502048e328SMark Yao }
2512048e328SMark Yao #endif
2522048e328SMark Yao 
2532048e328SMark Yao static const struct dev_pm_ops rockchip_drm_pm_ops = {
2542048e328SMark Yao 	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
2552048e328SMark Yao 				rockchip_drm_sys_resume)
2562048e328SMark Yao };
2572048e328SMark Yao 
2588820b68bSJeffy Chen #define MAX_ROCKCHIP_SUB_DRIVERS 16
2598820b68bSJeffy Chen static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS];
2608820b68bSJeffy Chen static int num_rockchip_sub_drivers;
2618820b68bSJeffy Chen 
2623880f62eSHeiko Stuebner /*
263cf544c6aSSascha Hauer  * Get the endpoint id of the remote endpoint of the given encoder. This
264cf544c6aSSascha Hauer  * information is used by the VOP2 driver to identify the encoder.
265cf544c6aSSascha Hauer  *
266cf544c6aSSascha Hauer  * @rkencoder: The encoder to get the remote endpoint id from
267cf544c6aSSascha Hauer  * @np: The encoder device node
268cf544c6aSSascha Hauer  * @port: The number of the port leading to the VOP2
269cf544c6aSSascha Hauer  * @reg: The endpoint number leading to the VOP2
270cf544c6aSSascha Hauer  */
rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder * rkencoder,struct device_node * np,int port,int reg)271cf544c6aSSascha Hauer int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rkencoder,
272cf544c6aSSascha Hauer 					      struct device_node *np, int port, int reg)
273cf544c6aSSascha Hauer {
274cf544c6aSSascha Hauer 	struct of_endpoint ep;
275cf544c6aSSascha Hauer 	struct device_node *en, *ren;
276cf544c6aSSascha Hauer 	int ret;
277cf544c6aSSascha Hauer 
278cf544c6aSSascha Hauer 	en = of_graph_get_endpoint_by_regs(np, port, reg);
279cf544c6aSSascha Hauer 	if (!en)
280cf544c6aSSascha Hauer 		return -ENOENT;
281cf544c6aSSascha Hauer 
282cf544c6aSSascha Hauer 	ren = of_graph_get_remote_endpoint(en);
283cf544c6aSSascha Hauer 	if (!ren)
284cf544c6aSSascha Hauer 		return -ENOENT;
285cf544c6aSSascha Hauer 
286cf544c6aSSascha Hauer 	ret = of_graph_parse_endpoint(ren, &ep);
287cf544c6aSSascha Hauer 	if (ret)
288cf544c6aSSascha Hauer 		return ret;
289cf544c6aSSascha Hauer 
290cf544c6aSSascha Hauer 	rkencoder->crtc_endpoint_id = ep.id;
291cf544c6aSSascha Hauer 
292cf544c6aSSascha Hauer 	return 0;
293cf544c6aSSascha Hauer }
294cf544c6aSSascha Hauer 
295cf544c6aSSascha Hauer /*
2963880f62eSHeiko Stuebner  * Check if a vop endpoint is leading to a rockchip subdriver or bridge.
2973880f62eSHeiko Stuebner  * Should be called from the component bind stage of the drivers
2983880f62eSHeiko Stuebner  * to ensure that all subdrivers are probed.
2993880f62eSHeiko Stuebner  *
3003880f62eSHeiko Stuebner  * @ep: endpoint of a rockchip vop
3013880f62eSHeiko Stuebner  *
3023880f62eSHeiko Stuebner  * returns true if subdriver, false if external bridge and -ENODEV
3033880f62eSHeiko Stuebner  * if remote port does not contain a device.
3043880f62eSHeiko Stuebner  */
rockchip_drm_endpoint_is_subdriver(struct device_node * ep)3053880f62eSHeiko Stuebner int rockchip_drm_endpoint_is_subdriver(struct device_node *ep)
3063880f62eSHeiko Stuebner {
3073880f62eSHeiko Stuebner 	struct device_node *node = of_graph_get_remote_port_parent(ep);
3083880f62eSHeiko Stuebner 	struct platform_device *pdev;
3093880f62eSHeiko Stuebner 	struct device_driver *drv;
3103880f62eSHeiko Stuebner 	int i;
3113880f62eSHeiko Stuebner 
3123880f62eSHeiko Stuebner 	if (!node)
3133880f62eSHeiko Stuebner 		return -ENODEV;
3143880f62eSHeiko Stuebner 
3153880f62eSHeiko Stuebner 	/* status disabled will prevent creation of platform-devices */
31637825e07SAlex Bee 	if (!of_device_is_available(node)) {
31737825e07SAlex Bee 		of_node_put(node);
31837825e07SAlex Bee 		return -ENODEV;
31937825e07SAlex Bee 	}
32037825e07SAlex Bee 
3213880f62eSHeiko Stuebner 	pdev = of_find_device_by_node(node);
3223880f62eSHeiko Stuebner 	of_node_put(node);
32337825e07SAlex Bee 
32437825e07SAlex Bee 	/* enabled non-platform-devices can immediately return here */
3253880f62eSHeiko Stuebner 	if (!pdev)
32637825e07SAlex Bee 		return false;
3273880f62eSHeiko Stuebner 
3283880f62eSHeiko Stuebner 	/*
3293880f62eSHeiko Stuebner 	 * All rockchip subdrivers have probed at this point, so
3303880f62eSHeiko Stuebner 	 * any device not having a driver now is an external bridge.
3313880f62eSHeiko Stuebner 	 */
3323880f62eSHeiko Stuebner 	drv = pdev->dev.driver;
3333880f62eSHeiko Stuebner 	if (!drv) {
3343880f62eSHeiko Stuebner 		platform_device_put(pdev);
3353880f62eSHeiko Stuebner 		return false;
3363880f62eSHeiko Stuebner 	}
3373880f62eSHeiko Stuebner 
3383880f62eSHeiko Stuebner 	for (i = 0; i < num_rockchip_sub_drivers; i++) {
3393880f62eSHeiko Stuebner 		if (rockchip_sub_drivers[i] == to_platform_driver(drv)) {
3403880f62eSHeiko Stuebner 			platform_device_put(pdev);
3413880f62eSHeiko Stuebner 			return true;
3423880f62eSHeiko Stuebner 		}
3433880f62eSHeiko Stuebner 	}
3443880f62eSHeiko Stuebner 
3453880f62eSHeiko Stuebner 	platform_device_put(pdev);
3463880f62eSHeiko Stuebner 	return false;
3473880f62eSHeiko Stuebner }
3483880f62eSHeiko Stuebner 
rockchip_drm_match_remove(struct device * dev)3497d4e981dSJeffy Chen static void rockchip_drm_match_remove(struct device *dev)
3507d4e981dSJeffy Chen {
3517d4e981dSJeffy Chen 	struct device_link *link;
3527d4e981dSJeffy Chen 
3537d4e981dSJeffy Chen 	list_for_each_entry(link, &dev->links.consumers, s_node)
3547d4e981dSJeffy Chen 		device_link_del(link);
3557d4e981dSJeffy Chen }
3567d4e981dSJeffy Chen 
rockchip_drm_match_add(struct device * dev)3578820b68bSJeffy Chen static struct component_match *rockchip_drm_match_add(struct device *dev)
3585bad7d29SMark Yao {
3598820b68bSJeffy Chen 	struct component_match *match = NULL;
3608820b68bSJeffy Chen 	int i;
3615bad7d29SMark Yao 
3628820b68bSJeffy Chen 	for (i = 0; i < num_rockchip_sub_drivers; i++) {
3638820b68bSJeffy Chen 		struct platform_driver *drv = rockchip_sub_drivers[i];
3648820b68bSJeffy Chen 		struct device *p = NULL, *d;
3658820b68bSJeffy Chen 
3668820b68bSJeffy Chen 		do {
36736f3313dSSuzuki K Poulose 			d = platform_find_device_by_driver(p, &drv->driver);
3688820b68bSJeffy Chen 			put_device(p);
3698820b68bSJeffy Chen 			p = d;
3708820b68bSJeffy Chen 
3718820b68bSJeffy Chen 			if (!d)
3728820b68bSJeffy Chen 				break;
3737d4e981dSJeffy Chen 
3747d4e981dSJeffy Chen 			device_link_add(dev, d, DL_FLAG_STATELESS);
375f798aa41SYong Wu 			component_match_add(dev, &match, component_compare_dev, d);
3768820b68bSJeffy Chen 		} while (true);
3775bad7d29SMark Yao 	}
3785bad7d29SMark Yao 
3797d4e981dSJeffy Chen 	if (IS_ERR(match))
3807d4e981dSJeffy Chen 		rockchip_drm_match_remove(dev);
3817d4e981dSJeffy Chen 
3828820b68bSJeffy Chen 	return match ?: ERR_PTR(-ENODEV);
3835bad7d29SMark Yao }
3845bad7d29SMark Yao 
3852048e328SMark Yao static const struct component_master_ops rockchip_drm_ops = {
3862048e328SMark Yao 	.bind = rockchip_drm_bind,
3872048e328SMark Yao 	.unbind = rockchip_drm_unbind,
3882048e328SMark Yao };
3892048e328SMark Yao 
rockchip_drm_platform_of_probe(struct device * dev)3908820b68bSJeffy Chen static int rockchip_drm_platform_of_probe(struct device *dev)
3912048e328SMark Yao {
3925bad7d29SMark Yao 	struct device_node *np = dev->of_node;
3935bad7d29SMark Yao 	struct device_node *port;
3948820b68bSJeffy Chen 	bool found = false;
3955bad7d29SMark Yao 	int i;
3962048e328SMark Yao 
3975bad7d29SMark Yao 	if (!np)
3982048e328SMark Yao 		return -ENODEV;
3998820b68bSJeffy Chen 
4005bad7d29SMark Yao 	for (i = 0;; i++) {
4015bad7d29SMark Yao 		port = of_parse_phandle(np, "ports", i);
4025bad7d29SMark Yao 		if (!port)
4035bad7d29SMark Yao 			break;
4042048e328SMark Yao 
4055bad7d29SMark Yao 		if (!of_device_is_available(port->parent)) {
4065bad7d29SMark Yao 			of_node_put(port);
4075bad7d29SMark Yao 			continue;
4085bad7d29SMark Yao 		}
4095bad7d29SMark Yao 
4108820b68bSJeffy Chen 		found = true;
4115bad7d29SMark Yao 		of_node_put(port);
4125bad7d29SMark Yao 	}
4135bad7d29SMark Yao 
4145bad7d29SMark Yao 	if (i == 0) {
415d8dd6804SHaneen Mohammed 		DRM_DEV_ERROR(dev, "missing 'ports' property\n");
4165bad7d29SMark Yao 		return -ENODEV;
4175bad7d29SMark Yao 	}
4185bad7d29SMark Yao 
4198820b68bSJeffy Chen 	if (!found) {
420d8dd6804SHaneen Mohammed 		DRM_DEV_ERROR(dev,
421d8dd6804SHaneen Mohammed 			      "No available vop found for display-subsystem.\n");
4225bad7d29SMark Yao 		return -ENODEV;
4235bad7d29SMark Yao 	}
4245bad7d29SMark Yao 
4258820b68bSJeffy Chen 	return 0;
4265bad7d29SMark Yao }
4275bad7d29SMark Yao 
rockchip_drm_platform_probe(struct platform_device * pdev)4288820b68bSJeffy Chen static int rockchip_drm_platform_probe(struct platform_device *pdev)
4298820b68bSJeffy Chen {
4308820b68bSJeffy Chen 	struct device *dev = &pdev->dev;
4318820b68bSJeffy Chen 	struct component_match *match = NULL;
4328820b68bSJeffy Chen 	int ret;
4338820b68bSJeffy Chen 
4348820b68bSJeffy Chen 	ret = rockchip_drm_platform_of_probe(dev);
4358820b68bSJeffy Chen 	if (ret)
4368820b68bSJeffy Chen 		return ret;
4378820b68bSJeffy Chen 
4388820b68bSJeffy Chen 	match = rockchip_drm_match_add(dev);
4398820b68bSJeffy Chen 	if (IS_ERR(match))
4408820b68bSJeffy Chen 		return PTR_ERR(match);
4415bad7d29SMark Yao 
4427d4e981dSJeffy Chen 	ret = component_master_add_with_match(dev, &rockchip_drm_ops, match);
4437d4e981dSJeffy Chen 	if (ret < 0) {
4447d4e981dSJeffy Chen 		rockchip_drm_match_remove(dev);
4457d4e981dSJeffy Chen 		return ret;
4467d4e981dSJeffy Chen 	}
4477d4e981dSJeffy Chen 
4487d4e981dSJeffy Chen 	return 0;
4492048e328SMark Yao }
4502048e328SMark Yao 
rockchip_drm_platform_remove(struct platform_device * pdev)4513c855610SUwe Kleine-König static void rockchip_drm_platform_remove(struct platform_device *pdev)
4522048e328SMark Yao {
4532048e328SMark Yao 	component_master_del(&pdev->dev, &rockchip_drm_ops);
4542048e328SMark Yao 
4557d4e981dSJeffy Chen 	rockchip_drm_match_remove(&pdev->dev);
4562048e328SMark Yao }
4572048e328SMark Yao 
rockchip_drm_platform_shutdown(struct platform_device * pdev)458b8f9d7f3SVicente Bergas static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
459b8f9d7f3SVicente Bergas {
460b8f9d7f3SVicente Bergas 	struct drm_device *drm = platform_get_drvdata(pdev);
461b8f9d7f3SVicente Bergas 
462b8f9d7f3SVicente Bergas 	if (drm)
463b8f9d7f3SVicente Bergas 		drm_atomic_helper_shutdown(drm);
464b8f9d7f3SVicente Bergas }
465b8f9d7f3SVicente Bergas 
4662048e328SMark Yao static const struct of_device_id rockchip_drm_dt_ids[] = {
4672048e328SMark Yao 	{ .compatible = "rockchip,display-subsystem", },
4682048e328SMark Yao 	{ /* sentinel */ },
4692048e328SMark Yao };
4702048e328SMark Yao MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
4712048e328SMark Yao 
4722048e328SMark Yao static struct platform_driver rockchip_drm_platform_driver = {
4732048e328SMark Yao 	.probe = rockchip_drm_platform_probe,
4743c855610SUwe Kleine-König 	.remove_new = rockchip_drm_platform_remove,
475b8f9d7f3SVicente Bergas 	.shutdown = rockchip_drm_platform_shutdown,
4762048e328SMark Yao 	.driver = {
4772048e328SMark Yao 		.name = "rockchip-drm",
4782048e328SMark Yao 		.of_match_table = rockchip_drm_dt_ids,
4792048e328SMark Yao 		.pm = &rockchip_drm_pm_ops,
4802048e328SMark Yao 	},
4812048e328SMark Yao };
4822048e328SMark Yao 
4838820b68bSJeffy Chen #define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \
4848820b68bSJeffy Chen 	if (IS_ENABLED(cond) && \
4858820b68bSJeffy Chen 	    !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \
4868820b68bSJeffy Chen 		rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \
4878820b68bSJeffy Chen }
4888820b68bSJeffy Chen 
rockchip_drm_init(void)4898820b68bSJeffy Chen static int __init rockchip_drm_init(void)
4908820b68bSJeffy Chen {
4918820b68bSJeffy Chen 	int ret;
4928820b68bSJeffy Chen 
49309037781SJavier Martinez Canillas 	if (drm_firmware_drivers_only())
49409037781SJavier Martinez Canillas 		return -ENODEV;
49509037781SJavier Martinez Canillas 
4968820b68bSJeffy Chen 	num_rockchip_sub_drivers = 0;
497b382406aSSascha Hauer 	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
498604be855SAndy Yan 	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
49934cc0aa2SSandy Huang 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
50034cc0aa2SSandy Huang 				CONFIG_ROCKCHIP_LVDS);
5018820b68bSJeffy Chen 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
5028820b68bSJeffy Chen 				CONFIG_ROCKCHIP_ANALOGIX_DP);
5038820b68bSJeffy Chen 	ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
5048820b68bSJeffy Chen 	ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
5058820b68bSJeffy Chen 				CONFIG_ROCKCHIP_DW_HDMI);
5062d4f7bdaSNickey Yang 	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
5078820b68bSJeffy Chen 				CONFIG_ROCKCHIP_DW_MIPI_DSI);
5088820b68bSJeffy Chen 	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
509f84d3d37SZheng Yang 	ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
510f84d3d37SZheng Yang 				CONFIG_ROCKCHIP_RK3066_HDMI);
5118820b68bSJeffy Chen 
5128820b68bSJeffy Chen 	ret = platform_register_drivers(rockchip_sub_drivers,
5138820b68bSJeffy Chen 					num_rockchip_sub_drivers);
5148820b68bSJeffy Chen 	if (ret)
5158820b68bSJeffy Chen 		return ret;
5168820b68bSJeffy Chen 
5178820b68bSJeffy Chen 	ret = platform_driver_register(&rockchip_drm_platform_driver);
5188820b68bSJeffy Chen 	if (ret)
5198820b68bSJeffy Chen 		goto err_unreg_drivers;
5208820b68bSJeffy Chen 
5218820b68bSJeffy Chen 	return 0;
5228820b68bSJeffy Chen 
5238820b68bSJeffy Chen err_unreg_drivers:
5248820b68bSJeffy Chen 	platform_unregister_drivers(rockchip_sub_drivers,
5258820b68bSJeffy Chen 				    num_rockchip_sub_drivers);
5268820b68bSJeffy Chen 	return ret;
5278820b68bSJeffy Chen }
5288820b68bSJeffy Chen 
rockchip_drm_fini(void)5298820b68bSJeffy Chen static void __exit rockchip_drm_fini(void)
5308820b68bSJeffy Chen {
5318820b68bSJeffy Chen 	platform_driver_unregister(&rockchip_drm_platform_driver);
5328820b68bSJeffy Chen 
5338820b68bSJeffy Chen 	platform_unregister_drivers(rockchip_sub_drivers,
5348820b68bSJeffy Chen 				    num_rockchip_sub_drivers);
5358820b68bSJeffy Chen }
5368820b68bSJeffy Chen 
5378820b68bSJeffy Chen module_init(rockchip_drm_init);
5388820b68bSJeffy Chen module_exit(rockchip_drm_fini);
5392048e328SMark Yao 
5402048e328SMark Yao MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
5412048e328SMark Yao MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
5422048e328SMark Yao MODULE_LICENSE("GPL v2");
543