dss.c 20.7 KB
Newer Older
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
/*
 * linux/drivers/video/omap2/dss/dss.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 "DSS"

#include <linux/kernel.h>
T
Tomi Valkeinen 已提交
26
#include <linux/module.h>
27
#include <linux/io.h>
28
#include <linux/export.h>
29 30 31 32
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/clk.h>
33
#include <linux/platform_device.h>
34
#include <linux/pm_runtime.h>
35
#include <linux/gfp.h>
36
#include <linux/sizes.h>
T
Tomi Valkeinen 已提交
37
#include <linux/of.h>
38

39
#include <video/omapdss.h>
40

41
#include "dss.h"
42
#include "dss_features.h"
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

#define DSS_SZ_REGS			SZ_512

struct dss_reg {
	u16 idx;
};

#define DSS_REG(idx)			((const struct dss_reg) { idx })

#define DSS_REVISION			DSS_REG(0x0000)
#define DSS_SYSCONFIG			DSS_REG(0x0010)
#define DSS_SYSSTATUS			DSS_REG(0x0014)
#define DSS_CONTROL			DSS_REG(0x0040)
#define DSS_SDI_CONTROL			DSS_REG(0x0044)
#define DSS_PLL_CONTROL			DSS_REG(0x0048)
#define DSS_SDI_STATUS			DSS_REG(0x005C)

#define REG_GET(idx, start, end) \
	FLD_GET(dss_read_reg(idx), start, end)

#define REG_FLD_MOD(idx, val, start, end) \
	dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))

66 67 68
static int dss_runtime_get(void);
static void dss_runtime_put(void);

69 70 71
struct dss_features {
	u8 fck_div_max;
	u8 dss_fck_multiplier;
72
	const char *parent_clk_name;
73
	int (*dpi_select_source)(enum omap_channel channel);
74 75
};

76
static struct {
77
	struct platform_device *pdev;
78
	void __iomem    *base;
79

80
	struct clk	*parent_clk;
81
	struct clk	*dss_clk;
82
	unsigned long	dss_clk_rate;
83 84 85 86 87

	unsigned long	cache_req_pck;
	unsigned long	cache_prate;
	struct dispc_clock_info cache_dispc_cinfo;

88
	enum omap_dss_clk_source dsi_clk_source[MAX_NUM_DSI];
89 90
	enum omap_dss_clk_source dispc_clk_source;
	enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS];
91

92
	bool		ctx_valid;
93
	u32		ctx[DSS_SZ_REGS / sizeof(u32)];
94 95

	const struct dss_features *feat;
96 97
} dss;

98
static const char * const dss_generic_clk_source_names[] = {
99 100 101
	[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC]	= "DSI_PLL_HSDIV_DISPC",
	[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI]	= "DSI_PLL_HSDIV_DSI",
	[OMAP_DSS_CLK_SRC_FCK]			= "DSS_FCK",
T
Tomi Valkeinen 已提交
102 103
	[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC]	= "DSI_PLL2_HSDIV_DISPC",
	[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI]	= "DSI_PLL2_HSDIV_DSI",
104 105
};

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
{
	__raw_writel(val, dss.base + idx.idx);
}

static inline u32 dss_read_reg(const struct dss_reg idx)
{
	return __raw_readl(dss.base + idx.idx);
}

#define SR(reg) \
	dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
#define RR(reg) \
	dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])

121
static void dss_save_context(void)
122
{
123
	DSSDBG("dss_save_context\n");
124 125 126

	SR(CONTROL);

127 128 129 130 131
	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
			OMAP_DISPLAY_TYPE_SDI) {
		SR(SDI_CONTROL);
		SR(PLL_CONTROL);
	}
132 133 134 135

	dss.ctx_valid = true;

	DSSDBG("context saved\n");
136 137
}

138
static void dss_restore_context(void)
139
{
140
	DSSDBG("dss_restore_context\n");
141

142 143 144
	if (!dss.ctx_valid)
		return;

145 146
	RR(CONTROL);

147 148 149 150 151
	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
			OMAP_DISPLAY_TYPE_SDI) {
		RR(SDI_CONTROL);
		RR(PLL_CONTROL);
	}
152 153

	DSSDBG("context restored\n");
154 155 156 157 158
}

#undef SR
#undef RR

159
void dss_sdi_init(int datapairs)
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
{
	u32 l;

	BUG_ON(datapairs > 3 || datapairs < 1);

	l = dss_read_reg(DSS_SDI_CONTROL);
	l = FLD_MOD(l, 0xf, 19, 15);		/* SDI_PDIV */
	l = FLD_MOD(l, datapairs-1, 3, 2);	/* SDI_PRSEL */
	l = FLD_MOD(l, 2, 1, 0);		/* SDI_BWSEL */
	dss_write_reg(DSS_SDI_CONTROL, l);

	l = dss_read_reg(DSS_PLL_CONTROL);
	l = FLD_MOD(l, 0x7, 25, 22);	/* SDI_PLL_FREQSEL */
	l = FLD_MOD(l, 0xb, 16, 11);	/* SDI_PLL_REGN */
	l = FLD_MOD(l, 0xb4, 10, 1);	/* SDI_PLL_REGM */
	dss_write_reg(DSS_PLL_CONTROL, l);
}

int dss_sdi_enable(void)
{
	unsigned long timeout;

	dispc_pck_free_enable(1);

	/* Reset SDI PLL */
	REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
	udelay(1);	/* wait 2x PCLK */

	/* Lock SDI PLL */
	REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */

	/* Waiting for PLL lock request to complete */
	timeout = jiffies + msecs_to_jiffies(500);
	while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
		if (time_after_eq(jiffies, timeout)) {
			DSSERR("PLL lock request timed out\n");
			goto err1;
		}
	}

	/* Clearing PLL_GO bit */
	REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);

	/* Waiting for PLL to lock */
	timeout = jiffies + msecs_to_jiffies(500);
	while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
		if (time_after_eq(jiffies, timeout)) {
			DSSERR("PLL lock timed out\n");
			goto err1;
		}
	}

	dispc_lcd_enable_signal(1);

	/* Waiting for SDI reset to complete */
	timeout = jiffies + msecs_to_jiffies(500);
	while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
		if (time_after_eq(jiffies, timeout)) {
			DSSERR("SDI reset timed out\n");
			goto err2;
		}
	}

	return 0;

 err2:
	dispc_lcd_enable_signal(0);
 err1:
	/* Reset SDI PLL */
	REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */

	dispc_pck_free_enable(0);

	return -ETIMEDOUT;
}

void dss_sdi_disable(void)
{
	dispc_lcd_enable_signal(0);

	dispc_pck_free_enable(0);

	/* Reset SDI PLL */
	REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
}

246
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src)
247
{
248
	return dss_generic_clk_source_names[clk_src];
249 250
}

251 252
void dss_dump_clocks(struct seq_file *s)
{
253 254
	const char *fclk_name, *fclk_real_name;
	unsigned long fclk_rate;
255

256 257
	if (dss_runtime_get())
		return;
258 259 260

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

261 262
	fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK);
	fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK);
263
	fclk_rate = clk_get_rate(dss.dss_clk);
264

T
Tomi Valkeinen 已提交
265 266 267
	seq_printf(s, "%s (%s) = %lu\n",
			fclk_name, fclk_real_name,
			fclk_rate);
268

269
	dss_runtime_put();
270 271
}

272
static void dss_dump_regs(struct seq_file *s)
273 274 275
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))

276 277
	if (dss_runtime_get())
		return;
278 279 280 281 282

	DUMPREG(DSS_REVISION);
	DUMPREG(DSS_SYSCONFIG);
	DUMPREG(DSS_SYSSTATUS);
	DUMPREG(DSS_CONTROL);
283 284 285 286 287 288 289

	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
			OMAP_DISPLAY_TYPE_SDI) {
		DUMPREG(DSS_SDI_CONTROL);
		DUMPREG(DSS_PLL_CONTROL);
		DUMPREG(DSS_SDI_STATUS);
	}
290

291
	dss_runtime_put();
292 293 294
#undef DUMPREG
}

295
static void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src)
296
{
297
	struct platform_device *dsidev;
298
	int b;
299
	u8 start, end;
300

301
	switch (clk_src) {
302
	case OMAP_DSS_CLK_SRC_FCK:
303 304
		b = 0;
		break;
305
	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
306
		b = 1;
307 308
		dsidev = dsi_get_dsidev_from_id(0);
		dsi_wait_pll_hsdiv_dispc_active(dsidev);
309
		break;
310 311 312 313 314
	case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
		b = 2;
		dsidev = dsi_get_dsidev_from_id(1);
		dsi_wait_pll_hsdiv_dispc_active(dsidev);
		break;
315 316
	default:
		BUG();
317
		return;
318
	}
319

320 321 322
	dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end);

	REG_FLD_MOD(DSS_CONTROL, b, start, end);	/* DISPC_CLK_SWITCH */
323 324 325 326

	dss.dispc_clk_source = clk_src;
}

327 328
void dss_select_dsi_clk_source(int dsi_module,
		enum omap_dss_clk_source clk_src)
329
{
330
	struct platform_device *dsidev;
331
	int b, pos;
332

333
	switch (clk_src) {
334
	case OMAP_DSS_CLK_SRC_FCK:
335 336
		b = 0;
		break;
337
	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI:
338
		BUG_ON(dsi_module != 0);
339
		b = 1;
340 341
		dsidev = dsi_get_dsidev_from_id(0);
		dsi_wait_pll_hsdiv_dsi_active(dsidev);
342
		break;
343 344 345 346 347 348
	case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI:
		BUG_ON(dsi_module != 1);
		b = 1;
		dsidev = dsi_get_dsidev_from_id(1);
		dsi_wait_pll_hsdiv_dsi_active(dsidev);
		break;
349 350
	default:
		BUG();
351
		return;
352
	}
353

354 355
	pos = dsi_module == 0 ? 1 : 10;
	REG_FLD_MOD(DSS_CONTROL, b, pos, pos);	/* DSIx_CLK_SWITCH */
356

357
	dss.dsi_clk_source[dsi_module] = clk_src;
358 359
}

360
void dss_select_lcd_clk_source(enum omap_channel channel,
361
		enum omap_dss_clk_source clk_src)
362
{
363
	struct platform_device *dsidev;
364 365
	int b, ix, pos;

366 367
	if (!dss_has_feature(FEAT_LCD_CLK_SRC)) {
		dss_select_dispc_clk_source(clk_src);
368
		return;
369
	}
370 371

	switch (clk_src) {
372
	case OMAP_DSS_CLK_SRC_FCK:
373 374
		b = 0;
		break;
375
	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
376 377
		BUG_ON(channel != OMAP_DSS_CHANNEL_LCD);
		b = 1;
378 379
		dsidev = dsi_get_dsidev_from_id(0);
		dsi_wait_pll_hsdiv_dispc_active(dsidev);
380
		break;
381
	case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
382 383
		BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 &&
		       channel != OMAP_DSS_CHANNEL_LCD3);
384 385 386 387
		b = 1;
		dsidev = dsi_get_dsidev_from_id(1);
		dsi_wait_pll_hsdiv_dispc_active(dsidev);
		break;
388 389
	default:
		BUG();
390
		return;
391 392
	}

393 394
	pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
	     (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19);
395 396
	REG_FLD_MOD(DSS_CONTROL, b, pos, pos);	/* LCDx_CLK_SWITCH */

397 398
	ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
	    (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
399 400 401
	dss.lcd_clk_source[ix] = clk_src;
}

402
enum omap_dss_clk_source dss_get_dispc_clk_source(void)
403
{
404
	return dss.dispc_clk_source;
405 406
}

407
enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module)
408
{
409
	return dss.dsi_clk_source[dsi_module];
410 411
}

412
enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
413
{
414
	if (dss_has_feature(FEAT_LCD_CLK_SRC)) {
415 416
		int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
			(channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
417 418 419 420 421 422
		return dss.lcd_clk_source[ix];
	} else {
		/* LCD_CLK source is the same as DISPC_FCLK source for
		 * OMAP2 and OMAP3 */
		return dss.dispc_clk_source;
	}
423 424
}

425 426
bool dss_div_calc(unsigned long pck, unsigned long fck_min,
		dss_div_calc_func func, void *data)
427 428 429 430 431 432
{
	int fckd, fckd_start, fckd_stop;
	unsigned long fck;
	unsigned long fck_hw_max;
	unsigned long fckd_hw_max;
	unsigned long prate;
433
	unsigned m;
434

435 436
	fck_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);

437
	if (dss.parent_clk == NULL) {
438 439 440 441 442 443 444 445
		unsigned pckd;

		pckd = fck_hw_max / pck;

		fck = pck * pckd;

		fck = clk_round_rate(dss.dss_clk, fck);

446
		return func(fck, data);
447 448 449 450
	}

	fckd_hw_max = dss.feat->fck_div_max;

451
	m = dss.feat->dss_fck_multiplier;
452
	prate = clk_get_rate(dss.parent_clk);
453 454 455

	fck_min = fck_min ? fck_min : 1;

456 457
	fckd_start = min(prate * m / fck_min, fckd_hw_max);
	fckd_stop = max(DIV_ROUND_UP(prate * m, fck_hw_max), 1ul);
458 459

	for (fckd = fckd_start; fckd >= fckd_stop; --fckd) {
460
		fck = DIV_ROUND_UP(prate, fckd) * m;
461

462
		if (func(fck, data))
463 464 465 466 467 468
			return true;
	}

	return false;
}

469
int dss_set_fck_rate(unsigned long rate)
470
{
471
	int r;
472

473
	DSSDBG("set fck to %lu\n", rate);
474

475 476 477
	r = clk_set_rate(dss.dss_clk, rate);
	if (r)
		return r;
478

479 480
	dss.dss_clk_rate = clk_get_rate(dss.dss_clk);

481
	WARN_ONCE(dss.dss_clk_rate != rate,
482
			"clk rate mismatch: %lu != %lu", dss.dss_clk_rate,
483
			rate);
484 485 486 487

	return 0;
}

488 489 490 491 492
unsigned long dss_get_dispc_clk_rate(void)
{
	return dss.dss_clk_rate;
}

T
Tomi Valkeinen 已提交
493 494 495
static int dss_setup_default_clock(void)
{
	unsigned long max_dss_fck, prate;
496
	unsigned long fck;
T
Tomi Valkeinen 已提交
497 498 499 500 501
	unsigned fck_div;
	int r;

	max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);

502 503 504 505
	if (dss.parent_clk == NULL) {
		fck = clk_round_rate(dss.dss_clk, max_dss_fck);
	} else {
		prate = clk_get_rate(dss.parent_clk);
T
Tomi Valkeinen 已提交
506

507 508
		fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier,
				max_dss_fck);
509
		fck = DIV_ROUND_UP(prate, fck_div) * dss.feat->dss_fck_multiplier;
510
	}
T
Tomi Valkeinen 已提交
511

512
	r = dss_set_fck_rate(fck);
T
Tomi Valkeinen 已提交
513 514 515 516 517 518
	if (r)
		return r;

	return 0;
}

519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
void dss_set_venc_output(enum omap_dss_venc_type type)
{
	int l = 0;

	if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
		l = 0;
	else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
		l = 1;
	else
		BUG();

	/* venc out selection. 0 = comp, 1 = svideo */
	REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
}

void dss_set_dac_pwrdn_bgz(bool enable)
{
	REG_FLD_MOD(DSS_CONTROL, enable, 5, 5);	/* DAC Power-Down Control */
}

539
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select src)
540
{
541 542 543 544 545 546 547 548 549 550
	enum omap_display_type dp;
	dp = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT);

	/* Complain about invalid selections */
	WARN_ON((src == DSS_VENC_TV_CLK) && !(dp & OMAP_DISPLAY_TYPE_VENC));
	WARN_ON((src == DSS_HDMI_M_PCLK) && !(dp & OMAP_DISPLAY_TYPE_HDMI));

	/* Select only if we have options */
	if ((dp & OMAP_DISPLAY_TYPE_VENC) && (dp & OMAP_DISPLAY_TYPE_HDMI))
		REG_FLD_MOD(DSS_CONTROL, src, 15, 15);	/* VENC_HDMI_SWITCH */
551 552
}

553 554 555 556 557 558 559 560
enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void)
{
	enum omap_display_type displays;

	displays = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT);
	if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0)
		return DSS_VENC_TV_CLK;

561 562 563
	if ((displays & OMAP_DISPLAY_TYPE_VENC) == 0)
		return DSS_HDMI_M_PCLK;

564 565 566
	return REG_GET(DSS_CONTROL, 15, 15);
}

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
static int dss_dpi_select_source_omap2_omap3(enum omap_channel channel)
{
	if (channel != OMAP_DSS_CHANNEL_LCD)
		return -EINVAL;

	return 0;
}

static int dss_dpi_select_source_omap4(enum omap_channel channel)
{
	int val;

	switch (channel) {
	case OMAP_DSS_CHANNEL_LCD2:
		val = 0;
		break;
	case OMAP_DSS_CHANNEL_DIGIT:
		val = 1;
		break;
	default:
		return -EINVAL;
	}

	REG_FLD_MOD(DSS_CONTROL, val, 17, 17);

	return 0;
}

static int dss_dpi_select_source_omap5(enum omap_channel channel)
{
	int val;

	switch (channel) {
	case OMAP_DSS_CHANNEL_LCD:
		val = 1;
		break;
	case OMAP_DSS_CHANNEL_LCD2:
		val = 2;
		break;
	case OMAP_DSS_CHANNEL_LCD3:
		val = 3;
		break;
	case OMAP_DSS_CHANNEL_DIGIT:
		val = 0;
		break;
	default:
		return -EINVAL;
	}

	REG_FLD_MOD(DSS_CONTROL, val, 17, 16);

	return 0;
}

int dss_dpi_select_source(enum omap_channel channel)
{
	return dss.feat->dpi_select_source(channel);
}

626 627
static int dss_get_clocks(void)
{
628
	struct clk *clk;
629

A
Archit Taneja 已提交
630
	clk = devm_clk_get(&dss.pdev->dev, "fck");
631 632
	if (IS_ERR(clk)) {
		DSSERR("can't get clock fck\n");
A
Archit Taneja 已提交
633
		return PTR_ERR(clk);
634
	}
635

636
	dss.dss_clk = clk;
637

638 639
	if (dss.feat->parent_clk_name) {
		clk = clk_get(NULL, dss.feat->parent_clk_name);
640
		if (IS_ERR(clk)) {
641
			DSSERR("Failed to get %s\n", dss.feat->parent_clk_name);
A
Archit Taneja 已提交
642
			return PTR_ERR(clk);
643 644 645
		}
	} else {
		clk = NULL;
646 647
	}

648
	dss.parent_clk = clk;
649

650 651 652 653 654
	return 0;
}

static void dss_put_clocks(void)
{
655 656
	if (dss.parent_clk)
		clk_put(dss.parent_clk);
657 658
}

659
static int dss_runtime_get(void)
660
{
661
	int r;
662

663
	DSSDBG("dss_runtime_get\n");
664

665 666 667
	r = pm_runtime_get_sync(&dss.pdev->dev);
	WARN_ON(r < 0);
	return r < 0 ? r : 0;
668 669
}

670
static void dss_runtime_put(void)
671
{
672
	int r;
673

674
	DSSDBG("dss_runtime_put\n");
675

676
	r = pm_runtime_put_sync(&dss.pdev->dev);
677
	WARN_ON(r < 0 && r != -ENOSYS && r != -EBUSY);
678 679 680
}

/* DEBUGFS */
681
#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
682 683 684 685 686 687 688 689 690 691
void dss_debug_dump_clocks(struct seq_file *s)
{
	dss_dump_clocks(s);
	dispc_dump_clocks(s);
#ifdef CONFIG_OMAP2_DSS_DSI
	dsi_dump_clocks(s);
#endif
}
#endif

692
static const struct dss_features omap24xx_dss_feats __initconst = {
693 694 695 696 697
	/*
	 * fck div max is really 16, but the divider range has gaps. The range
	 * from 1 to 6 has no gaps, so let's use that as a max.
	 */
	.fck_div_max		=	6,
698
	.dss_fck_multiplier	=	2,
699
	.parent_clk_name	=	"core_ck",
700
	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3,
701 702 703 704 705
};

static const struct dss_features omap34xx_dss_feats __initconst = {
	.fck_div_max		=	16,
	.dss_fck_multiplier	=	2,
706
	.parent_clk_name	=	"dpll4_ck",
707
	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3,
708 709 710 711 712
};

static const struct dss_features omap3630_dss_feats __initconst = {
	.fck_div_max		=	32,
	.dss_fck_multiplier	=	1,
713
	.parent_clk_name	=	"dpll4_ck",
714
	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3,
715 716 717 718 719
};

static const struct dss_features omap44xx_dss_feats __initconst = {
	.fck_div_max		=	32,
	.dss_fck_multiplier	=	1,
720
	.parent_clk_name	=	"dpll_per_x2_ck",
721
	.dpi_select_source	=	&dss_dpi_select_source_omap4,
722 723 724 725 726
};

static const struct dss_features omap54xx_dss_feats __initconst = {
	.fck_div_max		=	64,
	.dss_fck_multiplier	=	1,
727
	.parent_clk_name	=	"dpll_per_x2_ck",
728
	.dpi_select_source	=	&dss_dpi_select_source_omap5,
729 730
};

731 732 733 734 735 736 737
static const struct dss_features am43xx_dss_feats __initconst = {
	.fck_div_max		=	0,
	.dss_fck_multiplier	=	0,
	.parent_clk_name	=	NULL,
	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3,
};

T
Tomi Valkeinen 已提交
738
static int __init dss_init_features(struct platform_device *pdev)
739 740 741 742
{
	const struct dss_features *src;
	struct dss_features *dst;

T
Tomi Valkeinen 已提交
743
	dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
744
	if (!dst) {
T
Tomi Valkeinen 已提交
745
		dev_err(&pdev->dev, "Failed to allocate local DSS Features\n");
746 747 748
		return -ENOMEM;
	}

749
	switch (omapdss_get_version()) {
T
Tomi Valkeinen 已提交
750
	case OMAPDSS_VER_OMAP24xx:
751
		src = &omap24xx_dss_feats;
T
Tomi Valkeinen 已提交
752 753 754 755 756
		break;

	case OMAPDSS_VER_OMAP34xx_ES1:
	case OMAPDSS_VER_OMAP34xx_ES3:
	case OMAPDSS_VER_AM35xx:
757
		src = &omap34xx_dss_feats;
T
Tomi Valkeinen 已提交
758 759 760
		break;

	case OMAPDSS_VER_OMAP3630:
761
		src = &omap3630_dss_feats;
T
Tomi Valkeinen 已提交
762 763 764 765 766
		break;

	case OMAPDSS_VER_OMAP4430_ES1:
	case OMAPDSS_VER_OMAP4430_ES2:
	case OMAPDSS_VER_OMAP4:
767
		src = &omap44xx_dss_feats;
T
Tomi Valkeinen 已提交
768 769 770
		break;

	case OMAPDSS_VER_OMAP5:
771
		src = &omap54xx_dss_feats;
T
Tomi Valkeinen 已提交
772 773
		break;

774 775 776 777
	case OMAPDSS_VER_AM43xx:
		src = &am43xx_dss_feats;
		break;

T
Tomi Valkeinen 已提交
778
	default:
779
		return -ENODEV;
T
Tomi Valkeinen 已提交
780
	}
781 782 783 784 785 786 787

	memcpy(dst, src, sizeof(*dst));
	dss.feat = dst;

	return 0;
}

788
static int __init dss_init_ports(struct platform_device *pdev)
T
Tomi Valkeinen 已提交
789 790 791 792 793 794 795 796 797
{
	struct device_node *parent = pdev->dev.of_node;
	struct device_node *port;
	int r;

	if (parent == NULL)
		return 0;

	port = omapdss_of_get_next_port(parent, NULL);
798
	if (!port)
T
Tomi Valkeinen 已提交
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
		return 0;

	do {
		u32 reg;

		r = of_property_read_u32(port, "reg", &reg);
		if (r)
			reg = 0;

#ifdef CONFIG_OMAP2_DSS_DPI
		if (reg == 0)
			dpi_init_port(pdev, port);
#endif

#ifdef CONFIG_OMAP2_DSS_SDI
		if (reg == 1)
			sdi_init_port(pdev, port);
#endif

	} while ((port = omapdss_of_get_next_port(parent, port)) != NULL);

	return 0;
}

823
static void __exit dss_uninit_ports(void)
T
Tomi Valkeinen 已提交
824 825 826 827 828 829 830 831 832 833
{
#ifdef CONFIG_OMAP2_DSS_DPI
	dpi_uninit_port();
#endif

#ifdef CONFIG_OMAP2_DSS_SDI
	sdi_uninit_port();
#endif
}

834
/* DSS HW IP initialisation */
T
Tomi Valkeinen 已提交
835
static int __init omap_dsshw_probe(struct platform_device *pdev)
836
{
837 838
	struct resource *dss_mem;
	u32 rev;
839 840 841 842
	int r;

	dss.pdev = pdev;

T
Tomi Valkeinen 已提交
843
	r = dss_init_features(dss.pdev);
844 845 846
	if (r)
		return r;

847 848 849
	dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
	if (!dss_mem) {
		DSSERR("can't get IORESOURCE_MEM DSS\n");
850
		return -EINVAL;
851
	}
852

J
Julia Lawall 已提交
853 854
	dss.base = devm_ioremap(&pdev->dev, dss_mem->start,
				resource_size(dss_mem));
855 856
	if (!dss.base) {
		DSSERR("can't ioremap DSS\n");
857
		return -ENOMEM;
858 859
	}

860 861
	r = dss_get_clocks();
	if (r)
862
		return r;
863

T
Tomi Valkeinen 已提交
864 865 866 867
	r = dss_setup_default_clock();
	if (r)
		goto err_setup_clocks;

868
	pm_runtime_enable(&pdev->dev);
869

870 871 872
	r = dss_runtime_get();
	if (r)
		goto err_runtime_get;
873

874 875
	dss.dss_clk_rate = clk_get_rate(dss.dss_clk);

876 877 878
	/* Select DPLL */
	REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);

879 880
	dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);

881 882 883 884 885 886 887 888 889 890
#ifdef CONFIG_OMAP2_DSS_VENC
	REG_FLD_MOD(DSS_CONTROL, 1, 4, 4);	/* venc dac demen */
	REG_FLD_MOD(DSS_CONTROL, 1, 3, 3);	/* venc clock 4x enable */
	REG_FLD_MOD(DSS_CONTROL, 0, 2, 2);	/* venc clock mode = normal */
#endif
	dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
	dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
	dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK;
	dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
	dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
891

T
Tomi Valkeinen 已提交
892 893
	dss_init_ports(pdev);

894 895 896 897
	rev = dss_read_reg(DSS_REVISION);
	printk(KERN_INFO "OMAP DSS rev %d.%d\n",
			FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));

898
	dss_runtime_put();
899

900 901
	dss_debugfs_create_file("dss", dss_dump_regs);

902
	return 0;
903

904 905
err_runtime_get:
	pm_runtime_disable(&pdev->dev);
T
Tomi Valkeinen 已提交
906
err_setup_clocks:
907
	dss_put_clocks();
908 909 910
	return r;
}

T
Tomi Valkeinen 已提交
911
static int __exit omap_dsshw_remove(struct platform_device *pdev)
912
{
T
Tomi Valkeinen 已提交
913 914
	dss_uninit_ports();

915
	pm_runtime_disable(&pdev->dev);
916 917

	dss_put_clocks();
918

919 920 921
	return 0;
}

922 923 924
static int dss_runtime_suspend(struct device *dev)
{
	dss_save_context();
925
	dss_set_min_bus_tput(dev, 0);
926 927 928 929 930
	return 0;
}

static int dss_runtime_resume(struct device *dev)
{
931 932 933 934 935 936 937 938 939 940 941 942
	int r;
	/*
	 * Set an arbitrarily high tput request to ensure OPP100.
	 * What we should really do is to make a request to stay in OPP100,
	 * without any tput requirements, but that is not currently possible
	 * via the PM layer.
	 */

	r = dss_set_min_bus_tput(dev, 1000000000);
	if (r)
		return r;

943
	dss_restore_context();
944 945 946 947 948 949 950 951
	return 0;
}

static const struct dev_pm_ops dss_pm_ops = {
	.runtime_suspend = dss_runtime_suspend,
	.runtime_resume = dss_runtime_resume,
};

T
Tomi Valkeinen 已提交
952 953 954 955 956 957 958 959 960
static const struct of_device_id dss_of_match[] = {
	{ .compatible = "ti,omap2-dss", },
	{ .compatible = "ti,omap3-dss", },
	{ .compatible = "ti,omap4-dss", },
	{},
};

MODULE_DEVICE_TABLE(of, dss_of_match);

961
static struct platform_driver omap_dsshw_driver = {
T
Tomi Valkeinen 已提交
962
	.remove         = __exit_p(omap_dsshw_remove),
963 964 965
	.driver         = {
		.name   = "omapdss_dss",
		.owner  = THIS_MODULE,
966
		.pm	= &dss_pm_ops,
T
Tomi Valkeinen 已提交
967
		.of_match_table = dss_of_match,
968 969 970
	},
};

T
Tomi Valkeinen 已提交
971
int __init dss_init_platform_driver(void)
972
{
973
	return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe);
974 975 976 977
}

void dss_uninit_platform_driver(void)
{
978
	platform_driver_unregister(&omap_dsshw_driver);
979
}