apply.c 23.6 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
/*
 * 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.
 *
34 35
 *       set_info()
 *          v
T
Tomi Valkeinen 已提交
36
 * +--------------------+
37
 * |     user_info      |
T
Tomi Valkeinen 已提交
38 39 40 41 42
 * +--------------------+
 *          v
 *        apply()
 *          v
 * +--------------------+
43
 * |       info         |
T
Tomi Valkeinen 已提交
44 45
 * +--------------------+
 *          v
46
 *      write_regs()
T
Tomi Valkeinen 已提交
47 48 49 50 51 52 53 54 55 56 57 58
 *          v
 * +--------------------+
 * |  shadow registers  |
 * +--------------------+
 *          v
 * VFP or lcd/digit_enable
 *          v
 * +--------------------+
 * |      registers     |
 * +--------------------+
 */

59
struct ovl_priv_data {
60 61 62 63

	bool user_info_dirty;
	struct omap_overlay_info user_info;

64
	bool info_dirty;
T
Tomi Valkeinen 已提交
65 66
	struct omap_overlay_info info;

67 68
	bool shadow_info_dirty;

69 70 71 72
	bool extra_info_dirty;
	bool shadow_extra_info_dirty;

	bool enabled;
73
	enum omap_channel channel;
74
	u32 fifo_low, fifo_high;
T
Tomi Valkeinen 已提交
75 76
};

77
struct mgr_priv_data {
78 79 80 81

	bool user_info_dirty;
	struct omap_overlay_manager_info user_info;

82
	bool info_dirty;
T
Tomi Valkeinen 已提交
83 84
	struct omap_overlay_manager_info info;

85 86
	bool shadow_info_dirty;

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

T
Tomi Valkeinen 已提交
91 92 93
	/* If true, dispc output is enabled */
	bool updating;

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
static DECLARE_COMPLETION(extra_updated_completion);
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 126 127
	const int num_ovls = dss_feat_get_num_ovls();
	int i;

128
	spin_lock_init(&data_lock);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

	for (i = 0; i < num_ovls; ++i) {
		struct ovl_priv_data *op;

		op = &dss_data.ovl_priv_data_array[i];

		op->info.global_alpha = 255;

		switch (i) {
		case 0:
			op->info.zorder = 0;
			break;
		case 1:
			op->info.zorder =
				dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0;
			break;
		case 2:
			op->info.zorder =
				dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0;
			break;
		case 3:
			op->info.zorder =
				dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0;
			break;
		}

		op->user_info = op->info;
	}
T
Tomi Valkeinen 已提交
157 158 159 160 161 162 163 164 165 166 167 168
}

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

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
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 已提交
185 186 187 188 189 190 191 192
		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;
193

T
Tomi Valkeinen 已提交
194
			/* to write new values to registers */
195
			if (mp->info_dirty)
T
Tomi Valkeinen 已提交
196
				return true;
197

T
Tomi Valkeinen 已提交
198 199
			list_for_each_entry(ovl, &mgr->overlays, list) {
				struct ovl_priv_data *op;
200

T
Tomi Valkeinen 已提交
201
				op = get_ovl_priv(ovl);
202

T
Tomi Valkeinen 已提交
203 204
				if (!op->enabled)
					continue;
205

T
Tomi Valkeinen 已提交
206
				/* to write new values to registers */
207
				if (op->info_dirty || op->extra_info_dirty)
T
Tomi Valkeinen 已提交
208 209
					return true;
			}
210 211 212 213 214 215 216 217 218 219 220 221 222 223
		}
	}

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

224
	if (mp->shadow_info_dirty)
225 226 227 228
		return true;

	list_for_each_entry(ovl, &mgr->overlays, list) {
		op = get_ovl_priv(ovl);
229
		if (op->shadow_info_dirty || op->shadow_extra_info_dirty)
230 231 232 233 234 235
			return true;
	}

	return false;
}

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 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
/* returns true if an extra_info field is currently being updated */
static bool extra_info_update_ongoing(void)
{
	const int num_ovls = omap_dss_get_num_overlays();
	struct ovl_priv_data *op;
	struct omap_overlay *ovl;
	struct mgr_priv_data *mp;
	int i;
	bool eid;

	for (i = 0; i < num_ovls; ++i) {
		ovl = omap_dss_get_overlay(i);
		op = get_ovl_priv(ovl);

		if (!op->enabled)
			continue;

		mp = get_mgr_priv(ovl->manager);

		if (!mp->enabled)
			continue;

		eid = op->extra_info_dirty || op->shadow_extra_info_dirty;

		if (!eid)
			continue;

		if (ovl_manual_update(ovl) && !mp->updating)
			continue;

		return true;
	}

	return false;
}

/* wait until no extra_info updates are pending */
static void wait_pending_extra_info_updates(void)
{
	bool updating;
	unsigned long flags;
	unsigned long t;

	spin_lock_irqsave(&data_lock, flags);

	updating = extra_info_update_ongoing();

	if (!updating) {
		spin_unlock_irqrestore(&data_lock, flags);
		return;
	}

	init_completion(&extra_updated_completion);

	spin_unlock_irqrestore(&data_lock, flags);

	t = msecs_to_jiffies(500);
	wait_for_completion_timeout(&extra_updated_completion, t);

	updating = extra_info_update_ongoing();

	WARN_ON(updating);
}

T
Tomi Valkeinen 已提交
300 301 302
int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
	unsigned long timeout = msecs_to_jiffies(500);
303
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
304 305 306 307 308 309 310 311 312 313 314
	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;

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

317
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
318 319 320 321 322
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

323
		spin_lock_irqsave(&data_lock, flags);
324 325
		dirty = mp->info_dirty;
		shadow_dirty = mp->shadow_info_dirty;
326
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

		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);
361
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
	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;

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

380
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
381 382 383 384 385
	i = 0;
	while (1) {
		unsigned long flags;
		bool shadow_dirty, dirty;

386
		spin_lock_irqsave(&data_lock, flags);
387 388
		dirty = op->info_dirty;
		shadow_dirty = op->shadow_info_dirty;
389
		spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420

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

421
static void dss_ovl_write_regs(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
422
{
423
	struct ovl_priv_data *op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
424 425
	struct omap_overlay_info *oi;
	bool ilace, replication;
T
Tomi Valkeinen 已提交
426
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
427 428
	int r;

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

431
	if (!op->enabled || !op->info_dirty)
432
		return;
T
Tomi Valkeinen 已提交
433

434
	oi = &op->info;
T
Tomi Valkeinen 已提交
435 436 437 438 439

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

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

440
	r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
T
Tomi Valkeinen 已提交
441
	if (r) {
442 443 444 445
		/*
		 * We can't do much here, as this function can be called from
		 * vsync interrupt.
		 */
446
		DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
447 448 449 450 451

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

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

456
	op->info_dirty = false;
T
Tomi Valkeinen 已提交
457
	if (mp->updating)
458
		op->shadow_info_dirty = true;
T
Tomi Valkeinen 已提交
459 460
}

461 462 463
static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
464
	struct mgr_priv_data *mp;
465 466 467

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

468 469 470
	if (!op->extra_info_dirty)
		return;

471 472 473 474
	/* note: write also when op->enabled == false, so that the ovl gets
	 * disabled */

	dispc_ovl_enable(ovl->id, op->enabled);
475
	dispc_ovl_set_channel_out(ovl->id, op->channel);
476
	dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
477

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

480
	op->extra_info_dirty = false;
T
Tomi Valkeinen 已提交
481 482
	if (mp->updating)
		op->shadow_extra_info_dirty = true;
483 484
}

485
static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
486
{
487 488
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	struct omap_overlay *ovl;
T
Tomi Valkeinen 已提交
489

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

492 493
	if (!mp->enabled)
		return;
T
Tomi Valkeinen 已提交
494

495
	WARN_ON(mp->busy);
T
Tomi Valkeinen 已提交
496 497

	/* Commit overlay settings */
498 499
	list_for_each_entry(ovl, &mgr->overlays, list) {
		dss_ovl_write_regs(ovl);
500 501 502
		dss_ovl_write_regs_extra(ovl);
	}

503
	if (mp->info_dirty) {
504
		dispc_mgr_setup(mgr->id, &mp->info);
T
Tomi Valkeinen 已提交
505

506
		mp->info_dirty = false;
T
Tomi Valkeinen 已提交
507
		if (mp->updating)
508
			mp->shadow_info_dirty = true;
T
Tomi Valkeinen 已提交
509
	}
510 511 512 513 514 515
}

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

	for (i = 0; i < num_mgrs; ++i) {
518 519 520
		struct omap_overlay_manager *mgr;
		struct mgr_priv_data *mp;

521 522
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
523

524
		if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
T
Tomi Valkeinen 已提交
525 526
			continue;

527 528 529
		dss_mgr_write_regs(mgr);

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

532 533
			if (!dss_data.irq_enabled && need_isr())
				dss_register_vsync_isr();
T
Tomi Valkeinen 已提交
534

535 536 537
			dispc_mgr_go(mgr->id);
		}
	}
T
Tomi Valkeinen 已提交
538 539 540 541
}

void dss_mgr_start_update(struct omap_overlay_manager *mgr)
{
542
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
543 544 545
	unsigned long flags;

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

T
Tomi Valkeinen 已提交
547 548
	WARN_ON(mp->updating);

549
	dss_mgr_write_regs(mgr);
T
Tomi Valkeinen 已提交
550

T
Tomi Valkeinen 已提交
551
	mp->updating = true;
T
Tomi Valkeinen 已提交
552

T
Tomi Valkeinen 已提交
553 554
	if (!dss_data.irq_enabled && need_isr())
		dss_register_vsync_isr();
T
Tomi Valkeinen 已提交
555

556
	dispc_mgr_enable(mgr->id, true);
557 558

	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
559 560
}

561 562 563 564
static void dss_apply_irq_handler(void *data, u32 mask);

static void dss_register_vsync_isr(void)
{
565
	const int num_mgrs = dss_feat_get_num_mgrs();
566
	u32 mask;
567
	int r, i;
568

569 570 571
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
572

T
Tomi Valkeinen 已提交
573 574 575
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_framedone_irq(i);

576 577 578
	r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
	WARN_ON(r);

579
	dss_data.irq_enabled = true;
580 581 582 583
}

static void dss_unregister_vsync_isr(void)
{
584
	const int num_mgrs = dss_feat_get_num_mgrs();
585
	u32 mask;
586
	int r, i;
587

588 589 590
	mask = 0;
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_vsync_irq(i);
591

T
Tomi Valkeinen 已提交
592 593 594
	for (i = 0; i < num_mgrs; ++i)
		mask |= dispc_mgr_get_framedone_irq(i);

595 596 597
	r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
	WARN_ON(r);

598
	dss_data.irq_enabled = false;
599 600
}

601
static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
602
{
603
	struct omap_overlay *ovl;
604
	struct mgr_priv_data *mp;
605
	struct ovl_priv_data *op;
606 607

	mp = get_mgr_priv(mgr);
608
	mp->shadow_info_dirty = false;
609 610 611

	list_for_each_entry(ovl, &mgr->overlays, list) {
		op = get_ovl_priv(ovl);
612
		op->shadow_info_dirty = false;
613 614 615 616 617 618
		op->shadow_extra_info_dirty = false;
	}
}

static void dss_apply_irq_handler(void *data, u32 mask)
{
T
Tomi Valkeinen 已提交
619
	const int num_mgrs = dss_feat_get_num_mgrs();
620
	int i;
621
	bool extra_updating;
T
Tomi Valkeinen 已提交
622

623
	spin_lock(&data_lock);
T
Tomi Valkeinen 已提交
624

625
	/* clear busy, updating flags, shadow_dirty flags */
626
	for (i = 0; i < num_mgrs; i++) {
627 628 629
		struct omap_overlay_manager *mgr;
		struct mgr_priv_data *mp;

630 631 632
		mgr = omap_dss_get_overlay_manager(i);
		mp = get_mgr_priv(mgr);

633
		if (!mp->enabled)
634 635
			continue;

636
		mp->updating = dispc_mgr_is_enabled(i);
T
Tomi Valkeinen 已提交
637

638 639
		if (!mgr_manual_update(mgr)) {
			mp->busy = dispc_mgr_go_busy(i);
640

641 642 643 644 645 646
			if (!mp->busy)
				mgr_clear_shadow_dirty(mgr);
		} else {
			if (!mp->updating)
				mgr_clear_shadow_dirty(mgr);
		}
T
Tomi Valkeinen 已提交
647 648
	}

649
	dss_write_regs();
T
Tomi Valkeinen 已提交
650

651 652 653 654
	extra_updating = extra_info_update_ongoing();
	if (!extra_updating)
		complete_all(&extra_updated_completion);

655 656
	if (!need_isr())
		dss_unregister_vsync_isr();
T
Tomi Valkeinen 已提交
657

658
	spin_unlock(&data_lock);
T
Tomi Valkeinen 已提交
659 660
}

661
static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
T
Tomi Valkeinen 已提交
662
{
663
	struct ovl_priv_data *op;
T
Tomi Valkeinen 已提交
664

665
	op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
666

667
	if (!op->user_info_dirty)
668
		return;
T
Tomi Valkeinen 已提交
669

670
	op->user_info_dirty = false;
671
	op->info_dirty = true;
672
	op->info = op->user_info;
T
Tomi Valkeinen 已提交
673 674 675 676
}

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

679
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
680

681
	if (!mp->user_info_dirty)
T
Tomi Valkeinen 已提交
682 683
		return;

684
	mp->user_info_dirty = false;
685
	mp->info_dirty = true;
686
	mp->info = mp->user_info;
T
Tomi Valkeinen 已提交
687 688
}

689
int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
690
{
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
	unsigned long flags;
	struct omap_overlay *ovl;

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

	spin_lock_irqsave(&data_lock, flags);

	/* Configure overlays */
	list_for_each_entry(ovl, &mgr->overlays, list)
		omap_dss_mgr_apply_ovl(ovl);

	/* Configure manager */
	omap_dss_mgr_apply_mgr(mgr);

	dss_write_regs();

	spin_unlock_irqrestore(&data_lock, flags);

709
	return 0;
710 711
}

712 713 714 715 716 717 718 719 720 721 722 723 724
static void dss_apply_ovl_enable(struct omap_overlay *ovl, bool enable)
{
	struct ovl_priv_data *op;

	op = get_ovl_priv(ovl);

	if (op->enabled == enable)
		return;

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

725 726 727
static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
{
	struct ovl_priv_data *op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
728 729
	struct omap_dss_device *dssdev;
	u32 size, burst_size;
730
	u32 fifo_low, fifo_high;
T
Tomi Valkeinen 已提交
731 732 733 734 735 736 737 738 739 740 741 742 743 744

	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,
745
				burst_size, &fifo_low, &fifo_high);
T
Tomi Valkeinen 已提交
746 747 748 749
		break;
#ifdef CONFIG_OMAP2_DSS_DSI
	case OMAP_DISPLAY_TYPE_DSI:
		dsi_get_overlay_fifo_thresholds(ovl->id, size,
750
				burst_size, &fifo_low, &fifo_high);
T
Tomi Valkeinen 已提交
751 752 753 754 755
		break;
#endif
	default:
		BUG();
	}
756 757 758 759

	op->fifo_low = fifo_low;
	op->fifo_high = fifo_high;
	op->extra_info_dirty = true;
T
Tomi Valkeinen 已提交
760 761
}

762
static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
T
Tomi Valkeinen 已提交
763
{
764
	struct omap_overlay *ovl;
765 766
	struct ovl_priv_data *op;
	struct mgr_priv_data *mp;
T
Tomi Valkeinen 已提交
767

768
	mp = get_mgr_priv(mgr);
T
Tomi Valkeinen 已提交
769

770 771
	if (!mp->enabled)
		return;
T
Tomi Valkeinen 已提交
772

773 774
	list_for_each_entry(ovl, &mgr->overlays, list) {
		op = get_ovl_priv(ovl);
T
Tomi Valkeinen 已提交
775

776 777
		if (!op->enabled)
			continue;
T
Tomi Valkeinen 已提交
778

779 780
		dss_ovl_setup_fifo(ovl);
	}
T
Tomi Valkeinen 已提交
781 782
}

783 784
void dss_mgr_enable(struct omap_overlay_manager *mgr)
{
785 786 787
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
788 789
	mutex_lock(&apply_lock);

790 791 792
	if (mp->enabled)
		goto out;

793 794 795 796
	spin_lock_irqsave(&data_lock, flags);

	mp->enabled = true;

797 798
	dss_mgr_setup_fifos(mgr);

799 800
	dss_write_regs();

T
Tomi Valkeinen 已提交
801 802 803
	if (!mgr_manual_update(mgr))
		mp->updating = true;

804
	spin_unlock_irqrestore(&data_lock, flags);
T
Tomi Valkeinen 已提交
805

806 807 808
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, true);

809
out:
T
Tomi Valkeinen 已提交
810
	mutex_unlock(&apply_lock);
811 812 813 814
}

void dss_mgr_disable(struct omap_overlay_manager *mgr)
{
815 816 817
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
	unsigned long flags;

T
Tomi Valkeinen 已提交
818 819
	mutex_lock(&apply_lock);

820 821 822
	if (!mp->enabled)
		goto out;

823 824
	if (!mgr_manual_update(mgr))
		dispc_mgr_enable(mgr->id, false);
825 826 827

	spin_lock_irqsave(&data_lock, flags);

T
Tomi Valkeinen 已提交
828
	mp->updating = false;
829 830 831
	mp->enabled = false;

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

833
out:
T
Tomi Valkeinen 已提交
834
	mutex_unlock(&apply_lock);
835 836
}

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
static int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
		const struct omap_overlay_manager_info *info)
{
	if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
		/*
		 * OMAP3 supports only graphics source transparency color key
		 * and alpha blending simultaneously. See TRM 15.4.2.4.2.2
		 * Alpha Mode.
		 */
		if (info->partial_alpha_enabled && info->trans_enabled
			&& info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) {
			DSSERR("check_manager: illegal transparency key\n");
			return -EINVAL;
		}
	}

	return 0;
}

856 857 858
int dss_mgr_set_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
859
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
860
	unsigned long flags;
861 862 863 864 865
	int r;

	r = dss_mgr_simple_check(mgr, info);
	if (r)
		return r;
866 867 868

	spin_lock_irqsave(&data_lock, flags);

869 870
	mp->user_info = *info;
	mp->user_info_dirty = true;
871

872 873
	spin_unlock_irqrestore(&data_lock, flags);

874 875 876 877 878 879
	return 0;
}

void dss_mgr_get_info(struct omap_overlay_manager *mgr,
		struct omap_overlay_manager_info *info)
{
880
	struct mgr_priv_data *mp = get_mgr_priv(mgr);
881 882 883 884
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

885
	*info = mp->user_info;
886 887

	spin_unlock_irqrestore(&data_lock, flags);
888 889 890 891 892 893 894
}

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

T
Tomi Valkeinen 已提交
895 896
	mutex_lock(&apply_lock);

897 898 899
	if (dssdev->manager) {
		DSSERR("display '%s' already has a manager '%s'\n",
			       dssdev->name, dssdev->manager->name);
T
Tomi Valkeinen 已提交
900 901
		r = -EINVAL;
		goto err;
902 903 904 905 906
	}

	if ((mgr->supported_displays & dssdev->type) == 0) {
		DSSERR("display '%s' does not support manager '%s'\n",
			       dssdev->name, mgr->name);
T
Tomi Valkeinen 已提交
907 908
		r = -EINVAL;
		goto err;
909 910 911 912 913
	}

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

T
Tomi Valkeinen 已提交
914 915
	mutex_unlock(&apply_lock);

916
	return 0;
T
Tomi Valkeinen 已提交
917 918 919
err:
	mutex_unlock(&apply_lock);
	return r;
920 921 922 923
}

int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
{
T
Tomi Valkeinen 已提交
924 925 926 927
	int r;

	mutex_lock(&apply_lock);

928 929
	if (!mgr->device) {
		DSSERR("failed to unset display, display not set.\n");
T
Tomi Valkeinen 已提交
930 931
		r = -EINVAL;
		goto err;
932 933 934 935 936 937
	}

	/*
	 * Don't allow currently enabled displays to have the overlay manager
	 * pulled out from underneath them
	 */
T
Tomi Valkeinen 已提交
938 939 940 941
	if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
		r = -EINVAL;
		goto err;
	}
942 943 944 945

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

T
Tomi Valkeinen 已提交
946 947
	mutex_unlock(&apply_lock);

948
	return 0;
T
Tomi Valkeinen 已提交
949 950 951
err:
	mutex_unlock(&apply_lock);
	return r;
952 953 954
}


955 956 957 958 959 960 961 962 963 964 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
static int dss_ovl_simple_check(struct omap_overlay *ovl,
		const struct omap_overlay_info *info)
{
	if (info->paddr == 0) {
		DSSERR("check_overlay: paddr cannot be 0\n");
		return -EINVAL;
	}

	if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
		if (info->out_width != 0 && info->width != info->out_width) {
			DSSERR("check_overlay: overlay %d doesn't support "
					"scaling\n", ovl->id);
			return -EINVAL;
		}

		if (info->out_height != 0 && info->height != info->out_height) {
			DSSERR("check_overlay: overlay %d doesn't support "
					"scaling\n", ovl->id);
			return -EINVAL;
		}
	}

	if ((ovl->supported_modes & info->color_mode) == 0) {
		DSSERR("check_overlay: overlay %d doesn't support mode %d\n",
				ovl->id, info->color_mode);
		return -EINVAL;
	}

	if (info->zorder >= omap_dss_get_num_overlays()) {
		DSSERR("check_overlay: zorder %d too high\n", info->zorder);
		return -EINVAL;
	}

	return 0;
}
990

991 992 993
int dss_ovl_set_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
994
	struct ovl_priv_data *op = get_ovl_priv(ovl);
995
	unsigned long flags;
996 997 998 999 1000
	int r;

	r = dss_ovl_simple_check(ovl, info);
	if (r)
		return r;
1001 1002 1003

	spin_lock_irqsave(&data_lock, flags);

1004 1005
	op->user_info = *info;
	op->user_info_dirty = true;
1006

1007 1008
	spin_unlock_irqrestore(&data_lock, flags);

1009 1010 1011 1012 1013 1014
	return 0;
}

void dss_ovl_get_info(struct omap_overlay *ovl,
		struct omap_overlay_info *info)
{
1015
	struct ovl_priv_data *op = get_ovl_priv(ovl);
1016 1017 1018 1019
	unsigned long flags;

	spin_lock_irqsave(&data_lock, flags);

1020
	*info = op->user_info;
1021 1022

	spin_unlock_irqrestore(&data_lock, flags);
1023 1024 1025 1026 1027
}

int dss_ovl_set_manager(struct omap_overlay *ovl,
		struct omap_overlay_manager *mgr)
{
1028 1029
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
T
Tomi Valkeinen 已提交
1030 1031
	int r;

1032 1033 1034
	if (!mgr)
		return -EINVAL;

T
Tomi Valkeinen 已提交
1035 1036
	mutex_lock(&apply_lock);

1037 1038 1039
	if (ovl->manager) {
		DSSERR("overlay '%s' already has a manager '%s'\n",
				ovl->name, ovl->manager->name);
T
Tomi Valkeinen 已提交
1040 1041
		r = -EINVAL;
		goto err;
1042 1043
	}

1044 1045 1046 1047
	spin_lock_irqsave(&data_lock, flags);

	if (op->enabled) {
		spin_unlock_irqrestore(&data_lock, flags);
1048
		DSSERR("overlay has to be disabled to change the manager\n");
T
Tomi Valkeinen 已提交
1049 1050
		r = -EINVAL;
		goto err;
1051 1052
	}

1053 1054 1055
	op->channel = mgr->id;
	op->extra_info_dirty = true;

1056 1057 1058
	ovl->manager = mgr;
	list_add_tail(&ovl->list, &mgr->overlays);

1059 1060
	spin_unlock_irqrestore(&data_lock, flags);

1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
	/* 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 已提交
1074 1075
	mutex_unlock(&apply_lock);

1076
	return 0;
T
Tomi Valkeinen 已提交
1077 1078 1079
err:
	mutex_unlock(&apply_lock);
	return r;
1080 1081 1082 1083
}

int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
1084 1085
	struct ovl_priv_data *op = get_ovl_priv(ovl);
	unsigned long flags;
T
Tomi Valkeinen 已提交
1086 1087 1088 1089
	int r;

	mutex_lock(&apply_lock);

1090 1091
	if (!ovl->manager) {
		DSSERR("failed to detach overlay: manager not set\n");
T
Tomi Valkeinen 已提交
1092 1093
		r = -EINVAL;
		goto err;
1094 1095
	}

1096 1097 1098 1099
	spin_lock_irqsave(&data_lock, flags);

	if (op->enabled) {
		spin_unlock_irqrestore(&data_lock, flags);
1100
		DSSERR("overlay has to be disabled to unset the manager\n");
T
Tomi Valkeinen 已提交
1101 1102
		r = -EINVAL;
		goto err;
1103 1104
	}

1105 1106
	op->channel = -1;

1107 1108 1109
	ovl->manager = NULL;
	list_del(&ovl->list);

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
	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);

1143 1144 1145 1146 1147
	if (op->enabled) {
		r = 0;
		goto err;
	}

1148 1149 1150 1151 1152 1153 1154
	if (ovl->manager == NULL || ovl->manager->device == NULL) {
		r = -EINVAL;
		goto err;
	}

	spin_lock_irqsave(&data_lock, flags);

1155
	dss_apply_ovl_enable(ovl, true);
1156

1157 1158
	dss_ovl_setup_fifo(ovl);

1159 1160
	dss_write_regs();

1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
	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);

1179 1180 1181 1182 1183
	if (!op->enabled) {
		r = 0;
		goto err;
	}

1184 1185 1186 1187 1188 1189 1190
	if (ovl->manager == NULL || ovl->manager->device == NULL) {
		r = -EINVAL;
		goto err;
	}

	spin_lock_irqsave(&data_lock, flags);

1191
	dss_apply_ovl_enable(ovl, false);
1192

1193 1194
	dss_write_regs();

1195 1196
	spin_unlock_irqrestore(&data_lock, flags);

T
Tomi Valkeinen 已提交
1197 1198
	mutex_unlock(&apply_lock);

1199
	return 0;
1200

T
Tomi Valkeinen 已提交
1201 1202 1203
err:
	mutex_unlock(&apply_lock);
	return r;
1204 1205
}