]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/vt/hw/ofwfb/ofwfb.c
MFC r269685 (nwhitehorn): Retire vd_maskbitbltchr.
[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 #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