omap_crtc.c 14.6 KB
Newer Older
1
/*
R
Rob Clark 已提交
2
 * drivers/gpu/drm/omapdrm/omap_crtc.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 21
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
22 23
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
24
#include <drm/drm_mode.h>
25
#include <drm/drm_plane_helper.h>
26 27

#include "omap_drv.h"
28 29 30 31 32

#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)

struct omap_crtc {
	struct drm_crtc base;
33

34
	const char *name;
35 36 37 38 39 40 41 42
	enum omap_channel channel;

	/*
	 * Temporary: eventually this will go away, but it is needed
	 * for now to keep the output's happy.  (They only need
	 * mgr->id.)  Eventually this will be replaced w/ something
	 * more common-panel-framework-y
	 */
43
	struct omap_overlay_manager *mgr;
44 45 46

	struct omap_video_timings timings;

47
	struct omap_drm_irq vblank_irq;
48 49
	struct omap_drm_irq error_irq;

50
	bool ignore_digit_sync_lost;
51 52 53

	bool pending;
	wait_queue_head_t pending_wait;
54 55
};

56 57 58 59
/* -----------------------------------------------------------------------------
 * Helper Functions
 */

60 61 62 63 64 65 66
uint32_t pipe2vbl(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	return dispc_mgr_get_vsync_irq(omap_crtc->channel);
}

67
struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
68 69 70 71 72 73 74 75 76 77 78
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	return &omap_crtc->timings;
}

enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	return omap_crtc->channel;
}

79 80 81 82
int omap_crtc_wait_pending(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

83 84 85 86
	/*
	 * Timeout is set to a "sufficiently" high value, which should cover
	 * a single frame refresh even on slower displays.
	 */
87 88
	return wait_event_timeout(omap_crtc->pending_wait,
				  !omap_crtc->pending,
89
				  msecs_to_jiffies(250));
90 91
}

92 93 94 95
/* -----------------------------------------------------------------------------
 * DSS Manager Functions
 */

96 97 98 99 100 101 102 103 104
/*
 * Manager-ops, callbacks from output when they need to configure
 * the upstream part of the video pipe.
 *
 * Most of these we can ignore until we add support for command-mode
 * panels.. for video-mode the crtc-helpers already do an adequate
 * job of sequencing the setup of the video pipe in the proper order
 */

105 106 107
/* ovl-mgr-id -> crtc */
static struct omap_crtc *omap_crtcs[8];

108
/* we can probably ignore these until we support command-mode panels: */
109
static int omap_crtc_dss_connect(struct omap_overlay_manager *mgr,
110
		struct omap_dss_device *dst)
111 112 113 114
{
	if (mgr->output)
		return -EINVAL;

115
	if ((dispc_mgr_get_supported_outputs(mgr->id) & dst->id) == 0)
116 117 118 119 120 121 122 123
		return -EINVAL;

	dst->manager = mgr;
	mgr->output = dst;

	return 0;
}

124
static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr,
125
		struct omap_dss_device *dst)
126 127 128 129 130
{
	mgr->output->manager = NULL;
	mgr->output = NULL;
}

131
static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr)
132 133 134
{
}

135
/* Called only from the encoder enable/disable and suspend/resume handlers. */
136 137 138 139 140 141 142 143 144
static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
{
	struct drm_device *dev = crtc->dev;
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	enum omap_channel channel = omap_crtc->channel;
	struct omap_irq_wait *wait;
	u32 framedone_irq, vsync_irq;
	int ret;

145 146 147 148 149
	if (omap_crtc->mgr->output->output_type == OMAP_DISPLAY_TYPE_HDMI) {
		dispc_mgr_enable(channel, enable);
		return;
	}

150 151 152
	if (dispc_mgr_is_enabled(channel) == enable)
		return;

153 154 155 156 157 158 159
	if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
		/*
		 * Digit output produces some sync lost interrupts during the
		 * first frame when enabling, so we need to ignore those.
		 */
		omap_crtc->ignore_digit_sync_lost = true;
	}
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

	framedone_irq = dispc_mgr_get_framedone_irq(channel);
	vsync_irq = dispc_mgr_get_vsync_irq(channel);

	if (enable) {
		wait = omap_irq_wait_init(dev, vsync_irq, 1);
	} else {
		/*
		 * When we disable the digit output, we need to wait for
		 * FRAMEDONE to know that DISPC has finished with the output.
		 *
		 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
		 * that case we need to use vsync interrupt, and wait for both
		 * even and odd frames.
		 */

		if (framedone_irq)
			wait = omap_irq_wait_init(dev, framedone_irq, 1);
		else
			wait = omap_irq_wait_init(dev, vsync_irq, 2);
	}

	dispc_mgr_enable(channel, enable);

	ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
	if (ret) {
		dev_err(dev->dev, "%s: timeout waiting for %s\n",
				omap_crtc->name, enable ? "enable" : "disable");
	}

190 191 192 193 194
	if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
		omap_crtc->ignore_digit_sync_lost = false;
		/* make sure the irq handler sees the value above */
		mb();
	}
195 196
}

197

198
static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr)
199
{
200
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
201
	struct omap_overlay_manager_info info;
202

203 204 205 206 207 208 209
	memset(&info, 0, sizeof(info));
	info.default_color = 0x00000000;
	info.trans_key = 0x00000000;
	info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
	info.trans_enabled = false;

	dispc_mgr_setup(omap_crtc->channel, &info);
210 211
	dispc_mgr_set_timings(omap_crtc->channel,
			&omap_crtc->timings);
212
	omap_crtc_set_enabled(&omap_crtc->base, true);
213

214 215 216
	return 0;
}

217
static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr)
218
{
219 220
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];

221
	omap_crtc_set_enabled(&omap_crtc->base, false);
222 223
}

224
static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr,
225 226
		const struct omap_video_timings *timings)
{
227
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
228 229 230 231
	DBG("%s", omap_crtc->name);
	omap_crtc->timings = *timings;
}

232
static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr,
233 234
		const struct dss_lcd_mgr_config *config)
{
235
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
236 237 238 239
	DBG("%s", omap_crtc->name);
	dispc_mgr_set_lcd_config(omap_crtc->channel, config);
}

240
static int omap_crtc_dss_register_framedone(
241 242 243 244 245 246
		struct omap_overlay_manager *mgr,
		void (*handler)(void *), void *data)
{
	return 0;
}

247
static void omap_crtc_dss_unregister_framedone(
248 249 250 251 252 253
		struct omap_overlay_manager *mgr,
		void (*handler)(void *), void *data)
{
}

static const struct dss_mgr_ops mgr_ops = {
254 255 256 257 258 259 260 261 262
	.connect = omap_crtc_dss_connect,
	.disconnect = omap_crtc_dss_disconnect,
	.start_update = omap_crtc_dss_start_update,
	.enable = omap_crtc_dss_enable,
	.disable = omap_crtc_dss_disable,
	.set_timings = omap_crtc_dss_set_timings,
	.set_lcd_config = omap_crtc_dss_set_lcd_config,
	.register_framedone_handler = omap_crtc_dss_register_framedone,
	.unregister_framedone_handler = omap_crtc_dss_unregister_framedone,
263 264
};

265
/* -----------------------------------------------------------------------------
266
 * Setup, Flush and Page Flip
267 268
 */

269
static void omap_crtc_complete_page_flip(struct drm_crtc *crtc)
270
{
271
	struct drm_pending_vblank_event *event;
272
	struct drm_device *dev = crtc->dev;
273
	unsigned long flags;
274

275
	event = crtc->state->event;
276

277 278
	if (!event)
		return;
279 280 281

	spin_lock_irqsave(&dev->event_lock, flags);

282
	list_del(&event->base.link);
283

284 285 286 287 288 289 290 291
	/*
	 * Queue the event for delivery if it's still linked to a file
	 * handle, otherwise just destroy it.
	 */
	if (event->base.file_priv)
		drm_crtc_send_vblank_event(crtc, event);
	else
		event->base.destroy(&event->base);
292

293
	spin_unlock_irqrestore(&dev->event_lock, flags);
294 295
}

296 297 298 299
static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
	struct omap_crtc *omap_crtc =
			container_of(irq, struct omap_crtc, error_irq);
300 301 302 303 304 305 306

	if (omap_crtc->ignore_digit_sync_lost) {
		irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
		if (!irqstatus)
			return;
	}

307
	DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
308 309
}

310
static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
311 312
{
	struct omap_crtc *omap_crtc =
313 314
			container_of(irq, struct omap_crtc, vblank_irq);
	struct drm_device *dev = omap_crtc->base.dev;
315

316 317 318 319
	if (dispc_mgr_go_busy(omap_crtc->channel))
		return;

	DBG("%s: apply done", omap_crtc->name);
320

321 322
	__omap_irq_unregister(dev, &omap_crtc->vblank_irq);

323 324 325 326 327 328
	rmb();
	WARN_ON(!omap_crtc->pending);
	omap_crtc->pending = false;
	wmb();

	/* wake up userspace */
329
	omap_crtc_complete_page_flip(&omap_crtc->base);
330

331 332
	/* wake up omap_atomic_complete */
	wake_up(&omap_crtc->pending_wait);
333 334 335 336
}

/* -----------------------------------------------------------------------------
 * CRTC Functions
337 338
 */

339 340 341
static void omap_crtc_destroy(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
342 343 344

	DBG("%s", omap_crtc->name);

345
	WARN_ON(omap_crtc->vblank_irq.registered);
346 347
	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);

348
	drm_crtc_cleanup(crtc);
349

350 351 352
	kfree(omap_crtc);
}

353 354 355 356 357 358 359 360
static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
		const struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
{
	return true;
}

static void omap_crtc_enable(struct drm_crtc *crtc)
361 362 363
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

364
	DBG("%s", omap_crtc->name);
365

366 367 368 369 370 371 372
	rmb();
	WARN_ON(omap_crtc->pending);
	omap_crtc->pending = true;
	wmb();

	omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);

373
	drm_crtc_vblank_on(crtc);
374 375
}

376
static void omap_crtc_disable(struct drm_crtc *crtc)
377
{
378 379 380 381 382
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	DBG("%s", omap_crtc->name);

	drm_crtc_vblank_off(crtc);
383 384
}

385
static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc)
386 387
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
388
	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
389 390

	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
391 392 393 394 395
	    omap_crtc->name, mode->base.id, mode->name,
	    mode->vrefresh, mode->clock,
	    mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal,
	    mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal,
	    mode->type, mode->flags);
396 397

	copy_timings_drm_to_omap(&omap_crtc->timings, mode);
398 399
}

D
Daniel Vetter 已提交
400 401
static void omap_crtc_atomic_begin(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
402
{
403
}
404

D
Daniel Vetter 已提交
405 406
static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
407
{
408 409 410 411 412
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	WARN_ON(omap_crtc->vblank_irq.registered);

	if (dispc_mgr_is_enabled(omap_crtc->channel)) {
413

414 415
		DBG("%s: GO", omap_crtc->name);

416 417 418 419 420
		rmb();
		WARN_ON(omap_crtc->pending);
		omap_crtc->pending = true;
		wmb();

421 422 423
		dispc_mgr_go(omap_crtc->channel);
		omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
	}
424 425
}

426 427 428 429 430 431 432 433 434
static bool omap_crtc_is_plane_prop(struct drm_device *dev,
	struct drm_property *property)
{
	struct omap_drm_private *priv = dev->dev_private;

	return property == priv->zorder_prop ||
		property == dev->mode_config.rotation_property;
}

435 436 437 438
static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
					 struct drm_crtc_state *state,
					 struct drm_property *property,
					 uint64_t val)
439
{
440
	struct drm_device *dev = crtc->dev;
441

442 443 444 445 446 447 448 449
	if (omap_crtc_is_plane_prop(dev, property)) {
		struct drm_plane_state *plane_state;
		struct drm_plane *plane = crtc->primary;

		/*
		 * Delegate property set to the primary plane. Get the plane
		 * state and set the property directly.
		 */
450

451 452 453
		plane_state = drm_atomic_get_plane_state(state->state, plane);
		if (IS_ERR(plane_state))
			return PTR_ERR(plane_state);
454

455 456 457 458 459
		return drm_atomic_plane_set_property(plane, plane_state,
				property, val);
	}

	return -EINVAL;
460
}
461

462 463 464 465 466
static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
					 const struct drm_crtc_state *state,
					 struct drm_property *property,
					 uint64_t *val)
{
467 468 469 470 471 472 473 474 475 476 477 478 479 480
	struct drm_device *dev = crtc->dev;

	if (omap_crtc_is_plane_prop(dev, property)) {
		/*
		 * Delegate property get to the primary plane. The
		 * drm_atomic_plane_get_property() function isn't exported, but
		 * can be called through drm_object_property_get_value() as that
		 * will call drm_atomic_get_property() for atomic drivers.
		 */
		return drm_object_property_get_value(&crtc->primary->base,
				property, val);
	}

	return -EINVAL;
481 482
}

483
static const struct drm_crtc_funcs omap_crtc_funcs = {
484
	.reset = drm_atomic_helper_crtc_reset,
485
	.set_config = drm_atomic_helper_set_config,
486
	.destroy = omap_crtc_destroy,
487
	.page_flip = drm_atomic_helper_page_flip,
488
	.set_property = drm_atomic_helper_crtc_set_property,
489 490
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
491 492
	.atomic_set_property = omap_crtc_atomic_set_property,
	.atomic_get_property = omap_crtc_atomic_get_property,
493 494 495 496
};

static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
	.mode_fixup = omap_crtc_mode_fixup,
497
	.mode_set_nofb = omap_crtc_mode_set_nofb,
498 499
	.disable = omap_crtc_disable,
	.enable = omap_crtc_enable,
500 501
	.atomic_begin = omap_crtc_atomic_begin,
	.atomic_flush = omap_crtc_atomic_flush,
502 503
};

504 505 506
/* -----------------------------------------------------------------------------
 * Init and Cleanup
 */
507

508
static const char *channel_names[] = {
509 510 511 512
	[OMAP_DSS_CHANNEL_LCD] = "lcd",
	[OMAP_DSS_CHANNEL_DIGIT] = "tv",
	[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
	[OMAP_DSS_CHANNEL_LCD3] = "lcd3",
513 514
};

515 516 517 518 519
void omap_crtc_pre_init(void)
{
	dss_install_mgr_ops(&mgr_ops);
}

520 521 522 523 524
void omap_crtc_pre_uninit(void)
{
	dss_uninstall_mgr_ops();
}

525 526
/* initialize crtc */
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
527
		struct drm_plane *plane, enum omap_channel channel, int id)
528 529
{
	struct drm_crtc *crtc = NULL;
530
	struct omap_crtc *omap_crtc;
531
	int ret;
532 533

	DBG("%s", channel_names[channel]);
534

535
	omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
536
	if (!omap_crtc)
537
		return NULL;
538 539

	crtc = &omap_crtc->base;
540

541
	init_waitqueue_head(&omap_crtc->pending_wait);
542

543 544 545
	omap_crtc->channel = channel;
	omap_crtc->name = channel_names[channel];

546 547
	omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
	omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
548 549 550 551 552 553 554

	omap_crtc->error_irq.irqmask =
			dispc_mgr_get_sync_lost_irq(channel);
	omap_crtc->error_irq.irq = omap_crtc_error_irq;
	omap_irq_register(dev, &omap_crtc->error_irq);

	/* temporary: */
555
	omap_crtc->mgr = omap_dss_get_overlay_manager(channel);
556

557
	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
558
					&omap_crtc_funcs, NULL);
559 560 561 562 563
	if (ret < 0) {
		kfree(omap_crtc);
		return NULL;
	}

564 565
	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);

566
	omap_plane_install_properties(crtc->primary, &crtc->base);
567

568 569
	omap_crtcs[channel] = omap_crtc;

570 571
	return crtc;
}