summaryrefslogtreecommitdiffstats
path: root/sys/arm/samsung/exynos/exynos5_fimd.c
blob: d7b7330fb7f3fa7f5865ee9a60fd8e01e73c12bc (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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*-
 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Samsung Exynos 5 Display Controller
 * Chapter 15, Exynos 5 Dual User's Manual Public Rev 1.00
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/timeet.h>
#include <sys/timetc.h>
#include <sys/watchdog.h>
#include <sys/fbio.h>
#include <sys/consio.h>
#include <sys/eventhandler.h>
#include <sys/gpio.h>

#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>

#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>

#include <dev/vt/vt.h>
#include <dev/vt/colors/vt_termcolors.h>

#include <arm/samsung/exynos/exynos5_common.h>

#include "gpio_if.h"

#include <machine/bus.h>
#include <machine/fdt.h>
#include <machine/cpu.h>
#include <machine/intr.h>

#include "fb_if.h"

#define	FIMDBYPASS_DISP1	(1 << 15)

#define	VIDCON0		(0x0)
#define	VIDCON0_ENVID	(1 << 1)
#define	VIDCON0_ENVID_F	(1 << 0)
#define	CLKVAL_F	0xb
#define	CLKVAL_F_OFFSET	6

#define	WINCON0		0x0020
#define	WINCON1		0x0024
#define	WINCON2		0x0028
#define	WINCON3		0x002C
#define	WINCON4		0x0030

#define	ENLOCAL_F			(1 << 22)
#define	BPPMODE_F_RGB_16BIT_565		0x5
#define	BPPMODE_F_OFFSET		2
#define	ENWIN_F_ENABLE			(1 << 0)
#define	HALF_WORD_SWAP_EN		(1 << 16)

#define	SHADOWCON	0x0034
#define	CHANNEL0_EN	(1 << 0)

#define	VIDOSD0A	0x0040
#define	VIDOSD0B	0x0044
#define	VIDOSD0C	0x0048

#define	VIDW00ADD0B0	0x00A0
#define	VIDW00ADD0B1	0x00A4
#define	VIDW00ADD0B2	0x20A0
#define	VIDW00ADD1B0	0x00D0
#define	VIDW00ADD1B1	0x00D4
#define	VIDW00ADD1B2	0x20D0

#define	VIDW00ADD2	0x0100
#define	VIDW01ADD2	0x0104
#define	VIDW02ADD2	0x0108
#define	VIDW03ADD2	0x010C
#define	VIDW04ADD2	0x0110

#define	VIDCON1		(0x04)
#define	VIDTCON0	0x0010
#define	VIDTCON1	0x0014
#define	VIDTCON2	0x0018
#define	VIDTCON3	0x001C

#define	VIDINTCON0	0x0130
#define	VIDINTCON1	0x0134

#define	VSYNC_PULSE_WIDTH_VAL		0x3
#define	VSYNC_PULSE_WIDTH_OFFSET	0
#define	V_FRONT_PORCH_VAL		0x3
#define	V_FRONT_PORCH_OFFSET		8
#define	V_BACK_PORCH_VAL		0x3
#define	V_BACK_PORCH_OFFSET		16

#define	HSYNC_PULSE_WIDTH_VAL		0x3
#define	HSYNC_PULSE_WIDTH_OFFSET	0
#define	H_FRONT_PORCH_VAL		0x3
#define	H_FRONT_PORCH_OFFSET		8
#define	H_BACK_PORCH_VAL		0x3
#define	H_BACK_PORCH_OFFSET		16

#define	HOZVAL_OFFSET		0
#define	LINEVAL_OFFSET		11

#define	OSD_RIGHTBOTX_F_OFFSET		11
#define	OSD_RIGHTBOTY_F_OFFSET		0

#define	DPCLKCON	0x27c
#define	DPCLKCON_EN	(1 << 1)

#define	DREAD4(_sc, _reg)         \
	bus_space_read_4(_sc->bst_disp, _sc->bsh_disp, _reg)
#define	DWRITE4(_sc, _reg, _val)  \
	bus_space_write_4(_sc->bst_disp, _sc->bsh_disp, _reg, _val)

struct panel_info {
	uint32_t	width;
	uint32_t	height;
	uint32_t	h_back_porch;
	uint32_t	h_pulse_width;
	uint32_t	h_front_porch;
	uint32_t	v_back_porch;
	uint32_t	v_pulse_width;
	uint32_t	v_front_porch;
	uint32_t	clk_div;
	uint32_t	backlight_pin;
	uint32_t	fixvclk;
	uint32_t	ivclk;
	uint32_t	clkval_f;
};

struct fimd_softc {
	struct resource		*res[3];
	bus_space_tag_t		bst;
	bus_space_handle_t	bsh;
	bus_space_tag_t		bst_disp;
	bus_space_handle_t	bsh_disp;
	bus_space_tag_t		bst_sysreg;
	bus_space_handle_t	bsh_sysreg;

	void			*ih;
	device_t		dev;
	device_t		sc_fbd;		/* fbd child */
	struct fb_info		sc_info;
	struct panel_info	*panel;
};

static struct resource_spec fimd_spec[] = {
	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* Timer registers */
	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },	/* FIMD */
	{ SYS_RES_MEMORY,	2,	RF_ACTIVE },	/* DISP */
	{ -1, 0 }
};

static int
fimd_probe(device_t dev)
{

	if (!ofw_bus_status_okay(dev))
		return (ENXIO);

	if (!ofw_bus_is_compatible(dev, "exynos,fimd"))
		return (ENXIO);

	device_set_desc(dev, "Samsung Exynos 5 Display Controller");
	return (BUS_PROBE_DEFAULT);
}

static int
get_panel_info(struct fimd_softc *sc, struct panel_info *panel)
{
	phandle_t node;
	pcell_t dts_value[3];
	int len;

	if ((node = ofw_bus_get_node(sc->dev)) == -1)
		return (ENXIO);

	/* panel size */
	if ((len = OF_getproplen(node, "panel-size")) <= 0)
		return (ENXIO);
	OF_getprop(node, "panel-size", &dts_value, len);
	panel->width = fdt32_to_cpu(dts_value[0]);
	panel->height = fdt32_to_cpu(dts_value[1]);

	/* hsync */
	if ((len = OF_getproplen(node, "panel-hsync")) <= 0)
		return (ENXIO);
	OF_getprop(node, "panel-hsync", &dts_value, len);
	panel->h_back_porch = fdt32_to_cpu(dts_value[0]);
	panel->h_pulse_width = fdt32_to_cpu(dts_value[1]);
	panel->h_front_porch = fdt32_to_cpu(dts_value[2]);

	/* vsync */
	if ((len = OF_getproplen(node, "panel-vsync")) <= 0)
		return (ENXIO);
	OF_getprop(node, "panel-vsync", &dts_value, len);
	panel->v_back_porch = fdt32_to_cpu(dts_value[0]);
	panel->v_pulse_width = fdt32_to_cpu(dts_value[1]);
	panel->v_front_porch = fdt32_to_cpu(dts_value[2]);

	/* clk divider */
	if ((len = OF_getproplen(node, "panel-clk-div")) <= 0)
		return (ENXIO);
	OF_getprop(node, "panel-clk-div", &dts_value, len);
	panel->clk_div = fdt32_to_cpu(dts_value[0]);

	/* backlight pin */
	if ((len = OF_getproplen(node, "panel-backlight-pin")) <= 0)
		return (ENXIO);
	OF_getprop(node, "panel-backlight-pin", &dts_value, len);
	panel->backlight_pin = fdt32_to_cpu(dts_value[0]);

	return (0);
}

static int
fimd_init(struct fimd_softc *sc)
{
	struct panel_info *panel;
	int reg;

	panel = sc->panel;

	/* fb_init */
	reg = panel->ivclk | panel->fixvclk;
	DWRITE4(sc,VIDCON1,reg);

	reg = (VIDCON0_ENVID | VIDCON0_ENVID_F);
	reg |= (panel->clkval_f << CLKVAL_F_OFFSET);
	WRITE4(sc,VIDCON0,reg);

	reg = (panel->v_pulse_width << VSYNC_PULSE_WIDTH_OFFSET);
	reg |= (panel->v_front_porch << V_FRONT_PORCH_OFFSET);
	reg |= (panel->v_back_porch << V_BACK_PORCH_OFFSET);
	DWRITE4(sc,VIDTCON0,reg);

	reg = (panel->h_pulse_width << HSYNC_PULSE_WIDTH_OFFSET);
	reg |= (panel->h_front_porch << H_FRONT_PORCH_OFFSET);
	reg |= (panel->h_back_porch << H_BACK_PORCH_OFFSET);
	DWRITE4(sc,VIDTCON1,reg);

	reg = ((panel->width - 1) << HOZVAL_OFFSET);
	reg |= ((panel->height - 1) << LINEVAL_OFFSET);
	DWRITE4(sc,VIDTCON2,reg);

	reg = sc->sc_info.fb_pbase;
	WRITE4(sc, VIDW00ADD0B0, reg);
	reg += (sc->sc_info.fb_stride * (sc->sc_info.fb_height + 1));
	WRITE4(sc, VIDW00ADD1B0, reg);
	WRITE4(sc, VIDW00ADD2, sc->sc_info.fb_stride);

	reg = ((panel->width - 1) << OSD_RIGHTBOTX_F_OFFSET);
	reg |= ((panel->height - 1) << OSD_RIGHTBOTY_F_OFFSET);
	WRITE4(sc,VIDOSD0B,reg);

	reg = panel->width * panel->height;
	WRITE4(sc,VIDOSD0C,reg);

	reg = READ4(sc, SHADOWCON);
	reg |= CHANNEL0_EN;
	reg &= ~(1 << 5); /* disable local path for channel0 */
	WRITE4(sc,SHADOWCON,reg);

	reg = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET;
	reg |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN; /* Note: swap=0 when ENLOCAL==1 */
	reg &= ~ENLOCAL_F; /* use DMA */
	WRITE4(sc,WINCON0,reg);

	/* Enable DisplayPort Clk */
	WRITE4(sc, DPCLKCON, DPCLKCON_EN);

	return (0);
}

static int
fimd_attach(device_t dev)
{
	struct panel_info panel;
	struct fimd_softc *sc;
	device_t gpio_dev;
	int reg;

	sc = device_get_softc(dev);
	sc->dev = dev;

	if (bus_alloc_resources(dev, fimd_spec, sc->res)) {
		device_printf(dev, "could not allocate resources\n");
		return (ENXIO);
	}

	/* Memory interface */
	sc->bst = rman_get_bustag(sc->res[0]);
	sc->bsh = rman_get_bushandle(sc->res[0]);
	sc->bst_disp = rman_get_bustag(sc->res[1]);
	sc->bsh_disp = rman_get_bushandle(sc->res[1]);
	sc->bst_sysreg = rman_get_bustag(sc->res[2]);
	sc->bsh_sysreg = rman_get_bushandle(sc->res[2]);

	if (get_panel_info(sc, &panel)) {
		device_printf(dev, "Can't get panel info\n");
		return (ENXIO);
	}

	panel.fixvclk = 0;
	panel.ivclk = 0;
	panel.clkval_f = 2;

	sc->panel = &panel;

	/* Get the GPIO device, we need this to give power to USB */
	gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
	if (gpio_dev == NULL) {
		/* TODO */
	}

	reg = bus_space_read_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214);
	reg |= FIMDBYPASS_DISP1;
	bus_space_write_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214, reg);

	sc->sc_info.fb_width = panel.width;
	sc->sc_info.fb_height = panel.height;
	sc->sc_info.fb_stride = sc->sc_info.fb_width * 2;
	sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 16;
	sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride;
	sc->sc_info.fb_vbase = (intptr_t)kmem_alloc_contig(kernel_arena,
	    sc->sc_info.fb_size, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
	sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase);

#if 0
	printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height,
	    sc->sc_info.fb_stride);
	printf("pbase == 0x%08x\n", sc->sc_info.fb_pbase);
#endif

	memset((int8_t *)sc->sc_info.fb_vbase, 0x0, sc->sc_info.fb_size);

	fimd_init(sc);

	sc->sc_info.fb_name = device_get_nameunit(dev);

	/* Ask newbus to attach framebuffer device to me. */
	sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev));
	if (sc->sc_fbd == NULL)
		device_printf(dev, "Can't attach fbd device\n");

	if (device_probe_and_attach(sc->sc_fbd) != 0) {
		device_printf(sc->dev, "Failed to attach fbd device\n");
	}

	return (0);
}

static struct fb_info *
fimd_fb_getinfo(device_t dev)
{
	struct fimd_softc *sc = device_get_softc(dev);

	return (&sc->sc_info);
}

static device_method_t fimd_methods[] = {
	DEVMETHOD(device_probe,		fimd_probe),
	DEVMETHOD(device_attach,	fimd_attach),

	/* Framebuffer service methods */
	DEVMETHOD(fb_getinfo,		fimd_fb_getinfo),
	{ 0, 0 }
};

static driver_t fimd_driver = {
	"fb",
	fimd_methods,
	sizeof(struct fimd_softc),
};

static devclass_t fimd_devclass;

DRIVER_MODULE(fb, simplebus, fimd_driver, fimd_devclass, 0, 0);
OpenPOWER on IntegriCloud