1 /* $OpenBSD: agten.c,v 1.11 2018/12/27 11:09:17 claudio Exp $ */ 2 /* 3 * Copyright (c) 2002, 2003, Miodrag Vallat. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28 29 /* 30 * Fujitsu AG-10 framebuffer driver. 31 * 32 * The AG-10 is mostly made of: 33 * - a 3DLabs 300SX Glint chip, with two 6MB independent framebuffer spaces 34 * - a Number Nine Imagine 128 chip with its own 4MB framebuffer space 35 * - a Weitek P9100 with its own 2MB of framebuffer memory 36 * - an IBM PaletteDAC 561 ramdac 37 * - an Analog Devices ADSP-21062 38 * 39 * All of these chips (memory, registers, etc) are mapped in the SBus 40 * memory space associated to the board. What is unexpected, however, is 41 * that there are also PCI registers mappings for the first three chips! 42 * 43 * The three graphics chips act as overlays of each other, for the final 44 * video output. 45 * 46 * The PROM initialization will use the I128 framebuffer memory for output, 47 * which is ``above'' the P9100. The P9100 seems to only be there to provide 48 * a simple RAMDAC interface, but its frame buffer memory is accessible and 49 * will appear as an ``underlay'' plane. 50 */ 51 52 /* 53 * TODO 54 * - initialize the I128 in 32bit mode 55 * - use the i128 acceleration features 56 */ 57 58 #include <sys/param.h> 59 #include <sys/systm.h> 60 #include <sys/buf.h> 61 #include <sys/device.h> 62 #include <sys/ioctl.h> 63 #include <sys/malloc.h> 64 #include <sys/mman.h> 65 #include <sys/tty.h> 66 #include <sys/conf.h> 67 68 #include <uvm/uvm_extern.h> 69 70 #include <machine/autoconf.h> 71 #include <machine/pmap.h> 72 #include <machine/cpu.h> 73 #include <machine/conf.h> 74 75 #include <dev/wscons/wsconsio.h> 76 #include <dev/wscons/wsdisplayvar.h> 77 #include <dev/rasops/rasops.h> 78 #include <machine/fbvar.h> 79 80 #include <dev/ic/p9000.h> 81 #include <dev/ic/ibm561reg.h> 82 83 #include <dev/sbus/sbusvar.h> 84 85 struct agten_cmap { 86 u_int8_t cm_red[256]; 87 u_int8_t cm_green[256]; 88 u_int8_t cm_blue[256]; 89 }; 90 91 /* per-display variables */ 92 struct agten_softc { 93 struct sunfb sc_sunfb; /* common base part */ 94 95 bus_space_tag_t sc_bustag; 96 bus_addr_t sc_paddr; 97 off_t sc_physoffset; /* offset for frame buffer */ 98 99 volatile u_int8_t *sc_p9100; 100 struct agten_cmap sc_cmap; /* shadow color map */ 101 102 volatile u_int32_t *sc_i128_fb; 103 104 int sc_nscreens; 105 }; 106 107 int agten_ioctl(void *, u_long, caddr_t, int, struct proc *); 108 paddr_t agten_mmap(void *, off_t, int); 109 void agten_reset(struct agten_softc *); 110 void agten_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); 111 112 static __inline__ void ibm561_write(struct agten_softc *, u_int32_t, u_int32_t); 113 int agten_getcmap(struct agten_cmap *, struct wsdisplay_cmap *); 114 int agten_putcmap(struct agten_cmap *, struct wsdisplay_cmap *); 115 void agten_loadcmap(struct agten_softc *, u_int, u_int); 116 117 struct wsdisplay_accessops agten_accessops = { 118 .ioctl = agten_ioctl, 119 .mmap = agten_mmap 120 }; 121 122 int agtenmatch(struct device *, void *, void *); 123 void agtenattach(struct device *, struct device *, void *); 124 125 struct cfattach agten_ca = { 126 sizeof(struct agten_softc), agtenmatch, agtenattach 127 }; 128 129 struct cfdriver agten_cd = { 130 NULL, "agten", DV_DULL 131 }; 132 133 int 134 agtenmatch(struct device *parent, void *vcf, void *aux) 135 { 136 struct sbus_attach_args *sa = aux; 137 138 if (strcmp(sa->sa_name, "PFU,aga") != 0) 139 return (0); 140 141 return (1); 142 } 143 144 void 145 agtenattach(struct device *parent, struct device *self, void *args) 146 { 147 struct agten_softc *sc = (struct agten_softc *)self; 148 struct sbus_attach_args *sa = args; 149 bus_space_tag_t bt; 150 bus_space_handle_t bh; 151 int node, isconsole; 152 char *nam; 153 154 bt = sa->sa_bustag; 155 node = sa->sa_node; 156 nam = getpropstring(node, "model"); 157 printf(": model %s", nam); 158 159 isconsole = node == fbnode; 160 161 /* 162 * Map the various beasts of this card we are interested in. 163 */ 164 165 sc->sc_bustag = bt; 166 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset); 167 168 sc->sc_physoffset = 169 (off_t)getpropint(node, "i128_fb_physaddr", 0x8000000); 170 171 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + sc->sc_physoffset, 172 getpropint(node, "i128_fb_size", 0x400000), BUS_SPACE_MAP_LINEAR, 173 0, &bh) != 0) { 174 printf("\n%s: couldn't map video memory\n", self->dv_xname); 175 return; 176 } 177 sc->sc_i128_fb = bus_space_vaddr(bt, bh); 178 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + 179 getpropint(node, "p9100_reg_physaddr", 0x10a0000), 0x4000, 180 BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) { 181 printf("\n%s: couldn't map control registers\n", self->dv_xname); 182 return; 183 } 184 sc->sc_p9100 = bus_space_vaddr(bt, bh); 185 186 /* 187 * For some reason the agten does not use the canonical name for 188 * properties, but uses an ffb_ prefix; and the linebytes property is 189 * missing. 190 * The following is a specific version of 191 * fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, BUS_SBUS); 192 * using the correct property names. 193 */ 194 #ifdef notyet 195 sc->sc_sunfb.sf_depth = 32; 196 #else 197 sc->sc_sunfb.sf_depth = getpropint(node, "ffb_depth", 8); 198 #endif 199 sc->sc_sunfb.sf_width = getpropint(node, "ffb_width", 1152); 200 sc->sc_sunfb.sf_height = getpropint(node, "ffb_height", 900); 201 sc->sc_sunfb.sf_linebytes = 202 roundup(sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_depth) * 203 sc->sc_sunfb.sf_depth / 8; 204 sc->sc_sunfb.sf_fbsize = 205 sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes; 206 207 printf(", %dx%d, depth %d\n", 208 sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height, 209 sc->sc_sunfb.sf_depth); 210 211 sc->sc_sunfb.sf_ro.ri_bits = (void *)sc->sc_i128_fb; 212 213 sc->sc_sunfb.sf_ro.ri_hw = sc; 214 fbwscons_init(&sc->sc_sunfb, 0, isconsole); 215 fbwscons_setcolormap(&sc->sc_sunfb, agten_setcolor); 216 217 if (isconsole) 218 fbwscons_console_init(&sc->sc_sunfb, -1); 219 220 fbwscons_attach(&sc->sc_sunfb, &agten_accessops, isconsole); 221 } 222 223 int 224 agten_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p) 225 { 226 struct agten_softc *sc = dev; 227 struct wsdisplay_cmap *cm; 228 struct wsdisplay_fbinfo *wdf; 229 int error; 230 231 switch (cmd) { 232 case WSDISPLAYIO_GTYPE: 233 *(u_int *)data = WSDISPLAY_TYPE_AGTEN; 234 break; 235 case WSDISPLAYIO_GINFO: 236 wdf = (struct wsdisplay_fbinfo *)data; 237 wdf->height = sc->sc_sunfb.sf_height; 238 wdf->width = sc->sc_sunfb.sf_width; 239 wdf->depth = sc->sc_sunfb.sf_depth; 240 wdf->cmsize = (sc->sc_sunfb.sf_depth == 8) ? 256 : 0; 241 break; 242 case WSDISPLAYIO_LINEBYTES: 243 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 244 break; 245 246 case WSDISPLAYIO_GETCMAP: 247 if (sc->sc_sunfb.sf_depth == 8) { 248 cm = (struct wsdisplay_cmap *)data; 249 error = agten_getcmap(&sc->sc_cmap, cm); 250 if (error) 251 return (error); 252 } 253 break; 254 case WSDISPLAYIO_PUTCMAP: 255 if (sc->sc_sunfb.sf_depth == 8) { 256 cm = (struct wsdisplay_cmap *)data; 257 error = agten_putcmap(&sc->sc_cmap, cm); 258 if (error) 259 return (error); 260 agten_loadcmap(sc, 0, 256); 261 } 262 break; 263 264 case WSDISPLAYIO_SVIDEO: 265 case WSDISPLAYIO_GVIDEO: 266 break; 267 268 case WSDISPLAYIO_GCURPOS: 269 case WSDISPLAYIO_SCURPOS: 270 case WSDISPLAYIO_GCURMAX: 271 case WSDISPLAYIO_GCURSOR: 272 case WSDISPLAYIO_SCURSOR: 273 default: 274 return (-1); /* not supported yet */ 275 } 276 277 return (0); 278 } 279 280 /* 281 * Return the address that would map the given device at the given 282 * offset, allowing for the given protection, or return -1 for error. 283 */ 284 paddr_t 285 agten_mmap(void *v, off_t offset, int prot) 286 { 287 struct agten_softc *sc = v; 288 289 if (offset & PGOFSET) 290 return (-1); 291 292 /* Allow mapping as a dumb framebuffer from offset 0 */ 293 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 294 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 295 sc->sc_physoffset + offset, prot, BUS_SPACE_MAP_LINEAR)); 296 } 297 298 return (-1); /* not a user-map offset */ 299 } 300 301 void 302 agten_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 303 { 304 struct agten_softc *sc = v; 305 306 sc->sc_cmap.cm_red[index] = r; 307 sc->sc_cmap.cm_green[index] = g; 308 sc->sc_cmap.cm_blue[index] = b; 309 310 agten_loadcmap(sc, index, 1); 311 } 312 313 int 314 agten_getcmap(struct agten_cmap *cm, struct wsdisplay_cmap *rcm) 315 { 316 u_int index = rcm->index, count = rcm->count; 317 int error; 318 319 if (index >= 256 || count > 256 - index) 320 return (EINVAL); 321 322 if ((error = copyout(&cm->cm_red[index], rcm->red, count)) != 0) 323 return (error); 324 if ((error = copyout(&cm->cm_green[index], rcm->green, count)) != 0) 325 return (error); 326 if ((error = copyout(&cm->cm_blue[index], rcm->blue, count)) != 0) 327 return (error); 328 329 return (0); 330 } 331 332 int 333 agten_putcmap(struct agten_cmap *cm, struct wsdisplay_cmap *rcm) 334 { 335 u_int index = rcm->index, count = rcm->count; 336 int error; 337 338 if (index >= 256 || count > 256 - index) 339 return (EINVAL); 340 341 if ((error = copyin(rcm->red, &cm->cm_red[index], count)) != 0) 342 return (error); 343 if ((error = copyin(rcm->green, &cm->cm_green[index], count)) != 0) 344 return (error); 345 if ((error = copyin(rcm->blue, &cm->cm_blue[index], count)) != 0) 346 return (error); 347 348 return (0); 349 } 350 351 static __inline__ void 352 ibm561_write(struct agten_softc *sc, u_int32_t reg, u_int32_t value) 353 { 354 /* 355 * For some design reason the IBM561 PaletteDac needs to be fed 356 * values shifted left by 16 bits. What happened to simplicity? 357 */ 358 *(volatile u_int32_t *)(sc->sc_p9100 + P9100_RAMDAC_REGISTER(reg)) = 359 (value) << 16; 360 } 361 362 void 363 agten_loadcmap(struct agten_softc *sc, u_int start, u_int ncolors) 364 { 365 int i; 366 u_int8_t *red, *green, *blue; 367 368 ibm561_write(sc, IBM561_ADDR_LOW, 369 (IBM561_CMAP_TABLE + start) & 0xff); 370 ibm561_write(sc, IBM561_ADDR_HIGH, 371 ((IBM561_CMAP_TABLE + start) >> 8) & 0xff); 372 373 red = sc->sc_cmap.cm_red; 374 green = sc->sc_cmap.cm_green; 375 blue = sc->sc_cmap.cm_blue; 376 for (i = start; i < start + ncolors; i++) { 377 ibm561_write(sc, IBM561_CMD_CMAP, *red++); 378 ibm561_write(sc, IBM561_CMD_CMAP, *green++); 379 ibm561_write(sc, IBM561_CMD_CMAP, *blue++); 380 } 381 } 382