dispc.c 110.2 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
/*
 * linux/drivers/video/omap2/dss/dispc.c
 *
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
 *
 * Some code and ideas taken from drivers/video/omap/ driver
 * by Imre Deak.
 *
 * 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 "DISPC"

#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
28
#include <linux/export.h>
T
Tomi Valkeinen 已提交
29 30 31 32 33 34
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
35
#include <linux/hardirq.h>
36
#include <linux/platform_device.h>
37
#include <linux/pm_runtime.h>
38
#include <linux/sizes.h>
39 40 41
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of.h>
42
#include <linux/of_device.h>
T
Tomi Valkeinen 已提交
43
#include <linux/component.h>
44
#include <linux/sys_soc.h>
45
#include <drm/drm_fourcc.h>
46
#include <drm/drm_blend.h>
T
Tomi Valkeinen 已提交
47

48
#include "omapdss.h"
T
Tomi Valkeinen 已提交
49
#include "dss.h"
50
#include "dss_features.h"
51
#include "dispc.h"
T
Tomi Valkeinen 已提交
52 53

/* DISPC */
54
#define DISPC_SZ_REGS			SZ_4K
T
Tomi Valkeinen 已提交
55

56 57 58 59 60 61
enum omap_burst_size {
	BURST_SIZE_X2 = 0,
	BURST_SIZE_X4 = 1,
	BURST_SIZE_X8 = 2,
};

T
Tomi Valkeinen 已提交
62 63 64 65 66 67
#define REG_GET(idx, start, end) \
	FLD_GET(dispc_read_reg(idx), start, end)

#define REG_FLD_MOD(idx, val, start, end)				\
	dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end))

68 69 70 71 72 73 74
struct dispc_features {
	u8 sw_start;
	u8 fp_start;
	u8 bp_start;
	u16 sw_max;
	u16 vp_max;
	u16 hp_max;
75 76 77 78
	u8 mgr_width_start;
	u8 mgr_height_start;
	u16 mgr_width_max;
	u16 mgr_height_max;
79 80
	unsigned long max_lcd_pclk;
	unsigned long max_tv_pclk;
81
	int (*calc_scaling) (unsigned long pclk, unsigned long lclk,
82
		const struct videomode *vm,
83
		u16 width, u16 height, u16 out_width, u16 out_height,
84
		u32 fourcc, bool *five_taps,
85
		int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
86
		u16 pos_x, unsigned long *core_clk, bool mem_to_mem);
87
	unsigned long (*calc_core_clk) (unsigned long pclk,
88 89
		u16 width, u16 height, u16 out_width, u16 out_height,
		bool mem_to_mem);
90
	u8 num_fifos;
91
	const enum omap_overlay_caps *overlay_caps;
92
	const u32 **supported_color_modes;
93 94
	unsigned int buffer_size_unit;
	unsigned int burst_size_unit;
95 96 97

	/* swap GFX & WB fifos */
	bool gfx_fifo_workaround:1;
98 99 100

	/* no DISPC_IRQ_FRAMEDONETV on this SoC */
	bool no_framedone_tv:1;
101 102 103

	/* revert to the OMAP4 mechanism of DISPC Smart Standby operation */
	bool mstandby_workaround:1;
104 105

	bool set_max_preload:1;
106 107 108

	/* PIXEL_INC is not added to the last pixel of a line */
	bool last_pixel_inc_missing:1;
109 110 111

	/* POL_FREQ has ALIGN bit */
	bool supports_sync_align:1;
T
Tomi Valkeinen 已提交
112 113

	bool has_writeback:1;
114 115

	bool supports_double_pixel:1;
116 117 118 119 120 121 122

	/*
	 * Field order for VENC is different than HDMI. We should handle this in
	 * some intelligent manner, but as the SoCs have either HDMI or VENC,
	 * never both, we can just use this flag for now.
	 */
	bool reverse_ilace_field_order:1;
123 124

	bool has_gamma_table:1;
125 126

	bool has_gamma_i734_bug:1;
127 128
};

129
#define DISPC_MAX_NR_FIFOS 5
130
#define DISPC_MAX_CHANNEL_GAMMA 4
131

T
Tomi Valkeinen 已提交
132
static struct {
133
	struct platform_device *pdev;
T
Tomi Valkeinen 已提交
134
	void __iomem    *base;
135

136
	int irq;
137 138
	irq_handler_t user_handler;
	void *user_data;
T
Tomi Valkeinen 已提交
139

140
	unsigned long core_clk_rate;
141
	unsigned long tv_pclk_rate;
142

143 144 145
	u32 fifo_size[DISPC_MAX_NR_FIFOS];
	/* maps which plane is using a fifo. fifo-id -> plane-id */
	int fifo_assignment[DISPC_MAX_NR_FIFOS];
T
Tomi Valkeinen 已提交
146

147
	bool		ctx_valid;
T
Tomi Valkeinen 已提交
148
	u32		ctx[DISPC_SZ_REGS / sizeof(u32)];
149

150 151
	u32 *gamma_table[DISPC_MAX_CHANNEL_GAMMA];

152
	const struct dispc_features *feat;
153 154

	bool is_enabled;
155 156 157

	struct regmap *syscon_pol;
	u32 syscon_pol_offset;
158 159 160

	/* DISPC_CONTROL & DISPC_CONFIG lock*/
	spinlock_t control_lock;
T
Tomi Valkeinen 已提交
161 162
} dispc;

163 164 165 166 167 168
enum omap_color_component {
	/* used for all color formats for OMAP3 and earlier
	 * and for RGB and Y color component on OMAP4
	 */
	DISPC_COLOR_COMPONENT_RGB_Y		= 1 << 0,
	/* used for UV component for
169
	 * DRM_FORMAT_YUYV, DRM_FORMAT_UYVY, DRM_FORMAT_NV12
170 171 172 173 174
	 * color formats on OMAP4
	 */
	DISPC_COLOR_COMPONENT_UV		= 1 << 1,
};

175 176 177 178 179 180 181 182 183 184 185 186 187 188
enum mgr_reg_fields {
	DISPC_MGR_FLD_ENABLE,
	DISPC_MGR_FLD_STNTFT,
	DISPC_MGR_FLD_GO,
	DISPC_MGR_FLD_TFTDATALINES,
	DISPC_MGR_FLD_STALLMODE,
	DISPC_MGR_FLD_TCKENABLE,
	DISPC_MGR_FLD_TCKSELECTION,
	DISPC_MGR_FLD_CPR,
	DISPC_MGR_FLD_FIFOHANDCHECK,
	/* used to maintain a count of the above fields */
	DISPC_MGR_FLD_NUM,
};

189 190 191 192 193 194
struct dispc_reg_field {
	u16 reg;
	u8 high;
	u8 low;
};

195 196 197 198 199 200 201
struct dispc_gamma_desc {
	u32 len;
	u32 bits;
	u16 reg;
	bool has_index;
};

202 203 204 205 206
static const struct {
	const char *name;
	u32 vsync_irq;
	u32 framedone_irq;
	u32 sync_lost_irq;
207
	struct dispc_gamma_desc gamma;
208
	struct dispc_reg_field reg_desc[DISPC_MGR_FLD_NUM];
209 210 211 212 213 214
} mgr_desc[] = {
	[OMAP_DSS_CHANNEL_LCD] = {
		.name		= "LCD",
		.vsync_irq	= DISPC_IRQ_VSYNC,
		.framedone_irq	= DISPC_IRQ_FRAMEDONE,
		.sync_lost_irq	= DISPC_IRQ_SYNC_LOST,
215 216 217 218 219 220
		.gamma		= {
			.len	= 256,
			.bits	= 8,
			.reg	= DISPC_GAMMA_TABLE0,
			.has_index = true,
		},
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
		.reg_desc	= {
			[DISPC_MGR_FLD_ENABLE]		= { DISPC_CONTROL,  0,  0 },
			[DISPC_MGR_FLD_STNTFT]		= { DISPC_CONTROL,  3,  3 },
			[DISPC_MGR_FLD_GO]		= { DISPC_CONTROL,  5,  5 },
			[DISPC_MGR_FLD_TFTDATALINES]	= { DISPC_CONTROL,  9,  8 },
			[DISPC_MGR_FLD_STALLMODE]	= { DISPC_CONTROL, 11, 11 },
			[DISPC_MGR_FLD_TCKENABLE]	= { DISPC_CONFIG,  10, 10 },
			[DISPC_MGR_FLD_TCKSELECTION]	= { DISPC_CONFIG,  11, 11 },
			[DISPC_MGR_FLD_CPR]		= { DISPC_CONFIG,  15, 15 },
			[DISPC_MGR_FLD_FIFOHANDCHECK]	= { DISPC_CONFIG,  16, 16 },
		},
	},
	[OMAP_DSS_CHANNEL_DIGIT] = {
		.name		= "DIGIT",
		.vsync_irq	= DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
236
		.framedone_irq	= DISPC_IRQ_FRAMEDONETV,
237
		.sync_lost_irq	= DISPC_IRQ_SYNC_LOST_DIGIT,
238 239 240 241 242 243
		.gamma		= {
			.len	= 1024,
			.bits	= 10,
			.reg	= DISPC_GAMMA_TABLE2,
			.has_index = false,
		},
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
		.reg_desc	= {
			[DISPC_MGR_FLD_ENABLE]		= { DISPC_CONTROL,  1,  1 },
			[DISPC_MGR_FLD_STNTFT]		= { },
			[DISPC_MGR_FLD_GO]		= { DISPC_CONTROL,  6,  6 },
			[DISPC_MGR_FLD_TFTDATALINES]	= { },
			[DISPC_MGR_FLD_STALLMODE]	= { },
			[DISPC_MGR_FLD_TCKENABLE]	= { DISPC_CONFIG,  12, 12 },
			[DISPC_MGR_FLD_TCKSELECTION]	= { DISPC_CONFIG,  13, 13 },
			[DISPC_MGR_FLD_CPR]		= { },
			[DISPC_MGR_FLD_FIFOHANDCHECK]	= { DISPC_CONFIG,  16, 16 },
		},
	},
	[OMAP_DSS_CHANNEL_LCD2] = {
		.name		= "LCD2",
		.vsync_irq	= DISPC_IRQ_VSYNC2,
		.framedone_irq	= DISPC_IRQ_FRAMEDONE2,
		.sync_lost_irq	= DISPC_IRQ_SYNC_LOST2,
261 262 263 264 265 266
		.gamma		= {
			.len	= 256,
			.bits	= 8,
			.reg	= DISPC_GAMMA_TABLE1,
			.has_index = true,
		},
267 268 269 270 271 272 273 274 275 276 277 278
		.reg_desc	= {
			[DISPC_MGR_FLD_ENABLE]		= { DISPC_CONTROL2,  0,  0 },
			[DISPC_MGR_FLD_STNTFT]		= { DISPC_CONTROL2,  3,  3 },
			[DISPC_MGR_FLD_GO]		= { DISPC_CONTROL2,  5,  5 },
			[DISPC_MGR_FLD_TFTDATALINES]	= { DISPC_CONTROL2,  9,  8 },
			[DISPC_MGR_FLD_STALLMODE]	= { DISPC_CONTROL2, 11, 11 },
			[DISPC_MGR_FLD_TCKENABLE]	= { DISPC_CONFIG2,  10, 10 },
			[DISPC_MGR_FLD_TCKSELECTION]	= { DISPC_CONFIG2,  11, 11 },
			[DISPC_MGR_FLD_CPR]		= { DISPC_CONFIG2,  15, 15 },
			[DISPC_MGR_FLD_FIFOHANDCHECK]	= { DISPC_CONFIG2,  16, 16 },
		},
	},
279 280 281 282 283
	[OMAP_DSS_CHANNEL_LCD3] = {
		.name		= "LCD3",
		.vsync_irq	= DISPC_IRQ_VSYNC3,
		.framedone_irq	= DISPC_IRQ_FRAMEDONE3,
		.sync_lost_irq	= DISPC_IRQ_SYNC_LOST3,
284 285 286 287 288 289
		.gamma		= {
			.len	= 256,
			.bits	= 8,
			.reg	= DISPC_GAMMA_TABLE3,
			.has_index = true,
		},
290 291 292 293 294 295 296 297 298 299 300 301
		.reg_desc	= {
			[DISPC_MGR_FLD_ENABLE]		= { DISPC_CONTROL3,  0,  0 },
			[DISPC_MGR_FLD_STNTFT]		= { DISPC_CONTROL3,  3,  3 },
			[DISPC_MGR_FLD_GO]		= { DISPC_CONTROL3,  5,  5 },
			[DISPC_MGR_FLD_TFTDATALINES]	= { DISPC_CONTROL3,  9,  8 },
			[DISPC_MGR_FLD_STALLMODE]	= { DISPC_CONTROL3, 11, 11 },
			[DISPC_MGR_FLD_TCKENABLE]	= { DISPC_CONFIG3,  10, 10 },
			[DISPC_MGR_FLD_TCKSELECTION]	= { DISPC_CONFIG3,  11, 11 },
			[DISPC_MGR_FLD_CPR]		= { DISPC_CONFIG3,  15, 15 },
			[DISPC_MGR_FLD_FIFOHANDCHECK]	= { DISPC_CONFIG3,  16, 16 },
		},
	},
302 303
};

304 305 306 307 308
struct color_conv_coef {
	int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
	int full_range;
};

309 310 311 312 313
static unsigned long dispc_fclk_rate(void);
static unsigned long dispc_core_clk_rate(void);
static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);

314 315
static unsigned long dispc_plane_pclk_rate(enum omap_plane_id plane);
static unsigned long dispc_plane_lclk_rate(enum omap_plane_id plane);
T
Tomi Valkeinen 已提交
316

317 318 319 320
static void dispc_clear_irqstatus(u32 mask);
static bool dispc_mgr_is_enabled(enum omap_channel channel);
static void dispc_clear_irqstatus(u32 mask);

321
static inline void dispc_write_reg(const u16 idx, u32 val)
T
Tomi Valkeinen 已提交
322
{
323
	__raw_writel(val, dispc.base + idx);
T
Tomi Valkeinen 已提交
324 325
}

326
static inline u32 dispc_read_reg(const u16 idx)
T
Tomi Valkeinen 已提交
327
{
328
	return __raw_readl(dispc.base + idx);
T
Tomi Valkeinen 已提交
329 330
}

331 332
static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
{
333
	const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld];
334 335 336 337 338
	return REG_GET(rfld.reg, rfld.high, rfld.low);
}

static void mgr_fld_write(enum omap_channel channel,
					enum mgr_reg_fields regfld, int val) {
339
	const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld];
340 341 342 343 344 345
	const bool need_lock = rfld.reg == DISPC_CONTROL || rfld.reg == DISPC_CONFIG;
	unsigned long flags;

	if (need_lock)
		spin_lock_irqsave(&dispc.control_lock, flags);

346
	REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low);
347 348 349

	if (need_lock)
		spin_unlock_irqrestore(&dispc.control_lock, flags);
350 351
}

T
Tomi Valkeinen 已提交
352
#define SR(reg) \
353
	dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
T
Tomi Valkeinen 已提交
354
#define RR(reg) \
355
	dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)])
T
Tomi Valkeinen 已提交
356

357
static void dispc_save_context(void)
T
Tomi Valkeinen 已提交
358
{
359
	int i, j;
T
Tomi Valkeinen 已提交
360

361 362
	DSSDBG("dispc_save_context\n");

T
Tomi Valkeinen 已提交
363 364 365 366
	SR(IRQENABLE);
	SR(CONTROL);
	SR(CONFIG);
	SR(LINE_NUMBER);
367 368
	if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
			dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
369
		SR(GLOBAL_ALPHA);
370 371 372 373
	if (dss_has_feature(FEAT_MGR_LCD2)) {
		SR(CONTROL2);
		SR(CONFIG2);
	}
374 375 376 377
	if (dss_has_feature(FEAT_MGR_LCD3)) {
		SR(CONTROL3);
		SR(CONFIG3);
	}
T
Tomi Valkeinen 已提交
378

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
	for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
		SR(DEFAULT_COLOR(i));
		SR(TRANS_COLOR(i));
		SR(SIZE_MGR(i));
		if (i == OMAP_DSS_CHANNEL_DIGIT)
			continue;
		SR(TIMING_H(i));
		SR(TIMING_V(i));
		SR(POL_FREQ(i));
		SR(DIVISORo(i));

		SR(DATA_CYCLE1(i));
		SR(DATA_CYCLE2(i));
		SR(DATA_CYCLE3(i));

394
		if (dss_has_feature(FEAT_CPR)) {
395 396 397
			SR(CPR_COEF_R(i));
			SR(CPR_COEF_G(i));
			SR(CPR_COEF_B(i));
398
		}
399
	}
T
Tomi Valkeinen 已提交
400

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
	for (i = 0; i < dss_feat_get_num_ovls(); i++) {
		SR(OVL_BA0(i));
		SR(OVL_BA1(i));
		SR(OVL_POSITION(i));
		SR(OVL_SIZE(i));
		SR(OVL_ATTRIBUTES(i));
		SR(OVL_FIFO_THRESHOLD(i));
		SR(OVL_ROW_INC(i));
		SR(OVL_PIXEL_INC(i));
		if (dss_has_feature(FEAT_PRELOAD))
			SR(OVL_PRELOAD(i));
		if (i == OMAP_DSS_GFX) {
			SR(OVL_WINDOW_SKIP(i));
			SR(OVL_TABLE_BA(i));
			continue;
		}
		SR(OVL_FIR(i));
		SR(OVL_PICTURE_SIZE(i));
		SR(OVL_ACCU0(i));
		SR(OVL_ACCU1(i));
421

422 423
		for (j = 0; j < 8; j++)
			SR(OVL_FIR_COEF_H(i, j));
424

425 426
		for (j = 0; j < 8; j++)
			SR(OVL_FIR_COEF_HV(i, j));
427

428 429
		for (j = 0; j < 5; j++)
			SR(OVL_CONV_COEF(i, j));
430

431 432 433 434
		if (dss_has_feature(FEAT_FIR_COEF_V)) {
			for (j = 0; j < 8; j++)
				SR(OVL_FIR_COEF_V(i, j));
		}
435

436 437 438 439 440 441
		if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
			SR(OVL_BA0_UV(i));
			SR(OVL_BA1_UV(i));
			SR(OVL_FIR2(i));
			SR(OVL_ACCU2_0(i));
			SR(OVL_ACCU2_1(i));
442

443 444
			for (j = 0; j < 8; j++)
				SR(OVL_FIR_COEF_H2(i, j));
445

446 447
			for (j = 0; j < 8; j++)
				SR(OVL_FIR_COEF_HV2(i, j));
448

449 450 451 452 453
			for (j = 0; j < 8; j++)
				SR(OVL_FIR_COEF_V2(i, j));
		}
		if (dss_has_feature(FEAT_ATTR2))
			SR(OVL_ATTRIBUTES2(i));
454
	}
455 456 457

	if (dss_has_feature(FEAT_CORE_CLK_DIV))
		SR(DIVISOR);
458 459 460

	dispc.ctx_valid = true;

461
	DSSDBG("context saved\n");
T
Tomi Valkeinen 已提交
462 463
}

464
static void dispc_restore_context(void)
T
Tomi Valkeinen 已提交
465
{
466
	int i, j;
467 468 469

	DSSDBG("dispc_restore_context\n");

470 471 472
	if (!dispc.ctx_valid)
		return;

473
	/*RR(IRQENABLE);*/
T
Tomi Valkeinen 已提交
474 475 476
	/*RR(CONTROL);*/
	RR(CONFIG);
	RR(LINE_NUMBER);
477 478
	if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
			dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
479
		RR(GLOBAL_ALPHA);
480
	if (dss_has_feature(FEAT_MGR_LCD2))
481
		RR(CONFIG2);
482 483
	if (dss_has_feature(FEAT_MGR_LCD3))
		RR(CONFIG3);
T
Tomi Valkeinen 已提交
484

485 486 487 488 489 490 491 492 493 494 495 496 497 498
	for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
		RR(DEFAULT_COLOR(i));
		RR(TRANS_COLOR(i));
		RR(SIZE_MGR(i));
		if (i == OMAP_DSS_CHANNEL_DIGIT)
			continue;
		RR(TIMING_H(i));
		RR(TIMING_V(i));
		RR(POL_FREQ(i));
		RR(DIVISORo(i));

		RR(DATA_CYCLE1(i));
		RR(DATA_CYCLE2(i));
		RR(DATA_CYCLE3(i));
499

500
		if (dss_has_feature(FEAT_CPR)) {
501 502 503
			RR(CPR_COEF_R(i));
			RR(CPR_COEF_G(i));
			RR(CPR_COEF_B(i));
504
		}
505
	}
T
Tomi Valkeinen 已提交
506

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
	for (i = 0; i < dss_feat_get_num_ovls(); i++) {
		RR(OVL_BA0(i));
		RR(OVL_BA1(i));
		RR(OVL_POSITION(i));
		RR(OVL_SIZE(i));
		RR(OVL_ATTRIBUTES(i));
		RR(OVL_FIFO_THRESHOLD(i));
		RR(OVL_ROW_INC(i));
		RR(OVL_PIXEL_INC(i));
		if (dss_has_feature(FEAT_PRELOAD))
			RR(OVL_PRELOAD(i));
		if (i == OMAP_DSS_GFX) {
			RR(OVL_WINDOW_SKIP(i));
			RR(OVL_TABLE_BA(i));
			continue;
		}
		RR(OVL_FIR(i));
		RR(OVL_PICTURE_SIZE(i));
		RR(OVL_ACCU0(i));
		RR(OVL_ACCU1(i));
527

528 529
		for (j = 0; j < 8; j++)
			RR(OVL_FIR_COEF_H(i, j));
530

531 532
		for (j = 0; j < 8; j++)
			RR(OVL_FIR_COEF_HV(i, j));
533

534 535
		for (j = 0; j < 5; j++)
			RR(OVL_CONV_COEF(i, j));
536

537 538 539 540
		if (dss_has_feature(FEAT_FIR_COEF_V)) {
			for (j = 0; j < 8; j++)
				RR(OVL_FIR_COEF_V(i, j));
		}
541

542 543 544 545 546 547
		if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
			RR(OVL_BA0_UV(i));
			RR(OVL_BA1_UV(i));
			RR(OVL_FIR2(i));
			RR(OVL_ACCU2_0(i));
			RR(OVL_ACCU2_1(i));
548

549 550
			for (j = 0; j < 8; j++)
				RR(OVL_FIR_COEF_H2(i, j));
551

552 553
			for (j = 0; j < 8; j++)
				RR(OVL_FIR_COEF_HV2(i, j));
554

555 556 557 558 559
			for (j = 0; j < 8; j++)
				RR(OVL_FIR_COEF_V2(i, j));
		}
		if (dss_has_feature(FEAT_ATTR2))
			RR(OVL_ATTRIBUTES2(i));
560
	}
T
Tomi Valkeinen 已提交
561

562 563 564
	if (dss_has_feature(FEAT_CORE_CLK_DIV))
		RR(DIVISOR);

T
Tomi Valkeinen 已提交
565 566
	/* enable last, because LCD & DIGIT enable are here */
	RR(CONTROL);
567 568
	if (dss_has_feature(FEAT_MGR_LCD2))
		RR(CONTROL2);
569 570
	if (dss_has_feature(FEAT_MGR_LCD3))
		RR(CONTROL3);
571
	/* clear spurious SYNC_LOST_DIGIT interrupts */
572
	dispc_clear_irqstatus(DISPC_IRQ_SYNC_LOST_DIGIT);
573 574 575 576 577 578

	/*
	 * enable last so IRQs won't trigger before
	 * the context is fully restored
	 */
	RR(IRQENABLE);
579 580

	DSSDBG("context restored\n");
T
Tomi Valkeinen 已提交
581 582 583 584 585
}

#undef SR
#undef RR

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
int dispc_runtime_get(void)
{
	int r;

	DSSDBG("dispc_runtime_get\n");

	r = pm_runtime_get_sync(&dispc.pdev->dev);
	WARN_ON(r < 0);
	return r < 0 ? r : 0;
}

void dispc_runtime_put(void)
{
	int r;

	DSSDBG("dispc_runtime_put\n");

603
	r = pm_runtime_put_sync(&dispc.pdev->dev);
604
	WARN_ON(r < 0 && r != -ENOSYS);
T
Tomi Valkeinen 已提交
605 606
}

607
static u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
608
{
609
	return mgr_desc[channel].vsync_irq;
610 611
}

612
static u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
613
{
614 615 616
	if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc.feat->no_framedone_tv)
		return 0;

617
	return mgr_desc[channel].framedone_irq;
618 619
}

620
static u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel)
621 622 623 624
{
	return mgr_desc[channel].sync_lost_irq;
}

625 626 627 628 629
u32 dispc_wb_get_framedone_irq(void)
{
	return DISPC_IRQ_FRAMEDONEWB;
}

630
static void dispc_mgr_enable(enum omap_channel channel, bool enable)
631 632 633 634 635 636 637 638 639 640 641
{
	mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
	/* flush posted write */
	mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}

static bool dispc_mgr_is_enabled(enum omap_channel channel)
{
	return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}

642
static bool dispc_mgr_go_busy(enum omap_channel channel)
T
Tomi Valkeinen 已提交
643
{
644
	return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
T
Tomi Valkeinen 已提交
645 646
}

647
static void dispc_mgr_go(enum omap_channel channel)
T
Tomi Valkeinen 已提交
648
{
649
	WARN_ON(!dispc_mgr_is_enabled(channel));
650
	WARN_ON(dispc_mgr_go_busy(channel));
T
Tomi Valkeinen 已提交
651

652
	DSSDBG("GO %s\n", mgr_desc[channel].name);
T
Tomi Valkeinen 已提交
653

654
	mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
T
Tomi Valkeinen 已提交
655 656
}

657 658 659 660 661 662 663
bool dispc_wb_go_busy(void)
{
	return REG_GET(DISPC_CONTROL2, 6, 6) == 1;
}

void dispc_wb_go(void)
{
664
	enum omap_plane_id plane = OMAP_DSS_WB;
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
	bool enable, go;

	enable = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1;

	if (!enable)
		return;

	go = REG_GET(DISPC_CONTROL2, 6, 6) == 1;
	if (go) {
		DSSERR("GO bit not down for WB\n");
		return;
	}

	REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6);
}

681 682
static void dispc_ovl_write_firh_reg(enum omap_plane_id plane, int reg,
				     u32 value)
T
Tomi Valkeinen 已提交
683
{
684
	dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value);
T
Tomi Valkeinen 已提交
685 686
}

687 688
static void dispc_ovl_write_firhv_reg(enum omap_plane_id plane, int reg,
				      u32 value)
T
Tomi Valkeinen 已提交
689
{
690
	dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value);
T
Tomi Valkeinen 已提交
691 692
}

693 694
static void dispc_ovl_write_firv_reg(enum omap_plane_id plane, int reg,
				     u32 value)
T
Tomi Valkeinen 已提交
695
{
696
	dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value);
T
Tomi Valkeinen 已提交
697 698
}

699 700
static void dispc_ovl_write_firh2_reg(enum omap_plane_id plane, int reg,
				      u32 value)
701 702 703 704 705 706
{
	BUG_ON(plane == OMAP_DSS_GFX);

	dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value);
}

707
static void dispc_ovl_write_firhv2_reg(enum omap_plane_id plane, int reg,
708
		u32 value)
709 710 711 712 713 714
{
	BUG_ON(plane == OMAP_DSS_GFX);

	dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value);
}

715 716
static void dispc_ovl_write_firv2_reg(enum omap_plane_id plane, int reg,
				      u32 value)
717 718 719 720 721 722
{
	BUG_ON(plane == OMAP_DSS_GFX);

	dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value);
}

723
static void dispc_ovl_set_scale_coef(enum omap_plane_id plane, int fir_hinc,
724 725
				int fir_vinc, int five_taps,
				enum omap_color_component color_comp)
T
Tomi Valkeinen 已提交
726
{
727
	const struct dispc_coef *h_coef, *v_coef;
T
Tomi Valkeinen 已提交
728 729
	int i;

730 731
	h_coef = dispc_ovl_get_scale_coef(fir_hinc, true);
	v_coef = dispc_ovl_get_scale_coef(fir_vinc, five_taps);
T
Tomi Valkeinen 已提交
732 733 734 735

	for (i = 0; i < 8; i++) {
		u32 h, hv;

736 737 738 739 740 741 742 743
		h = FLD_VAL(h_coef[i].hc0_vc00, 7, 0)
			| FLD_VAL(h_coef[i].hc1_vc0, 15, 8)
			| FLD_VAL(h_coef[i].hc2_vc1, 23, 16)
			| FLD_VAL(h_coef[i].hc3_vc2, 31, 24);
		hv = FLD_VAL(h_coef[i].hc4_vc22, 7, 0)
			| FLD_VAL(v_coef[i].hc1_vc0, 15, 8)
			| FLD_VAL(v_coef[i].hc2_vc1, 23, 16)
			| FLD_VAL(v_coef[i].hc3_vc2, 31, 24);
T
Tomi Valkeinen 已提交
744

745
		if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
746 747
			dispc_ovl_write_firh_reg(plane, i, h);
			dispc_ovl_write_firhv_reg(plane, i, hv);
748
		} else {
749 750
			dispc_ovl_write_firh2_reg(plane, i, h);
			dispc_ovl_write_firhv2_reg(plane, i, hv);
751 752
		}

T
Tomi Valkeinen 已提交
753 754
	}

755 756 757
	if (five_taps) {
		for (i = 0; i < 8; i++) {
			u32 v;
758 759
			v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0)
				| FLD_VAL(v_coef[i].hc4_vc22, 15, 8);
760
			if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
761
				dispc_ovl_write_firv_reg(plane, i, v);
762
			else
763
				dispc_ovl_write_firv2_reg(plane, i, v);
764
		}
T
Tomi Valkeinen 已提交
765 766 767 768
	}
}


769
static void dispc_ovl_write_color_conv_coef(enum omap_plane_id plane,
770 771
		const struct color_conv_coef *ct)
{
T
Tomi Valkeinen 已提交
772 773
#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))

774 775 776 777 778
	dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry));
	dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy,  ct->rcb));
	dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr));
	dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by));
	dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb));
T
Tomi Valkeinen 已提交
779

780
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11);
T
Tomi Valkeinen 已提交
781 782 783 784

#undef CVAL
}

785 786 787 788 789
static void dispc_setup_color_conv_coef(void)
{
	int i;
	int num_ovl = dss_feat_get_num_ovls();
	const struct color_conv_coef ctbl_bt601_5_ovl = {
790
		/* YUV -> RGB */
791 792 793
		298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
	};
	const struct color_conv_coef ctbl_bt601_5_wb = {
794 795
		/* RGB -> YUV */
		66, 129, 25, 112, -94, -18, -38, -74, 112, 0,
796 797 798 799 800
	};

	for (i = 1; i < num_ovl; i++)
		dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl);

T
Tomi Valkeinen 已提交
801 802
	if (dispc.feat->has_writeback)
		dispc_ovl_write_color_conv_coef(OMAP_DSS_WB, &ctbl_bt601_5_wb);
803
}
T
Tomi Valkeinen 已提交
804

805
static void dispc_ovl_set_ba0(enum omap_plane_id plane, u32 paddr)
T
Tomi Valkeinen 已提交
806
{
807
	dispc_write_reg(DISPC_OVL_BA0(plane), paddr);
T
Tomi Valkeinen 已提交
808 809
}

810
static void dispc_ovl_set_ba1(enum omap_plane_id plane, u32 paddr)
T
Tomi Valkeinen 已提交
811
{
812
	dispc_write_reg(DISPC_OVL_BA1(plane), paddr);
T
Tomi Valkeinen 已提交
813 814
}

815
static void dispc_ovl_set_ba0_uv(enum omap_plane_id plane, u32 paddr)
816 817 818 819
{
	dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr);
}

820
static void dispc_ovl_set_ba1_uv(enum omap_plane_id plane, u32 paddr)
821 822 823 824
{
	dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr);
}

825
static void dispc_ovl_set_pos(enum omap_plane_id plane,
826
		enum omap_overlay_caps caps, int x, int y)
T
Tomi Valkeinen 已提交
827
{
828 829 830 831 832 833
	u32 val;

	if ((caps & OMAP_DSS_OVL_CAP_POS) == 0)
		return;

	val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
834 835

	dispc_write_reg(DISPC_OVL_POSITION(plane), val);
T
Tomi Valkeinen 已提交
836 837
}

838
static void dispc_ovl_set_input_size(enum omap_plane_id plane, int width,
839
		int height)
T
Tomi Valkeinen 已提交
840 841
{
	u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
842

843
	if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB)
844 845 846
		dispc_write_reg(DISPC_OVL_SIZE(plane), val);
	else
		dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
T
Tomi Valkeinen 已提交
847 848
}

849
static void dispc_ovl_set_output_size(enum omap_plane_id plane, int width,
850
		int height)
T
Tomi Valkeinen 已提交
851 852 853 854 855 856
{
	u32 val;

	BUG_ON(plane == OMAP_DSS_GFX);

	val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
857

858 859 860 861
	if (plane == OMAP_DSS_WB)
		dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
	else
		dispc_write_reg(DISPC_OVL_SIZE(plane), val);
T
Tomi Valkeinen 已提交
862 863
}

864
static void dispc_ovl_set_zorder(enum omap_plane_id plane,
865
		enum omap_overlay_caps caps, u8 zorder)
866
{
867
	if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
		return;

	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26);
}

static void dispc_ovl_enable_zorder_planes(void)
{
	int i;

	if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
		return;

	for (i = 0; i < dss_feat_get_num_ovls(); i++)
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25);
}

884
static void dispc_ovl_set_pre_mult_alpha(enum omap_plane_id plane,
885
		enum omap_overlay_caps caps, bool enable)
886
{
887
	if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
888 889
		return;

890
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28);
891 892
}

893
static void dispc_ovl_setup_global_alpha(enum omap_plane_id plane,
894
		enum omap_overlay_caps caps, u8 global_alpha)
T
Tomi Valkeinen 已提交
895
{
896
	static const unsigned shifts[] = { 0, 8, 16, 24, };
897 898
	int shift;

899
	if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
900
		return;
901

902 903
	shift = shifts[plane];
	REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift);
T
Tomi Valkeinen 已提交
904 905
}

906
static void dispc_ovl_set_pix_inc(enum omap_plane_id plane, s32 inc)
T
Tomi Valkeinen 已提交
907
{
908
	dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc);
T
Tomi Valkeinen 已提交
909 910
}

911
static void dispc_ovl_set_row_inc(enum omap_plane_id plane, s32 inc)
T
Tomi Valkeinen 已提交
912
{
913
	dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc);
T
Tomi Valkeinen 已提交
914 915
}

916
static void dispc_ovl_set_color_mode(enum omap_plane_id plane, u32 fourcc)
T
Tomi Valkeinen 已提交
917 918
{
	u32 m = 0;
919
	if (plane != OMAP_DSS_GFX) {
920
		switch (fourcc) {
921
		case DRM_FORMAT_NV12:
922
			m = 0x0; break;
923
		case DRM_FORMAT_XRGB4444:
924
			m = 0x1; break;
925
		case DRM_FORMAT_RGBA4444:
926
			m = 0x2; break;
927
		case DRM_FORMAT_RGBX4444:
928
			m = 0x4; break;
929
		case DRM_FORMAT_ARGB4444:
930
			m = 0x5; break;
931
		case DRM_FORMAT_RGB565:
932
			m = 0x6; break;
933
		case DRM_FORMAT_ARGB1555:
934
			m = 0x7; break;
935
		case DRM_FORMAT_XRGB8888:
936
			m = 0x8; break;
937
		case DRM_FORMAT_RGB888:
938
			m = 0x9; break;
939
		case DRM_FORMAT_YUYV:
940
			m = 0xa; break;
941
		case DRM_FORMAT_UYVY:
942
			m = 0xb; break;
943
		case DRM_FORMAT_ARGB8888:
944
			m = 0xc; break;
945
		case DRM_FORMAT_RGBA8888:
946
			m = 0xd; break;
947
		case DRM_FORMAT_RGBX8888:
948
			m = 0xe; break;
949
		case DRM_FORMAT_XRGB1555:
950 951
			m = 0xf; break;
		default:
952
			BUG(); return;
953 954
		}
	} else {
955
		switch (fourcc) {
956
		case DRM_FORMAT_RGBX4444:
957
			m = 0x4; break;
958
		case DRM_FORMAT_ARGB4444:
959
			m = 0x5; break;
960
		case DRM_FORMAT_RGB565:
961
			m = 0x6; break;
962
		case DRM_FORMAT_ARGB1555:
963
			m = 0x7; break;
964
		case DRM_FORMAT_XRGB8888:
965
			m = 0x8; break;
966
		case DRM_FORMAT_RGB888:
967
			m = 0x9; break;
968
		case DRM_FORMAT_XRGB4444:
969
			m = 0xa; break;
970
		case DRM_FORMAT_RGBA4444:
971
			m = 0xb; break;
972
		case DRM_FORMAT_ARGB8888:
973
			m = 0xc; break;
974
		case DRM_FORMAT_RGBA8888:
975
			m = 0xd; break;
976
		case DRM_FORMAT_RGBX8888:
977
			m = 0xe; break;
978
		case DRM_FORMAT_XRGB1555:
979 980
			m = 0xf; break;
		default:
981
			BUG(); return;
982
		}
T
Tomi Valkeinen 已提交
983 984
	}

985
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
T
Tomi Valkeinen 已提交
986 987
}

988
static bool format_is_yuv(u32 fourcc)
989
{
990
	switch (fourcc) {
991 992 993
	case DRM_FORMAT_YUYV:
	case DRM_FORMAT_UYVY:
	case DRM_FORMAT_NV12:
994 995 996 997 998 999
		return true;
	default:
		return false;
	}
}

1000
static void dispc_ovl_configure_burst_type(enum omap_plane_id plane,
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
		enum omap_dss_rotation_type rotation_type)
{
	if (dss_has_feature(FEAT_BURST_2D) == 0)
		return;

	if (rotation_type == OMAP_DSS_ROT_TILER)
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29);
	else
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29);
}

1012 1013
static void dispc_ovl_set_channel_out(enum omap_plane_id plane,
				      enum omap_channel channel)
T
Tomi Valkeinen 已提交
1014 1015 1016
{
	int shift;
	u32 val;
1017
	int chan = 0, chan2 = 0;
T
Tomi Valkeinen 已提交
1018 1019 1020 1021 1022 1023 1024

	switch (plane) {
	case OMAP_DSS_GFX:
		shift = 8;
		break;
	case OMAP_DSS_VIDEO1:
	case OMAP_DSS_VIDEO2:
1025
	case OMAP_DSS_VIDEO3:
T
Tomi Valkeinen 已提交
1026 1027 1028 1029 1030 1031 1032
		shift = 16;
		break;
	default:
		BUG();
		return;
	}

1033
	val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
	if (dss_has_feature(FEAT_MGR_LCD2)) {
		switch (channel) {
		case OMAP_DSS_CHANNEL_LCD:
			chan = 0;
			chan2 = 0;
			break;
		case OMAP_DSS_CHANNEL_DIGIT:
			chan = 1;
			chan2 = 0;
			break;
		case OMAP_DSS_CHANNEL_LCD2:
			chan = 0;
			chan2 = 1;
			break;
1048 1049 1050 1051 1052 1053 1054 1055 1056
		case OMAP_DSS_CHANNEL_LCD3:
			if (dss_has_feature(FEAT_MGR_LCD3)) {
				chan = 0;
				chan2 = 2;
			} else {
				BUG();
				return;
			}
			break;
1057 1058 1059 1060
		case OMAP_DSS_CHANNEL_WB:
			chan = 0;
			chan2 = 3;
			break;
1061 1062
		default:
			BUG();
1063
			return;
1064 1065 1066 1067 1068 1069 1070
		}

		val = FLD_MOD(val, chan, shift, shift);
		val = FLD_MOD(val, chan2, 31, 30);
	} else {
		val = FLD_MOD(val, channel, shift, shift);
	}
1071
	dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
T
Tomi Valkeinen 已提交
1072 1073
}

1074
static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane_id plane)
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
{
	int shift;
	u32 val;

	switch (plane) {
	case OMAP_DSS_GFX:
		shift = 8;
		break;
	case OMAP_DSS_VIDEO1:
	case OMAP_DSS_VIDEO2:
	case OMAP_DSS_VIDEO3:
		shift = 16;
		break;
	default:
		BUG();
1090
		return 0;
1091 1092 1093 1094
	}

	val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));

1095 1096
	if (FLD_GET(val, shift, shift) == 1)
		return OMAP_DSS_CHANNEL_DIGIT;
1097

1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
	if (!dss_has_feature(FEAT_MGR_LCD2))
		return OMAP_DSS_CHANNEL_LCD;

	switch (FLD_GET(val, 31, 30)) {
	case 0:
	default:
		return OMAP_DSS_CHANNEL_LCD;
	case 1:
		return OMAP_DSS_CHANNEL_LCD2;
	case 2:
		return OMAP_DSS_CHANNEL_LCD3;
1109 1110
	case 3:
		return OMAP_DSS_CHANNEL_WB;
1111
	}
1112 1113
}

1114 1115
void dispc_wb_set_channel_in(enum dss_writeback_channel channel)
{
1116
	enum omap_plane_id plane = OMAP_DSS_WB;
1117 1118 1119 1120

	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), channel, 18, 16);
}

1121
static void dispc_ovl_set_burst_size(enum omap_plane_id plane,
T
Tomi Valkeinen 已提交
1122 1123
		enum omap_burst_size burst_size)
{
1124
	static const unsigned shifts[] = { 6, 14, 14, 14, 14, };
T
Tomi Valkeinen 已提交
1125 1126
	int shift;

1127
	shift = shifts[plane];
1128
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift);
T
Tomi Valkeinen 已提交
1129 1130
}

1131 1132 1133 1134 1135 1136
static void dispc_configure_burst_sizes(void)
{
	int i;
	const int burst_size = BURST_SIZE_X8;

	/* Configure burst size always to maximum size */
1137
	for (i = 0; i < dss_feat_get_num_ovls(); ++i)
1138
		dispc_ovl_set_burst_size(i, burst_size);
1139 1140
	if (dispc.feat->has_writeback)
		dispc_ovl_set_burst_size(OMAP_DSS_WB, burst_size);
1141 1142
}

1143
static u32 dispc_ovl_get_burst_size(enum omap_plane_id plane)
1144 1145
{
	/* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
1146
	return dispc.feat->burst_size_unit * 8;
1147 1148
}

1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
static bool dispc_ovl_color_mode_supported(enum omap_plane_id plane, u32 fourcc)
{
	const u32 *modes;
	unsigned int i;

	modes = dispc.feat->supported_color_modes[plane];

	for (i = 0; modes[i]; ++i) {
		if (modes[i] == fourcc)
			return true;
	}

	return false;
}

1164
static const u32 *dispc_ovl_get_color_modes(enum omap_plane_id plane)
1165
{
1166
	return dispc.feat->supported_color_modes[plane];
1167 1168
}

1169
static int dispc_get_num_ovls(void)
1170 1171 1172 1173
{
	return dss_feat_get_num_ovls();
}

1174
static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
1175
{
1176
	if (channel == OMAP_DSS_CHANNEL_DIGIT)
1177 1178
		return;

1179
	mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable);
1180 1181
}

1182
static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
1183
		const struct omap_dss_cpr_coefs *coefs)
1184 1185 1186
{
	u32 coef_r, coef_g, coef_b;

1187
	if (!dss_mgr_is_lcd(channel))
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
		return;

	coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
		FLD_VAL(coefs->rb, 9, 0);
	coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) |
		FLD_VAL(coefs->gb, 9, 0);
	coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) |
		FLD_VAL(coefs->bb, 9, 0);

	dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r);
	dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g);
	dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b);
}

1202 1203
static void dispc_ovl_set_vid_color_conv(enum omap_plane_id plane,
					 bool enable)
T
Tomi Valkeinen 已提交
1204 1205 1206 1207 1208
{
	u32 val;

	BUG_ON(plane == OMAP_DSS_GFX);

1209
	val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
T
Tomi Valkeinen 已提交
1210
	val = FLD_MOD(val, enable, 9, 9);
1211
	dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
T
Tomi Valkeinen 已提交
1212 1213
}

1214
static void dispc_ovl_enable_replication(enum omap_plane_id plane,
1215
		enum omap_overlay_caps caps, bool enable)
T
Tomi Valkeinen 已提交
1216
{
1217
	static const unsigned shifts[] = { 5, 10, 10, 10 };
1218
	int shift;
T
Tomi Valkeinen 已提交
1219

1220 1221 1222
	if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0)
		return;

1223 1224
	shift = shifts[plane];
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift);
T
Tomi Valkeinen 已提交
1225 1226
}

1227
static void dispc_mgr_set_size(enum omap_channel channel, u16 width,
1228
		u16 height)
T
Tomi Valkeinen 已提交
1229 1230 1231
{
	u32 val;

1232 1233 1234
	val = FLD_VAL(height - 1, dispc.feat->mgr_height_start, 16) |
		FLD_VAL(width - 1, dispc.feat->mgr_width_start, 0);

1235
	dispc_write_reg(DISPC_SIZE_MGR(channel), val);
T
Tomi Valkeinen 已提交
1236 1237
}

1238
static void dispc_init_fifos(void)
T
Tomi Valkeinen 已提交
1239 1240
{
	u32 size;
1241
	int fifo;
1242
	u8 start, end;
1243
	u32 unit;
1244
	int i;
1245

1246
	unit = dispc.feat->buffer_size_unit;
T
Tomi Valkeinen 已提交
1247

1248
	dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
T
Tomi Valkeinen 已提交
1249

1250 1251
	for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
		size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end);
1252
		size *= unit;
1253 1254 1255 1256 1257 1258 1259
		dispc.fifo_size[fifo] = size;

		/*
		 * By default fifos are mapped directly to overlays, fifo 0 to
		 * ovl 0, fifo 1 to ovl 1, etc.
		 */
		dispc.fifo_assignment[fifo] = fifo;
T
Tomi Valkeinen 已提交
1260
	}
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283

	/*
	 * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo
	 * causes problems with certain use cases, like using the tiler in 2D
	 * mode. The below hack swaps the fifos of GFX and WB planes, thus
	 * giving GFX plane a larger fifo. WB but should work fine with a
	 * smaller fifo.
	 */
	if (dispc.feat->gfx_fifo_workaround) {
		u32 v;

		v = dispc_read_reg(DISPC_GLOBAL_BUFFER);

		v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */
		v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */
		v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */
		v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */

		dispc_write_reg(DISPC_GLOBAL_BUFFER, v);

		dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB;
		dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX;
	}
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297

	/*
	 * Setup default fifo thresholds.
	 */
	for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
		u32 low, high;
		const bool use_fifomerge = false;
		const bool manual_update = false;

		dispc_ovl_compute_fifo_thresholds(i, &low, &high,
			use_fifomerge, manual_update);

		dispc_ovl_set_fifo_threshold(i, low, high);
	}
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308

	if (dispc.feat->has_writeback) {
		u32 low, high;
		const bool use_fifomerge = false;
		const bool manual_update = false;

		dispc_ovl_compute_fifo_thresholds(OMAP_DSS_WB, &low, &high,
			use_fifomerge, manual_update);

		dispc_ovl_set_fifo_threshold(OMAP_DSS_WB, low, high);
	}
T
Tomi Valkeinen 已提交
1309 1310
}

1311
static u32 dispc_ovl_get_fifo_size(enum omap_plane_id plane)
T
Tomi Valkeinen 已提交
1312
{
1313 1314 1315 1316 1317 1318 1319 1320 1321
	int fifo;
	u32 size = 0;

	for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
		if (dispc.fifo_assignment[fifo] == plane)
			size += dispc.fifo_size[fifo];
	}

	return size;
T
Tomi Valkeinen 已提交
1322 1323
}

1324 1325
void dispc_ovl_set_fifo_threshold(enum omap_plane_id plane, u32 low,
				  u32 high)
T
Tomi Valkeinen 已提交
1326
{
1327
	u8 hi_start, hi_end, lo_start, lo_end;
1328 1329
	u32 unit;

1330
	unit = dispc.feat->buffer_size_unit;
1331 1332 1333 1334 1335 1336

	WARN_ON(low % unit != 0);
	WARN_ON(high % unit != 0);

	low /= unit;
	high /= unit;
1337

1338 1339 1340
	dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
	dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);

1341
	DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n",
T
Tomi Valkeinen 已提交
1342
			plane,
1343
			REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
1344
				lo_start, lo_end) * unit,
1345
			REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
1346 1347
				hi_start, hi_end) * unit,
			low * unit, high * unit);
T
Tomi Valkeinen 已提交
1348

1349
	dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane),
1350 1351
			FLD_VAL(high, hi_start, hi_end) |
			FLD_VAL(low, lo_start, lo_end));
1352 1353 1354 1355 1356 1357 1358 1359 1360

	/*
	 * configure the preload to the pipeline's high threhold, if HT it's too
	 * large for the preload field, set the threshold to the maximum value
	 * that can be held by the preload register
	 */
	if (dss_has_feature(FEAT_PRELOAD) && dispc.feat->set_max_preload &&
			plane != OMAP_DSS_WB)
		dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu));
T
Tomi Valkeinen 已提交
1361 1362 1363 1364
}

void dispc_enable_fifomerge(bool enable)
{
1365 1366 1367 1368 1369
	if (!dss_has_feature(FEAT_FIFO_MERGE)) {
		WARN_ON(enable);
		return;
	}

T
Tomi Valkeinen 已提交
1370 1371 1372 1373
	DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
	REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
}

1374
void dispc_ovl_compute_fifo_thresholds(enum omap_plane_id plane,
1375 1376
		u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
		bool manual_update)
1377 1378 1379 1380 1381 1382
{
	/*
	 * All sizes are in bytes. Both the buffer and burst are made of
	 * buffer_units, and the fifo thresholds must be buffer_unit aligned.
	 */

1383
	unsigned buf_unit = dispc.feat->buffer_size_unit;
1384 1385
	unsigned ovl_fifo_size, total_fifo_size, burst_size;
	int i;
1386 1387

	burst_size = dispc_ovl_get_burst_size(plane);
1388
	ovl_fifo_size = dispc_ovl_get_fifo_size(plane);
1389

1390 1391
	if (use_fifomerge) {
		total_fifo_size = 0;
1392
		for (i = 0; i < dss_feat_get_num_ovls(); ++i)
1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
			total_fifo_size += dispc_ovl_get_fifo_size(i);
	} else {
		total_fifo_size = ovl_fifo_size;
	}

	/*
	 * We use the same low threshold for both fifomerge and non-fifomerge
	 * cases, but for fifomerge we calculate the high threshold using the
	 * combined fifo size
	 */

1404
	if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) {
1405 1406
		*fifo_low = ovl_fifo_size - burst_size * 2;
		*fifo_high = total_fifo_size - burst_size;
1407 1408 1409 1410 1411 1412 1413 1414
	} else if (plane == OMAP_DSS_WB) {
		/*
		 * Most optimal configuration for writeback is to push out data
		 * to the interconnect the moment writeback pushes enough pixels
		 * in the FIFO to form a burst
		 */
		*fifo_low = 0;
		*fifo_high = burst_size;
1415 1416 1417 1418
	} else {
		*fifo_low = ovl_fifo_size - burst_size;
		*fifo_high = total_fifo_size - buf_unit;
	}
1419 1420
}

1421
static void dispc_ovl_set_mflag(enum omap_plane_id plane, bool enable)
T
Tomi Valkeinen 已提交
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
{
	int bit;

	if (plane == OMAP_DSS_GFX)
		bit = 14;
	else
		bit = 23;

	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit);
}

1433
static void dispc_ovl_set_mflag_threshold(enum omap_plane_id plane,
T
Tomi Valkeinen 已提交
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
	int low, int high)
{
	dispc_write_reg(DISPC_OVL_MFLAG_THRESHOLD(plane),
		FLD_VAL(high, 31, 16) |	FLD_VAL(low, 15, 0));
}

static void dispc_init_mflag(void)
{
	int i;

1444 1445 1446 1447 1448 1449 1450 1451 1452 1453
	/*
	 * HACK: NV12 color format and MFLAG seem to have problems working
	 * together: using two displays, and having an NV12 overlay on one of
	 * the displays will cause underflows/synclosts when MFLAG_CTRL=2.
	 * Changing MFLAG thresholds and PRELOAD to certain values seem to
	 * remove the errors, but there doesn't seem to be a clear logic on
	 * which values work and which not.
	 *
	 * As a work-around, set force MFLAG to always on.
	 */
T
Tomi Valkeinen 已提交
1454
	dispc_write_reg(DISPC_GLOBAL_MFLAG_ATTRIBUTE,
1455
		(1 << 0) |	/* MFLAG_CTRL = force always on */
T
Tomi Valkeinen 已提交
1456 1457 1458 1459
		(0 << 2));	/* MFLAG_START = disable */

	for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
		u32 size = dispc_ovl_get_fifo_size(i);
1460
		u32 unit = dispc.feat->buffer_size_unit;
T
Tomi Valkeinen 已提交
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
		u32 low, high;

		dispc_ovl_set_mflag(i, true);

		/*
		 * Simulation team suggests below thesholds:
		 * HT = fifosize * 5 / 8;
		 * LT = fifosize * 4 / 8;
		 */

		low = size * 4 / 8 / unit;
		high = size * 5 / 8 / unit;

		dispc_ovl_set_mflag_threshold(i, low, high);
	}
1476 1477 1478

	if (dispc.feat->has_writeback) {
		u32 size = dispc_ovl_get_fifo_size(OMAP_DSS_WB);
1479
		u32 unit = dispc.feat->buffer_size_unit;
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
		u32 low, high;

		dispc_ovl_set_mflag(OMAP_DSS_WB, true);

		/*
		 * Simulation team suggests below thesholds:
		 * HT = fifosize * 5 / 8;
		 * LT = fifosize * 4 / 8;
		 */

		low = size * 4 / 8 / unit;
		high = size * 5 / 8 / unit;

		dispc_ovl_set_mflag_threshold(OMAP_DSS_WB, low, high);
	}
T
Tomi Valkeinen 已提交
1495 1496
}

1497
static void dispc_ovl_set_fir(enum omap_plane_id plane,
1498 1499
				int hinc, int vinc,
				enum omap_color_component color_comp)
T
Tomi Valkeinen 已提交
1500 1501 1502
{
	u32 val;

1503 1504
	if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
		u8 hinc_start, hinc_end, vinc_start, vinc_end;
1505

1506 1507 1508 1509 1510 1511
		dss_feat_get_reg_field(FEAT_REG_FIRHINC,
					&hinc_start, &hinc_end);
		dss_feat_get_reg_field(FEAT_REG_FIRVINC,
					&vinc_start, &vinc_end);
		val = FLD_VAL(vinc, vinc_start, vinc_end) |
				FLD_VAL(hinc, hinc_start, hinc_end);
1512

1513 1514 1515 1516 1517
		dispc_write_reg(DISPC_OVL_FIR(plane), val);
	} else {
		val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
		dispc_write_reg(DISPC_OVL_FIR2(plane), val);
	}
T
Tomi Valkeinen 已提交
1518 1519
}

1520 1521
static void dispc_ovl_set_vid_accu0(enum omap_plane_id plane, int haccu,
				    int vaccu)
T
Tomi Valkeinen 已提交
1522 1523
{
	u32 val;
1524
	u8 hor_start, hor_end, vert_start, vert_end;
T
Tomi Valkeinen 已提交
1525

1526 1527 1528 1529 1530 1531
	dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end);
	dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end);

	val = FLD_VAL(vaccu, vert_start, vert_end) |
			FLD_VAL(haccu, hor_start, hor_end);

1532
	dispc_write_reg(DISPC_OVL_ACCU0(plane), val);
T
Tomi Valkeinen 已提交
1533 1534
}

1535 1536
static void dispc_ovl_set_vid_accu1(enum omap_plane_id plane, int haccu,
				    int vaccu)
T
Tomi Valkeinen 已提交
1537 1538
{
	u32 val;
1539
	u8 hor_start, hor_end, vert_start, vert_end;
T
Tomi Valkeinen 已提交
1540

1541 1542 1543 1544 1545 1546
	dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end);
	dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end);

	val = FLD_VAL(vaccu, vert_start, vert_end) |
			FLD_VAL(haccu, hor_start, hor_end);

1547
	dispc_write_reg(DISPC_OVL_ACCU1(plane), val);
T
Tomi Valkeinen 已提交
1548 1549
}

1550
static void dispc_ovl_set_vid_accu2_0(enum omap_plane_id plane, int haccu,
1551
		int vaccu)
1552 1553 1554 1555 1556 1557 1558
{
	u32 val;

	val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0);
	dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val);
}

1559
static void dispc_ovl_set_vid_accu2_1(enum omap_plane_id plane, int haccu,
1560
		int vaccu)
1561 1562 1563 1564 1565 1566
{
	u32 val;

	val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0);
	dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val);
}
T
Tomi Valkeinen 已提交
1567

1568
static void dispc_ovl_set_scale_param(enum omap_plane_id plane,
T
Tomi Valkeinen 已提交
1569 1570
		u16 orig_width, u16 orig_height,
		u16 out_width, u16 out_height,
1571 1572
		bool five_taps, u8 rotation,
		enum omap_color_component color_comp)
T
Tomi Valkeinen 已提交
1573
{
1574
	int fir_hinc, fir_vinc;
T
Tomi Valkeinen 已提交
1575

1576 1577
	fir_hinc = 1024 * orig_width / out_width;
	fir_vinc = 1024 * orig_height / out_height;
T
Tomi Valkeinen 已提交
1578

1579 1580
	dispc_ovl_set_scale_coef(plane, fir_hinc, fir_vinc, five_taps,
				color_comp);
1581
	dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp);
1582 1583
}

1584
static void dispc_ovl_set_accu_uv(enum omap_plane_id plane,
1585
		u16 orig_width,	u16 orig_height, u16 out_width, u16 out_height,
1586
		bool ilace, u32 fourcc, u8 rotation)
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623
{
	int h_accu2_0, h_accu2_1;
	int v_accu2_0, v_accu2_1;
	int chroma_hinc, chroma_vinc;
	int idx;

	struct accu {
		s8 h0_m, h0_n;
		s8 h1_m, h1_n;
		s8 v0_m, v0_n;
		s8 v1_m, v1_n;
	};

	const struct accu *accu_table;
	const struct accu *accu_val;

	static const struct accu accu_nv12[4] = {
		{  0, 1,  0, 1 , -1, 2, 0, 1 },
		{  1, 2, -3, 4 ,  0, 1, 0, 1 },
		{ -1, 1,  0, 1 , -1, 2, 0, 1 },
		{ -1, 2, -1, 2 , -1, 1, 0, 1 },
	};

	static const struct accu accu_nv12_ilace[4] = {
		{  0, 1,  0, 1 , -3, 4, -1, 4 },
		{ -1, 4, -3, 4 ,  0, 1,  0, 1 },
		{ -1, 1,  0, 1 , -1, 4, -3, 4 },
		{ -3, 4, -3, 4 , -1, 1,  0, 1 },
	};

	static const struct accu accu_yuv[4] = {
		{  0, 1, 0, 1,  0, 1, 0, 1 },
		{  0, 1, 0, 1,  0, 1, 0, 1 },
		{ -1, 1, 0, 1,  0, 1, 0, 1 },
		{  0, 1, 0, 1, -1, 1, 0, 1 },
	};

1624 1625 1626 1627
	/* Note: DSS HW rotates clockwise, DRM_MODE_ROTATE_* counter-clockwise */
	switch (rotation & DRM_MODE_ROTATE_MASK) {
	default:
	case DRM_MODE_ROTATE_0:
1628 1629
		idx = 0;
		break;
1630 1631
	case DRM_MODE_ROTATE_90:
		idx = 3;
1632
		break;
1633
	case DRM_MODE_ROTATE_180:
1634 1635
		idx = 2;
		break;
1636 1637
	case DRM_MODE_ROTATE_270:
		idx = 1;
1638 1639 1640
		break;
	}

1641
	switch (fourcc) {
1642
	case DRM_FORMAT_NV12:
1643 1644 1645 1646 1647
		if (ilace)
			accu_table = accu_nv12_ilace;
		else
			accu_table = accu_nv12;
		break;
1648 1649
	case DRM_FORMAT_YUYV:
	case DRM_FORMAT_UYVY:
1650 1651 1652 1653
		accu_table = accu_yuv;
		break;
	default:
		BUG();
1654
		return;
1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
	}

	accu_val = &accu_table[idx];

	chroma_hinc = 1024 * orig_width / out_width;
	chroma_vinc = 1024 * orig_height / out_height;

	h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024;
	h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024;
	v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024;
	v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024;

	dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0);
	dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1);
}

1671
static void dispc_ovl_set_scaling_common(enum omap_plane_id plane,
1672 1673 1674
		u16 orig_width, u16 orig_height,
		u16 out_width, u16 out_height,
		bool ilace, bool five_taps,
1675
		bool fieldmode, u32 fourcc,
1676 1677 1678 1679 1680
		u8 rotation)
{
	int accu0 = 0;
	int accu1 = 0;
	u32 l;
T
Tomi Valkeinen 已提交
1681

1682
	dispc_ovl_set_scale_param(plane, orig_width, orig_height,
1683 1684
				out_width, out_height, five_taps,
				rotation, DISPC_COLOR_COMPONENT_RGB_Y);
1685
	l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
T
Tomi Valkeinen 已提交
1686

1687 1688
	/* RESIZEENABLE and VERTICALTAPS */
	l &= ~((0x3 << 5) | (0x1 << 21));
1689 1690
	l |= (orig_width != out_width) ? (1 << 5) : 0;
	l |= (orig_height != out_height) ? (1 << 6) : 0;
1691
	l |= five_taps ? (1 << 21) : 0;
T
Tomi Valkeinen 已提交
1692

1693 1694 1695
	/* VRESIZECONF and HRESIZECONF */
	if (dss_has_feature(FEAT_RESIZECONF)) {
		l &= ~(0x3 << 7);
1696 1697
		l |= (orig_width <= out_width) ? 0 : (1 << 7);
		l |= (orig_height <= out_height) ? 0 : (1 << 8);
1698
	}
T
Tomi Valkeinen 已提交
1699

1700 1701 1702 1703 1704
	/* LINEBUFFERSPLIT */
	if (dss_has_feature(FEAT_LINEBUFFERSPLIT)) {
		l &= ~(0x1 << 22);
		l |= five_taps ? (1 << 22) : 0;
	}
T
Tomi Valkeinen 已提交
1705

1706
	dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l);
T
Tomi Valkeinen 已提交
1707 1708 1709 1710 1711 1712 1713

	/*
	 * field 0 = even field = bottom field
	 * field 1 = odd field = top field
	 */
	if (ilace && !fieldmode) {
		accu1 = 0;
1714
		accu0 = ((1024 * orig_height / out_height) / 2) & 0x3ff;
T
Tomi Valkeinen 已提交
1715 1716 1717 1718 1719 1720
		if (accu0 >= 1024/2) {
			accu1 = 1024/2;
			accu0 -= accu1;
		}
	}

1721 1722
	dispc_ovl_set_vid_accu0(plane, 0, accu0);
	dispc_ovl_set_vid_accu1(plane, 0, accu1);
T
Tomi Valkeinen 已提交
1723 1724
}

1725
static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane,
1726 1727 1728
		u16 orig_width, u16 orig_height,
		u16 out_width, u16 out_height,
		bool ilace, bool five_taps,
1729
		bool fieldmode, u32 fourcc,
1730 1731 1732 1733
		u8 rotation)
{
	int scale_x = out_width != orig_width;
	int scale_y = out_height != orig_height;
1734
	bool chroma_upscale = plane != OMAP_DSS_WB;
1735 1736 1737

	if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE))
		return;
1738

1739
	if (!format_is_yuv(fourcc)) {
1740
		/* reset chroma resampling for RGB formats  */
1741 1742
		if (plane != OMAP_DSS_WB)
			REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8);
1743 1744
		return;
	}
1745 1746

	dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width,
1747
			out_height, ilace, fourcc, rotation);
1748

1749
	switch (fourcc) {
1750
	case DRM_FORMAT_NV12:
1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
		if (chroma_upscale) {
			/* UV is subsampled by 2 horizontally and vertically */
			orig_height >>= 1;
			orig_width >>= 1;
		} else {
			/* UV is downsampled by 2 horizontally and vertically */
			orig_height <<= 1;
			orig_width <<= 1;
		}

1761
		break;
1762 1763
	case DRM_FORMAT_YUYV:
	case DRM_FORMAT_UYVY:
1764
		/* For YUV422 with 90/270 rotation, we don't upsample chroma */
1765
		if (!drm_rotation_90_or_270(rotation)) {
1766 1767 1768 1769 1770 1771 1772 1773
			if (chroma_upscale)
				/* UV is subsampled by 2 horizontally */
				orig_width >>= 1;
			else
				/* UV is downsampled by 2 horizontally */
				orig_width <<= 1;
		}

1774
		/* must use FIR for YUV422 if rotated */
1775
		if ((rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0)
1776
			scale_x = scale_y = true;
1777

1778 1779 1780
		break;
	default:
		BUG();
1781
		return;
1782 1783 1784 1785 1786 1787 1788
	}

	if (out_width != orig_width)
		scale_x = true;
	if (out_height != orig_height)
		scale_y = true;

1789
	dispc_ovl_set_scale_param(plane, orig_width, orig_height,
1790 1791 1792
			out_width, out_height, five_taps,
				rotation, DISPC_COLOR_COMPONENT_UV);

1793 1794 1795 1796
	if (plane != OMAP_DSS_WB)
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane),
			(scale_x || scale_y) ? 1 : 0, 8, 8);

1797 1798 1799 1800 1801 1802
	/* set H scaling */
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5);
	/* set V scaling */
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6);
}

1803
static void dispc_ovl_set_scaling(enum omap_plane_id plane,
1804 1805 1806
		u16 orig_width, u16 orig_height,
		u16 out_width, u16 out_height,
		bool ilace, bool five_taps,
1807
		bool fieldmode, u32 fourcc,
1808 1809 1810 1811
		u8 rotation)
{
	BUG_ON(plane == OMAP_DSS_GFX);

1812
	dispc_ovl_set_scaling_common(plane,
1813 1814 1815
			orig_width, orig_height,
			out_width, out_height,
			ilace, five_taps,
1816
			fieldmode, fourcc,
1817 1818
			rotation);

1819
	dispc_ovl_set_scaling_uv(plane,
1820 1821 1822
		orig_width, orig_height,
		out_width, out_height,
		ilace, five_taps,
1823
		fieldmode, fourcc,
1824 1825 1826
		rotation);
}

1827
static void dispc_ovl_set_rotation_attrs(enum omap_plane_id plane, u8 rotation,
1828
		enum omap_dss_rotation_type rotation_type, u32 fourcc)
T
Tomi Valkeinen 已提交
1829
{
1830 1831 1832
	bool row_repeat = false;
	int vidrot = 0;

1833
	/* Note: DSS HW rotates clockwise, DRM_MODE_ROTATE_* counter-clockwise */
1834
	if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY) {
T
Tomi Valkeinen 已提交
1835

1836
		if (rotation & DRM_MODE_REFLECT_X) {
1837 1838
			switch (rotation & DRM_MODE_ROTATE_MASK) {
			case DRM_MODE_ROTATE_0:
T
Tomi Valkeinen 已提交
1839 1840
				vidrot = 2;
				break;
1841
			case DRM_MODE_ROTATE_90:
1842
				vidrot = 1;
T
Tomi Valkeinen 已提交
1843
				break;
1844
			case DRM_MODE_ROTATE_180:
T
Tomi Valkeinen 已提交
1845 1846
				vidrot = 0;
				break;
1847
			case DRM_MODE_ROTATE_270:
1848
				vidrot = 3;
T
Tomi Valkeinen 已提交
1849 1850 1851
				break;
			}
		} else {
1852 1853
			switch (rotation & DRM_MODE_ROTATE_MASK) {
			case DRM_MODE_ROTATE_0:
T
Tomi Valkeinen 已提交
1854 1855
				vidrot = 0;
				break;
1856 1857
			case DRM_MODE_ROTATE_90:
				vidrot = 3;
T
Tomi Valkeinen 已提交
1858
				break;
1859
			case DRM_MODE_ROTATE_180:
T
Tomi Valkeinen 已提交
1860 1861
				vidrot = 2;
				break;
1862 1863
			case DRM_MODE_ROTATE_270:
				vidrot = 1;
T
Tomi Valkeinen 已提交
1864 1865 1866 1867
				break;
			}
		}

1868
		if (drm_rotation_90_or_270(rotation))
1869
			row_repeat = true;
T
Tomi Valkeinen 已提交
1870
		else
1871
			row_repeat = false;
T
Tomi Valkeinen 已提交
1872
	}
1873

1874 1875 1876 1877 1878
	/*
	 * OMAP4/5 Errata i631:
	 * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra
	 * rows beyond the framebuffer, which may cause OCP error.
	 */
1879
	if (fourcc == DRM_FORMAT_NV12 && rotation_type != OMAP_DSS_ROT_TILER)
1880 1881
		vidrot = 1;

1882
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12);
1883
	if (dss_has_feature(FEAT_ROWREPEATENABLE))
1884 1885
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane),
			row_repeat ? 1 : 0, 18, 18);
1886

1887
	if (dispc_ovl_color_mode_supported(plane, DRM_FORMAT_NV12)) {
1888
		bool doublestride =
1889
			fourcc == DRM_FORMAT_NV12 &&
1890
			rotation_type == OMAP_DSS_ROT_TILER &&
1891
			!drm_rotation_90_or_270(rotation);
1892

1893 1894 1895
		/* DOUBLESTRIDE */
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22);
	}
T
Tomi Valkeinen 已提交
1896 1897
}

1898
static int color_mode_to_bpp(u32 fourcc)
T
Tomi Valkeinen 已提交
1899
{
1900
	switch (fourcc) {
1901
	case DRM_FORMAT_NV12:
T
Tomi Valkeinen 已提交
1902
		return 8;
1903 1904 1905 1906 1907 1908 1909 1910 1911
	case DRM_FORMAT_RGBX4444:
	case DRM_FORMAT_RGB565:
	case DRM_FORMAT_ARGB4444:
	case DRM_FORMAT_YUYV:
	case DRM_FORMAT_UYVY:
	case DRM_FORMAT_RGBA4444:
	case DRM_FORMAT_XRGB4444:
	case DRM_FORMAT_ARGB1555:
	case DRM_FORMAT_XRGB1555:
T
Tomi Valkeinen 已提交
1912
		return 16;
1913
	case DRM_FORMAT_RGB888:
T
Tomi Valkeinen 已提交
1914
		return 24;
1915 1916 1917 1918
	case DRM_FORMAT_XRGB8888:
	case DRM_FORMAT_ARGB8888:
	case DRM_FORMAT_RGBA8888:
	case DRM_FORMAT_RGBX8888:
T
Tomi Valkeinen 已提交
1919 1920 1921
		return 32;
	default:
		BUG();
1922
		return 0;
T
Tomi Valkeinen 已提交
1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935
	}
}

static s32 pixinc(int pixels, u8 ps)
{
	if (pixels == 1)
		return 1;
	else if (pixels > 1)
		return 1 + (pixels - 1) * ps;
	else if (pixels < 0)
		return 1 - (-pixels + 1) * ps;
	else
		BUG();
1936
		return 0;
T
Tomi Valkeinen 已提交
1937 1938
}

1939
static void calc_offset(u16 screen_width, u16 width,
1940
		u32 fourcc, bool fieldmode,
1941
		unsigned int field_offset, unsigned *offset0, unsigned *offset1,
1942 1943
		s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim,
		enum omap_dss_rotation_type rotation_type, u8 rotation)
1944 1945 1946
{
	u8 ps;

1947
	ps = color_mode_to_bpp(fourcc) / 8;
1948 1949 1950

	DSSDBG("scrw %d, width %d\n", screen_width, width);

1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964
	if (rotation_type == OMAP_DSS_ROT_TILER &&
	    (fourcc == DRM_FORMAT_UYVY || fourcc == DRM_FORMAT_YUYV) &&
	    drm_rotation_90_or_270(rotation)) {
		/*
		 * HACK: ROW_INC needs to be calculated with TILER units.
		 * We get such 'screen_width' that multiplying it with the
		 * YUV422 pixel size gives the correct TILER container width.
		 * However, 'width' is in pixels and multiplying it with YUV422
		 * pixel size gives incorrect result. We thus multiply it here
		 * with 2 to match the 32 bit TILER unit size.
		 */
		width *= 2;
	}

1965 1966 1967 1968
	/*
	 * field 0 = even field = bottom field
	 * field 1 = odd field = top field
	 */
1969
	*offset0 = field_offset * screen_width * ps;
1970
	*offset1 = 0;
1971

1972 1973
	*row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) +
			(fieldmode ? screen_width : 0), ps);
1974
	if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY)
1975 1976 1977 1978 1979
		*pix_inc = pixinc(x_predecim, 2 * ps);
	else
		*pix_inc = pixinc(x_predecim, ps);
}

1980 1981 1982 1983
/*
 * This function is used to avoid synclosts in OMAP3, because of some
 * undocumented horizontal position and timing related limitations.
 */
1984
static int check_horiz_timing_omap3(unsigned long pclk, unsigned long lclk,
1985
		const struct videomode *vm, u16 pos_x,
1986 1987
		u16 width, u16 height, u16 out_width, u16 out_height,
		bool five_taps)
1988
{
1989
	const int ds = DIV_ROUND_UP(height, out_height);
1990
	unsigned long nonactive;
1991 1992 1993 1994
	static const u8 limits[3] = { 8, 10, 20 };
	u64 val, blank;
	int i;

1995 1996
	nonactive = vm->hactive + vm->hfront_porch + vm->hsync_len +
		    vm->hback_porch - out_width;
1997 1998 1999 2000 2001 2002

	i = 0;
	if (out_height < height)
		i++;
	if (out_width < width)
		i++;
2003
	blank = div_u64((u64)(vm->hback_porch + vm->hsync_len + vm->hfront_porch) *
2004
			lclk, pclk);
2005 2006 2007 2008
	DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]);
	if (blank <= limits[i])
		return -EINVAL;

2009 2010 2011 2012
	/* FIXME add checks for 3-tap filter once the limitations are known */
	if (!five_taps)
		return 0;

2013 2014 2015 2016 2017 2018 2019
	/*
	 * Pixel data should be prepared before visible display point starts.
	 * So, atleast DS-2 lines must have already been fetched by DISPC
	 * during nonactive - pos_x period.
	 */
	val = div_u64((u64)(nonactive - pos_x) * lclk, pclk);
	DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n",
2020 2021
		val, max(0, ds - 2) * width);
	if (val < max(0, ds - 2) * width)
2022 2023 2024 2025 2026 2027 2028 2029 2030
		return -EINVAL;

	/*
	 * All lines need to be refilled during the nonactive period of which
	 * only one line can be loaded during the active period. So, atleast
	 * DS - 1 lines should be loaded during nonactive period.
	 */
	val =  div_u64((u64)nonactive * lclk, pclk);
	DSSDBG("nonactive * pcd  = %llu, max(0, DS - 1) * width = %d\n",
2031 2032
		val, max(0, ds - 1) * width);
	if (val < max(0, ds - 1) * width)
2033 2034 2035 2036 2037
		return -EINVAL;

	return 0;
}

2038
static unsigned long calc_core_clk_five_taps(unsigned long pclk,
2039
		const struct videomode *vm, u16 width,
2040
		u16 height, u16 out_width, u16 out_height,
2041
		u32 fourcc)
T
Tomi Valkeinen 已提交
2042
{
2043
	u32 core_clk = 0;
2044
	u64 tmp;
T
Tomi Valkeinen 已提交
2045

2046 2047 2048
	if (height <= out_height && width <= out_width)
		return (unsigned long) pclk;

T
Tomi Valkeinen 已提交
2049
	if (height > out_height) {
2050
		unsigned int ppl = vm->hactive;
T
Tomi Valkeinen 已提交
2051

2052
		tmp = (u64)pclk * height * out_width;
T
Tomi Valkeinen 已提交
2053
		do_div(tmp, 2 * out_height * ppl);
2054
		core_clk = tmp;
T
Tomi Valkeinen 已提交
2055

2056 2057 2058 2059
		if (height > 2 * out_height) {
			if (ppl == out_width)
				return 0;

2060
			tmp = (u64)pclk * (height - 2 * out_height) * out_width;
T
Tomi Valkeinen 已提交
2061
			do_div(tmp, 2 * out_height * (ppl - out_width));
2062
			core_clk = max_t(u32, core_clk, tmp);
T
Tomi Valkeinen 已提交
2063 2064 2065 2066
		}
	}

	if (width > out_width) {
2067
		tmp = (u64)pclk * width;
T
Tomi Valkeinen 已提交
2068
		do_div(tmp, out_width);
2069
		core_clk = max_t(u32, core_clk, tmp);
T
Tomi Valkeinen 已提交
2070

2071
		if (fourcc == DRM_FORMAT_XRGB8888)
2072
			core_clk <<= 1;
T
Tomi Valkeinen 已提交
2073 2074
	}

2075
	return core_clk;
T
Tomi Valkeinen 已提交
2076 2077
}

2078
static unsigned long calc_core_clk_24xx(unsigned long pclk, u16 width,
2079
		u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
2080 2081 2082 2083 2084 2085 2086
{
	if (height > out_height && width > out_width)
		return pclk * 4;
	else
		return pclk * 2;
}

2087
static unsigned long calc_core_clk_34xx(unsigned long pclk, u16 width,
2088
		u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
T
Tomi Valkeinen 已提交
2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
{
	unsigned int hf, vf;

	/*
	 * FIXME how to determine the 'A' factor
	 * for the no downscaling case ?
	 */

	if (width > 3 * out_width)
		hf = 4;
	else if (width > 2 * out_width)
		hf = 3;
	else if (width > out_width)
		hf = 2;
	else
		hf = 1;
	if (height > out_height)
		vf = 2;
	else
		vf = 1;

2110 2111 2112
	return pclk * vf * hf;
}

2113
static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width,
2114
		u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
2115
{
2116 2117 2118 2119 2120 2121 2122 2123 2124
	/*
	 * If the overlay/writeback is in mem to mem mode, there are no
	 * downscaling limitations with respect to pixel clock, return 1 as
	 * required core clock to represent that we have sufficient enough
	 * core clock to do maximum downscaling
	 */
	if (mem_to_mem)
		return 1;

2125 2126 2127 2128 2129 2130
	if (width > out_width)
		return DIV_ROUND_UP(pclk, out_width) * width;
	else
		return pclk;
}

2131
static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk,
2132
		const struct videomode *vm,
2133
		u16 width, u16 height, u16 out_width, u16 out_height,
2134
		u32 fourcc, bool *five_taps,
2135
		int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
2136
		u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
2137 2138 2139 2140 2141 2142
{
	int error;
	u16 in_width, in_height;
	int min_factor = min(*decim_x, *decim_y);
	const int maxsinglelinewidth =
			dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
2143

2144 2145 2146
	*five_taps = false;

	do {
2147 2148
		in_height = height / *decim_y;
		in_width = width / *decim_x;
2149
		*core_clk = dispc.feat->calc_core_clk(pclk, in_width,
2150
				in_height, out_width, out_height, mem_to_mem);
2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
		error = (in_width > maxsinglelinewidth || !*core_clk ||
			*core_clk > dispc_core_clk_rate());
		if (error) {
			if (*decim_x == *decim_y) {
				*decim_x = min_factor;
				++*decim_y;
			} else {
				swap(*decim_x, *decim_y);
				if (*decim_x < *decim_y)
					++*decim_x;
			}
		}
	} while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);

2165 2166 2167 2168 2169
	if (error) {
		DSSERR("failed to find scaling settings\n");
		return -EINVAL;
	}

2170 2171 2172 2173 2174 2175 2176
	if (in_width > maxsinglelinewidth) {
		DSSERR("Cannot scale max input width exceeded");
		return -EINVAL;
	}
	return 0;
}

2177
static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk,
2178
		const struct videomode *vm,
2179
		u16 width, u16 height, u16 out_width, u16 out_height,
2180
		u32 fourcc, bool *five_taps,
2181
		int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
2182
		u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
2183 2184 2185 2186 2187 2188 2189
{
	int error;
	u16 in_width, in_height;
	const int maxsinglelinewidth =
			dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);

	do {
2190 2191
		in_height = height / *decim_y;
		in_width = width / *decim_x;
2192
		*five_taps = in_height > out_height;
2193 2194 2195 2196 2197

		if (in_width > maxsinglelinewidth)
			if (in_height > out_height &&
						in_height < out_height * 2)
				*five_taps = false;
2198 2199
again:
		if (*five_taps)
2200
			*core_clk = calc_core_clk_five_taps(pclk, vm,
2201
						in_width, in_height, out_width,
2202
						out_height, fourcc);
2203
		else
2204
			*core_clk = dispc.feat->calc_core_clk(pclk, in_width,
2205 2206
					in_height, out_width, out_height,
					mem_to_mem);
2207

2208
		error = check_horiz_timing_omap3(pclk, lclk, vm,
2209 2210 2211 2212 2213 2214 2215
				pos_x, in_width, in_height, out_width,
				out_height, *five_taps);
		if (error && *five_taps) {
			*five_taps = false;
			goto again;
		}

2216 2217 2218
		error = (error || in_width > maxsinglelinewidth * 2 ||
			(in_width > maxsinglelinewidth && *five_taps) ||
			!*core_clk || *core_clk > dispc_core_clk_rate());
2219 2220 2221 2222 2223 2224 2225 2226 2227

		if (!error) {
			/* verify that we're inside the limits of scaler */
			if (in_width / 4 > out_width)
					error = 1;

			if (*five_taps) {
				if (in_height / 4 > out_height)
					error = 1;
2228
			} else {
2229 2230
				if (in_height / 2 > out_height)
					error = 1;
2231 2232
			}
		}
2233

2234 2235
		if (error)
			++*decim_y;
2236 2237
	} while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);

2238 2239 2240 2241 2242
	if (error) {
		DSSERR("failed to find scaling settings\n");
		return -EINVAL;
	}

2243
	if (check_horiz_timing_omap3(pclk, lclk, vm, pos_x, in_width,
2244
				in_height, out_width, out_height, *five_taps)) {
2245 2246
			DSSERR("horizontal timing too tight\n");
			return -EINVAL;
2247
	}
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261

	if (in_width > (maxsinglelinewidth * 2)) {
		DSSERR("Cannot setup scaling");
		DSSERR("width exceeds maximum width possible");
		return -EINVAL;
	}

	if (in_width > maxsinglelinewidth && *five_taps) {
		DSSERR("cannot setup scaling with five taps");
		return -EINVAL;
	}
	return 0;
}

2262
static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk,
2263
		const struct videomode *vm,
2264
		u16 width, u16 height, u16 out_width, u16 out_height,
2265
		u32 fourcc, bool *five_taps,
2266
		int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
2267
		u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
2268 2269 2270
{
	u16 in_width, in_width_max;
	int decim_x_min = *decim_x;
2271
	u16 in_height = height / *decim_y;
2272 2273
	const int maxsinglelinewidth =
				dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
2274
	const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
2275

2276 2277 2278
	if (mem_to_mem) {
		in_width_max = out_width * maxdownscale;
	} else {
2279 2280
		in_width_max = dispc_core_clk_rate() /
					DIV_ROUND_UP(pclk, out_width);
2281
	}
2282 2283 2284 2285 2286 2287 2288 2289

	*decim_x = DIV_ROUND_UP(width, in_width_max);

	*decim_x = *decim_x > decim_x_min ? *decim_x : decim_x_min;
	if (*decim_x > *x_predecim)
		return -EINVAL;

	do {
2290
		in_width = width / *decim_x;
2291 2292 2293 2294 2295 2296 2297 2298
	} while (*decim_x <= *x_predecim &&
			in_width > maxsinglelinewidth && ++*decim_x);

	if (in_width > maxsinglelinewidth) {
		DSSERR("Cannot scale width exceeds max line width");
		return -EINVAL;
	}

2299
	if (*decim_x > 4 && fourcc != DRM_FORMAT_NV12) {
2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317
		/*
		 * Let's disable all scaling that requires horizontal
		 * decimation with higher factor than 4, until we have
		 * better estimates of what we can and can not
		 * do. However, NV12 color format appears to work Ok
		 * with all decimation factors.
		 *
		 * When decimating horizontally by more that 4 the dss
		 * is not able to fetch the data in burst mode. When
		 * this happens it is hard to tell if there enough
		 * bandwidth. Despite what theory says this appears to
		 * be true also for 16-bit color formats.
		 */
		DSSERR("Not enough bandwidth, too much downscaling (x-decimation factor %d > 4)", *decim_x);

		return -EINVAL;
	}

2318
	*core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height,
2319
				out_width, out_height, mem_to_mem);
2320
	return 0;
T
Tomi Valkeinen 已提交
2321 2322
}

2323 2324 2325
#define DIV_FRAC(dividend, divisor) \
	((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100))

2326
static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
2327
		enum omap_overlay_caps caps,
2328
		const struct videomode *vm,
2329
		u16 width, u16 height, u16 out_width, u16 out_height,
2330
		u32 fourcc, bool *five_taps,
2331
		int *x_predecim, int *y_predecim, u16 pos_x,
2332
		enum omap_dss_rotation_type rotation_type, bool mem_to_mem)
2333
{
2334
	const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
2335
	const int max_decim_limit = 16;
2336
	unsigned long core_clk = 0;
2337
	int decim_x, decim_y, ret;
2338

2339 2340 2341
	if (width == out_width && height == out_height)
		return 0;

2342
	if (!mem_to_mem && (pclk == 0 || vm->pixelclock == 0)) {
2343 2344 2345 2346
		DSSERR("cannot calculate scaling settings: pclk is zero\n");
		return -EINVAL;
	}

2347
	if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
2348
		return -EINVAL;
2349

2350
	if (mem_to_mem) {
2351 2352 2353 2354 2355 2356 2357
		*x_predecim = *y_predecim = 1;
	} else {
		*x_predecim = max_decim_limit;
		*y_predecim = (rotation_type == OMAP_DSS_ROT_TILER &&
				dss_has_feature(FEAT_BURST_2D)) ?
				2 : max_decim_limit;
	}
2358 2359 2360 2361 2362

	decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale);
	decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale);

	if (decim_x > *x_predecim || out_width > width * 8)
2363 2364
		return -EINVAL;

2365
	if (decim_y > *y_predecim || out_height > height * 8)
2366 2367
		return -EINVAL;

2368
	ret = dispc.feat->calc_scaling(pclk, lclk, vm, width, height,
2369
		out_width, out_height, fourcc, five_taps,
2370 2371
		x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk,
		mem_to_mem);
2372 2373
	if (ret)
		return ret;
2374

2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387
	DSSDBG("%dx%d -> %dx%d (%d.%02d x %d.%02d), decim %dx%d %dx%d (%d.%02d x %d.%02d), taps %d, req clk %lu, cur clk %lu\n",
		width, height,
		out_width, out_height,
		out_width / width, DIV_FRAC(out_width, width),
		out_height / height, DIV_FRAC(out_height, height),

		decim_x, decim_y,
		width / decim_x, height / decim_y,
		out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x),
		out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y),

		*five_taps ? 5 : 3,
		core_clk, dispc_core_clk_rate());
2388

2389
	if (!core_clk || core_clk > dispc_core_clk_rate()) {
2390
		DSSERR("failed to set up scaling, "
2391 2392 2393
			"required core clk rate = %lu Hz, "
			"current core clk rate = %lu Hz\n",
			core_clk, dispc_core_clk_rate());
2394 2395 2396
		return -EINVAL;
	}

2397 2398
	*x_predecim = decim_x;
	*y_predecim = decim_y;
2399 2400 2401
	return 0;
}

2402
static int dispc_ovl_setup_common(enum omap_plane_id plane,
2403 2404
		enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr,
		u16 screen_width, int pos_x, int pos_y, u16 width, u16 height,
2405
		u16 out_width, u16 out_height, u32 fourcc,
2406
		u8 rotation, u8 zorder, u8 pre_mult_alpha,
2407
		u8 global_alpha, enum omap_dss_rotation_type rotation_type,
2408
		bool replication, const struct videomode *vm,
2409
		bool mem_to_mem)
T
Tomi Valkeinen 已提交
2410
{
2411
	bool five_taps = true;
2412
	bool fieldmode = false;
2413
	int r, cconv = 0;
T
Tomi Valkeinen 已提交
2414 2415 2416
	unsigned offset0, offset1;
	s32 row_inc;
	s32 pix_inc;
2417
	u16 frame_width, frame_height;
T
Tomi Valkeinen 已提交
2418
	unsigned int field_offset = 0;
2419 2420
	u16 in_height = height;
	u16 in_width = width;
2421
	int x_predecim = 1, y_predecim = 1;
2422
	bool ilace = !!(vm->flags & DISPLAY_FLAGS_INTERLACED);
2423 2424
	unsigned long pclk = dispc_plane_pclk_rate(plane);
	unsigned long lclk = dispc_plane_lclk_rate(plane);
2425

2426
	if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER)
T
Tomi Valkeinen 已提交
2427 2428
		return -EINVAL;

2429
	if (format_is_yuv(fourcc) && (in_width & 1)) {
2430 2431
		DSSERR("input width %d is not even for YUV format\n", in_width);
		return -EINVAL;
2432 2433
	}

2434 2435
	out_width = out_width == 0 ? width : out_width;
	out_height = out_height == 0 ? height : out_height;
2436

2437
	if (ilace && height == out_height)
2438
		fieldmode = true;
T
Tomi Valkeinen 已提交
2439 2440 2441

	if (ilace) {
		if (fieldmode)
2442
			in_height /= 2;
2443
		pos_y /= 2;
2444
		out_height /= 2;
T
Tomi Valkeinen 已提交
2445 2446

		DSSDBG("adjusting for ilace: height %d, pos_y %d, "
2447 2448
			"out_height %d\n", in_height, pos_y,
			out_height);
T
Tomi Valkeinen 已提交
2449 2450
	}

2451
	if (!dispc_ovl_color_mode_supported(plane, fourcc))
2452 2453
		return -EINVAL;

2454
	r = dispc_ovl_calc_scaling(pclk, lclk, caps, vm, in_width,
2455
			in_height, out_width, out_height, fourcc,
2456
			&five_taps, &x_predecim, &y_predecim, pos_x,
2457
			rotation_type, mem_to_mem);
2458 2459
	if (r)
		return r;
T
Tomi Valkeinen 已提交
2460

2461 2462
	in_width = in_width / x_predecim;
	in_height = in_height / y_predecim;
2463

2464 2465 2466 2467
	if (x_predecim > 1 || y_predecim > 1)
		DSSDBG("predecimation %d x %x, new input size %d x %d\n",
			x_predecim, y_predecim, in_width, in_height);

2468
	if (format_is_yuv(fourcc) && (in_width & 1)) {
2469 2470 2471
		DSSDBG("predecimated input width is not even for YUV format\n");
		DSSDBG("adjusting input width %d -> %d\n",
			in_width, in_width & ~1);
2472

2473
		in_width &= ~1;
2474 2475
	}

2476
	if (format_is_yuv(fourcc))
2477
		cconv = 1;
T
Tomi Valkeinen 已提交
2478 2479 2480 2481 2482 2483 2484 2485 2486

	if (ilace && !fieldmode) {
		/*
		 * when downscaling the bottom field may have to start several
		 * source lines below the top field. Unfortunately ACCUI
		 * registers will only hold the fractional part of the offset
		 * so the integer part must be added to the base address of the
		 * bottom field.
		 */
2487
		if (!in_height || in_height == out_height)
T
Tomi Valkeinen 已提交
2488 2489
			field_offset = 0;
		else
2490
			field_offset = in_height / out_height / 2;
T
Tomi Valkeinen 已提交
2491 2492 2493 2494 2495 2496
	}

	/* Fields are independent but interleaved in memory. */
	if (fieldmode)
		field_offset = 1;

2497 2498 2499 2500 2501
	offset0 = 0;
	offset1 = 0;
	row_inc = 0;
	pix_inc = 0;

2502 2503 2504 2505 2506 2507 2508 2509
	if (plane == OMAP_DSS_WB) {
		frame_width = out_width;
		frame_height = out_height;
	} else {
		frame_width = in_width;
		frame_height = height;
	}

2510
	calc_offset(screen_width, frame_width,
2511
			fourcc, fieldmode, field_offset,
2512
			&offset0, &offset1, &row_inc, &pix_inc,
2513 2514
			x_predecim, y_predecim,
			rotation_type, rotation);
T
Tomi Valkeinen 已提交
2515 2516 2517 2518

	DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
			offset0, offset1, row_inc, pix_inc);

2519
	dispc_ovl_set_color_mode(plane, fourcc);
T
Tomi Valkeinen 已提交
2520

2521
	dispc_ovl_configure_burst_type(plane, rotation_type);
2522

2523 2524 2525
	if (dispc.feat->reverse_ilace_field_order)
		swap(offset0, offset1);

2526 2527
	dispc_ovl_set_ba0(plane, paddr + offset0);
	dispc_ovl_set_ba1(plane, paddr + offset1);
T
Tomi Valkeinen 已提交
2528

2529
	if (fourcc == DRM_FORMAT_NV12) {
2530 2531
		dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0);
		dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1);
2532 2533
	}

2534 2535 2536
	if (dispc.feat->last_pixel_inc_missing)
		row_inc += pix_inc - 1;

2537 2538
	dispc_ovl_set_row_inc(plane, row_inc);
	dispc_ovl_set_pix_inc(plane, pix_inc);
T
Tomi Valkeinen 已提交
2539

2540
	DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, in_width,
2541
			in_height, out_width, out_height);
T
Tomi Valkeinen 已提交
2542

2543
	dispc_ovl_set_pos(plane, caps, pos_x, pos_y);
T
Tomi Valkeinen 已提交
2544

2545
	dispc_ovl_set_input_size(plane, in_width, in_height);
T
Tomi Valkeinen 已提交
2546

2547
	if (caps & OMAP_DSS_OVL_CAP_SCALE) {
2548 2549
		dispc_ovl_set_scaling(plane, in_width, in_height, out_width,
				   out_height, ilace, five_taps, fieldmode,
2550
				   fourcc, rotation);
2551
		dispc_ovl_set_output_size(plane, out_width, out_height);
2552
		dispc_ovl_set_vid_color_conv(plane, cconv);
T
Tomi Valkeinen 已提交
2553 2554
	}

2555
	dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, fourcc);
T
Tomi Valkeinen 已提交
2556

2557 2558 2559
	dispc_ovl_set_zorder(plane, caps, zorder);
	dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha);
	dispc_ovl_setup_global_alpha(plane, caps, global_alpha);
T
Tomi Valkeinen 已提交
2560

2561
	dispc_ovl_enable_replication(plane, caps, replication);
2562

T
Tomi Valkeinen 已提交
2563 2564 2565
	return 0;
}

2566
static int dispc_ovl_setup(enum omap_plane_id plane,
2567
		const struct omap_overlay_info *oi,
2568 2569
		const struct videomode *vm, bool mem_to_mem,
		enum omap_channel channel)
2570 2571
{
	int r;
2572
	enum omap_overlay_caps caps = dispc.feat->overlay_caps[plane];
T
Tomi Valkeinen 已提交
2573
	const bool replication = true;
2574

2575
	DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->"
2576
		" %dx%d, cmode %x, rot %d, chan %d repl %d\n",
2577
		plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x,
2578
		oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
2579
		oi->fourcc, oi->rotation, channel, replication);
2580

2581 2582
	dispc_ovl_set_channel_out(plane, channel);

2583
	r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr,
2584
		oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
2585
		oi->out_width, oi->out_height, oi->fourcc, oi->rotation,
2586
		oi->zorder, oi->pre_mult_alpha, oi->global_alpha,
2587
		oi->rotation_type, replication, vm, mem_to_mem);
2588 2589 2590 2591

	return r;
}

2592
int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
2593
		bool mem_to_mem, const struct videomode *vm)
2594 2595
{
	int r;
2596
	u32 l;
2597
	enum omap_plane_id plane = OMAP_DSS_WB;
2598 2599
	const int pos_x = 0, pos_y = 0;
	const u8 zorder = 0, global_alpha = 0;
T
Tomi Valkeinen 已提交
2600
	const bool replication = true;
2601
	bool truncation;
2602 2603
	int in_width = vm->hactive;
	int in_height = vm->vactive;
2604 2605 2606 2607
	enum omap_overlay_caps caps =
		OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA;

	DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, "
2608 2609
		"rot %d\n", wi->paddr, wi->p_uv_addr, in_width,
		in_height, wi->width, wi->height, wi->fourcc, wi->rotation);
2610 2611 2612

	r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr,
		wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width,
2613
		wi->height, wi->fourcc, wi->rotation, zorder,
2614
		wi->pre_mult_alpha, global_alpha, wi->rotation_type,
2615
		replication, vm, mem_to_mem);
2616

2617
	switch (wi->fourcc) {
2618 2619 2620 2621 2622 2623 2624 2625
	case DRM_FORMAT_RGB565:
	case DRM_FORMAT_RGB888:
	case DRM_FORMAT_ARGB4444:
	case DRM_FORMAT_RGBA4444:
	case DRM_FORMAT_RGBX4444:
	case DRM_FORMAT_ARGB1555:
	case DRM_FORMAT_XRGB1555:
	case DRM_FORMAT_XRGB4444:
2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636
		truncation = true;
		break;
	default:
		truncation = false;
		break;
	}

	/* setup extra DISPC_WB_ATTRIBUTES */
	l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
	l = FLD_MOD(l, truncation, 10, 10);	/* TRUNCATIONENABLE */
	l = FLD_MOD(l, mem_to_mem, 19, 19);	/* WRITEBACKMODE */
2637 2638
	if (mem_to_mem)
		l = FLD_MOD(l, 1, 26, 24);	/* CAPTUREMODE */
2639 2640
	else
		l = FLD_MOD(l, 0, 26, 24);	/* CAPTUREMODE */
2641
	dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l);
2642

2643 2644 2645 2646 2647 2648
	if (mem_to_mem) {
		/* WBDELAYCOUNT */
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0);
	} else {
		int wbdelay;

2649 2650
		wbdelay = min(vm->vfront_porch +
			      vm->vsync_len + vm->vback_porch, (u32)255);
2651 2652 2653 2654 2655

		/* WBDELAYCOUNT */
		REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0);
	}

2656 2657 2658
	return r;
}

2659
static int dispc_ovl_enable(enum omap_plane_id plane, bool enable)
T
Tomi Valkeinen 已提交
2660
{
2661 2662
	DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);

2663
	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0);
2664 2665

	return 0;
T
Tomi Valkeinen 已提交
2666 2667
}

2668
static enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel)
2669 2670 2671 2672
{
	return dss_feat_get_supported_outputs(channel);
}

2673
static void dispc_lcd_enable_signal_polarity(bool act_high)
T
Tomi Valkeinen 已提交
2674
{
2675 2676 2677
	if (!dss_has_feature(FEAT_LCDENABLEPOL))
		return;

T
Tomi Valkeinen 已提交
2678 2679 2680 2681 2682
	REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29);
}

void dispc_lcd_enable_signal(bool enable)
{
2683 2684 2685
	if (!dss_has_feature(FEAT_LCDENABLESIGNAL))
		return;

T
Tomi Valkeinen 已提交
2686 2687 2688 2689 2690
	REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28);
}

void dispc_pck_free_enable(bool enable)
{
2691 2692 2693
	if (!dss_has_feature(FEAT_PCKFREEENABLE))
		return;

T
Tomi Valkeinen 已提交
2694 2695 2696
	REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
}

2697
static int dispc_get_num_mgrs(void)
2698 2699 2700 2701
{
	return dss_feat_get_num_mgrs();
}

2702
static void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
T
Tomi Valkeinen 已提交
2703
{
2704
	mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
T
Tomi Valkeinen 已提交
2705 2706 2707
}


2708
static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
T
Tomi Valkeinen 已提交
2709
{
2710
	mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
T
Tomi Valkeinen 已提交
2711 2712
}

2713
static void dispc_set_loadmode(enum omap_dss_load_mode mode)
T
Tomi Valkeinen 已提交
2714 2715 2716 2717 2718
{
	REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
}


2719
static void dispc_mgr_set_default_color(enum omap_channel channel, u32 color)
T
Tomi Valkeinen 已提交
2720
{
2721
	dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color);
T
Tomi Valkeinen 已提交
2722 2723
}

2724
static void dispc_mgr_set_trans_key(enum omap_channel ch,
T
Tomi Valkeinen 已提交
2725 2726 2727
		enum omap_dss_trans_key_type type,
		u32 trans_key)
{
2728
	mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type);
T
Tomi Valkeinen 已提交
2729

2730
	dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
T
Tomi Valkeinen 已提交
2731 2732
}

2733
static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
T
Tomi Valkeinen 已提交
2734
{
2735
	mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable);
T
Tomi Valkeinen 已提交
2736
}
2737

2738 2739
static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
		bool enable)
T
Tomi Valkeinen 已提交
2740
{
2741
	if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
T
Tomi Valkeinen 已提交
2742 2743 2744 2745
		return;

	if (ch == OMAP_DSS_CHANNEL_LCD)
		REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
2746
	else if (ch == OMAP_DSS_CHANNEL_DIGIT)
T
Tomi Valkeinen 已提交
2747 2748
		REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
}
2749

2750
static void dispc_mgr_setup(enum omap_channel channel,
2751
		const struct omap_overlay_manager_info *info)
2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762
{
	dispc_mgr_set_default_color(channel, info->default_color);
	dispc_mgr_set_trans_key(channel, info->trans_key_type, info->trans_key);
	dispc_mgr_enable_trans_key(channel, info->trans_enabled);
	dispc_mgr_enable_alpha_fixed_zorder(channel,
			info->partial_alpha_enabled);
	if (dss_has_feature(FEAT_CPR)) {
		dispc_mgr_enable_cpr(channel, info->cpr_enable);
		dispc_mgr_set_cpr_coef(channel, &info->cpr_coefs);
	}
}
T
Tomi Valkeinen 已提交
2763

2764
static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
T
Tomi Valkeinen 已提交
2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785
{
	int code;

	switch (data_lines) {
	case 12:
		code = 0;
		break;
	case 16:
		code = 1;
		break;
	case 18:
		code = 2;
		break;
	case 24:
		code = 3;
		break;
	default:
		BUG();
		return;
	}

2786
	mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
T
Tomi Valkeinen 已提交
2787 2788
}

2789
static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
T
Tomi Valkeinen 已提交
2790 2791
{
	u32 l;
2792
	int gpout0, gpout1;
T
Tomi Valkeinen 已提交
2793 2794

	switch (mode) {
2795 2796 2797
	case DSS_IO_PAD_MODE_RESET:
		gpout0 = 0;
		gpout1 = 0;
T
Tomi Valkeinen 已提交
2798
		break;
2799 2800
	case DSS_IO_PAD_MODE_RFBI:
		gpout0 = 1;
T
Tomi Valkeinen 已提交
2801 2802
		gpout1 = 0;
		break;
2803 2804
	case DSS_IO_PAD_MODE_BYPASS:
		gpout0 = 1;
T
Tomi Valkeinen 已提交
2805 2806 2807 2808 2809 2810 2811
		gpout1 = 1;
		break;
	default:
		BUG();
		return;
	}

2812 2813 2814 2815 2816 2817
	l = dispc_read_reg(DISPC_CONTROL);
	l = FLD_MOD(l, gpout0, 15, 15);
	l = FLD_MOD(l, gpout1, 16, 16);
	dispc_write_reg(DISPC_CONTROL, l);
}

2818
static void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
2819
{
2820
	mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
T
Tomi Valkeinen 已提交
2821 2822
}

2823
static void dispc_mgr_set_lcd_config(enum omap_channel channel,
2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839
		const struct dss_lcd_mgr_config *config)
{
	dispc_mgr_set_io_pad_mode(config->io_pad_mode);

	dispc_mgr_enable_stallmode(channel, config->stallmode);
	dispc_mgr_enable_fifohandcheck(channel, config->fifohandcheck);

	dispc_mgr_set_clock_div(channel, &config->clock_info);

	dispc_mgr_set_tft_data_lines(channel, config->video_port_width);

	dispc_lcd_enable_signal_polarity(config->lcden_sig_polarity);

	dispc_mgr_set_lcd_type_tft(channel);
}

2840 2841
static bool _dispc_mgr_size_ok(u16 width, u16 height)
{
2842 2843
	return width <= dispc.feat->mgr_width_max &&
		height <= dispc.feat->mgr_height_max;
2844 2845
}

2846
static bool _dispc_lcd_timings_ok(int hsync_len, int hfp, int hbp,
T
Tomi Valkeinen 已提交
2847 2848
		int vsw, int vfp, int vbp)
{
2849
	if (hsync_len < 1 || hsync_len > dispc.feat->sw_max ||
2850 2851 2852 2853 2854 2855
			hfp < 1 || hfp > dispc.feat->hp_max ||
			hbp < 1 || hbp > dispc.feat->hp_max ||
			vsw < 1 || vsw > dispc.feat->sw_max ||
			vfp < 0 || vfp > dispc.feat->vp_max ||
			vbp < 0 || vbp > dispc.feat->vp_max)
		return false;
T
Tomi Valkeinen 已提交
2856 2857 2858
	return true;
}

2859 2860 2861 2862
static bool _dispc_mgr_pclk_ok(enum omap_channel channel,
		unsigned long pclk)
{
	if (dss_mgr_is_lcd(channel))
2863
		return pclk <= dispc.feat->max_lcd_pclk;
2864
	else
2865
		return pclk <= dispc.feat->max_tv_pclk;
2866 2867
}

2868
bool dispc_mgr_timings_ok(enum omap_channel channel, const struct videomode *vm)
T
Tomi Valkeinen 已提交
2869
{
2870
	if (!_dispc_mgr_size_ok(vm->hactive, vm->vactive))
2871
		return false;
2872

2873
	if (!_dispc_mgr_pclk_ok(channel, vm->pixelclock))
2874
		return false;
2875 2876

	if (dss_mgr_is_lcd(channel)) {
2877
		/* TODO: OMAP4+ supports interlace for LCD outputs */
2878
		if (vm->flags & DISPLAY_FLAGS_INTERLACED)
2879
			return false;
2880

2881 2882 2883 2884
		if (!_dispc_lcd_timings_ok(vm->hsync_len,
				vm->hfront_porch, vm->hback_porch,
				vm->vsync_len, vm->vfront_porch,
				vm->vback_porch))
2885
			return false;
2886
	}
2887

2888
	return true;
T
Tomi Valkeinen 已提交
2889 2890
}

2891
static void _dispc_mgr_set_lcd_timings(enum omap_channel channel,
2892
				       const struct videomode *vm)
T
Tomi Valkeinen 已提交
2893
{
2894
	u32 timing_h, timing_v, l;
2895
	bool onoff, rf, ipc, vs, hs, de;
T
Tomi Valkeinen 已提交
2896

2897 2898 2899 2900 2901 2902
	timing_h = FLD_VAL(vm->hsync_len - 1, dispc.feat->sw_start, 0) |
		   FLD_VAL(vm->hfront_porch - 1, dispc.feat->fp_start, 8) |
		   FLD_VAL(vm->hback_porch - 1, dispc.feat->bp_start, 20);
	timing_v = FLD_VAL(vm->vsync_len - 1, dispc.feat->sw_start, 0) |
		   FLD_VAL(vm->vfront_porch, dispc.feat->fp_start, 8) |
		   FLD_VAL(vm->vback_porch, dispc.feat->bp_start, 20);
T
Tomi Valkeinen 已提交
2903

2904 2905
	dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
	dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
2906

2907
	if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH)
2908
		vs = false;
2909 2910
	else
		vs = true;
2911

2912
	if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH)
2913
		hs = false;
2914 2915
	else
		hs = true;
2916

2917
	if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
2918
		de = false;
2919 2920
	else
		de = true;
2921

2922
	if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
2923
		ipc = false;
2924
	else
2925 2926
		ipc = true;

2927 2928 2929
	/* always use the 'rf' setting */
	onoff = true;

2930
	if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE)
2931
		rf = true;
2932 2933
	else
		rf = false;
2934

2935 2936
	l = FLD_VAL(onoff, 17, 17) |
		FLD_VAL(rf, 16, 16) |
2937
		FLD_VAL(de, 15, 15) |
2938
		FLD_VAL(ipc, 14, 14) |
2939 2940
		FLD_VAL(hs, 13, 13) |
		FLD_VAL(vs, 12, 12);
2941

2942 2943 2944 2945
	/* always set ALIGN bit when available */
	if (dispc.feat->supports_sync_align)
		l |= (1 << 18);

2946
	dispc_write_reg(DISPC_POL_FREQ(channel), l);
2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965

	if (dispc.syscon_pol) {
		const int shifts[] = {
			[OMAP_DSS_CHANNEL_LCD] = 0,
			[OMAP_DSS_CHANNEL_LCD2] = 1,
			[OMAP_DSS_CHANNEL_LCD3] = 2,
		};

		u32 mask, val;

		mask = (1 << 0) | (1 << 3) | (1 << 6);
		val = (rf << 0) | (ipc << 3) | (onoff << 6);

		mask <<= 16 + shifts[channel];
		val <<= 16 + shifts[channel];

		regmap_update_bits(dispc.syscon_pol, dispc.syscon_pol_offset,
			mask, val);
	}
T
Tomi Valkeinen 已提交
2966 2967
}

2968 2969 2970 2971 2972 2973 2974 2975 2976 2977
static int vm_flag_to_int(enum display_flags flags, enum display_flags high,
	enum display_flags low)
{
	if (flags & high)
		return 1;
	if (flags & low)
		return -1;
	return 0;
}

T
Tomi Valkeinen 已提交
2978
/* change name to mode? */
2979
static void dispc_mgr_set_timings(enum omap_channel channel,
2980
			   const struct videomode *vm)
T
Tomi Valkeinen 已提交
2981 2982 2983
{
	unsigned xtot, ytot;
	unsigned long ht, vt;
2984
	struct videomode t = *vm;
T
Tomi Valkeinen 已提交
2985

2986
	DSSDBG("channel %d xres %u yres %u\n", channel, t.hactive, t.vactive);
T
Tomi Valkeinen 已提交
2987

2988
	if (!dispc_mgr_timings_ok(channel, &t)) {
2989
		BUG();
2990 2991
		return;
	}
T
Tomi Valkeinen 已提交
2992

2993
	if (dss_mgr_is_lcd(channel)) {
2994
		_dispc_mgr_set_lcd_timings(channel, &t);
T
Tomi Valkeinen 已提交
2995

2996
		xtot = t.hactive + t.hfront_porch + t.hsync_len + t.hback_porch;
2997
		ytot = t.vactive + t.vfront_porch + t.vsync_len + t.vback_porch;
T
Tomi Valkeinen 已提交
2998

2999 3000
		ht = vm->pixelclock / xtot;
		vt = vm->pixelclock / xtot / ytot;
3001

3002
		DSSDBG("pck %lu\n", vm->pixelclock);
3003
		DSSDBG("hsync_len %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
3004
			t.hsync_len, t.hfront_porch, t.hback_porch,
3005
			t.vsync_len, t.vfront_porch, t.vback_porch);
3006
		DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n",
3007 3008 3009 3010 3011
			vm_flag_to_int(t.flags, DISPLAY_FLAGS_VSYNC_HIGH, DISPLAY_FLAGS_VSYNC_LOW),
			vm_flag_to_int(t.flags, DISPLAY_FLAGS_HSYNC_HIGH, DISPLAY_FLAGS_HSYNC_LOW),
			vm_flag_to_int(t.flags, DISPLAY_FLAGS_PIXDATA_POSEDGE, DISPLAY_FLAGS_PIXDATA_NEGEDGE),
			vm_flag_to_int(t.flags, DISPLAY_FLAGS_DE_HIGH, DISPLAY_FLAGS_DE_LOW),
			vm_flag_to_int(t.flags, DISPLAY_FLAGS_SYNC_POSEDGE, DISPLAY_FLAGS_SYNC_NEGEDGE));
T
Tomi Valkeinen 已提交
3012

3013
		DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
3014
	} else {
3015
		if (t.flags & DISPLAY_FLAGS_INTERLACED)
3016
			t.vactive /= 2;
3017 3018

		if (dispc.feat->supports_double_pixel)
3019 3020 3021
			REG_FLD_MOD(DISPC_CONTROL,
				    !!(t.flags & DISPLAY_FLAGS_DOUBLECLK),
				    19, 17);
3022
	}
3023

3024
	dispc_mgr_set_size(channel, t.hactive, t.vactive);
T
Tomi Valkeinen 已提交
3025 3026
}

3027
static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
3028
		u16 pck_div)
T
Tomi Valkeinen 已提交
3029 3030
{
	BUG_ON(lck_div < 1);
3031
	BUG_ON(pck_div < 1);
T
Tomi Valkeinen 已提交
3032

3033
	dispc_write_reg(DISPC_DIVISORo(channel),
T
Tomi Valkeinen 已提交
3034
			FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
3035

3036
	if (!dss_has_feature(FEAT_CORE_CLK_DIV) &&
3037 3038
			channel == OMAP_DSS_CHANNEL_LCD)
		dispc.core_clk_rate = dispc_fclk_rate() / lck_div;
T
Tomi Valkeinen 已提交
3039 3040
}

3041
static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div,
3042
		int *pck_div)
T
Tomi Valkeinen 已提交
3043 3044
{
	u32 l;
3045
	l = dispc_read_reg(DISPC_DIVISORo(channel));
T
Tomi Valkeinen 已提交
3046 3047 3048 3049
	*lck_div = FLD_GET(l, 23, 16);
	*pck_div = FLD_GET(l, 7, 0);
}

3050
static unsigned long dispc_fclk_rate(void)
T
Tomi Valkeinen 已提交
3051
{
3052 3053
	unsigned long r;
	enum dss_clk_source src;
T
Tomi Valkeinen 已提交
3054

3055 3056 3057
	src = dss_get_dispc_clk_source();

	if (src == DSS_CLK_SRC_FCK) {
3058
		r = dss_get_dispc_clk_rate();
3059 3060 3061
	} else {
		struct dss_pll *pll;
		unsigned clkout_idx;
3062

3063 3064
		pll = dss_pll_find_by_src(src);
		clkout_idx = dss_pll_get_clkout_idx_for_src(src);
3065

3066
		r = pll->cinfo.clkout[clkout_idx];
3067 3068
	}

T
Tomi Valkeinen 已提交
3069 3070 3071
	return r;
}

3072
static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
T
Tomi Valkeinen 已提交
3073 3074 3075
{
	int lcd;
	unsigned long r;
3076
	enum dss_clk_source src;
T
Tomi Valkeinen 已提交
3077

3078 3079 3080
	/* for TV, LCLK rate is the FCLK rate */
	if (!dss_mgr_is_lcd(channel))
		return dispc_fclk_rate();
T
Tomi Valkeinen 已提交
3081

3082
	src = dss_get_lcd_clk_source(channel);
3083

3084 3085 3086 3087 3088
	if (src == DSS_CLK_SRC_FCK) {
		r = dss_get_dispc_clk_rate();
	} else {
		struct dss_pll *pll;
		unsigned clkout_idx;
3089

3090 3091
		pll = dss_pll_find_by_src(src);
		clkout_idx = dss_pll_get_clkout_idx_for_src(src);
T
Tomi Valkeinen 已提交
3092

3093
		r = pll->cinfo.clkout[clkout_idx];
3094
	}
3095 3096 3097 3098

	lcd = REG_GET(DISPC_DIVISORo(channel), 23, 16);

	return r / lcd;
T
Tomi Valkeinen 已提交
3099 3100
}

3101
static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
T
Tomi Valkeinen 已提交
3102 3103 3104
{
	unsigned long r;

3105
	if (dss_mgr_is_lcd(channel)) {
3106 3107
		int pcd;
		u32 l;
T
Tomi Valkeinen 已提交
3108

3109
		l = dispc_read_reg(DISPC_DIVISORo(channel));
T
Tomi Valkeinen 已提交
3110

3111
		pcd = FLD_GET(l, 7, 0);
T
Tomi Valkeinen 已提交
3112

3113 3114 3115 3116
		r = dispc_mgr_lclk_rate(channel);

		return r / pcd;
	} else {
3117
		return dispc.tv_pclk_rate;
3118
	}
T
Tomi Valkeinen 已提交
3119 3120
}

3121 3122 3123 3124 3125
void dispc_set_tv_pclk(unsigned long pclk)
{
	dispc.tv_pclk_rate = pclk;
}

3126
static unsigned long dispc_core_clk_rate(void)
3127
{
3128
	return dispc.core_clk_rate;
3129 3130
}

3131
static unsigned long dispc_plane_pclk_rate(enum omap_plane_id plane)
3132
{
3133 3134 3135 3136 3137 3138
	enum omap_channel channel;

	if (plane == OMAP_DSS_WB)
		return 0;

	channel = dispc_ovl_get_channel_out(plane);
3139 3140 3141 3142

	return dispc_mgr_pclk_rate(channel);
}

3143
static unsigned long dispc_plane_lclk_rate(enum omap_plane_id plane)
3144
{
3145 3146 3147 3148 3149 3150
	enum omap_channel channel;

	if (plane == OMAP_DSS_WB)
		return 0;

	channel	= dispc_ovl_get_channel_out(plane);
3151

3152
	return dispc_mgr_lclk_rate(channel);
3153
}
3154

3155
static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
T
Tomi Valkeinen 已提交
3156 3157
{
	int lcd, pcd;
3158
	enum dss_clk_source lcd_clk_src;
3159 3160 3161 3162 3163

	seq_printf(s, "- %s -\n", mgr_desc[channel].name);

	lcd_clk_src = dss_get_lcd_clk_source(channel);

3164
	seq_printf(s, "%s clk source = %s\n", mgr_desc[channel].name,
3165
		dss_get_clk_source_name(lcd_clk_src));
3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177

	dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd);

	seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
		dispc_mgr_lclk_rate(channel), lcd);
	seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
		dispc_mgr_pclk_rate(channel), pcd);
}

void dispc_dump_clocks(struct seq_file *s)
{
	int lcd;
3178
	u32 l;
3179
	enum dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
T
Tomi Valkeinen 已提交
3180

3181 3182
	if (dispc_runtime_get())
		return;
T
Tomi Valkeinen 已提交
3183 3184 3185

	seq_printf(s, "- DISPC -\n");

3186
	seq_printf(s, "dispc fclk source = %s\n",
3187
			dss_get_clk_source_name(dispc_clk_src));
T
Tomi Valkeinen 已提交
3188 3189

	seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate());
3190

3191 3192 3193 3194 3195 3196 3197 3198
	if (dss_has_feature(FEAT_CORE_CLK_DIV)) {
		seq_printf(s, "- DISPC-CORE-CLK -\n");
		l = dispc_read_reg(DISPC_DIVISOR);
		lcd = FLD_GET(l, 23, 16);

		seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
				(dispc_fclk_rate()/lcd), lcd);
	}
3199

3200
	dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD);
3201

3202 3203 3204 3205
	if (dss_has_feature(FEAT_MGR_LCD2))
		dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2);
	if (dss_has_feature(FEAT_MGR_LCD3))
		dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3);
3206 3207

	dispc_runtime_put();
T
Tomi Valkeinen 已提交
3208 3209
}

3210
static void dispc_dump_regs(struct seq_file *s)
T
Tomi Valkeinen 已提交
3211
{
3212 3213 3214 3215 3216
	int i, j;
	const char *mgr_names[] = {
		[OMAP_DSS_CHANNEL_LCD]		= "LCD",
		[OMAP_DSS_CHANNEL_DIGIT]	= "TV",
		[OMAP_DSS_CHANNEL_LCD2]		= "LCD2",
3217
		[OMAP_DSS_CHANNEL_LCD3]		= "LCD3",
3218 3219 3220 3221 3222
	};
	const char *ovl_names[] = {
		[OMAP_DSS_GFX]		= "GFX",
		[OMAP_DSS_VIDEO1]	= "VID1",
		[OMAP_DSS_VIDEO2]	= "VID2",
3223
		[OMAP_DSS_VIDEO3]	= "VID3",
T
Tomi Valkeinen 已提交
3224
		[OMAP_DSS_WB]		= "WB",
3225 3226 3227
	};
	const char **p_names;

3228
#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r))
T
Tomi Valkeinen 已提交
3229

3230 3231
	if (dispc_runtime_get())
		return;
T
Tomi Valkeinen 已提交
3232

3233
	/* DISPC common registers */
T
Tomi Valkeinen 已提交
3234 3235 3236 3237 3238 3239 3240 3241 3242 3243
	DUMPREG(DISPC_REVISION);
	DUMPREG(DISPC_SYSCONFIG);
	DUMPREG(DISPC_SYSSTATUS);
	DUMPREG(DISPC_IRQSTATUS);
	DUMPREG(DISPC_IRQENABLE);
	DUMPREG(DISPC_CONTROL);
	DUMPREG(DISPC_CONFIG);
	DUMPREG(DISPC_CAPABLE);
	DUMPREG(DISPC_LINE_STATUS);
	DUMPREG(DISPC_LINE_NUMBER);
3244 3245
	if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
			dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
3246
		DUMPREG(DISPC_GLOBAL_ALPHA);
3247 3248 3249
	if (dss_has_feature(FEAT_MGR_LCD2)) {
		DUMPREG(DISPC_CONTROL2);
		DUMPREG(DISPC_CONFIG2);
3250
	}
3251 3252 3253 3254
	if (dss_has_feature(FEAT_MGR_LCD3)) {
		DUMPREG(DISPC_CONTROL3);
		DUMPREG(DISPC_CONFIG3);
	}
T
Tomi Valkeinen 已提交
3255 3256
	if (dss_has_feature(FEAT_MFLAG))
		DUMPREG(DISPC_GLOBAL_MFLAG_ATTRIBUTE);
3257 3258 3259 3260

#undef DUMPREG

#define DISPC_REG(i, name) name(i)
3261
#define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \
T
Tomi Valkeinen 已提交
3262
	(int)(48 - strlen(#r) - strlen(p_names[i])), " ", \
3263 3264
	dispc_read_reg(DISPC_REG(i, r)))

3265
	p_names = mgr_names;
3266

3267 3268 3269 3270 3271
	/* DISPC channel specific registers */
	for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
		DUMPREG(i, DISPC_DEFAULT_COLOR);
		DUMPREG(i, DISPC_TRANS_COLOR);
		DUMPREG(i, DISPC_SIZE_MGR);
T
Tomi Valkeinen 已提交
3272

3273 3274
		if (i == OMAP_DSS_CHANNEL_DIGIT)
			continue;
3275

3276 3277 3278 3279
		DUMPREG(i, DISPC_TIMING_H);
		DUMPREG(i, DISPC_TIMING_V);
		DUMPREG(i, DISPC_POL_FREQ);
		DUMPREG(i, DISPC_DIVISORo);
3280

3281 3282 3283
		DUMPREG(i, DISPC_DATA_CYCLE1);
		DUMPREG(i, DISPC_DATA_CYCLE2);
		DUMPREG(i, DISPC_DATA_CYCLE3);
3284

3285
		if (dss_has_feature(FEAT_CPR)) {
3286 3287 3288
			DUMPREG(i, DISPC_CPR_COEF_R);
			DUMPREG(i, DISPC_CPR_COEF_G);
			DUMPREG(i, DISPC_CPR_COEF_B);
3289
		}
3290
	}
T
Tomi Valkeinen 已提交
3291

3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303
	p_names = ovl_names;

	for (i = 0; i < dss_feat_get_num_ovls(); i++) {
		DUMPREG(i, DISPC_OVL_BA0);
		DUMPREG(i, DISPC_OVL_BA1);
		DUMPREG(i, DISPC_OVL_POSITION);
		DUMPREG(i, DISPC_OVL_SIZE);
		DUMPREG(i, DISPC_OVL_ATTRIBUTES);
		DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD);
		DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS);
		DUMPREG(i, DISPC_OVL_ROW_INC);
		DUMPREG(i, DISPC_OVL_PIXEL_INC);
3304

3305 3306
		if (dss_has_feature(FEAT_PRELOAD))
			DUMPREG(i, DISPC_OVL_PRELOAD);
3307 3308
		if (dss_has_feature(FEAT_MFLAG))
			DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD);
3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328

		if (i == OMAP_DSS_GFX) {
			DUMPREG(i, DISPC_OVL_WINDOW_SKIP);
			DUMPREG(i, DISPC_OVL_TABLE_BA);
			continue;
		}

		DUMPREG(i, DISPC_OVL_FIR);
		DUMPREG(i, DISPC_OVL_PICTURE_SIZE);
		DUMPREG(i, DISPC_OVL_ACCU0);
		DUMPREG(i, DISPC_OVL_ACCU1);
		if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
			DUMPREG(i, DISPC_OVL_BA0_UV);
			DUMPREG(i, DISPC_OVL_BA1_UV);
			DUMPREG(i, DISPC_OVL_FIR2);
			DUMPREG(i, DISPC_OVL_ACCU2_0);
			DUMPREG(i, DISPC_OVL_ACCU2_1);
		}
		if (dss_has_feature(FEAT_ATTR2))
			DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
3329
	}
3330

T
Tomi Valkeinen 已提交
3331
	if (dispc.feat->has_writeback) {
T
Tomi Valkeinen 已提交
3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359
		i = OMAP_DSS_WB;
		DUMPREG(i, DISPC_OVL_BA0);
		DUMPREG(i, DISPC_OVL_BA1);
		DUMPREG(i, DISPC_OVL_SIZE);
		DUMPREG(i, DISPC_OVL_ATTRIBUTES);
		DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD);
		DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS);
		DUMPREG(i, DISPC_OVL_ROW_INC);
		DUMPREG(i, DISPC_OVL_PIXEL_INC);

		if (dss_has_feature(FEAT_MFLAG))
			DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD);

		DUMPREG(i, DISPC_OVL_FIR);
		DUMPREG(i, DISPC_OVL_PICTURE_SIZE);
		DUMPREG(i, DISPC_OVL_ACCU0);
		DUMPREG(i, DISPC_OVL_ACCU1);
		if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
			DUMPREG(i, DISPC_OVL_BA0_UV);
			DUMPREG(i, DISPC_OVL_BA1_UV);
			DUMPREG(i, DISPC_OVL_FIR2);
			DUMPREG(i, DISPC_OVL_ACCU2_0);
			DUMPREG(i, DISPC_OVL_ACCU2_1);
		}
		if (dss_has_feature(FEAT_ATTR2))
			DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
	}

3360 3361 3362 3363 3364
#undef DISPC_REG
#undef DUMPREG

#define DISPC_REG(plane, name, i) name(plane, i)
#define DUMPREG(plane, name, i) \
3365
	seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \
T
Tomi Valkeinen 已提交
3366
	(int)(46 - strlen(#name) - strlen(p_names[plane])), " ", \
3367 3368
	dispc_read_reg(DISPC_REG(plane, name, i)))

3369
	/* Video pipeline coefficient registers */
3370

3371 3372 3373 3374
	/* start from OMAP_DSS_VIDEO1 */
	for (i = 1; i < dss_feat_get_num_ovls(); i++) {
		for (j = 0; j < 8; j++)
			DUMPREG(i, DISPC_OVL_FIR_COEF_H, j);
3375

3376 3377
		for (j = 0; j < 8; j++)
			DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j);
3378

3379 3380
		for (j = 0; j < 5; j++)
			DUMPREG(i, DISPC_OVL_CONV_COEF, j);
3381

3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396
		if (dss_has_feature(FEAT_FIR_COEF_V)) {
			for (j = 0; j < 8; j++)
				DUMPREG(i, DISPC_OVL_FIR_COEF_V, j);
		}

		if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
			for (j = 0; j < 8; j++)
				DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j);

			for (j = 0; j < 8; j++)
				DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j);

			for (j = 0; j < 8; j++)
				DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j);
		}
3397
	}
T
Tomi Valkeinen 已提交
3398

3399
	dispc_runtime_put();
3400 3401

#undef DISPC_REG
T
Tomi Valkeinen 已提交
3402 3403 3404 3405 3406 3407 3408 3409 3410
#undef DUMPREG
}

/* calculate clock rates using dividers in cinfo */
int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
		struct dispc_clock_info *cinfo)
{
	if (cinfo->lck_div > 255 || cinfo->lck_div == 0)
		return -EINVAL;
3411
	if (cinfo->pck_div < 1 || cinfo->pck_div > 255)
T
Tomi Valkeinen 已提交
3412 3413 3414 3415
		return -EINVAL;

	cinfo->lck = dispc_fclk_rate / cinfo->lck_div;
	cinfo->pck = cinfo->lck / cinfo->pck_div;
3416

T
Tomi Valkeinen 已提交
3417 3418 3419
	return 0;
}

3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430
bool dispc_div_calc(unsigned long dispc,
		unsigned long pck_min, unsigned long pck_max,
		dispc_div_calc_func func, void *data)
{
	int lckd, lckd_start, lckd_stop;
	int pckd, pckd_start, pckd_stop;
	unsigned long pck, lck;
	unsigned long lck_max;
	unsigned long pckd_hw_min, pckd_hw_max;
	unsigned min_fck_per_pck;
	unsigned long fck;
T
Tomi Valkeinen 已提交
3431

3432 3433 3434 3435 3436
#ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK
	min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
#else
	min_fck_per_pck = 0;
#endif
T
Tomi Valkeinen 已提交
3437

3438 3439
	pckd_hw_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD);
	pckd_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD);
T
Tomi Valkeinen 已提交
3440

3441
	lck_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
T
Tomi Valkeinen 已提交
3442

3443 3444
	pck_min = pck_min ? pck_min : 1;
	pck_max = pck_max ? pck_max : ULONG_MAX;
T
Tomi Valkeinen 已提交
3445

3446 3447
	lckd_start = max(DIV_ROUND_UP(dispc, lck_max), 1ul);
	lckd_stop = min(dispc / pck_min, 255ul);
T
Tomi Valkeinen 已提交
3448

3449 3450
	for (lckd = lckd_start; lckd <= lckd_stop; ++lckd) {
		lck = dispc / lckd;
T
Tomi Valkeinen 已提交
3451

3452 3453
		pckd_start = max(DIV_ROUND_UP(lck, pck_max), pckd_hw_min);
		pckd_stop = min(lck / pck_min, pckd_hw_max);
T
Tomi Valkeinen 已提交
3454

3455 3456
		for (pckd = pckd_start; pckd <= pckd_stop; ++pckd) {
			pck = lck / pckd;
T
Tomi Valkeinen 已提交
3457

3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477
			/*
			 * For OMAP2/3 the DISPC fclk is the same as LCD's logic
			 * clock, which means we're configuring DISPC fclk here
			 * also. Thus we need to use the calculated lck. For
			 * OMAP4+ the DISPC fclk is a separate clock.
			 */
			if (dss_has_feature(FEAT_CORE_CLK_DIV))
				fck = dispc_core_clk_rate();
			else
				fck = lck;

			if (fck < pck * min_fck_per_pck)
				continue;

			if (func(lckd, pckd, lck, pck, data))
				return true;
		}
	}

	return false;
T
Tomi Valkeinen 已提交
3478 3479
}

3480
void dispc_mgr_set_clock_div(enum omap_channel channel,
3481
		const struct dispc_clock_info *cinfo)
T
Tomi Valkeinen 已提交
3482 3483 3484 3485
{
	DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
	DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);

3486
	dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
T
Tomi Valkeinen 已提交
3487 3488
}

3489
int dispc_mgr_get_clock_div(enum omap_channel channel,
3490
		struct dispc_clock_info *cinfo)
T
Tomi Valkeinen 已提交
3491 3492 3493 3494 3495
{
	unsigned long fck;

	fck = dispc_fclk_rate();

3496 3497
	cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16);
	cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0);
T
Tomi Valkeinen 已提交
3498 3499 3500 3501 3502 3503 3504

	cinfo->lck = fck / cinfo->lck_div;
	cinfo->pck = cinfo->lck / cinfo->pck_div;

	return 0;
}

3505
static u32 dispc_read_irqstatus(void)
3506 3507 3508 3509
{
	return dispc_read_reg(DISPC_IRQSTATUS);
}

3510
static void dispc_clear_irqstatus(u32 mask)
3511 3512 3513 3514
{
	dispc_write_reg(DISPC_IRQSTATUS, mask);
}

3515
static void dispc_write_irqenable(u32 mask)
3516 3517 3518 3519 3520 3521 3522
{
	u32 old_mask = dispc_read_reg(DISPC_IRQENABLE);

	/* clear the irqstatus for newly enabled irqs */
	dispc_clear_irqstatus((mask ^ old_mask) & mask);

	dispc_write_reg(DISPC_IRQENABLE, mask);
3523 3524 3525

	/* flush posted write */
	dispc_read_reg(DISPC_IRQENABLE);
3526 3527
}

T
Tomi Valkeinen 已提交
3528 3529 3530 3531 3532 3533 3534 3535 3536 3537
void dispc_enable_sidle(void)
{
	REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3);	/* SIDLEMODE: smart idle */
}

void dispc_disable_sidle(void)
{
	REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3);	/* SIDLEMODE: no idle */
}

3538
static u32 dispc_mgr_gamma_size(enum omap_channel channel)
3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590
{
	const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma;

	if (!dispc.feat->has_gamma_table)
		return 0;

	return gdesc->len;
}

static void dispc_mgr_write_gamma_table(enum omap_channel channel)
{
	const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma;
	u32 *table = dispc.gamma_table[channel];
	unsigned int i;

	DSSDBG("%s: channel %d\n", __func__, channel);

	for (i = 0; i < gdesc->len; ++i) {
		u32 v = table[i];

		if (gdesc->has_index)
			v |= i << 24;
		else if (i == 0)
			v |= 1 << 31;

		dispc_write_reg(gdesc->reg, v);
	}
}

static void dispc_restore_gamma_tables(void)
{
	DSSDBG("%s()\n", __func__);

	if (!dispc.feat->has_gamma_table)
		return;

	dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD);

	dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_DIGIT);

	if (dss_has_feature(FEAT_MGR_LCD2))
		dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD2);

	if (dss_has_feature(FEAT_MGR_LCD3))
		dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD3);
}

static const struct drm_color_lut dispc_mgr_gamma_default_lut[] = {
	{ .red = 0, .green = 0, .blue = 0, },
	{ .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, },
};

3591
static void dispc_mgr_set_gamma(enum omap_channel channel,
3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668
			 const struct drm_color_lut *lut,
			 unsigned int length)
{
	const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma;
	u32 *table = dispc.gamma_table[channel];
	uint i;

	DSSDBG("%s: channel %d, lut len %u, hw len %u\n", __func__,
	       channel, length, gdesc->len);

	if (!dispc.feat->has_gamma_table)
		return;

	if (lut == NULL || length < 2) {
		lut = dispc_mgr_gamma_default_lut;
		length = ARRAY_SIZE(dispc_mgr_gamma_default_lut);
	}

	for (i = 0; i < length - 1; ++i) {
		uint first = i * (gdesc->len - 1) / (length - 1);
		uint last = (i + 1) * (gdesc->len - 1) / (length - 1);
		uint w = last - first;
		u16 r, g, b;
		uint j;

		if (w == 0)
			continue;

		for (j = 0; j <= w; j++) {
			r = (lut[i].red * (w - j) + lut[i+1].red * j) / w;
			g = (lut[i].green * (w - j) + lut[i+1].green * j) / w;
			b = (lut[i].blue * (w - j) + lut[i+1].blue * j) / w;

			r >>= 16 - gdesc->bits;
			g >>= 16 - gdesc->bits;
			b >>= 16 - gdesc->bits;

			table[first + j] = (r << (gdesc->bits * 2)) |
				(g << gdesc->bits) | b;
		}
	}

	if (dispc.is_enabled)
		dispc_mgr_write_gamma_table(channel);
}

static int dispc_init_gamma_tables(void)
{
	int channel;

	if (!dispc.feat->has_gamma_table)
		return 0;

	for (channel = 0; channel < ARRAY_SIZE(dispc.gamma_table); channel++) {
		const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma;
		u32 *gt;

		if (channel == OMAP_DSS_CHANNEL_LCD2 &&
		    !dss_has_feature(FEAT_MGR_LCD2))
			continue;

		if (channel == OMAP_DSS_CHANNEL_LCD3 &&
		    !dss_has_feature(FEAT_MGR_LCD3))
			continue;

		gt = devm_kmalloc_array(&dispc.pdev->dev, gdesc->len,
					   sizeof(u32), GFP_KERNEL);
		if (!gt)
			return -ENOMEM;

		dispc.gamma_table[channel] = gt;

		dispc_mgr_set_gamma(channel, NULL, 0);
	}
	return 0;
}

T
Tomi Valkeinen 已提交
3669 3670 3671 3672
static void _omap_dispc_initial_config(void)
{
	u32 l;

3673 3674 3675 3676 3677 3678 3679
	/* Exclusively enable DISPC_CORE_CLK and set divider to 1 */
	if (dss_has_feature(FEAT_CORE_CLK_DIV)) {
		l = dispc_read_reg(DISPC_DIVISOR);
		/* Use DISPC_DIVISOR.LCD, instead of DISPC_DIVISOR1.LCD */
		l = FLD_MOD(l, 1, 0, 0);
		l = FLD_MOD(l, 1, 23, 16);
		dispc_write_reg(DISPC_DIVISOR, l);
3680 3681

		dispc.core_clk_rate = dispc_fclk_rate();
3682 3683
	}

3684 3685 3686 3687 3688 3689 3690 3691 3692
	/* Use gamma table mode, instead of palette mode */
	if (dispc.feat->has_gamma_table)
		REG_FLD_MOD(DISPC_CONFIG, 1, 3, 3);

	/* For older DSS versions (FEAT_FUNCGATED) this enables
	 * func-clock auto-gating. For newer versions
	 * (dispc.feat->has_gamma_table) this enables tv-out gamma tables.
	 */
	if (dss_has_feature(FEAT_FUNCGATED) || dispc.feat->has_gamma_table)
3693
		REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
T
Tomi Valkeinen 已提交
3694

3695
	dispc_setup_color_conv_coef();
T
Tomi Valkeinen 已提交
3696 3697 3698

	dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);

3699
	dispc_init_fifos();
3700 3701

	dispc_configure_burst_sizes();
3702 3703

	dispc_ovl_enable_zorder_planes();
3704 3705 3706

	if (dispc.feat->mstandby_workaround)
		REG_FLD_MOD(DISPC_MSTANDBY_CTRL, 1, 0, 0);
T
Tomi Valkeinen 已提交
3707 3708 3709

	if (dss_has_feature(FEAT_MFLAG))
		dispc_init_mflag();
T
Tomi Valkeinen 已提交
3710 3711
}

3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775
static const enum omap_overlay_caps omap2_dispc_overlay_caps[] = {
	/* OMAP_DSS_GFX */
	OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO1 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO2 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,
};

static const enum omap_overlay_caps omap3430_dispc_overlay_caps[] = {
	/* OMAP_DSS_GFX */
	OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO1 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO2 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
		OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
};

static const enum omap_overlay_caps omap3630_dispc_overlay_caps[] = {
	/* OMAP_DSS_GFX */
	OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
		OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO1 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO2 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
		OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,
};

static const enum omap_overlay_caps omap4_dispc_overlay_caps[] = {
	/* OMAP_DSS_GFX */
	OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
		OMAP_DSS_OVL_CAP_ZORDER | OMAP_DSS_OVL_CAP_POS |
		OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO1 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
		OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
		OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO2 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
		OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
		OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,

	/* OMAP_DSS_VIDEO3 */
	OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
		OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
		OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
};

3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875
#define COLOR_ARRAY(arr...) (const u32[]) { arr, 0 }

static const u32 *omap2_dispc_supported_color_modes[] = {

	/* OMAP_DSS_GFX */
	COLOR_ARRAY(
	DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565,
	DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888),

	/* OMAP_DSS_VIDEO1 */
	COLOR_ARRAY(
	DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_YUYV,
	DRM_FORMAT_UYVY),

	/* OMAP_DSS_VIDEO2 */
	COLOR_ARRAY(
	DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_YUYV,
	DRM_FORMAT_UYVY),
};

static const u32 *omap3_dispc_supported_color_modes[] = {
	/* OMAP_DSS_GFX */
	COLOR_ARRAY(
	DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444,
	DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888),

	/* OMAP_DSS_VIDEO1 */
	COLOR_ARRAY(
	DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888,
	DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565,
	DRM_FORMAT_YUYV, DRM_FORMAT_UYVY),

	/* OMAP_DSS_VIDEO2 */
	COLOR_ARRAY(
	DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444,
	DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_YUYV,
	DRM_FORMAT_UYVY, DRM_FORMAT_ARGB8888,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888),
};

static const u32 *omap4_dispc_supported_color_modes[] = {
	/* OMAP_DSS_GFX */
	COLOR_ARRAY(
	DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444,
	DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888,
	DRM_FORMAT_ARGB1555, DRM_FORMAT_XRGB4444,
	DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB1555),

	/* OMAP_DSS_VIDEO1 */
	COLOR_ARRAY(
	DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444,
	DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12,
	DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_UYVY,
	DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555,
	DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444,
	DRM_FORMAT_RGBX8888),

       /* OMAP_DSS_VIDEO2 */
	COLOR_ARRAY(
	DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444,
	DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12,
	DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_UYVY,
	DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555,
	DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444,
	DRM_FORMAT_RGBX8888),

	/* OMAP_DSS_VIDEO3 */
	COLOR_ARRAY(
	DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444,
	DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12,
	DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_UYVY,
	DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555,
	DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444,
	DRM_FORMAT_RGBX8888),

	/* OMAP_DSS_WB */
	COLOR_ARRAY(
	DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444,
	DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555,
	DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12,
	DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB888, DRM_FORMAT_UYVY,
	DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555,
	DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444,
	DRM_FORMAT_RGBX8888),
};

3876
static const struct dispc_features omap24xx_dispc_feats = {
3877 3878 3879 3880 3881 3882
	.sw_start		=	5,
	.fp_start		=	15,
	.bp_start		=	27,
	.sw_max			=	64,
	.vp_max			=	255,
	.hp_max			=	256,
3883 3884 3885 3886
	.mgr_width_start	=	10,
	.mgr_height_start	=	26,
	.mgr_width_max		=	2048,
	.mgr_height_max		=	2048,
3887
	.max_lcd_pclk		=	66500000,
3888 3889
	.calc_scaling		=	dispc_ovl_calc_scaling_24xx,
	.calc_core_clk		=	calc_core_clk_24xx,
3890
	.num_fifos		=	3,
3891
	.overlay_caps		=	omap2_dispc_overlay_caps,
3892
	.supported_color_modes	=	omap2_dispc_supported_color_modes,
3893 3894
	.buffer_size_unit	=	1,
	.burst_size_unit	=	8,
3895
	.no_framedone_tv	=	true,
3896
	.set_max_preload	=	false,
3897
	.last_pixel_inc_missing	=	true,
3898 3899
};

3900
static const struct dispc_features omap34xx_rev1_0_dispc_feats = {
3901 3902 3903 3904 3905 3906
	.sw_start		=	5,
	.fp_start		=	15,
	.bp_start		=	27,
	.sw_max			=	64,
	.vp_max			=	255,
	.hp_max			=	256,
3907 3908 3909 3910
	.mgr_width_start	=	10,
	.mgr_height_start	=	26,
	.mgr_width_max		=	2048,
	.mgr_height_max		=	2048,
3911 3912
	.max_lcd_pclk		=	173000000,
	.max_tv_pclk		=	59000000,
3913 3914
	.calc_scaling		=	dispc_ovl_calc_scaling_34xx,
	.calc_core_clk		=	calc_core_clk_34xx,
3915
	.num_fifos		=	3,
3916
	.overlay_caps		=	omap3430_dispc_overlay_caps,
3917
	.supported_color_modes	=	omap3_dispc_supported_color_modes,
3918 3919
	.buffer_size_unit	=	1,
	.burst_size_unit	=	8,
3920
	.no_framedone_tv	=	true,
3921
	.set_max_preload	=	false,
3922
	.last_pixel_inc_missing	=	true,
3923 3924
};

3925
static const struct dispc_features omap34xx_rev3_0_dispc_feats = {
3926 3927 3928 3929 3930 3931
	.sw_start		=	7,
	.fp_start		=	19,
	.bp_start		=	31,
	.sw_max			=	256,
	.vp_max			=	4095,
	.hp_max			=	4096,
3932 3933 3934 3935
	.mgr_width_start	=	10,
	.mgr_height_start	=	26,
	.mgr_width_max		=	2048,
	.mgr_height_max		=	2048,
3936 3937
	.max_lcd_pclk		=	173000000,
	.max_tv_pclk		=	59000000,
3938 3939
	.calc_scaling		=	dispc_ovl_calc_scaling_34xx,
	.calc_core_clk		=	calc_core_clk_34xx,
3940
	.num_fifos		=	3,
3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966
	.overlay_caps		=	omap3430_dispc_overlay_caps,
	.supported_color_modes	=	omap3_dispc_supported_color_modes,
	.buffer_size_unit	=	1,
	.burst_size_unit	=	8,
	.no_framedone_tv	=	true,
	.set_max_preload	=	false,
	.last_pixel_inc_missing	=	true,
};

static const struct dispc_features omap36xx_dispc_feats = {
	.sw_start		=	7,
	.fp_start		=	19,
	.bp_start		=	31,
	.sw_max			=	256,
	.vp_max			=	4095,
	.hp_max			=	4096,
	.mgr_width_start	=	10,
	.mgr_height_start	=	26,
	.mgr_width_max		=	2048,
	.mgr_height_max		=	2048,
	.max_lcd_pclk		=	173000000,
	.max_tv_pclk		=	59000000,
	.calc_scaling		=	dispc_ovl_calc_scaling_34xx,
	.calc_core_clk		=	calc_core_clk_34xx,
	.num_fifos		=	3,
	.overlay_caps		=	omap3630_dispc_overlay_caps,
3967
	.supported_color_modes	=	omap3_dispc_supported_color_modes,
3968 3969
	.buffer_size_unit	=	1,
	.burst_size_unit	=	8,
3970
	.no_framedone_tv	=	true,
3971
	.set_max_preload	=	false,
3972
	.last_pixel_inc_missing	=	true,
3973 3974
};

3975
static const struct dispc_features omap44xx_dispc_feats = {
3976 3977 3978 3979 3980 3981
	.sw_start		=	7,
	.fp_start		=	19,
	.bp_start		=	31,
	.sw_max			=	256,
	.vp_max			=	4095,
	.hp_max			=	4096,
3982 3983 3984 3985
	.mgr_width_start	=	10,
	.mgr_height_start	=	26,
	.mgr_width_max		=	2048,
	.mgr_height_max		=	2048,
3986 3987
	.max_lcd_pclk		=	170000000,
	.max_tv_pclk		=	185625000,
3988 3989
	.calc_scaling		=	dispc_ovl_calc_scaling_44xx,
	.calc_core_clk		=	calc_core_clk_44xx,
3990
	.num_fifos		=	5,
3991
	.overlay_caps		=	omap4_dispc_overlay_caps,
3992
	.supported_color_modes	=	omap4_dispc_supported_color_modes,
3993 3994
	.buffer_size_unit	=	16,
	.burst_size_unit	=	16,
3995
	.gfx_fifo_workaround	=	true,
3996
	.set_max_preload	=	true,
3997
	.supports_sync_align	=	true,
T
Tomi Valkeinen 已提交
3998
	.has_writeback		=	true,
3999
	.supports_double_pixel	=	true,
4000
	.reverse_ilace_field_order =	true,
4001
	.has_gamma_table	=	true,
4002
	.has_gamma_i734_bug	=	true,
4003 4004
};

4005
static const struct dispc_features omap54xx_dispc_feats = {
4006 4007 4008 4009 4010 4011 4012 4013 4014 4015
	.sw_start		=	7,
	.fp_start		=	19,
	.bp_start		=	31,
	.sw_max			=	256,
	.vp_max			=	4095,
	.hp_max			=	4096,
	.mgr_width_start	=	11,
	.mgr_height_start	=	27,
	.mgr_width_max		=	4096,
	.mgr_height_max		=	4096,
4016 4017
	.max_lcd_pclk		=	170000000,
	.max_tv_pclk		=	186000000,
4018 4019 4020
	.calc_scaling		=	dispc_ovl_calc_scaling_44xx,
	.calc_core_clk		=	calc_core_clk_44xx,
	.num_fifos		=	5,
4021
	.overlay_caps		=	omap4_dispc_overlay_caps,
4022
	.supported_color_modes	=	omap4_dispc_supported_color_modes,
4023 4024
	.buffer_size_unit	=	16,
	.burst_size_unit	=	16,
4025
	.gfx_fifo_workaround	=	true,
4026
	.mstandby_workaround	=	true,
4027
	.set_max_preload	=	true,
4028
	.supports_sync_align	=	true,
T
Tomi Valkeinen 已提交
4029
	.has_writeback		=	true,
4030
	.supports_double_pixel	=	true,
4031
	.reverse_ilace_field_order =	true,
4032
	.has_gamma_table	=	true,
4033
	.has_gamma_i734_bug	=	true,
4034 4035
};

4036 4037 4038 4039 4040 4041 4042 4043
static irqreturn_t dispc_irq_handler(int irq, void *arg)
{
	if (!dispc.is_enabled)
		return IRQ_NONE;

	return dispc.user_handler(irq, dispc.user_data);
}

4044
static int dispc_request_irq(irq_handler_t handler, void *dev_id)
4045
{
4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064
	int r;

	if (dispc.user_handler != NULL)
		return -EBUSY;

	dispc.user_handler = handler;
	dispc.user_data = dev_id;

	/* ensure the dispc_irq_handler sees the values above */
	smp_wmb();

	r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler,
			     IRQF_SHARED, "OMAP DISPC", &dispc);
	if (r) {
		dispc.user_handler = NULL;
		dispc.user_data = NULL;
	}

	return r;
4065 4066
}

4067
static void dispc_free_irq(void *dev_id)
4068
{
4069 4070 4071 4072
	devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc);

	dispc.user_handler = NULL;
	dispc.user_data = NULL;
4073 4074
}

4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093
/*
 * Workaround for errata i734 in DSS dispc
 *  - LCD1 Gamma Correction Is Not Working When GFX Pipe Is Disabled
 *
 * For gamma tables to work on LCD1 the GFX plane has to be used at
 * least once after DSS HW has come out of reset. The workaround
 * sets up a minimal LCD setup with GFX plane and waits for one
 * vertical sync irq before disabling the setup and continuing with
 * the context restore. The physical outputs are gated during the
 * operation. This workaround requires that gamma table's LOADMODE
 * is set to 0x2 in DISPC_CONTROL1 register.
 *
 * For details see:
 * OMAP543x Multimedia Device Silicon Revision 2.0 Silicon Errata
 * Literature Number: SWPZ037E
 * Or some other relevant errata document for the DSS IP version.
 */

static const struct dispc_errata_i734_data {
4094
	struct videomode vm;
4095 4096 4097 4098
	struct omap_overlay_info ovli;
	struct omap_overlay_manager_info mgri;
	struct dss_lcd_mgr_config lcd_conf;
} i734 = {
4099
	.vm = {
4100
		.hactive = 8, .vactive = 1,
4101
		.pixelclock = 16000000,
4102
		.hsync_len = 8, .hfront_porch = 4, .hback_porch = 4,
4103
		.vsync_len = 1, .vfront_porch = 1, .vback_porch = 1,
4104

4105
		.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
4106 4107
			 DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_POSEDGE |
			 DISPLAY_FLAGS_PIXDATA_POSEDGE,
4108 4109 4110 4111
	},
	.ovli = {
		.screen_width = 1,
		.width = 1, .height = 1,
4112
		.fourcc = DRM_FORMAT_XRGB8888,
4113
		.rotation = DRM_MODE_ROTATE_0,
4114
		.rotation_type = OMAP_DSS_ROT_NONE,
4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151
		.pos_x = 0, .pos_y = 0,
		.out_width = 0, .out_height = 0,
		.global_alpha = 0xff,
		.pre_mult_alpha = 0,
		.zorder = 0,
	},
	.mgri = {
		.default_color = 0,
		.trans_enabled = false,
		.partial_alpha_enabled = false,
		.cpr_enable = false,
	},
	.lcd_conf = {
		.io_pad_mode = DSS_IO_PAD_MODE_BYPASS,
		.stallmode = false,
		.fifohandcheck = false,
		.clock_info = {
			.lck_div = 1,
			.pck_div = 2,
		},
		.video_port_width = 24,
		.lcden_sig_polarity = 0,
	},
};

static struct i734_buf {
	size_t size;
	dma_addr_t paddr;
	void *vaddr;
} i734_buf;

static int dispc_errata_i734_wa_init(void)
{
	if (!dispc.feat->has_gamma_i734_bug)
		return 0;

	i734_buf.size = i734.ovli.width * i734.ovli.height *
4152
		color_mode_to_bpp(i734.ovli.fourcc) / 8;
4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194

	i734_buf.vaddr = dma_alloc_writecombine(&dispc.pdev->dev, i734_buf.size,
						&i734_buf.paddr, GFP_KERNEL);
	if (!i734_buf.vaddr) {
		dev_err(&dispc.pdev->dev, "%s: dma_alloc_writecombine failed",
			__func__);
		return -ENOMEM;
	}

	return 0;
}

static void dispc_errata_i734_wa_fini(void)
{
	if (!dispc.feat->has_gamma_i734_bug)
		return;

	dma_free_writecombine(&dispc.pdev->dev, i734_buf.size, i734_buf.vaddr,
			      i734_buf.paddr);
}

static void dispc_errata_i734_wa(void)
{
	u32 framedone_irq = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_LCD);
	struct omap_overlay_info ovli;
	struct dss_lcd_mgr_config lcd_conf;
	u32 gatestate;
	unsigned int count;

	if (!dispc.feat->has_gamma_i734_bug)
		return;

	gatestate = REG_GET(DISPC_CONFIG, 8, 4);

	ovli = i734.ovli;
	ovli.paddr = i734_buf.paddr;
	lcd_conf = i734.lcd_conf;

	/* Gate all LCD1 outputs */
	REG_FLD_MOD(DISPC_CONFIG, 0x1f, 8, 4);

	/* Setup and enable GFX plane */
4195 4196
	dispc_ovl_setup(OMAP_DSS_GFX, &ovli, &i734.vm, false,
		OMAP_DSS_CHANNEL_LCD);
4197 4198 4199 4200 4201 4202 4203
	dispc_ovl_enable(OMAP_DSS_GFX, true);

	/* Set up and enable display manager for LCD1 */
	dispc_mgr_setup(OMAP_DSS_CHANNEL_LCD, &i734.mgri);
	dispc_calc_clock_rates(dss_get_dispc_clk_rate(),
			       &lcd_conf.clock_info);
	dispc_mgr_set_lcd_config(OMAP_DSS_CHANNEL_LCD, &lcd_conf);
4204
	dispc_mgr_set_timings(OMAP_DSS_CHANNEL_LCD, &i734.vm);
4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232

	dispc_clear_irqstatus(framedone_irq);

	/* Enable and shut the channel to produce just one frame */
	dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, true);
	dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, false);

	/* Busy wait for framedone. We can't fiddle with irq handlers
	 * in PM resume. Typically the loop runs less than 5 times and
	 * waits less than a micro second.
	 */
	count = 0;
	while (!(dispc_read_irqstatus() & framedone_irq)) {
		if (count++ > 10000) {
			dev_err(&dispc.pdev->dev, "%s: framedone timeout\n",
				__func__);
			break;
		}
	}
	dispc_ovl_enable(OMAP_DSS_GFX, false);

	/* Clear all irq bits before continuing */
	dispc_clear_irqstatus(0xffffffff);

	/* Restore the original state to LCD1 output gates */
	REG_FLD_MOD(DISPC_CONFIG, gatestate, 8, 4);
}

T
Tomi Valkeinen 已提交
4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265
static const struct dispc_ops dispc_ops = {
	.read_irqstatus = dispc_read_irqstatus,
	.clear_irqstatus = dispc_clear_irqstatus,
	.write_irqenable = dispc_write_irqenable,

	.request_irq = dispc_request_irq,
	.free_irq = dispc_free_irq,

	.runtime_get = dispc_runtime_get,
	.runtime_put = dispc_runtime_put,

	.get_num_ovls = dispc_get_num_ovls,
	.get_num_mgrs = dispc_get_num_mgrs,

	.mgr_enable = dispc_mgr_enable,
	.mgr_is_enabled = dispc_mgr_is_enabled,
	.mgr_get_vsync_irq = dispc_mgr_get_vsync_irq,
	.mgr_get_framedone_irq = dispc_mgr_get_framedone_irq,
	.mgr_get_sync_lost_irq = dispc_mgr_get_sync_lost_irq,
	.mgr_go_busy = dispc_mgr_go_busy,
	.mgr_go = dispc_mgr_go,
	.mgr_set_lcd_config = dispc_mgr_set_lcd_config,
	.mgr_set_timings = dispc_mgr_set_timings,
	.mgr_setup = dispc_mgr_setup,
	.mgr_get_supported_outputs = dispc_mgr_get_supported_outputs,
	.mgr_gamma_size = dispc_mgr_gamma_size,
	.mgr_set_gamma = dispc_mgr_set_gamma,

	.ovl_enable = dispc_ovl_enable,
	.ovl_setup = dispc_ovl_setup,
	.ovl_get_color_modes = dispc_ovl_get_color_modes,
};

4266
/* DISPC HW IP initialisation */
4267 4268
static const struct of_device_id dispc_of_match[] = {
	{ .compatible = "ti,omap2-dispc", .data = &omap24xx_dispc_feats },
4269
	{ .compatible = "ti,omap3-dispc", .data = &omap36xx_dispc_feats },
4270 4271 4272 4273 4274 4275 4276 4277 4278
	{ .compatible = "ti,omap4-dispc", .data = &omap44xx_dispc_feats },
	{ .compatible = "ti,omap5-dispc", .data = &omap54xx_dispc_feats },
	{ .compatible = "ti,dra7-dispc",  .data = &omap54xx_dispc_feats },
	{},
};

static const struct soc_device_attribute dispc_soc_devices[] = {
	{ .machine = "OMAP3[45]*",
	  .revision = "ES[12].?",	.data = &omap34xx_rev1_0_dispc_feats },
4279 4280 4281
	{ .machine = "OMAP3[45]*",	.data = &omap34xx_rev3_0_dispc_feats },
	{ .machine = "AM35*",		.data = &omap34xx_rev3_0_dispc_feats },
	{ .machine = "AM43*",		.data = &omap34xx_rev3_0_dispc_feats },
4282 4283 4284
	{ /* sentinel */ }
};

T
Tomi Valkeinen 已提交
4285
static int dispc_bind(struct device *dev, struct device *master, void *data)
4286
{
T
Tomi Valkeinen 已提交
4287
	struct platform_device *pdev = to_platform_device(dev);
4288
	const struct soc_device_attribute *soc;
4289
	u32 rev;
4290
	int r = 0;
4291
	struct resource *dispc_mem;
4292
	struct device_node *np = pdev->dev.of_node;
4293

4294 4295
	dispc.pdev = pdev;

4296 4297
	spin_lock_init(&dispc.control_lock);

4298
	/*
4299 4300
	 * The OMAP34xx and OMAP36xx can't be told apart using the compatible
	 * string, use SoC device matching.
4301 4302 4303 4304 4305 4306
	 */
	soc = soc_device_match(dispc_soc_devices);
	if (soc)
		dispc.feat = soc->data;
	else
		dispc.feat = of_match_device(dispc_of_match, &pdev->dev)->data;
4307

4308 4309 4310 4311
	r = dispc_errata_i734_wa_init();
	if (r)
		return r;

4312
	dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0);
4313 4314 4315
	dispc.base = devm_ioremap_resource(&pdev->dev, dispc_mem);
	if (IS_ERR(dispc.base))
		return PTR_ERR(dispc.base);
4316

4317 4318 4319
	dispc.irq = platform_get_irq(dispc.pdev, 0);
	if (dispc.irq < 0) {
		DSSERR("platform_get_irq failed\n");
4320
		return -ENODEV;
4321 4322
	}

4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336
	if (np && of_property_read_bool(np, "syscon-pol")) {
		dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol");
		if (IS_ERR(dispc.syscon_pol)) {
			dev_err(&pdev->dev, "failed to get syscon-pol regmap\n");
			return PTR_ERR(dispc.syscon_pol);
		}

		if (of_property_read_u32_index(np, "syscon-pol", 1,
				&dispc.syscon_pol_offset)) {
			dev_err(&pdev->dev, "failed to get syscon-pol offset\n");
			return -EINVAL;
		}
	}

4337 4338 4339 4340
	r = dispc_init_gamma_tables();
	if (r)
		return r;

4341 4342 4343 4344 4345
	pm_runtime_enable(&pdev->dev);

	r = dispc_runtime_get();
	if (r)
		goto err_runtime_get;
4346 4347 4348 4349

	_omap_dispc_initial_config();

	rev = dispc_read_reg(DISPC_REVISION);
4350
	dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n",
4351 4352
	       FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));

4353
	dispc_runtime_put();
4354

T
Tomi Valkeinen 已提交
4355 4356
	dispc_set_ops(&dispc_ops);

4357 4358
	dss_debugfs_create_file("dispc", dispc_dump_regs);

4359
	return 0;
4360 4361 4362

err_runtime_get:
	pm_runtime_disable(&pdev->dev);
4363
	return r;
4364 4365
}

T
Tomi Valkeinen 已提交
4366 4367
static void dispc_unbind(struct device *dev, struct device *master,
			       void *data)
4368
{
T
Tomi Valkeinen 已提交
4369 4370
	dispc_set_ops(NULL);

T
Tomi Valkeinen 已提交
4371
	pm_runtime_disable(dev);
4372 4373

	dispc_errata_i734_wa_fini();
T
Tomi Valkeinen 已提交
4374 4375 4376 4377 4378 4379
}

static const struct component_ops dispc_component_ops = {
	.bind	= dispc_bind,
	.unbind	= dispc_unbind,
};
4380

T
Tomi Valkeinen 已提交
4381 4382 4383 4384 4385 4386 4387 4388
static int dispc_probe(struct platform_device *pdev)
{
	return component_add(&pdev->dev, &dispc_component_ops);
}

static int dispc_remove(struct platform_device *pdev)
{
	component_del(&pdev->dev, &dispc_component_ops);
4389 4390 4391
	return 0;
}

4392 4393
static int dispc_runtime_suspend(struct device *dev)
{
4394 4395 4396 4397 4398 4399
	dispc.is_enabled = false;
	/* ensure the dispc_irq_handler sees the is_enabled value */
	smp_wmb();
	/* wait for current handler to finish before turning the DISPC off */
	synchronize_irq(dispc.irq);

4400 4401 4402 4403 4404 4405 4406
	dispc_save_context();

	return 0;
}

static int dispc_runtime_resume(struct device *dev)
{
4407 4408 4409 4410 4411 4412
	/*
	 * The reset value for load mode is 0 (OMAP_DSS_LOAD_CLUT_AND_FRAME)
	 * but we always initialize it to 2 (OMAP_DSS_LOAD_FRAME_ONLY) in
	 * _omap_dispc_initial_config(). We can thus use it to detect if
	 * we have lost register context.
	 */
4413 4414
	if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) {
		_omap_dispc_initial_config();
4415

4416 4417
		dispc_errata_i734_wa();

4418
		dispc_restore_context();
4419 4420

		dispc_restore_gamma_tables();
4421
	}
4422

4423 4424 4425
	dispc.is_enabled = true;
	/* ensure the dispc_irq_handler sees the is_enabled value */
	smp_wmb();
4426 4427 4428 4429 4430 4431 4432 4433 4434

	return 0;
}

static const struct dev_pm_ops dispc_pm_ops = {
	.runtime_suspend = dispc_runtime_suspend,
	.runtime_resume = dispc_runtime_resume,
};

4435
static struct platform_driver omap_dispchw_driver = {
T
Tomi Valkeinen 已提交
4436 4437
	.probe		= dispc_probe,
	.remove         = dispc_remove,
4438 4439
	.driver         = {
		.name   = "omapdss_dispc",
4440
		.pm	= &dispc_pm_ops,
4441
		.of_match_table = dispc_of_match,
T
Tomi Valkeinen 已提交
4442
		.suppress_bind_attrs = true,
4443 4444 4445
	},
};

T
Tomi Valkeinen 已提交
4446
int __init dispc_init_platform_driver(void)
4447
{
T
Tomi Valkeinen 已提交
4448
	return platform_driver_register(&omap_dispchw_driver);
4449 4450
}

4451
void dispc_uninit_platform_driver(void)
4452
{
4453
	platform_driver_unregister(&omap_dispchw_driver);
4454
}