tilcdc_drv.c 17.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (C) 2012 Texas Instruments
 * Author: Rob Clark <robdclark@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* LCDC DRM driver, based on da8xx-fb */

20
#include <linux/component.h>
D
Dave Gerlach 已提交
21 22
#include <linux/pinctrl/consumer.h>
#include <linux/suspend.h>
23 24
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
25
#include <drm/drm_fb_helper.h>
26

27 28 29
#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
#include "tilcdc_tfp410.h"
30
#include "tilcdc_panel.h"
31
#include "tilcdc_external.h"
32 33 34

static LIST_HEAD(module_list);

35 36 37 38 39 40 41 42 43 44 45 46 47 48
static const u32 tilcdc_rev1_formats[] = { DRM_FORMAT_RGB565 };

static const u32 tilcdc_straight_formats[] = { DRM_FORMAT_RGB565,
					       DRM_FORMAT_BGR888,
					       DRM_FORMAT_XBGR8888 };

static const u32 tilcdc_crossed_formats[] = { DRM_FORMAT_BGR565,
					      DRM_FORMAT_RGB888,
					      DRM_FORMAT_XRGB8888 };

static const u32 tilcdc_legacy_formats[] = { DRM_FORMAT_RGB565,
					     DRM_FORMAT_RGB888,
					     DRM_FORMAT_XRGB8888 };

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
		const struct tilcdc_module_ops *funcs)
{
	mod->name = name;
	mod->funcs = funcs;
	INIT_LIST_HEAD(&mod->list);
	list_add(&mod->list, &module_list);
}

void tilcdc_module_cleanup(struct tilcdc_module *mod)
{
	list_del(&mod->list);
}

static struct of_device_id tilcdc_of_match[];

static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
66
		struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
67 68 69 70 71 72 73
{
	return drm_fb_cma_create(dev, file_priv, mode_cmd);
}

static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;
74
	drm_fbdev_cma_hotplug_event(priv->fbdev);
75 76
}

77 78
static int tilcdc_atomic_check(struct drm_device *dev,
			       struct drm_atomic_state *state)
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
{
	int ret;

	ret = drm_atomic_helper_check_modeset(dev, state);
	if (ret)
		return ret;

	ret = drm_atomic_helper_check_planes(dev, state);
	if (ret)
		return ret;

	/*
	 * tilcdc ->atomic_check can update ->mode_changed if pixel format
	 * changes, hence will we check modeset changes again.
	 */
	ret = drm_atomic_helper_check_modeset(dev, state);
	if (ret)
		return ret;

	return ret;
}

static int tilcdc_commit(struct drm_device *dev,
		  struct drm_atomic_state *state,
		  bool async)
{
	int ret;

	ret = drm_atomic_helper_prepare_planes(dev, state);
	if (ret)
		return ret;

111 112 113 114 115
	ret = drm_atomic_helper_swap_state(state, true);
	if (ret) {
		drm_atomic_helper_cleanup_planes(dev, state);
		return ret;
	}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

	/*
	 * Everything below can be run asynchronously without the need to grab
	 * any modeset locks at all under one condition: It must be guaranteed
	 * that the asynchronous work has either been cancelled (if the driver
	 * supports it, which at least requires that the framebuffers get
	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
	 * before the new state gets committed on the software side with
	 * drm_atomic_helper_swap_state().
	 *
	 * This scheme allows new atomic state updates to be prepared and
	 * checked in parallel to the asynchronous completion of the previous
	 * update. Which is important since compositors need to figure out the
	 * composition of the next frame right after having submitted the
	 * current layout.
	 */

	drm_atomic_helper_commit_modeset_disables(dev, state);

135
	drm_atomic_helper_commit_planes(dev, state, 0);
136 137 138 139 140 141 142 143 144 145

	drm_atomic_helper_commit_modeset_enables(dev, state);

	drm_atomic_helper_wait_for_vblanks(dev, state);

	drm_atomic_helper_cleanup_planes(dev, state);

	return 0;
}

146 147 148
static const struct drm_mode_config_funcs mode_config_funcs = {
	.fb_create = tilcdc_fb_create,
	.output_poll_changed = tilcdc_fb_output_poll_changed,
149 150
	.atomic_check = tilcdc_atomic_check,
	.atomic_commit = tilcdc_commit,
151 152
};

153
static void modeset_init(struct drm_device *dev)
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
{
	struct tilcdc_drm_private *priv = dev->dev_private;
	struct tilcdc_module *mod;

	list_for_each_entry(mod, &module_list, list) {
		DBG("loading module: %s", mod->name);
		mod->funcs->modeset_init(mod, dev);
	}

	dev->mode_config.min_width = 0;
	dev->mode_config.min_height = 0;
	dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc);
	dev->mode_config.max_height = 2048;
	dev->mode_config.funcs = &mode_config_funcs;
}

#ifdef CONFIG_CPU_FREQ
static int cpufreq_transition(struct notifier_block *nb,
				     unsigned long val, void *data)
{
	struct tilcdc_drm_private *priv = container_of(nb,
			struct tilcdc_drm_private, freq_transition);
176

177 178
	if (val == CPUFREQ_POSTCHANGE)
		tilcdc_crtc_update_clk(priv->crtc);
179 180 181 182 183 184 185 186 187

	return 0;
}
#endif

/*
 * DRM operations:
 */

188
static void tilcdc_fini(struct drm_device *dev)
189 190 191
{
	struct tilcdc_drm_private *priv = dev->dev_private;

192
	if (priv->crtc)
193
		tilcdc_crtc_shutdown(priv->crtc);
194

195 196
	if (priv->is_registered)
		drm_dev_unregister(dev);
197

198
	drm_kms_helper_poll_fini(dev);
199 200 201 202

	if (priv->fbdev)
		drm_fbdev_cma_fini(priv->fbdev);

203
	drm_irq_uninstall(dev);
204
	drm_mode_config_cleanup(dev);
205
	tilcdc_remove_external_device(dev);
206 207

#ifdef CONFIG_CPU_FREQ
208 209 210
	if (priv->freq_transition.notifier_call)
		cpufreq_unregister_notifier(&priv->freq_transition,
					    CPUFREQ_TRANSITION_NOTIFIER);
211 212 213 214 215 216 217 218
#endif

	if (priv->clk)
		clk_put(priv->clk);

	if (priv->mmio)
		iounmap(priv->mmio);

219 220 221 222
	if (priv->wq) {
		flush_workqueue(priv->wq);
		destroy_workqueue(priv->wq);
	}
223 224 225 226 227

	dev->dev_private = NULL;

	pm_runtime_disable(dev->dev);

228
	drm_dev_unref(dev);
229 230
}

231
static int tilcdc_init(struct drm_driver *ddrv, struct device *dev)
232
{
233 234 235
	struct drm_device *ddev;
	struct platform_device *pdev = to_platform_device(dev);
	struct device_node *node = dev->of_node;
236 237
	struct tilcdc_drm_private *priv;
	struct resource *res;
238
	u32 bpp = 0;
239 240
	int ret;

241
	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
242
	if (!priv) {
243
		dev_err(dev, "failed to allocate private data\n");
244 245 246
		return -ENOMEM;
	}

247 248 249 250 251
	ddev = drm_dev_alloc(ddrv, dev);
	if (IS_ERR(ddev))
		return PTR_ERR(ddev);

	ddev->dev_private = priv;
252 253
	platform_set_drvdata(pdev, ddev);
	drm_mode_config_init(ddev);
254

255
	priv->is_componentized =
256
		tilcdc_get_external_components(dev, NULL) > 0;
257

258
	priv->wq = alloc_ordered_workqueue("tilcdc", 0);
259 260
	if (!priv->wq) {
		ret = -ENOMEM;
261
		goto init_failed;
262
	}
263 264 265

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
266
		dev_err(dev, "failed to get memory resource\n");
267
		ret = -EINVAL;
268
		goto init_failed;
269 270 271 272
	}

	priv->mmio = ioremap_nocache(res->start, resource_size(res));
	if (!priv->mmio) {
273
		dev_err(dev, "failed to ioremap\n");
274
		ret = -ENOMEM;
275
		goto init_failed;
276 277
	}

278
	priv->clk = clk_get(dev, "fck");
279
	if (IS_ERR(priv->clk)) {
280
		dev_err(dev, "failed to get functional clock\n");
281
		ret = -ENODEV;
282
		goto init_failed;
283 284 285 286 287 288 289
	}

#ifdef CONFIG_CPU_FREQ
	priv->freq_transition.notifier_call = cpufreq_transition;
	ret = cpufreq_register_notifier(&priv->freq_transition,
			CPUFREQ_TRANSITION_NOTIFIER);
	if (ret) {
290
		dev_err(dev, "failed to register cpufreq notifier\n");
291 292
		priv->freq_transition.notifier_call = NULL;
		goto init_failed;
293 294 295 296
	}
#endif

	if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth))
297 298 299 300
		priv->max_bandwidth = TILCDC_DEFAULT_MAX_BANDWIDTH;

	DBG("Maximum Bandwidth Value %d", priv->max_bandwidth);

301
	if (of_property_read_u32(node, "max-width", &priv->max_width))
302 303 304 305
		priv->max_width = TILCDC_DEFAULT_MAX_WIDTH;

	DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width);

306
	if (of_property_read_u32(node, "max-pixelclock",
307 308 309 310
					&priv->max_pixelclock))
		priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK;

	DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock);
311

312
	pm_runtime_enable(dev);
313 314

	/* Determine LCD IP Version */
315 316
	pm_runtime_get_sync(dev);
	switch (tilcdc_read(ddev, LCDC_PID_REG)) {
317 318 319 320 321 322 323 324
	case 0x4c100102:
		priv->rev = 1;
		break;
	case 0x4f200800:
	case 0x4f201000:
		priv->rev = 2;
		break;
	default:
325 326 327
		dev_warn(dev, "Unknown PID Reg value 0x%08x, "
			"defaulting to LCD revision 1\n",
			tilcdc_read(ddev, LCDC_PID_REG));
328 329 330 331
		priv->rev = 1;
		break;
	}

332
	pm_runtime_put_sync(dev);
333

334 335 336 337
	if (priv->rev == 1) {
		DBG("Revision 1 LCDC supports only RGB565 format");
		priv->pixelformats = tilcdc_rev1_formats;
		priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats);
338
		bpp = 16;
339 340 341 342 343 344 345 346 347
	} else {
		const char *str = "\0";

		of_property_read_string(node, "blue-and-red-wiring", &str);
		if (0 == strcmp(str, "crossed")) {
			DBG("Configured for crossed blue and red wires");
			priv->pixelformats = tilcdc_crossed_formats;
			priv->num_pixelformats =
				ARRAY_SIZE(tilcdc_crossed_formats);
348
			bpp = 32; /* Choose bpp with RGB support for fbdef */
349 350 351 352 353
		} else if (0 == strcmp(str, "straight")) {
			DBG("Configured for straight blue and red wires");
			priv->pixelformats = tilcdc_straight_formats;
			priv->num_pixelformats =
				ARRAY_SIZE(tilcdc_straight_formats);
354
			bpp = 16; /* Choose bpp with RGB support for fbdef */
355 356 357 358 359 360
		} else {
			DBG("Blue and red wiring '%s' unknown, use legacy mode",
			    str);
			priv->pixelformats = tilcdc_legacy_formats;
			priv->num_pixelformats =
				ARRAY_SIZE(tilcdc_legacy_formats);
361
			bpp = 16; /* This is just a guess */
362 363 364
		}
	}

365
	ret = tilcdc_crtc_create(ddev);
366
	if (ret < 0) {
367
		dev_err(dev, "failed to create crtc\n");
368
		goto init_failed;
369
	}
370
	modeset_init(ddev);
371

372
	if (priv->is_componentized) {
373
		ret = component_bind_all(dev, ddev);
374
		if (ret < 0)
375
			goto init_failed;
376

377
		ret = tilcdc_add_component_encoder(ddev);
378
		if (ret < 0)
379
			goto init_failed;
380 381 382 383
	} else {
		ret = tilcdc_attach_external_device(ddev);
		if (ret)
			goto init_failed;
384 385
	}

386 387
	if (!priv->external_connector &&
	    ((priv->num_encoders == 0) || (priv->num_connectors == 0))) {
388
		dev_err(dev, "no encoders/connectors found\n");
389
		ret = -ENXIO;
390
		goto init_failed;
391 392
	}

393
	ret = drm_vblank_init(ddev, 1);
394
	if (ret < 0) {
395
		dev_err(dev, "failed to initialize vblank\n");
396
		goto init_failed;
397 398
	}

399
	ret = drm_irq_install(ddev, platform_get_irq(pdev, 0));
400
	if (ret < 0) {
401
		dev_err(dev, "failed to install IRQ handler\n");
402
		goto init_failed;
403 404
	}

405
	drm_mode_config_reset(ddev);
406

407
	priv->fbdev = drm_fbdev_cma_init(ddev, bpp,
408
					 ddev->mode_config.num_connector);
409 410
	if (IS_ERR(priv->fbdev)) {
		ret = PTR_ERR(priv->fbdev);
411
		goto init_failed;
412
	}
413

414 415 416 417
	drm_kms_helper_poll_init(ddev);

	ret = drm_dev_register(ddev, 0);
	if (ret)
418
		goto init_failed;
419

420
	priv->is_registered = true;
421 422
	return 0;

423 424
init_failed:
	tilcdc_fini(ddev);
425

426 427 428 429 430 431 432 433 434
	return ret;
}

static void tilcdc_lastclose(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;
	drm_fbdev_cma_restore_mode(priv->fbdev);
}

D
Daniel Vetter 已提交
435
static irqreturn_t tilcdc_irq(int irq, void *arg)
436 437 438 439 440 441
{
	struct drm_device *dev = arg;
	struct tilcdc_drm_private *priv = dev->dev_private;
	return tilcdc_crtc_irq(priv->crtc);
}

442
#if defined(CONFIG_DEBUG_FS)
443 444 445 446 447
static const struct {
	const char *name;
	uint8_t  rev;
	uint8_t  save;
	uint32_t reg;
448
} registers[] =		{
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
#define REG(rev, save, reg) { #reg, rev, save, reg }
		/* exists in revision 1: */
		REG(1, false, LCDC_PID_REG),
		REG(1, true,  LCDC_CTRL_REG),
		REG(1, false, LCDC_STAT_REG),
		REG(1, true,  LCDC_RASTER_CTRL_REG),
		REG(1, true,  LCDC_RASTER_TIMING_0_REG),
		REG(1, true,  LCDC_RASTER_TIMING_1_REG),
		REG(1, true,  LCDC_RASTER_TIMING_2_REG),
		REG(1, true,  LCDC_DMA_CTRL_REG),
		REG(1, true,  LCDC_DMA_FB_BASE_ADDR_0_REG),
		REG(1, true,  LCDC_DMA_FB_CEILING_ADDR_0_REG),
		REG(1, true,  LCDC_DMA_FB_BASE_ADDR_1_REG),
		REG(1, true,  LCDC_DMA_FB_CEILING_ADDR_1_REG),
		/* new in revision 2: */
		REG(2, false, LCDC_RAW_STAT_REG),
		REG(2, false, LCDC_MASKED_STAT_REG),
466
		REG(2, true, LCDC_INT_ENABLE_SET_REG),
467 468 469 470 471
		REG(2, false, LCDC_INT_ENABLE_CLR_REG),
		REG(2, false, LCDC_END_OF_INT_IND_REG),
		REG(2, true,  LCDC_CLK_ENABLE_REG),
#undef REG
};
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
#endif

#ifdef CONFIG_DEBUG_FS
static int tilcdc_regs_show(struct seq_file *m, void *arg)
{
	struct drm_info_node *node = (struct drm_info_node *) m->private;
	struct drm_device *dev = node->minor->dev;
	struct tilcdc_drm_private *priv = dev->dev_private;
	unsigned i;

	pm_runtime_get_sync(dev->dev);

	seq_printf(m, "revision: %d\n", priv->rev);

	for (i = 0; i < ARRAY_SIZE(registers); i++)
		if (priv->rev >= registers[i].rev)
			seq_printf(m, "%s:\t %08x\n", registers[i].name,
					tilcdc_read(dev, registers[i].reg));

	pm_runtime_put_sync(dev->dev);

	return 0;
}

static int tilcdc_mm_show(struct seq_file *m, void *arg)
{
	struct drm_info_node *node = (struct drm_info_node *) m->private;
	struct drm_device *dev = node->minor->dev;
D
Daniel Vetter 已提交
501 502 503
	struct drm_printer p = drm_seq_file_printer(m);
	drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p);
	return 0;
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
}

static struct drm_info_list tilcdc_debugfs_list[] = {
		{ "regs", tilcdc_regs_show, 0 },
		{ "mm",   tilcdc_mm_show,   0 },
		{ "fb",   drm_fb_cma_debugfs_show, 0 },
};

static int tilcdc_debugfs_init(struct drm_minor *minor)
{
	struct drm_device *dev = minor->dev;
	struct tilcdc_module *mod;
	int ret;

	ret = drm_debugfs_create_files(tilcdc_debugfs_list,
			ARRAY_SIZE(tilcdc_debugfs_list),
			minor->debugfs_root, minor);

	list_for_each_entry(mod, &module_list, list)
		if (mod->funcs->debugfs_init)
			mod->funcs->debugfs_init(mod, minor);

	if (ret) {
		dev_err(dev->dev, "could not install tilcdc_debugfs_list\n");
		return ret;
	}

	return ret;
}
#endif

535
DEFINE_DRM_GEM_CMA_FOPS(fops);
536 537

static struct drm_driver tilcdc_driver = {
538
	.driver_features    = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |
539
			       DRIVER_PRIME | DRIVER_ATOMIC),
540 541
	.lastclose          = tilcdc_lastclose,
	.irq_handler        = tilcdc_irq,
542
	.gem_free_object_unlocked = drm_gem_cma_free_object,
543 544 545
	.gem_vm_ops         = &drm_gem_cma_vm_ops,
	.dumb_create        = drm_gem_cma_dumb_create,
	.dumb_map_offset    = drm_gem_cma_dumb_map_offset,
546
	.dumb_destroy       = drm_gem_dumb_destroy,
547 548 549 550 551 552 553 554 555 556

	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
	.gem_prime_import	= drm_gem_prime_import,
	.gem_prime_export	= drm_gem_prime_export,
	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
#ifdef CONFIG_DEBUG_FS
	.debugfs_init       = tilcdc_debugfs_init,
#endif
	.fops               = &fops,
	.name               = "tilcdc",
	.desc               = "TI LCD Controller DRM",
	.date               = "20121205",
	.major              = 1,
	.minor              = 0,
};

/*
 * Power management:
 */

#ifdef CONFIG_PM_SLEEP
static int tilcdc_pm_suspend(struct device *dev)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct tilcdc_drm_private *priv = ddev->dev_private;

578
	priv->saved_state = drm_atomic_helper_suspend(ddev);
579

580 581 582
	/* Select sleep pin state */
	pinctrl_pm_select_sleep_state(dev);

583 584 585 586 587 588 589
	return 0;
}

static int tilcdc_pm_resume(struct device *dev)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct tilcdc_drm_private *priv = ddev->dev_private;
590
	int ret = 0;
591

D
Dave Gerlach 已提交
592 593 594
	/* Select default pin state */
	pinctrl_pm_select_default_state(dev);

595 596
	if (priv->saved_state)
		ret = drm_atomic_helper_resume(ddev, priv->saved_state);
597

598
	return ret;
599 600 601 602 603 604 605 606 607 608
}
#endif

static const struct dev_pm_ops tilcdc_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume)
};

/*
 * Platform driver:
 */
609 610
static int tilcdc_bind(struct device *dev)
{
611
	return tilcdc_init(&tilcdc_driver, dev);
612 613 614 615
}

static void tilcdc_unbind(struct device *dev)
{
616 617 618 619 620 621
	struct drm_device *ddev = dev_get_drvdata(dev);

	/* Check if a subcomponent has already triggered the unloading. */
	if (!ddev->dev_private)
		return;

622
	tilcdc_fini(dev_get_drvdata(dev));
623 624 625 626 627 628 629
}

static const struct component_master_ops tilcdc_comp_ops = {
	.bind = tilcdc_bind,
	.unbind = tilcdc_unbind,
};

630 631
static int tilcdc_pdev_probe(struct platform_device *pdev)
{
632 633 634
	struct component_match *match = NULL;
	int ret;

635 636 637 638 639 640
	/* bail out early if no DT data: */
	if (!pdev->dev.of_node) {
		dev_err(&pdev->dev, "device-tree data is missing\n");
		return -ENXIO;
	}

641 642 643 644
	ret = tilcdc_get_external_components(&pdev->dev, &match);
	if (ret < 0)
		return ret;
	else if (ret == 0)
645
		return tilcdc_init(&tilcdc_driver, &pdev->dev);
646 647 648 649
	else
		return component_master_add_with_match(&pdev->dev,
						       &tilcdc_comp_ops,
						       match);
650 651 652 653
}

static int tilcdc_pdev_remove(struct platform_device *pdev)
{
654
	int ret;
655

656 657 658 659
	ret = tilcdc_get_external_components(&pdev->dev, NULL);
	if (ret < 0)
		return ret;
	else if (ret == 0)
660
		tilcdc_fini(platform_get_drvdata(pdev));
661 662
	else
		component_master_del(&pdev->dev, &tilcdc_comp_ops);
663 664 665 666 667 668

	return 0;
}

static struct of_device_id tilcdc_of_match[] = {
		{ .compatible = "ti,am33xx-tilcdc", },
669
		{ .compatible = "ti,da850-tilcdc", },
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
		{ },
};
MODULE_DEVICE_TABLE(of, tilcdc_of_match);

static struct platform_driver tilcdc_platform_driver = {
	.probe      = tilcdc_pdev_probe,
	.remove     = tilcdc_pdev_remove,
	.driver     = {
		.name   = "tilcdc",
		.pm     = &tilcdc_pm_ops,
		.of_match_table = tilcdc_of_match,
	},
};

static int __init tilcdc_drm_init(void)
{
	DBG("init");
	tilcdc_tfp410_init();
688
	tilcdc_panel_init();
689 690 691 692 693 694 695
	return platform_driver_register(&tilcdc_platform_driver);
}

static void __exit tilcdc_drm_fini(void)
{
	DBG("fini");
	platform_driver_unregister(&tilcdc_platform_driver);
696 697
	tilcdc_panel_fini();
	tilcdc_tfp410_fini();
698 699
}

700
module_init(tilcdc_drm_init);
701 702 703 704 705
module_exit(tilcdc_drm_fini);

MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
MODULE_DESCRIPTION("TI LCD Controller DRM Driver");
MODULE_LICENSE("GPL");