1 /* $NetBSD: arm_simplefb.c,v 1.10 2021/03/02 11:51:00 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jared McNeill <jmcneill@invisible.ca>. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "pci.h" 33 #include "opt_pci.h" 34 #include "opt_vcons.h" 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: arm_simplefb.c,v 1.10 2021/03/02 11:51:00 jmcneill Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/bus.h> 41 #include <sys/cpu.h> 42 #include <sys/device.h> 43 44 #include <dev/fdt/fdtvar.h> 45 #include <arm/fdt/arm_fdtvar.h> 46 47 #include <dev/wscons/wsconsio.h> 48 #include <dev/wscons/wsdisplayvar.h> 49 #include <dev/rasops/rasops.h> 50 #include <dev/wsfont/wsfont.h> 51 #include <dev/wscons/wsdisplay_vconsvar.h> 52 53 #if NPCI > 0 && defined(PCI_NETBSD_CONFIGURE) 54 #include <dev/pci/pcireg.h> 55 #include <dev/pci/pcivar.h> 56 #include <dev/pci/pciconf.h> 57 #endif 58 59 #include <arm/fdt/arm_simplefb.h> 60 61 #include <libfdt.h> 62 63 extern struct bus_space arm_generic_bs_tag; 64 65 static struct arm_simplefb_softc { 66 uint32_t sc_width; 67 uint32_t sc_height; 68 uint32_t sc_stride; 69 uint16_t sc_depth; 70 bool sc_swapped; 71 void *sc_bits; 72 } arm_simplefb_softc; 73 74 static struct wsscreen_descr arm_simplefb_stdscreen = { 75 .name = "std", 76 .ncols = 0, 77 .nrows = 0, 78 .textops = NULL, 79 .fontwidth = 0, 80 .fontheight = 0, 81 .capabilities = 0, 82 .modecookie = NULL 83 }; 84 85 static struct wsdisplay_accessops arm_simplefb_accessops; 86 static struct vcons_data arm_simplefb_vcons_data; 87 static struct vcons_screen arm_simplefb_screen; 88 89 static bus_addr_t arm_simplefb_addr; 90 static bus_size_t arm_simplefb_size; 91 static bus_space_handle_t arm_simplefb_bsh; 92 93 static const struct device_compatible_entry compat_data[] = { 94 { .compat = "simple-framebuffer" }, 95 DEVICE_COMPAT_EOL 96 }; 97 98 static int 99 arm_simplefb_find_node(void) 100 { 101 int chosen_phandle, child; 102 103 chosen_phandle = OF_finddevice("/chosen"); 104 if (chosen_phandle == -1) 105 return -1; 106 107 for (child = OF_child(chosen_phandle); child; child = OF_peer(child)) { 108 if (!fdtbus_status_okay(child)) 109 continue; 110 if (!of_compatible_match(child, compat_data)) 111 continue; 112 113 return child; 114 } 115 116 return -1; 117 } 118 119 static void 120 arm_simplefb_init_screen(void *cookie, struct vcons_screen *scr, 121 int existing, long *defattr) 122 { 123 struct arm_simplefb_softc * const sc = cookie; 124 struct rasops_info *ri = &scr->scr_ri; 125 126 ri->ri_width = sc->sc_width; 127 ri->ri_height = sc->sc_height; 128 ri->ri_depth = sc->sc_depth; 129 ri->ri_stride = sc->sc_stride; 130 ri->ri_bits = sc->sc_bits; 131 ri->ri_flg = RI_CENTER | RI_FULLCLEAR | RI_CLEAR; 132 133 if (sc->sc_swapped) { 134 KASSERT(ri->ri_depth == 32); 135 ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8; 136 ri->ri_rpos = 8; 137 ri->ri_gpos = 16; 138 ri->ri_bpos = 24; 139 } 140 141 scr->scr_flags |= VCONS_LOADFONT; 142 scr->scr_flags |= VCONS_DONT_READ; 143 144 rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8); 145 ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE; 146 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 147 sc->sc_width / ri->ri_font->fontwidth); 148 149 ri->ri_hw = scr; 150 } 151 152 static int 153 arm_simplefb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 154 lwp_t *l) 155 { 156 return EPASSTHROUGH; 157 } 158 159 static paddr_t 160 arm_simplefb_mmap(void *v, void *vs, off_t offset, int prot) 161 { 162 return -1; 163 } 164 165 static void 166 arm_simplefb_pollc(void *v, int on) 167 { 168 } 169 170 #if NPCI > 0 && defined(PCI_NETBSD_CONFIGURE) 171 static void 172 arm_simplefb_reconfig(void *arg, uint64_t new_addr) 173 { 174 struct arm_simplefb_softc * const sc = &arm_simplefb_softc; 175 struct rasops_info *ri = &arm_simplefb_screen.scr_ri; 176 bus_space_tag_t bst = &arm_generic_bs_tag; 177 178 bus_space_unmap(bst, arm_simplefb_bsh, arm_simplefb_size); 179 bus_space_map(bst, new_addr, arm_simplefb_size, 180 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, 181 &arm_simplefb_bsh); 182 183 sc->sc_bits = bus_space_vaddr(bst, arm_simplefb_bsh); 184 ri->ri_bits = sc->sc_bits; 185 186 arm_simplefb_addr = (bus_addr_t)new_addr; 187 } 188 #endif 189 190 uint64_t 191 arm_simplefb_physaddr(void) 192 { 193 return arm_simplefb_addr; 194 } 195 196 void 197 arm_simplefb_preattach(void) 198 { 199 struct arm_simplefb_softc * const sc = &arm_simplefb_softc; 200 struct rasops_info *ri = &arm_simplefb_screen.scr_ri; 201 bus_space_tag_t bst = &arm_generic_bs_tag; 202 bus_space_handle_t bsh; 203 uint32_t width, height, stride; 204 const char *format; 205 bus_addr_t addr; 206 bus_size_t size; 207 uint16_t depth; 208 long defattr; 209 bool swapped = false; 210 211 const int phandle = arm_simplefb_find_node(); 212 if (phandle == -1) 213 return; 214 215 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0 || size == 0) 216 return; 217 218 if (of_getprop_uint32(phandle, "width", &width) != 0 || 219 of_getprop_uint32(phandle, "height", &height) != 0 || 220 of_getprop_uint32(phandle, "stride", &stride) != 0 || 221 (format = fdtbus_get_string(phandle, "format")) == NULL) 222 return; 223 224 if (width == 0 || height == 0) 225 return; 226 227 if (strcmp(format, "a8b8g8r8") == 0 || 228 strcmp(format, "x8r8g8b8") == 0) { 229 depth = 32; 230 } else if (strcmp(format, "r8g8b8a8") == 0 || 231 strcmp(format, "b8g8r8x8") == 0) { 232 depth = 32; 233 swapped = true; 234 } else if (strcmp(format, "r5g6b5") == 0) { 235 depth = 16; 236 } else { 237 return; 238 } 239 240 /* 241 * Make sure that the size of the linear FB mapping is big enough 242 * to fit the requested screen dimensions. 243 */ 244 if (size < stride * height) { 245 return; 246 } 247 248 if (bus_space_map(bst, addr, size, 249 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &bsh) != 0) 250 return; 251 252 arm_simplefb_addr = addr; 253 arm_simplefb_size = size; 254 arm_simplefb_bsh = bsh; 255 256 sc->sc_width = width; 257 sc->sc_height = height; 258 sc->sc_depth = depth; 259 sc->sc_stride = stride; 260 sc->sc_bits = bus_space_vaddr(bst, bsh); 261 sc->sc_swapped = swapped; 262 263 wsfont_init(); 264 265 arm_simplefb_accessops.ioctl = arm_simplefb_ioctl; 266 arm_simplefb_accessops.mmap = arm_simplefb_mmap; 267 arm_simplefb_accessops.pollc = arm_simplefb_pollc; 268 269 vcons_earlyinit(&arm_simplefb_vcons_data, sc, &arm_simplefb_stdscreen, 270 &arm_simplefb_accessops); 271 arm_simplefb_vcons_data.init_screen = arm_simplefb_init_screen; 272 #ifdef VCONS_DRAW_INTR 273 arm_simplefb_vcons_data.use_intr = 0; 274 #endif 275 vcons_init_screen(&arm_simplefb_vcons_data, &arm_simplefb_screen, 1, 276 &defattr); 277 arm_simplefb_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 278 279 if (ri->ri_rows < 1 || ri->ri_cols < 1) 280 return; 281 282 arm_simplefb_stdscreen.nrows = ri->ri_rows; 283 arm_simplefb_stdscreen.ncols = ri->ri_cols; 284 arm_simplefb_stdscreen.textops = &ri->ri_ops; 285 arm_simplefb_stdscreen.capabilities = ri->ri_caps; 286 287 wsdisplay_preattach(&arm_simplefb_stdscreen, ri, 0, 0, defattr); 288 289 vcons_replay_msgbuf(&arm_simplefb_screen); 290 291 #if NPCI > 0 && defined(PCI_NETBSD_CONFIGURE) 292 /* 293 * Let the PCI resource allocator know about our framebuffer. This 294 * lets us know if the FB base address changes so we can remap the 295 * framebuffer if necessary. 296 */ 297 pciconf_resource_reserve(PCI_CONF_MAP_MEM, addr, size, 298 arm_simplefb_reconfig, NULL); 299 #endif 300 } 301