xref: /f-stack/freebsd/mips/ingenic/jz4780_lcd.c (revision 22ce4aff)
1 /*-
2  * Copyright (c) 2016 Jared McNeill <[email protected]>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 /*
29  * Ingenic JZ4780 LCD Controller
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/eventhandler.h>
39 #include <sys/rman.h>
40 #include <sys/condvar.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/fbio.h>
44 #include <vm/vm.h>
45 #include <vm/vm_extern.h>
46 #include <vm/vm_kern.h>
47 #include <vm/pmap.h>
48 
49 #include <machine/bus.h>
50 
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
53 
54 #include <dev/videomode/videomode.h>
55 #include <dev/videomode/edidvar.h>
56 
57 #include <dev/extres/clk/clk.h>
58 
59 #include <mips/ingenic/jz4780_lcd.h>
60 
61 #include "fb_if.h"
62 #include "hdmi_if.h"
63 
64 #define	FB_DEFAULT_W	800
65 #define	FB_DEFAULT_H	600
66 #define	FB_DEFAULT_REF	60
67 #define	FB_BPP		32
68 #define	FB_ALIGN	(16 * 4)
69 #define	FB_MAX_BW	(1920 * 1080 * 60)
70 #define	FB_MAX_W	2048
71 #define	FB_MAX_H	2048
72 #define FB_DIVIDE(x, y)	(((x) + ((y) / 2)) / (y))
73 
74 #define	PCFG_MAGIC	0xc7ff2100
75 
76 #define	DOT_CLOCK_TO_HZ(c)	((c) * 1000)
77 
78 #ifndef VM_MEMATTR_WRITE_COMBINING
79 #define	VM_MEMATTR_WRITE_COMBINING VM_MEMATTR_UNCACHEABLE
80 #endif
81 
82 struct jzlcd_softc {
83 	device_t		dev;
84 	device_t		fbdev;
85 	struct resource		*res[1];
86 
87 	/* Clocks */
88 	clk_t			clk;
89 	clk_t			clk_pix;
90 
91 	/* Framebuffer */
92 	struct fb_info		info;
93 	size_t			fbsize;
94 	bus_addr_t		paddr;
95 	vm_offset_t		vaddr;
96 
97 	/* HDMI */
98 	eventhandler_tag	hdmi_evh;
99 
100 	/* Frame descriptor DMA */
101 	bus_dma_tag_t		fdesc_tag;
102 	bus_dmamap_t		fdesc_map;
103 	bus_addr_t		fdesc_paddr;
104 	struct lcd_frame_descriptor	*fdesc;
105 };
106 
107 static struct resource_spec jzlcd_spec[] = {
108 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
109 	{ -1, 0 }
110 };
111 
112 #define	LCD_READ(sc, reg)		bus_read_4((sc)->res[0], (reg))
113 #define	LCD_WRITE(sc, reg, val)		bus_write_4((sc)->res[0], (reg), (val))
114 
115 static int
jzlcd_allocfb(struct jzlcd_softc * sc)116 jzlcd_allocfb(struct jzlcd_softc *sc)
117 {
118 	sc->vaddr = kmem_alloc_contig(sc->fbsize, M_NOWAIT | M_ZERO, 0, ~0,
119 	    FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING);
120 	if (sc->vaddr == 0) {
121 		device_printf(sc->dev, "failed to allocate FB memory\n");
122 		return (ENOMEM);
123 	}
124 	sc->paddr = pmap_kextract(sc->vaddr);
125 
126 	return (0);
127 }
128 
129 static void
jzlcd_freefb(struct jzlcd_softc * sc)130 jzlcd_freefb(struct jzlcd_softc *sc)
131 {
132 	kmem_free(sc->vaddr, sc->fbsize);
133 }
134 
135 static void
jzlcd_start(struct jzlcd_softc * sc)136 jzlcd_start(struct jzlcd_softc *sc)
137 {
138 	uint32_t ctrl;
139 
140 	/* Clear status registers */
141 	LCD_WRITE(sc, LCDSTATE, 0);
142 	LCD_WRITE(sc, LCDOSDS, 0);
143 	/* Enable the controller */
144 	ctrl = LCD_READ(sc, LCDCTRL);
145 	ctrl |= LCDCTRL_ENA;
146 	ctrl &= ~LCDCTRL_DIS;
147 	LCD_WRITE(sc, LCDCTRL, ctrl);
148 }
149 
150 static void
jzlcd_stop(struct jzlcd_softc * sc)151 jzlcd_stop(struct jzlcd_softc *sc)
152 {
153 	uint32_t ctrl;
154 
155 	ctrl = LCD_READ(sc, LCDCTRL);
156 	if ((ctrl & LCDCTRL_ENA) != 0) {
157 		/* Disable the controller and wait for it to stop */
158 		ctrl |= LCDCTRL_DIS;
159 		LCD_WRITE(sc, LCDCTRL, ctrl);
160 		while ((LCD_READ(sc, LCDSTATE) & LCDSTATE_LDD) == 0)
161 			DELAY(100);
162 	}
163 	/* Clear all status except for disable */
164 	LCD_WRITE(sc, LCDSTATE, LCD_READ(sc, LCDSTATE) & ~LCDSTATE_LDD);
165 }
166 
167 static void
jzlcd_setup_descriptor(struct jzlcd_softc * sc,const struct videomode * mode,u_int desno)168 jzlcd_setup_descriptor(struct jzlcd_softc *sc, const struct videomode *mode,
169     u_int desno)
170 {
171 	struct lcd_frame_descriptor *fdesc;
172 	int line_sz;
173 
174 	/* Frame size is specified in # words */
175 	line_sz = (mode->hdisplay * FB_BPP) >> 3;
176 	line_sz = ((line_sz + 3) & ~3) / 4;
177 
178 	fdesc = sc->fdesc + desno;
179 
180 	if (desno == 0)
181 		fdesc->next = sc->fdesc_paddr +
182 		    sizeof(struct lcd_frame_descriptor);
183 	else
184 		fdesc->next = sc->fdesc_paddr;
185 	fdesc->physaddr = sc->paddr;
186 	fdesc->id = desno;
187 	fdesc->cmd = LCDCMD_FRM_EN | (line_sz * mode->vdisplay);
188 	fdesc->offs = 0;
189 	fdesc->pw = 0;
190 	fdesc->cnum_pos = LCDPOS_BPP01_18_24 |
191 	    LCDPOS_PREMULTI01 |
192 	    (desno == 0 ? LCDPOS_COEF_BLE01_1 : LCDPOS_COEF_SLE01);
193 	fdesc->dessize = LCDDESSIZE_ALPHA |
194 	    ((mode->vdisplay - 1) << LCDDESSIZE_HEIGHT_SHIFT) |
195 	    ((mode->hdisplay - 1) << LCDDESSIZE_WIDTH_SHIFT);
196 }
197 
198 static int
jzlcd_set_videomode(struct jzlcd_softc * sc,const struct videomode * mode)199 jzlcd_set_videomode(struct jzlcd_softc *sc, const struct videomode *mode)
200 {
201 	u_int hbp, hfp, hsw, vbp, vfp, vsw;
202 	u_int hds, hde, ht, vds, vde, vt;
203 	uint32_t ctrl;
204 	int error;
205 
206 	hbp = mode->htotal - mode->hsync_end;
207 	hfp = mode->hsync_start - mode->hdisplay;
208 	hsw = mode->hsync_end - mode->hsync_start;
209 	vbp = mode->vtotal - mode->vsync_end;
210 	vfp = mode->vsync_start - mode->vdisplay;
211 	vsw = mode->vsync_end - mode->vsync_start;
212 
213 	hds = hsw + hbp;
214 	hde = hds + mode->hdisplay;
215 	ht = hde + hfp;
216 
217 	vds = vsw + vbp;
218 	vde = vds + mode->vdisplay;
219 	vt = vde + vfp;
220 
221 	/* Setup timings */
222 	LCD_WRITE(sc, LCDVAT,
223 	    (ht << LCDVAT_HT_SHIFT) | (vt << LCDVAT_VT_SHIFT));
224 	LCD_WRITE(sc, LCDDAH,
225 	    (hds << LCDDAH_HDS_SHIFT) | (hde << LCDDAH_HDE_SHIFT));
226 	LCD_WRITE(sc, LCDDAV,
227 	    (vds << LCDDAV_VDS_SHIFT) | (vde << LCDDAV_VDE_SHIFT));
228 	LCD_WRITE(sc, LCDHSYNC, hsw);
229 	LCD_WRITE(sc, LCDVSYNC, vsw);
230 
231 	/* Set configuration */
232 	LCD_WRITE(sc, LCDCFG, LCDCFG_NEWDES | LCDCFG_RECOVER | LCDCFG_24 |
233 	    LCDCFG_PSM | LCDCFG_CLSM | LCDCFG_SPLM | LCDCFG_REVM | LCDCFG_PCP);
234 	ctrl = LCD_READ(sc, LCDCTRL);
235 	ctrl &= ~LCDCTRL_BST;
236 	ctrl |= LCDCTRL_BST_64 | LCDCTRL_OFUM;
237 	LCD_WRITE(sc, LCDCTRL, ctrl);
238 	LCD_WRITE(sc, LCDPCFG, PCFG_MAGIC);
239 	LCD_WRITE(sc, LCDRGBC, LCDRGBC_RGBFMT);
240 
241 	/* Update registers */
242 	LCD_WRITE(sc, LCDSTATE, 0);
243 
244 	/* Setup frame descriptors */
245 	jzlcd_setup_descriptor(sc, mode, 0);
246 	jzlcd_setup_descriptor(sc, mode, 1);
247 	bus_dmamap_sync(sc->fdesc_tag, sc->fdesc_map, BUS_DMASYNC_PREWRITE);
248 
249 	/* Setup DMA channels */
250 	LCD_WRITE(sc, LCDDA0, sc->fdesc_paddr
251 	    + sizeof(struct lcd_frame_descriptor));
252 	LCD_WRITE(sc, LCDDA1, sc->fdesc_paddr);
253 
254 	/* Set display clock */
255 	error = clk_set_freq(sc->clk_pix, DOT_CLOCK_TO_HZ(mode->dot_clock), 0);
256 	if (error != 0) {
257 		device_printf(sc->dev, "failed to set pixel clock to %u Hz\n",
258 		    DOT_CLOCK_TO_HZ(mode->dot_clock));
259 		return (error);
260 	}
261 
262 	return (0);
263 }
264 
265 static int
jzlcd_configure(struct jzlcd_softc * sc,const struct videomode * mode)266 jzlcd_configure(struct jzlcd_softc *sc, const struct videomode *mode)
267 {
268 	size_t fbsize;
269 	int error;
270 
271 	fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY));
272 
273 	/* Detach the old FB device */
274 	if (sc->fbdev != NULL) {
275 		device_delete_child(sc->dev, sc->fbdev);
276 		sc->fbdev = NULL;
277 	}
278 
279 	/* If the FB size has changed, free the old FB memory */
280 	if (sc->fbsize > 0 && sc->fbsize != fbsize) {
281 		jzlcd_freefb(sc);
282 		sc->vaddr = 0;
283 	}
284 
285 	/* Allocate the FB if necessary */
286 	sc->fbsize = fbsize;
287 	if (sc->vaddr == 0) {
288 		error = jzlcd_allocfb(sc);
289 		if (error != 0) {
290 			device_printf(sc->dev, "failed to allocate FB memory\n");
291 			return (ENXIO);
292 		}
293 	}
294 
295 	/* Setup video mode */
296 	error = jzlcd_set_videomode(sc, mode);
297 	if (error != 0)
298 		return (error);
299 
300 	/* Attach framebuffer device */
301 	sc->info.fb_name = device_get_nameunit(sc->dev);
302 	sc->info.fb_vbase = (intptr_t)sc->vaddr;
303 	sc->info.fb_pbase = sc->paddr;
304 	sc->info.fb_size = sc->fbsize;
305 	sc->info.fb_bpp = sc->info.fb_depth = FB_BPP;
306 	sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY);
307 	sc->info.fb_width = mode->hdisplay;
308 	sc->info.fb_height = mode->vdisplay;
309 #ifdef VM_MEMATTR_WRITE_COMBINING
310 	sc->info.fb_flags = FB_FLAG_MEMATTR;
311 	sc->info.fb_memattr = VM_MEMATTR_WRITE_COMBINING;
312 #endif
313 	sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
314 	if (sc->fbdev == NULL) {
315 		device_printf(sc->dev, "failed to add fbd child\n");
316 		return (ENOENT);
317 	}
318 
319 	error = device_probe_and_attach(sc->fbdev);
320 	if (error != 0) {
321 		device_printf(sc->dev, "failed to attach fbd device\n");
322 		return (error);
323 	}
324 
325 	return (0);
326 }
327 
328 static int
jzlcd_get_bandwidth(const struct videomode * mode)329 jzlcd_get_bandwidth(const struct videomode *mode)
330 {
331 	int refresh;
332 
333 	refresh = FB_DIVIDE(FB_DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock),
334 	    mode->htotal), mode->vtotal);
335 
336 	return mode->hdisplay * mode->vdisplay * refresh;
337 }
338 
339 static int
jzlcd_mode_supported(const struct videomode * mode)340 jzlcd_mode_supported(const struct videomode *mode)
341 {
342 	/* Width and height must be less than 2048 */
343 	if (mode->hdisplay > FB_MAX_W || mode->vdisplay > FB_MAX_H)
344 		return (0);
345 
346 	/* Bandwidth check */
347 	if (jzlcd_get_bandwidth(mode) > FB_MAX_BW)
348 		return (0);
349 
350 	/* Interlace modes not yet supported by the driver */
351 	if ((mode->flags & VID_INTERLACE) != 0)
352 		return (0);
353 
354 	return (1);
355 }
356 
357 static const struct videomode *
jzlcd_find_mode(struct edid_info * ei)358 jzlcd_find_mode(struct edid_info *ei)
359 {
360 	const struct videomode *best;
361 	int n, bw, best_bw;
362 
363 	/* If the preferred mode is OK, just use it */
364 	if (jzlcd_mode_supported(ei->edid_preferred_mode) != 0)
365 		return ei->edid_preferred_mode;
366 
367 	/* Pick the mode with the highest bandwidth requirements */
368 	best = NULL;
369 	best_bw = 0;
370 	for (n = 0; n < ei->edid_nmodes; n++) {
371 		if (jzlcd_mode_supported(&ei->edid_modes[n]) == 0)
372 			continue;
373 		bw = jzlcd_get_bandwidth(&ei->edid_modes[n]);
374 		if (bw > FB_MAX_BW)
375 			continue;
376 		if (best == NULL || bw > best_bw) {
377 			best = &ei->edid_modes[n];
378 			best_bw = bw;
379 		}
380 	}
381 
382 	return best;
383 }
384 
385 static void
jzlcd_hdmi_event(void * arg,device_t hdmi_dev)386 jzlcd_hdmi_event(void *arg, device_t hdmi_dev)
387 {
388 	const struct videomode *mode;
389 	struct videomode hdmi_mode;
390 	struct jzlcd_softc *sc;
391 	struct edid_info ei;
392 	uint8_t *edid;
393 	uint32_t edid_len;
394 	int error;
395 
396 	sc = arg;
397 	edid = NULL;
398 	edid_len = 0;
399 	mode = NULL;
400 
401 	error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len);
402 	if (error != 0) {
403 		device_printf(sc->dev, "failed to get EDID: %d\n", error);
404 	} else {
405 		error = edid_parse(edid, &ei);
406 		if (error != 0) {
407 			device_printf(sc->dev, "failed to parse EDID: %d\n",
408 			    error);
409 		} else {
410 			if (bootverbose)
411 				edid_print(&ei);
412 
413 			mode = jzlcd_find_mode(&ei);
414 		}
415 	}
416 
417 	/* If a suitable mode could not be found, try the default */
418 	if (mode == NULL)
419 		mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H,
420 		    FB_DEFAULT_REF);
421 
422 	if (mode == NULL) {
423 		device_printf(sc->dev, "failed to find usable video mode\n");
424 		return;
425 	}
426 
427 	if (bootverbose)
428 		device_printf(sc->dev, "using %dx%d\n",
429 		    mode->hdisplay, mode->vdisplay);
430 
431 	/* Stop the controller */
432 	jzlcd_stop(sc);
433 
434 	/* Configure LCD controller */
435 	error = jzlcd_configure(sc, mode);
436 	if (error != 0) {
437 		device_printf(sc->dev, "failed to configure FB: %d\n", error);
438 		return;
439 	}
440 
441 	/* Enable HDMI TX */
442 	hdmi_mode = *mode;
443 	HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
444 
445 	/* Start the controller! */
446 	jzlcd_start(sc);
447 }
448 
449 static void
jzlcd_dmamap_cb(void * arg,bus_dma_segment_t * segs,int nseg,int error)450 jzlcd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
451 {
452 	if (error != 0)
453 		return;
454 	*(bus_addr_t *)arg = segs[0].ds_addr;
455 }
456 
457 static int
jzlcd_probe(device_t dev)458 jzlcd_probe(device_t dev)
459 {
460 	if (!ofw_bus_status_okay(dev))
461 		return (ENXIO);
462 
463 	if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-lcd"))
464 		return (ENXIO);
465 
466 	device_set_desc(dev, "Ingenic JZ4780 LCD Controller");
467 	return (BUS_PROBE_DEFAULT);
468 }
469 
470 static int
jzlcd_attach(device_t dev)471 jzlcd_attach(device_t dev)
472 {
473 	struct jzlcd_softc *sc;
474 	int error;
475 
476 	sc = device_get_softc(dev);
477 
478 	sc->dev = dev;
479 
480 	if (bus_alloc_resources(dev, jzlcd_spec, sc->res)) {
481 		device_printf(dev, "cannot allocate resources for device\n");
482 		goto failed;
483 	}
484 
485 	if (clk_get_by_ofw_name(dev, 0, "lcd_clk", &sc->clk) != 0 ||
486 	    clk_get_by_ofw_name(dev, 0, "lcd_pixclk", &sc->clk_pix) != 0) {
487 		device_printf(dev, "cannot get clocks\n");
488 		goto failed;
489 	}
490 	if (clk_enable(sc->clk) != 0 || clk_enable(sc->clk_pix) != 0) {
491 		device_printf(dev, "cannot enable clocks\n");
492 		goto failed;
493 	}
494 
495 	error = bus_dma_tag_create(
496 	    bus_get_dma_tag(dev),
497 	    sizeof(struct lcd_frame_descriptor), 0,
498 	    BUS_SPACE_MAXADDR_32BIT,
499 	    BUS_SPACE_MAXADDR,
500 	    NULL, NULL,
501 	    sizeof(struct lcd_frame_descriptor) * 2, 1,
502 	    sizeof(struct lcd_frame_descriptor) * 2,
503 	    0,
504 	    NULL, NULL,
505 	    &sc->fdesc_tag);
506 	if (error != 0) {
507 		device_printf(dev, "cannot create bus dma tag\n");
508 		goto failed;
509 	}
510 
511 	error = bus_dmamem_alloc(sc->fdesc_tag, (void **)&sc->fdesc,
512 	    BUS_DMA_NOCACHE | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->fdesc_map);
513 	if (error != 0) {
514 		device_printf(dev, "cannot allocate dma descriptor\n");
515 		goto dmaalloc_failed;
516 	}
517 
518 	error = bus_dmamap_load(sc->fdesc_tag, sc->fdesc_map, sc->fdesc,
519 	    sizeof(struct lcd_frame_descriptor) * 2, jzlcd_dmamap_cb,
520 	    &sc->fdesc_paddr, 0);
521 	if (error != 0) {
522 		device_printf(dev, "cannot load dma map\n");
523 		goto dmaload_failed;
524 	}
525 
526 	sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
527 	    jzlcd_hdmi_event, sc, 0);
528 
529 	return (0);
530 
531 dmaload_failed:
532 	bus_dmamem_free(sc->fdesc_tag, sc->fdesc, sc->fdesc_map);
533 dmaalloc_failed:
534 	bus_dma_tag_destroy(sc->fdesc_tag);
535 failed:
536 	if (sc->clk_pix != NULL)
537 		clk_release(sc->clk);
538 	if (sc->clk != NULL)
539 		clk_release(sc->clk);
540 	if (sc->res != NULL)
541 		bus_release_resources(dev, jzlcd_spec, sc->res);
542 
543 	return (ENXIO);
544 }
545 
546 static struct fb_info *
jzlcd_fb_getinfo(device_t dev)547 jzlcd_fb_getinfo(device_t dev)
548 {
549 	struct jzlcd_softc *sc;
550 
551 	sc = device_get_softc(dev);
552 
553 	return (&sc->info);
554 }
555 
556 static device_method_t jzlcd_methods[] = {
557 	/* Device interface */
558 	DEVMETHOD(device_probe,		jzlcd_probe),
559 	DEVMETHOD(device_attach,	jzlcd_attach),
560 
561 	/* FB interface */
562 	DEVMETHOD(fb_getinfo,		jzlcd_fb_getinfo),
563 
564 	DEVMETHOD_END
565 };
566 
567 static driver_t jzlcd_driver = {
568 	"fb",
569 	jzlcd_methods,
570 	sizeof(struct jzlcd_softc),
571 };
572 
573 static devclass_t jzlcd_devclass;
574 
575 DRIVER_MODULE(fb, simplebus, jzlcd_driver, jzlcd_devclass, 0, 0);
576