xref: /linux/drivers/gpu/drm/rockchip/rockchip_drm_drv.c (revision 634bd2985716bcb0b5b8b22a0a117bce283d1b08)
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