18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci * Authors:
58c2ecf20Sopenharmony_ci *	Inki Dae <inki.dae@samsung.com>
68c2ecf20Sopenharmony_ci *	Joonyoung Shim <jy0922.shim@samsung.com>
78c2ecf20Sopenharmony_ci *	Seung-Woo Kim <sw0312.kim@samsung.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/component.h>
118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_drv.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
218c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_ioctl.h>
238c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
248c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
258c2ecf20Sopenharmony_ci#include <drm/exynos_drm.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h"
288c2ecf20Sopenharmony_ci#include "exynos_drm_fb.h"
298c2ecf20Sopenharmony_ci#include "exynos_drm_fbdev.h"
308c2ecf20Sopenharmony_ci#include "exynos_drm_g2d.h"
318c2ecf20Sopenharmony_ci#include "exynos_drm_gem.h"
328c2ecf20Sopenharmony_ci#include "exynos_drm_ipp.h"
338c2ecf20Sopenharmony_ci#include "exynos_drm_plane.h"
348c2ecf20Sopenharmony_ci#include "exynos_drm_vidi.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define DRIVER_NAME	"exynos"
378c2ecf20Sopenharmony_ci#define DRIVER_DESC	"Samsung SoC DRM"
388c2ecf20Sopenharmony_ci#define DRIVER_DATE	"20180330"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Interface history:
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * 1.0 - Original version
448c2ecf20Sopenharmony_ci * 1.1 - Upgrade IPP driver to version 2.0
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci#define DRIVER_MAJOR	1
478c2ecf20Sopenharmony_ci#define DRIVER_MINOR	1
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct drm_exynos_file_private *file_priv;
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
558c2ecf20Sopenharmony_ci	if (!file_priv)
568c2ecf20Sopenharmony_ci		return -ENOMEM;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	file->driver_priv = file_priv;
598c2ecf20Sopenharmony_ci	ret = g2d_open(dev, file);
608c2ecf20Sopenharmony_ci	if (ret)
618c2ecf20Sopenharmony_ci		goto err_file_priv_free;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cierr_file_priv_free:
668c2ecf20Sopenharmony_ci	kfree(file_priv);
678c2ecf20Sopenharmony_ci	file->driver_priv = NULL;
688c2ecf20Sopenharmony_ci	return ret;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	g2d_close(dev, file);
748c2ecf20Sopenharmony_ci	kfree(file->driver_priv);
758c2ecf20Sopenharmony_ci	file->driver_priv = NULL;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic const struct vm_operations_struct exynos_drm_gem_vm_ops = {
798c2ecf20Sopenharmony_ci	.open = drm_gem_vm_open,
808c2ecf20Sopenharmony_ci	.close = drm_gem_vm_close,
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct drm_ioctl_desc exynos_ioctls[] = {
848c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
858c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
868c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP, exynos_drm_gem_map_ioctl,
878c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
888c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl,
898c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
908c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl,
918c2ecf20Sopenharmony_ci			DRM_AUTH),
928c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl,
938c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
948c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl,
958c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
968c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl,
978c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
988c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_RESOURCES,
998c2ecf20Sopenharmony_ci			exynos_drm_ipp_get_res_ioctl,
1008c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
1018c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_CAPS, exynos_drm_ipp_get_caps_ioctl,
1028c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
1038c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_LIMITS,
1048c2ecf20Sopenharmony_ci			exynos_drm_ipp_get_limits_ioctl,
1058c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
1068c2ecf20Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_COMMIT, exynos_drm_ipp_commit_ioctl,
1078c2ecf20Sopenharmony_ci			DRM_RENDER_ALLOW),
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic const struct file_operations exynos_drm_driver_fops = {
1118c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1128c2ecf20Sopenharmony_ci	.open		= drm_open,
1138c2ecf20Sopenharmony_ci	.mmap		= exynos_drm_gem_mmap,
1148c2ecf20Sopenharmony_ci	.poll		= drm_poll,
1158c2ecf20Sopenharmony_ci	.read		= drm_read,
1168c2ecf20Sopenharmony_ci	.unlocked_ioctl	= drm_ioctl,
1178c2ecf20Sopenharmony_ci	.compat_ioctl = drm_compat_ioctl,
1188c2ecf20Sopenharmony_ci	.release	= drm_release,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic struct drm_driver exynos_drm_driver = {
1228c2ecf20Sopenharmony_ci	.driver_features	= DRIVER_MODESET | DRIVER_GEM
1238c2ecf20Sopenharmony_ci				  | DRIVER_ATOMIC | DRIVER_RENDER,
1248c2ecf20Sopenharmony_ci	.open			= exynos_drm_open,
1258c2ecf20Sopenharmony_ci	.lastclose		= drm_fb_helper_lastclose,
1268c2ecf20Sopenharmony_ci	.postclose		= exynos_drm_postclose,
1278c2ecf20Sopenharmony_ci	.gem_free_object_unlocked = exynos_drm_gem_free_object,
1288c2ecf20Sopenharmony_ci	.gem_vm_ops		= &exynos_drm_gem_vm_ops,
1298c2ecf20Sopenharmony_ci	.dumb_create		= exynos_drm_gem_dumb_create,
1308c2ecf20Sopenharmony_ci	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
1318c2ecf20Sopenharmony_ci	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
1328c2ecf20Sopenharmony_ci	.gem_prime_import	= exynos_drm_gem_prime_import,
1338c2ecf20Sopenharmony_ci	.gem_prime_get_sg_table	= exynos_drm_gem_prime_get_sg_table,
1348c2ecf20Sopenharmony_ci	.gem_prime_import_sg_table	= exynos_drm_gem_prime_import_sg_table,
1358c2ecf20Sopenharmony_ci	.gem_prime_vmap		= exynos_drm_gem_prime_vmap,
1368c2ecf20Sopenharmony_ci	.gem_prime_vunmap	= exynos_drm_gem_prime_vunmap,
1378c2ecf20Sopenharmony_ci	.gem_prime_mmap		= exynos_drm_gem_prime_mmap,
1388c2ecf20Sopenharmony_ci	.ioctls			= exynos_ioctls,
1398c2ecf20Sopenharmony_ci	.num_ioctls		= ARRAY_SIZE(exynos_ioctls),
1408c2ecf20Sopenharmony_ci	.fops			= &exynos_drm_driver_fops,
1418c2ecf20Sopenharmony_ci	.name	= DRIVER_NAME,
1428c2ecf20Sopenharmony_ci	.desc	= DRIVER_DESC,
1438c2ecf20Sopenharmony_ci	.date	= DRIVER_DATE,
1448c2ecf20Sopenharmony_ci	.major	= DRIVER_MAJOR,
1458c2ecf20Sopenharmony_ci	.minor	= DRIVER_MINOR,
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int exynos_drm_suspend(struct device *dev)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct drm_device *drm_dev = dev_get_drvdata(dev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return  drm_mode_config_helper_suspend(drm_dev);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void exynos_drm_resume(struct device *dev)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct drm_device *drm_dev = dev_get_drvdata(dev);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	drm_mode_config_helper_resume(drm_dev);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos_drm_pm_ops = {
1638c2ecf20Sopenharmony_ci	.prepare = exynos_drm_suspend,
1648c2ecf20Sopenharmony_ci	.complete = exynos_drm_resume,
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* forward declaration */
1688c2ecf20Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistruct exynos_drm_driver_info {
1718c2ecf20Sopenharmony_ci	struct platform_driver *driver;
1728c2ecf20Sopenharmony_ci	unsigned int flags;
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci#define DRM_COMPONENT_DRIVER	BIT(0)	/* supports component framework */
1768c2ecf20Sopenharmony_ci#define DRM_VIRTUAL_DEVICE	BIT(1)	/* create virtual platform device */
1778c2ecf20Sopenharmony_ci#define DRM_FIMC_DEVICE		BIT(2)	/* devices shared with V4L2 subsystem */
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci#define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL)
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/*
1828c2ecf20Sopenharmony_ci * Connector drivers should not be placed before associated crtc drivers,
1838c2ecf20Sopenharmony_ci * because connector requires pipe number of its crtc during initialization.
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistatic struct exynos_drm_driver_info exynos_drm_drivers[] = {
1868c2ecf20Sopenharmony_ci	{
1878c2ecf20Sopenharmony_ci		DRV_PTR(fimd_driver, CONFIG_DRM_EXYNOS_FIMD),
1888c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
1898c2ecf20Sopenharmony_ci	}, {
1908c2ecf20Sopenharmony_ci		DRV_PTR(exynos5433_decon_driver, CONFIG_DRM_EXYNOS5433_DECON),
1918c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
1928c2ecf20Sopenharmony_ci	}, {
1938c2ecf20Sopenharmony_ci		DRV_PTR(decon_driver, CONFIG_DRM_EXYNOS7_DECON),
1948c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
1958c2ecf20Sopenharmony_ci	}, {
1968c2ecf20Sopenharmony_ci		DRV_PTR(mixer_driver, CONFIG_DRM_EXYNOS_MIXER),
1978c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
1988c2ecf20Sopenharmony_ci	}, {
1998c2ecf20Sopenharmony_ci		DRV_PTR(mic_driver, CONFIG_DRM_EXYNOS_MIC),
2008c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2018c2ecf20Sopenharmony_ci	}, {
2028c2ecf20Sopenharmony_ci		DRV_PTR(dp_driver, CONFIG_DRM_EXYNOS_DP),
2038c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2048c2ecf20Sopenharmony_ci	}, {
2058c2ecf20Sopenharmony_ci		DRV_PTR(dsi_driver, CONFIG_DRM_EXYNOS_DSI),
2068c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2078c2ecf20Sopenharmony_ci	}, {
2088c2ecf20Sopenharmony_ci		DRV_PTR(hdmi_driver, CONFIG_DRM_EXYNOS_HDMI),
2098c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2108c2ecf20Sopenharmony_ci	}, {
2118c2ecf20Sopenharmony_ci		DRV_PTR(vidi_driver, CONFIG_DRM_EXYNOS_VIDI),
2128c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER | DRM_VIRTUAL_DEVICE
2138c2ecf20Sopenharmony_ci	}, {
2148c2ecf20Sopenharmony_ci		DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D),
2158c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2168c2ecf20Sopenharmony_ci	}, {
2178c2ecf20Sopenharmony_ci		DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC),
2188c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER | DRM_FIMC_DEVICE,
2198c2ecf20Sopenharmony_ci	}, {
2208c2ecf20Sopenharmony_ci		DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR),
2218c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2228c2ecf20Sopenharmony_ci	}, {
2238c2ecf20Sopenharmony_ci		DRV_PTR(scaler_driver, CONFIG_DRM_EXYNOS_SCALER),
2248c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2258c2ecf20Sopenharmony_ci	}, {
2268c2ecf20Sopenharmony_ci		DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC),
2278c2ecf20Sopenharmony_ci		DRM_COMPONENT_DRIVER
2288c2ecf20Sopenharmony_ci	}, {
2298c2ecf20Sopenharmony_ci		&exynos_drm_platform_driver,
2308c2ecf20Sopenharmony_ci		DRM_VIRTUAL_DEVICE
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci};
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int compare_dev(struct device *dev, void *data)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	return dev == (struct device *)data;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic struct component_match *exynos_drm_match_add(struct device *dev)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct component_match *match = NULL;
2428c2ecf20Sopenharmony_ci	int i;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
2458c2ecf20Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
2468c2ecf20Sopenharmony_ci		struct device *p = NULL, *d;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		if (!info->driver || !(info->flags & DRM_COMPONENT_DRIVER))
2498c2ecf20Sopenharmony_ci			continue;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		while ((d = platform_find_device_by_driver(p, &info->driver->driver))) {
2528c2ecf20Sopenharmony_ci			put_device(p);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci			if (!(info->flags & DRM_FIMC_DEVICE) ||
2558c2ecf20Sopenharmony_ci			    exynos_drm_check_fimc_device(d) == 0)
2568c2ecf20Sopenharmony_ci				component_match_add(dev, &match,
2578c2ecf20Sopenharmony_ci						    compare_dev, d);
2588c2ecf20Sopenharmony_ci			p = d;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci		put_device(p);
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return match ?: ERR_PTR(-ENODEV);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int exynos_drm_bind(struct device *dev)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct exynos_drm_private *private;
2698c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
2708c2ecf20Sopenharmony_ci	struct drm_device *drm;
2718c2ecf20Sopenharmony_ci	unsigned int clone_mask;
2728c2ecf20Sopenharmony_ci	int ret;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	drm = drm_dev_alloc(&exynos_drm_driver, dev);
2758c2ecf20Sopenharmony_ci	if (IS_ERR(drm))
2768c2ecf20Sopenharmony_ci		return PTR_ERR(drm);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
2798c2ecf20Sopenharmony_ci	if (!private) {
2808c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2818c2ecf20Sopenharmony_ci		goto err_free_drm;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	init_waitqueue_head(&private->wait);
2858c2ecf20Sopenharmony_ci	spin_lock_init(&private->lock);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, drm);
2888c2ecf20Sopenharmony_ci	drm->dev_private = (void *)private;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	drm_mode_config_init(drm);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	exynos_drm_mode_config_init(drm);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* setup possible_clones. */
2958c2ecf20Sopenharmony_ci	clone_mask = 0;
2968c2ecf20Sopenharmony_ci	list_for_each_entry(encoder, &drm->mode_config.encoder_list, head)
2978c2ecf20Sopenharmony_ci		clone_mask |= drm_encoder_mask(encoder);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	list_for_each_entry(encoder, &drm->mode_config.encoder_list, head)
3008c2ecf20Sopenharmony_ci		encoder->possible_clones = clone_mask;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Try to bind all sub drivers. */
3038c2ecf20Sopenharmony_ci	ret = component_bind_all(drm->dev, drm);
3048c2ecf20Sopenharmony_ci	if (ret)
3058c2ecf20Sopenharmony_ci		goto err_mode_config_cleanup;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
3088c2ecf20Sopenharmony_ci	if (ret)
3098c2ecf20Sopenharmony_ci		goto err_unbind_all;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	drm_mode_config_reset(drm);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/*
3148c2ecf20Sopenharmony_ci	 * enable drm irq mode.
3158c2ecf20Sopenharmony_ci	 * - with irq_enabled = true, we can use the vblank feature.
3168c2ecf20Sopenharmony_ci	 *
3178c2ecf20Sopenharmony_ci	 * P.S. note that we wouldn't use drm irq handler but
3188c2ecf20Sopenharmony_ci	 *	just specific driver own one instead because
3198c2ecf20Sopenharmony_ci	 *	drm framework supports only one irq handler.
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci	drm->irq_enabled = true;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* init kms poll for handling hpd */
3248c2ecf20Sopenharmony_ci	drm_kms_helper_poll_init(drm);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ret = exynos_drm_fbdev_init(drm);
3278c2ecf20Sopenharmony_ci	if (ret)
3288c2ecf20Sopenharmony_ci		goto err_cleanup_poll;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* register the DRM device */
3318c2ecf20Sopenharmony_ci	ret = drm_dev_register(drm, 0);
3328c2ecf20Sopenharmony_ci	if (ret < 0)
3338c2ecf20Sopenharmony_ci		goto err_cleanup_fbdev;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return 0;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cierr_cleanup_fbdev:
3388c2ecf20Sopenharmony_ci	exynos_drm_fbdev_fini(drm);
3398c2ecf20Sopenharmony_cierr_cleanup_poll:
3408c2ecf20Sopenharmony_ci	drm_kms_helper_poll_fini(drm);
3418c2ecf20Sopenharmony_cierr_unbind_all:
3428c2ecf20Sopenharmony_ci	component_unbind_all(drm->dev, drm);
3438c2ecf20Sopenharmony_cierr_mode_config_cleanup:
3448c2ecf20Sopenharmony_ci	drm_mode_config_cleanup(drm);
3458c2ecf20Sopenharmony_ci	exynos_drm_cleanup_dma(drm);
3468c2ecf20Sopenharmony_ci	kfree(private);
3478c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, NULL);
3488c2ecf20Sopenharmony_cierr_free_drm:
3498c2ecf20Sopenharmony_ci	drm_dev_put(drm);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return ret;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic void exynos_drm_unbind(struct device *dev)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(dev);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	drm_dev_unregister(drm);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	exynos_drm_fbdev_fini(drm);
3618c2ecf20Sopenharmony_ci	drm_kms_helper_poll_fini(drm);
3628c2ecf20Sopenharmony_ci	drm_atomic_helper_shutdown(drm);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	component_unbind_all(drm->dev, drm);
3658c2ecf20Sopenharmony_ci	drm_mode_config_cleanup(drm);
3668c2ecf20Sopenharmony_ci	exynos_drm_cleanup_dma(drm);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	kfree(drm->dev_private);
3698c2ecf20Sopenharmony_ci	drm->dev_private = NULL;
3708c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, NULL);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	drm_dev_put(drm);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic const struct component_master_ops exynos_drm_ops = {
3768c2ecf20Sopenharmony_ci	.bind		= exynos_drm_bind,
3778c2ecf20Sopenharmony_ci	.unbind		= exynos_drm_unbind,
3788c2ecf20Sopenharmony_ci};
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int exynos_drm_platform_probe(struct platform_device *pdev)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct component_match *match;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	match = exynos_drm_match_add(&pdev->dev);
3878c2ecf20Sopenharmony_ci	if (IS_ERR(match))
3888c2ecf20Sopenharmony_ci		return PTR_ERR(match);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
3918c2ecf20Sopenharmony_ci					       match);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int exynos_drm_platform_remove(struct platform_device *pdev)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	component_master_del(&pdev->dev, &exynos_drm_ops);
3978c2ecf20Sopenharmony_ci	return 0;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic void exynos_drm_platform_shutdown(struct platform_device *pdev)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct drm_device *drm = platform_get_drvdata(pdev);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (drm)
4058c2ecf20Sopenharmony_ci		drm_atomic_helper_shutdown(drm);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver = {
4098c2ecf20Sopenharmony_ci	.probe	= exynos_drm_platform_probe,
4108c2ecf20Sopenharmony_ci	.remove	= exynos_drm_platform_remove,
4118c2ecf20Sopenharmony_ci	.shutdown = exynos_drm_platform_shutdown,
4128c2ecf20Sopenharmony_ci	.driver	= {
4138c2ecf20Sopenharmony_ci		.name	= "exynos-drm",
4148c2ecf20Sopenharmony_ci		.pm	= &exynos_drm_pm_ops,
4158c2ecf20Sopenharmony_ci	},
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic void exynos_drm_unregister_devices(void)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	int i;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) {
4238c2ecf20Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
4248c2ecf20Sopenharmony_ci		struct device *dev;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE))
4278c2ecf20Sopenharmony_ci			continue;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		while ((dev = platform_find_device_by_driver(NULL,
4308c2ecf20Sopenharmony_ci						&info->driver->driver))) {
4318c2ecf20Sopenharmony_ci			put_device(dev);
4328c2ecf20Sopenharmony_ci			platform_device_unregister(to_platform_device(dev));
4338c2ecf20Sopenharmony_ci		}
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int exynos_drm_register_devices(void)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct platform_device *pdev;
4408c2ecf20Sopenharmony_ci	int i;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
4438c2ecf20Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE))
4468c2ecf20Sopenharmony_ci			continue;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		pdev = platform_device_register_simple(
4498c2ecf20Sopenharmony_ci					info->driver->driver.name, -1, NULL, 0);
4508c2ecf20Sopenharmony_ci		if (IS_ERR(pdev))
4518c2ecf20Sopenharmony_ci			goto fail;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_cifail:
4568c2ecf20Sopenharmony_ci	exynos_drm_unregister_devices();
4578c2ecf20Sopenharmony_ci	return PTR_ERR(pdev);
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic void exynos_drm_unregister_drivers(void)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	int i;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) {
4658c2ecf20Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		if (!info->driver)
4688c2ecf20Sopenharmony_ci			continue;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		platform_driver_unregister(info->driver);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int exynos_drm_register_drivers(void)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	int i, ret;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
4798c2ecf20Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		if (!info->driver)
4828c2ecf20Sopenharmony_ci			continue;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		ret = platform_driver_register(info->driver);
4858c2ecf20Sopenharmony_ci		if (ret)
4868c2ecf20Sopenharmony_ci			goto fail;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_cifail:
4908c2ecf20Sopenharmony_ci	exynos_drm_unregister_drivers();
4918c2ecf20Sopenharmony_ci	return ret;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int exynos_drm_init(void)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	int ret;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ret = exynos_drm_register_devices();
4998c2ecf20Sopenharmony_ci	if (ret)
5008c2ecf20Sopenharmony_ci		return ret;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	ret = exynos_drm_register_drivers();
5038c2ecf20Sopenharmony_ci	if (ret)
5048c2ecf20Sopenharmony_ci		goto err_unregister_pdevs;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return 0;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cierr_unregister_pdevs:
5098c2ecf20Sopenharmony_ci	exynos_drm_unregister_devices();
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return ret;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void exynos_drm_exit(void)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	exynos_drm_unregister_drivers();
5178c2ecf20Sopenharmony_ci	exynos_drm_unregister_devices();
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cimodule_init(exynos_drm_init);
5218c2ecf20Sopenharmony_cimodule_exit(exynos_drm_exit);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
5248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
5258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
5268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung SoC DRM Driver");
5278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
528