apply.c 19.7 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
	/* 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_info info;

	enum omap_channel channel;

	u32 fifo_low;
	u32 fifo_high;
72 73 74 75 76 77

	bool extra_info_dirty;
	bool shadow_extra_info_dirty;

	bool enabled;

T
Tomi Valkeinen 已提交
78 79
};

80
struct mgr_priv_data {
T
Tomi Valkeinen 已提交
81 82 83 84 85 86 87 88 89 90
	/* 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;

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

T
Tomi Valkeinen 已提交
95 96 97
	/* If true, dispc output is enabled */
	bool updating;

98 99
	/* If true, a display is enabled using this manager */
	bool enabled;
T
Tomi Valkeinen 已提交
100 101 102
};

static struct {
103
	struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
104
	struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
T
Tomi Valkeinen 已提交
105 106

	bool irq_enabled;
107
} dss_data;
T
Tomi Valkeinen 已提交
108

109
/* protects dss_data */
110
static spinlock_t data_lock;
T
Tomi Valkeinen 已提交
111 112
/* lock for blocking functions */
static DEFINE_MUTEX(apply_lock);
113

114 115
static void dss_register_vsync_isr(void);

116 117
static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
{
118
	return &dss_data.ovl_priv_data_array[ovl->id];
119 120
}

121 122
static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
{
123
	return &dss_data.mgr_priv_data_array[mgr->id];
124 125
}

T
Tomi Valkeinen 已提交
126 127
void dss_apply_init(void)
{
128
	spin_lock_init(&data_lock);
T
Tomi Valkeinen 已提交
129 130 131 132 133 134 135 136 137 138 139 140
}

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

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
static bool need_isr(void)
{
	const int num_mgrs = dss_feat_get_num_mgrs();
	int i;

	for (i = 0; i < num_mgrs; ++i) {
		struct omap_overlay_manager *mgr;
		struct mgr_priv_data *mp;
		struct omap_overlay *ovl;

		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);

		if (!mp->enabled)
			continue;

T
Tomi Valkeinen 已提交
157 158 159 160 161 162 163 164
		if (mgr_manual_update(mgr)) {
			/* to catch FRAMEDONE */
			if (mp->updating)
				return true;
		} else {
			/* to catch GO bit going down */
			if (mp->busy)
				return true;
165

T
Tomi Valkeinen 已提交
166 167 168
			/* to write new values to registers */
			if (mp->dirty)
				return true;
169

T
Tomi Valkeinen 已提交
170 171
			list_for_each_entry(ovl, &mgr->overlays, list) {
				struct ovl_priv_data *op;
172

T
Tomi Valkeinen 已提交
173
				op = get_ovl_priv(ovl);
174

T
Tomi Valkeinen 已提交
175 176
				if (!op->enabled)
					continue;
177

T
Tomi Valkeinen 已提交
178 179 180 181
				/* to write new values to registers */
				if (op->dirty || op->extra_info_dirty)
					return true;
			}
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
		}
	}

	return false;
}

static bool need_go(struct omap_overlay_manager *mgr)
{
	struct omap_overlay *ovl;
	struct mgr_priv_data *mp;
	struct ovl_priv_data *op;

	mp = get_mgr_priv(mgr);

	if (mp->shadow_dirty)
		return true;

	list_for_each_entry(ovl, &mgr->overlays, list) {
		op = get_ovl_priv(ovl);
		if (op->shadow_dirty || op->shadow_extra_info_dirty)
			return true;
	}

	return false;
}

T
Tomi Valkeinen 已提交
208 209 210
int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
	unsigned long timeout = msecs_to_jiffies(500);
211
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
212 213 214 215 216 217 218 219 220 221 222
	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;

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

225
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
226 227 228 229 230
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

231
		spin_lock_irqsave(&data_lock, flags);
232 233
		dirty = mp->dirty;
		shadow_dirty = mp->shadow_dirty;
234
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
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 261 262 263 264 265 266 267 268

		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);
269
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	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;

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

288
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
289 290 291 292 293
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

294
		spin_lock_irqsave(&data_lock, flags);
295 296
		dirty = op->dirty;
		shadow_dirty = op->shadow_dirty;
297
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

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

329
static void dss_ovl_write_regs(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
330
{
331
	struct ovl_priv_data *op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
332 333
	struct omap_overlay_info *oi;
	bool ilace, replication;
T
Tomi Valkeinen 已提交
334
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
335 336
	int r;

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

339 340
	if (!op->enabled || !op->dirty)
		return;
T
Tomi Valkeinen 已提交
341

342
	oi = &op->info;
T
Tomi Valkeinen 已提交
343 344 345 346 347

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

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

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

350
	r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
T
Tomi Valkeinen 已提交
351
	if (r) {
352 353 354 355
		/*
		 * We can't do much here, as this function can be called from
		 * vsync interrupt.
		 */
356
		DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
357 358 359 360 361

		/* This will leave fifo configurations in a nonoptimal state */
		op->enabled = false;
		dispc_ovl_enable(ovl->id, false);
		return;
T
Tomi Valkeinen 已提交
362 363
	}

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

T
Tomi Valkeinen 已提交
366 367
	mp = get_mgr_priv(ovl->manager);

368
	op->dirty = false;
T
Tomi Valkeinen 已提交
369 370
	if (mp->updating)
		op->shadow_dirty = true;
T
Tomi Valkeinen 已提交
371 372
}

373 374 375
static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
376
	struct mgr_priv_data *mp;
377 378 379

	DSSDBGF("%d", ovl->id);

380 381 382
	if (!op->extra_info_dirty)
		return;

383 384 385 386
	/* note: write also when op->enabled == false, so that the ovl gets
	 * disabled */

	dispc_ovl_enable(ovl->id, op->enabled);
387

T
Tomi Valkeinen 已提交
388 389
	mp = get_mgr_priv(ovl->manager);

390
	op->extra_info_dirty = false;
T
Tomi Valkeinen 已提交
391 392
	if (mp->updating)
		op->shadow_extra_info_dirty = true;
393 394
}

395
static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
396
{
397 398
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	struct omap_overlay *ovl;
T
Tomi Valkeinen 已提交
399

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

402 403
	if (!mp->enabled)
		return;
T
Tomi Valkeinen 已提交
404

405
	WARN_ON(mp->busy);
T
Tomi Valkeinen 已提交
406 407

	/* Commit overlay settings */
408 409
	list_for_each_entry(ovl, &mgr->overlays, list) {
		dss_ovl_write_regs(ovl);
410 411 412
		dss_ovl_write_regs_extra(ovl);
	}

413 414
	if (mp->dirty) {
		dispc_mgr_setup(mgr->id, &mp->info);
T
Tomi Valkeinen 已提交
415

416
		mp->dirty = false;
T
Tomi Valkeinen 已提交
417 418
		if (mp->updating)
			mp->shadow_dirty = true;
T
Tomi Valkeinen 已提交
419
	}
420 421 422 423 424 425
}

static void dss_write_regs(void)
{
	const int num_mgrs = omap_dss_get_num_overlay_managers();
	int i;
T
Tomi Valkeinen 已提交
426 427

	for (i = 0; i < num_mgrs; ++i) {
428 429 430
		struct omap_overlay_manager *mgr;
		struct mgr_priv_data *mp;

431 432
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
433

434
		if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
T
Tomi Valkeinen 已提交
435 436
			continue;

437 438 439
		dss_mgr_write_regs(mgr);

		if (need_go(mgr)) {
440
			mp->busy = true;
T
Tomi Valkeinen 已提交
441

442 443
			if (!dss_data.irq_enabled && need_isr())
				dss_register_vsync_isr();
T
Tomi Valkeinen 已提交
444

445 446 447
			dispc_mgr_go(mgr->id);
		}
	}
T
Tomi Valkeinen 已提交
448 449 450 451
}

void dss_mgr_start_update(struct omap_overlay_manager *mgr)
{
452
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
453 454 455
	unsigned long flags;

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

T
Tomi Valkeinen 已提交
457 458
	WARN_ON(mp->updating);

459
	dss_mgr_write_regs(mgr);
T
Tomi Valkeinen 已提交
460

T
Tomi Valkeinen 已提交
461
	mp->updating = true;
T
Tomi Valkeinen 已提交
462

T
Tomi Valkeinen 已提交
463 464
	if (!dss_data.irq_enabled && need_isr())
		dss_register_vsync_isr();
T
Tomi Valkeinen 已提交
465

466
	dispc_mgr_enable(mgr->id, true);
467 468

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
469 470
}

471 472 473 474
static void dss_apply_irq_handler(void *data, u32 mask);

static void dss_register_vsync_isr(void)
{
475
	const int num_mgrs = dss_feat_get_num_mgrs();
476
	u32 mask;
477
	int r, i;
478

479 480 481
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
482

T
Tomi Valkeinen 已提交
483 484 485
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_framedone_irq(i);

486 487 488
	r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
	WARN_ON(r);

489
	dss_data.irq_enabled = true;
490 491 492 493
}

static void dss_unregister_vsync_isr(void)
{
494
	const int num_mgrs = dss_feat_get_num_mgrs();
495
	u32 mask;
496
	int r, i;
497

498 499 500
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
501

T
Tomi Valkeinen 已提交
502 503 504
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_framedone_irq(i);

505 506 507
	r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
	WARN_ON(r);

508
	dss_data.irq_enabled = false;
509 510
}

T
Tomi Valkeinen 已提交
511 512
static void dss_apply_irq_handler(void *data, u32 mask)
{
513
	struct omap_overlay *ovl;
514 515
	struct omap_overlay_manager *mgr;
	struct mgr_priv_data *mp;
516
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
517 518
	const int num_ovls = dss_feat_get_num_ovls();
	const int num_mgrs = dss_feat_get_num_mgrs();
519
	int i;
T
Tomi Valkeinen 已提交
520

521
	spin_lock(&data_lock);
T
Tomi Valkeinen 已提交
522

523 524 525 526 527
	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 已提交
528
		mp->updating = dispc_mgr_is_enabled(i);
529 530
	}

T
Tomi Valkeinen 已提交
531
	for (i = 0; i < num_ovls; ++i) {
532 533
		ovl = omap_dss_get_overlay(i);
		op = get_ovl_priv(ovl);
534 535 536 537 538 539

		if (!op->enabled)
			continue;

		mp = get_mgr_priv(ovl->manager);

540
		if (!mp->busy) {
541
			op->shadow_dirty = false;
542 543
			op->shadow_extra_info_dirty = false;
		}
T
Tomi Valkeinen 已提交
544 545 546
	}

	for (i = 0; i < num_mgrs; ++i) {
547 548
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
549 550

		if (!mp->busy)
551
			mp->shadow_dirty = false;
T
Tomi Valkeinen 已提交
552 553
	}

554
	dss_write_regs();
T
Tomi Valkeinen 已提交
555

556 557
	if (!need_isr())
		dss_unregister_vsync_isr();
T
Tomi Valkeinen 已提交
558

559
	spin_unlock(&data_lock);
T
Tomi Valkeinen 已提交
560 561
}

562
static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
563
{
564
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
565

566
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
567 568 569 570 571 572 573

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

	if (!ovl->info_dirty)
574
		return;
T
Tomi Valkeinen 已提交
575 576

	ovl->info_dirty = false;
577 578
	op->dirty = true;
	op->info = ovl->info;
T
Tomi Valkeinen 已提交
579

580
	op->channel = ovl->manager->id;
T
Tomi Valkeinen 已提交
581 582 583 584
}

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

587
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
588 589 590 591 592 593 594 595 596 597

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

	if (!mgr->info_dirty)
		return;

	mgr->info_dirty = false;
598 599
	mp->dirty = true;
	mp->info = mgr->info;
T
Tomi Valkeinen 已提交
600 601 602 603
}

static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
{
604
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
605 606 607
	struct omap_dss_device *dssdev;
	u32 size, burst_size;

608
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
609 610 611 612 613 614 615 616 617 618 619 620 621 622

	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,
623 624
				burst_size, &op->fifo_low,
				&op->fifo_high);
T
Tomi Valkeinen 已提交
625 626 627 628
		break;
#ifdef CONFIG_OMAP2_DSS_DSI
	case OMAP_DISPLAY_TYPE_DSI:
		dsi_get_overlay_fifo_thresholds(ovl->id, size,
629 630
				burst_size, &op->fifo_low,
				&op->fifo_high);
T
Tomi Valkeinen 已提交
631 632 633 634 635 636 637 638 639
		break;
#endif
	default:
		BUG();
	}
}

int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
640
	int r;
T
Tomi Valkeinen 已提交
641
	unsigned long flags;
642
	struct omap_overlay *ovl;
T
Tomi Valkeinen 已提交
643 644 645 646 647 648 649

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

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

650
	spin_lock_irqsave(&data_lock, flags);
T
Tomi Valkeinen 已提交
651 652

	/* Configure overlays */
653
	list_for_each_entry(ovl, &mgr->overlays, list)
T
Tomi Valkeinen 已提交
654 655 656 657 658 659
		omap_dss_mgr_apply_ovl(ovl);

	/* Configure manager */
	omap_dss_mgr_apply_mgr(mgr);

	/* Configure overlay fifos */
660
	list_for_each_entry(ovl, &mgr->overlays, list)
T
Tomi Valkeinen 已提交
661 662
		omap_dss_mgr_apply_ovl_fifos(ovl);

663
	dss_write_regs();
T
Tomi Valkeinen 已提交
664

665
	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
666 667 668 669 670 671

	dispc_runtime_put();

	return r;
}

672 673
void dss_mgr_enable(struct omap_overlay_manager *mgr)
{
674 675 676
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
677 678
	mutex_lock(&apply_lock);

679 680 681 682
	spin_lock_irqsave(&data_lock, flags);

	mp->enabled = true;

683 684
	dss_write_regs();

T
Tomi Valkeinen 已提交
685 686 687
	if (!mgr_manual_update(mgr))
		mp->updating = true;

688
	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
689

690 691 692
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, true);

T
Tomi Valkeinen 已提交
693
	mutex_unlock(&apply_lock);
694 695 696 697
}

void dss_mgr_disable(struct omap_overlay_manager *mgr)
{
698 699 700
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
701 702
	mutex_lock(&apply_lock);

703 704
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, false);
705 706 707

	spin_lock_irqsave(&data_lock, flags);

T
Tomi Valkeinen 已提交
708
	mp->updating = false;
709 710 711
	mp->enabled = false;

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
712 713

	mutex_unlock(&apply_lock);
714 715
}

716 717 718
int dss_mgr_set_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
719 720 721 722
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

723 724 725
	mgr->info = *info;
	mgr->info_dirty = true;

726 727
	spin_unlock_irqrestore(&data_lock, flags);

728 729 730 731 732 733
	return 0;
}

void dss_mgr_get_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
734 735 736 737
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

738
	*info = mgr->info;
739 740

	spin_unlock_irqrestore(&data_lock, flags);
741 742 743 744 745 746 747
}

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

T
Tomi Valkeinen 已提交
748 749
	mutex_lock(&apply_lock);

750 751 752
	if (dssdev->manager) {
		DSSERR("display '%s' already has a manager '%s'\n",
			       dssdev->name, dssdev->manager->name);
T
Tomi Valkeinen 已提交
753 754
		r = -EINVAL;
		goto err;
755 756 757 758 759
	}

	if ((mgr->supported_displays & dssdev->type) == 0) {
		DSSERR("display '%s' does not support manager '%s'\n",
			       dssdev->name, mgr->name);
T
Tomi Valkeinen 已提交
760 761
		r = -EINVAL;
		goto err;
762 763 764 765 766 767
	}

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

T
Tomi Valkeinen 已提交
768 769
	mutex_unlock(&apply_lock);

770
	return 0;
T
Tomi Valkeinen 已提交
771 772 773
err:
	mutex_unlock(&apply_lock);
	return r;
774 775 776 777
}

int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
{
T
Tomi Valkeinen 已提交
778 779 780 781
	int r;

	mutex_lock(&apply_lock);

782 783
	if (!mgr->device) {
		DSSERR("failed to unset display, display not set.\n");
T
Tomi Valkeinen 已提交
784 785
		r = -EINVAL;
		goto err;
786 787 788 789 790 791
	}

	/*
	 * Don't allow currently enabled displays to have the overlay manager
	 * pulled out from underneath them
	 */
T
Tomi Valkeinen 已提交
792 793 794 795
	if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
		r = -EINVAL;
		goto err;
	}
796 797 798 799 800

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

T
Tomi Valkeinen 已提交
801 802
	mutex_unlock(&apply_lock);

803
	return 0;
T
Tomi Valkeinen 已提交
804 805 806
err:
	mutex_unlock(&apply_lock);
	return r;
807 808 809 810
}



811 812 813
int dss_ovl_set_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
814 815 816 817
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

818 819 820
	ovl->info = *info;
	ovl->info_dirty = true;

821 822
	spin_unlock_irqrestore(&data_lock, flags);

823 824 825 826 827 828
	return 0;
}

void dss_ovl_get_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
829 830 831 832
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

833
	*info = ovl->info;
834 835

	spin_unlock_irqrestore(&data_lock, flags);
836 837 838 839 840
}

int dss_ovl_set_manager(struct omap_overlay *ovl,
		struct omap_overlay_manager *mgr)
{
841 842
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
T
Tomi Valkeinen 已提交
843 844
	int r;

845 846 847
	if (!mgr)
		return -EINVAL;

T
Tomi Valkeinen 已提交
848 849
	mutex_lock(&apply_lock);

850 851 852
	if (ovl->manager) {
		DSSERR("overlay '%s' already has a manager '%s'\n",
				ovl->name, ovl->manager->name);
T
Tomi Valkeinen 已提交
853 854
		r = -EINVAL;
		goto err;
855 856
	}

857 858 859 860
	spin_lock_irqsave(&data_lock, flags);

	if (op->enabled) {
		spin_unlock_irqrestore(&data_lock, flags);
861
		DSSERR("overlay has to be disabled to change the manager\n");
T
Tomi Valkeinen 已提交
862 863
		r = -EINVAL;
		goto err;
864 865 866 867 868 869
	}

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

870 871
	spin_unlock_irqrestore(&data_lock, flags);

872 873 874 875 876 877 878 879 880 881 882 883 884
	/* 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 已提交
885 886
	mutex_unlock(&apply_lock);

887
	return 0;
T
Tomi Valkeinen 已提交
888 889 890
err:
	mutex_unlock(&apply_lock);
	return r;
891 892 893 894
}

int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
895 896
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
T
Tomi Valkeinen 已提交
897 898 899 900
	int r;

	mutex_lock(&apply_lock);

901 902
	if (!ovl->manager) {
		DSSERR("failed to detach overlay: manager not set\n");
T
Tomi Valkeinen 已提交
903 904
		r = -EINVAL;
		goto err;
905 906
	}

907 908 909 910
	spin_lock_irqsave(&data_lock, flags);

	if (op->enabled) {
		spin_unlock_irqrestore(&data_lock, flags);
911
		DSSERR("overlay has to be disabled to unset the manager\n");
T
Tomi Valkeinen 已提交
912 913
		r = -EINVAL;
		goto err;
914 915 916 917 918 919
	}

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

920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
	spin_unlock_irqrestore(&data_lock, flags);

	mutex_unlock(&apply_lock);

	return 0;
err:
	mutex_unlock(&apply_lock);
	return r;
}

bool dss_ovl_is_enabled(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
	bool e;

	spin_lock_irqsave(&data_lock, flags);

	e = op->enabled;

	spin_unlock_irqrestore(&data_lock, flags);

	return e;
}

int dss_ovl_enable(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
	int r;

	mutex_lock(&apply_lock);

	if (ovl->manager == NULL || ovl->manager->device == NULL) {
		r = -EINVAL;
		goto err;
	}

	spin_lock_irqsave(&data_lock, flags);

	op->enabled = true;
	op->extra_info_dirty = true;

963 964
	dss_write_regs();

965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
	spin_unlock_irqrestore(&data_lock, flags);

	mutex_unlock(&apply_lock);

	return 0;
err:
	mutex_unlock(&apply_lock);
	return r;
}

int dss_ovl_disable(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
	int r;

	mutex_lock(&apply_lock);

	if (ovl->manager == NULL || ovl->manager->device == NULL) {
		r = -EINVAL;
		goto err;
	}

	spin_lock_irqsave(&data_lock, flags);

	op->enabled = false;
	op->extra_info_dirty = true;

993 994
	dss_write_regs();

995 996
	spin_unlock_irqrestore(&data_lock, flags);

T
Tomi Valkeinen 已提交
997 998
	mutex_unlock(&apply_lock);

999
	return 0;
1000

T
Tomi Valkeinen 已提交
1001 1002 1003
err:
	mutex_unlock(&apply_lock);
	return r;
1004 1005
}