]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/vt/hw/fb/vt_early_fb.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / vt / hw / fb / vt_early_fb.c
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Aleksandr Rybalko under sponsorship from the
6  * FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/fbio.h>
39
40 #include "opt_platform.h"
41
42 #ifdef  FDT
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/ofw/ofw_pci.h>
47 #include <machine/fdt.h>
48 #endif
49
50 #include <dev/vt/vt.h>
51 #include <dev/vt/hw/fb/vt_fb.h>
52 #include <dev/vt/colors/vt_termcolors.h>
53
54 static vd_init_t vt_efb_init;
55 static vd_probe_t vt_efb_probe;
56
57 static struct vt_driver vt_fb_early_driver = {
58         .vd_name = "efb",
59         .vd_probe = vt_efb_probe,
60         .vd_init = vt_efb_init,
61         .vd_blank = vt_fb_blank,
62         .vd_bitblt_text = vt_fb_bitblt_text,
63         .vd_bitblt_bmp = vt_fb_bitblt_bitmap,
64         .vd_drawrect = vt_fb_drawrect,
65         .vd_setpixel = vt_fb_setpixel,
66         .vd_priority = VD_PRIORITY_GENERIC,
67 };
68
69 static struct fb_info local_info;
70 VT_DRIVER_DECLARE(vt_efb, vt_fb_early_driver);
71
72 static void
73 #ifdef  FDT
74 vt_efb_initialize(struct fb_info *info, phandle_t node)
75 #else
76 vt_efb_initialize(struct fb_info *info)
77 #endif
78 {
79 #ifdef  FDT
80         char name[64];
81         cell_t retval;
82         ihandle_t ih;
83         int i;
84
85         /* Open display device, thereby initializing it */
86         memset(name, 0, sizeof(name));
87         OF_package_to_path(node, name, sizeof(name));
88         ih = OF_open(name);
89 #endif
90
91         /*
92          * Set up the color map
93          */
94         switch (info->fb_depth) {
95         case 8:
96                 vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
97                     0x7, 5, 0x7, 2, 0x3, 0);
98                 break;
99         case 15:
100                 vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
101                     0x1f, 10, 0x1f, 5, 0x1f, 0);
102                 break;
103         case 16:
104                 vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
105                     0x1f, 11, 0x3f, 5, 0x1f, 0);
106                 break;
107         case 24:
108         case 32:
109 #if BYTE_ORDER == BIG_ENDIAN
110                 vt_generate_cons_palette(info->fb_cmap,
111                     COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
112 #else
113                 vt_generate_cons_palette(info->fb_cmap,
114                     COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
115 #endif
116 #ifdef  FDT
117                 for (i = 0; i < 16; i++) {
118                         OF_call_method("color!", ih, 4, 1,
119                             (cell_t)((info->fb_cmap[i] >> 16) & 0xff),
120                             (cell_t)((info->fb_cmap[i] >> 8) & 0xff),
121                             (cell_t)((info->fb_cmap[i] >> 0) & 0xff),
122                             (cell_t)i, &retval);
123                 }
124 #endif
125                 break;
126
127         default:
128                 panic("Unknown color space fb_depth %d", info->fb_depth);
129                 break;
130         }
131 }
132
133 static phandle_t
134 vt_efb_get_fbnode()
135 {
136         phandle_t chosen, node;
137         ihandle_t stdout;
138         char type[64];
139
140         chosen = OF_finddevice("/chosen");
141         OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
142         node = OF_instance_to_package(stdout);
143         if (node != -1) {
144                 /* The "/chosen/stdout" present. */
145                 OF_getprop(node, "device_type", type, sizeof(type));
146                 /* Check if it has "display" type. */
147                 if (strcmp(type, "display") == 0)
148                         return (node);
149         }
150         /* Try device with name "screen". */
151         node = OF_finddevice("screen");
152
153         return (node);
154 }
155
156 static int
157 vt_efb_probe(struct vt_device *vd)
158 {
159         phandle_t node;
160
161         node = vt_efb_get_fbnode();
162         if (node == -1)
163                 return (CN_DEAD);
164
165         if ((OF_getproplen(node, "height") <= 0) ||
166             (OF_getproplen(node, "width") <= 0) ||
167             (OF_getproplen(node, "depth") <= 0) ||
168             (OF_getproplen(node, "linebytes") <= 0))
169                 return (CN_DEAD);
170
171         return (CN_INTERNAL);
172 }
173
174 static int
175 vt_efb_init(struct vt_device *vd)
176 {
177         struct ofw_pci_register pciaddrs[8];
178         struct fb_info *info;
179         int i, len, n_pciaddrs;
180         phandle_t node;
181
182         if (vd->vd_softc == NULL)
183                 vd->vd_softc = (void *)&local_info;
184
185         info = vd->vd_softc;
186
187         node = vt_efb_get_fbnode();
188         if (node == -1)
189                 return (CN_DEAD);
190
191 #define GET(name, var)                                                  \
192         if (OF_getproplen(node, (name)) != sizeof(info->fb_##var))      \
193                 return (CN_DEAD);                                       \
194         OF_getencprop(node, (name), &info->fb_##var, sizeof(info->fb_##var)); \
195         if (info->fb_##var == 0)                                        \
196                 return (CN_DEAD);
197
198         GET("height", height)
199         GET("width", width)
200         GET("depth", depth)
201         GET("linebytes", stride)
202 #undef GET
203
204         info->fb_size = info->fb_height * info->fb_stride;
205
206         /*
207          * Get the PCI addresses of the adapter, if present. The node may be the
208          * child of the PCI device: in that case, try the parent for
209          * the assigned-addresses property.
210          */
211         len = OF_getprop(node, "assigned-addresses", pciaddrs,
212             sizeof(pciaddrs));
213         if (len == -1) {
214                 len = OF_getprop(OF_parent(node), "assigned-addresses",
215                     pciaddrs, sizeof(pciaddrs));
216         }
217         if (len == -1)
218                 len = 0;
219         n_pciaddrs = len / sizeof(struct ofw_pci_register);
220
221         /*
222          * Grab the physical address of the framebuffer, and then map it
223          * into our memory space. If the MMU is not yet up, it will be
224          * remapped for us when relocation turns on.
225          */
226         if (OF_getproplen(node, "address") == sizeof(info->fb_pbase)) {
227                 /* XXX We assume #address-cells is 1 at this point. */
228                 OF_getencprop(node, "address", &info->fb_pbase,
229                     sizeof(info->fb_pbase));
230
231         #if defined(__powerpc__)
232                 sc->sc_memt = &bs_be_tag;
233                 bus_space_map(sc->sc_memt, info->fb_pbase, info->fb_size,
234                     BUS_SPACE_MAP_PREFETCHABLE, &info->fb_vbase);
235         #elif defined(__sparc64__)
236                 OF_decode_addr(node, 0, &space, &phys);
237                 sc->sc_memt = &vt_efb_memt[0];
238                 info->addr = sparc64_fake_bustag(space, fb_phys, sc->sc_memt);
239         #else
240                 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
241                     BUS_SPACE_MAP_PREFETCHABLE,
242                     (bus_space_handle_t *)&info->fb_vbase);
243         #endif
244         } else {
245                 /*
246                  * Some IBM systems don't have an address property. Try to
247                  * guess the framebuffer region from the assigned addresses.
248                  * This is ugly, but there doesn't seem to be an alternative.
249                  * Linux does the same thing.
250                  */
251
252                 info->fb_pbase = n_pciaddrs;
253                 for (i = 0; i < n_pciaddrs; i++) {
254                         /* If it is too small, not the framebuffer */
255                         if (pciaddrs[i].size_lo < info->fb_size)
256                                 continue;
257                         /* If it is not memory, it isn't either */
258                         if (!(pciaddrs[i].phys_hi &
259                             OFW_PCI_PHYS_HI_SPACE_MEM32))
260                                 continue;
261
262                         /* This could be the framebuffer */
263                         info->fb_pbase = i;
264
265                         /* If it is prefetchable, it certainly is */
266                         if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
267                                 break;
268                 }
269
270                 if (info->fb_pbase == n_pciaddrs) /* No candidates found */
271                         return (CN_DEAD);
272
273         #if defined(__powerpc__)
274                 OF_decode_addr(node, info->fb_pbase, &sc->sc_memt,
275                     &info->fb_vbase);
276         #elif defined(__sparc64__)
277                 OF_decode_addr(node, info->fb_pbase, &space, &info->fb_pbase);
278                 sc->sc_memt = &vt_efb_memt[0];
279                 info->fb_vbase = sparc64_fake_bustag(space, info->fb_pbase,
280                     sc->sc_memt);
281         #else
282                 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
283                     BUS_SPACE_MAP_PREFETCHABLE,
284                     (bus_space_handle_t *)&info->fb_vbase);
285         #endif
286         }
287
288         /* blank full size */
289         len = info->fb_size / 4;
290         for (i = 0; i < len; i++) {
291                 ((uint32_t *)info->fb_vbase)[i] = 0;
292         }
293
294         /* Get pixel storage size. */
295         info->fb_bpp = info->fb_stride / info->fb_width * 8;
296
297 #ifdef  FDT
298         vt_efb_initialize(info, node);
299 #else
300         vt_efb_initialize(info);
301 #endif
302         vt_fb_init(vd);
303
304         return (CN_INTERNAL);
305 }