1239278Sgonzo/*-
2239278Sgonzo * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
3239278Sgonzo * All rights reserved.
4239278Sgonzo *
5239278Sgonzo * Redistribution and use in source and binary forms, with or without
6239278Sgonzo * modification, are permitted provided that the following conditions
7239278Sgonzo * are met:
8239278Sgonzo * 1. Redistributions of source code must retain the above copyright
9239278Sgonzo *    notice, this list of conditions and the following disclaimer.
10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239278Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239278Sgonzo *    documentation and/or other materials provided with the distribution.
13239278Sgonzo *
14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239278Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239278Sgonzo * SUCH DAMAGE.
25239278Sgonzo *
26239278Sgonzo */
27239278Sgonzo#include <sys/cdefs.h>
28239278Sgonzo__FBSDID("$FreeBSD$");
29239278Sgonzo
30239278Sgonzo#include <sys/param.h>
31239278Sgonzo#include <sys/systm.h>
32239278Sgonzo#include <sys/bio.h>
33239278Sgonzo#include <sys/bus.h>
34239278Sgonzo#include <sys/conf.h>
35239278Sgonzo#include <sys/endian.h>
36239278Sgonzo#include <sys/kernel.h>
37239278Sgonzo#include <sys/kthread.h>
38239278Sgonzo#include <sys/lock.h>
39239278Sgonzo#include <sys/malloc.h>
40239278Sgonzo#include <sys/module.h>
41239278Sgonzo#include <sys/mutex.h>
42239278Sgonzo#include <sys/queue.h>
43239278Sgonzo#include <sys/resource.h>
44239278Sgonzo#include <sys/rman.h>
45239278Sgonzo#include <sys/time.h>
46239278Sgonzo#include <sys/timetc.h>
47239278Sgonzo#include <sys/watchdog.h>
48239278Sgonzo
49239278Sgonzo#include <sys/kdb.h>
50239278Sgonzo
51239278Sgonzo#include <machine/bus.h>
52239278Sgonzo#include <machine/cpu.h>
53239278Sgonzo#include <machine/cpufunc.h>
54239278Sgonzo#include <machine/resource.h>
55239278Sgonzo#include <machine/frame.h>
56239278Sgonzo#include <machine/intr.h>
57239278Sgonzo
58239278Sgonzo#include <dev/fdt/fdt_common.h>
59239278Sgonzo#include <dev/ofw/ofw_bus.h>
60239278Sgonzo#include <dev/ofw/ofw_bus_subr.h>
61239278Sgonzo
62239278Sgonzo#include <arm/lpc/lpcreg.h>
63239278Sgonzo#include <arm/lpc/lpcvar.h>
64239278Sgonzo
65239278Sgonzo
66239278Sgonzostruct lpc_fb_dmamap_arg {
67239278Sgonzo	bus_addr_t		lf_dma_busaddr;
68239278Sgonzo};
69239278Sgonzo
70239278Sgonzostruct lpc_lcd_config {
71239278Sgonzo	int			lc_xres;
72239278Sgonzo	int			lc_yres;
73239278Sgonzo	int			lc_bpp;
74239278Sgonzo	uint32_t		lc_pixelclock;
75239278Sgonzo	int			lc_left_margin;
76239278Sgonzo	int			lc_right_margin;
77239278Sgonzo	int			lc_upper_margin;
78239278Sgonzo	int			lc_lower_margin;
79239278Sgonzo	int			lc_hsync_len;
80239278Sgonzo	int			lc_vsync_len;
81239278Sgonzo};
82239278Sgonzo
83239278Sgonzostruct lpc_fb_softc {
84239278Sgonzo	device_t		lf_dev;
85239278Sgonzo	struct cdev *		lf_cdev;
86239278Sgonzo	struct mtx		lf_mtx;
87239278Sgonzo	struct resource *	lf_mem_res;
88239278Sgonzo	struct resource *	lf_irq_res;
89239278Sgonzo	bus_space_tag_t		lf_bst;
90239278Sgonzo	bus_space_handle_t	lf_bsh;
91239278Sgonzo	void *			lf_intrhand;
92239278Sgonzo	bus_dma_tag_t		lf_dma_tag;
93239278Sgonzo	bus_dmamap_t		lf_dma_map;
94239278Sgonzo	void *			lf_buffer;
95239278Sgonzo	bus_addr_t		lf_buffer_phys;
96239278Sgonzo	bus_size_t		lf_buffer_size;
97239278Sgonzo	struct lpc_lcd_config	lf_lcd_config;
98239278Sgonzo	int			lf_initialized;
99239278Sgonzo	int			lf_opened;
100239278Sgonzo};
101239278Sgonzo
102239278Sgonzoextern void ssd1289_configure(void);
103239278Sgonzo
104239278Sgonzo#define	lpc_fb_lock(_sc)	mtx_lock(&(_sc)->lf_mtx)
105239278Sgonzo#define	lpc_fb_unlock(_sc)	mtx_unlock(&(_sc)->lf_mtx)
106239278Sgonzo#define	lpc_fb_lock_assert(sc)	mtx_assert(&(_sc)->lf_mtx, MA_OWNED)
107239278Sgonzo
108239278Sgonzo#define	lpc_fb_read_4(_sc, _reg)		\
109239278Sgonzo    bus_space_read_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg))
110239278Sgonzo#define	lpc_fb_write_4(_sc, _reg, _val)		\
111239278Sgonzo    bus_space_write_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg), (_val))
112239278Sgonzo
113239278Sgonzo
114239278Sgonzo
115239278Sgonzostatic int lpc_fb_probe(device_t);
116239278Sgonzostatic int lpc_fb_attach(device_t);
117239278Sgonzostatic void lpc_fb_intr(void *);
118239278Sgonzostatic void lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err);
119239278Sgonzo
120239278Sgonzostatic int lpc_fb_fdt_read(phandle_t, const char *, uint32_t *);
121239278Sgonzostatic int lpc_fb_read_lcd_config(phandle_t, struct lpc_lcd_config *);
122239278Sgonzo
123239278Sgonzostatic int lpc_fb_open(struct cdev *, int, int, struct thread *);
124239278Sgonzostatic int lpc_fb_close(struct cdev *, int, int, struct thread *);
125239278Sgonzostatic int lpc_fb_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
126239278Sgonzostatic int lpc_fb_mmap(struct cdev *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *);
127239278Sgonzo
128239278Sgonzostatic void lpc_fb_blank(struct lpc_fb_softc *);
129239278Sgonzo
130239278Sgonzostatic struct cdevsw lpc_fb_cdevsw = {
131239278Sgonzo	.d_open		= lpc_fb_open,
132239278Sgonzo	.d_close	= lpc_fb_close,
133239278Sgonzo	.d_ioctl	= lpc_fb_ioctl,
134239278Sgonzo	.d_mmap		= lpc_fb_mmap,
135239278Sgonzo	.d_name		= "lpcfb",
136239278Sgonzo	.d_version	= D_VERSION,
137239278Sgonzo};
138239278Sgonzo
139239278Sgonzostatic int
140239278Sgonzolpc_fb_probe(device_t dev)
141239278Sgonzo{
142239278Sgonzo	if (!ofw_bus_is_compatible(dev, "lpc,fb"))
143239278Sgonzo		return (ENXIO);
144239278Sgonzo
145239278Sgonzo	device_set_desc(dev, "LPC32x0 framebuffer device");
146239278Sgonzo	return (BUS_PROBE_DEFAULT);
147239278Sgonzo}
148239278Sgonzo
149239278Sgonzostatic int
150239278Sgonzolpc_fb_attach(device_t dev)
151239278Sgonzo{
152239278Sgonzo	struct lpc_fb_softc *sc = device_get_softc(dev);
153239278Sgonzo	struct lpc_fb_dmamap_arg ctx;
154239278Sgonzo	phandle_t node;
155239278Sgonzo	int mode, rid, err = 0;
156239278Sgonzo
157239278Sgonzo	sc->lf_dev = dev;
158239278Sgonzo	mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF);
159239278Sgonzo
160239278Sgonzo	rid = 0;
161239278Sgonzo	sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
162239278Sgonzo	    RF_ACTIVE);
163239278Sgonzo	if (!sc->lf_mem_res) {
164239278Sgonzo		device_printf(dev, "cannot allocate memory window\n");
165239278Sgonzo		return (ENXIO);
166239278Sgonzo	}
167239278Sgonzo
168239278Sgonzo	sc->lf_bst = rman_get_bustag(sc->lf_mem_res);
169239278Sgonzo	sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res);
170239278Sgonzo
171239278Sgonzo	rid = 0;
172239278Sgonzo	sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
173239278Sgonzo	    RF_ACTIVE);
174239278Sgonzo	if (!sc->lf_irq_res) {
175239278Sgonzo		device_printf(dev, "cannot allocate interrupt\n");
176239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
177239278Sgonzo		return (ENXIO);
178239278Sgonzo	}
179239278Sgonzo
180239278Sgonzo	if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
181239278Sgonzo	    NULL, lpc_fb_intr, sc, &sc->lf_intrhand))
182239278Sgonzo	{
183239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
184239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res);
185239278Sgonzo		device_printf(dev, "cannot setup interrupt handler\n");
186239278Sgonzo		return (ENXIO);
187239278Sgonzo	}
188239278Sgonzo
189239278Sgonzo	node = ofw_bus_get_node(dev);
190239278Sgonzo
191239278Sgonzo	err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config);
192239278Sgonzo	if (err) {
193239278Sgonzo		device_printf(dev, "cannot read LCD configuration\n");
194239278Sgonzo		goto fail;
195239278Sgonzo	}
196239278Sgonzo
197239278Sgonzo	sc->lf_buffer_size = sc->lf_lcd_config.lc_xres *
198239278Sgonzo	    sc->lf_lcd_config.lc_yres *
199239278Sgonzo	    (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2);
200239278Sgonzo
201239278Sgonzo	device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n",
202239278Sgonzo	    sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres,
203239278Sgonzo	    sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000);
204239278Sgonzo
205239278Sgonzo	err = bus_dma_tag_create(
206239278Sgonzo	    bus_get_dma_tag(sc->lf_dev),
207239278Sgonzo	    4, 0,			/* alignment, boundary */
208239278Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
209239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
210239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
211239278Sgonzo	    sc->lf_buffer_size, 1,	/* maxsize, nsegments */
212239278Sgonzo	    sc->lf_buffer_size, 0,	/* maxsegsize, flags */
213239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
214239278Sgonzo	    &sc->lf_dma_tag);
215239278Sgonzo
216239278Sgonzo	err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer,
217239278Sgonzo	    0, &sc->lf_dma_map);
218239278Sgonzo	if (err) {
219239278Sgonzo		device_printf(dev, "cannot allocate framebuffer\n");
220239278Sgonzo		goto fail;
221239278Sgonzo	}
222239278Sgonzo
223239278Sgonzo	err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer,
224239278Sgonzo	    sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
225239278Sgonzo	if (err) {
226239278Sgonzo		device_printf(dev, "cannot load DMA map\n");
227239278Sgonzo		goto fail;
228239278Sgonzo	}
229239278Sgonzo
230239278Sgonzo	switch (sc->lf_lcd_config.lc_bpp) {
231239278Sgonzo	case 12:
232239278Sgonzo		mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12;
233239278Sgonzo		break;
234239278Sgonzo	case 15:
235239278Sgonzo		mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15;
236239278Sgonzo		break;
237239278Sgonzo	case 16:
238239278Sgonzo		mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16;
239239278Sgonzo		break;
240239278Sgonzo	case 24:
241239278Sgonzo		mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24;
242239278Sgonzo		break;
243239278Sgonzo	default:
244239278Sgonzo		panic("unsupported bpp");
245239278Sgonzo	}
246239278Sgonzo
247239278Sgonzo	lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL,
248239278Sgonzo	    LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) |
249239278Sgonzo	    LPC_CLKPWR_LCDCLK_CTRL_HCLKEN);
250239278Sgonzo
251239278Sgonzo	sc->lf_buffer_phys = ctx.lf_dma_busaddr;
252239278Sgonzo	sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL,
253239278Sgonzo	    0600, "lpcfb");
254239278Sgonzo
255239278Sgonzo	sc->lf_cdev->si_drv1 = sc;
256239278Sgonzo
257239278Sgonzo	return (0);
258239278Sgonzofail:
259239278Sgonzo	return (ENXIO);
260239278Sgonzo}
261239278Sgonzo
262239278Sgonzostatic void
263239278Sgonzolpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
264239278Sgonzo{
265239278Sgonzo	struct lpc_fb_dmamap_arg *ctx;
266239278Sgonzo
267239278Sgonzo	if (err)
268239278Sgonzo		return;
269239278Sgonzo
270239278Sgonzo	ctx = (struct lpc_fb_dmamap_arg *)arg;
271239278Sgonzo	ctx->lf_dma_busaddr = segs[0].ds_addr;
272239278Sgonzo}
273239278Sgonzo
274239278Sgonzostatic void
275239278Sgonzolpc_fb_intr(void *arg)
276239278Sgonzo{
277239278Sgonzo}
278239278Sgonzo
279239278Sgonzostatic int
280239278Sgonzolpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret)
281239278Sgonzo{
282239278Sgonzo	if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0)
283239278Sgonzo		return (ENOENT);
284239278Sgonzo
285239278Sgonzo	*ret = fdt32_to_cpu(*ret);
286239278Sgonzo	return (0);
287239278Sgonzo}
288239278Sgonzo
289239278Sgonzostatic int
290239278Sgonzolpc_fb_read_lcd_config(phandle_t node, struct lpc_lcd_config *cfg)
291239278Sgonzo{
292239278Sgonzo	if (lpc_fb_fdt_read(node, "horizontal-resolution", &cfg->lc_xres))
293239278Sgonzo		return (ENXIO);
294239278Sgonzo
295239278Sgonzo	if (lpc_fb_fdt_read(node, "vertical-resolution", &cfg->lc_yres))
296239278Sgonzo		return (ENXIO);
297239278Sgonzo
298239278Sgonzo	if (lpc_fb_fdt_read(node, "bits-per-pixel", &cfg->lc_bpp))
299239278Sgonzo		return (ENXIO);
300239278Sgonzo
301239278Sgonzo	if (lpc_fb_fdt_read(node, "pixel-clock", &cfg->lc_pixelclock))
302239278Sgonzo		return (ENXIO);
303239278Sgonzo
304239278Sgonzo	if (lpc_fb_fdt_read(node, "left-margin", &cfg->lc_left_margin))
305239278Sgonzo		return (ENXIO);
306239278Sgonzo
307239278Sgonzo	if (lpc_fb_fdt_read(node, "right-margin", &cfg->lc_right_margin))
308239278Sgonzo		return (ENXIO);
309239278Sgonzo
310239278Sgonzo	if (lpc_fb_fdt_read(node, "upper-margin", &cfg->lc_upper_margin))
311239278Sgonzo		return (ENXIO);
312239278Sgonzo
313239278Sgonzo	if (lpc_fb_fdt_read(node, "lower-margin", &cfg->lc_lower_margin))
314239278Sgonzo		return (ENXIO);
315239278Sgonzo
316239278Sgonzo	if (lpc_fb_fdt_read(node, "hsync-len", &cfg->lc_hsync_len))
317239278Sgonzo		return (ENXIO);
318239278Sgonzo
319239278Sgonzo	if (lpc_fb_fdt_read(node, "vsync-len", &cfg->lc_vsync_len))
320239278Sgonzo		return (ENXIO);
321239278Sgonzo
322239278Sgonzo	return (0);
323239278Sgonzo}
324239278Sgonzo
325239278Sgonzostatic void
326239278Sgonzolpc_fb_setup(struct lpc_fb_softc *sc)
327239278Sgonzo{
328239278Sgonzo	struct lpc_lcd_config *cfg = &sc->lf_lcd_config;
329239278Sgonzo	uint32_t bpp;
330239278Sgonzo
331239278Sgonzo	lpc_fb_write_4(sc, LPC_LCD_TIMH,
332239278Sgonzo	    LPC_LCD_TIMH_PPL(cfg->lc_xres) |
333239278Sgonzo	    LPC_LCD_TIMH_HSW(cfg->lc_hsync_len - 1) |
334239278Sgonzo	    LPC_LCD_TIMH_HFP(cfg->lc_right_margin - 1) |
335239278Sgonzo	    LPC_LCD_TIMH_HBP(cfg->lc_left_margin - 1));
336239278Sgonzo
337239278Sgonzo	lpc_fb_write_4(sc, LPC_LCD_TIMV,
338239278Sgonzo	    LPC_LCD_TIMV_LPP(cfg->lc_yres - 1) |
339239278Sgonzo	    LPC_LCD_TIMV_VSW(cfg->lc_vsync_len - 1) |
340239278Sgonzo	    LPC_LCD_TIMV_VFP(cfg->lc_lower_margin) |
341239278Sgonzo	    LPC_LCD_TIMV_VBP(cfg->lc_upper_margin));
342239278Sgonzo
343239278Sgonzo	/* XXX LPC_LCD_POL_PCD_LO */
344239278Sgonzo	lpc_fb_write_4(sc, LPC_LCD_POL,
345239278Sgonzo	    LPC_LCD_POL_IHS | LPC_LCD_POL_IVS |
346239278Sgonzo	    LPC_LCD_POL_CPL(cfg->lc_xres - 1) |
347239278Sgonzo	    LPC_LCD_POL_PCD_LO(4));
348239278Sgonzo
349239278Sgonzo	lpc_fb_write_4(sc, LPC_LCD_UPBASE, sc->lf_buffer_phys);
350239278Sgonzo
351239278Sgonzo	switch (cfg->lc_bpp) {
352239278Sgonzo	case 1:
353239278Sgonzo		bpp = LPC_LCD_CTRL_BPP1;
354239278Sgonzo		break;
355239278Sgonzo	case 2:
356239278Sgonzo		bpp = LPC_LCD_CTRL_BPP2;
357239278Sgonzo		break;
358239278Sgonzo	case 4:
359239278Sgonzo		bpp = LPC_LCD_CTRL_BPP4;
360239278Sgonzo		break;
361239278Sgonzo	case 8:
362239278Sgonzo		bpp = LPC_LCD_CTRL_BPP8;
363239278Sgonzo		break;
364239278Sgonzo	case 12:
365239278Sgonzo		bpp = LPC_LCD_CTRL_BPP12_444;
366239278Sgonzo		break;
367239278Sgonzo	case 15:
368239278Sgonzo		bpp = LPC_LCD_CTRL_BPP16;
369239278Sgonzo		break;
370239278Sgonzo	case 16:
371239278Sgonzo		bpp = LPC_LCD_CTRL_BPP16_565;
372239278Sgonzo		break;
373239278Sgonzo	case 24:
374239278Sgonzo		bpp = LPC_LCD_CTRL_BPP24;
375239278Sgonzo		break;
376239278Sgonzo	default:
377239278Sgonzo		panic("LCD unknown bpp: %d", cfg->lc_bpp);
378239278Sgonzo	}
379239278Sgonzo
380239278Sgonzo	lpc_fb_write_4(sc, LPC_LCD_CTRL,
381239278Sgonzo	    LPC_LCD_CTRL_LCDVCOMP(1) |
382239278Sgonzo	    LPC_LCD_CTRL_LCDPWR |
383239278Sgonzo	    LPC_LCD_CTRL_BGR |
384239278Sgonzo	    LPC_LCD_CTRL_LCDTFT |
385239278Sgonzo	    LPC_LCD_CTRL_LCDBPP(bpp) |
386239278Sgonzo	    LPC_LCD_CTRL_LCDEN);
387239278Sgonzo}
388239278Sgonzo
389239278Sgonzo
390239278Sgonzostatic int
391239278Sgonzolpc_fb_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
392239278Sgonzo{
393239278Sgonzo	struct lpc_fb_softc *sc = cdev->si_drv1;
394239278Sgonzo
395239278Sgonzo	lpc_fb_lock(sc);
396239278Sgonzo
397239278Sgonzo	if (sc->lf_opened)
398239278Sgonzo		return (EBUSY);
399239278Sgonzo
400239278Sgonzo	sc->lf_opened = 1;
401239278Sgonzo
402239278Sgonzo	lpc_fb_unlock(sc);
403239278Sgonzo
404239278Sgonzo	if (!sc->lf_initialized) {
405239278Sgonzo		ssd1289_configure();
406239278Sgonzo		lpc_fb_setup(sc);
407239278Sgonzo		lpc_fb_blank(sc);
408239278Sgonzo		sc->lf_initialized = 1;
409239278Sgonzo	}
410239278Sgonzo
411239278Sgonzo	return (0);
412239278Sgonzo}
413239278Sgonzo
414239278Sgonzostatic int
415239278Sgonzolpc_fb_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
416239278Sgonzo{
417239278Sgonzo	struct lpc_fb_softc *sc = cdev->si_drv1;
418239278Sgonzo
419239278Sgonzo	lpc_fb_lock(sc);
420239278Sgonzo	sc->lf_opened = 0;
421239278Sgonzo	lpc_fb_unlock(sc);
422239278Sgonzo
423239278Sgonzo	return (0);
424239278Sgonzo}
425239278Sgonzo
426239278Sgonzostatic int
427239278Sgonzolpc_fb_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int x,
428239278Sgonzo    struct thread *td)
429239278Sgonzo{
430239278Sgonzo
431239278Sgonzo	return (EINVAL);
432239278Sgonzo}
433239278Sgonzo
434239278Sgonzostatic int
435239278Sgonzolpc_fb_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
436239278Sgonzo    int nprot, vm_memattr_t *memattr)
437239278Sgonzo{
438239278Sgonzo	struct lpc_fb_softc *sc = cdev->si_drv1;
439239278Sgonzo
440239278Sgonzo	*paddr = (vm_paddr_t)(sc->lf_buffer_phys + offset);
441239278Sgonzo	return (0);
442239278Sgonzo}
443239278Sgonzo
444239278Sgonzostatic void
445239278Sgonzolpc_fb_blank(struct lpc_fb_softc *sc)
446239278Sgonzo{
447239278Sgonzo	memset(sc->lf_buffer, 0xffff, sc->lf_buffer_size);
448239278Sgonzo}
449239278Sgonzo
450239278Sgonzostatic device_method_t lpc_fb_methods[] = {
451239278Sgonzo	/* Device interface */
452239278Sgonzo	DEVMETHOD(device_probe,		lpc_fb_probe),
453239278Sgonzo	DEVMETHOD(device_attach,	lpc_fb_attach),
454239278Sgonzo
455239278Sgonzo	{ 0, 0 }
456239278Sgonzo};
457239278Sgonzo
458239278Sgonzostatic devclass_t lpc_fb_devclass;
459239278Sgonzo
460239278Sgonzostatic driver_t lpc_fb_driver = {
461239278Sgonzo	"lpcfb",
462239278Sgonzo	lpc_fb_methods,
463239278Sgonzo	sizeof(struct lpc_fb_softc),
464239278Sgonzo};
465239278Sgonzo
466239278SgonzoDRIVER_MODULE(lpcfb, simplebus, lpc_fb_driver, lpc_fb_devclass, 0, 0);
467