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