]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/arm/lpc/lpc_fb.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / arm / lpc / lpc_fb.c
1 /*-
2  * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
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 AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, 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  */
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bio.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/endian.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/lock.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/queue.h>
43 #include <sys/resource.h>
44 #include <sys/rman.h>
45 #include <sys/time.h>
46 #include <sys/timetc.h>
47 #include <sys/watchdog.h>
48
49 #include <sys/kdb.h>
50
51 #include <machine/bus.h>
52 #include <machine/cpu.h>
53 #include <machine/cpufunc.h>
54 #include <machine/resource.h>
55 #include <machine/intr.h>
56
57 #include <dev/fdt/fdt_common.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/ofw_bus_subr.h>
60
61 #include <arm/lpc/lpcreg.h>
62 #include <arm/lpc/lpcvar.h>
63
64
65 struct lpc_fb_dmamap_arg {
66         bus_addr_t              lf_dma_busaddr;
67 };
68
69 struct lpc_lcd_config {
70         int                     lc_xres;
71         int                     lc_yres;
72         int                     lc_bpp;
73         uint32_t                lc_pixelclock;
74         int                     lc_left_margin;
75         int                     lc_right_margin;
76         int                     lc_upper_margin;
77         int                     lc_lower_margin;
78         int                     lc_hsync_len;
79         int                     lc_vsync_len;
80 };
81
82 struct lpc_fb_softc {
83         device_t                lf_dev;
84         struct cdev *           lf_cdev;
85         struct mtx              lf_mtx;
86         struct resource *       lf_mem_res;
87         struct resource *       lf_irq_res;
88         bus_space_tag_t         lf_bst;
89         bus_space_handle_t      lf_bsh;
90         void *                  lf_intrhand;
91         bus_dma_tag_t           lf_dma_tag;
92         bus_dmamap_t            lf_dma_map;
93         void *                  lf_buffer;
94         bus_addr_t              lf_buffer_phys;
95         bus_size_t              lf_buffer_size;
96         struct lpc_lcd_config   lf_lcd_config;
97         int                     lf_initialized;
98         int                     lf_opened;
99 };
100
101 extern void ssd1289_configure(void);
102
103 #define lpc_fb_lock(_sc)        mtx_lock(&(_sc)->lf_mtx)
104 #define lpc_fb_unlock(_sc)      mtx_unlock(&(_sc)->lf_mtx)
105 #define lpc_fb_lock_assert(sc)  mtx_assert(&(_sc)->lf_mtx, MA_OWNED)
106
107 #define lpc_fb_read_4(_sc, _reg)                \
108     bus_space_read_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg))
109 #define lpc_fb_write_4(_sc, _reg, _val)         \
110     bus_space_write_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg), (_val))
111
112
113
114 static int lpc_fb_probe(device_t);
115 static int lpc_fb_attach(device_t);
116 static void lpc_fb_intr(void *);
117 static void lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err);
118
119 static int lpc_fb_fdt_read(phandle_t, const char *, uint32_t *);
120 static int lpc_fb_read_lcd_config(phandle_t, struct lpc_lcd_config *);
121
122 static int lpc_fb_open(struct cdev *, int, int, struct thread *);
123 static int lpc_fb_close(struct cdev *, int, int, struct thread *);
124 static int lpc_fb_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
125 static int lpc_fb_mmap(struct cdev *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *);
126
127 static void lpc_fb_blank(struct lpc_fb_softc *);
128
129 static struct cdevsw lpc_fb_cdevsw = {
130         .d_open         = lpc_fb_open,
131         .d_close        = lpc_fb_close,
132         .d_ioctl        = lpc_fb_ioctl,
133         .d_mmap         = lpc_fb_mmap,
134         .d_name         = "lpcfb",
135         .d_version      = D_VERSION,
136 };
137
138 static int
139 lpc_fb_probe(device_t dev)
140 {
141
142         if (!ofw_bus_status_okay(dev))
143                 return (ENXIO);
144
145         if (!ofw_bus_is_compatible(dev, "lpc,fb"))
146                 return (ENXIO);
147
148         device_set_desc(dev, "LPC32x0 framebuffer device");
149         return (BUS_PROBE_DEFAULT);
150 }
151
152 static int
153 lpc_fb_attach(device_t dev)
154 {
155         struct lpc_fb_softc *sc = device_get_softc(dev);
156         struct lpc_fb_dmamap_arg ctx;
157         phandle_t node;
158         int mode, rid, err = 0;
159
160         sc->lf_dev = dev;
161         mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF);
162
163         rid = 0;
164         sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
165             RF_ACTIVE);
166         if (!sc->lf_mem_res) {
167                 device_printf(dev, "cannot allocate memory window\n");
168                 return (ENXIO);
169         }
170
171         sc->lf_bst = rman_get_bustag(sc->lf_mem_res);
172         sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res);
173
174         rid = 0;
175         sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
176             RF_ACTIVE);
177         if (!sc->lf_irq_res) {
178                 device_printf(dev, "cannot allocate interrupt\n");
179                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
180                 return (ENXIO);
181         }
182
183         if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
184             NULL, lpc_fb_intr, sc, &sc->lf_intrhand))
185         {
186                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
187                 bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res);
188                 device_printf(dev, "cannot setup interrupt handler\n");
189                 return (ENXIO);
190         }
191
192         node = ofw_bus_get_node(dev);
193
194         err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config);
195         if (err) {
196                 device_printf(dev, "cannot read LCD configuration\n");
197                 goto fail;
198         }
199
200         sc->lf_buffer_size = sc->lf_lcd_config.lc_xres * 
201             sc->lf_lcd_config.lc_yres *
202             (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2);
203
204         device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n",
205             sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres,
206             sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000);
207
208         err = bus_dma_tag_create(
209             bus_get_dma_tag(sc->lf_dev),
210             4, 0,                       /* alignment, boundary */
211             BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
212             BUS_SPACE_MAXADDR,          /* highaddr */
213             NULL, NULL,                 /* filter, filterarg */
214             sc->lf_buffer_size, 1,      /* maxsize, nsegments */
215             sc->lf_buffer_size, 0,      /* maxsegsize, flags */
216             NULL, NULL,                 /* lockfunc, lockarg */
217             &sc->lf_dma_tag);
218
219         err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer,
220             0, &sc->lf_dma_map);
221         if (err) {
222                 device_printf(dev, "cannot allocate framebuffer\n");
223                 goto fail;
224         }
225
226         err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer,
227             sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
228         if (err) {
229                 device_printf(dev, "cannot load DMA map\n");
230                 goto fail;
231         }
232
233         switch (sc->lf_lcd_config.lc_bpp) {
234         case 12:
235                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12;
236                 break;
237         case 15:
238                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15;
239                 break;
240         case 16:
241                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16;
242                 break;
243         case 24:
244                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24;
245                 break;
246         default:
247                 panic("unsupported bpp");
248         }
249
250         lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL,
251             LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) |
252             LPC_CLKPWR_LCDCLK_CTRL_HCLKEN);
253
254         sc->lf_buffer_phys = ctx.lf_dma_busaddr;
255         sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL,
256             0600, "lpcfb");
257
258         sc->lf_cdev->si_drv1 = sc;
259
260         return (0);
261 fail:
262         return (ENXIO);
263 }
264
265 static void
266 lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
267 {
268         struct lpc_fb_dmamap_arg *ctx;
269
270         if (err)
271                 return;
272
273         ctx = (struct lpc_fb_dmamap_arg *)arg;
274         ctx->lf_dma_busaddr = segs[0].ds_addr;
275 }
276
277 static void
278 lpc_fb_intr(void *arg)
279 {
280 }
281
282 static int
283 lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret)
284 {
285         if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0)
286                 return (ENOENT);
287
288         *ret = fdt32_to_cpu(*ret);
289         return (0);
290 }
291
292 static int
293 lpc_fb_read_lcd_config(phandle_t node, struct lpc_lcd_config *cfg)
294 {
295         if (lpc_fb_fdt_read(node, "horizontal-resolution", &cfg->lc_xres))
296                 return (ENXIO);
297
298         if (lpc_fb_fdt_read(node, "vertical-resolution", &cfg->lc_yres))
299                 return (ENXIO);
300
301         if (lpc_fb_fdt_read(node, "bits-per-pixel", &cfg->lc_bpp))
302                 return (ENXIO);
303
304         if (lpc_fb_fdt_read(node, "pixel-clock", &cfg->lc_pixelclock))
305                 return (ENXIO);
306
307         if (lpc_fb_fdt_read(node, "left-margin", &cfg->lc_left_margin))
308                 return (ENXIO);
309
310         if (lpc_fb_fdt_read(node, "right-margin", &cfg->lc_right_margin))
311                 return (ENXIO);
312
313         if (lpc_fb_fdt_read(node, "upper-margin", &cfg->lc_upper_margin))
314                 return (ENXIO);
315
316         if (lpc_fb_fdt_read(node, "lower-margin", &cfg->lc_lower_margin))
317                 return (ENXIO);
318
319         if (lpc_fb_fdt_read(node, "hsync-len", &cfg->lc_hsync_len))
320                 return (ENXIO);
321
322         if (lpc_fb_fdt_read(node, "vsync-len", &cfg->lc_vsync_len))
323                 return (ENXIO);
324
325         return (0);
326 }
327
328 static void
329 lpc_fb_setup(struct lpc_fb_softc *sc)
330 {
331         struct lpc_lcd_config *cfg = &sc->lf_lcd_config;
332         uint32_t bpp;
333
334         lpc_fb_write_4(sc, LPC_LCD_TIMH,
335             LPC_LCD_TIMH_PPL(cfg->lc_xres) |
336             LPC_LCD_TIMH_HSW(cfg->lc_hsync_len - 1) |
337             LPC_LCD_TIMH_HFP(cfg->lc_right_margin - 1) |
338             LPC_LCD_TIMH_HBP(cfg->lc_left_margin - 1));
339
340         lpc_fb_write_4(sc, LPC_LCD_TIMV,
341             LPC_LCD_TIMV_LPP(cfg->lc_yres - 1) |
342             LPC_LCD_TIMV_VSW(cfg->lc_vsync_len - 1) |
343             LPC_LCD_TIMV_VFP(cfg->lc_lower_margin) |
344             LPC_LCD_TIMV_VBP(cfg->lc_upper_margin));
345
346         /* XXX LPC_LCD_POL_PCD_LO */
347         lpc_fb_write_4(sc, LPC_LCD_POL,
348             LPC_LCD_POL_IHS | LPC_LCD_POL_IVS |
349             LPC_LCD_POL_CPL(cfg->lc_xres - 1) |
350             LPC_LCD_POL_PCD_LO(4));
351         
352         lpc_fb_write_4(sc, LPC_LCD_UPBASE, sc->lf_buffer_phys);
353         
354         switch (cfg->lc_bpp) {
355         case 1:
356                 bpp = LPC_LCD_CTRL_BPP1;
357                 break;
358         case 2:
359                 bpp = LPC_LCD_CTRL_BPP2;
360                 break;
361         case 4:
362                 bpp = LPC_LCD_CTRL_BPP4;
363                 break;
364         case 8:
365                 bpp = LPC_LCD_CTRL_BPP8;
366                 break;
367         case 12:
368                 bpp = LPC_LCD_CTRL_BPP12_444;
369                 break;
370         case 15:
371                 bpp = LPC_LCD_CTRL_BPP16;
372                 break;
373         case 16:
374                 bpp = LPC_LCD_CTRL_BPP16_565;
375                 break;
376         case 24:
377                 bpp = LPC_LCD_CTRL_BPP24;
378                 break;
379         default:
380                 panic("LCD unknown bpp: %d", cfg->lc_bpp);
381         }
382
383         lpc_fb_write_4(sc, LPC_LCD_CTRL,
384             LPC_LCD_CTRL_LCDVCOMP(1) |
385             LPC_LCD_CTRL_LCDPWR |
386             LPC_LCD_CTRL_BGR |
387             LPC_LCD_CTRL_LCDTFT |
388             LPC_LCD_CTRL_LCDBPP(bpp) |
389             LPC_LCD_CTRL_LCDEN);
390 }
391
392
393 static int
394 lpc_fb_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
395 {
396         struct lpc_fb_softc *sc = cdev->si_drv1;
397
398         lpc_fb_lock(sc);
399
400         if (sc->lf_opened)
401                 return (EBUSY);
402
403         sc->lf_opened = 1;
404
405         lpc_fb_unlock(sc);
406
407         if (!sc->lf_initialized) {
408                 ssd1289_configure();
409                 lpc_fb_setup(sc);
410                 lpc_fb_blank(sc);
411                 sc->lf_initialized = 1;
412         }
413
414         return (0);
415 }
416
417 static int
418 lpc_fb_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
419 {       
420         struct lpc_fb_softc *sc = cdev->si_drv1;
421
422         lpc_fb_lock(sc);
423         sc->lf_opened = 0;
424         lpc_fb_unlock(sc);
425
426         return (0);
427 }
428
429 static int
430 lpc_fb_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int x,
431     struct thread *td)
432 {
433         
434         return (EINVAL);
435 }
436
437 static int
438 lpc_fb_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
439     int nprot, vm_memattr_t *memattr)
440 {
441         struct lpc_fb_softc *sc = cdev->si_drv1;
442
443         *paddr = (vm_paddr_t)(sc->lf_buffer_phys + offset);
444         return (0);
445 }
446
447 static void
448 lpc_fb_blank(struct lpc_fb_softc *sc)
449 {
450         memset(sc->lf_buffer, 0xffff, sc->lf_buffer_size);
451 }
452
453 static device_method_t lpc_fb_methods[] = {
454         /* Device interface */
455         DEVMETHOD(device_probe,         lpc_fb_probe),
456         DEVMETHOD(device_attach,        lpc_fb_attach),
457
458         { 0, 0 }
459 };
460
461 static devclass_t lpc_fb_devclass;
462
463 static driver_t lpc_fb_driver = {
464         "lpcfb",
465         lpc_fb_methods,
466         sizeof(struct lpc_fb_softc),
467 };
468
469 DRIVER_MODULE(lpcfb, simplebus, lpc_fb_driver, lpc_fb_devclass, 0, 0);