1 /* $NetBSD: valkyriefb.c,v 1.9 2023/09/24 10:51:28 andvar Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * A console driver for Apple's Valkyrie onboard video controller, found in 30 * for example the Performa 6360 31 * This should be easy enough to adapt to mac68k but I don't have the hardware 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: valkyriefb.c,v 1.9 2023/09/24 10:51:28 andvar Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/device.h> 41 42 #include <dev/ofw/openfirm.h> 43 44 #include <machine/autoconf.h> 45 46 #include <uvm/uvm_extern.h> 47 #include <powerpc/oea/pmap.h> 48 49 #include <dev/wscons/wsdisplayvar.h> 50 #include <dev/wscons/wsconsio.h> 51 #include <dev/wsfont/wsfont.h> 52 #include <dev/rasops/rasops.h> 53 #include <dev/wscons/wsdisplay_vconsvar.h> 54 55 #include <dev/videomode/videomode.h> 56 57 #include <arch/macppc/dev/valkyriefbreg.h> 58 #include <arch/macppc/dev/videopllvar.h> 59 60 #include "opt_wsemul.h" 61 #include "opt_valkyriefb.h" 62 63 #include "videopll.h" 64 #if NVIDEOPLL < 1 65 #error "valkyriefb requires videopll at iic" 66 #endif 67 68 struct valkyriefb_softc { 69 device_t sc_dev; 70 int sc_node; 71 uint8_t *sc_base; 72 uint8_t *sc_fbaddr; 73 74 int sc_depth; 75 int sc_width, sc_height, sc_linebytes; 76 const struct videomode *sc_videomode; 77 uint8_t sc_modereg; 78 79 int sc_mode; 80 uint32_t sc_bg; 81 82 u_char sc_cmap_red[256]; 83 u_char sc_cmap_green[256]; 84 u_char sc_cmap_blue[256]; 85 86 struct vcons_data vd; 87 }; 88 89 struct valkyriefb_mode { 90 int width; 91 int height; 92 uint8_t mode; 93 }; 94 95 /* 96 * Translate screen resolutions to values for Valkyrie's mode register. 97 * These numbers seem to roughly correspond to MacOS video mode numbers 98 * there are many numbers that result in the same resolution which in MacOS 99 * only differ by the display refresh rate, which isn't set by Valkyrie at 100 * all, instead there's a PLL chip hooked to cuda's i2c bus. It doesn't seem 101 * to matter which number we use as long as the resolution is right and we 102 * program the PLL for the right pixel clock 103 */ 104 static struct valkyriefb_mode modetab[] = { 105 { 640, 480, 6 }, 106 { 800, 600, 12 }, 107 { 832, 624, 9 }, 108 { 1024, 768, 14}, 109 }; 110 111 static struct vcons_screen valkyriefb_console_screen; 112 113 static int valkyriefb_match(device_t, cfdata_t, void *); 114 static void valkyriefb_attach(device_t, device_t, void *); 115 static int valkyriefb_init(device_t); 116 static int valkyriefb_set_mode(struct valkyriefb_softc *, 117 const struct videomode *, int); 118 119 CFATTACH_DECL_NEW(valkyriefb, sizeof(struct valkyriefb_softc), 120 valkyriefb_match, valkyriefb_attach, NULL, NULL); 121 122 struct wsscreen_descr valkyriefb_defaultscreen = { 123 "default", 124 0, 0, 125 NULL, 126 8, 16, 127 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 128 NULL, 129 }; 130 131 const struct wsscreen_descr *_valkyriefb_scrlist[] = { 132 &valkyriefb_defaultscreen, 133 /* XXX other formats, graphics screen? */ 134 }; 135 136 struct wsscreen_list valkyriefb_screenlist = { 137 sizeof(_valkyriefb_scrlist) / sizeof(struct wsscreen_descr *), 138 _valkyriefb_scrlist 139 }; 140 141 static int valkyriefb_ioctl(void *, void *, u_long, void *, int, 142 struct lwp *); 143 static paddr_t valkyriefb_mmap(void *, void *, off_t, int); 144 145 static void valkyriefb_init_screen(void *, struct vcons_screen *, int, 146 long *); 147 148 149 struct wsdisplay_accessops valkyriefb_accessops = { 150 valkyriefb_ioctl, 151 valkyriefb_mmap, 152 NULL, 153 NULL, 154 NULL, 155 NULL, /* load_font */ 156 NULL, /* polls */ 157 NULL, /* scroll */ 158 }; 159 160 static inline void 161 valkyriefb_write_reg(struct valkyriefb_softc *sc, int reg, uint8_t val) 162 { 163 *(sc->sc_base + VAL_REGS_OFFSET + reg) = val; 164 __asm volatile("eieio; sync;" ::: "memory"); 165 } 166 167 #ifdef VALKYRIEFB_DEBUG 168 static inline uint8_t 169 valkyriefb_read_reg(struct valkyriefb_softc *sc, int reg) 170 { 171 return *(sc->sc_base + VAL_REGS_OFFSET + reg); 172 } 173 #endif 174 175 static void 176 valkyriefb_write_cmap(struct valkyriefb_softc *sc, 177 int reg, uint8_t r, uint8_t g, uint8_t b) 178 { 179 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_ADDR) = reg; 180 __asm volatile("eieio; sync;" ::: "memory"); 181 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_LUT) = r; 182 __asm volatile("eieio; sync;" ::: "memory"); 183 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_LUT) = g; 184 __asm volatile("eieio; sync;" ::: "memory"); 185 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_LUT) = b; 186 __asm volatile("eieio; sync;" ::: "memory"); 187 } 188 189 static int 190 valkyriefb_match(device_t parent, cfdata_t cf, void *aux) 191 { 192 struct confargs *ca = aux; 193 194 if (strcmp(ca->ca_name, "valkyrie") != 0) 195 return 0; 196 return 1; 197 } 198 199 static void 200 valkyriefb_attach(device_t parent, device_t self, void *aux) 201 { 202 struct valkyriefb_softc *sc = device_private(self); 203 struct confargs *ca = aux; 204 205 sc->sc_dev = self; 206 sc->sc_node = ca->ca_node; 207 208 aprint_normal(" address 0x%08x\n", ca->ca_reg[0]); 209 aprint_verbose_dev(sc->sc_dev, "waiting for videopll...\n"); 210 sc->sc_base = (uint8_t *)ca->ca_reg[0]; 211 #ifdef VALKYRIEFB_DEBUG 212 for (int i = 0; i < 0x40; i += 8) { 213 aprint_error_dev(sc->sc_dev, "%02x: %02x\n", i, 214 valkyriefb_read_reg(sc, i)); 215 } 216 #endif 217 config_finalize_register(sc->sc_dev, valkyriefb_init); 218 sc->sc_fbaddr = (uint8_t *)(sc->sc_base + 0x1000); 219 } 220 221 static int 222 valkyriefb_init(device_t self) 223 { 224 struct valkyriefb_softc *sc = device_private(self); 225 const struct videomode *mode; 226 struct rasops_info *ri; 227 prop_dictionary_t dict; 228 struct wsemuldisplaydev_attach_args aa; 229 bool console = FALSE; 230 long defattr; 231 232 mode = pick_mode_by_ref(800, 600, 60); 233 if (mode == NULL) 234 return 0; 235 236 valkyriefb_set_mode(sc, mode, 8); 237 238 vcons_init(&sc->vd, sc, &valkyriefb_defaultscreen, 239 &valkyriefb_accessops); 240 sc->vd.init_screen = valkyriefb_init_screen; 241 242 dict = device_properties(sc->sc_dev); 243 prop_dictionary_get_bool(dict, "is_console", &console); 244 245 ri = &valkyriefb_console_screen.scr_ri; 246 vcons_init_screen(&sc->vd, &valkyriefb_console_screen, 1, &defattr); 247 memset(sc->sc_base + 0x1000, ri->ri_devcmap[(defattr >> 16) & 0xf], 248 sc->sc_width * sc->sc_linebytes); 249 valkyriefb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 250 251 valkyriefb_defaultscreen.textops = &ri->ri_ops; 252 valkyriefb_defaultscreen.capabilities = ri->ri_caps; 253 valkyriefb_defaultscreen.nrows = ri->ri_rows; 254 valkyriefb_defaultscreen.ncols = ri->ri_cols; 255 if (console) { 256 wsdisplay_cnattach(&valkyriefb_defaultscreen, ri, 0, 0, 257 defattr); 258 vcons_replay_msgbuf(&valkyriefb_console_screen); 259 } 260 aa.console = console; 261 aa.scrdata = &valkyriefb_screenlist; 262 aa.accessops = &valkyriefb_accessops; 263 aa.accesscookie = &sc->vd; 264 265 config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE); 266 267 return 0; 268 } 269 270 static int 271 valkyriefb_set_mode(struct valkyriefb_softc *sc, 272 const struct videomode *mode, int depth) 273 { 274 int i; 275 uint8_t modereg, tmp; 276 277 /* first find the parameter for the mode register */ 278 i = 0; 279 while ((i < __arraycount(modetab)) && 280 (modetab[i].width != mode->hdisplay) && 281 (modetab[i].height != mode->vdisplay)) 282 i++; 283 if (i >= __arraycount(modetab)) { 284 aprint_error_dev(sc->sc_dev, 285 "Can't find a mode register value for %s\n", mode->name); 286 return EINVAL; 287 } else 288 modereg = modetab[i].mode; 289 290 /* check if we have enough video memory */ 291 if ((mode->hdisplay * mode->vdisplay * (depth >> 3)) > 0x100000) { 292 aprint_error_dev(sc->sc_dev, "Not enough video RAM for %s\n", 293 mode->name); 294 return EINVAL; 295 } 296 297 /* reject depths other than 8 or 16 */ 298 if ((depth != 8) && (depth != 16)) { 299 aprint_error_dev(sc->sc_dev, 300 "Depth [%d] is unsupported for %s\n", depth, mode->name); 301 return EINVAL; 302 } 303 304 /* now start programming the chip */ 305 valkyriefb_write_reg(sc, VAL_STATUS, 0); 306 delay(100); 307 valkyriefb_write_reg(sc, VAL_MODE, modereg | VAL_MODE_STOP); 308 309 if (depth == 8) { 310 sc->sc_depth = 8; 311 valkyriefb_write_reg(sc, VAL_DEPTH, VAL_DEPTH_8); 312 for (i = 0; i < 256; i++) { 313 tmp = i & 0xe0; 314 /* 315 * replicate bits so 0xe0 maps to a red value of 0xff 316 * in order to make white look actually white 317 */ 318 tmp |= (tmp >> 3) | (tmp >> 6); 319 sc->sc_cmap_red[i] = tmp; 320 321 tmp = (i & 0x1c) << 3; 322 tmp |= (tmp >> 3) | (tmp >> 6); 323 sc->sc_cmap_green[i] = tmp; 324 325 tmp = (i & 0x03) << 6; 326 tmp |= tmp >> 2; 327 tmp |= tmp >> 4; 328 sc->sc_cmap_blue[i] = tmp; 329 330 valkyriefb_write_cmap(sc, i, sc->sc_cmap_red[i], 331 sc->sc_cmap_green[i], sc->sc_cmap_blue[i]); 332 } 333 } else if (depth == 16) { 334 sc->sc_depth = 16; 335 valkyriefb_write_reg(sc, VAL_DEPTH, VAL_DEPTH_16); 336 /* does the palette have any effect in 16bit? */ 337 } 338 339 videopll_set_freq(mode->dot_clock); 340 delay(100); 341 valkyriefb_write_reg(sc, VAL_MODE, modereg); 342 343 sc->sc_modereg = modereg; 344 sc->sc_videomode = mode; 345 sc->sc_width = mode->hdisplay; 346 sc->sc_height = mode->vdisplay; 347 sc->sc_linebytes = mode->hdisplay * (sc->sc_depth >> 3); 348 aprint_normal_dev(sc->sc_dev, "switched to %d x %d in %d bit colour\n", 349 sc->sc_width, sc->sc_height, sc->sc_depth); 350 return 0; 351 } 352 353 static int 354 valkyriefb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 355 struct lwp *l) 356 { 357 struct vcons_data *vd = v; 358 struct valkyriefb_softc *sc = vd->cookie; 359 struct wsdisplay_fbinfo *wdf; 360 struct vcons_screen *ms = vd->active; 361 362 switch (cmd) { 363 case WSDISPLAYIO_GTYPE: 364 *(u_int *)data = WSDISPLAY_TYPE_VALKYRIE; 365 return 0; 366 367 case WSDISPLAYIO_GINFO: 368 wdf = (void *)data; 369 wdf->height = ms->scr_ri.ri_height; 370 wdf->width = ms->scr_ri.ri_width; 371 wdf->depth = ms->scr_ri.ri_depth; 372 wdf->cmsize = 256; 373 return 0; 374 #if 0 375 case WSDISPLAYIO_GETCMAP: 376 return valkyriefb_getcmap(sc, 377 (struct wsdisplay_cmap *)data); 378 379 case WSDISPLAYIO_PUTCMAP: 380 return valkyriefb_putcmap(sc, 381 (struct wsdisplay_cmap *)data); 382 #endif 383 384 case WSDISPLAYIO_SMODE: { 385 int new_mode = *(int*)data; 386 if (new_mode != sc->sc_mode) { 387 sc->sc_mode = new_mode; 388 if (new_mode == WSDISPLAYIO_MODE_EMUL) { 389 vcons_redraw_screen(ms); 390 } 391 } 392 } 393 return 0; 394 } 395 return EPASSTHROUGH; 396 } 397 398 static paddr_t 399 valkyriefb_mmap(void *v, void *vs, off_t offset, int prot) 400 { 401 struct vcons_data *vd = v; 402 struct valkyriefb_softc *sc = vd->cookie; 403 paddr_t pa; 404 405 /* 'regular' framebuffer mmap()ing */ 406 if (offset < 0x100000) { 407 pa = (paddr_t)(sc->sc_base + 0x1000 + offset); 408 pa |= POWERPC_MMAP_FLAG_PREFETCHABLE; 409 return pa; 410 } 411 return -1; 412 } 413 414 static void 415 valkyriefb_init_screen(void *cookie, struct vcons_screen *scr, 416 int existing, long *defattr) 417 { 418 struct valkyriefb_softc *sc = cookie; 419 struct rasops_info *ri = &scr->scr_ri; 420 421 memset(ri, 0, sizeof(struct rasops_info)); 422 ri->ri_depth = sc->sc_depth; 423 ri->ri_width = sc->sc_width; 424 ri->ri_height = sc->sc_height; 425 ri->ri_stride = sc->sc_linebytes; 426 ri->ri_flg = RI_CENTER | RI_8BIT_IS_RGB | RI_ENABLE_ALPHA; 427 ri->ri_bits = sc->sc_fbaddr; 428 429 scr->scr_flags |= VCONS_DONT_READ; 430 431 rasops_init(ri, 0, 0); 432 ri->ri_caps = WSSCREEN_WSCOLORS; 433 434 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 435 sc->sc_width / ri->ri_font->fontwidth); 436 437 ri->ri_hw = scr; 438 } 439