1/* $OpenBSD: tvtwo.c,v 1.18 2022/07/15 17:57:27 kettenis Exp $ */ 2 3/* 4 * Copyright (c) 2003, 2006, 2008, Miodrag Vallat. 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 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30/* 31 * Driver for the Parallax XVideo and PowerVideo graphics boards. 32 * 33 * Some details about these board used to be available at: 34 * http://www.jlw.com/~woolsey/parallax/support/developers/xvideotech.html 35 */ 36 37/* 38 * The Parallax XVideo series frame buffers are 8/24-bit accelerated 39 * frame buffers, with hardware MPEG capabilities using a CCube chipset. 40 */ 41 42/* 43 * Currently, this driver can only handle the 8-bit and 24-bit planes of the 44 * frame buffer, in an unaccelerated mode. 45 * 46 * TODO: 47 * - nvram handling 48 * - use the accelerator 49 * - interface to the c^3 50 */ 51 52#include <sys/param.h> 53#include <sys/systm.h> 54#include <sys/buf.h> 55#include <sys/device.h> 56#include <sys/ioctl.h> 57#include <sys/mman.h> 58#include <sys/conf.h> 59 60#include <uvm/uvm_extern.h> 61 62#include <machine/autoconf.h> 63#include <machine/bus.h> 64#include <machine/pmap.h> 65#include <machine/cpu.h> 66#include <machine/conf.h> 67 68#include <dev/wscons/wsconsio.h> 69#include <dev/wscons/wsdisplayvar.h> 70#include <dev/rasops/rasops.h> 71#include <machine/fbvar.h> 72 73#include <dev/sbus/sbusvar.h> 74 75/* 76 * The memory layout of the board is as follows: 77 * 78 * PROM0 000000 - 00ffff 79 * overlay plane 010000 - 037fff 80 * registers 040000 - 0404d0 81 * CCube 050000 - 05ffff 82 * 8-bit plane 080000 - 17ffff 83 * 24-bit plane 200000 - 6fffff 84 * PROM1 7f0000 - 7fffff 85 */ 86 87#define PX_PROM0_OFFSET 0x000000 88#define PX_OVERLAY_OFFSET 0x010000 89#define PX_REG_OFFSET 0x040000 90#define PX_CCUBE_OFFSET 0x050000 91#define PX_PLANE8_OFFSET 0x080000 92#define PX_PLANE24_OFFSET 0x200000 93#define PX_PROM1_OFFSET 0x7f0000 94 95#define PX_MAP_SIZE 0x800000 96 97/* 98 * Partial registers layout 99 */ 100 101#define PX_REG_DISPKLUGE 0x00b8 /* write only */ 102#define DISPKLUGE_DEFAULT 0xc41f 103#define DISPKLUGE_BLANK (1 << 12) 104#define DISPKLUGE_SYNC (1 << 13) 105 106#define PX_REG_BT463_RED 0x0480 107#define PX_REG_BT463_GREEN 0x0490 108#define PX_REG_BT463_BLUE 0x04a0 109#define PX_REG_BT463_ALL 0x04b0 110 111#define PX_REG_SIZE 0x04d0 112 113 114/* per-display variables */ 115struct tvtwo_softc { 116 struct sunfb sc_sunfb; /* common base device */ 117 118 bus_space_tag_t sc_bustag; 119 bus_addr_t sc_paddr; 120 121 volatile u_int8_t *sc_m8; 122 volatile u_int8_t *sc_m24; 123 volatile u_int8_t *sc_regs; 124 125 int sc_nscreens; 126}; 127 128int tvtwo_ioctl(void *, u_long, caddr_t, int, struct proc *); 129paddr_t tvtwo_mmap(void *, off_t, int); 130void tvtwo_burner(void *, u_int, u_int); 131 132struct wsdisplay_accessops tvtwo_accessops = { 133 .ioctl = tvtwo_ioctl, 134 .mmap = tvtwo_mmap, 135 .burn_screen = tvtwo_burner 136}; 137 138void tvtwo_directcmap(struct tvtwo_softc *); 139static __inline__ 140void tvtwo_ramdac_wraddr(struct tvtwo_softc *, u_int32_t); 141void tvtwo_reset(struct tvtwo_softc *, u_int); 142void tvtwo_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); 143 144int tvtwomatch(struct device *, void *, void *); 145void tvtwoattach(struct device *, struct device *, void *); 146 147const struct cfattach tvtwo_ca = { 148 sizeof(struct tvtwo_softc), tvtwomatch, tvtwoattach 149}; 150 151struct cfdriver tvtwo_cd = { 152 NULL, "tvtwo", DV_DULL 153}; 154 155/* 156 * Default frame buffer resolution, depending upon the "freqcode" 157 */ 158#define NFREQCODE 5 159const int defwidth[NFREQCODE] = { 1152, 1152, 1152, 1024, 640 }; 160const int defheight[NFREQCODE] = { 900, 900, 900, 768, 480 }; 161 162/* 163 * Match an XVideo or PowerVideo card. 164 */ 165int 166tvtwomatch(struct device *parent, void *vcf, void *aux) 167{ 168 struct sbus_attach_args *sa = aux; 169 170 if (strcmp(sa->sa_name, "PGI,tvtwo") == 0 || 171 strcmp(sa->sa_name, "PGI,tvthree") == 0) 172 return (1); 173 174 return (0); 175} 176 177/* 178 * Attach a display. 179 */ 180void 181tvtwoattach(struct device *parent, struct device *self, void *args) 182{ 183 struct tvtwo_softc *sc = (struct tvtwo_softc *)self; 184 struct sbus_attach_args *sa = args; 185 bus_space_tag_t bt; 186 bus_space_handle_t bh; 187 int node, width, height, freqcode; 188 int isconsole; 189 char *freqstring, *revision; 190 191 bt = sa->sa_bustag; 192 node = sa->sa_node; 193 194 printf(": %s", getpropstring(node, "model")); 195 revision = getpropstring(node, "revision"); 196 if (*revision != '\0') 197 printf(", revision %s", revision); 198 199 /* Older XVideo provide two sets of SBus registers: 200 * R0 040000 - 040800 201 * R1 080000 - 17d200 202 * While the more recent revisions provide only one register: 203 * R0 000000 - 7fffff 204 * 205 * We'll simply ``rewrite'' R0 on older boards and handle them as 206 * recent boards. 207 */ 208 if (sa->sa_nreg > 1) { 209 sa->sa_offset -= PX_REG_OFFSET; 210 sa->sa_size = PX_MAP_SIZE; 211 } 212 213 isconsole = node == fbnode; 214 215 /* Map registers. */ 216 sc->sc_bustag = bt; 217 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_REG_OFFSET, 218 PX_REG_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) { 219 printf("%s: couldn't map registers\n", self->dv_xname); 220 return; 221 } 222 sc->sc_regs = bus_space_vaddr(bt, bh); 223 224 /* 225 * Compute framebuffer size. 226 * Older boards do not have the ``freqcode'' property and are 227 * restricted to 1152x900. 228 */ 229 freqstring = getpropstring(node, "freqcode"); 230 if (*freqstring != '\0') { 231 freqcode = (int)*freqstring; 232 if (freqcode == 'g') { 233 width = height = 1024; 234 } else { 235 if (freqcode < '1' || freqcode > '6') 236 freqcode = 0; 237 else 238 freqcode -= '1'; 239 width = defwidth[freqcode]; 240 height = defheight[freqcode]; 241 242 /* in case our table is wrong or incomplete... */ 243 width = getpropint(node, "hres", width); 244 height = getpropint(node, "vres", height); 245 } 246 } else { 247 width = 1152; 248 height = 900; 249 } 250 251 /* 252 * Since the depth property is missing, we could do 253 * fb_setsize(&sc->sc_sunfb, 8, width, height, node, 0); 254 * but for safety in case it would exist and be set to 32, do it 255 * manually... 256 */ 257 sc->sc_sunfb.sf_depth = 8; 258 sc->sc_sunfb.sf_width = width; 259 sc->sc_sunfb.sf_height = height; 260 sc->sc_sunfb.sf_linebytes = width >= 1024 ? width : 1024; 261 sc->sc_sunfb.sf_fbsize = sc->sc_sunfb.sf_linebytes * height; 262 263 printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height); 264 265 /* Map the frame buffer memory area we're interested in. */ 266 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset); 267 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE8_OFFSET, 268 round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0, 269 &bh) != 0) { 270 printf("%s: couldn't map 8-bit video plane\n", self->dv_xname); 271 return; 272 } 273 sc->sc_m8 = bus_space_vaddr(bt, bh); 274 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE24_OFFSET, 275 round_page(4 * sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0, 276 &bh) != 0) { 277 printf("%s: couldn't map 32-bit video plane\n", self->dv_xname); 278 return; 279 } 280 sc->sc_m24 = bus_space_vaddr(bt, bh); 281 282 /* Enable video. */ 283 tvtwo_burner(sc, 1, 0); 284 285 sc->sc_sunfb.sf_ro.ri_hw = sc; 286 sc->sc_sunfb.sf_ro.ri_bits = (u_char *)sc->sc_m8; 287 288 fbwscons_init(&sc->sc_sunfb, 0, isconsole); 289 fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor); 290 291 if (isconsole) 292 fbwscons_console_init(&sc->sc_sunfb, -1); 293 294 fbwscons_attach(&sc->sc_sunfb, &tvtwo_accessops, isconsole); 295} 296 297int 298tvtwo_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p) 299{ 300 struct tvtwo_softc *sc = dev; 301 struct wsdisplay_fbinfo *wdf; 302 303 /* 304 * Note that, although the emulation (text) mode is running in a 305 * 8-bit plane, we advertise the frame buffer as 32-bit. 306 */ 307 switch (cmd) { 308 case WSDISPLAYIO_GTYPE: 309 *(u_int *)data = WSDISPLAY_TYPE_XVIDEO; 310 break; 311 case WSDISPLAYIO_GINFO: 312 wdf = (struct wsdisplay_fbinfo *)data; 313 wdf->height = sc->sc_sunfb.sf_height; 314 wdf->width = sc->sc_sunfb.sf_width; 315 wdf->depth = 32; 316 wdf->stride = sc->sc_sunfb.sf_linebytes * 4; 317 wdf->offset = 0; 318 wdf->cmsize = 0; 319 break; 320 case WSDISPLAYIO_GETSUPPORTEDDEPTH: 321 *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; 322 break; 323 case WSDISPLAYIO_LINEBYTES: 324 *(u_int *)data = sc->sc_sunfb.sf_linebytes * 4; 325 break; 326 327 case WSDISPLAYIO_GETCMAP: 328 case WSDISPLAYIO_PUTCMAP: 329 break; 330 331 case WSDISPLAYIO_SMODE: 332 if (*(int *)data == WSDISPLAYIO_MODE_EMUL) { 333 /* Back from X11 to text mode */ 334 tvtwo_reset(sc, 8); 335 } else { 336 /* Starting X11, initialize 32-bit mode */ 337 tvtwo_reset(sc, 32); 338 } 339 break; 340 341 case WSDISPLAYIO_SVIDEO: 342 case WSDISPLAYIO_GVIDEO: 343 break; 344 345 case WSDISPLAYIO_GCURPOS: 346 case WSDISPLAYIO_SCURPOS: 347 case WSDISPLAYIO_GCURMAX: 348 case WSDISPLAYIO_GCURSOR: 349 case WSDISPLAYIO_SCURSOR: 350 default: 351 return (-1); 352 } 353 354 return (0); 355} 356 357/* 358 * Return the address that would map the given device at the given 359 * offset, allowing for the given protection, or return -1 for error. 360 */ 361paddr_t 362tvtwo_mmap(void *v, off_t offset, int prot) 363{ 364 struct tvtwo_softc *sc = v; 365 366 if (offset & PGOFSET) 367 return (-1); 368 369 /* Allow mapping as a dumb framebuffer from offset 0 */ 370 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize * 4) { 371 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 372 PX_PLANE24_OFFSET + offset, prot, BUS_SPACE_MAP_LINEAR)); 373 } 374 375 return (-1); 376} 377 378void 379tvtwo_burner(void *v, u_int on, u_int flags) 380{ 381 struct tvtwo_softc *sc = v; 382 u_int32_t dispkluge; 383 384 if (on) 385 dispkluge = DISPKLUGE_DEFAULT & ~DISPKLUGE_BLANK; 386 else { 387 dispkluge = DISPKLUGE_DEFAULT | DISPKLUGE_BLANK; 388 if (flags & WSDISPLAY_BURN_VBLANK) 389 dispkluge |= DISPKLUGE_SYNC; 390 } 391 392 *(volatile u_int32_t *)(sc->sc_regs + PX_REG_DISPKLUGE) = dispkluge; 393} 394 395void 396tvtwo_reset(struct tvtwo_softc *sc, u_int depth) 397{ 398 if (depth == 32) { 399 /* Initialize a direct color map. */ 400 tvtwo_directcmap(sc); 401 } else { 402 fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor); 403 } 404} 405 406/* 407 * Simple Bt463 programming routines. 408 */ 409 410static __inline__ void 411tvtwo_ramdac_wraddr(struct tvtwo_softc *sc, u_int32_t addr) 412{ 413 volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED); 414 415 dac[0] = (addr & 0xff); /* lo addr */ 416 dac[1] = ((addr >> 8) & 0xff); /* hi addr */ 417} 418 419void 420tvtwo_directcmap(struct tvtwo_softc *sc) 421{ 422 volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED); 423 u_int32_t c; 424 425 tvtwo_ramdac_wraddr(sc, 0); 426 for (c = 0; c < 256; c++) { 427 dac[3] = c; /* R */ 428 dac[3] = c; /* G */ 429 dac[3] = c; /* B */ 430 } 431} 432 433void 434tvtwo_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 435{ 436 struct tvtwo_softc *sc = v; 437 volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED); 438 439 tvtwo_ramdac_wraddr(sc, index); 440 dac[3] = r; 441 dac[3] = g; 442 dac[3] = b; 443} 444