xref: /freebsd/sys/dev/vt/hw/ofwfb/ofwfb.c (revision e45764721aedfa6460e1767664864bda9457c10e)
1 /*-
2  * Copyright (c) 2011 Nathan Whitehorn
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/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/fbio.h>
34 
35 #include <dev/vt/vt.h>
36 #include <dev/vt/hw/fb/vt_fb.h>
37 #include <dev/vt/colors/vt_termcolors.h>
38 
39 #include <vm/vm.h>
40 #include <vm/pmap.h>
41 
42 #include <machine/bus.h>
43 #ifdef __sparc64__
44 #include <machine/bus_private.h>
45 #endif
46 
47 #include <dev/ofw/openfirm.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_pci.h>
50 
51 struct ofwfb_softc {
52 	struct fb_info	fb;
53 
54 	phandle_t	sc_node;
55 	ihandle_t	sc_handle;
56 	bus_space_tag_t	sc_memt;
57 };
58 
59 static vd_probe_t	ofwfb_probe;
60 static vd_init_t	ofwfb_init;
61 static vd_bitbltchr_t	ofwfb_bitbltchr;
62 
63 static const struct vt_driver vt_ofwfb_driver = {
64 	.vd_name	= "ofwfb",
65 	.vd_probe	= ofwfb_probe,
66 	.vd_init	= ofwfb_init,
67 	.vd_blank	= vt_fb_blank,
68 	.vd_bitbltchr	= ofwfb_bitbltchr,
69 	.vd_fb_ioctl	= vt_fb_ioctl,
70 	.vd_fb_mmap	= vt_fb_mmap,
71 	.vd_priority	= VD_PRIORITY_GENERIC+1,
72 };
73 
74 static struct ofwfb_softc ofwfb_conssoftc;
75 VT_DRIVER_DECLARE(vt_ofwfb, vt_ofwfb_driver);
76 
77 static int
78 ofwfb_probe(struct vt_device *vd)
79 {
80 	phandle_t chosen, node;
81 	ihandle_t stdout;
82 	char type[64];
83 
84 	chosen = OF_finddevice("/chosen");
85 	OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
86 	node = OF_instance_to_package(stdout);
87 	if (node == -1) {
88 		/*
89 		 * The "/chosen/stdout" does not exist try
90 		 * using "screen" directly.
91 		 */
92 		node = OF_finddevice("screen");
93 	}
94 	OF_getprop(node, "device_type", type, sizeof(type));
95 	if (strcmp(type, "display") != 0)
96 		return (CN_DEAD);
97 
98 	/* Looks OK... */
99 	return (CN_INTERNAL);
100 }
101 
102 static void
103 ofwfb_bitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
104     int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
105     unsigned int height, term_color_t fg, term_color_t bg)
106 {
107 	struct fb_info *sc = vd->vd_softc;
108 	u_long line;
109 	uint32_t fgc, bgc;
110 	int c;
111 	uint8_t b, m;
112 	union {
113 		uint32_t l;
114 		uint8_t	 c[4];
115 	} ch1, ch2;
116 
117 	fgc = sc->fb_cmap[fg];
118 	bgc = sc->fb_cmap[bg];
119 	b = m = 0;
120 
121 	/* Don't try to put off screen pixels */
122 	if (((left + width) > vd->vd_width) || ((top + height) >
123 	    vd->vd_height))
124 		return;
125 
126 	line = (sc->fb_stride * top) + left * sc->fb_bpp/8;
127 	if (mask == NULL && sc->fb_bpp == 8 && (width % 8 == 0)) {
128 		for (; height > 0; height--) {
129 			for (c = 0; c < width; c += 8) {
130 				b = *src++;
131 
132 				/*
133 				 * Assume that there is more background than
134 				 * foreground in characters and init accordingly
135 				 */
136 				ch1.l = ch2.l = (bg << 24) | (bg << 16) |
137 				    (bg << 8) | bg;
138 
139 				/*
140 				 * Calculate 2 x 4-chars at a time, and then
141 				 * write these out.
142 				 */
143 				if (b & 0x80) ch1.c[0] = fg;
144 				if (b & 0x40) ch1.c[1] = fg;
145 				if (b & 0x20) ch1.c[2] = fg;
146 				if (b & 0x10) ch1.c[3] = fg;
147 
148 				if (b & 0x08) ch2.c[0] = fg;
149 				if (b & 0x04) ch2.c[1] = fg;
150 				if (b & 0x02) ch2.c[2] = fg;
151 				if (b & 0x01) ch2.c[3] = fg;
152 
153 				*(uint32_t *)(sc->fb_vbase + line + c) = ch1.l;
154 				*(uint32_t *)(sc->fb_vbase + line + c + 4) =
155 				    ch2.l;
156 			}
157 			line += sc->fb_stride;
158 		}
159 	} else {
160 		for (; height > 0; height--) {
161 			for (c = 0; c < width; c++) {
162 				if (c % 8 == 0)
163 					b = *src++;
164 				else
165 					b <<= 1;
166 				if (mask != NULL) {
167 					if (c % 8 == 0)
168 						m = *mask++;
169 					else
170 						m <<= 1;
171 					/* Skip pixel write, if mask not set. */
172 					if ((m & 0x80) == 0)
173 						continue;
174 				}
175 				switch(sc->fb_bpp) {
176 				case 8:
177 					*(uint8_t *)(sc->fb_vbase + line + c) =
178 					    b & 0x80 ? fg : bg;
179 					break;
180 				case 32:
181 					*(uint32_t *)(sc->fb_vbase + line + 4*c)
182 					    = (b & 0x80) ? fgc : bgc;
183 					break;
184 				default:
185 					/* panic? */
186 					break;
187 				}
188 			}
189 			line += sc->fb_stride;
190 		}
191 	}
192 }
193 
194 static void
195 ofwfb_initialize(struct vt_device *vd)
196 {
197 	struct ofwfb_softc *sc = vd->vd_softc;
198 	int i;
199 	cell_t retval;
200 	uint32_t oldpix;
201 
202 	/*
203 	 * Set up the color map
204 	 */
205 
206 	switch (sc->fb.fb_bpp) {
207 	case 8:
208 		vt_generate_cons_palette(sc->fb.fb_cmap, COLOR_FORMAT_RGB, 255,
209 		    16, 255, 8, 255, 0);
210 
211 		for (i = 0; i < 16; i++) {
212 			OF_call_method("color!", sc->sc_handle, 4, 1,
213 			    (cell_t)((sc->fb.fb_cmap[i] >> 16) & 0xff),
214 			    (cell_t)((sc->fb.fb_cmap[i] >> 8) & 0xff),
215 			    (cell_t)((sc->fb.fb_cmap[i] >> 0) & 0xff),
216 			    (cell_t)i, &retval);
217 		}
218 		break;
219 
220 	case 32:
221 		/*
222 		 * We bypass the usual bus_space_() accessors here, mostly
223 		 * for performance reasons. In particular, we don't want
224 		 * any barrier operations that may be performed and handle
225 		 * endianness slightly different. Figure out the host-view
226 		 * endianness of the frame buffer.
227 		 */
228 		oldpix = bus_space_read_4(sc->sc_memt, sc->fb.fb_vbase, 0);
229 		bus_space_write_4(sc->sc_memt, sc->fb.fb_vbase, 0, 0xff000000);
230 		if (*(uint8_t *)(sc->fb.fb_vbase) == 0xff)
231 			vt_generate_cons_palette(sc->fb.fb_cmap,
232 			    COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
233 		else
234 			vt_generate_cons_palette(sc->fb.fb_cmap,
235 			    COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
236 		bus_space_write_4(sc->sc_memt, sc->fb.fb_vbase, 0, oldpix);
237 		break;
238 
239 	default:
240 		panic("Unknown color space depth %d", sc->fb.fb_bpp);
241 		break;
242         }
243 
244 	sc->fb.fb_cmsize = 16;
245 }
246 
247 static int
248 ofwfb_init(struct vt_device *vd)
249 {
250 	struct ofwfb_softc *sc;
251 	char type[64];
252 	phandle_t chosen;
253 	phandle_t node;
254 	uint32_t depth, height, width, stride;
255 	uint32_t fb_phys;
256 	int i, len;
257 #ifdef __sparc64__
258 	static struct bus_space_tag ofwfb_memt[1];
259 	bus_addr_t phys;
260 	int space;
261 #endif
262 
263 	/* Initialize softc */
264 	vd->vd_softc = sc = &ofwfb_conssoftc;
265 
266 	chosen = OF_finddevice("/chosen");
267 	OF_getprop(chosen, "stdout", &sc->sc_handle, sizeof(ihandle_t));
268 	node = OF_instance_to_package(sc->sc_handle);
269 	if (node == -1) {
270 		/*
271 		 * The "/chosen/stdout" does not exist try
272 		 * using "screen" directly.
273 		 */
274 		node = OF_finddevice("screen");
275 		sc->sc_handle = OF_open("screen");
276 	}
277 	OF_getprop(node, "device_type", type, sizeof(type));
278 	if (strcmp(type, "display") != 0)
279 		return (CN_DEAD);
280 
281 	/* Keep track of the OF node */
282 	sc->sc_node = node;
283 
284 	/*
285 	 * Try to use a 32-bit framebuffer if possible. This may be
286 	 * unimplemented and fail. That's fine -- it just means we are
287 	 * stuck with the defaults.
288 	 */
289 	OF_call_method("set-depth", sc->sc_handle, 1, 1, (cell_t)32, &i);
290 
291 	/* Make sure we have needed properties */
292 	if (OF_getproplen(node, "height") != sizeof(height) ||
293 	    OF_getproplen(node, "width") != sizeof(width) ||
294 	    OF_getproplen(node, "depth") != sizeof(depth) ||
295 	    OF_getproplen(node, "linebytes") != sizeof(sc->fb.fb_stride))
296 		return (CN_DEAD);
297 
298 	/* Only support 8 and 32-bit framebuffers */
299 	OF_getprop(node, "depth", &depth, sizeof(depth));
300 	if (depth != 8 && depth != 32)
301 		return (CN_DEAD);
302 	sc->fb.fb_bpp = sc->fb.fb_depth = depth;
303 
304 	OF_getprop(node, "height", &height, sizeof(height));
305 	OF_getprop(node, "width", &width, sizeof(width));
306 	OF_getprop(node, "linebytes", &stride, sizeof(stride));
307 
308 	sc->fb.fb_height = height;
309 	sc->fb.fb_width = width;
310 	sc->fb.fb_stride = stride;
311 	sc->fb.fb_size = sc->fb.fb_height * sc->fb.fb_stride;
312 
313 	/*
314 	 * Grab the physical address of the framebuffer, and then map it
315 	 * into our memory space. If the MMU is not yet up, it will be
316 	 * remapped for us when relocation turns on.
317 	 */
318 	if (OF_getproplen(node, "address") == sizeof(fb_phys)) {
319 	 	/* XXX We assume #address-cells is 1 at this point. */
320 		OF_getprop(node, "address", &fb_phys, sizeof(fb_phys));
321 
322 	#if defined(__powerpc__)
323 		sc->sc_memt = &bs_be_tag;
324 		bus_space_map(sc->sc_memt, fb_phys, sc->fb.fb_size,
325 		    BUS_SPACE_MAP_PREFETCHABLE, &sc->fb.fb_vbase);
326 	#elif defined(__sparc64__)
327 		OF_decode_addr(node, 0, &space, &phys);
328 		sc->sc_memt = &ofwfb_memt[0];
329 		sc->fb.fb_vbase =
330 		    sparc64_fake_bustag(space, fb_phys, sc->sc_memt);
331 	#elif defined(__arm__)
332 		sc->sc_memt = fdtbus_bs_tag;
333 		bus_space_map(sc->sc_memt, sc->fb.fb_pbase, sc->fb.fb_size,
334 		    BUS_SPACE_MAP_PREFETCHABLE,
335 		    (bus_space_handle_t *)&sc->fb.fb_vbase);
336 	#else
337 		#error Unsupported platform!
338 	#endif
339 
340 		sc->fb.fb_pbase = fb_phys;
341 	} else {
342 		/*
343 		 * Some IBM systems don't have an address property. Try to
344 		 * guess the framebuffer region from the assigned addresses.
345 		 * This is ugly, but there doesn't seem to be an alternative.
346 		 * Linux does the same thing.
347 		 */
348 
349 		struct ofw_pci_register pciaddrs[8];
350 		int num_pciaddrs = 0;
351 
352 		/*
353 		 * Get the PCI addresses of the adapter, if present. The node
354 		 * may be the child of the PCI device: in that case, try the
355 		 * parent for the assigned-addresses property.
356 		 */
357 		len = OF_getprop(node, "assigned-addresses", pciaddrs,
358 		    sizeof(pciaddrs));
359 		if (len == -1) {
360 			len = OF_getprop(OF_parent(node), "assigned-addresses",
361 			    pciaddrs, sizeof(pciaddrs));
362 		}
363 		if (len == -1)
364 			len = 0;
365 		num_pciaddrs = len / sizeof(struct ofw_pci_register);
366 
367 		fb_phys = num_pciaddrs;
368 		for (i = 0; i < num_pciaddrs; i++) {
369 			/* If it is too small, not the framebuffer */
370 			if (pciaddrs[i].size_lo < sc->fb.fb_stride * height)
371 				continue;
372 			/* If it is not memory, it isn't either */
373 			if (!(pciaddrs[i].phys_hi &
374 			    OFW_PCI_PHYS_HI_SPACE_MEM32))
375 				continue;
376 
377 			/* This could be the framebuffer */
378 			fb_phys = i;
379 
380 			/* If it is prefetchable, it certainly is */
381 			if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
382 				break;
383 		}
384 
385 		if (fb_phys == num_pciaddrs) /* No candidates found */
386 			return (CN_DEAD);
387 
388 	#if defined(__powerpc__)
389 		OF_decode_addr(node, fb_phys, &sc->sc_memt, &sc->fb.fb_vbase);
390 		sc->fb.fb_pbase = sc->fb.fb_vbase; /* 1:1 mapped */
391 	#else
392 		/* No ability to interpret assigned-addresses otherwise */
393 		return (CN_DEAD);
394 	#endif
395         }
396 
397 
398 	ofwfb_initialize(vd);
399 	vt_fb_init(vd);
400 
401 	return (CN_INTERNAL);
402 }
403 
404