omap_crtc.c 14.1 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 115 116 117 118 119 120 121 122 123
{
	if (mgr->output)
		return -EINVAL;

	if ((mgr->supported_outputs & dst->id) == 0)
		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
static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
					 struct drm_crtc_state *state,
					 struct drm_property *property,
					 uint64_t val)
430
{
431 432 433 434 435 436 437 438 439
	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.
	 */

	plane_state = drm_atomic_get_plane_state(state->state, plane);
440 441
	if (IS_ERR(plane_state))
		return PTR_ERR(plane_state);
442 443 444

	return drm_atomic_plane_set_property(plane, plane_state, property, val);
}
445

446 447 448 449 450 451 452 453 454 455 456 457 458
static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
					 const struct drm_crtc_state *state,
					 struct drm_property *property,
					 uint64_t *val)
{
	/*
	 * 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);
459 460
}

461
static const struct drm_crtc_funcs omap_crtc_funcs = {
462
	.reset = drm_atomic_helper_crtc_reset,
463
	.set_config = drm_atomic_helper_set_config,
464
	.destroy = omap_crtc_destroy,
465
	.page_flip = drm_atomic_helper_page_flip,
466
	.set_property = drm_atomic_helper_crtc_set_property,
467 468
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
469 470
	.atomic_set_property = omap_crtc_atomic_set_property,
	.atomic_get_property = omap_crtc_atomic_get_property,
471 472 473 474
};

static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
	.mode_fixup = omap_crtc_mode_fixup,
475
	.mode_set_nofb = omap_crtc_mode_set_nofb,
476 477
	.disable = omap_crtc_disable,
	.enable = omap_crtc_enable,
478 479
	.atomic_begin = omap_crtc_atomic_begin,
	.atomic_flush = omap_crtc_atomic_flush,
480 481
};

482 483 484
/* -----------------------------------------------------------------------------
 * Init and Cleanup
 */
485

486
static const char *channel_names[] = {
487 488 489 490
	[OMAP_DSS_CHANNEL_LCD] = "lcd",
	[OMAP_DSS_CHANNEL_DIGIT] = "tv",
	[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
	[OMAP_DSS_CHANNEL_LCD3] = "lcd3",
491 492
};

493 494 495 496 497
void omap_crtc_pre_init(void)
{
	dss_install_mgr_ops(&mgr_ops);
}

498 499 500 501 502
void omap_crtc_pre_uninit(void)
{
	dss_uninstall_mgr_ops();
}

503 504
/* initialize crtc */
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
505
		struct drm_plane *plane, enum omap_channel channel, int id)
506 507
{
	struct drm_crtc *crtc = NULL;
508
	struct omap_crtc *omap_crtc;
509
	int ret;
510 511

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

513
	omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
514
	if (!omap_crtc)
515
		return NULL;
516 517

	crtc = &omap_crtc->base;
518

519
	init_waitqueue_head(&omap_crtc->pending_wait);
520

521 522 523
	omap_crtc->channel = channel;
	omap_crtc->name = channel_names[channel];

524 525
	omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
	omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
526 527 528 529 530 531 532

	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: */
533
	omap_crtc->mgr = omap_dss_get_overlay_manager(channel);
534

535
	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
536
					&omap_crtc_funcs, NULL);
537 538 539 540 541
	if (ret < 0) {
		kfree(omap_crtc);
		return NULL;
	}

542 543
	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);

544
	omap_plane_install_properties(crtc->primary, &crtc->base);
545

546 547
	omap_crtcs[channel] = omap_crtc;

548 549
	return crtc;
}