FreeBSD/Linux Kernel Cross Reference
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: releng/10.2/sys/arm/lpc/lpc_fb.c 266152 2014-05-15 16:11:06Z ian $");
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);
Cache object: 9cc41a76376dd368546e800d882d8bbb
|