summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/85xx/corenet_diu.c
blob: cb5a512482ecd1e13c633ac16094cf1c2bf3a1f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
 * CoreNet platform DIU operation
 *
 * Copyright 2014 Freescale Semiconductor Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 */

#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include <sysdev/fsl_soc.h>

/*DIU Pixel ClockCR offset in scfg*/
#define CCSR_SCFG_PIXCLKCR      0x28

/* DIU Pixel Clock bits of the PIXCLKCR */
#define PIXCLKCR_PXCKEN		0x80000000
#define PIXCLKCR_PXCKINV	0x40000000
#define PIXCLKCR_PXCKDLY	0x0000FF00
#define PIXCLKCR_PXCLK_MASK	0x00FF0000

/* Some CPLD register definitions */
#define CPLD_DIUCSR		0x16
#define CPLD_DIUCSR_DVIEN	0x80
#define CPLD_DIUCSR_BACKLIGHT	0x0f

/**
 * t1042rdb_set_monitor_port: switch the output to a different monitor port
 */
static void t1042rdb_set_monitor_port(enum fsl_diu_monitor_port port)
{
	struct device_node *cpld_node;
	static void __iomem *cpld_base;

	cpld_node = of_find_compatible_node(NULL, NULL, "fsl,t104xrdb-cpld");
	if (!cpld_node) {
		pr_err("%s: Missing CPLD node\n", __func__);
		return;
	}

	cpld_base = of_iomap(cpld_node, 0);
	if (!cpld_base) {
		pr_err("%s: Could not map cpld registers\n", __func__);
		goto exit;
	}

	switch (port) {
	case FSL_DIU_PORT_DVI:
		/* Enable the DVI(HDMI) port, disable the DFP and
		 * the backlight
		 */
		clrbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_DVIEN);
		break;
	case FSL_DIU_PORT_LVDS:
		/*
		 * LVDS also needs backlight enabled, otherwise the display
		 * will be blank.
		 */
		/* Enable the DFP port, disable the DVI*/
		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 8);
		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 4);
		setbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_BACKLIGHT);
		break;
	default:
		pr_err("%s: Unsupported monitor port %i\n", __func__, port);
	}

	iounmap(cpld_base);
exit:
	of_node_put(cpld_node);
}

/**
 * t102xqds_set_monitor_port: switch the output to a different monitor port
 */

#define BRDCFG5				0x55
#define BRDCFG15			0x5F
/* BRDCFG5[0:1] controls routing and use of I2C3 & I2C4 ports*/
#define BRDCFG5_IMX_DIU_HIGH		0x80
#define BRDCFG5_IMX_DIU_LOW		0x40

/* BRDCFG15[3] controls LCD Panel Powerdown*/
#define BRDCFG15_LCD_PD			0x10
#define BRDCFG15_LCD_ENABLED		0x80

/* BRDCFG15[6:7] controls DIU MUX selction*/
#define BRDCFG15_DIUSEL_HDMI		0x03
#define BRDCFG15_DIUSEL_LVDS		0x01

static void t102xqds_set_monitor_port(enum fsl_diu_monitor_port port)
{
	struct device_node *pixis_node;
	void __iomem *pixis;

	pixis_node = of_find_compatible_node(NULL, NULL, "fsl,tetra-fpga");
	if (!pixis_node) {
		pr_err("t102xqds: missing pixis node\n");
		goto exit;
	}

	pixis = of_iomap(pixis_node, 0);
	of_node_put(pixis_node);
	if (!pixis) {
		pr_err("t102xqds: could not map pixis registers\n");
		goto exit;
	}

	/* Route I2C4 to DIU system as HSYNC/VSYNC */
	clrbits8(pixis + BRDCFG5, BRDCFG5_IMX_DIU_LOW);
	setbits8(pixis + BRDCFG5, BRDCFG5_IMX_DIU_HIGH);

	switch (port) {
	case FSL_DIU_PORT_DVI:
		/* Enable the DVI(HDMI) port, disable the DFP and
		 * the backlight
		 */
		clrbits8(pixis + BRDCFG15, BRDCFG15_LCD_ENABLED);
		setbits8(pixis + BRDCFG15, BRDCFG15_LCD_PD);

		clrbits8(pixis + BRDCFG15, BRDCFG15_DIUSEL_HDMI);
		break;
	case FSL_DIU_PORT_LVDS:
		/*
		 * LVDS also needs backlight enabled, otherwise the display
		 * will be blank.
		 */
		/* Enable the DFP port, disable the DVI*/
		setbits8(pixis + BRDCFG15, BRDCFG15_DIUSEL_LVDS);

		clrbits8(pixis + BRDCFG15, BRDCFG15_LCD_PD);
		setbits8(pixis + BRDCFG15, BRDCFG15_LCD_ENABLED);
		break;
	default:
		pr_err("%s: Unsupported monitor port %i\n", __func__, port);
	}

	iounmap(pixis);

exit:
	return;
}

static const struct of_device_id scfg_matches[] = {
	{
		.compatible = "fsl,t1040-scfg",
	},
	{
		.compatible = "fsl,t1024-scfg",
	},
	{},
};


/**
 * t1042rdb_set_pixel_clock: program the DIU's clock
 *
 * @pixclock: the wavelength, in picoseconds, of the clock
 */
static void corenet_set_pixel_clock(unsigned int pixclock)
{
	struct device_node *scfg_np = NULL;
	void __iomem *scfg;
	unsigned long freq;
	u64 temp;
	u32 pxclk;

	/* Map the global utilities registers. */
	scfg_np = of_find_matching_node_and_match(NULL, scfg_matches, NULL);
	if (!scfg_np) {
		freq = temp;
		pr_err("%s: Missing scfg node. Can not display video.\n",
		       __func__);
		return;
	}

	scfg = of_iomap(scfg_np, 0);
	of_node_put(scfg_np);
	if (!scfg) {
		pr_err("%s: Could not map device. Can not display video.\n",
		       __func__);
		return;
	}

	/* Convert pixclock from a wavelength to a frequency */
	temp = 1000000000000ULL;
	do_div(temp, pixclock);
	freq = temp;

	/*
	 * 'pxclk' is the ratio of the platform clock to the pixel clock.
	 * This number is programmed into the PIXCLKCR register, and the valid
	 * range of values is 2-255.
	 */
	pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq);
	pxclk = clamp_t(u32, pxclk, 2, 255);

	/* Disable the pixel clock, and set it to non-inverted and no delay */
	clrbits32(scfg + CCSR_SCFG_PIXCLKCR,
		  PIXCLKCR_PXCKEN | PIXCLKCR_PXCKDLY | PIXCLKCR_PXCLK_MASK);

	/* Enable the clock and set the pxclk */
	setbits32(scfg + CCSR_SCFG_PIXCLKCR, PIXCLKCR_PXCKEN | (pxclk << 16));

	iounmap(scfg);
}

/**
 * corenet_valid_monitor_port: set the monitor port for sysfs
 */
static enum fsl_diu_monitor_port
corenet_valid_monitor_port(enum fsl_diu_monitor_port port)
{
	switch (port) {
	case FSL_DIU_PORT_DVI:
	case FSL_DIU_PORT_LVDS:
		return port;
	default:
		return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */
	}
}

static void t1042rdb_diu_init(void)
{
	diu_ops.set_monitor_port	= t1042rdb_set_monitor_port;
	diu_ops.set_pixel_clock		= corenet_set_pixel_clock;
	diu_ops.valid_monitor_port	= corenet_valid_monitor_port;
}

static void t1024qds_diu_init(void)
{
	diu_ops.set_monitor_port        = t102xqds_set_monitor_port;
	diu_ops.set_pixel_clock         = corenet_set_pixel_clock;
	diu_ops.valid_monitor_port      = corenet_valid_monitor_port;
}

static int __init corenet_diu_init(void)
{
	struct device_node *np;

	np = of_find_compatible_node(NULL, NULL, "fsl,diu");
	if (!np)
		return 0;

	/* T1042RDB_PI board */
	if (of_find_compatible_node(NULL, NULL, "fsl,T1042RDB_PI"))
		t1042rdb_diu_init();

	/* T1024QDS board */
	if (of_find_compatible_node(NULL, NULL, "fsl,T1024QDS"))
		t1024qds_diu_init();

	return 0;
}
early_initcall(corenet_diu_init);