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