162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci * Authors:
562306a36Sopenharmony_ci *	Inki Dae <inki.dae@samsung.com>
662306a36Sopenharmony_ci *	Joonyoung Shim <jy0922.shim@samsung.com>
762306a36Sopenharmony_ci *	Seung-Woo Kim <sw0312.kim@samsung.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/component.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <drm/drm_atomic.h>
1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1862306a36Sopenharmony_ci#include <drm/drm_drv.h>
1962306a36Sopenharmony_ci#include <drm/drm_file.h>
2062306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2162306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
2262306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
2362306a36Sopenharmony_ci#include <drm/drm_vblank.h>
2462306a36Sopenharmony_ci#include <drm/exynos_drm.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "exynos_drm_drv.h"
2762306a36Sopenharmony_ci#include "exynos_drm_fb.h"
2862306a36Sopenharmony_ci#include "exynos_drm_fbdev.h"
2962306a36Sopenharmony_ci#include "exynos_drm_g2d.h"
3062306a36Sopenharmony_ci#include "exynos_drm_gem.h"
3162306a36Sopenharmony_ci#include "exynos_drm_ipp.h"
3262306a36Sopenharmony_ci#include "exynos_drm_plane.h"
3362306a36Sopenharmony_ci#include "exynos_drm_vidi.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRIVER_NAME	"exynos"
3662306a36Sopenharmony_ci#define DRIVER_DESC	"Samsung SoC DRM"
3762306a36Sopenharmony_ci#define DRIVER_DATE	"20180330"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * Interface history:
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * 1.0 - Original version
4362306a36Sopenharmony_ci * 1.1 - Upgrade IPP driver to version 2.0
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci#define DRIVER_MAJOR	1
4662306a36Sopenharmony_ci#define DRIVER_MINOR	1
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct drm_exynos_file_private *file_priv;
5162306a36Sopenharmony_ci	int ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
5462306a36Sopenharmony_ci	if (!file_priv)
5562306a36Sopenharmony_ci		return -ENOMEM;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	file->driver_priv = file_priv;
5862306a36Sopenharmony_ci	ret = g2d_open(dev, file);
5962306a36Sopenharmony_ci	if (ret)
6062306a36Sopenharmony_ci		goto err_file_priv_free;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return ret;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cierr_file_priv_free:
6562306a36Sopenharmony_ci	kfree(file_priv);
6662306a36Sopenharmony_ci	file->driver_priv = NULL;
6762306a36Sopenharmony_ci	return ret;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	g2d_close(dev, file);
7362306a36Sopenharmony_ci	kfree(file->driver_priv);
7462306a36Sopenharmony_ci	file->driver_priv = NULL;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct drm_ioctl_desc exynos_ioctls[] = {
7862306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
7962306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
8062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP, exynos_drm_gem_map_ioctl,
8162306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
8262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl,
8362306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
8462306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl,
8562306a36Sopenharmony_ci			DRM_AUTH),
8662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl,
8762306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
8862306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl,
8962306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
9062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl,
9162306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
9262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_RESOURCES,
9362306a36Sopenharmony_ci			exynos_drm_ipp_get_res_ioctl,
9462306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
9562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_CAPS, exynos_drm_ipp_get_caps_ioctl,
9662306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
9762306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_LIMITS,
9862306a36Sopenharmony_ci			exynos_drm_ipp_get_limits_ioctl,
9962306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
10062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_COMMIT, exynos_drm_ipp_commit_ioctl,
10162306a36Sopenharmony_ci			DRM_RENDER_ALLOW),
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(exynos_drm_driver_fops);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic const struct drm_driver exynos_drm_driver = {
10762306a36Sopenharmony_ci	.driver_features	= DRIVER_MODESET | DRIVER_GEM
10862306a36Sopenharmony_ci				  | DRIVER_ATOMIC | DRIVER_RENDER,
10962306a36Sopenharmony_ci	.open			= exynos_drm_open,
11062306a36Sopenharmony_ci	.postclose		= exynos_drm_postclose,
11162306a36Sopenharmony_ci	.dumb_create		= exynos_drm_gem_dumb_create,
11262306a36Sopenharmony_ci	.gem_prime_import	= exynos_drm_gem_prime_import,
11362306a36Sopenharmony_ci	.gem_prime_import_sg_table	= exynos_drm_gem_prime_import_sg_table,
11462306a36Sopenharmony_ci	.ioctls			= exynos_ioctls,
11562306a36Sopenharmony_ci	.num_ioctls		= ARRAY_SIZE(exynos_ioctls),
11662306a36Sopenharmony_ci	.fops			= &exynos_drm_driver_fops,
11762306a36Sopenharmony_ci	.name	= DRIVER_NAME,
11862306a36Sopenharmony_ci	.desc	= DRIVER_DESC,
11962306a36Sopenharmony_ci	.date	= DRIVER_DATE,
12062306a36Sopenharmony_ci	.major	= DRIVER_MAJOR,
12162306a36Sopenharmony_ci	.minor	= DRIVER_MINOR,
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int exynos_drm_suspend(struct device *dev)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct drm_device *drm_dev = dev_get_drvdata(dev);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return  drm_mode_config_helper_suspend(drm_dev);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void exynos_drm_resume(struct device *dev)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct drm_device *drm_dev = dev_get_drvdata(dev);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	drm_mode_config_helper_resume(drm_dev);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct dev_pm_ops exynos_drm_pm_ops = {
13962306a36Sopenharmony_ci	.prepare = exynos_drm_suspend,
14062306a36Sopenharmony_ci	.complete = exynos_drm_resume,
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* forward declaration */
14462306a36Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistruct exynos_drm_driver_info {
14762306a36Sopenharmony_ci	struct platform_driver *driver;
14862306a36Sopenharmony_ci	unsigned int flags;
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#define DRM_COMPONENT_DRIVER	BIT(0)	/* supports component framework */
15262306a36Sopenharmony_ci#define DRM_VIRTUAL_DEVICE	BIT(1)	/* create virtual platform device */
15362306a36Sopenharmony_ci#define DRM_FIMC_DEVICE		BIT(2)	/* devices shared with V4L2 subsystem */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL)
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/*
15862306a36Sopenharmony_ci * Connector drivers should not be placed before associated crtc drivers,
15962306a36Sopenharmony_ci * because connector requires pipe number of its crtc during initialization.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic struct exynos_drm_driver_info exynos_drm_drivers[] = {
16262306a36Sopenharmony_ci	{
16362306a36Sopenharmony_ci		DRV_PTR(fimd_driver, CONFIG_DRM_EXYNOS_FIMD),
16462306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
16562306a36Sopenharmony_ci	}, {
16662306a36Sopenharmony_ci		DRV_PTR(exynos5433_decon_driver, CONFIG_DRM_EXYNOS5433_DECON),
16762306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
16862306a36Sopenharmony_ci	}, {
16962306a36Sopenharmony_ci		DRV_PTR(decon_driver, CONFIG_DRM_EXYNOS7_DECON),
17062306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
17162306a36Sopenharmony_ci	}, {
17262306a36Sopenharmony_ci		DRV_PTR(mixer_driver, CONFIG_DRM_EXYNOS_MIXER),
17362306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
17462306a36Sopenharmony_ci	}, {
17562306a36Sopenharmony_ci		DRV_PTR(dp_driver, CONFIG_DRM_EXYNOS_DP),
17662306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
17762306a36Sopenharmony_ci	}, {
17862306a36Sopenharmony_ci		DRV_PTR(dsi_driver, CONFIG_DRM_EXYNOS_DSI),
17962306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
18062306a36Sopenharmony_ci	}, {
18162306a36Sopenharmony_ci		DRV_PTR(mic_driver, CONFIG_DRM_EXYNOS_MIC),
18262306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
18362306a36Sopenharmony_ci	}, {
18462306a36Sopenharmony_ci		DRV_PTR(hdmi_driver, CONFIG_DRM_EXYNOS_HDMI),
18562306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
18662306a36Sopenharmony_ci	}, {
18762306a36Sopenharmony_ci		DRV_PTR(vidi_driver, CONFIG_DRM_EXYNOS_VIDI),
18862306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER | DRM_VIRTUAL_DEVICE
18962306a36Sopenharmony_ci	}, {
19062306a36Sopenharmony_ci		DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D),
19162306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
19262306a36Sopenharmony_ci	}, {
19362306a36Sopenharmony_ci		DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC),
19462306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER | DRM_FIMC_DEVICE,
19562306a36Sopenharmony_ci	}, {
19662306a36Sopenharmony_ci		DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR),
19762306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
19862306a36Sopenharmony_ci	}, {
19962306a36Sopenharmony_ci		DRV_PTR(scaler_driver, CONFIG_DRM_EXYNOS_SCALER),
20062306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
20162306a36Sopenharmony_ci	}, {
20262306a36Sopenharmony_ci		DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC),
20362306a36Sopenharmony_ci		DRM_COMPONENT_DRIVER
20462306a36Sopenharmony_ci	}, {
20562306a36Sopenharmony_ci		&exynos_drm_platform_driver,
20662306a36Sopenharmony_ci		DRM_VIRTUAL_DEVICE
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic struct component_match *exynos_drm_match_add(struct device *dev)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct component_match *match = NULL;
21362306a36Sopenharmony_ci	int i;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
21662306a36Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
21762306a36Sopenharmony_ci		struct device *p = NULL, *d;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (!info->driver || !(info->flags & DRM_COMPONENT_DRIVER))
22062306a36Sopenharmony_ci			continue;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		while ((d = platform_find_device_by_driver(p, &info->driver->driver))) {
22362306a36Sopenharmony_ci			put_device(p);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci			if (!(info->flags & DRM_FIMC_DEVICE) ||
22662306a36Sopenharmony_ci			    exynos_drm_check_fimc_device(d) == 0)
22762306a36Sopenharmony_ci				component_match_add(dev, &match, component_compare_dev, d);
22862306a36Sopenharmony_ci			p = d;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci		put_device(p);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return match ?: ERR_PTR(-ENODEV);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int exynos_drm_bind(struct device *dev)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct exynos_drm_private *private;
23962306a36Sopenharmony_ci	struct drm_encoder *encoder;
24062306a36Sopenharmony_ci	struct drm_device *drm;
24162306a36Sopenharmony_ci	unsigned int clone_mask;
24262306a36Sopenharmony_ci	int ret;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	drm = drm_dev_alloc(&exynos_drm_driver, dev);
24562306a36Sopenharmony_ci	if (IS_ERR(drm))
24662306a36Sopenharmony_ci		return PTR_ERR(drm);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
24962306a36Sopenharmony_ci	if (!private) {
25062306a36Sopenharmony_ci		ret = -ENOMEM;
25162306a36Sopenharmony_ci		goto err_free_drm;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	init_waitqueue_head(&private->wait);
25562306a36Sopenharmony_ci	spin_lock_init(&private->lock);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	dev_set_drvdata(dev, drm);
25862306a36Sopenharmony_ci	drm->dev_private = (void *)private;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	drm_mode_config_init(drm);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	exynos_drm_mode_config_init(drm);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* setup possible_clones. */
26562306a36Sopenharmony_ci	clone_mask = 0;
26662306a36Sopenharmony_ci	list_for_each_entry(encoder, &drm->mode_config.encoder_list, head)
26762306a36Sopenharmony_ci		clone_mask |= drm_encoder_mask(encoder);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	list_for_each_entry(encoder, &drm->mode_config.encoder_list, head)
27062306a36Sopenharmony_ci		encoder->possible_clones = clone_mask;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Try to bind all sub drivers. */
27362306a36Sopenharmony_ci	ret = component_bind_all(drm->dev, drm);
27462306a36Sopenharmony_ci	if (ret)
27562306a36Sopenharmony_ci		goto err_mode_config_cleanup;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
27862306a36Sopenharmony_ci	if (ret)
27962306a36Sopenharmony_ci		goto err_unbind_all;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	drm_mode_config_reset(drm);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* init kms poll for handling hpd */
28462306a36Sopenharmony_ci	drm_kms_helper_poll_init(drm);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* register the DRM device */
28762306a36Sopenharmony_ci	ret = drm_dev_register(drm, 0);
28862306a36Sopenharmony_ci	if (ret < 0)
28962306a36Sopenharmony_ci		goto err_cleanup_poll;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	exynos_drm_fbdev_setup(drm);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cierr_cleanup_poll:
29662306a36Sopenharmony_ci	drm_kms_helper_poll_fini(drm);
29762306a36Sopenharmony_cierr_unbind_all:
29862306a36Sopenharmony_ci	component_unbind_all(drm->dev, drm);
29962306a36Sopenharmony_cierr_mode_config_cleanup:
30062306a36Sopenharmony_ci	drm_mode_config_cleanup(drm);
30162306a36Sopenharmony_ci	exynos_drm_cleanup_dma(drm);
30262306a36Sopenharmony_ci	kfree(private);
30362306a36Sopenharmony_ci	dev_set_drvdata(dev, NULL);
30462306a36Sopenharmony_cierr_free_drm:
30562306a36Sopenharmony_ci	drm_dev_put(drm);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void exynos_drm_unbind(struct device *dev)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(dev);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	drm_dev_unregister(drm);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	drm_kms_helper_poll_fini(drm);
31762306a36Sopenharmony_ci	drm_atomic_helper_shutdown(drm);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	component_unbind_all(drm->dev, drm);
32062306a36Sopenharmony_ci	drm_mode_config_cleanup(drm);
32162306a36Sopenharmony_ci	exynos_drm_cleanup_dma(drm);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	kfree(drm->dev_private);
32462306a36Sopenharmony_ci	drm->dev_private = NULL;
32562306a36Sopenharmony_ci	dev_set_drvdata(dev, NULL);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	drm_dev_put(drm);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic const struct component_master_ops exynos_drm_ops = {
33162306a36Sopenharmony_ci	.bind		= exynos_drm_bind,
33262306a36Sopenharmony_ci	.unbind		= exynos_drm_unbind,
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int exynos_drm_platform_probe(struct platform_device *pdev)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct component_match *match;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	match = exynos_drm_match_add(&pdev->dev);
34262306a36Sopenharmony_ci	if (IS_ERR(match))
34362306a36Sopenharmony_ci		return PTR_ERR(match);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
34662306a36Sopenharmony_ci					       match);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int exynos_drm_platform_remove(struct platform_device *pdev)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	component_master_del(&pdev->dev, &exynos_drm_ops);
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void exynos_drm_platform_shutdown(struct platform_device *pdev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct drm_device *drm = platform_get_drvdata(pdev);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (drm)
36062306a36Sopenharmony_ci		drm_atomic_helper_shutdown(drm);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver = {
36462306a36Sopenharmony_ci	.probe	= exynos_drm_platform_probe,
36562306a36Sopenharmony_ci	.remove	= exynos_drm_platform_remove,
36662306a36Sopenharmony_ci	.shutdown = exynos_drm_platform_shutdown,
36762306a36Sopenharmony_ci	.driver	= {
36862306a36Sopenharmony_ci		.name	= "exynos-drm",
36962306a36Sopenharmony_ci		.pm	= &exynos_drm_pm_ops,
37062306a36Sopenharmony_ci	},
37162306a36Sopenharmony_ci};
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void exynos_drm_unregister_devices(void)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	int i;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) {
37862306a36Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
37962306a36Sopenharmony_ci		struct device *dev;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE))
38262306a36Sopenharmony_ci			continue;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		while ((dev = platform_find_device_by_driver(NULL,
38562306a36Sopenharmony_ci						&info->driver->driver))) {
38662306a36Sopenharmony_ci			put_device(dev);
38762306a36Sopenharmony_ci			platform_device_unregister(to_platform_device(dev));
38862306a36Sopenharmony_ci		}
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int exynos_drm_register_devices(void)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct platform_device *pdev;
39562306a36Sopenharmony_ci	int i;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
39862306a36Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE))
40162306a36Sopenharmony_ci			continue;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		pdev = platform_device_register_simple(
40462306a36Sopenharmony_ci					info->driver->driver.name, -1, NULL, 0);
40562306a36Sopenharmony_ci		if (IS_ERR(pdev))
40662306a36Sopenharmony_ci			goto fail;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return 0;
41062306a36Sopenharmony_cifail:
41162306a36Sopenharmony_ci	exynos_drm_unregister_devices();
41262306a36Sopenharmony_ci	return PTR_ERR(pdev);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void exynos_drm_unregister_drivers(void)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	int i;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) {
42062306a36Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		if (!info->driver)
42362306a36Sopenharmony_ci			continue;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		platform_driver_unregister(info->driver);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int exynos_drm_register_drivers(void)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	int i, ret;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
43462306a36Sopenharmony_ci		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		if (!info->driver)
43762306a36Sopenharmony_ci			continue;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		ret = platform_driver_register(info->driver);
44062306a36Sopenharmony_ci		if (ret)
44162306a36Sopenharmony_ci			goto fail;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_cifail:
44562306a36Sopenharmony_ci	exynos_drm_unregister_drivers();
44662306a36Sopenharmony_ci	return ret;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic int exynos_drm_init(void)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int ret;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (drm_firmware_drivers_only())
45462306a36Sopenharmony_ci		return -ENODEV;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	ret = exynos_drm_register_devices();
45762306a36Sopenharmony_ci	if (ret)
45862306a36Sopenharmony_ci		return ret;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ret = exynos_drm_register_drivers();
46162306a36Sopenharmony_ci	if (ret)
46262306a36Sopenharmony_ci		goto err_unregister_pdevs;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cierr_unregister_pdevs:
46762306a36Sopenharmony_ci	exynos_drm_unregister_devices();
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return ret;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void exynos_drm_exit(void)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	exynos_drm_unregister_drivers();
47562306a36Sopenharmony_ci	exynos_drm_unregister_devices();
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cimodule_init(exynos_drm_init);
47962306a36Sopenharmony_cimodule_exit(exynos_drm_exit);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciMODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
48262306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
48362306a36Sopenharmony_ciMODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
48462306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung SoC DRM Driver");
48562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
486