sunxi_lcdc.c revision 1.3 1 /* $NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/intr.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/conf.h>
39
40 #include <drm/drmP.h>
41 #include <drm/drm_crtc_helper.h>
42
43 #include <dev/fdt/fdtvar.h>
44 #include <dev/fdt/fdt_port.h>
45
46 #define TCON_GCTL_REG 0x000
47 #define TCON_GCTL_TCON_EN __BIT(31)
48 #define TCON_GCTL_GAMMA_EN __BIT(30)
49 #define TCON_GCTL_IO_MAP_SEL __BIT(0)
50 #define TCON_GINT0_REG 0x004
51 #define TCON_GINT1_REG 0x008
52 #define TCON_GINT1_TCON1_LINE_INT_NUM __BITS(11,0)
53
54 #define TCON0_CTL_REG 0x040
55 #define TCON0_CTL_TCON0_EN __BIT(31)
56 #define TCON0_CTL_START_DELAY __BITS(8,4)
57 #define TCON0_CTL_TCON0_SRC_SEL __BITS(2,0)
58 #define TCON0_DCLK_REG 0x044
59 #define TCON0_DCLK_EN __BITS(31,28)
60 #define TCON0_DCLK_DIV __BITS(6,0)
61 #define TCON0_BASIC0_REG 0x048
62 #define TCON0_BASIC1_REG 0x04c
63 #define TCON0_BASIC2_REG 0x050
64 #define TCON0_BASIC3_REG 0x054
65 #define TCON0_IO_POL_REG 0x088
66 #define TCON0_IO_POL_IO_OUTPUT_SEL __BIT(31)
67 #define TCON0_IO_POL_DCLK_SEL __BITS(30,28)
68 #define TCON0_IO_POL_IO3_INV __BIT(27)
69 #define TCON0_IO_POL_IO2_INV __BIT(26)
70 #define TCON0_IO_POL_IO1_INV __BIT(25)
71 #define TCON0_IO_POL_IO0_INV __BIT(24)
72 #define TCON0_IO_POL_DATA_INV __BITS(23,0)
73 #define TCON0_IO_TRI_REG 0x08c
74
75 #define TCON1_CTL_REG 0x090
76 #define TCON1_CTL_TCON1_EN __BIT(31)
77 #define TCON1_CTL_START_DELAY __BITS(8,4)
78 #define TCON1_CTL_TCON1_SRC_SEL __BIT(1)
79 #define TCON1_BASIC0_REG 0x094
80 #define TCON1_BASIC1_REG 0x098
81 #define TCON1_BASIC2_REG 0x09c
82 #define TCON1_BASIC3_REG 0x0a0
83 #define TCON1_BASIC4_REG 0x0a4
84 #define TCON1_BASIC5_REG 0x0a8
85 #define TCON1_IO_POL_REG 0x0f0
86 #define TCON1_IO_POL_IO3_INV __BIT(27)
87 #define TCON1_IO_POL_IO2_INV __BIT(26)
88 #define TCON1_IO_POL_IO1_INV __BIT(25)
89 #define TCON1_IO_POL_IO0_INV __BIT(24)
90 #define TCON1_IO_POL_DATA_INV __BITS(23,0)
91 #define TCON1_IO_TRI_REG 0x0f4
92
93 enum {
94 TCON_PORT_INPUT = 0,
95 TCON_PORT_OUTPUT = 1,
96 };
97
98 enum tcon_type {
99 TYPE_TCON0,
100 TYPE_TCON1,
101 };
102
103 static const struct of_compat_data compat_data[] = {
104 { "allwinner,sun8i-h3-tcon-tv", TYPE_TCON1 },
105 { "allwinner,sun50i-a64-tcon-lcd", TYPE_TCON0 },
106 { "allwinner,sun50i-a64-tcon-tv", TYPE_TCON1 },
107 { NULL }
108 };
109
110 struct sunxi_lcdc_softc;
111
112 struct sunxi_lcdc_encoder {
113 struct drm_encoder base;
114 struct sunxi_lcdc_softc *sc;
115 struct drm_display_mode curmode;
116 };
117
118 struct sunxi_lcdc_softc {
119 device_t sc_dev;
120 bus_space_tag_t sc_bst;
121 bus_space_handle_t sc_bsh;
122 int sc_phandle;
123
124 enum tcon_type sc_type;
125
126 struct clk *sc_clk_ch[2];
127
128 struct sunxi_lcdc_encoder sc_encoder;
129 struct drm_connector sc_connector;
130
131 struct fdt_device_ports sc_ports;
132 };
133
134 #define to_sunxi_lcdc_encoder(x) container_of(x, struct sunxi_lcdc_encoder, base)
135
136 #define TCON_READ(sc, reg) \
137 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
138 #define TCON_WRITE(sc, reg, val) \
139 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
140
141 static void
142 sunxi_lcdc_destroy(struct drm_encoder *encoder)
143 {
144 }
145
146 static const struct drm_encoder_funcs sunxi_lcdc_funcs = {
147 .destroy = sunxi_lcdc_destroy,
148 };
149
150 static void
151 sunxi_lcdc_tcon_dpms(struct drm_encoder *encoder, int mode)
152 {
153 }
154
155 static bool
156 sunxi_lcdc_tcon_mode_fixup(struct drm_encoder *encoder,
157 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
158 {
159 return true;
160 }
161
162 static void
163 sunxi_lcdc_tcon_mode_set(struct drm_encoder *encoder,
164 struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
165 {
166 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
167
168 lcdc_encoder->curmode = *adjusted_mode;
169 }
170
171 static void
172 sunxi_lcdc_tcon0_prepare(struct drm_encoder *encoder)
173 {
174 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
175 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
176 uint32_t val;
177
178 val = TCON_READ(sc, TCON_GCTL_REG);
179 val |= TCON_GCTL_TCON_EN;
180 TCON_WRITE(sc, TCON_GCTL_REG, val);
181
182 TCON_WRITE(sc, TCON0_IO_TRI_REG, 0);
183 }
184
185 static void
186 sunxi_lcdc_tcon1_prepare(struct drm_encoder *encoder)
187 {
188 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
189 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
190 uint32_t val;
191
192 val = TCON_READ(sc, TCON_GCTL_REG);
193 val |= TCON_GCTL_TCON_EN;
194 TCON_WRITE(sc, TCON_GCTL_REG, val);
195
196 TCON_WRITE(sc, TCON1_IO_POL_REG, 0);
197 TCON_WRITE(sc, TCON1_IO_TRI_REG, 0xffffffff);
198 }
199
200 static void
201 sunxi_lcdc_tcon0_commit(struct drm_encoder *encoder)
202 {
203 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
204 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
205 struct drm_display_mode *mode = &lcdc_encoder->curmode;
206 uint32_t val;
207 int error;
208
209 const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0;
210 const u_int hspw = mode->hsync_end - mode->hsync_start;
211 const u_int hbp = mode->htotal - mode->hsync_start;
212 const u_int vspw = mode->vsync_end - mode->vsync_start;
213 const u_int vbp = mode->vtotal - mode->vsync_start;
214 const u_int vblank_len =
215 ((mode->vtotal << interlace_p) >> 1) - mode->vdisplay - 2;
216 const u_int start_delay =
217 vblank_len >= 32 ? 30 : vblank_len - 2;
218
219 val = TCON0_CTL_TCON0_EN |
220 __SHIFTIN(start_delay, TCON0_CTL_START_DELAY);
221 TCON_WRITE(sc, TCON0_CTL_REG, val);
222
223 TCON_WRITE(sc, TCON0_BASIC0_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
224 TCON_WRITE(sc, TCON0_BASIC1_REG, ((mode->htotal - 1) << 16) | (hbp - 1));
225 TCON_WRITE(sc, TCON0_BASIC2_REG, ((mode->vtotal * 2) << 16) | (vbp - 1));
226 TCON_WRITE(sc, TCON0_BASIC3_REG, ((hspw - 1) << 16) | (vspw - 1));
227
228 val = TCON_READ(sc, TCON0_IO_POL_REG);
229 val &= ~(TCON0_IO_POL_IO3_INV|TCON0_IO_POL_IO2_INV|
230 TCON0_IO_POL_IO1_INV|TCON0_IO_POL_IO0_INV|
231 TCON0_IO_POL_DATA_INV);
232 if ((mode->flags & DRM_MODE_FLAG_PHSYNC) == 0)
233 val |= TCON0_IO_POL_IO1_INV;
234 if ((mode->flags & DRM_MODE_FLAG_PVSYNC) == 0)
235 val |= TCON0_IO_POL_IO0_INV;
236 TCON_WRITE(sc, TCON0_IO_POL_REG, val);
237
238 if (sc->sc_clk_ch[0] != NULL) {
239 error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000);
240 if (error != 0) {
241 device_printf(sc->sc_dev, "failed to set CH0 PLL rate to %u Hz: %d\n",
242 mode->crtc_clock * 1000, error);
243 return;
244 }
245 error = clk_enable(sc->sc_clk_ch[1]);
246 if (error != 0) {
247 device_printf(sc->sc_dev, "failed to enable CH0 PLL: %d\n", error);
248 return;
249 }
250 } else {
251 device_printf(sc->sc_dev, "no CH0 PLL configured\n");
252 }
253 }
254
255 static void
256 sunxi_lcdc_tcon1_commit(struct drm_encoder *encoder)
257 {
258 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
259 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
260 struct drm_display_mode *mode = &lcdc_encoder->curmode;
261 uint32_t val;
262 int error;
263
264 const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0;
265 const u_int hspw = mode->hsync_end - mode->hsync_start;
266 const u_int hbp = mode->htotal - mode->hsync_start;
267 const u_int vspw = mode->vsync_end - mode->vsync_start;
268 const u_int vbp = mode->vtotal - mode->vsync_start;
269 const u_int vblank_len =
270 ((mode->vtotal << interlace_p) >> 1) - mode->vdisplay - 2;
271 const u_int start_delay =
272 vblank_len >= 32 ? 30 : vblank_len - 2;
273
274 val = TCON1_CTL_TCON1_EN |
275 __SHIFTIN(start_delay, TCON1_CTL_START_DELAY);
276 TCON_WRITE(sc, TCON1_CTL_REG, val);
277
278 TCON_WRITE(sc, TCON1_BASIC0_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
279 TCON_WRITE(sc, TCON1_BASIC1_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
280 TCON_WRITE(sc, TCON1_BASIC2_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
281 TCON_WRITE(sc, TCON1_BASIC3_REG, ((mode->htotal - 1) << 16) | (hbp - 1));
282 TCON_WRITE(sc, TCON1_BASIC4_REG, ((mode->vtotal * 2) << 16) | (vbp - 1));
283 TCON_WRITE(sc, TCON1_BASIC5_REG, ((hspw - 1) << 16) | (vspw - 1));
284
285 TCON_WRITE(sc, TCON_GINT1_REG,
286 __SHIFTIN(start_delay + 2, TCON_GINT1_TCON1_LINE_INT_NUM));
287
288 if (sc->sc_clk_ch[1] != NULL) {
289 error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000);
290 if (error != 0) {
291 device_printf(sc->sc_dev, "failed to set CH1 PLL rate to %u Hz: %d\n",
292 mode->crtc_clock * 1000, error);
293 return;
294 }
295 error = clk_enable(sc->sc_clk_ch[1]);
296 if (error != 0) {
297 device_printf(sc->sc_dev, "failed to enable CH1 PLL: %d\n", error);
298 return;
299 }
300 } else {
301 device_printf(sc->sc_dev, "no CH1 PLL configured\n");
302 }
303 }
304
305 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon0_helper_funcs = {
306 .dpms = sunxi_lcdc_tcon_dpms,
307 .mode_fixup = sunxi_lcdc_tcon_mode_fixup,
308 .prepare = sunxi_lcdc_tcon0_prepare,
309 .commit = sunxi_lcdc_tcon0_commit,
310 .mode_set = sunxi_lcdc_tcon_mode_set,
311 };
312
313 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon1_helper_funcs = {
314 .dpms = sunxi_lcdc_tcon_dpms,
315 .mode_fixup = sunxi_lcdc_tcon_mode_fixup,
316 .prepare = sunxi_lcdc_tcon1_prepare,
317 .commit = sunxi_lcdc_tcon1_commit,
318 .mode_set = sunxi_lcdc_tcon_mode_set,
319 };
320
321 static int
322 sunxi_lcdc_encoder_mode(struct fdt_endpoint *out_ep)
323 {
324 struct fdt_endpoint *remote_ep = fdt_endpoint_remote(out_ep);
325
326 if (remote_ep == NULL)
327 return DRM_MODE_ENCODER_NONE;
328
329 switch (fdt_endpoint_type(remote_ep)) {
330 case EP_DRM_BRIDGE:
331 return DRM_MODE_ENCODER_TMDS;
332 case EP_DRM_PANEL:
333 return DRM_MODE_ENCODER_LVDS;
334 default:
335 return DRM_MODE_ENCODER_NONE;
336 }
337 }
338
339 static int
340 sunxi_lcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
341 {
342 struct sunxi_lcdc_softc * const sc = device_private(dev);
343 struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep);
344 struct fdt_endpoint *out_ep;
345 struct drm_crtc *crtc;
346
347 if (!activate)
348 return EINVAL;
349
350 if (fdt_endpoint_port_index(ep) != TCON_PORT_INPUT)
351 return EINVAL;
352
353 if (fdt_endpoint_type(in_ep) != EP_DRM_CRTC)
354 return EINVAL;
355
356 crtc = fdt_endpoint_get_data(in_ep);
357
358 sc->sc_encoder.sc = sc;
359 sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(crtc);
360
361 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 0);
362 if (out_ep != NULL) {
363 drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs,
364 sunxi_lcdc_encoder_mode(out_ep));
365 drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon0_helper_funcs);
366
367 return fdt_endpoint_activate(out_ep, activate);
368 }
369
370 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 1);
371 if (out_ep != NULL) {
372 drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs,
373 sunxi_lcdc_encoder_mode(out_ep));
374 drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon1_helper_funcs);
375
376 return fdt_endpoint_activate(out_ep, activate);
377 }
378
379 return ENXIO;
380 }
381
382 static void *
383 sunxi_lcdc_ep_get_data(device_t dev, struct fdt_endpoint *ep)
384 {
385 struct sunxi_lcdc_softc * const sc = device_private(dev);
386
387 return &sc->sc_encoder;
388 }
389
390 static int
391 sunxi_lcdc_match(device_t parent, cfdata_t cf, void *aux)
392 {
393 struct fdt_attach_args * const faa = aux;
394
395 return of_match_compat_data(faa->faa_phandle, compat_data);
396 }
397
398 static void
399 sunxi_lcdc_attach(device_t parent, device_t self, void *aux)
400 {
401 struct sunxi_lcdc_softc * const sc = device_private(self);
402 struct fdt_attach_args * const faa = aux;
403 const int phandle = faa->faa_phandle;
404 struct fdtbus_reset *rst;
405 struct clk *clk;
406 bus_addr_t addr;
407 bus_size_t size;
408
409 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
410 aprint_error(": couldn't get registers\n");
411 return;
412 }
413
414 rst = fdtbus_reset_get(phandle, "lcd");
415 if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
416 aprint_error(": couldn't de-assert reset\n");
417 return;
418 }
419
420 clk = fdtbus_clock_get(phandle, "ahb");
421 if (clk == NULL || clk_enable(clk) != 0) {
422 aprint_error(": couldn't enable bus clock\n");
423 return;
424 }
425
426 sc->sc_dev = self;
427 sc->sc_bst = faa->faa_bst;
428 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
429 aprint_error(": couldn't map registers\n");
430 return;
431 }
432 sc->sc_phandle = faa->faa_phandle;
433 sc->sc_type = of_search_compatible(phandle, compat_data)->data;
434 sc->sc_clk_ch[0] = fdtbus_clock_get(phandle, "tcon-ch0");
435 sc->sc_clk_ch[1] = fdtbus_clock_get(phandle, "tcon-ch1");
436
437 aprint_naive("\n");
438 switch (sc->sc_type) {
439 case TYPE_TCON0:
440 aprint_normal(": TCON0\n");
441 break;
442 case TYPE_TCON1:
443 aprint_normal(": TCON1\n");
444 break;
445 }
446
447 sc->sc_ports.dp_ep_activate = sunxi_lcdc_ep_activate;
448 sc->sc_ports.dp_ep_get_data = sunxi_lcdc_ep_get_data;
449 fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER);
450 }
451
452 CFATTACH_DECL_NEW(sunxi_lcdc, sizeof(struct sunxi_lcdc_softc),
453 sunxi_lcdc_match, sunxi_lcdc_attach, NULL, NULL);
454