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