apply.c 18.0 KB
Newer Older
T
Tomi Valkeinen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/*
 * Copyright (C) 2011 Texas Instruments
 * Author: Tomi Valkeinen <tomi.valkeinen@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/>.
 */

#define DSS_SUBSYS_NAME "APPLY"

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>

#include <video/omapdss.h>

#include "dss.h"
#include "dss_features.h"

/*
 * We have 4 levels of cache for the dispc settings. First two are in SW and
 * the latter two in HW.
 *
 * +--------------------+
 * |overlay/manager_info|
 * +--------------------+
 *          v
 *        apply()
 *          v
 * +--------------------+
41
 * |       info         |
T
Tomi Valkeinen 已提交
42 43
 * +--------------------+
 *          v
44
 *      write_regs()
T
Tomi Valkeinen 已提交
45 46 47 48 49 50 51 52 53 54 55 56
 *          v
 * +--------------------+
 * |  shadow registers  |
 * +--------------------+
 *          v
 * VFP or lcd/digit_enable
 *          v
 * +--------------------+
 * |      registers     |
 * +--------------------+
 */

57
struct ovl_priv_data {
T
Tomi Valkeinen 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
	/* If true, cache changed, but not written to shadow registers. Set
	 * in apply(), cleared when registers written. */
	bool dirty;
	/* If true, shadow registers contain changed values not yet in real
	 * registers. Set when writing to shadow registers, cleared at
	 * VSYNC/EVSYNC */
	bool shadow_dirty;

	bool enabled;

	struct omap_overlay_info info;

	enum omap_channel channel;

	u32 fifo_low;
	u32 fifo_high;
};

76
struct mgr_priv_data {
T
Tomi Valkeinen 已提交
77 78 79 80 81 82 83 84 85 86 87 88
	/* If true, cache changed, but not written to shadow registers. Set
	 * in apply(), cleared when registers written. */
	bool dirty;
	/* If true, shadow registers contain changed values not yet in real
	 * registers. Set when writing to shadow registers, cleared at
	 * VSYNC/EVSYNC */
	bool shadow_dirty;

	struct omap_overlay_manager_info info;

	bool manual_update;
	bool do_manual_update;
89

90 91 92 93
	/* If true, GO bit is up and shadow registers cannot be written.
	 * Never true for manual update displays */
	bool busy;

94 95
	/* If true, a display is enabled using this manager */
	bool enabled;
T
Tomi Valkeinen 已提交
96 97 98
};

static struct {
99
	struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
100
	struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
T
Tomi Valkeinen 已提交
101 102

	bool irq_enabled;
103
} dss_data;
T
Tomi Valkeinen 已提交
104

105
/* protects dss_data */
106
static spinlock_t data_lock;
T
Tomi Valkeinen 已提交
107 108
/* lock for blocking functions */
static DEFINE_MUTEX(apply_lock);
109

110 111
static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
{
112
	return &dss_data.ovl_priv_data_array[ovl->id];
113 114
}

115 116
static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
{
117
	return &dss_data.mgr_priv_data_array[mgr->id];
118 119
}

T
Tomi Valkeinen 已提交
120 121
void dss_apply_init(void)
{
122
	spin_lock_init(&data_lock);
T
Tomi Valkeinen 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
}

static bool ovl_manual_update(struct omap_overlay *ovl)
{
	return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
}

static bool mgr_manual_update(struct omap_overlay_manager *mgr)
{
	return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
}

static int overlay_enabled(struct omap_overlay *ovl)
{
	return ovl->info.enabled && ovl->manager && ovl->manager->device;
}

int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
	unsigned long timeout = msecs_to_jiffies(500);
143
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
144 145 146 147 148 149 150 151 152 153 154
	u32 irq;
	int r;
	int i;
	struct omap_dss_device *dssdev = mgr->device;

	if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
		return 0;

	if (mgr_manual_update(mgr))
		return 0;

155
	irq = dispc_mgr_get_vsync_irq(mgr->id);
T
Tomi Valkeinen 已提交
156

157
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
158 159 160 161 162
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

163
		spin_lock_irqsave(&data_lock, flags);
164 165
		dirty = mp->dirty;
		shadow_dirty = mp->shadow_dirty;
166
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

		if (!dirty && !shadow_dirty) {
			r = 0;
			break;
		}

		/* 4 iterations is the worst case:
		 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
		 * 2 - first VSYNC, dirty = true
		 * 3 - dirty = false, shadow_dirty = true
		 * 4 - shadow_dirty = false */
		if (i++ == 3) {
			DSSERR("mgr(%d)->wait_for_go() not finishing\n",
					mgr->id);
			r = 0;
			break;
		}

		r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
		if (r == -ERESTARTSYS)
			break;

		if (r) {
			DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
			break;
		}
	}

	return r;
}

int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
{
	unsigned long timeout = msecs_to_jiffies(500);
201
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	struct omap_dss_device *dssdev;
	u32 irq;
	int r;
	int i;

	if (!ovl->manager)
		return 0;

	dssdev = ovl->manager->device;

	if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
		return 0;

	if (ovl_manual_update(ovl))
		return 0;

218
	irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
T
Tomi Valkeinen 已提交
219

220
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
221 222 223 224 225
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

226
		spin_lock_irqsave(&data_lock, flags);
227 228
		dirty = op->dirty;
		shadow_dirty = op->shadow_dirty;
229
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260

		if (!dirty && !shadow_dirty) {
			r = 0;
			break;
		}

		/* 4 iterations is the worst case:
		 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
		 * 2 - first VSYNC, dirty = true
		 * 3 - dirty = false, shadow_dirty = true
		 * 4 - shadow_dirty = false */
		if (i++ == 3) {
			DSSERR("ovl(%d)->wait_for_go() not finishing\n",
					ovl->id);
			r = 0;
			break;
		}

		r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
		if (r == -ERESTARTSYS)
			break;

		if (r) {
			DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
			break;
		}
	}

	return r;
}

261
static int dss_ovl_write_regs(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
262
{
263
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
264 265 266 267
	struct omap_overlay_info *oi;
	bool ilace, replication;
	int r;

268
	DSSDBGF("%d", ovl->id);
T
Tomi Valkeinen 已提交
269

270 271
	op = get_ovl_priv(ovl);
	oi = &op->info;
T
Tomi Valkeinen 已提交
272

273
	if (!op->enabled) {
274
		dispc_ovl_enable(ovl->id, 0);
T
Tomi Valkeinen 已提交
275 276 277 278 279 280 281
		return 0;
	}

	replication = dss_use_replication(ovl->manager->device, oi->color_mode);

	ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;

282
	dispc_ovl_set_channel_out(ovl->id, op->channel);
T
Tomi Valkeinen 已提交
283

284
	r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
T
Tomi Valkeinen 已提交
285 286
	if (r) {
		/* this shouldn't happen */
287 288
		DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
		dispc_ovl_enable(ovl->id, 0);
T
Tomi Valkeinen 已提交
289 290 291
		return r;
	}

292
	dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
T
Tomi Valkeinen 已提交
293

294
	dispc_ovl_enable(ovl->id, 1);
T
Tomi Valkeinen 已提交
295 296 297 298

	return 0;
}

299
static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
300
{
301
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
302 303
	struct omap_overlay_manager_info *mi;

304
	DSSDBGF("%d", mgr->id);
T
Tomi Valkeinen 已提交
305

306 307
	mp = get_mgr_priv(mgr);
	mi = &mp->info;
T
Tomi Valkeinen 已提交
308

309
	dispc_mgr_setup(mgr->id, mi);
T
Tomi Valkeinen 已提交
310 311
}

312
/* dss_write_regs() tries to write values from cache to shadow registers.
T
Tomi Valkeinen 已提交
313 314 315
 * It writes only to those managers/overlays that are not busy.
 * returns 0 if everything could be written to shadow registers.
 * returns 1 if not everything could be written to shadow registers. */
316
static int dss_write_regs(void)
T
Tomi Valkeinen 已提交
317
{
318 319
	struct omap_overlay *ovl;
	struct omap_overlay_manager *mgr;
320
	struct ovl_priv_data *op;
321
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
322 323 324 325
	const int num_ovls = dss_feat_get_num_ovls();
	const int num_mgrs = dss_feat_get_num_mgrs();
	int i;
	int r;
326
	bool mgr_go[MAX_DSS_MANAGERS] = { false };
T
Tomi Valkeinen 已提交
327 328 329 330 331 332 333
	bool busy;

	r = 0;
	busy = false;

	/* Commit overlay settings */
	for (i = 0; i < num_ovls; ++i) {
334
		ovl = omap_dss_get_overlay(i);
335
		op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
336

337
		if (!op->dirty)
T
Tomi Valkeinen 已提交
338 339
			continue;

340 341 342
		mp = get_mgr_priv(ovl->manager);

		if (mp->manual_update && !mp->do_manual_update)
T
Tomi Valkeinen 已提交
343 344
			continue;

345
		if (mp->busy) {
T
Tomi Valkeinen 已提交
346 347 348 349
			busy = true;
			continue;
		}

350
		r = dss_ovl_write_regs(ovl);
T
Tomi Valkeinen 已提交
351
		if (r)
352
			DSSERR("dss_ovl_write_regs %d failed\n", i);
T
Tomi Valkeinen 已提交
353

354 355 356
		op->dirty = false;
		op->shadow_dirty = true;
		mgr_go[op->channel] = true;
T
Tomi Valkeinen 已提交
357 358 359 360
	}

	/* Commit manager settings */
	for (i = 0; i < num_mgrs; ++i) {
361
		mgr = omap_dss_get_overlay_manager(i);
362
		mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
363

364
		if (!mp->dirty)
T
Tomi Valkeinen 已提交
365 366
			continue;

367
		if (mp->manual_update && !mp->do_manual_update)
T
Tomi Valkeinen 已提交
368 369
			continue;

370
		if (mp->busy) {
T
Tomi Valkeinen 已提交
371 372 373 374
			busy = true;
			continue;
		}

375
		dss_mgr_write_regs(mgr);
376 377
		mp->dirty = false;
		mp->shadow_dirty = true;
T
Tomi Valkeinen 已提交
378 379 380 381 382
		mgr_go[i] = true;
	}

	/* set GO */
	for (i = 0; i < num_mgrs; ++i) {
383 384
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
385 386 387 388 389 390 391

		if (!mgr_go[i])
			continue;

		/* We don't need GO with manual update display. LCD iface will
		 * always be turned off after frame, and new settings will be
		 * taken in to use at next update */
392 393
		if (!mp->manual_update) {
			mp->busy = true;
T
Tomi Valkeinen 已提交
394
			dispc_mgr_go(i);
395
		}
T
Tomi Valkeinen 已提交
396 397 398 399 400 401 402 403 404 405 406 407
	}

	if (busy)
		r = 1;
	else
		r = 0;

	return r;
}

void dss_mgr_start_update(struct omap_overlay_manager *mgr)
{
408
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
409
	struct ovl_priv_data *op;
410
	struct omap_overlay *ovl;
411 412 413
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);
T
Tomi Valkeinen 已提交
414

415
	mp->do_manual_update = true;
416
	dss_write_regs();
417
	mp->do_manual_update = false;
T
Tomi Valkeinen 已提交
418

419
	list_for_each_entry(ovl, &mgr->overlays, list) {
420 421
		op = get_ovl_priv(ovl);
		op->shadow_dirty = false;
T
Tomi Valkeinen 已提交
422 423
	}

424
	mp->shadow_dirty = false;
T
Tomi Valkeinen 已提交
425

426
	dispc_mgr_enable(mgr->id, true);
427 428

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
429 430
}

431 432 433 434
static void dss_apply_irq_handler(void *data, u32 mask);

static void dss_register_vsync_isr(void)
{
435
	const int num_mgrs = dss_feat_get_num_mgrs();
436
	u32 mask;
437
	int r, i;
438

439 440 441
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
442 443 444 445

	r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
	WARN_ON(r);

446
	dss_data.irq_enabled = true;
447 448 449 450
}

static void dss_unregister_vsync_isr(void)
{
451
	const int num_mgrs = dss_feat_get_num_mgrs();
452
	u32 mask;
453
	int r, i;
454

455 456 457
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
458 459 460 461

	r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
	WARN_ON(r);

462
	dss_data.irq_enabled = false;
463 464
}

T
Tomi Valkeinen 已提交
465 466
static void dss_apply_irq_handler(void *data, u32 mask)
{
467
	struct omap_overlay *ovl;
468 469
	struct omap_overlay_manager *mgr;
	struct mgr_priv_data *mp;
470
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
471 472 473 474
	const int num_ovls = dss_feat_get_num_ovls();
	const int num_mgrs = dss_feat_get_num_mgrs();
	int i, r;

475
	spin_lock(&data_lock);
T
Tomi Valkeinen 已提交
476

477 478 479 480 481 482 483
	for (i = 0; i < num_mgrs; i++) {
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);

		mp->busy = dispc_mgr_go_busy(i);
	}

T
Tomi Valkeinen 已提交
484
	for (i = 0; i < num_ovls; ++i) {
485 486
		ovl = omap_dss_get_overlay(i);
		op = get_ovl_priv(ovl);
487 488 489 490 491 492 493

		if (!op->enabled)
			continue;

		mp = get_mgr_priv(ovl->manager);

		if (!mp->busy)
494
			op->shadow_dirty = false;
T
Tomi Valkeinen 已提交
495 496 497
	}

	for (i = 0; i < num_mgrs; ++i) {
498 499
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
500 501

		if (!mp->busy)
502
			mp->shadow_dirty = false;
T
Tomi Valkeinen 已提交
503 504
	}

505
	r = dss_write_regs();
T
Tomi Valkeinen 已提交
506 507 508 509
	if (r == 1)
		goto end;

	/* re-read busy flags */
510 511 512 513 514 515
	for (i = 0; i < num_mgrs; i++) {
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);

		mp->busy = dispc_mgr_go_busy(i);
	}
T
Tomi Valkeinen 已提交
516 517 518 519

	/* keep running as long as there are busy managers, so that
	 * we can collect overlay-applied information */
	for (i = 0; i < num_mgrs; ++i) {
520 521 522 523
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);

		if (mp->busy)
T
Tomi Valkeinen 已提交
524 525 526
			goto end;
	}

527
	dss_unregister_vsync_isr();
T
Tomi Valkeinen 已提交
528 529

end:
530
	spin_unlock(&data_lock);
T
Tomi Valkeinen 已提交
531 532
}

533
static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
534
{
535
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
536

537
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
538 539 540 541 542 543 544

	if (ovl->manager_changed) {
		ovl->manager_changed = false;
		ovl->info_dirty  = true;
	}

	if (!overlay_enabled(ovl)) {
545 546 547
		if (op->enabled) {
			op->enabled = false;
			op->dirty = true;
T
Tomi Valkeinen 已提交
548
		}
549
		return;
T
Tomi Valkeinen 已提交
550 551 552
	}

	if (!ovl->info_dirty)
553
		return;
T
Tomi Valkeinen 已提交
554 555

	ovl->info_dirty = false;
556 557
	op->dirty = true;
	op->info = ovl->info;
T
Tomi Valkeinen 已提交
558

559
	op->channel = ovl->manager->id;
T
Tomi Valkeinen 已提交
560

561
	op->enabled = true;
T
Tomi Valkeinen 已提交
562 563 564 565
}

static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
{
566
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
567

568
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581

	if (mgr->device_changed) {
		mgr->device_changed = false;
		mgr->info_dirty  = true;
	}

	if (!mgr->info_dirty)
		return;

	if (!mgr->device)
		return;

	mgr->info_dirty = false;
582 583
	mp->dirty = true;
	mp->info = mgr->info;
T
Tomi Valkeinen 已提交
584

585
	mp->manual_update = mgr_manual_update(mgr);
T
Tomi Valkeinen 已提交
586 587 588 589
}

static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
{
590
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
591 592 593
	struct omap_dss_device *dssdev;
	u32 size, burst_size;

594
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
595

596
	if (!op->enabled)
T
Tomi Valkeinen 已提交
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
		return;

	dssdev = ovl->manager->device;

	size = dispc_ovl_get_fifo_size(ovl->id);

	burst_size = dispc_ovl_get_burst_size(ovl->id);

	switch (dssdev->type) {
	case OMAP_DISPLAY_TYPE_DPI:
	case OMAP_DISPLAY_TYPE_DBI:
	case OMAP_DISPLAY_TYPE_SDI:
	case OMAP_DISPLAY_TYPE_VENC:
	case OMAP_DISPLAY_TYPE_HDMI:
		default_get_overlay_fifo_thresholds(ovl->id, size,
612 613
				burst_size, &op->fifo_low,
				&op->fifo_high);
T
Tomi Valkeinen 已提交
614 615 616 617
		break;
#ifdef CONFIG_OMAP2_DSS_DSI
	case OMAP_DISPLAY_TYPE_DSI:
		dsi_get_overlay_fifo_thresholds(ovl->id, size,
618 619
				burst_size, &op->fifo_low,
				&op->fifo_high);
T
Tomi Valkeinen 已提交
620 621 622 623 624 625 626 627 628
		break;
#endif
	default:
		BUG();
	}
}

int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
629
	int r;
T
Tomi Valkeinen 已提交
630
	unsigned long flags;
631
	struct omap_overlay *ovl;
632
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
633 634 635 636 637 638 639

	DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);

	r = dispc_runtime_get();
	if (r)
		return r;

640
	spin_lock_irqsave(&data_lock, flags);
T
Tomi Valkeinen 已提交
641 642

	/* Configure overlays */
643
	list_for_each_entry(ovl, &mgr->overlays, list)
T
Tomi Valkeinen 已提交
644 645 646 647 648 649
		omap_dss_mgr_apply_ovl(ovl);

	/* Configure manager */
	omap_dss_mgr_apply_mgr(mgr);

	/* Configure overlay fifos */
650
	list_for_each_entry(ovl, &mgr->overlays, list)
T
Tomi Valkeinen 已提交
651 652 653
		omap_dss_mgr_apply_ovl_fifos(ovl);

	r = 0;
654
	if (mp->enabled && !mgr_manual_update(mgr)) {
655
		if (!dss_data.irq_enabled)
656
			dss_register_vsync_isr();
T
Tomi Valkeinen 已提交
657

658
		dss_write_regs();
659
	}
T
Tomi Valkeinen 已提交
660

661
	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
662 663 664 665 666 667

	dispc_runtime_put();

	return r;
}

668 669
void dss_mgr_enable(struct omap_overlay_manager *mgr)
{
670 671 672
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
673 674
	mutex_lock(&apply_lock);

675 676
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, true);
677 678 679 680 681 682

	spin_lock_irqsave(&data_lock, flags);

	mp->enabled = true;

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
683 684

	mutex_unlock(&apply_lock);
685 686 687 688
}

void dss_mgr_disable(struct omap_overlay_manager *mgr)
{
689 690 691
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
692 693
	mutex_lock(&apply_lock);

694 695
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, false);
696 697 698 699 700 701

	spin_lock_irqsave(&data_lock, flags);

	mp->enabled = false;

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
702 703

	mutex_unlock(&apply_lock);
704 705
}

706 707 708
int dss_mgr_set_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
709 710 711 712
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

713 714 715
	mgr->info = *info;
	mgr->info_dirty = true;

716 717
	spin_unlock_irqrestore(&data_lock, flags);

718 719 720 721 722 723
	return 0;
}

void dss_mgr_get_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
724 725 726 727
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

728
	*info = mgr->info;
729 730

	spin_unlock_irqrestore(&data_lock, flags);
731 732 733 734 735 736 737
}

int dss_mgr_set_device(struct omap_overlay_manager *mgr,
		struct omap_dss_device *dssdev)
{
	int r;

T
Tomi Valkeinen 已提交
738 739
	mutex_lock(&apply_lock);

740 741 742
	if (dssdev->manager) {
		DSSERR("display '%s' already has a manager '%s'\n",
			       dssdev->name, dssdev->manager->name);
T
Tomi Valkeinen 已提交
743 744
		r = -EINVAL;
		goto err;
745 746 747 748 749
	}

	if ((mgr->supported_displays & dssdev->type) == 0) {
		DSSERR("display '%s' does not support manager '%s'\n",
			       dssdev->name, mgr->name);
T
Tomi Valkeinen 已提交
750 751
		r = -EINVAL;
		goto err;
752 753 754 755 756 757
	}

	dssdev->manager = mgr;
	mgr->device = dssdev;
	mgr->device_changed = true;

T
Tomi Valkeinen 已提交
758 759
	mutex_unlock(&apply_lock);

760
	return 0;
T
Tomi Valkeinen 已提交
761 762 763
err:
	mutex_unlock(&apply_lock);
	return r;
764 765 766 767
}

int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
{
T
Tomi Valkeinen 已提交
768 769 770 771
	int r;

	mutex_lock(&apply_lock);

772 773
	if (!mgr->device) {
		DSSERR("failed to unset display, display not set.\n");
T
Tomi Valkeinen 已提交
774 775
		r = -EINVAL;
		goto err;
776 777 778 779 780 781
	}

	/*
	 * Don't allow currently enabled displays to have the overlay manager
	 * pulled out from underneath them
	 */
T
Tomi Valkeinen 已提交
782 783 784 785
	if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
		r = -EINVAL;
		goto err;
	}
786 787 788 789 790

	mgr->device->manager = NULL;
	mgr->device = NULL;
	mgr->device_changed = true;

T
Tomi Valkeinen 已提交
791 792
	mutex_unlock(&apply_lock);

793
	return 0;
T
Tomi Valkeinen 已提交
794 795 796
err:
	mutex_unlock(&apply_lock);
	return r;
797 798 799 800
}



801 802 803
int dss_ovl_set_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
804 805 806 807
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

808 809 810
	ovl->info = *info;
	ovl->info_dirty = true;

811 812
	spin_unlock_irqrestore(&data_lock, flags);

813 814 815 816 817 818
	return 0;
}

void dss_ovl_get_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
819 820 821 822
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

823
	*info = ovl->info;
824 825

	spin_unlock_irqrestore(&data_lock, flags);
826 827 828 829 830
}

int dss_ovl_set_manager(struct omap_overlay *ovl,
		struct omap_overlay_manager *mgr)
{
T
Tomi Valkeinen 已提交
831 832
	int r;

833 834 835
	if (!mgr)
		return -EINVAL;

T
Tomi Valkeinen 已提交
836 837
	mutex_lock(&apply_lock);

838 839 840
	if (ovl->manager) {
		DSSERR("overlay '%s' already has a manager '%s'\n",
				ovl->name, ovl->manager->name);
T
Tomi Valkeinen 已提交
841 842
		r = -EINVAL;
		goto err;
843 844 845 846
	}

	if (ovl->info.enabled) {
		DSSERR("overlay has to be disabled to change the manager\n");
T
Tomi Valkeinen 已提交
847 848
		r = -EINVAL;
		goto err;
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
	}

	ovl->manager = mgr;
	list_add_tail(&ovl->list, &mgr->overlays);
	ovl->manager_changed = true;

	/* XXX: When there is an overlay on a DSI manual update display, and
	 * the overlay is first disabled, then moved to tv, and enabled, we
	 * seem to get SYNC_LOST_DIGIT error.
	 *
	 * Waiting doesn't seem to help, but updating the manual update display
	 * after disabling the overlay seems to fix this. This hints that the
	 * overlay is perhaps somehow tied to the LCD output until the output
	 * is updated.
	 *
	 * Userspace workaround for this is to update the LCD after disabling
	 * the overlay, but before moving the overlay to TV.
	 */

T
Tomi Valkeinen 已提交
868 869
	mutex_unlock(&apply_lock);

870
	return 0;
T
Tomi Valkeinen 已提交
871 872 873
err:
	mutex_unlock(&apply_lock);
	return r;
874 875 876 877
}

int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
T
Tomi Valkeinen 已提交
878 879 880 881
	int r;

	mutex_lock(&apply_lock);

882 883
	if (!ovl->manager) {
		DSSERR("failed to detach overlay: manager not set\n");
T
Tomi Valkeinen 已提交
884 885
		r = -EINVAL;
		goto err;
886 887 888 889
	}

	if (ovl->info.enabled) {
		DSSERR("overlay has to be disabled to unset the manager\n");
T
Tomi Valkeinen 已提交
890 891
		r = -EINVAL;
		goto err;
892 893 894 895 896 897
	}

	ovl->manager = NULL;
	list_del(&ovl->list);
	ovl->manager_changed = true;

T
Tomi Valkeinen 已提交
898 899
	mutex_unlock(&apply_lock);

900
	return 0;
T
Tomi Valkeinen 已提交
901 902 903
err:
	mutex_unlock(&apply_lock);
	return r;
904 905
}