valkyriefb.c revision 1.1
1/* $NetBSD: valkyriefb.c,v 1.1 2012/01/24 04:33:11 macallan 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.1 2012/01/24 04:33:11 macallan 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 <dev/wscons/wsdisplayvar.h> 47#include <dev/wscons/wsconsio.h> 48#include <dev/wsfont/wsfont.h> 49#include <dev/rasops/rasops.h> 50#include <dev/wscons/wsdisplay_vconsvar.h> 51 52#include <dev/videomode/videomode.h> 53 54#include <arch/macppc/dev/valkyriefbreg.h> 55#include <arch/macppc/dev/videopllvar.h> 56 57#include "opt_wsemul.h" 58#include "opt_valkyriefb.h" 59 60#include "videopll.h" 61#if NVIDEOPLL < 1 62#error "valkyriefb requires videopll at iic" 63#endif 64 65struct valkyriefb_softc { 66 device_t sc_dev; 67 int sc_node; 68 uint8_t *sc_base; 69 70 int sc_depth; 71 int sc_width, sc_height, sc_linebytes; 72 const struct videomode *sc_videomode; 73 uint8_t sc_modereg; 74 75 int sc_mode; 76 uint32_t sc_bg; 77 78 u_char sc_cmap_red[256]; 79 u_char sc_cmap_green[256]; 80 u_char sc_cmap_blue[256]; 81 82 struct vcons_data vd; 83}; 84 85struct valkyriefb_mode { 86 int width; 87 int height; 88 uint8_t mode; 89}; 90 91/* 92 * Translate screen resolutions to values for Valkyrie's mode register. 93 * These numbers seem to roughly correspond to MacOS video mode numbers 94 * there are many numbers that result in the same resolution which in MacOS 95 * only differ by the display refresh rate, which isn't set by Valkyrie at 96 * all, instead there's a PLL chip hooked to cuda's i2c bus. It doesn't seem 97 * to matter which number we use as long as the resolution is right and we 98 * program the PLL for the right pixel clock 99 */ 100static struct valkyriefb_mode modetab[] = { 101 { 640, 480, 6 }, 102 { 800, 600, 12 }, 103 { 832, 624, 9 }, 104 { 1024, 768, 14}, 105}; 106 107static struct vcons_screen valkyriefb_console_screen; 108 109static int valkyriefb_match(device_t, cfdata_t, void *); 110static void valkyriefb_attach(device_t, device_t, void *); 111static int valkyriefb_init(device_t); 112static int valkyriefb_set_mode(struct valkyriefb_softc *, 113 const struct videomode *, int); 114 115CFATTACH_DECL_NEW(valkyriefb, sizeof(struct valkyriefb_softc), 116 valkyriefb_match, valkyriefb_attach, NULL, NULL); 117 118struct wsscreen_descr valkyriefb_defaultscreen = { 119 "default", 120 0, 0, 121 NULL, 122 8, 16, 123 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 124 NULL, 125}; 126 127const struct wsscreen_descr *_valkyriefb_scrlist[] = { 128 &valkyriefb_defaultscreen, 129 /* XXX other formats, graphics screen? */ 130}; 131 132struct wsscreen_list valkyriefb_screenlist = { 133 sizeof(_valkyriefb_scrlist) / sizeof(struct wsscreen_descr *), _valkyriefb_scrlist 134}; 135 136static int valkyriefb_ioctl(void *, void *, u_long, void *, int, 137 struct lwp *); 138static paddr_t valkyriefb_mmap(void *, void *, off_t, int); 139 140static void valkyriefb_init_screen(void *, struct vcons_screen *, int, 141 long *); 142 143 144struct wsdisplay_accessops valkyriefb_accessops = { 145 valkyriefb_ioctl, 146 valkyriefb_mmap, 147 NULL, 148 NULL, 149 NULL, 150 NULL, /* load_font */ 151 NULL, /* polls */ 152 NULL, /* scroll */ 153}; 154 155static inline void 156valkyriefb_write_reg(struct valkyriefb_softc *sc, int reg, uint8_t val) 157{ 158 *(sc->sc_base + VAL_REGS_OFFSET + reg) = val; 159 __asm("eieio; sync;"); 160} 161 162static inline uint8_t 163valkyriefb_read_reg(struct valkyriefb_softc *sc, int reg) 164{ 165 return *(sc->sc_base + VAL_REGS_OFFSET + reg); 166} 167 168static void 169valkyriefb_write_cmap(struct valkyriefb_softc *sc, 170 int reg, uint8_t r, uint8_t g, uint8_t b) 171{ 172 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_ADDR) = reg; 173 __asm("eieio; sync;"); 174 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_LUT) = r; 175 __asm("eieio; sync;"); 176 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_LUT) = g; 177 __asm("eieio; sync;"); 178 *(sc->sc_base + VAL_CMAP_OFFSET + VAL_CMAP_LUT) = b; 179 __asm("eieio; sync;"); 180} 181 182static int 183valkyriefb_match(device_t parent, cfdata_t cf, void *aux) 184{ 185 struct confargs *ca = aux; 186 187 if (strcmp(ca->ca_name, "valkyrie") != 0) 188 return 0; 189 return 1; 190} 191 192static void 193valkyriefb_attach(device_t parent, device_t self, void *aux) 194{ 195 struct valkyriefb_softc *sc = device_private(self); 196 struct confargs *ca = aux; 197 int i; 198 volatile uint32_t *fb; 199 200 sc->sc_dev = self; 201 sc->sc_node = ca->ca_node; 202 203 aprint_normal(" address 0x%08x\n", ca->ca_reg[0]); 204 aprint_verbose_dev(sc->sc_dev, "waiting for videopll...\n"); 205 sc->sc_base = (uint8_t *)ca->ca_reg[0]; 206#ifdef VALKYRIEFB_DEBUG 207 for (i = 0; i < 0x40; i += 8) { 208 aprint_error_dev(sc->sc_dev, "%02x: %02x\n", i, valkyriefb_read_reg(sc, i)); 209 } 210#endif 211 config_finalize_register(sc->sc_dev, valkyriefb_init); 212 fb = (volatile uint32_t *)(sc->sc_base + 0x1000); 213 for (i = 0; i < 256; i++) 214 fb[i] = 0xffffffff; 215 for (i = 256; i < 0x10000; i++) 216 fb[i] = 0x80808080; 217} 218 219static int 220valkyriefb_init(device_t self) 221{ 222 struct valkyriefb_softc *sc = device_private(self); 223 const struct videomode *mode; 224 struct rasops_info *ri; 225 prop_dictionary_t dict; 226 struct wsemuldisplaydev_attach_args aa; 227 bool console = FALSE; 228 long defattr; 229 230 mode = pick_mode_by_ref(800, 600, 60); 231 if (mode == NULL) 232 return 0; 233 234 valkyriefb_set_mode(sc, mode, 8); 235 236 vcons_init(&sc->vd, sc, &valkyriefb_defaultscreen, &valkyriefb_accessops); 237 sc->vd.init_screen = valkyriefb_init_screen; 238 239 dict = device_properties(sc->sc_dev); 240 prop_dictionary_get_bool(dict, "is_console", &console); 241 242 ri = &valkyriefb_console_screen.scr_ri; 243 vcons_init_screen(&sc->vd, &valkyriefb_console_screen, 1, &defattr); 244 valkyriefb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 245 246 valkyriefb_defaultscreen.textops = &ri->ri_ops; 247 valkyriefb_defaultscreen.capabilities = ri->ri_caps; 248 valkyriefb_defaultscreen.nrows = ri->ri_rows; 249 valkyriefb_defaultscreen.ncols = ri->ri_cols; 250 if (console) { 251 wsdisplay_cnattach(&valkyriefb_defaultscreen, ri, 0, 0, defattr); 252 } 253 254 memset(sc->sc_base + 0x1000, ri->ri_devcmap[(defattr >> 16) & 0xf], 255 sc->sc_width * sc->sc_linebytes); 256 257 if (console) 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); 266 267 return 0; 268} 269 270static int 271valkyriefb_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 ((modetab[i].width == mode->hdisplay) && 284 (modetab[i].height == mode->vdisplay)) { 285 modereg = modetab[i].mode; 286 } else { 287 aprint_error_dev(sc->sc_dev, "Can't find a mode register value for %s\n", mode->name); 288 return EINVAL; 289 } 290 291 /* check if we have enough video memory */ 292 if ((mode->hdisplay * mode->vdisplay * (depth >> 3)) > 0x100000) { 293 aprint_error_dev(sc->sc_dev, "Not enough video RAM for %s\n", 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, "Depth [%d] is unsupported for %s\n", depth, mode->name); 300 return EINVAL; 301 } 302 303 /* now start programming the chip */ 304 valkyriefb_write_reg(sc, VAL_STATUS, 0); 305 delay(100); 306 valkyriefb_write_reg(sc, VAL_MODE, modereg | VAL_MODE_STOP); 307 308 if (depth == 8) { 309 sc->sc_depth = 8; 310 valkyriefb_write_reg(sc, VAL_DEPTH, VAL_DEPTH_8); 311 for (i = 0; i < 256; i++) { 312 tmp = i & 0xe0; 313 /* 314 * replicate bits so 0xe0 maps to a red value of 0xff 315 * in order to make white look actually white 316 */ 317 tmp |= (tmp >> 3) | (tmp >> 6); 318 sc->sc_cmap_red[i] = tmp; 319 320 tmp = (i & 0x1c) << 3; 321 tmp |= (tmp >> 3) | (tmp >> 6); 322 sc->sc_cmap_green[i] = tmp; 323 324 tmp = (i & 0x03) << 6; 325 tmp |= tmp >> 2; 326 tmp |= tmp >> 4; 327 sc->sc_cmap_blue[i] = tmp; 328 329 valkyriefb_write_cmap(sc, i, sc->sc_cmap_red[i], 330 sc->sc_cmap_green[i], sc->sc_cmap_blue[i]); 331 } 332 } else if (depth == 16) { 333 sc->sc_depth = 16; 334 valkyriefb_write_reg(sc, VAL_DEPTH, VAL_DEPTH_16); 335 /* does the palette have any effect in 16bit? */ 336 } 337 338 videopll_set_freq(mode->dot_clock); 339 delay(100); 340 valkyriefb_write_reg(sc, VAL_MODE, modereg); 341 342 sc->sc_modereg = modereg; 343 sc->sc_videomode = mode; 344 sc->sc_width = mode->hdisplay; 345 sc->sc_height = mode->vdisplay; 346 sc->sc_linebytes = mode->hdisplay * (sc->sc_depth >> 3); 347 aprint_normal_dev(sc->sc_dev, "switched to %d x %d in %d bit colour\n", 348 sc->sc_width, sc->sc_height, sc->sc_depth); 349 return 0; 350} 351 352static int 353valkyriefb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 354 struct lwp *l) 355{ 356 struct vcons_data *vd = v; 357 struct valkyriefb_softc *sc = vd->cookie; 358 struct wsdisplay_fbinfo *wdf; 359 struct vcons_screen *ms = vd->active; 360 361 switch (cmd) { 362 case WSDISPLAYIO_GTYPE: 363 *(u_int *)data = WSDISPLAY_TYPE_VALKYRIE; 364 return 0; 365 366 case WSDISPLAYIO_GINFO: 367 wdf = (void *)data; 368 wdf->height = ms->scr_ri.ri_height; 369 wdf->width = ms->scr_ri.ri_width; 370 wdf->depth = ms->scr_ri.ri_depth; 371 wdf->cmsize = 256; 372 return 0; 373#if 0 374 case WSDISPLAYIO_GETCMAP: 375 return valkyriefb_getcmap(sc, 376 (struct wsdisplay_cmap *)data); 377 378 case WSDISPLAYIO_PUTCMAP: 379 return valkyriefb_putcmap(sc, 380 (struct wsdisplay_cmap *)data); 381#endif 382 383 case WSDISPLAYIO_SMODE: { 384 int new_mode = *(int*)data; 385 if (new_mode != sc->sc_mode) { 386 sc->sc_mode = new_mode; 387 if (new_mode == WSDISPLAYIO_MODE_EMUL) { 388 vcons_redraw_screen(ms); 389 } 390 } 391 } 392 return 0; 393 } 394 return EPASSTHROUGH; 395} 396 397static paddr_t 398valkyriefb_mmap(void *v, void *vs, off_t offset, int prot) 399{ 400 struct vcons_data *vd = v; 401 struct valkyriefb_softc *sc = vd->cookie; 402 paddr_t pa; 403 404 /* 'regular' framebuffer mmap()ing */ 405 if (offset < 0x100000) { 406 pa = (paddr_t)(sc->sc_base + 0x1000 + offset); 407 return pa; 408 } 409 return -1; 410} 411 412static void 413valkyriefb_init_screen(void *cookie, struct vcons_screen *scr, 414 int existing, long *defattr) 415{ 416 struct valkyriefb_softc *sc = cookie; 417 struct rasops_info *ri = &scr->scr_ri; 418 419 ri->ri_depth = sc->sc_depth; 420 ri->ri_width = sc->sc_width; 421 ri->ri_height = sc->sc_height; 422 ri->ri_stride = sc->sc_linebytes; 423 ri->ri_flg = RI_CENTER | RI_8BIT_IS_RGB | RI_ENABLE_ALPHA; 424 ri->ri_bits = sc->sc_base + 0x1000; 425 /* 426 * We probably shouldn't set this flag together with RI_ENABLE_ALPHA 427 * since the CPU is likely slow enough to make scrolling using 428 * framebuffer reads faster than redrawing 429 */ 430 scr->scr_flags |= VCONS_DONT_READ; 431 432 rasops_init(ri, 0, 0); 433 ri->ri_caps = WSSCREEN_WSCOLORS; 434 435 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 436 sc->sc_width / ri->ri_font->fontwidth); 437 438 ri->ri_hw = scr; 439} 440