exynos_drm_drv.c 17.6 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
 * Authors:
 *	Inki Dae <inki.dae@samsung.com>
 *	Joonyoung Shim <jy0922.shim@samsung.com>
 *	Seung-Woo Kim <sw0312.kim@samsung.com>
 *
8 9 10 11
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
12 13
 */

14
#include <linux/pm_runtime.h>
15
#include <drm/drmP.h>
16 17
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
18
#include <drm/drm_crtc_helper.h>
19

20
#include <linux/component.h>
21

22 23 24 25 26 27 28
#include <drm/exynos_drm.h>

#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
29
#include "exynos_drm_plane.h"
30
#include "exynos_drm_vidi.h"
J
Joonyoung Shim 已提交
31
#include "exynos_drm_g2d.h"
E
Eunchul Kim 已提交
32
#include "exynos_drm_ipp.h"
33
#include "exynos_drm_iommu.h"
34

I
Inki Dae 已提交
35
#define DRIVER_NAME	"exynos"
36 37 38 39 40
#define DRIVER_DESC	"Samsung SoC DRM"
#define DRIVER_DATE	"20110530"
#define DRIVER_MAJOR	1
#define DRIVER_MINOR	0

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
struct exynos_atomic_commit {
	struct work_struct	work;
	struct drm_device	*dev;
	struct drm_atomic_state *state;
	u32			crtcs;
};

static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
{
	struct drm_device *dev = commit->dev;
	struct exynos_drm_private *priv = dev->dev_private;
	struct drm_atomic_state *state = commit->state;

	drm_atomic_helper_commit_modeset_disables(dev, state);

	drm_atomic_helper_commit_modeset_enables(dev, state);

	/*
	 * Exynos can't update planes with CRTCs and encoders disabled,
	 * its updates routines, specially for FIMD, requires the clocks
	 * to be enabled. So it is necessary to handle the modeset operations
	 * *before* the commit_planes() step, this way it will always
	 * have the relevant clocks enabled to perform the update.
	 */

66
	drm_atomic_helper_commit_planes(dev, state, 0);
67

68
	drm_atomic_helper_wait_for_vblanks(dev, state);
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

	drm_atomic_helper_cleanup_planes(dev, state);

	drm_atomic_state_free(state);

	spin_lock(&priv->lock);
	priv->pending &= ~commit->crtcs;
	spin_unlock(&priv->lock);

	wake_up_all(&priv->wait);

	kfree(commit);
}

static void exynos_drm_atomic_work(struct work_struct *work)
{
	struct exynos_atomic_commit *commit = container_of(work,
				struct exynos_atomic_commit, work);

	exynos_atomic_commit_complete(commit);
}

91 92
static struct device *exynos_drm_get_dma_device(void);

93 94 95
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
	struct exynos_drm_private *private;
96 97 98
	struct drm_encoder *encoder;
	unsigned int clone_mask;
	int cnt, ret;
99 100

	private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
101
	if (!private)
102 103
		return -ENOMEM;

104 105 106
	init_waitqueue_head(&private->wait);
	spin_lock_init(&private->lock);

107
	dev_set_drvdata(dev->dev, dev);
108 109
	dev->dev_private = (void *)private;

110 111 112 113 114 115 116 117 118 119
	/* the first real CRTC device is used for all dma mapping operations */
	private->dma_dev = exynos_drm_get_dma_device();
	if (!private->dma_dev) {
		DRM_ERROR("no device found for DMA mapping operations.\n");
		ret = -ENODEV;
		goto err_free_private;
	}
	DRM_INFO("Exynos DRM: using %s device for DMA mapping operations\n",
		 dev_name(private->dma_dev));

120
	/* create common IOMMU mapping for all devices attached to Exynos DRM */
121 122 123
	ret = drm_create_iommu_mapping(dev);
	if (ret < 0) {
		DRM_ERROR("failed to create iommu mapping.\n");
124
		goto err_free_private;
125 126
	}

127 128 129 130
	drm_mode_config_init(dev);

	exynos_drm_mode_config_init(dev);

131
	/* setup possible_clones. */
132 133 134 135 136 137 138
	cnt = 0;
	clone_mask = 0;
	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
		clone_mask |= (1 << (cnt++));

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
		encoder->possible_clones = clone_mask;
139

140 141
	platform_set_drvdata(dev->platformdev, dev);

142 143 144
	/* Try to bind all sub drivers. */
	ret = component_bind_all(dev->dev, dev);
	if (ret)
145 146 147 148 149
		goto err_mode_config_cleanup;

	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
	if (ret)
		goto err_unbind_all;
150

151
	/* Probe non kms sub drivers and virtual display driver. */
152 153
	ret = exynos_drm_device_subdrv_probe(dev);
	if (ret)
154
		goto err_cleanup_vblank;
155

156 157
	drm_mode_config_reset(dev);

158 159 160 161 162 163 164 165 166 167
	/*
	 * enable drm irq mode.
	 * - with irq_enabled = true, we can use the vblank feature.
	 *
	 * P.S. note that we wouldn't use drm irq handler but
	 *	just specific driver own one instead because
	 *	drm framework supports only one irq handler.
	 */
	dev->irq_enabled = true;

168 169 170 171 172 173
	/* init kms poll for handling hpd */
	drm_kms_helper_poll_init(dev);

	/* force connectors detection */
	drm_helper_hpd_irq_event(dev);

174 175
	return 0;

176
err_cleanup_vblank:
177
	drm_vblank_cleanup(dev);
178 179
err_unbind_all:
	component_unbind_all(dev->dev, dev);
180 181
err_mode_config_cleanup:
	drm_mode_config_cleanup(dev);
182
	drm_release_iommu_mapping(dev);
183
err_free_private:
184 185 186 187 188 189 190
	kfree(private);

	return ret;
}

static int exynos_drm_unload(struct drm_device *dev)
{
191 192
	exynos_drm_device_subdrv_remove(dev);

193
	exynos_drm_fbdev_fini(dev);
194
	drm_kms_helper_poll_fini(dev);
195

196
	drm_vblank_cleanup(dev);
197
	component_unbind_all(dev->dev, dev);
198
	drm_mode_config_cleanup(dev);
199
	drm_release_iommu_mapping(dev);
200

201
	kfree(dev->dev_private);
202 203 204 205 206
	dev->dev_private = NULL;

	return 0;
}

207 208 209 210 211 212 213 214 215 216 217 218
static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs)
{
	bool pending;

	spin_lock(&priv->lock);
	pending = priv->pending & crtcs;
	spin_unlock(&priv->lock);

	return pending;
}

int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
219
			 bool nonblock)
220 221 222
{
	struct exynos_drm_private *priv = dev->dev_private;
	struct exynos_atomic_commit *commit;
223 224
	struct drm_crtc *crtc;
	struct drm_crtc_state *crtc_state;
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
	int i, ret;

	commit = kzalloc(sizeof(*commit), GFP_KERNEL);
	if (!commit)
		return -ENOMEM;

	ret = drm_atomic_helper_prepare_planes(dev, state);
	if (ret) {
		kfree(commit);
		return ret;
	}

	/* This is the point of no return */

	INIT_WORK(&commit->work, exynos_drm_atomic_work);
	commit->dev = dev;
	commit->state = state;

	/* Wait until all affected CRTCs have completed previous commits and
	 * mark them as pending.
	 */
246 247
	for_each_crtc_in_state(state, crtc, crtc_state, i)
		commit->crtcs |= drm_crtc_mask(crtc);
248 249 250 251 252 253 254

	wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));

	spin_lock(&priv->lock);
	priv->pending |= commit->crtcs;
	spin_unlock(&priv->lock);

255
	drm_atomic_helper_swap_state(state, true);
256

257
	if (nonblock)
258 259 260 261 262 263 264
		schedule_work(&commit->work);
	else
		exynos_atomic_commit_complete(commit);

	return 0;
}

265 266
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
J
Joonyoung Shim 已提交
267
	struct drm_exynos_file_private *file_priv;
268
	int ret;
J
Joonyoung Shim 已提交
269 270 271 272 273 274

	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
	if (!file_priv)
		return -ENOMEM;

	file->driver_priv = file_priv;
I
Inki Dae 已提交
275

276
	ret = exynos_drm_subdrv_open(dev, file);
277
	if (ret)
278
		goto err_file_priv_free;
279

280
	return ret;
281 282

err_file_priv_free:
283 284
	kfree(file_priv);
	file->driver_priv = NULL;
285
	return ret;
286 287
}

I
Inki Dae 已提交
288
static void exynos_drm_preclose(struct drm_device *dev,
289
					struct drm_file *file)
290
{
291 292
	struct drm_crtc *crtc;

293
	exynos_drm_subdrv_close(dev, file);
294 295 296

	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
		exynos_drm_crtc_cancel_page_flip(crtc, file);
297 298 299
}

static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
I
Inki Dae 已提交
300
{
301 302 303 304
	kfree(file->driver_priv);
	file->driver_priv = NULL;
}

305 306 307 308 309
static void exynos_drm_lastclose(struct drm_device *dev)
{
	exynos_drm_fbdev_restore_mode(dev);
}

310
static const struct vm_operations_struct exynos_drm_gem_vm_ops = {
311 312 313 314 315
	.fault = exynos_drm_gem_fault,
	.open = drm_gem_vm_open,
	.close = drm_gem_vm_close,
};

R
Rob Clark 已提交
316
static const struct drm_ioctl_desc exynos_ioctls[] = {
317
	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
318
			DRM_AUTH | DRM_RENDER_ALLOW),
319 320
	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP, exynos_drm_gem_map_ioctl,
			DRM_AUTH | DRM_RENDER_ALLOW),
321
	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl,
322
			DRM_RENDER_ALLOW),
323
	DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl,
324
			DRM_AUTH),
325
	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl,
326
			DRM_AUTH | DRM_RENDER_ALLOW),
327
	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl,
328
			DRM_AUTH | DRM_RENDER_ALLOW),
329
	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl,
330
			DRM_AUTH | DRM_RENDER_ALLOW),
331
	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, exynos_drm_ipp_get_property,
332
			DRM_AUTH | DRM_RENDER_ALLOW),
333
	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, exynos_drm_ipp_set_property,
334
			DRM_AUTH | DRM_RENDER_ALLOW),
335
	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, exynos_drm_ipp_queue_buf,
336
			DRM_AUTH | DRM_RENDER_ALLOW),
337
	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl,
338
			DRM_AUTH | DRM_RENDER_ALLOW),
339 340
};

J
Joonyoung Shim 已提交
341 342 343 344 345 346 347
static const struct file_operations exynos_drm_driver_fops = {
	.owner		= THIS_MODULE,
	.open		= drm_open,
	.mmap		= exynos_drm_gem_mmap,
	.poll		= drm_poll,
	.read		= drm_read,
	.unlocked_ioctl	= drm_ioctl,
348 349 350
#ifdef CONFIG_COMPAT
	.compat_ioctl = drm_compat_ioctl,
#endif
J
Joonyoung Shim 已提交
351 352 353
	.release	= drm_release,
};

354
static struct drm_driver exynos_drm_driver = {
355
	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
356
				  | DRIVER_ATOMIC | DRIVER_RENDER,
357 358
	.load			= exynos_drm_load,
	.unload			= exynos_drm_unload,
359
	.open			= exynos_drm_open,
I
Inki Dae 已提交
360
	.preclose		= exynos_drm_preclose,
361
	.lastclose		= exynos_drm_lastclose,
362
	.postclose		= exynos_drm_postclose,
363
	.get_vblank_counter	= drm_vblank_no_hw_counter,
364 365
	.enable_vblank		= exynos_drm_crtc_enable_vblank,
	.disable_vblank		= exynos_drm_crtc_disable_vblank,
366
	.gem_free_object_unlocked = exynos_drm_gem_free_object,
367 368 369
	.gem_vm_ops		= &exynos_drm_gem_vm_ops,
	.dumb_create		= exynos_drm_gem_dumb_create,
	.dumb_map_offset	= exynos_drm_gem_dumb_map_offset,
370
	.dumb_destroy		= drm_gem_dumb_destroy,
I
Inki Dae 已提交
371 372
	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
J
Joonyoung Shim 已提交
373 374 375 376 377 378
	.gem_prime_export	= drm_gem_prime_export,
	.gem_prime_import	= drm_gem_prime_import,
	.gem_prime_get_sg_table	= exynos_drm_gem_prime_get_sg_table,
	.gem_prime_import_sg_table	= exynos_drm_gem_prime_import_sg_table,
	.gem_prime_vmap		= exynos_drm_gem_prime_vmap,
	.gem_prime_vunmap	= exynos_drm_gem_prime_vunmap,
379
	.gem_prime_mmap		= exynos_drm_gem_prime_mmap,
380
	.ioctls			= exynos_ioctls,
R
Rob Clark 已提交
381
	.num_ioctls		= ARRAY_SIZE(exynos_ioctls),
J
Joonyoung Shim 已提交
382
	.fops			= &exynos_drm_driver_fops,
383 384 385 386 387 388 389
	.name	= DRIVER_NAME,
	.desc	= DRIVER_DESC,
	.date	= DRIVER_DATE,
	.major	= DRIVER_MAJOR,
	.minor	= DRIVER_MINOR,
};

390
#ifdef CONFIG_PM_SLEEP
A
Andrzej Hajda 已提交
391
static int exynos_drm_suspend(struct device *dev)
392 393
{
	struct drm_device *drm_dev = dev_get_drvdata(dev);
A
Andrzej Hajda 已提交
394
	struct drm_connector *connector;
395

396
	if (pm_runtime_suspended(dev) || !drm_dev)
397 398
		return 0;

A
Andrzej Hajda 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411
	drm_modeset_lock_all(drm_dev);
	drm_for_each_connector(connector, drm_dev) {
		int old_dpms = connector->dpms;

		if (connector->funcs->dpms)
			connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);

		/* Set the old mode back to the connector for resume */
		connector->dpms = old_dpms;
	}
	drm_modeset_unlock_all(drm_dev);

	return 0;
412 413
}

A
Andrzej Hajda 已提交
414
static int exynos_drm_resume(struct device *dev)
415 416
{
	struct drm_device *drm_dev = dev_get_drvdata(dev);
A
Andrzej Hajda 已提交
417
	struct drm_connector *connector;
418

419
	if (pm_runtime_suspended(dev) || !drm_dev)
420 421
		return 0;

A
Andrzej Hajda 已提交
422 423 424 425 426 427 428 429 430 431 432 433
	drm_modeset_lock_all(drm_dev);
	drm_for_each_connector(connector, drm_dev) {
		if (connector->funcs->dpms) {
			int dpms = connector->dpms;

			connector->dpms = DRM_MODE_DPMS_OFF;
			connector->funcs->dpms(connector, dpms);
		}
	}
	drm_modeset_unlock_all(drm_dev);

	return 0;
434 435 436 437
}
#endif

static const struct dev_pm_ops exynos_drm_pm_ops = {
A
Andrzej Hajda 已提交
438
	SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_suspend, exynos_drm_resume)
439 440
};

441 442
/* forward declaration */
static struct platform_driver exynos_drm_platform_driver;
443

444 445 446 447 448 449 450
struct exynos_drm_driver_info {
	struct platform_driver *driver;
	unsigned int flags;
};

#define DRM_COMPONENT_DRIVER	BIT(0)	/* supports component framework */
#define DRM_VIRTUAL_DEVICE	BIT(1)	/* create virtual platform device */
451
#define DRM_DMA_DEVICE		BIT(2)	/* can be used for dma allocations */
452 453 454

#define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL)

455 456 457 458
/*
 * Connector drivers should not be placed before associated crtc drivers,
 * because connector requires pipe number of its crtc during initialization.
 */
459 460 461
static struct exynos_drm_driver_info exynos_drm_drivers[] = {
	{
		DRV_PTR(fimd_driver, CONFIG_DRM_EXYNOS_FIMD),
462
		DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE
463 464
	}, {
		DRV_PTR(exynos5433_decon_driver, CONFIG_DRM_EXYNOS5433_DECON),
465
		DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE
466 467
	}, {
		DRV_PTR(decon_driver, CONFIG_DRM_EXYNOS7_DECON),
468
		DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE
469 470
	}, {
		DRV_PTR(mixer_driver, CONFIG_DRM_EXYNOS_MIXER),
471
		DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
	}, {
		DRV_PTR(mic_driver, CONFIG_DRM_EXYNOS_MIC),
		DRM_COMPONENT_DRIVER
	}, {
		DRV_PTR(dp_driver, CONFIG_DRM_EXYNOS_DP),
		DRM_COMPONENT_DRIVER
	}, {
		DRV_PTR(dsi_driver, CONFIG_DRM_EXYNOS_DSI),
		DRM_COMPONENT_DRIVER
	}, {
		DRV_PTR(hdmi_driver, CONFIG_DRM_EXYNOS_HDMI),
		DRM_COMPONENT_DRIVER
	}, {
		DRV_PTR(vidi_driver, CONFIG_DRM_EXYNOS_VIDI),
		DRM_COMPONENT_DRIVER | DRM_VIRTUAL_DEVICE
	}, {
		DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D),
	}, {
		DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC),
	}, {
		DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR),
	}, {
		DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC),
	}, {
		DRV_PTR(ipp_driver, CONFIG_DRM_EXYNOS_IPP),
		DRM_VIRTUAL_DEVICE
	}, {
		&exynos_drm_platform_driver,
		DRM_VIRTUAL_DEVICE
	}
502
};
503

504
static int compare_dev(struct device *dev, void *data)
505 506 507 508
{
	return dev == (struct device *)data;
}

509
static struct component_match *exynos_drm_match_add(struct device *dev)
510
{
511
	struct component_match *match = NULL;
512
	int i;
513

514 515
	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
516
		struct device *p = NULL, *d;
517

518 519 520 521 522
		if (!info->driver || !(info->flags & DRM_COMPONENT_DRIVER))
			continue;

		while ((d = bus_find_device(&platform_bus_type, p,
					    &info->driver->driver,
523 524 525 526
					    (void *)platform_bus_type.match))) {
			put_device(p);
			component_match_add(dev, &match, compare_dev, d);
			p = d;
527
		}
528
		put_device(p);
529 530
	}

531
	return match ?: ERR_PTR(-ENODEV);
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
}

static int exynos_drm_bind(struct device *dev)
{
	return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
}

static void exynos_drm_unbind(struct device *dev)
{
	drm_put_dev(dev_get_drvdata(dev));
}

static const struct component_master_ops exynos_drm_ops = {
	.bind		= exynos_drm_bind,
	.unbind		= exynos_drm_unbind,
547 548
};

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
static int exynos_drm_platform_probe(struct platform_device *pdev)
{
	struct component_match *match;

	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);

	match = exynos_drm_match_add(&pdev->dev);
	if (IS_ERR(match))
		return PTR_ERR(match);

	return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
					       match);
}

static int exynos_drm_platform_remove(struct platform_device *pdev)
{
	component_master_del(&pdev->dev, &exynos_drm_ops);
	return 0;
}

static struct platform_driver exynos_drm_platform_driver = {
	.probe	= exynos_drm_platform_probe,
	.remove	= exynos_drm_platform_remove,
	.driver	= {
		.name	= "exynos-drm",
		.pm	= &exynos_drm_pm_ops,
	},
};

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
static struct device *exynos_drm_get_dma_device(void)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
		struct device *dev;

		if (!info->driver || !(info->flags & DRM_DMA_DEVICE))
			continue;

		while ((dev = bus_find_device(&platform_bus_type, NULL,
					    &info->driver->driver,
					    (void *)platform_bus_type.match))) {
			put_device(dev);
			return dev;
		}
	}
	return NULL;
}

600
static void exynos_drm_unregister_devices(void)
601
{
602 603 604 605 606
	int i;

	for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) {
		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
		struct device *dev;
607

608 609 610 611 612 613 614 615 616
		if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE))
			continue;

		while ((dev = bus_find_device(&platform_bus_type, NULL,
					    &info->driver->driver,
					    (void *)platform_bus_type.match))) {
			put_device(dev);
			platform_device_unregister(to_platform_device(dev));
		}
617 618
	}
}
A
Andrzej Hajda 已提交
619

620 621
static int exynos_drm_register_devices(void)
{
622
	struct platform_device *pdev;
623 624
	int i;

625 626
	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
627

628
		if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE))
629 630
			continue;

631 632 633 634
		pdev = platform_device_register_simple(
					info->driver->driver.name, -1, NULL, 0);
		if (IS_ERR(pdev))
			goto fail;
635
	}
636

637
	return 0;
638 639 640
fail:
	exynos_drm_unregister_devices();
	return PTR_ERR(pdev);
641 642
}

643
static void exynos_drm_unregister_drivers(void)
644
{
645
	int i;
646

647 648
	for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) {
		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
649

650
		if (!info->driver)
651 652
			continue;

653
		platform_driver_unregister(info->driver);
654
	}
655 656
}

657
static int exynos_drm_register_drivers(void)
658
{
659
	int i, ret;
660

661 662
	for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) {
		struct exynos_drm_driver_info *info = &exynos_drm_drivers[i];
663

664 665
		if (!info->driver)
			continue;
666

667 668 669 670 671 672 673 674
		ret = platform_driver_register(info->driver);
		if (ret)
			goto fail;
	}
	return 0;
fail:
	exynos_drm_unregister_drivers();
	return ret;
675 676
}

677 678
static int exynos_drm_init(void)
{
679
	int ret;
680

681 682 683
	ret = exynos_drm_register_devices();
	if (ret)
		return ret;
684

685
	ret = exynos_drm_register_drivers();
686 687
	if (ret)
		goto err_unregister_pdevs;
688 689 690

	return 0;

691 692
err_unregister_pdevs:
	exynos_drm_unregister_devices();
693 694 695 696 697 698

	return ret;
}

static void exynos_drm_exit(void)
{
699
	exynos_drm_unregister_drivers();
700
	exynos_drm_unregister_devices();
701 702 703 704 705 706 707 708 709 710
}

module_init(exynos_drm_init);
module_exit(exynos_drm_exit);

MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Driver");
MODULE_LICENSE("GPL");