]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/vt/hw/ofwfb/ofwfb.c
MFC 262785 263183 264182 264999 265391 265392 265395 265397 265398 265402 265403
[FreeBSD/stable/10.git] / sys / dev / vt / hw / ofwfb / ofwfb.c
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