bcm2835_fb.c revision 243666
1239922Sgonzo/*-
2239922Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3239922Sgonzo * All rights reserved.
4239922Sgonzo *
5239922Sgonzo * Redistribution and use in source and binary forms, with or without
6239922Sgonzo * modification, are permitted provided that the following conditions
7239922Sgonzo * are met:
8239922Sgonzo * 1. Redistributions of source code must retain the above copyright
9239922Sgonzo *    notice, this list of conditions and the following disclaimer.
10239922Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239922Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239922Sgonzo *    documentation and/or other materials provided with the distribution.
13239922Sgonzo *
14239922Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239922Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239922Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239922Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239922Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239922Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239922Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239922Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239922Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239922Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239922Sgonzo * SUCH DAMAGE.
25239922Sgonzo *
26239922Sgonzo */
27239922Sgonzo#include <sys/cdefs.h>
28239922Sgonzo__FBSDID("$FreeBSD: head/sys/arm/broadcom/bcm2835/bcm2835_fb.c 243666 2012-11-29 05:46:46Z gonzo $");
29239922Sgonzo
30239922Sgonzo#include <sys/param.h>
31239922Sgonzo#include <sys/systm.h>
32239922Sgonzo#include <sys/bio.h>
33239922Sgonzo#include <sys/bus.h>
34239922Sgonzo#include <sys/conf.h>
35239922Sgonzo#include <sys/endian.h>
36239922Sgonzo#include <sys/kernel.h>
37239922Sgonzo#include <sys/kthread.h>
38239922Sgonzo#include <sys/lock.h>
39239922Sgonzo#include <sys/malloc.h>
40239922Sgonzo#include <sys/module.h>
41239922Sgonzo#include <sys/mutex.h>
42239922Sgonzo#include <sys/queue.h>
43239922Sgonzo#include <sys/resource.h>
44239922Sgonzo#include <sys/rman.h>
45239922Sgonzo#include <sys/time.h>
46239922Sgonzo#include <sys/timetc.h>
47239922Sgonzo#include <sys/fbio.h>
48239922Sgonzo#include <sys/consio.h>
49239922Sgonzo
50239922Sgonzo#include <sys/kdb.h>
51239922Sgonzo
52239922Sgonzo#include <machine/bus.h>
53239922Sgonzo#include <machine/cpu.h>
54239922Sgonzo#include <machine/cpufunc.h>
55239922Sgonzo#include <machine/resource.h>
56239922Sgonzo#include <machine/frame.h>
57239922Sgonzo#include <machine/intr.h>
58239922Sgonzo
59239922Sgonzo#include <dev/fdt/fdt_common.h>
60239922Sgonzo#include <dev/ofw/ofw_bus.h>
61239922Sgonzo#include <dev/ofw/ofw_bus_subr.h>
62239922Sgonzo
63239922Sgonzo#include <dev/fb/fbreg.h>
64239922Sgonzo#include <dev/syscons/syscons.h>
65239922Sgonzo
66239922Sgonzo#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
67239922Sgonzo#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
68239922Sgonzo
69239922Sgonzo#define	BCMFB_FONT_HEIGHT	16
70239922Sgonzo
71243423Sgonzostruct argb {
72243423Sgonzo	uint8_t		a;
73243423Sgonzo	uint8_t		r;
74243423Sgonzo	uint8_t		g;
75243423Sgonzo	uint8_t		b;
76243423Sgonzo};
77243423Sgonzo
78243423Sgonzostatic struct argb bcmfb_palette[16] = {
79243423Sgonzo	{0x00, 0x00, 0x00, 0x00},
80243423Sgonzo	{0x00, 0x00, 0x00, 0xaa},
81243423Sgonzo	{0x00, 0x00, 0xaa, 0x00},
82243423Sgonzo	{0x00, 0x00, 0xaa, 0xaa},
83243423Sgonzo	{0x00, 0xaa, 0x00, 0x00},
84243423Sgonzo	{0x00, 0xaa, 0x00, 0xaa},
85243423Sgonzo	{0x00, 0xaa, 0x55, 0x00},
86243423Sgonzo	{0x00, 0xaa, 0xaa, 0xaa},
87243423Sgonzo	{0x00, 0x55, 0x55, 0x55},
88243423Sgonzo	{0x00, 0x55, 0x55, 0xff},
89243423Sgonzo	{0x00, 0x55, 0xff, 0x55},
90243423Sgonzo	{0x00, 0x55, 0xff, 0xff},
91243423Sgonzo	{0x00, 0xff, 0x55, 0x55},
92243423Sgonzo	{0x00, 0xff, 0x55, 0xff},
93243423Sgonzo	{0x00, 0xff, 0xff, 0x55},
94243423Sgonzo	{0x00, 0xff, 0xff, 0xff}
95243423Sgonzo};
96243423Sgonzo
97239922Sgonzo#define FB_WIDTH		640
98239922Sgonzo#define FB_HEIGHT		480
99243423Sgonzo#define FB_DEPTH		24
100239922Sgonzo
101239922Sgonzostruct bcm_fb_config {
102239922Sgonzo	uint32_t	xres;
103239922Sgonzo	uint32_t	yres;
104239922Sgonzo	uint32_t	vxres;
105239922Sgonzo	uint32_t	vyres;
106239922Sgonzo	uint32_t	pitch;
107239922Sgonzo	uint32_t	bpp;
108239922Sgonzo	uint32_t	xoffset;
109239922Sgonzo	uint32_t	yoffset;
110239922Sgonzo	/* Filled by videocore */
111239922Sgonzo	uint32_t	base;
112239922Sgonzo	uint32_t	screen_size;
113239922Sgonzo};
114239922Sgonzo
115239922Sgonzostruct bcmsc_softc {
116239922Sgonzo	device_t		dev;
117239922Sgonzo	struct cdev *		cdev;
118239922Sgonzo	struct mtx		mtx;
119239922Sgonzo	bus_dma_tag_t		dma_tag;
120239922Sgonzo	bus_dmamap_t		dma_map;
121239922Sgonzo	struct bcm_fb_config*	fb_config;
122239922Sgonzo	bus_addr_t		fb_config_phys;
123239922Sgonzo	struct intr_config_hook	init_hook;
124239922Sgonzo
125239922Sgonzo};
126239922Sgonzo
127239922Sgonzostruct video_adapter_softc {
128239922Sgonzo	/* Videoadpater part */
129239922Sgonzo	video_adapter_t	va;
130239922Sgonzo	int		console;
131239922Sgonzo
132239922Sgonzo	intptr_t	fb_addr;
133239922Sgonzo	unsigned int	fb_size;
134239922Sgonzo
135239922Sgonzo	unsigned int	height;
136239922Sgonzo	unsigned int	width;
137243423Sgonzo	unsigned int	depth;
138239922Sgonzo	unsigned int	stride;
139239922Sgonzo
140239922Sgonzo	unsigned int	xmargin;
141239922Sgonzo	unsigned int	ymargin;
142239922Sgonzo
143239922Sgonzo	unsigned char	*font;
144239922Sgonzo	int		initialized;
145239922Sgonzo};
146239922Sgonzo
147239922Sgonzostatic struct bcmsc_softc *bcmsc_softc;
148239922Sgonzostatic struct video_adapter_softc va_softc;
149239922Sgonzo
150239922Sgonzo#define	bcm_fb_lock(_sc)	mtx_lock(&(_sc)->mtx)
151239922Sgonzo#define	bcm_fb_unlock(_sc)	mtx_unlock(&(_sc)->mtx)
152239922Sgonzo#define	bcm_fb_lock_assert(sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
153239922Sgonzo
154239922Sgonzostatic int bcm_fb_probe(device_t);
155239922Sgonzostatic int bcm_fb_attach(device_t);
156239922Sgonzostatic void bcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err);
157243423Sgonzostatic void bcmfb_update_margins(video_adapter_t *adp);
158243423Sgonzostatic int bcmfb_configure(int);
159239922Sgonzo
160239922Sgonzostatic void
161239922Sgonzobcm_fb_init(void *arg)
162239922Sgonzo{
163239922Sgonzo	struct bcmsc_softc *sc = arg;
164239922Sgonzo	struct video_adapter_softc *va_sc = &va_softc;
165239922Sgonzo	int err;
166239922Sgonzo	volatile struct bcm_fb_config*	fb_config = sc->fb_config;
167243423Sgonzo	phandle_t node;
168243423Sgonzo	pcell_t cell;
169239922Sgonzo
170243423Sgonzo	node = ofw_bus_get_node(sc->dev);
171243423Sgonzo
172243423Sgonzo	fb_config->xres = 0;
173243423Sgonzo	fb_config->yres = 0;
174243423Sgonzo	fb_config->bpp = 0;
175243423Sgonzo
176243423Sgonzo	if ((OF_getprop(node, "broadcom,width", &cell, sizeof(cell))) > 0)
177243423Sgonzo		fb_config->xres = (int)fdt32_to_cpu(cell);
178243423Sgonzo	if (fb_config->xres == 0)
179243423Sgonzo		fb_config->xres = FB_WIDTH;
180243423Sgonzo
181243423Sgonzo	if ((OF_getprop(node, "broadcom,height", &cell, sizeof(cell))) > 0)
182243423Sgonzo		fb_config->yres = (uint32_t)fdt32_to_cpu(cell);
183243423Sgonzo	if (fb_config->yres == 0)
184243423Sgonzo		fb_config->yres = FB_HEIGHT;
185243423Sgonzo
186243423Sgonzo	if ((OF_getprop(node, "broadcom,depth", &cell, sizeof(cell))) > 0)
187243423Sgonzo		fb_config->bpp = (uint32_t)fdt32_to_cpu(cell);
188243423Sgonzo	if (fb_config->bpp == 0)
189243423Sgonzo		fb_config->bpp = FB_DEPTH;
190243423Sgonzo
191239922Sgonzo	fb_config->vxres = 0;
192239922Sgonzo	fb_config->vyres = 0;
193239922Sgonzo	fb_config->xoffset = 0;
194239922Sgonzo	fb_config->yoffset = 0;
195239922Sgonzo	fb_config->base = 0;
196239922Sgonzo	fb_config->pitch = 0;
197239922Sgonzo	fb_config->screen_size = 0;
198239922Sgonzo
199239922Sgonzo	bus_dmamap_sync(sc->dma_tag, sc->dma_map,
200239922Sgonzo		BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
201239922Sgonzo	bcm_mbox_write(BCM2835_MBOX_CHAN_FB, sc->fb_config_phys);
202239922Sgonzo	bcm_mbox_read(BCM2835_MBOX_CHAN_FB, &err);
203239922Sgonzo	bus_dmamap_sync(sc->dma_tag, sc->dma_map,
204239922Sgonzo		BUS_DMASYNC_POSTREAD);
205239922Sgonzo
206243423Sgonzo	if (fb_config->base != 0) {
207239922Sgonzo		device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n",
208239922Sgonzo			fb_config->xres, fb_config->yres,
209239922Sgonzo			fb_config->vxres, fb_config->vyres,
210239922Sgonzo			fb_config->xoffset, fb_config->yoffset,
211239922Sgonzo			fb_config->bpp);
212239922Sgonzo
213239922Sgonzo
214239922Sgonzo		device_printf(sc->dev, "pitch %d, base 0x%08x, screen_size %d\n",
215239922Sgonzo			fb_config->pitch, fb_config->base,
216239922Sgonzo			fb_config->screen_size);
217239922Sgonzo
218243423Sgonzo		va_sc->fb_addr = (intptr_t)pmap_mapdev(fb_config->base, fb_config->screen_size);
219243423Sgonzo		va_sc->fb_size = fb_config->screen_size;
220243423Sgonzo		va_sc->depth = fb_config->bpp;
221243423Sgonzo		va_sc->stride = fb_config->pitch;
222243423Sgonzo
223243423Sgonzo		va_sc->width = fb_config->xres;
224243423Sgonzo		va_sc->height = fb_config->yres;
225243423Sgonzo		bcmfb_update_margins(&va_sc->va);
226239922Sgonzo	}
227243423Sgonzo	else {
228239922Sgonzo		device_printf(sc->dev, "Failed to set framebuffer info\n");
229243423Sgonzo		return;
230243423Sgonzo	}
231239922Sgonzo
232239922Sgonzo	config_intrhook_disestablish(&sc->init_hook);
233239922Sgonzo}
234239922Sgonzo
235239922Sgonzostatic int
236239922Sgonzobcm_fb_probe(device_t dev)
237239922Sgonzo{
238243423Sgonzo	int error = 0;
239239922Sgonzo
240239922Sgonzo	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-fb"))
241239922Sgonzo		return (ENXIO);
242239922Sgonzo
243239922Sgonzo	device_set_desc(dev, "BCM2835 framebuffer device");
244239922Sgonzo
245239922Sgonzo	error = sc_probe_unit(device_get_unit(dev),
246239922Sgonzo	    device_get_flags(dev) | SC_AUTODETECT_KBD);
247239922Sgonzo	if (error != 0)
248239922Sgonzo		return (error);
249239922Sgonzo
250243423Sgonzo
251239922Sgonzo	return (BUS_PROBE_DEFAULT);
252239922Sgonzo}
253239922Sgonzo
254239922Sgonzostatic int
255239922Sgonzobcm_fb_attach(device_t dev)
256239922Sgonzo{
257239922Sgonzo	struct bcmsc_softc *sc = device_get_softc(dev);
258239922Sgonzo	int dma_size = sizeof(struct bcm_fb_config);
259239922Sgonzo	int err;
260239922Sgonzo
261239922Sgonzo	if (bcmsc_softc)
262239922Sgonzo		return (ENXIO);
263239922Sgonzo
264239922Sgonzo	bcmsc_softc = sc;
265239922Sgonzo
266239922Sgonzo	sc->dev = dev;
267239922Sgonzo	mtx_init(&sc->mtx, "bcm2835fb", "fb", MTX_DEF);
268239922Sgonzo
269239922Sgonzo	err = bus_dma_tag_create(
270239922Sgonzo	    bus_get_dma_tag(sc->dev),
271239922Sgonzo	    PAGE_SIZE, 0,		/* alignment, boundary */
272239922Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
273239922Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
274239922Sgonzo	    NULL, NULL,			/* filter, filterarg */
275239922Sgonzo	    dma_size, 1,		/* maxsize, nsegments */
276239922Sgonzo	    dma_size, 0,		/* maxsegsize, flags */
277239922Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
278239922Sgonzo	    &sc->dma_tag);
279239922Sgonzo
280239922Sgonzo	err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->fb_config,
281239922Sgonzo	    0, &sc->dma_map);
282239922Sgonzo	if (err) {
283239922Sgonzo		device_printf(dev, "cannot allocate framebuffer\n");
284239922Sgonzo		goto fail;
285239922Sgonzo	}
286239922Sgonzo
287239922Sgonzo	err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->fb_config,
288239922Sgonzo	    dma_size, bcm_fb_dmamap_cb, &sc->fb_config_phys, BUS_DMA_NOWAIT);
289239922Sgonzo
290239922Sgonzo	if (err) {
291239922Sgonzo		device_printf(dev, "cannot load DMA map\n");
292239922Sgonzo		goto fail;
293239922Sgonzo	}
294239922Sgonzo
295239922Sgonzo	err = (sc_attach_unit(device_get_unit(dev),
296239922Sgonzo	    device_get_flags(dev) | SC_AUTODETECT_KBD));
297239922Sgonzo
298239922Sgonzo	if (err) {
299239922Sgonzo		device_printf(dev, "failed to attach syscons\n");
300239922Sgonzo		goto fail;
301239922Sgonzo	}
302239922Sgonzo
303239922Sgonzo	/*
304239922Sgonzo	 * We have to wait until interrupts are enabled.
305239922Sgonzo	 * Mailbox relies on it to get data from VideoCore
306239922Sgonzo	 */
307239922Sgonzo        sc->init_hook.ich_func = bcm_fb_init;
308239922Sgonzo        sc->init_hook.ich_arg = sc;
309239922Sgonzo
310239922Sgonzo        if (config_intrhook_establish(&sc->init_hook) != 0) {
311239922Sgonzo		device_printf(dev, "failed to establish intrhook\n");
312239922Sgonzo                return (ENOMEM);
313239922Sgonzo	}
314239922Sgonzo
315239922Sgonzo	return (0);
316239922Sgonzo
317239922Sgonzofail:
318239922Sgonzo	return (ENXIO);
319239922Sgonzo}
320239922Sgonzo
321239922Sgonzo
322239922Sgonzostatic void
323239922Sgonzobcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
324239922Sgonzo{
325239922Sgonzo	bus_addr_t *addr;
326239922Sgonzo
327239922Sgonzo	if (err)
328239922Sgonzo		return;
329239922Sgonzo
330239922Sgonzo	addr = (bus_addr_t*)arg;
331239922Sgonzo	*addr = PHYS_TO_VCBUS(segs[0].ds_addr);
332239922Sgonzo}
333239922Sgonzo
334239922Sgonzostatic device_method_t bcm_fb_methods[] = {
335239922Sgonzo	/* Device interface */
336239922Sgonzo	DEVMETHOD(device_probe,		bcm_fb_probe),
337239922Sgonzo	DEVMETHOD(device_attach,	bcm_fb_attach),
338239922Sgonzo
339239922Sgonzo	{ 0, 0 }
340239922Sgonzo};
341239922Sgonzo
342239922Sgonzostatic devclass_t bcm_fb_devclass;
343239922Sgonzo
344239922Sgonzostatic driver_t bcm_fb_driver = {
345239922Sgonzo	"fb",
346239922Sgonzo	bcm_fb_methods,
347239922Sgonzo	sizeof(struct bcmsc_softc),
348239922Sgonzo};
349239922Sgonzo
350243423SgonzoDRIVER_MODULE(bcm2835fb, fdtbus, bcm_fb_driver, bcm_fb_devclass, 0, 0);
351239922Sgonzo
352239922Sgonzo/*
353239922Sgonzo * Video driver routines and glue.
354239922Sgonzo */
355239922Sgonzostatic vi_probe_t		bcmfb_probe;
356239922Sgonzostatic vi_init_t		bcmfb_init;
357239922Sgonzostatic vi_get_info_t		bcmfb_get_info;
358239922Sgonzostatic vi_query_mode_t		bcmfb_query_mode;
359239922Sgonzostatic vi_set_mode_t		bcmfb_set_mode;
360239922Sgonzostatic vi_save_font_t		bcmfb_save_font;
361239922Sgonzostatic vi_load_font_t		bcmfb_load_font;
362239922Sgonzostatic vi_show_font_t		bcmfb_show_font;
363239922Sgonzostatic vi_save_palette_t	bcmfb_save_palette;
364239922Sgonzostatic vi_load_palette_t	bcmfb_load_palette;
365239922Sgonzostatic vi_set_border_t		bcmfb_set_border;
366239922Sgonzostatic vi_save_state_t		bcmfb_save_state;
367239922Sgonzostatic vi_load_state_t		bcmfb_load_state;
368239922Sgonzostatic vi_set_win_org_t		bcmfb_set_win_org;
369239922Sgonzostatic vi_read_hw_cursor_t	bcmfb_read_hw_cursor;
370239922Sgonzostatic vi_set_hw_cursor_t	bcmfb_set_hw_cursor;
371239922Sgonzostatic vi_set_hw_cursor_shape_t	bcmfb_set_hw_cursor_shape;
372239922Sgonzostatic vi_blank_display_t	bcmfb_blank_display;
373239922Sgonzostatic vi_mmap_t		bcmfb_mmap;
374239922Sgonzostatic vi_ioctl_t		bcmfb_ioctl;
375239922Sgonzostatic vi_clear_t		bcmfb_clear;
376239922Sgonzostatic vi_fill_rect_t		bcmfb_fill_rect;
377239922Sgonzostatic vi_bitblt_t		bcmfb_bitblt;
378239922Sgonzostatic vi_diag_t		bcmfb_diag;
379239922Sgonzostatic vi_save_cursor_palette_t	bcmfb_save_cursor_palette;
380239922Sgonzostatic vi_load_cursor_palette_t	bcmfb_load_cursor_palette;
381239922Sgonzostatic vi_copy_t		bcmfb_copy;
382239922Sgonzostatic vi_putp_t		bcmfb_putp;
383239922Sgonzostatic vi_putc_t		bcmfb_putc;
384239922Sgonzostatic vi_puts_t		bcmfb_puts;
385239922Sgonzostatic vi_putm_t		bcmfb_putm;
386239922Sgonzo
387239922Sgonzostatic video_switch_t bcmfbvidsw = {
388239922Sgonzo	.probe			= bcmfb_probe,
389239922Sgonzo	.init			= bcmfb_init,
390239922Sgonzo	.get_info		= bcmfb_get_info,
391239922Sgonzo	.query_mode		= bcmfb_query_mode,
392239922Sgonzo	.set_mode		= bcmfb_set_mode,
393239922Sgonzo	.save_font		= bcmfb_save_font,
394239922Sgonzo	.load_font		= bcmfb_load_font,
395239922Sgonzo	.show_font		= bcmfb_show_font,
396239922Sgonzo	.save_palette		= bcmfb_save_palette,
397239922Sgonzo	.load_palette		= bcmfb_load_palette,
398239922Sgonzo	.set_border		= bcmfb_set_border,
399239922Sgonzo	.save_state		= bcmfb_save_state,
400239922Sgonzo	.load_state		= bcmfb_load_state,
401239922Sgonzo	.set_win_org		= bcmfb_set_win_org,
402239922Sgonzo	.read_hw_cursor		= bcmfb_read_hw_cursor,
403239922Sgonzo	.set_hw_cursor		= bcmfb_set_hw_cursor,
404239922Sgonzo	.set_hw_cursor_shape	= bcmfb_set_hw_cursor_shape,
405239922Sgonzo	.blank_display		= bcmfb_blank_display,
406239922Sgonzo	.mmap			= bcmfb_mmap,
407239922Sgonzo	.ioctl			= bcmfb_ioctl,
408239922Sgonzo	.clear			= bcmfb_clear,
409239922Sgonzo	.fill_rect		= bcmfb_fill_rect,
410239922Sgonzo	.bitblt			= bcmfb_bitblt,
411239922Sgonzo	.diag			= bcmfb_diag,
412239922Sgonzo	.save_cursor_palette	= bcmfb_save_cursor_palette,
413239922Sgonzo	.load_cursor_palette	= bcmfb_load_cursor_palette,
414239922Sgonzo	.copy			= bcmfb_copy,
415239922Sgonzo	.putp			= bcmfb_putp,
416239922Sgonzo	.putc			= bcmfb_putc,
417239922Sgonzo	.puts			= bcmfb_puts,
418239922Sgonzo	.putm			= bcmfb_putm,
419239922Sgonzo};
420239922Sgonzo
421239922SgonzoVIDEO_DRIVER(bcmfb, bcmfbvidsw, bcmfb_configure);
422239922Sgonzo
423239922Sgonzoextern sc_rndr_sw_t txtrndrsw;
424239922SgonzoRENDERER(bcmfb, 0, txtrndrsw, gfb_set);
425239922SgonzoRENDERER_MODULE(bcmfb, gfb_set);
426239922Sgonzo
427239922Sgonzostatic uint16_t bcmfb_static_window[ROW*COL];
428239922Sgonzoextern u_char dflt_font_16[];
429239922Sgonzo
430243423Sgonzo/*
431243423Sgonzo * Update videoadapter settings after changing resolution
432243423Sgonzo */
433243423Sgonzostatic void
434243423Sgonzobcmfb_update_margins(video_adapter_t *adp)
435243423Sgonzo{
436243423Sgonzo	struct video_adapter_softc *sc;
437243423Sgonzo	video_info_t *vi;
438243423Sgonzo
439243423Sgonzo	sc = (struct video_adapter_softc *)adp;
440243423Sgonzo	vi = &adp->va_info;
441243423Sgonzo
442243423Sgonzo	sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
443243423Sgonzo	sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
444243423Sgonzo}
445243423Sgonzo
446239922Sgonzostatic int
447239922Sgonzobcmfb_configure(int flags)
448239922Sgonzo{
449243423Sgonzo	struct video_adapter_softc *va_sc;
450239922Sgonzo
451243423Sgonzo	va_sc = &va_softc;
452243423Sgonzo	phandle_t display, root;
453243423Sgonzo	pcell_t cell;
454239922Sgonzo
455243423Sgonzo	if (va_sc->initialized)
456243423Sgonzo		return (0);
457239922Sgonzo
458243423Sgonzo	va_sc->width = 0;
459243423Sgonzo	va_sc->height = 0;
460239922Sgonzo
461243423Sgonzo	/*
462243423Sgonzo	 * It seems there is no way to let syscons framework know
463243423Sgonzo	 * that framebuffer resolution has changed. So just try
464243423Sgonzo	 * to fetch data from FDT and go with defaults if failed
465243423Sgonzo	 */
466243423Sgonzo	root = OF_finddevice("/");
467243423Sgonzo	if ((root != 0) &&
468243423Sgonzo	    (display = fdt_find_compatible(root, "broadcom,bcm2835-fb", 1))) {
469243423Sgonzo		if ((OF_getprop(display, "broadcom,width",
470243423Sgonzo		    &cell, sizeof(cell))) > 0)
471243423Sgonzo			va_sc->width = (int)fdt32_to_cpu(cell);
472239922Sgonzo
473243423Sgonzo		if ((OF_getprop(display, "broadcom,height",
474243423Sgonzo		    &cell, sizeof(cell))) > 0)
475243423Sgonzo			va_sc->height = (int)fdt32_to_cpu(cell);
476243423Sgonzo	}
477239922Sgonzo
478243423Sgonzo	if (va_sc->width == 0)
479243423Sgonzo		va_sc->width = FB_WIDTH;
480243423Sgonzo	if (va_sc->height == 0)
481243423Sgonzo		va_sc->height = FB_HEIGHT;
482243423Sgonzo
483243423Sgonzo	bcmfb_init(0, &va_sc->va, 0);
484243423Sgonzo
485243423Sgonzo	va_sc->initialized = 1;
486243423Sgonzo
487239922Sgonzo	return (0);
488239922Sgonzo}
489239922Sgonzo
490239922Sgonzostatic int
491239922Sgonzobcmfb_probe(int unit, video_adapter_t **adp, void *arg, int flags)
492239922Sgonzo{
493239922Sgonzo
494239922Sgonzo	return (0);
495239922Sgonzo}
496239922Sgonzo
497239922Sgonzostatic int
498239922Sgonzobcmfb_init(int unit, video_adapter_t *adp, int flags)
499239922Sgonzo{
500239922Sgonzo	struct video_adapter_softc *sc;
501239922Sgonzo	video_info_t *vi;
502239922Sgonzo
503239922Sgonzo	sc = (struct video_adapter_softc *)adp;
504239922Sgonzo	vi = &adp->va_info;
505239922Sgonzo
506239922Sgonzo	vid_init_struct(adp, "bcmfb", -1, unit);
507239922Sgonzo
508239922Sgonzo	sc->font = dflt_font_16;
509239922Sgonzo	vi->vi_cheight = BCMFB_FONT_HEIGHT;
510239922Sgonzo	vi->vi_cwidth = 8;
511243423Sgonzo
512239922Sgonzo	vi->vi_width = sc->width/8;
513239922Sgonzo	vi->vi_height = sc->height/vi->vi_cheight;
514239922Sgonzo
515239922Sgonzo	/*
516239922Sgonzo	 * Clamp width/height to syscons maximums
517239922Sgonzo	 */
518239922Sgonzo	if (vi->vi_width > COL)
519239922Sgonzo		vi->vi_width = COL;
520239922Sgonzo	if (vi->vi_height > ROW)
521239922Sgonzo		vi->vi_height = ROW;
522239922Sgonzo
523239922Sgonzo	sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2;
524239922Sgonzo	sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2;
525239922Sgonzo
526243423Sgonzo
527239922Sgonzo	adp->va_window = (vm_offset_t) bcmfb_static_window;
528239922Sgonzo	adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */;
529239922Sgonzo
530239922Sgonzo	vid_register(&sc->va);
531239922Sgonzo
532239922Sgonzo	return (0);
533239922Sgonzo}
534239922Sgonzo
535239922Sgonzostatic int
536239922Sgonzobcmfb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
537239922Sgonzo{
538239922Sgonzo	bcopy(&adp->va_info, info, sizeof(*info));
539239922Sgonzo	return (0);
540239922Sgonzo}
541239922Sgonzo
542239922Sgonzostatic int
543239922Sgonzobcmfb_query_mode(video_adapter_t *adp, video_info_t *info)
544239922Sgonzo{
545239922Sgonzo	return (0);
546239922Sgonzo}
547239922Sgonzo
548239922Sgonzostatic int
549239922Sgonzobcmfb_set_mode(video_adapter_t *adp, int mode)
550239922Sgonzo{
551239922Sgonzo	return (0);
552239922Sgonzo}
553239922Sgonzo
554239922Sgonzostatic int
555239922Sgonzobcmfb_save_font(video_adapter_t *adp, int page, int size, int width,
556239922Sgonzo    u_char *data, int c, int count)
557239922Sgonzo{
558239922Sgonzo	return (0);
559239922Sgonzo}
560239922Sgonzo
561239922Sgonzostatic int
562239922Sgonzobcmfb_load_font(video_adapter_t *adp, int page, int size, int width,
563239922Sgonzo    u_char *data, int c, int count)
564239922Sgonzo{
565239922Sgonzo	struct video_adapter_softc *sc = (struct video_adapter_softc *)adp;
566239922Sgonzo
567239922Sgonzo	sc->font = data;
568239922Sgonzo
569239922Sgonzo	return (0);
570239922Sgonzo}
571239922Sgonzo
572239922Sgonzostatic int
573239922Sgonzobcmfb_show_font(video_adapter_t *adp, int page)
574239922Sgonzo{
575239922Sgonzo	return (0);
576239922Sgonzo}
577239922Sgonzo
578239922Sgonzostatic int
579239922Sgonzobcmfb_save_palette(video_adapter_t *adp, u_char *palette)
580239922Sgonzo{
581239922Sgonzo	return (0);
582239922Sgonzo}
583239922Sgonzo
584239922Sgonzostatic int
585239922Sgonzobcmfb_load_palette(video_adapter_t *adp, u_char *palette)
586239922Sgonzo{
587239922Sgonzo	return (0);
588239922Sgonzo}
589239922Sgonzo
590239922Sgonzostatic int
591239922Sgonzobcmfb_set_border(video_adapter_t *adp, int border)
592239922Sgonzo{
593239922Sgonzo	return (bcmfb_blank_display(adp, border));
594239922Sgonzo}
595239922Sgonzo
596239922Sgonzostatic int
597239922Sgonzobcmfb_save_state(video_adapter_t *adp, void *p, size_t size)
598239922Sgonzo{
599239922Sgonzo	return (0);
600239922Sgonzo}
601239922Sgonzo
602239922Sgonzostatic int
603239922Sgonzobcmfb_load_state(video_adapter_t *adp, void *p)
604239922Sgonzo{
605239922Sgonzo	return (0);
606239922Sgonzo}
607239922Sgonzo
608239922Sgonzostatic int
609239922Sgonzobcmfb_set_win_org(video_adapter_t *adp, off_t offset)
610239922Sgonzo{
611239922Sgonzo	return (0);
612239922Sgonzo}
613239922Sgonzo
614239922Sgonzostatic int
615239922Sgonzobcmfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
616239922Sgonzo{
617239922Sgonzo	*col = *row = 0;
618239922Sgonzo
619239922Sgonzo	return (0);
620239922Sgonzo}
621239922Sgonzo
622239922Sgonzostatic int
623239922Sgonzobcmfb_set_hw_cursor(video_adapter_t *adp, int col, int row)
624239922Sgonzo{
625239922Sgonzo	return (0);
626239922Sgonzo}
627239922Sgonzo
628239922Sgonzostatic int
629239922Sgonzobcmfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
630239922Sgonzo    int celsize, int blink)
631239922Sgonzo{
632239922Sgonzo	return (0);
633239922Sgonzo}
634239922Sgonzo
635239922Sgonzostatic int
636239922Sgonzobcmfb_blank_display(video_adapter_t *adp, int mode)
637239922Sgonzo{
638239922Sgonzo
639243423Sgonzo	struct video_adapter_softc *sc;
640243423Sgonzo
641243423Sgonzo	sc = (struct video_adapter_softc *)adp;
642243423Sgonzo	if (sc && sc->fb_addr)
643243423Sgonzo		memset((void*)sc->fb_addr, 0, sc->fb_size);
644243423Sgonzo
645239922Sgonzo	return (0);
646239922Sgonzo}
647239922Sgonzo
648239922Sgonzostatic int
649239922Sgonzobcmfb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
650239922Sgonzo    int prot, vm_memattr_t *memattr)
651239922Sgonzo{
652239922Sgonzo	struct video_adapter_softc *sc;
653239922Sgonzo
654239922Sgonzo	sc = (struct video_adapter_softc *)adp;
655239922Sgonzo
656239922Sgonzo	/*
657239922Sgonzo	 * This might be a legacy VGA mem request: if so, just point it at the
658239922Sgonzo	 * framebuffer, since it shouldn't be touched
659239922Sgonzo	 */
660239922Sgonzo	if (offset < sc->stride*sc->height) {
661239922Sgonzo		*paddr = sc->fb_addr + offset;
662239922Sgonzo		return (0);
663239922Sgonzo	}
664239922Sgonzo
665239922Sgonzo	return (EINVAL);
666239922Sgonzo}
667239922Sgonzo
668239922Sgonzostatic int
669239922Sgonzobcmfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data)
670239922Sgonzo{
671239922Sgonzo
672239922Sgonzo	return (0);
673239922Sgonzo}
674239922Sgonzo
675239922Sgonzostatic int
676239922Sgonzobcmfb_clear(video_adapter_t *adp)
677239922Sgonzo{
678239922Sgonzo
679239922Sgonzo	return (bcmfb_blank_display(adp, 0));
680239922Sgonzo}
681239922Sgonzo
682239922Sgonzostatic int
683239922Sgonzobcmfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
684239922Sgonzo{
685239922Sgonzo
686239922Sgonzo	return (0);
687239922Sgonzo}
688239922Sgonzo
689239922Sgonzostatic int
690239922Sgonzobcmfb_bitblt(video_adapter_t *adp, ...)
691239922Sgonzo{
692239922Sgonzo
693239922Sgonzo	return (0);
694239922Sgonzo}
695239922Sgonzo
696239922Sgonzostatic int
697239922Sgonzobcmfb_diag(video_adapter_t *adp, int level)
698239922Sgonzo{
699239922Sgonzo
700239922Sgonzo	return (0);
701239922Sgonzo}
702239922Sgonzo
703239922Sgonzostatic int
704239922Sgonzobcmfb_save_cursor_palette(video_adapter_t *adp, u_char *palette)
705239922Sgonzo{
706239922Sgonzo
707239922Sgonzo	return (0);
708239922Sgonzo}
709239922Sgonzo
710239922Sgonzostatic int
711239922Sgonzobcmfb_load_cursor_palette(video_adapter_t *adp, u_char *palette)
712239922Sgonzo{
713239922Sgonzo
714239922Sgonzo	return (0);
715239922Sgonzo}
716239922Sgonzo
717239922Sgonzostatic int
718239922Sgonzobcmfb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n)
719239922Sgonzo{
720239922Sgonzo
721239922Sgonzo	return (0);
722239922Sgonzo}
723239922Sgonzo
724239922Sgonzostatic int
725239922Sgonzobcmfb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a,
726239922Sgonzo    int size, int bpp, int bit_ltor, int byte_ltor)
727239922Sgonzo{
728239922Sgonzo
729239922Sgonzo	return (0);
730239922Sgonzo}
731239922Sgonzo
732239922Sgonzostatic int
733239922Sgonzobcmfb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a)
734239922Sgonzo{
735239922Sgonzo	struct video_adapter_softc *sc;
736239922Sgonzo	int row;
737239922Sgonzo	int col;
738239922Sgonzo	int i, j, k;
739239922Sgonzo	uint8_t *addr;
740239922Sgonzo	u_char *p;
741239922Sgonzo	uint8_t fg, bg, color;
742243423Sgonzo	uint16_t rgb;
743239922Sgonzo
744239922Sgonzo	sc = (struct video_adapter_softc *)adp;
745239922Sgonzo
746239922Sgonzo	if (sc->fb_addr == 0)
747239922Sgonzo		return (0);
748239922Sgonzo
749239922Sgonzo	row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
750239922Sgonzo	col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
751239922Sgonzo	p = sc->font + c*BCMFB_FONT_HEIGHT;
752239922Sgonzo	addr = (uint8_t *)sc->fb_addr
753239922Sgonzo	    + (row + sc->ymargin)*(sc->stride)
754243666Sgonzo	    + (sc->depth/8) * (col + sc->xmargin);
755239922Sgonzo
756243423Sgonzo	fg = a & 0xf ;
757243423Sgonzo	bg = (a >> 8) & 0xf;
758239922Sgonzo
759239922Sgonzo	for (i = 0; i < BCMFB_FONT_HEIGHT; i++) {
760239922Sgonzo		for (j = 0, k = 7; j < 8; j++, k--) {
761239922Sgonzo			if ((p[i] & (1 << k)) == 0)
762239922Sgonzo				color = bg;
763239922Sgonzo			else
764239922Sgonzo				color = fg;
765239922Sgonzo
766243423Sgonzo			switch (sc->depth) {
767243423Sgonzo			case 32:
768243423Sgonzo				addr[4*j+0] = bcmfb_palette[color].r;
769243423Sgonzo				addr[4*j+1] = bcmfb_palette[color].g;
770243423Sgonzo				addr[4*j+2] = bcmfb_palette[color].b;
771243423Sgonzo				addr[4*j+3] = bcmfb_palette[color].a;
772243423Sgonzo				break;
773243423Sgonzo			case 24:
774243423Sgonzo				addr[3*j] = bcmfb_palette[color].r;
775243423Sgonzo				addr[3*j+1] = bcmfb_palette[color].g;
776243423Sgonzo				addr[3*j+2] = bcmfb_palette[color].b;
777243423Sgonzo				break;
778243423Sgonzo			case 16:
779243423Sgonzo				rgb = (bcmfb_palette[color].r >> 3) << 10;
780243423Sgonzo				rgb |= (bcmfb_palette[color].g >> 3) << 5;
781243423Sgonzo				rgb |= (bcmfb_palette[color].b >> 3);
782243423Sgonzo				addr[2*j] = (rgb >> 8) & 0xff;
783243423Sgonzo				addr[2*j + 1] = rgb & 0xff;
784243423Sgonzo			default:
785243423Sgonzo				/* Not supported yet */
786243423Sgonzo				break;
787243423Sgonzo			}
788239922Sgonzo		}
789239922Sgonzo
790239922Sgonzo		addr += (sc->stride);
791239922Sgonzo	}
792239922Sgonzo
793239922Sgonzo        return (0);
794239922Sgonzo}
795239922Sgonzo
796239922Sgonzostatic int
797239922Sgonzobcmfb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len)
798239922Sgonzo{
799239922Sgonzo	int i;
800239922Sgonzo
801239922Sgonzo	for (i = 0; i < len; i++)
802239922Sgonzo		bcmfb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8);
803239922Sgonzo
804239922Sgonzo	return (0);
805239922Sgonzo}
806239922Sgonzo
807239922Sgonzostatic int
808239922Sgonzobcmfb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image,
809239922Sgonzo    uint32_t pixel_mask, int size, int width)
810239922Sgonzo{
811239922Sgonzo
812239922Sgonzo	return (0);
813239922Sgonzo}
814239922Sgonzo
815239922Sgonzo/*
816239922Sgonzo * Define a stub keyboard driver in case one hasn't been
817239922Sgonzo * compiled into the kernel
818239922Sgonzo */
819239922Sgonzo#include <sys/kbio.h>
820239922Sgonzo#include <dev/kbd/kbdreg.h>
821239922Sgonzo
822239922Sgonzostatic int dummy_kbd_configure(int flags);
823239922Sgonzo
824239922Sgonzokeyboard_switch_t bcmdummysw;
825239922Sgonzo
826239922Sgonzostatic int
827239922Sgonzodummy_kbd_configure(int flags)
828239922Sgonzo{
829239922Sgonzo
830239922Sgonzo	return (0);
831239922Sgonzo}
832239922SgonzoKEYBOARD_DRIVER(bcmdummy, bcmdummysw, dummy_kbd_configure);
833