omap_crtc.c 13.9 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 83 84 85 86 87
int omap_crtc_wait_pending(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	return wait_event_timeout(omap_crtc->pending_wait,
				  !omap_crtc->pending,
				  msecs_to_jiffies(50));
}

88 89 90 91
/* -----------------------------------------------------------------------------
 * DSS Manager Functions
 */

92 93 94 95 96 97 98 99 100
/*
 * 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
 */

101 102 103
/* ovl-mgr-id -> crtc */
static struct omap_crtc *omap_crtcs[8];

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

	if ((mgr->supported_outputs & dst->id) == 0)
		return -EINVAL;

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

	return 0;
}

120
static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr,
121
		struct omap_dss_device *dst)
122 123 124 125 126
{
	mgr->output->manager = NULL;
	mgr->output = NULL;
}

127
static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr)
128 129 130
{
}

131
/* Called only from the encoder enable/disable and suspend/resume handlers. */
132 133 134 135 136 137 138 139 140 141 142 143
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;

	if (dispc_mgr_is_enabled(channel) == enable)
		return;

144 145 146 147 148 149 150
	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;
	}
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

	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");
	}

181 182 183 184 185
	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();
	}
186 187
}

188

189
static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr)
190
{
191
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
192
	struct omap_overlay_manager_info info;
193

194 195 196 197 198 199 200
	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);
201 202
	dispc_mgr_set_timings(omap_crtc->channel,
			&omap_crtc->timings);
203
	omap_crtc_set_enabled(&omap_crtc->base, true);
204

205 206 207
	return 0;
}

208
static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr)
209
{
210 211
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];

212
	omap_crtc_set_enabled(&omap_crtc->base, false);
213 214
}

215
static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr,
216 217
		const struct omap_video_timings *timings)
{
218
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
219 220 221 222
	DBG("%s", omap_crtc->name);
	omap_crtc->timings = *timings;
}

223
static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr,
224 225
		const struct dss_lcd_mgr_config *config)
{
226
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
227 228 229 230
	DBG("%s", omap_crtc->name);
	dispc_mgr_set_lcd_config(omap_crtc->channel, config);
}

231
static int omap_crtc_dss_register_framedone(
232 233 234 235 236 237
		struct omap_overlay_manager *mgr,
		void (*handler)(void *), void *data)
{
	return 0;
}

238
static void omap_crtc_dss_unregister_framedone(
239 240 241 242 243 244
		struct omap_overlay_manager *mgr,
		void (*handler)(void *), void *data)
{
}

static const struct dss_mgr_ops mgr_ops = {
245 246 247 248 249 250 251 252 253
	.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,
254 255
};

256
/* -----------------------------------------------------------------------------
257
 * Setup, Flush and Page Flip
258 259
 */

260
static void omap_crtc_complete_page_flip(struct drm_crtc *crtc)
261
{
262
	struct drm_pending_vblank_event *event;
263
	struct drm_device *dev = crtc->dev;
264
	unsigned long flags;
265

266
	event = crtc->state->event;
267

268 269
	if (!event)
		return;
270 271 272

	spin_lock_irqsave(&dev->event_lock, flags);

273
	list_del(&event->base.link);
274

275 276 277 278 279 280 281 282
	/*
	 * 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);
283

284
	spin_unlock_irqrestore(&dev->event_lock, flags);
285 286
}

287 288 289 290
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);
291 292 293 294 295 296 297

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

298
	DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
299 300
}

301
static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
302 303
{
	struct omap_crtc *omap_crtc =
304 305
			container_of(irq, struct omap_crtc, vblank_irq);
	struct drm_device *dev = omap_crtc->base.dev;
306

307 308 309 310
	if (dispc_mgr_go_busy(omap_crtc->channel))
		return;

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

312 313
	__omap_irq_unregister(dev, &omap_crtc->vblank_irq);

314 315 316 317 318 319
	rmb();
	WARN_ON(!omap_crtc->pending);
	omap_crtc->pending = false;
	wmb();

	/* wake up userspace */
320
	omap_crtc_complete_page_flip(&omap_crtc->base);
321

322 323
	/* wake up omap_atomic_complete */
	wake_up(&omap_crtc->pending_wait);
324 325 326 327
}

/* -----------------------------------------------------------------------------
 * CRTC Functions
328 329
 */

330 331 332
static void omap_crtc_destroy(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
333 334 335

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

336
	WARN_ON(omap_crtc->vblank_irq.registered);
337 338
	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);

339
	drm_crtc_cleanup(crtc);
340

341 342 343
	kfree(omap_crtc);
}

344 345 346 347 348 349 350 351
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)
352 353 354
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

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

357 358 359 360 361 362 363
	rmb();
	WARN_ON(omap_crtc->pending);
	omap_crtc->pending = true;
	wmb();

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

364
	drm_crtc_vblank_on(crtc);
365 366
}

367
static void omap_crtc_disable(struct drm_crtc *crtc)
368
{
369 370 371 372 373
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

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

	drm_crtc_vblank_off(crtc);
374 375
}

376
static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc)
377 378
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
379
	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
380 381

	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
382 383 384 385 386
	    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);
387 388

	copy_timings_drm_to_omap(&omap_crtc->timings, mode);
389 390
}

D
Daniel Vetter 已提交
391 392
static void omap_crtc_atomic_begin(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
393
{
394
}
395

D
Daniel Vetter 已提交
396 397
static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
398
{
399 400 401 402 403
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	WARN_ON(omap_crtc->vblank_irq.registered);

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

405 406
		DBG("%s: GO", omap_crtc->name);

407 408 409 410 411
		rmb();
		WARN_ON(omap_crtc->pending);
		omap_crtc->pending = true;
		wmb();

412 413 414
		dispc_mgr_go(omap_crtc->channel);
		omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
	}
415

416 417
	crtc->invert_dimensions = !!(crtc->primary->state->rotation &
				    (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)));
418 419
}

420 421 422 423
static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
					 struct drm_crtc_state *state,
					 struct drm_property *property,
					 uint64_t val)
424
{
425 426 427 428 429 430 431 432 433 434 435 436 437 438
	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);
	if (!plane_state)
		return -EINVAL;

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

440 441 442 443 444 445 446 447 448 449 450 451 452
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);
453 454
}

455
static const struct drm_crtc_funcs omap_crtc_funcs = {
456
	.reset = drm_atomic_helper_crtc_reset,
457
	.set_config = drm_atomic_helper_set_config,
458
	.destroy = omap_crtc_destroy,
459
	.page_flip = drm_atomic_helper_page_flip,
460
	.set_property = drm_atomic_helper_crtc_set_property,
461 462
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
463 464
	.atomic_set_property = omap_crtc_atomic_set_property,
	.atomic_get_property = omap_crtc_atomic_get_property,
465 466 467 468
};

static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
	.mode_fixup = omap_crtc_mode_fixup,
469
	.mode_set_nofb = omap_crtc_mode_set_nofb,
470 471
	.disable = omap_crtc_disable,
	.enable = omap_crtc_enable,
472 473
	.atomic_begin = omap_crtc_atomic_begin,
	.atomic_flush = omap_crtc_atomic_flush,
474 475
};

476 477 478
/* -----------------------------------------------------------------------------
 * Init and Cleanup
 */
479

480
static const char *channel_names[] = {
481 482 483 484
	[OMAP_DSS_CHANNEL_LCD] = "lcd",
	[OMAP_DSS_CHANNEL_DIGIT] = "tv",
	[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
	[OMAP_DSS_CHANNEL_LCD3] = "lcd3",
485 486
};

487 488 489 490 491
void omap_crtc_pre_init(void)
{
	dss_install_mgr_ops(&mgr_ops);
}

492 493 494 495 496
void omap_crtc_pre_uninit(void)
{
	dss_uninstall_mgr_ops();
}

497 498
/* initialize crtc */
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
499
		struct drm_plane *plane, enum omap_channel channel, int id)
500 501
{
	struct drm_crtc *crtc = NULL;
502
	struct omap_crtc *omap_crtc;
503
	int ret;
504 505

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

507
	omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
508
	if (!omap_crtc)
509
		return NULL;
510 511

	crtc = &omap_crtc->base;
512

513
	init_waitqueue_head(&omap_crtc->pending_wait);
514

515 516 517
	omap_crtc->channel = channel;
	omap_crtc->name = channel_names[channel];

518 519
	omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
	omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
520 521 522 523 524 525 526

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

529 530 531 532 533 534 535
	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
					&omap_crtc_funcs);
	if (ret < 0) {
		kfree(omap_crtc);
		return NULL;
	}

536 537
	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);

538
	omap_plane_install_properties(crtc->primary, &crtc->base);
539

540 541
	omap_crtcs[channel] = omap_crtc;

542 543
	return crtc;
}