omap_drv.c 17.8 KB
Newer Older
1
/*
Rob Clark's avatar
Rob Clark committed
2
 * drivers/gpu/drm/omapdrm/omap_drv.c
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * Copyright (C) 2011 Texas Instruments
 * Author: Rob Clark <rob@ti.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/>.
 */

20
#include <drm/drm_atomic.h>
21
#include <drm/drm_atomic_helper.h>
22 23
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
24

25
#include "omap_dmm_tiler.h"
26
#include "omap_drv.h"
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

#define DRIVER_NAME		MODULE_NAME
#define DRIVER_DESC		"OMAP DRM"
#define DRIVER_DATE		"20110917"
#define DRIVER_MAJOR		1
#define DRIVER_MINOR		0
#define DRIVER_PATCHLEVEL	0

/*
 * mode config funcs
 */

/* Notes about mapping DSS and DRM entities:
 *    CRTC:        overlay
 *    encoder:     manager.. with some extension to allow one primary CRTC
 *                 and zero or more video CRTC's to be mapped to one encoder?
 *    connector:   dssdev.. manager can be attached/detached from different
 *                 devices
 */

static void omap_fb_output_poll_changed(struct drm_device *dev)
{
	struct omap_drm_private *priv = dev->dev_private;
	DBG("dev=%p", dev);
51
	if (priv->fbdev)
52 53 54
		drm_fb_helper_hotplug_event(priv->fbdev);
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
static void omap_atomic_wait_for_completion(struct drm_device *dev,
					    struct drm_atomic_state *old_state)
{
	struct drm_crtc_state *old_crtc_state;
	struct drm_crtc *crtc;
	unsigned int i;
	int ret;

	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
		if (!crtc->state->enable)
			continue;

		ret = omap_crtc_wait_pending(crtc);

		if (!ret)
			dev_warn(dev->dev,
				 "atomic complete timeout (pipe %u)!\n", i);
	}
}

75
static void omap_atomic_commit_tail(struct drm_atomic_state *old_state)
76
{
77
	struct drm_device *dev = old_state->dev;
78 79
	struct omap_drm_private *priv = dev->dev_private;

Tomi Valkeinen's avatar
Tomi Valkeinen committed
80
	priv->dispc_ops->runtime_get();
81

82
	/* Apply the atomic update. */
83
	drm_atomic_helper_commit_modeset_disables(dev, old_state);
84 85 86 87 88 89 90 91 92 93 94 95

	/* With the current dss dispc implementation we have to enable
	 * the new modeset before we can commit planes. The dispc ovl
	 * configuration relies on the video mode configuration been
	 * written into the HW when the ovl configuration is
	 * calculated.
	 *
	 * This approach is not ideal because after a mode change the
	 * plane update is executed only after the first vblank
	 * interrupt. The dispc implementation should be fixed so that
	 * it is able use uncommitted drm state information.
	 */
96
	drm_atomic_helper_commit_modeset_enables(dev, old_state);
97 98 99
	omap_atomic_wait_for_completion(dev, old_state);

	drm_atomic_helper_commit_planes(dev, old_state, 0);
100

101 102 103 104 105 106
	drm_atomic_helper_commit_hw_done(old_state);

	/*
	 * Wait for completion of the page flips to ensure that old buffers
	 * can't be touched by the hardware anymore before cleaning up planes.
	 */
107
	omap_atomic_wait_for_completion(dev, old_state);
108 109 110

	drm_atomic_helper_cleanup_planes(dev, old_state);

Tomi Valkeinen's avatar
Tomi Valkeinen committed
111
	priv->dispc_ops->runtime_put();
112 113
}

114 115 116
static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = {
	.atomic_commit_tail = omap_atomic_commit_tail,
};
117

118
static const struct drm_mode_config_funcs omap_mode_config_funcs = {
119 120
	.fb_create = omap_framebuffer_create,
	.output_poll_changed = omap_fb_output_poll_changed,
121
	.atomic_check = drm_atomic_helper_check,
122
	.atomic_commit = drm_atomic_helper_commit,
123 124 125 126 127 128 129
};

static int get_connector_type(struct omap_dss_device *dssdev)
{
	switch (dssdev->type) {
	case OMAP_DISPLAY_TYPE_HDMI:
		return DRM_MODE_CONNECTOR_HDMIA;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
130 131
	case OMAP_DISPLAY_TYPE_DVI:
		return DRM_MODE_CONNECTOR_DVID;
132 133
	case OMAP_DISPLAY_TYPE_DSI:
		return DRM_MODE_CONNECTOR_DSI;
134 135 136 137 138
	default:
		return DRM_MODE_CONNECTOR_Unknown;
	}
}

139 140 141 142 143 144 145
static void omap_disconnect_dssdevs(void)
{
	struct omap_dss_device *dssdev = NULL;

	for_each_dss_dev(dssdev)
		dssdev->driver->disconnect(dssdev);
}
146

147 148 149 150
static int omap_connect_dssdevs(void)
{
	int r;
	struct omap_dss_device *dssdev = NULL;
151 152 153

	if (!omapdss_stack_is_ready())
		return -EPROBE_DEFER;
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

	for_each_dss_dev(dssdev) {
		r = dssdev->driver->connect(dssdev);
		if (r == -EPROBE_DEFER) {
			omap_dss_put_device(dssdev);
			goto cleanup;
		} else if (r) {
			dev_warn(dssdev->dev, "could not connect display: %s\n",
				dssdev->name);
		}
	}

	return 0;

cleanup:
	/*
	 * if we are deferring probe, we disconnect the devices we previously
	 * connected
	 */
173
	omap_disconnect_dssdevs();
174 175 176

	return r;
}
177

178 179 180
static int omap_modeset_init_properties(struct drm_device *dev)
{
	struct omap_drm_private *priv = dev->dev_private;
181
	unsigned int num_planes = priv->dispc_ops->get_num_ovls();
182

183 184
	priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0,
						      num_planes - 1);
185 186 187 188 189 190
	if (!priv->zorder_prop)
		return -ENOMEM;

	return 0;
}

191
static int omap_modeset_init(struct drm_device *dev)
192 193
{
	struct omap_drm_private *priv = dev->dev_private;
194
	struct omap_dss_device *dssdev = NULL;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
195 196
	int num_ovls = priv->dispc_ops->get_num_ovls();
	int num_mgrs = priv->dispc_ops->get_num_mgrs();
197
	int num_crtcs, crtc_idx, plane_idx;
198
	int ret;
199
	u32 plane_crtc_mask;
200

201
	drm_mode_config_init(dev);
202

203 204 205 206
	ret = omap_modeset_init_properties(dev);
	if (ret < 0)
		return ret;

207
	/*
208 209 210 211 212 213
	 * This function creates exactly one connector, encoder, crtc,
	 * and primary plane per each connected dss-device. Each
	 * connector->encoder->crtc chain is expected to be separate
	 * and each crtc is connect to a single dss-channel. If the
	 * configuration does not match the expectations or exceeds
	 * the available resources, the configuration is rejected.
214
	 */
215
	num_crtcs = 0;
216 217 218 219
	for_each_dss_dev(dssdev)
		if (omapdss_device_is_connected(dssdev))
			num_crtcs++;

220 221 222 223 224 225 226 227 228 229 230 231
	if (num_crtcs > num_mgrs || num_crtcs > num_ovls ||
	    num_crtcs > ARRAY_SIZE(priv->crtcs) ||
	    num_crtcs > ARRAY_SIZE(priv->planes) ||
	    num_crtcs > ARRAY_SIZE(priv->encoders) ||
	    num_crtcs > ARRAY_SIZE(priv->connectors)) {
		dev_err(dev->dev, "%s(): Too many connected displays\n",
			__func__);
		return -EINVAL;
	}

	/* All planes can be put to any CRTC */
	plane_crtc_mask = (1 << num_crtcs) - 1;
232

233
	dssdev = NULL;
234

235 236
	crtc_idx = 0;
	plane_idx = 0;
237 238 239
	for_each_dss_dev(dssdev) {
		struct drm_connector *connector;
		struct drm_encoder *encoder;
240 241
		struct drm_plane *plane;
		struct drm_crtc *crtc;
242

243
		if (!omapdss_device_is_connected(dssdev))
244
			continue;
245

246
		encoder = omap_encoder_init(dev, dssdev);
247
		if (!encoder)
248
			return -ENOMEM;
249

250 251
		connector = omap_connector_init(dev,
				get_connector_type(dssdev), dssdev, encoder);
252
		if (!connector)
253
			return -ENOMEM;
254

255 256 257 258
		plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_PRIMARY,
					plane_crtc_mask);
		if (IS_ERR(plane))
			return PTR_ERR(plane);
259

260 261 262
		crtc = omap_crtc_init(dev, plane, dssdev);
		if (IS_ERR(crtc))
			return PTR_ERR(crtc);
263

264
		drm_mode_connector_attach_encoder(connector, encoder);
265
		encoder->possible_crtcs = (1 << crtc_idx);
266

267 268 269 270
		priv->crtcs[priv->num_crtcs++] = crtc;
		priv->planes[priv->num_planes++] = plane;
		priv->encoders[priv->num_encoders++] = encoder;
		priv->connectors[priv->num_connectors++] = connector;
271

272 273
		plane_idx++;
		crtc_idx++;
274 275 276 277 278
	}

	/*
	 * Create normal planes for the remaining overlays:
	 */
279
	for (; plane_idx < num_ovls; plane_idx++) {
280 281
		struct drm_plane *plane;

282 283 284 285 286
		if (WARN_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)))
			return -EINVAL;

		plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_OVERLAY,
			plane_crtc_mask);
287 288
		if (IS_ERR(plane))
			return PTR_ERR(plane);
289 290 291 292 293 294 295 296

		priv->planes[priv->num_planes++] = plane;
	}

	DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n",
		priv->num_planes, priv->num_crtcs, priv->num_encoders,
		priv->num_connectors);

297 298
	dev->mode_config.min_width = 8;
	dev->mode_config.min_height = 2;
299 300 301 302 303 304 305 306

	/* note: eventually will need some cpu_is_omapXYZ() type stuff here
	 * to fill in these limits properly on different OMAP generations..
	 */
	dev->mode_config.max_width = 2048;
	dev->mode_config.max_height = 2048;

	dev->mode_config.funcs = &omap_mode_config_funcs;
307
	dev->mode_config.helper_private = &omap_mode_config_helper_funcs;
308

309 310
	drm_mode_config_reset(dev);

311 312
	omap_drm_irq_install(dev);

313 314 315 316 317 318 319 320 321 322 323
	return 0;
}

/*
 * drm ioctl funcs
 */


static int ioctl_get_param(struct drm_device *dev, void *data,
		struct drm_file *file_priv)
{
324
	struct omap_drm_private *priv = dev->dev_private;
325 326 327 328 329 330
	struct drm_omap_param *args = data;

	DBG("%p: param=%llu", dev, args->param);

	switch (args->param) {
	case OMAP_PARAM_CHIPSET_ID:
331
		args->value = priv->omaprev;
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
		break;
	default:
		DBG("unknown parameter %lld", args->param);
		return -EINVAL;
	}

	return 0;
}

static int ioctl_set_param(struct drm_device *dev, void *data,
		struct drm_file *file_priv)
{
	struct drm_omap_param *args = data;

	switch (args->param) {
	default:
		DBG("unknown parameter %lld", args->param);
		return -EINVAL;
	}

	return 0;
}

355 356
#define OMAP_BO_USER_MASK	0x00ffffff	/* flags settable by userspace */

357 358 359 360
static int ioctl_gem_new(struct drm_device *dev, void *data,
		struct drm_file *file_priv)
{
	struct drm_omap_gem_new *args = data;
361 362
	u32 flags = args->flags & OMAP_BO_USER_MASK;

363
	VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
364 365 366 367
	     args->size.bytes, flags);

	return omap_gem_new_handle(dev, file_priv, args->size, flags,
				   &args->handle);
368 369 370 371 372 373 374 375 376
}

static int ioctl_gem_info(struct drm_device *dev, void *data,
		struct drm_file *file_priv)
{
	struct drm_omap_gem_info *args = data;
	struct drm_gem_object *obj;
	int ret = 0;

377
	VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
378

379
	obj = drm_gem_object_lookup(file_priv, args->handle);
380
	if (!obj)
381 382
		return -ENOENT;

383
	args->size = omap_gem_mmap_size(obj);
384 385 386 387 388 389 390
	args->offset = omap_gem_mmap_offset(obj);

	drm_gem_object_unreference_unlocked(obj);

	return ret;
}

Rob Clark's avatar
Rob Clark committed
391
static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
392 393 394 395 396 397
	DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param,
			  DRM_AUTH | DRM_RENDER_ALLOW),
	DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param,
			  DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
	DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new,
			  DRM_AUTH | DRM_RENDER_ALLOW),
398 399
	/* Deprecated, to be removed. */
	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, drm_noop,
400
			  DRM_AUTH | DRM_RENDER_ALLOW),
401 402
	/* Deprecated, to be removed. */
	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, drm_noop,
403 404 405
			  DRM_AUTH | DRM_RENDER_ALLOW),
	DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info,
			  DRM_AUTH | DRM_RENDER_ALLOW),
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
};

/*
 * drm driver funcs
 */

static int dev_open(struct drm_device *dev, struct drm_file *file)
{
	file->driver_priv = NULL;

	DBG("open: dev=%p, file=%p", dev, file);

	return 0;
}

/**
 * lastclose - clean up after all DRM clients have exited
 * @dev: DRM device
 *
 * Take care of cleaning up after all DRM clients have exited.  In the
 * mode setting case, we want to restore the kernel's initial mode (just
 * in case the last client left us in a bad state).
 */
static void dev_lastclose(struct drm_device *dev)
{
431 432
	int i;

433
	/* we don't support vga_switcheroo.. so just make sure the fbdev
434 435 436 437 438 439 440
	 * mode is active
	 */
	struct omap_drm_private *priv = dev->dev_private;
	int ret;

	DBG("lastclose: dev=%p", dev);

441 442 443 444 445 446 447 448
	/* need to restore default rotation state.. not sure
	 * if there is a cleaner way to restore properties to
	 * default state?  Maybe a flag that properties should
	 * automatically be restored to default state on
	 * lastclose?
	 */
	for (i = 0; i < priv->num_crtcs; i++) {
		struct drm_crtc *crtc = priv->crtcs[i];
449

450 451 452 453 454
		if (!crtc->primary->rotation_property)
			continue;

		drm_object_property_set_value(&crtc->base,
					      crtc->primary->rotation_property,
455
					      DRM_MODE_ROTATE_0);
456 457 458 459 460 461 462 463 464 465
	}

	for (i = 0; i < priv->num_planes; i++) {
		struct drm_plane *plane = priv->planes[i];

		if (!plane->rotation_property)
			continue;

		drm_object_property_set_value(&plane->base,
					      plane->rotation_property,
466
					      DRM_MODE_ROTATE_0);
467 468
	}

469 470 471 472 473
	if (priv->fbdev) {
		ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
		if (ret)
			DBG("failed to restore crtc mode");
	}
474 475
}

476
static const struct vm_operations_struct omap_gem_vm_ops = {
477 478 479 480 481
	.fault = omap_gem_fault,
	.open = drm_gem_vm_open,
	.close = drm_gem_vm_close,
};

482
static const struct file_operations omapdriver_fops = {
483 484 485 486 487 488 489 490
	.owner = THIS_MODULE,
	.open = drm_open,
	.unlocked_ioctl = drm_ioctl,
	.release = drm_release,
	.mmap = omap_gem_mmap,
	.poll = drm_poll,
	.read = drm_read,
	.llseek = noop_llseek,
491 492
};

493
static struct drm_driver omap_drm_driver = {
494
	.driver_features = DRIVER_MODESET | DRIVER_GEM  | DRIVER_PRIME |
495
		DRIVER_ATOMIC | DRIVER_RENDER,
496 497
	.open = dev_open,
	.lastclose = dev_lastclose,
498
#ifdef CONFIG_DEBUG_FS
499
	.debugfs_init = omap_debugfs_init,
500
#endif
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
	.gem_prime_export = omap_gem_prime_export,
	.gem_prime_import = omap_gem_prime_import,
	.gem_free_object = omap_gem_free_object,
	.gem_vm_ops = &omap_gem_vm_ops,
	.dumb_create = omap_gem_dumb_create,
	.dumb_map_offset = omap_gem_dumb_map_offset,
	.dumb_destroy = drm_gem_dumb_destroy,
	.ioctls = ioctls,
	.num_ioctls = DRM_OMAP_NUM_IOCTLS,
	.fops = &omapdriver_fops,
	.name = DRIVER_NAME,
	.desc = DRIVER_DESC,
	.date = DRIVER_DATE,
	.major = DRIVER_MAJOR,
	.minor = DRIVER_MINOR,
	.patchlevel = DRIVER_PATCHLEVEL,
519 520
};

521
static int pdev_probe(struct platform_device *pdev)
522
{
523 524 525 526 527 528 529
	struct omap_drm_platform_data *pdata = pdev->dev.platform_data;
	struct omap_drm_private *priv;
	struct drm_device *ddev;
	unsigned int i;
	int ret;

	DBG("%s", pdev->name);
530

531 532 533
	if (omapdss_is_initialized() == false)
		return -EPROBE_DEFER;

534 535
	omap_crtc_pre_init();

536 537 538 539 540 541 542 543 544 545 546
	ret = omap_connect_dssdevs();
	if (ret)
		goto err_crtc_uninit;

	/* Allocate and initialize the driver private structure. */
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		ret = -ENOMEM;
		goto err_disconnect_dssdevs;
	}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
547 548
	priv->dispc_ops = dispc_get_ops();

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
	priv->omaprev = pdata->omaprev;
	priv->wq = alloc_ordered_workqueue("omapdrm", 0);

	spin_lock_init(&priv->list_lock);
	INIT_LIST_HEAD(&priv->obj_list);

	/* Allocate and initialize the DRM device. */
	ddev = drm_dev_alloc(&omap_drm_driver, &pdev->dev);
	if (IS_ERR(ddev)) {
		ret = PTR_ERR(ddev);
		goto err_free_priv;
	}

	ddev->dev_private = priv;
	platform_set_drvdata(pdev, ddev);

	omap_gem_init(ddev);

	ret = omap_modeset_init(ddev);
	if (ret) {
		dev_err(&pdev->dev, "omap_modeset_init failed: ret=%d\n", ret);
		goto err_free_drm_dev;
	}

	/* Initialize vblank handling, start with all CRTCs disabled. */
	ret = drm_vblank_init(ddev, priv->num_crtcs);
	if (ret) {
		dev_err(&pdev->dev, "could not init vblank\n");
		goto err_cleanup_modeset;
578 579
	}

580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
	for (i = 0; i < priv->num_crtcs; i++)
		drm_crtc_vblank_off(priv->crtcs[i]);

	priv->fbdev = omap_fbdev_init(ddev);

	drm_kms_helper_poll_init(ddev);

	/*
	 * Register the DRM device with the core and the connectors with
	 * sysfs.
	 */
	ret = drm_dev_register(ddev, 0);
	if (ret)
		goto err_cleanup_helpers;

	return 0;

err_cleanup_helpers:
	drm_kms_helper_poll_fini(ddev);
	if (priv->fbdev)
		omap_fbdev_free(ddev);
err_cleanup_modeset:
	drm_mode_config_cleanup(ddev);
	omap_drm_irq_uninstall(ddev);
err_free_drm_dev:
	omap_gem_deinit(ddev);
	drm_dev_unref(ddev);
err_free_priv:
	destroy_workqueue(priv->wq);
	kfree(priv);
err_disconnect_dssdevs:
	omap_disconnect_dssdevs();
err_crtc_uninit:
	omap_crtc_pre_uninit();
	return ret;
615 616
}

617
static int pdev_remove(struct platform_device *pdev)
618
{
619 620 621
	struct drm_device *ddev = platform_get_drvdata(pdev);
	struct omap_drm_private *priv = ddev->dev_private;

622
	DBG("");
623

624 625 626 627 628 629 630
	drm_dev_unregister(ddev);

	drm_kms_helper_poll_fini(ddev);

	if (priv->fbdev)
		omap_fbdev_free(ddev);

631 632
	drm_atomic_helper_shutdown(ddev);

633 634 635 636 637 638 639 640 641
	drm_mode_config_cleanup(ddev);

	omap_drm_irq_uninstall(ddev);
	omap_gem_deinit(ddev);

	drm_dev_unref(ddev);

	destroy_workqueue(priv->wq);
	kfree(priv);
642

643 644
	omap_disconnect_dssdevs();
	omap_crtc_pre_uninit();
645

646 647 648
	return 0;
}

649
#ifdef CONFIG_PM_SLEEP
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
static int omap_drm_suspend_all_displays(void)
{
	struct omap_dss_device *dssdev = NULL;

	for_each_dss_dev(dssdev) {
		if (!dssdev->driver)
			continue;

		if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
			dssdev->driver->disable(dssdev);
			dssdev->activate_after_resume = true;
		} else {
			dssdev->activate_after_resume = false;
		}
	}

	return 0;
}

static int omap_drm_resume_all_displays(void)
{
	struct omap_dss_device *dssdev = NULL;

	for_each_dss_dev(dssdev) {
		if (!dssdev->driver)
			continue;

		if (dssdev->activate_after_resume) {
			dssdev->driver->enable(dssdev);
			dssdev->activate_after_resume = false;
		}
	}

	return 0;
}

686 687 688 689 690 691
static int omap_drm_suspend(struct device *dev)
{
	struct drm_device *drm_dev = dev_get_drvdata(dev);

	drm_kms_helper_poll_disable(drm_dev);

692 693 694 695
	drm_modeset_lock_all(drm_dev);
	omap_drm_suspend_all_displays();
	drm_modeset_unlock_all(drm_dev);

696 697 698 699 700 701 702
	return 0;
}

static int omap_drm_resume(struct device *dev)
{
	struct drm_device *drm_dev = dev_get_drvdata(dev);

703 704 705 706
	drm_modeset_lock_all(drm_dev);
	omap_drm_resume_all_displays();
	drm_modeset_unlock_all(drm_dev);

707 708 709 710
	drm_kms_helper_poll_enable(drm_dev);

	return omap_gem_resume(dev);
}
Andy Gross's avatar
Andy Gross committed
711 712
#endif

713 714
static SIMPLE_DEV_PM_OPS(omapdrm_pm_ops, omap_drm_suspend, omap_drm_resume);

715
static struct platform_driver pdev = {
716 717 718 719 720 721
	.driver = {
		.name = DRIVER_NAME,
		.pm = &omapdrm_pm_ops,
	},
	.probe = pdev_probe,
	.remove = pdev_remove,
722 723
};

724 725 726 727 728
static struct platform_driver * const drivers[] = {
	&omap_dmm_driver,
	&pdev,
};

729 730 731
static int __init omap_drm_init(void)
{
	DBG("init");
732

733
	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
734 735 736 737 738
}

static void __exit omap_drm_fini(void)
{
	DBG("fini");
739

740
	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
741 742 743 744 745 746 747 748 749 750
}

/* need late_initcall() so we load after dss_driver's are loaded */
late_initcall(omap_drm_init);
module_exit(omap_drm_fini);

MODULE_AUTHOR("Rob Clark <rob@ti.com>");
MODULE_DESCRIPTION("OMAP DRM Display Driver");
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_LICENSE("GPL v2");