apply.c 19.3 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;

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

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

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

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

111 112
static void dss_register_vsync_isr(void);

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

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

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

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

138 139 140 141 142 143 144 145 146 147 148 149 150 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 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
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;

		if (mgr_manual_update(mgr))
			continue;

		/* to catch GO bit going down */
		if (mp->busy)
			return true;

		/* to write new values to registers */
		if (mp->dirty)
			return true;

		list_for_each_entry(ovl, &mgr->overlays, list) {
			struct ovl_priv_data *op;

			op = get_ovl_priv(ovl);

			if (!op->enabled)
				continue;

			/* to write new values to registers */
			if (op->dirty || op->extra_info_dirty)
				return true;
		}
	}

	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 已提交
202 203 204
int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
	unsigned long timeout = msecs_to_jiffies(500);
205
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
206 207 208 209 210 211 212 213 214 215 216
	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;

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

219
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
220 221 222 223 224
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

225
		spin_lock_irqsave(&data_lock, flags);
226 227
		dirty = mp->dirty;
		shadow_dirty = mp->shadow_dirty;
228
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
229 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 261 262

		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);
263
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
	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;

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

282
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
283 284 285 286 287
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

288
		spin_lock_irqsave(&data_lock, flags);
289 290
		dirty = op->dirty;
		shadow_dirty = op->shadow_dirty;
291
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
292 293 294 295 296 297 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

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

323
static void dss_ovl_write_regs(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
324
{
325
	struct ovl_priv_data *op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
326 327 328 329
	struct omap_overlay_info *oi;
	bool ilace, replication;
	int r;

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

332 333
	if (!op->enabled || !op->dirty)
		return;
T
Tomi Valkeinen 已提交
334

335
	oi = &op->info;
T
Tomi Valkeinen 已提交
336 337 338 339 340

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

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

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

343
	r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
T
Tomi Valkeinen 已提交
344
	if (r) {
345 346 347 348
		/*
		 * We can't do much here, as this function can be called from
		 * vsync interrupt.
		 */
349
		DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
350 351 352 353 354

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

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

359 360
	op->dirty = false;
	op->shadow_dirty = true;
T
Tomi Valkeinen 已提交
361 362
}

363 364 365 366 367 368
static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);

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

369 370 371
	if (!op->extra_info_dirty)
		return;

372 373 374 375
	/* note: write also when op->enabled == false, so that the ovl gets
	 * disabled */

	dispc_ovl_enable(ovl->id, op->enabled);
376 377 378

	op->extra_info_dirty = false;
	op->shadow_extra_info_dirty = true;
379 380
}

381
static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
382
{
383 384
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	struct omap_overlay *ovl;
T
Tomi Valkeinen 已提交
385

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

388 389
	if (!mp->enabled)
		return;
T
Tomi Valkeinen 已提交
390

391
	WARN_ON(mp->busy);
T
Tomi Valkeinen 已提交
392 393

	/* Commit overlay settings */
394 395
	list_for_each_entry(ovl, &mgr->overlays, list) {
		dss_ovl_write_regs(ovl);
396 397 398
		dss_ovl_write_regs_extra(ovl);
	}

399 400
	if (mp->dirty) {
		dispc_mgr_setup(mgr->id, &mp->info);
T
Tomi Valkeinen 已提交
401

402 403
		mp->dirty = false;
		mp->shadow_dirty = true;
T
Tomi Valkeinen 已提交
404
	}
405 406 407 408 409 410
}

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

	for (i = 0; i < num_mgrs; ++i) {
413 414 415
		struct omap_overlay_manager *mgr;
		struct mgr_priv_data *mp;

416 417
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
418

419
		if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
T
Tomi Valkeinen 已提交
420 421
			continue;

422 423 424
		dss_mgr_write_regs(mgr);

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

427 428
			if (!dss_data.irq_enabled && need_isr())
				dss_register_vsync_isr();
T
Tomi Valkeinen 已提交
429

430 431 432
			dispc_mgr_go(mgr->id);
		}
	}
T
Tomi Valkeinen 已提交
433 434 435 436
}

void dss_mgr_start_update(struct omap_overlay_manager *mgr)
{
437
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
438
	struct ovl_priv_data *op;
439
	struct omap_overlay *ovl;
440 441 442
	unsigned long flags;

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

444
	dss_mgr_write_regs(mgr);
T
Tomi Valkeinen 已提交
445

446
	list_for_each_entry(ovl, &mgr->overlays, list) {
447 448
		op = get_ovl_priv(ovl);
		op->shadow_dirty = false;
449
		op->shadow_extra_info_dirty = false;
T
Tomi Valkeinen 已提交
450 451
	}

452
	mp->shadow_dirty = false;
T
Tomi Valkeinen 已提交
453

454
	dispc_mgr_enable(mgr->id, true);
455 456

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
457 458
}

459 460 461 462
static void dss_apply_irq_handler(void *data, u32 mask);

static void dss_register_vsync_isr(void)
{
463
	const int num_mgrs = dss_feat_get_num_mgrs();
464
	u32 mask;
465
	int r, i;
466

467 468 469
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
470 471 472 473

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

474
	dss_data.irq_enabled = true;
475 476 477 478
}

static void dss_unregister_vsync_isr(void)
{
479
	const int num_mgrs = dss_feat_get_num_mgrs();
480
	u32 mask;
481
	int r, i;
482

483 484 485
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
486 487 488 489

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

490
	dss_data.irq_enabled = false;
491 492
}

T
Tomi Valkeinen 已提交
493 494
static void dss_apply_irq_handler(void *data, u32 mask)
{
495
	struct omap_overlay *ovl;
496 497
	struct omap_overlay_manager *mgr;
	struct mgr_priv_data *mp;
498
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
499 500
	const int num_ovls = dss_feat_get_num_ovls();
	const int num_mgrs = dss_feat_get_num_mgrs();
501
	int i;
T
Tomi Valkeinen 已提交
502

503
	spin_lock(&data_lock);
T
Tomi Valkeinen 已提交
504

505 506 507 508 509 510 511
	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 已提交
512
	for (i = 0; i < num_ovls; ++i) {
513 514
		ovl = omap_dss_get_overlay(i);
		op = get_ovl_priv(ovl);
515 516 517 518 519 520

		if (!op->enabled)
			continue;

		mp = get_mgr_priv(ovl->manager);

521
		if (!mp->busy) {
522
			op->shadow_dirty = false;
523 524
			op->shadow_extra_info_dirty = false;
		}
T
Tomi Valkeinen 已提交
525 526 527
	}

	for (i = 0; i < num_mgrs; ++i) {
528 529
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
530 531

		if (!mp->busy)
532
			mp->shadow_dirty = false;
T
Tomi Valkeinen 已提交
533 534
	}

535
	dss_write_regs();
T
Tomi Valkeinen 已提交
536

537 538
	if (!need_isr())
		dss_unregister_vsync_isr();
T
Tomi Valkeinen 已提交
539

540
	spin_unlock(&data_lock);
T
Tomi Valkeinen 已提交
541 542
}

543
static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
544
{
545
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
546

547
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
548 549 550 551 552 553 554

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

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

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

561
	op->channel = ovl->manager->id;
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

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

	if (!mgr->info_dirty)
		return;

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

static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
{
585
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
586 587 588
	struct omap_dss_device *dssdev;
	u32 size, burst_size;

589
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
590 591 592 593 594 595 596 597 598 599 600 601 602 603

	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,
604 605
				burst_size, &op->fifo_low,
				&op->fifo_high);
T
Tomi Valkeinen 已提交
606 607 608 609
		break;
#ifdef CONFIG_OMAP2_DSS_DSI
	case OMAP_DISPLAY_TYPE_DSI:
		dsi_get_overlay_fifo_thresholds(ovl->id, size,
610 611
				burst_size, &op->fifo_low,
				&op->fifo_high);
T
Tomi Valkeinen 已提交
612 613 614 615 616 617 618 619 620
		break;
#endif
	default:
		BUG();
	}
}

int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
621
	int r;
T
Tomi Valkeinen 已提交
622
	unsigned long flags;
623
	struct omap_overlay *ovl;
T
Tomi Valkeinen 已提交
624 625 626 627 628 629 630

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

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

631
	spin_lock_irqsave(&data_lock, flags);
T
Tomi Valkeinen 已提交
632 633

	/* Configure overlays */
634
	list_for_each_entry(ovl, &mgr->overlays, list)
T
Tomi Valkeinen 已提交
635 636 637 638 639 640
		omap_dss_mgr_apply_ovl(ovl);

	/* Configure manager */
	omap_dss_mgr_apply_mgr(mgr);

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

644
	dss_write_regs();
T
Tomi Valkeinen 已提交
645

646
	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
647 648 649 650 651 652

	dispc_runtime_put();

	return r;
}

653 654
void dss_mgr_enable(struct omap_overlay_manager *mgr)
{
655 656 657
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
658 659
	mutex_lock(&apply_lock);

660 661 662 663
	spin_lock_irqsave(&data_lock, flags);

	mp->enabled = true;

664 665
	dss_write_regs();

666
	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
667

668 669 670
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, true);

T
Tomi Valkeinen 已提交
671
	mutex_unlock(&apply_lock);
672 673 674 675
}

void dss_mgr_disable(struct omap_overlay_manager *mgr)
{
676 677 678
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
679 680
	mutex_lock(&apply_lock);

681 682
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, false);
683 684 685 686 687 688

	spin_lock_irqsave(&data_lock, flags);

	mp->enabled = false;

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

	mutex_unlock(&apply_lock);
691 692
}

693 694 695
int dss_mgr_set_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
696 697 698 699
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

700 701 702
	mgr->info = *info;
	mgr->info_dirty = true;

703 704
	spin_unlock_irqrestore(&data_lock, flags);

705 706 707 708 709 710
	return 0;
}

void dss_mgr_get_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
711 712 713 714
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

715
	*info = mgr->info;
716 717

	spin_unlock_irqrestore(&data_lock, flags);
718 719 720 721 722 723 724
}

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

T
Tomi Valkeinen 已提交
725 726
	mutex_lock(&apply_lock);

727 728 729
	if (dssdev->manager) {
		DSSERR("display '%s' already has a manager '%s'\n",
			       dssdev->name, dssdev->manager->name);
T
Tomi Valkeinen 已提交
730 731
		r = -EINVAL;
		goto err;
732 733 734 735 736
	}

	if ((mgr->supported_displays & dssdev->type) == 0) {
		DSSERR("display '%s' does not support manager '%s'\n",
			       dssdev->name, mgr->name);
T
Tomi Valkeinen 已提交
737 738
		r = -EINVAL;
		goto err;
739 740 741 742 743 744
	}

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

T
Tomi Valkeinen 已提交
745 746
	mutex_unlock(&apply_lock);

747
	return 0;
T
Tomi Valkeinen 已提交
748 749 750
err:
	mutex_unlock(&apply_lock);
	return r;
751 752 753 754
}

int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
{
T
Tomi Valkeinen 已提交
755 756 757 758
	int r;

	mutex_lock(&apply_lock);

759 760
	if (!mgr->device) {
		DSSERR("failed to unset display, display not set.\n");
T
Tomi Valkeinen 已提交
761 762
		r = -EINVAL;
		goto err;
763 764 765 766 767 768
	}

	/*
	 * Don't allow currently enabled displays to have the overlay manager
	 * pulled out from underneath them
	 */
T
Tomi Valkeinen 已提交
769 770 771 772
	if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
		r = -EINVAL;
		goto err;
	}
773 774 775 776 777

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

T
Tomi Valkeinen 已提交
778 779
	mutex_unlock(&apply_lock);

780
	return 0;
T
Tomi Valkeinen 已提交
781 782 783
err:
	mutex_unlock(&apply_lock);
	return r;
784 785 786 787
}



788 789 790
int dss_ovl_set_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
791 792 793 794
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

795 796 797
	ovl->info = *info;
	ovl->info_dirty = true;

798 799
	spin_unlock_irqrestore(&data_lock, flags);

800 801 802 803 804 805
	return 0;
}

void dss_ovl_get_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
806 807 808 809
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

810
	*info = ovl->info;
811 812

	spin_unlock_irqrestore(&data_lock, flags);
813 814 815 816 817
}

int dss_ovl_set_manager(struct omap_overlay *ovl,
		struct omap_overlay_manager *mgr)
{
818 819
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
T
Tomi Valkeinen 已提交
820 821
	int r;

822 823 824
	if (!mgr)
		return -EINVAL;

T
Tomi Valkeinen 已提交
825 826
	mutex_lock(&apply_lock);

827 828 829
	if (ovl->manager) {
		DSSERR("overlay '%s' already has a manager '%s'\n",
				ovl->name, ovl->manager->name);
T
Tomi Valkeinen 已提交
830 831
		r = -EINVAL;
		goto err;
832 833
	}

834 835 836 837
	spin_lock_irqsave(&data_lock, flags);

	if (op->enabled) {
		spin_unlock_irqrestore(&data_lock, flags);
838
		DSSERR("overlay has to be disabled to change the manager\n");
T
Tomi Valkeinen 已提交
839 840
		r = -EINVAL;
		goto err;
841 842 843 844 845 846
	}

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

847 848
	spin_unlock_irqrestore(&data_lock, flags);

849 850 851 852 853 854 855 856 857 858 859 860 861
	/* 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 已提交
862 863
	mutex_unlock(&apply_lock);

864
	return 0;
T
Tomi Valkeinen 已提交
865 866 867
err:
	mutex_unlock(&apply_lock);
	return r;
868 869 870 871
}

int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
872 873
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
T
Tomi Valkeinen 已提交
874 875 876 877
	int r;

	mutex_lock(&apply_lock);

878 879
	if (!ovl->manager) {
		DSSERR("failed to detach overlay: manager not set\n");
T
Tomi Valkeinen 已提交
880 881
		r = -EINVAL;
		goto err;
882 883
	}

884 885 886 887
	spin_lock_irqsave(&data_lock, flags);

	if (op->enabled) {
		spin_unlock_irqrestore(&data_lock, flags);
888
		DSSERR("overlay has to be disabled to unset the manager\n");
T
Tomi Valkeinen 已提交
889 890
		r = -EINVAL;
		goto err;
891 892 893 894 895 896
	}

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

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
	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;

940 941
	dss_write_regs();

942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
	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;

970 971
	dss_write_regs();

972 973
	spin_unlock_irqrestore(&data_lock, flags);

T
Tomi Valkeinen 已提交
974 975
	mutex_unlock(&apply_lock);

976
	return 0;
977

T
Tomi Valkeinen 已提交
978 979 980
err:
	mutex_unlock(&apply_lock);
	return r;
981 982
}