1 /* $NetBSD: voyagerfb.c,v 1.36 2026/06/18 22:10:27 rkujawa Exp $ */ 2 3 /* 4 * Copyright (c) 2009, 2011 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 Silicon Motion SM502 / Voyager GX graphics controllers 30 * tested on GDIUM only so far 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: voyagerfb.c,v 1.36 2026/06/18 22:10:27 rkujawa Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/device.h> 40 #include <sys/lwp.h> 41 #include <sys/kauth.h> 42 43 #include <dev/videomode/videomode.h> 44 45 #include <dev/pci/pcivar.h> 46 #include <dev/pci/pcireg.h> 47 #include <dev/pci/pcidevs.h> 48 #include <dev/pci/pciio.h> 49 #include <dev/ic/sm502reg.h> 50 51 #include <dev/wscons/wsdisplayvar.h> 52 #include <dev/wscons/wsconsio.h> 53 #include <dev/wsfont/wsfont.h> 54 #include <dev/rasops/rasops.h> 55 #include <dev/wscons/wsdisplay_vconsvar.h> 56 #include <dev/pci/wsdisplay_pci.h> 57 58 #include "opt_voyagerfb.h" 59 60 #include <dev/i2c/i2cvar.h> 61 #ifdef VOYAGERFB_MODE_SETTING 62 #include <dev/i2c/ddcvar.h> 63 #include <dev/videomode/edidvar.h> 64 #endif 65 #include <dev/pci/voyagervar.h> 66 #include <dev/wscons/wsdisplay_glyphcachevar.h> 67 68 #ifdef VOYAGERFB_DEBUG 69 #define DPRINTF aprint_error 70 #else 71 #define DPRINTF while (0) printf 72 #endif 73 74 /* XXX these are gdium-specific */ 75 #define GPIO_BACKLIGHT 0x20000000 76 77 struct voyagerfb_softc { 78 device_t sc_dev; 79 80 pci_chipset_tag_t sc_pc; 81 pcitag_t sc_pcitag; 82 bus_space_tag_t sc_memt; 83 84 bus_space_handle_t sc_fbh; 85 bus_space_handle_t sc_regh; 86 bus_addr_t sc_fb, sc_reg; 87 bus_size_t sc_fbsize, sc_regsize; 88 89 int sc_width, sc_height, sc_depth, sc_stride; 90 int sc_locked; 91 void *sc_fbaddr; 92 struct vcons_screen sc_console_screen; 93 struct wsscreen_descr sc_defaultscreen_descr; 94 const struct wsscreen_descr *sc_screens[1]; 95 struct wsscreen_list sc_screenlist; 96 struct vcons_data vd; 97 uint8_t *sc_dataport; 98 int sc_mode; 99 int sc_bl_on, sc_bl_level; 100 void *sc_gpio_cookie; 101 102 #ifdef VOYAGERFB_MODE_SETTING 103 /* DDC/EDID via the SM502 hardware I2C master (independent of parent) */ 104 struct i2c_controller sc_ddc_i2c; 105 uint8_t sc_edid[128]; 106 uint8_t sc_i2c_status; /* last engine status, for diag */ 107 bool sc_edid_valid; 108 struct edid_info sc_edid_info; 109 #endif 110 111 /* cursor stuff */ 112 int sc_cur_x; 113 int sc_cur_y; 114 int sc_hot_x; 115 int sc_hot_y; 116 uint32_t sc_cursor_addr; 117 uint32_t *sc_cursor; 118 119 /* colour map */ 120 u_char sc_cmap_red[256]; 121 u_char sc_cmap_green[256]; 122 u_char sc_cmap_blue[256]; 123 124 glyphcache sc_gc; 125 }; 126 127 static int voyagerfb_match(device_t, cfdata_t, void *); 128 static void voyagerfb_attach(device_t, device_t, void *); 129 130 CFATTACH_DECL_NEW(voyagerfb, sizeof(struct voyagerfb_softc), 131 voyagerfb_match, voyagerfb_attach, NULL, NULL); 132 133 extern const u_char rasops_cmap[768]; 134 135 static int voyagerfb_ioctl(void *, void *, u_long, void *, int, 136 struct lwp *); 137 static paddr_t voyagerfb_mmap(void *, void *, off_t, int); 138 static void voyagerfb_init_screen(void *, struct vcons_screen *, int, 139 long *); 140 141 static int voyagerfb_putcmap(struct voyagerfb_softc *, 142 struct wsdisplay_cmap *); 143 static int voyagerfb_getcmap(struct voyagerfb_softc *, 144 struct wsdisplay_cmap *); 145 static void voyagerfb_restore_palette(struct voyagerfb_softc *); 146 static int voyagerfb_putpalreg(struct voyagerfb_softc *, int, uint8_t, 147 uint8_t, uint8_t); 148 149 static void voyagerfb_init(struct voyagerfb_softc *); 150 151 #ifdef VOYAGERFB_MODE_SETTING 152 /* DDC/EDID over the SM502 hardware I2C master */ 153 static void voyagerfb_i2c_enable(struct voyagerfb_softc *); 154 static int voyagerfb_i2c_xfer(struct voyagerfb_softc *, int, i2c_addr_t, 155 uint8_t *, size_t); 156 static int voyagerfb_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, 157 size_t, void *, size_t, int); 158 static void voyagerfb_ddc_read(struct voyagerfb_softc *); 159 160 /* mode-setting */ 161 static void voyagerfb_set_videomode(struct voyagerfb_softc *, 162 const struct videomode *); 163 static bus_size_t voyagerfb_active_clock_reg(struct voyagerfb_softc *); 164 static void voyagerfb_solve_pll(int, uint32_t *, uint32_t *, uint32_t *); 165 static void voyagerfb_set_dotclock(struct voyagerfb_softc *, int); 166 static void voyagerfb_set_geometry(struct voyagerfb_softc *, 167 const struct videomode *); 168 static void voyagerfb_set_mode(struct voyagerfb_softc *, 169 const struct videomode *); 170 static bool voyagerfb_mode_ok(struct voyagerfb_softc *, 171 const struct videomode *); 172 static const struct videomode *voyagerfb_pick_mode(struct voyagerfb_softc *); 173 #endif 174 175 static void voyagerfb_rectfill(struct voyagerfb_softc *, int, int, int, int, 176 uint32_t); 177 static void voyagerfb_bitblt(void *, int, int, int, int, 178 int, int, int); 179 180 static void voyagerfb_cursor(void *, int, int, int); 181 static void voyagerfb_putchar_mono(void *, int, int, u_int, long); 182 static void voyagerfb_putchar_aa32(void *, int, int, u_int, long); 183 static void voyagerfb_putchar_aa8(void *, int, int, u_int, long); 184 static void voyagerfb_copycols(void *, int, int, int, int); 185 static void voyagerfb_erasecols(void *, int, int, int, long); 186 static void voyagerfb_copyrows(void *, int, int, int); 187 static void voyagerfb_eraserows(void *, int, int, long); 188 189 static int voyagerfb_set_curpos(struct voyagerfb_softc *, int, int); 190 static int voyagerfb_gcursor(struct voyagerfb_softc *, 191 struct wsdisplay_cursor *); 192 static int voyagerfb_scursor(struct voyagerfb_softc *, 193 struct wsdisplay_cursor *); 194 195 struct wsdisplay_accessops voyagerfb_accessops = { 196 voyagerfb_ioctl, 197 voyagerfb_mmap, 198 NULL, /* alloc_screen */ 199 NULL, /* free_screen */ 200 NULL, /* show_screen */ 201 NULL, /* load_font */ 202 NULL, /* pollc */ 203 NULL /* scroll */ 204 }; 205 206 static void voyagerfb_setup_backlight(struct voyagerfb_softc *); 207 static void voyagerfb_brightness_up(device_t); 208 static void voyagerfb_brightness_down(device_t); 209 /* set backlight level */ 210 static void voyagerfb_set_backlight(struct voyagerfb_softc *, int); 211 /* turn backlight on and off without messing with the level */ 212 static void voyagerfb_switch_backlight(struct voyagerfb_softc *, int); 213 214 /* wait for FIFO empty so we can feed it another command */ 215 static inline void 216 voyagerfb_ready(struct voyagerfb_softc *sc) 217 { 218 do {} while ((bus_space_read_4(sc->sc_memt, sc->sc_regh, 219 SM502_SYSTEM_CTRL) & SM502_SYSCTL_FIFO_EMPTY) == 0); 220 } 221 222 /* wait for the drawing engine to be idle */ 223 static inline void 224 voyagerfb_wait(struct voyagerfb_softc *sc) 225 { 226 do {} while ((bus_space_read_4(sc->sc_memt, sc->sc_regh, 227 SM502_SYSTEM_CTRL) & SM502_SYSCTL_ENGINE_BUSY) != 0); 228 } 229 230 #ifdef VOYAGERFB_MODE_SETTING 231 /* route GPIO46/47 to the I2C master and enable the engine */ 232 static void 233 voyagerfb_i2c_enable(struct voyagerfb_softc *sc) 234 { 235 uint32_t reg; 236 237 /* make sure the GPIO/PWM/I2C block is clocked */ 238 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_CURRENT_GATE); 239 if ((reg & SM502_GATE_GPIO_ENABLE) == 0) { 240 reg |= SM502_GATE_GPIO_ENABLE; 241 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CURRENT_GATE, 242 reg); 243 } 244 245 /* select the I2C special function on GPIO46/47 (1 = not plain GPIO) */ 246 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_GPIO1_CONTROL); 247 reg |= SM502_GPIO1_I2C_BITS; 248 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_GPIO1_CONTROL, reg); 249 250 /* clear any latched error, then enable in standard (100kHz) mode */ 251 bus_space_write_1(sc->sc_memt, sc->sc_regh, SM502_I2C_RESET, 0); 252 bus_space_write_1(sc->sc_memt, sc->sc_regh, SM502_I2C_CONTROL, 253 SM502_I2C_CTL_ENABLE); 254 } 255 256 /* 257 * Run one hardware I2C transaction of up to SM502_I2C_MAX_BYTES 258 */ 259 static int 260 voyagerfb_i2c_xfer(struct voyagerfb_softc *sc, int rw, i2c_addr_t addr, 261 uint8_t *data, size_t len) 262 { 263 uint8_t status = 0; 264 size_t i; 265 int timo; 266 267 if (len == 0 || len > SM502_I2C_MAX_BYTES) 268 return EINVAL; 269 270 /* 271 * Reset the engine 272 */ 273 bus_space_write_1(sc->sc_memt, sc->sc_regh, SM502_I2C_RESET, 0); 274 delay(1); 275 276 bus_space_write_1(sc->sc_memt, sc->sc_regh, SM502_I2C_SLAVE_ADDR, 277 (uint8_t)(addr << 1) | (rw ? SM502_I2C_ADDR_READ : 0)); 278 279 if (rw == 0) { 280 for (i = 0; i < len; i++) 281 bus_space_write_1(sc->sc_memt, sc->sc_regh, 282 SM502_I2C_DATA0 + i, data[i]); 283 } 284 285 bus_space_write_1(sc->sc_memt, sc->sc_regh, SM502_I2C_BYTE_COUNT, 286 (uint8_t)(len - 1)); 287 288 /* keep ENABLE set, kick off the transfer (START self-clears) */ 289 bus_space_write_1(sc->sc_memt, sc->sc_regh, SM502_I2C_CONTROL, 290 SM502_I2C_CTL_ENABLE | SM502_I2C_CTL_START); 291 292 /* 16-byte tx at 100kHz, allow 10ms */ 293 for (timo = 10000; timo > 0; timo--) { 294 status = bus_space_read_1(sc->sc_memt, sc->sc_regh, 295 SM502_I2C_STATUS); 296 if (status & (SM502_I2C_STAT_COMPLETE | SM502_I2C_STAT_ERROR)) 297 break; 298 delay(1); 299 } 300 sc->sc_i2c_status = status; 301 302 if (timo == 0 || (status & SM502_I2C_STAT_ERROR)) 303 return EIO; 304 if (status & SM502_I2C_STAT_NAK) 305 return ENXIO; 306 307 if (rw != 0) { 308 for (i = 0; i < len; i++) 309 data[i] = bus_space_read_1(sc->sc_memt, sc->sc_regh, 310 SM502_I2C_DATA0 + i); 311 } 312 313 return 0; 314 } 315 316 static int 317 voyagerfb_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 318 const void *cmdbuf, size_t cmdlen, void *buf, size_t buflen, int flags) 319 { 320 struct voyagerfb_softc *sc = cookie; 321 uint8_t *p; 322 size_t done, chunk; 323 int rw, error; 324 325 if (cmdlen > 0) { 326 uint8_t tmp[SM502_I2C_MAX_BYTES]; 327 328 if (cmdlen > sizeof(tmp)) 329 return EINVAL; 330 memcpy(tmp, cmdbuf, cmdlen); 331 error = voyagerfb_i2c_xfer(sc, 0, addr, tmp, cmdlen); 332 if (error) 333 return error; 334 } 335 336 if (buflen == 0) 337 return 0; 338 339 rw = I2C_OP_READ_P(op) ? SM502_I2C_ADDR_READ : 0; 340 p = buf; 341 for (done = 0; done < buflen; done += chunk) { 342 chunk = buflen - done; 343 if (chunk > SM502_I2C_MAX_BYTES) 344 chunk = SM502_I2C_MAX_BYTES; 345 error = voyagerfb_i2c_xfer(sc, rw, addr, p + done, chunk); 346 if (error) 347 return error; 348 } 349 350 return 0; 351 } 352 353 /* read and parse the monitor's EDID over the hardware I2C master */ 354 static void 355 voyagerfb_ddc_read(struct voyagerfb_softc *sc) 356 { 357 int i; 358 359 voyagerfb_i2c_enable(sc); 360 361 iic_tag_init(&sc->sc_ddc_i2c); 362 sc->sc_ddc_i2c.ic_cookie = sc; 363 sc->sc_ddc_i2c.ic_exec = voyagerfb_i2c_exec; 364 365 /* some displays miss the first attempt */ 366 memset(sc->sc_edid, 0, sizeof(sc->sc_edid)); 367 for (i = 0; i < 3; i++) { 368 if (ddc_read_edid(&sc->sc_ddc_i2c, sc->sc_edid, 369 sizeof(sc->sc_edid)) == 0 && sc->sc_edid[1] != 0) 370 break; 371 memset(sc->sc_edid, 0, sizeof(sc->sc_edid)); 372 } 373 374 if (sc->sc_edid[1] == 0) { 375 aprint_normal_dev(sc->sc_dev, 376 "DDC: no EDID response (I2C status 0x%02x)\n", 377 sc->sc_i2c_status); 378 return; 379 } 380 381 if (edid_parse(sc->sc_edid, &sc->sc_edid_info) == -1) { 382 aprint_error_dev(sc->sc_dev, "DDC: EDID parse failed\n"); 383 return; 384 } 385 386 sc->sc_edid_valid = true; 387 } 388 389 /* 390 * Program the panel timing generator from a videomode. 391 */ 392 static void 393 voyagerfb_set_videomode(struct voyagerfb_softc *sc, const struct videomode *vm) 394 { 395 uint32_t ht, hs, vt, vs, ctrl; 396 397 ht = ((uint32_t)(vm->htotal - 1) << SM502_HT_HTOTAL_SHIFT) | 398 (vm->hdisplay - 1); 399 hs = ((uint32_t)(vm->hsync_end - vm->hsync_start) << 400 SM502_HS_HSWIDTH_SHIFT) | (vm->hsync_start - 1); 401 vt = ((uint32_t)(vm->vtotal - 1) << SM502_VT_VTOTAL_SHIFT) | 402 (vm->vdisplay - 1); 403 vs = ((uint32_t)(vm->vsync_end - vm->vsync_start) << 404 SM502_VS_VSHEIGHT_SHIFT) | (vm->vsync_start - 1); 405 406 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_HTOTAL, ht); 407 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_HSYNC, hs); 408 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_VTOTAL, vt); 409 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_VSYNC, vs); 410 411 ctrl = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL); 412 ctrl &= ~(SM502_PDC_HSYNC_PHASE_LOW | SM502_PDC_VSYNC_PHASE_LOW); 413 if (vm->flags & VID_NHSYNC) 414 ctrl |= SM502_PDC_HSYNC_PHASE_LOW; 415 if (vm->flags & VID_NVSYNC) 416 ctrl |= SM502_PDC_VSYNC_PHASE_LOW; 417 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL, ctrl); 418 } 419 420 /* return the clock register of the currently-selected power mode */ 421 static bus_size_t 422 voyagerfb_active_clock_reg(struct voyagerfb_softc *sc) 423 { 424 uint32_t ctrl = bus_space_read_4(sc->sc_memt, sc->sc_regh, 425 SM502_POWER_MODE_CONTROL); 426 427 return (ctrl & SM502_PMCTL_MODE_MASK) ? SM502_POWER_MODE1_CLOCK : 428 SM502_POWER_MODE0_CLOCK; 429 } 430 431 /* 432 * Find programmable-PLL M/N/K 433 */ 434 static void 435 voyagerfb_solve_pll(int target_khz, uint32_t *rm, uint32_t *rn, uint32_t *rk) 436 { 437 uint32_t k, n, m, bm = 1, bn = 1, bk = 1; 438 int actual, err, vco, vcodist; 439 int best_err = 0x7fffffff, best_vcodist = 0x7fffffff; 440 441 for (k = 1; k <= 2; k++) { 442 for (n = 1; n <= 127; n++) { 443 m = ((uint32_t)target_khz * n * k + SM502_XTAL_KHZ / 2) / 444 SM502_XTAL_KHZ; 445 if (m < 1 || m > 255) 446 continue; 447 vco = (int)(SM502_XTAL_KHZ * m / n); /* before /K */ 448 if (vco < SM502_PLL_VCO_MIN || vco > SM502_PLL_VCO_MAX) 449 continue; 450 actual = vco / (int)k; 451 err = actual - target_khz; 452 if (err < 0) 453 err = -err; 454 vcodist = vco - SM502_PLL_VCO_NOM; 455 if (vcodist < 0) 456 vcodist = -vcodist; 457 if (err < best_err || 458 (err == best_err && vcodist < best_vcodist)) { 459 best_err = err; 460 best_vcodist = vcodist; 461 bm = m; 462 bn = n; 463 bk = k; 464 } 465 } 466 } 467 *rm = bm; 468 *rn = bn; 469 *rk = bk; 470 } 471 472 /* 473 * Program the panel pixel clock via the PLL 474 */ 475 static void 476 voyagerfb_set_dotclock(struct voyagerfb_softc *sc, int dotclock_khz) 477 { 478 bus_size_t clk = voyagerfb_active_clock_reg(sc); 479 uint32_t m, n, k, pll, reg; 480 481 voyagerfb_solve_pll(dotclock_khz * 2, &m, &n, &k); 482 483 pll = SM502_PLL_POWER | 484 ((n << SM502_PLL_N_SHIFT) & SM502_PLL_N_MASK) | 485 ((m << SM502_PLL_M_SHIFT) & SM502_PLL_M_MASK); 486 if (k == 2) 487 pll |= SM502_PLL_K_DIV2; 488 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PROG_PLL, pll); 489 delay(20000); /* >= 16 ms PLL lock */ 490 491 /* select the programmable PLL as P2XCLK source, then wait */ 492 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, clk); 493 reg = (reg & ~SM502_PMC_P2_SRC_MASK) | 494 (SM502_PMC_SRC_PROGPLL << SM502_PMC_P2_SRC_SHIFT); 495 bus_space_write_4(sc->sc_memt, sc->sc_regh, clk, reg); 496 delay(20000); 497 498 /* P2 divider = 1, 2x stage active (DIS2X clear) */ 499 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, clk); 500 reg &= ~(SM502_PMC_P2_DIV_MASK | SM502_PMC_P2_DIS2X); 501 bus_space_write_4(sc->sc_memt, sc->sc_regh, clk, reg); 502 delay(20000); 503 } 504 505 static const struct videomode * 506 voyagerfb_pick_mode(struct voyagerfb_softc *sc) 507 { 508 const struct videomode *vm; 509 510 if (!sc->sc_edid_valid) 511 return NULL; 512 513 vm = sc->sc_edid_info.edid_preferred_mode; 514 if (vm != NULL && voyagerfb_mode_ok(sc, vm)) 515 return vm; 516 return NULL; 517 } 518 519 /* program the panel framebuffer window/plane geometry for a mode */ 520 static void 521 voyagerfb_set_geometry(struct voyagerfb_softc *sc, const struct videomode *vm) 522 { 523 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_FB_WIDTH, 524 (uint32_t)vm->hdisplay << 16); 525 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_FB_HEIGHT, 526 (uint32_t)vm->vdisplay << 16); 527 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_TL, 0); 528 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_BR, 529 ((uint32_t)vm->vdisplay << 16) | (uint32_t)vm->hdisplay); 530 /* the FB stride is programmed from sc_stride later, in voyagerfb_init */ 531 } 532 533 static void 534 voyagerfb_set_mode(struct voyagerfb_softc *sc, const struct videomode *vm) 535 { 536 uint32_t ctrl; 537 538 ctrl = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL); 539 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL, 540 ctrl & ~SM502_PDC_TIMING_ENABLE); 541 542 voyagerfb_set_dotclock(sc, vm->dot_clock); 543 voyagerfb_set_videomode(sc, vm); /* timing + sync polarity */ 544 voyagerfb_set_geometry(sc, vm); 545 546 ctrl = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL); 547 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL, 548 ctrl | SM502_PDC_TIMING_ENABLE); 549 } 550 551 /* 552 * Sanity-check a candidate mode 553 */ 554 static bool 555 voyagerfb_mode_ok(struct voyagerfb_softc *sc, const struct videomode *vm) 556 { 557 #ifdef VOYAGERFB_DEPTH_32 558 const int bytespp = 4; 559 #else 560 const int bytespp = 1; 561 #endif 562 const struct edid_range *r = &sc->sc_edid_info.edid_range; 563 int hfreq, vfreq; 564 565 if (vm->hdisplay > 4095 || vm->htotal > 4095 || 566 vm->vdisplay > 2047 || vm->vtotal > 2047) 567 return false; 568 if ((uint32_t)vm->hdisplay * vm->vdisplay * bytespp > sc->sc_fbsize) 569 return false; 570 571 if (!sc->sc_edid_valid || !sc->sc_edid_info.edid_have_range) 572 return false; 573 574 hfreq = vm->dot_clock / vm->htotal; /* kHz */ 575 vfreq = (vm->dot_clock * 1000) / (vm->htotal * vm->vtotal); /* Hz */ 576 577 if (vm->dot_clock / 1000 > r->er_max_clock) 578 return false; 579 if (hfreq < r->er_min_hfreq || hfreq > r->er_max_hfreq) 580 return false; 581 if (vfreq < r->er_min_vfreq || vfreq > r->er_max_vfreq) 582 return false; 583 584 return true; 585 } 586 587 #endif /* VOYAGERFB_MODE_SETTING */ 588 589 static int 590 voyagerfb_match(device_t parent, cfdata_t match, void *aux) 591 { 592 struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux; 593 594 if (strcmp(vaa->vaa_name, "voyagerfb") == 0) return 100; 595 return 0; 596 } 597 598 static void 599 voyagerfb_attach(device_t parent, device_t self, void *aux) 600 { 601 struct voyagerfb_softc *sc = device_private(self); 602 struct voyager_attach_args *vaa = aux; 603 struct rasops_info *ri; 604 struct wsemuldisplaydev_attach_args aa; 605 prop_dictionary_t dict; 606 unsigned long defattr; 607 uint32_t reg; 608 bool is_console = false; 609 int i, j; 610 uint8_t cmap[768]; 611 612 sc->sc_pc = vaa->vaa_pc; 613 sc->sc_pcitag = vaa->vaa_pcitag; 614 sc->sc_memt = vaa->vaa_tag; 615 sc->sc_dev = self; 616 617 aprint_normal("\n"); 618 619 dict = device_properties(self); 620 prop_dictionary_get_bool(dict, "is_console", &is_console); 621 622 sc->sc_fb = vaa->vaa_mem_pa; 623 sc->sc_fbh = vaa->vaa_memh; 624 sc->sc_fbaddr = bus_space_vaddr(sc->sc_memt, sc->sc_fbh); 625 626 sc->sc_reg = vaa->vaa_reg_pa; 627 sc->sc_regh = vaa->vaa_regh; 628 sc->sc_regsize = 2 * 1024 * 1024; 629 sc->sc_dataport = bus_space_vaddr(sc->sc_memt, sc->sc_regh); 630 sc->sc_dataport += SM502_DATAPORT; 631 632 sc->sc_gpio_cookie = device_private(parent); 633 634 #ifdef VOYAGERFB_MODE_SETTING 635 voyagerfb_ddc_read(sc); 636 #endif 637 638 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_DRAM_CONTROL); 639 switch(reg & 0x0000e000) { 640 case SM502_MEM_2M: 641 sc->sc_fbsize = 2 * 1024 * 1024; 642 break; 643 case SM502_MEM_4M: 644 sc->sc_fbsize = 4 * 1024 * 1024; 645 break; 646 case SM502_MEM_8M: 647 sc->sc_fbsize = 8 * 1024 * 1024; 648 break; 649 case SM502_MEM_16M: 650 sc->sc_fbsize = 16 * 1024 * 1024; 651 break; 652 case SM502_MEM_32M: 653 sc->sc_fbsize = 32 * 1024 * 1024; 654 break; 655 case SM502_MEM_64M: 656 sc->sc_fbsize = 64 * 1024 * 1024; 657 break; 658 } 659 660 sc->sc_width = (bus_space_read_4(sc->sc_memt, sc->sc_regh, 661 SM502_PANEL_FB_WIDTH) & SM502_FBW_WIN_WIDTH_MASK) >> 16; 662 sc->sc_height = (bus_space_read_4(sc->sc_memt, sc->sc_regh, 663 SM502_PANEL_FB_HEIGHT) & SM502_FBH_WIN_HEIGHT_MASK) >> 16; 664 665 /* 666 * Demand an enabled panel to attach... 667 */ 668 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, 669 SM502_PANEL_DISP_CTRL); 670 if (sc->sc_fbsize == 0 || 671 (reg & SM502_PDC_PANEL_ENABLE) == 0 || 672 sc->sc_width == 0 || 673 (uint32_t)sc->sc_width * sc->sc_height > sc->sc_fbsize) { 674 aprint_normal_dev(self, 675 "panel not initialized by firmware, not attaching\n"); 676 return; 677 } 678 679 #ifdef VOYAGERFB_MODE_SETTING 680 /* 681 * Switch to the best mode the monitor advertises via EDID. 682 */ 683 { 684 const struct videomode *vm = voyagerfb_pick_mode(sc); 685 686 if (vm != NULL) { 687 voyagerfb_set_mode(sc, vm); 688 sc->sc_width = vm->hdisplay; 689 sc->sc_height = vm->vdisplay; 690 aprint_normal_dev(self, 691 "mode set to %dx%d from EDID\n", 692 vm->hdisplay, vm->vdisplay); 693 } 694 } 695 #endif 696 697 #ifdef VOYAGERFB_DEPTH_32 698 sc->sc_depth = 32; 699 #else 700 sc->sc_depth = 8; 701 #endif 702 703 /* 704 * XXX yeah, casting the fb address to uint32_t is formally wrong 705 * but as far as I know there are no SM502 with 64bit BARs 706 */ 707 aprint_normal_dev(self, "%d MB video memory at 0x%08x\n", 708 (int)(sc->sc_fbsize >> 20), (uint32_t)sc->sc_fb); 709 710 /* init engine here */ 711 voyagerfb_init(sc); 712 713 aprint_normal_dev(self, "%d x %d, %d bit, stride %d\n", 714 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); 715 716 sc->sc_defaultscreen_descr = (struct wsscreen_descr){ 717 "default", 718 0, 0, 719 NULL, 720 8, 16, 721 WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE | 722 WSSCREEN_RESIZE, 723 NULL 724 }; 725 sc->sc_screens[0] = &sc->sc_defaultscreen_descr; 726 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; 727 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 728 sc->sc_locked = 0; 729 730 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, 731 &voyagerfb_accessops); 732 sc->vd.init_screen = voyagerfb_init_screen; 733 sc->vd.show_screen_cookie = &sc->sc_gc; 734 sc->vd.show_screen_cb = glyphcache_adapt; 735 736 /* backlight control */ 737 voyagerfb_setup_backlight(sc); 738 739 ri = &sc->sc_console_screen.scr_ri; 740 741 sc->sc_gc.gc_bitblt = voyagerfb_bitblt; 742 sc->sc_gc.gc_blitcookie = sc; 743 sc->sc_gc.gc_rop = ROP_COPY; 744 if (is_console) { 745 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 746 &defattr); 747 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 748 749 sc->sc_defaultscreen_descr.textops = &ri->ri_ops; 750 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; 751 sc->sc_defaultscreen_descr.nrows = ri->ri_rows; 752 sc->sc_defaultscreen_descr.ncols = ri->ri_cols; 753 } else { 754 if (sc->sc_console_screen.scr_ri.ri_rows == 0) { 755 /* do some minimal setup to avoid weirdness later */ 756 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 757 &defattr); 758 } else 759 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 760 } 761 glyphcache_init(&sc->sc_gc, sc->sc_height, 762 ((sc->sc_fbsize - 16 * 64) / sc->sc_stride) - 763 sc->sc_height, 764 sc->sc_width, 765 ri->ri_font->fontwidth, 766 ri->ri_font->fontheight, 767 defattr); 768 if (is_console) 769 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, 770 defattr); 771 772 rasops_get_cmap(ri, cmap, sizeof(cmap)); 773 j = 0; 774 if (sc->sc_depth <= 8) { 775 for (i = 0; i < 256; i++) { 776 777 sc->sc_cmap_red[i] = cmap[j]; 778 sc->sc_cmap_green[i] = cmap[j + 1]; 779 sc->sc_cmap_blue[i] = cmap[j + 2]; 780 voyagerfb_putpalreg(sc, i, cmap[j], cmap[j + 1], 781 cmap[j + 2]); 782 j += 3; 783 } 784 } 785 786 voyagerfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, 787 ri->ri_devcmap[(defattr >> 16) & 0xff]); 788 789 if (is_console) 790 vcons_replay_msgbuf(&sc->sc_console_screen); 791 792 aa.console = is_console; 793 aa.scrdata = &sc->sc_screenlist; 794 aa.accessops = &voyagerfb_accessops; 795 aa.accesscookie = &sc->vd; 796 797 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, 798 CFARGS(.iattr = "wsemuldisplaydev")); 799 } 800 801 static int 802 voyagerfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 803 struct lwp *l) 804 { 805 struct vcons_data *vd = v; 806 struct voyagerfb_softc *sc = vd->cookie; 807 struct wsdisplay_fbinfo *wdf; 808 struct vcons_screen *ms = vd->active; 809 struct wsdisplay_param *param; 810 811 switch (cmd) { 812 case WSDISPLAYIO_GTYPE: 813 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; 814 return 0; 815 816 /* PCI config read/write pass through. */ 817 case PCI_IOC_CFGREAD: 818 case PCI_IOC_CFGWRITE: 819 return pci_devioctl(sc->sc_pc, sc->sc_pcitag, 820 cmd, data, flag, l); 821 822 case WSDISPLAYIO_GET_BUSID: 823 return wsdisplayio_busid_pci(device_parent(sc->sc_dev), 824 sc->sc_pc, sc->sc_pcitag, data); 825 826 case WSDISPLAYIO_GINFO: 827 if (ms == NULL) 828 return ENODEV; 829 wdf = (void *)data; 830 wdf->height = ms->scr_ri.ri_height; 831 wdf->width = ms->scr_ri.ri_width; 832 wdf->depth = 32; 833 wdf->cmsize = 256; 834 return 0; 835 836 case WSDISPLAYIO_GETCMAP: 837 return voyagerfb_getcmap(sc, 838 (struct wsdisplay_cmap *)data); 839 840 case WSDISPLAYIO_PUTCMAP: 841 return voyagerfb_putcmap(sc, 842 (struct wsdisplay_cmap *)data); 843 844 case WSDISPLAYIO_LINEBYTES: 845 *(u_int *)data = sc->sc_stride; 846 return 0; 847 848 case WSDISPLAYIO_SMODE: { 849 int new_mode = *(int*)data; 850 if (new_mode != sc->sc_mode) { 851 sc->sc_mode = new_mode; 852 if(new_mode == WSDISPLAYIO_MODE_EMUL) { 853 #ifdef VOYAGERFB_DEPTH_32 854 sc->sc_depth = 32; 855 #else 856 sc->sc_depth = 8; 857 #endif 858 glyphcache_wipe(&sc->sc_gc); 859 voyagerfb_init(sc); 860 voyagerfb_restore_palette(sc); 861 vcons_redraw_screen(ms); 862 } else { 863 sc->sc_depth = 32; 864 voyagerfb_init(sc); 865 } 866 } 867 } 868 return 0; 869 870 case WSDISPLAYIO_GVIDEO: 871 *(int *)data = sc->sc_bl_on ? WSDISPLAYIO_VIDEO_ON : 872 WSDISPLAYIO_VIDEO_OFF; 873 return 0; 874 875 case WSDISPLAYIO_SVIDEO: { 876 int new_bl = *(int *)data; 877 878 voyagerfb_switch_backlight(sc, new_bl); 879 } 880 return 0; 881 882 case WSDISPLAYIO_GETPARAM: 883 param = (struct wsdisplay_param *)data; 884 switch (param->param) { 885 case WSDISPLAYIO_PARAM_BRIGHTNESS: 886 param->min = 0; 887 param->max = 255; 888 param->curval = sc->sc_bl_level; 889 return 0; 890 case WSDISPLAYIO_PARAM_BACKLIGHT: 891 param->min = 0; 892 param->max = 1; 893 param->curval = sc->sc_bl_on; 894 return 0; 895 } 896 return EPASSTHROUGH; 897 898 case WSDISPLAYIO_SETPARAM: 899 param = (struct wsdisplay_param *)data; 900 switch (param->param) { 901 case WSDISPLAYIO_PARAM_BRIGHTNESS: 902 voyagerfb_set_backlight(sc, param->curval); 903 return 0; 904 case WSDISPLAYIO_PARAM_BACKLIGHT: 905 voyagerfb_switch_backlight(sc, param->curval); 906 return 0; 907 } 908 return EPASSTHROUGH; 909 910 case WSDISPLAYIO_GCURPOS: 911 { 912 struct wsdisplay_curpos *pos; 913 914 pos = (struct wsdisplay_curpos *)data; 915 pos->x = sc->sc_cur_x; 916 pos->y = sc->sc_cur_y; 917 } 918 return 0; 919 920 case WSDISPLAYIO_SCURPOS: 921 { 922 struct wsdisplay_curpos *pos; 923 924 pos = (struct wsdisplay_curpos *)data; 925 voyagerfb_set_curpos(sc, pos->x, pos->y); 926 } 927 return 0; 928 929 case WSDISPLAYIO_GCURMAX: 930 { 931 struct wsdisplay_curpos *pos; 932 933 pos = (struct wsdisplay_curpos *)data; 934 pos->x = 64; 935 pos->y = 64; 936 } 937 return 0; 938 939 case WSDISPLAYIO_GCURSOR: 940 { 941 struct wsdisplay_cursor *cu; 942 943 cu = (struct wsdisplay_cursor *)data; 944 return voyagerfb_gcursor(sc, cu); 945 } 946 947 case WSDISPLAYIO_SCURSOR: 948 { 949 struct wsdisplay_cursor *cu; 950 951 cu = (struct wsdisplay_cursor *)data; 952 return voyagerfb_scursor(sc, cu); 953 } 954 955 case WSDISPLAYIO_GET_FBINFO: 956 { 957 struct wsdisplayio_fbinfo *fbi = data; 958 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 959 } 960 } 961 return EPASSTHROUGH; 962 } 963 964 static paddr_t 965 voyagerfb_mmap(void *v, void *vs, off_t offset, int prot) 966 { 967 struct vcons_data *vd = v; 968 struct voyagerfb_softc *sc = vd->cookie; 969 paddr_t pa; 970 971 /* 'regular' framebuffer mmap()ing */ 972 if (offset < sc->sc_fbsize) { 973 pa = bus_space_mmap(sc->sc_memt, sc->sc_fb + offset, 0, prot, 974 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE); 975 return pa; 976 } 977 978 /* 979 * restrict all other mappings to processes with privileges 980 */ 981 if (kauth_authorize_machdep(kauth_cred_get(), 982 KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) { 983 aprint_normal("%s: mmap() rejected.\n", 984 device_xname(sc->sc_dev)); 985 return -1; 986 } 987 988 if ((offset >= sc->sc_fb) && (offset < (sc->sc_fb + sc->sc_fbsize))) { 989 pa = bus_space_mmap(sc->sc_memt, offset, 0, prot, 990 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE); 991 return pa; 992 } 993 994 if ((offset >= sc->sc_reg) && 995 (offset < (sc->sc_reg + sc->sc_regsize))) { 996 pa = bus_space_mmap(sc->sc_memt, offset, 0, prot, 0); 997 return pa; 998 } 999 1000 return -1; 1001 } 1002 1003 static void 1004 voyagerfb_init_screen(void *cookie, struct vcons_screen *scr, 1005 int existing, long *defattr) 1006 { 1007 struct voyagerfb_softc *sc = cookie; 1008 struct rasops_info *ri = &scr->scr_ri; 1009 1010 ri->ri_depth = sc->sc_depth; 1011 ri->ri_width = sc->sc_width; 1012 ri->ri_height = sc->sc_height; 1013 ri->ri_stride = sc->sc_stride; 1014 ri->ri_flg = RI_CENTER | RI_FULLCLEAR; 1015 1016 ri->ri_bits = (char *)sc->sc_fbaddr; 1017 1018 if (existing) { 1019 ri->ri_flg |= RI_CLEAR; 1020 } 1021 1022 if (sc->sc_depth == 8) { 1023 ri->ri_flg |= RI_8BIT_IS_RGB; 1024 #ifdef VOYAGERFB_ANTIALIAS 1025 ri->ri_flg |= RI_ENABLE_ALPHA | RI_PREFER_ALPHA; 1026 #endif 1027 } 1028 if (sc->sc_depth == 32) { 1029 #ifdef VOYAGERFB_ANTIALIAS 1030 ri->ri_flg |= RI_ENABLE_ALPHA; 1031 #endif 1032 /* we always run in RGB */ 1033 ri->ri_rnum = 8; 1034 ri->ri_gnum = 8; 1035 ri->ri_bnum = 8; 1036 ri->ri_rpos = 16; 1037 ri->ri_gpos = 8; 1038 ri->ri_bpos = 0; 1039 } 1040 1041 scr->scr_flags |= VCONS_LOADFONT; 1042 1043 rasops_init(ri, 0, 0); 1044 ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE | 1045 WSSCREEN_RESIZE; 1046 1047 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 1048 sc->sc_width / ri->ri_font->fontwidth); 1049 1050 ri->ri_hw = scr; 1051 ri->ri_ops.copyrows = voyagerfb_copyrows; 1052 ri->ri_ops.copycols = voyagerfb_copycols; 1053 ri->ri_ops.eraserows = voyagerfb_eraserows; 1054 ri->ri_ops.erasecols = voyagerfb_erasecols; 1055 ri->ri_ops.cursor = voyagerfb_cursor; 1056 if (FONT_IS_ALPHA(ri->ri_font)) { 1057 switch (sc->sc_depth) { 1058 case 32: 1059 ri->ri_ops.putchar = voyagerfb_putchar_aa32; 1060 break; 1061 case 8: 1062 ri->ri_ops.putchar = voyagerfb_putchar_aa8; 1063 break; 1064 default: 1065 printf("alpha font at %d!?\n", sc->sc_depth); 1066 } 1067 } else 1068 ri->ri_ops.putchar = voyagerfb_putchar_mono; 1069 } 1070 1071 static int 1072 voyagerfb_putcmap(struct voyagerfb_softc *sc, struct wsdisplay_cmap *cm) 1073 { 1074 u_char *r, *g, *b; 1075 u_int index = cm->index; 1076 u_int count = cm->count; 1077 int i, error; 1078 u_char rbuf[256], gbuf[256], bbuf[256]; 1079 1080 #ifdef VOYAGERFB_DEBUG 1081 aprint_debug("putcmap: %d %d\n",index, count); 1082 #endif 1083 if (cm->index >= 256 || cm->count > 256 || 1084 (cm->index + cm->count) > 256) 1085 return EINVAL; 1086 error = copyin(cm->red, &rbuf[index], count); 1087 if (error) 1088 return error; 1089 error = copyin(cm->green, &gbuf[index], count); 1090 if (error) 1091 return error; 1092 error = copyin(cm->blue, &bbuf[index], count); 1093 if (error) 1094 return error; 1095 1096 memcpy(&sc->sc_cmap_red[index], &rbuf[index], count); 1097 memcpy(&sc->sc_cmap_green[index], &gbuf[index], count); 1098 memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count); 1099 1100 r = &sc->sc_cmap_red[index]; 1101 g = &sc->sc_cmap_green[index]; 1102 b = &sc->sc_cmap_blue[index]; 1103 1104 for (i = 0; i < count; i++) { 1105 voyagerfb_putpalreg(sc, index, *r, *g, *b); 1106 index++; 1107 r++, g++, b++; 1108 } 1109 return 0; 1110 } 1111 1112 static int 1113 voyagerfb_getcmap(struct voyagerfb_softc *sc, struct wsdisplay_cmap *cm) 1114 { 1115 u_int index = cm->index; 1116 u_int count = cm->count; 1117 int error; 1118 1119 if (index >= 255 || count > 256 || index + count > 256) 1120 return EINVAL; 1121 1122 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 1123 if (error) 1124 return error; 1125 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 1126 if (error) 1127 return error; 1128 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 1129 if (error) 1130 return error; 1131 1132 return 0; 1133 } 1134 1135 static void 1136 voyagerfb_restore_palette(struct voyagerfb_softc *sc) 1137 { 1138 int i; 1139 1140 for (i = 0; i < 256; i++) { 1141 voyagerfb_putpalreg(sc, i, sc->sc_cmap_red[i], 1142 sc->sc_cmap_green[i], sc->sc_cmap_blue[i]); 1143 } 1144 } 1145 1146 static int 1147 voyagerfb_putpalreg(struct voyagerfb_softc *sc, int idx, uint8_t r, 1148 uint8_t g, uint8_t b) 1149 { 1150 uint32_t reg; 1151 1152 reg = (r << 16) | (g << 8) | b; 1153 /* XXX we should probably write the CRT palette too */ 1154 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1155 SM502_PALETTE_PANEL + (idx << 2), reg); 1156 return 0; 1157 } 1158 1159 static void 1160 voyagerfb_init(struct voyagerfb_softc *sc) 1161 { 1162 int reg; 1163 1164 voyagerfb_wait(sc); 1165 /* disable colour compare */ 1166 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_COLOR_COMP_MASK, 0); 1167 /* allow writes to all planes */ 1168 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PLANEMASK, 1169 0xffffffff); 1170 /* disable clipping */ 1171 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CLIP_TOP_LEFT, 0); 1172 /* source and destination in local memory, no offset */ 1173 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC_BASE, 0); 1174 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST_BASE, 0); 1175 /* pitch is screen stride */ 1176 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PITCH, 1177 sc->sc_width | (sc->sc_width << 16)); 1178 /* window is screen width */ 1179 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_WINDOW_WIDTH, 1180 sc->sc_width | (sc->sc_width << 16)); 1181 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL); 1182 reg &= ~SM502_PDC_DEPTH_MASK; 1183 1184 switch (sc->sc_depth) { 1185 case 8: 1186 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1187 SM502_STRETCH, SM502_STRETCH_8BIT); 1188 sc->sc_stride = sc->sc_width; 1189 reg |= SM502_PDC_8BIT; 1190 break; 1191 case 16: 1192 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1193 SM502_STRETCH, SM502_STRETCH_16BIT); 1194 sc->sc_stride = sc->sc_width << 1; 1195 reg |= SM502_PDC_16BIT; 1196 break; 1197 case 24: 1198 case 32: 1199 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1200 SM502_STRETCH, SM502_STRETCH_32BIT); 1201 sc->sc_stride = sc->sc_width << 2; 1202 reg |= SM502_PDC_32BIT; 1203 break; 1204 } 1205 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_FB_OFFSET, 1206 (sc->sc_stride << 16) | sc->sc_stride); 1207 1208 /* clear the screen... */ 1209 voyagerfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, 0); 1210 1211 /* ...and then switch colour depth. For aesthetic reasons. */ 1212 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL, 1213 reg); 1214 1215 /* put the cursor at the end of video memory */ 1216 sc->sc_cursor_addr = sc->sc_fbsize - 16 * 64; /* XXX */ 1217 DPRINTF("%s: %08x\n", __func__, sc->sc_cursor_addr); 1218 sc->sc_cursor = (uint32_t *)((uint8_t *)bus_space_vaddr(sc->sc_memt, 1219 sc->sc_fbh) + sc->sc_cursor_addr); 1220 #ifdef VOYAGERFB_DEBUG 1221 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_XY, 1222 0x00100010); 1223 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_COL12, 1224 0x0000ffff); 1225 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_COL3, 1226 0x0000f800); 1227 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_ADDR, 1228 SM502_CRSR_ENABLE | sc->sc_cursor_addr); 1229 sc->sc_cursor[0] = 0x00000000; 1230 sc->sc_cursor[1] = 0x00000000; 1231 sc->sc_cursor[2] = 0xffffffff; 1232 sc->sc_cursor[3] = 0xffffffff; 1233 sc->sc_cursor[4] = 0xaaaaaaaa; 1234 sc->sc_cursor[5] = 0xaaaaaaaa; 1235 sc->sc_cursor[6] = 0xffffffff; 1236 sc->sc_cursor[7] = 0x00000000; 1237 #else 1238 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_ADDR, 1239 sc->sc_cursor_addr); 1240 #endif 1241 } 1242 1243 static void 1244 voyagerfb_rectfill(struct voyagerfb_softc *sc, int x, int y, int wi, int he, 1245 uint32_t colour) 1246 { 1247 1248 voyagerfb_ready(sc); 1249 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, 1250 ROP_COPY | 1251 SM502_CTRL_USE_ROP2 | 1252 SM502_CTRL_CMD_RECTFILL | 1253 SM502_CTRL_QUICKSTART_E); 1254 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_FOREGROUND, 1255 colour); 1256 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, 1257 (x << 16) | y); 1258 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION, 1259 (wi << 16) | he); 1260 } 1261 1262 static void 1263 voyagerfb_bitblt(void *cookie, int xs, int ys, int xd, int yd, 1264 int wi, int he, int rop) 1265 { 1266 struct voyagerfb_softc *sc = cookie; 1267 uint32_t cmd; 1268 1269 cmd = (rop & 0xf) | SM502_CTRL_USE_ROP2 | SM502_CTRL_CMD_BITBLT | 1270 SM502_CTRL_QUICKSTART_E; 1271 1272 voyagerfb_ready(sc); 1273 1274 if (xd <= xs) { 1275 /* left to right */ 1276 } else { 1277 /* 1278 * According to the manual this flag reverses only the blitter's 1279 * X direction. At least on my Gdium it also reverses the Y 1280 * direction 1281 */ 1282 cmd |= SM502_CTRL_R_TO_L; 1283 xs += wi - 1; 1284 xd += wi - 1; 1285 ys += he - 1; 1286 yd += he - 1; 1287 } 1288 1289 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd); 1290 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 1291 (xs << 16) | ys); 1292 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, 1293 (xd << 16) | yd); 1294 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION, 1295 (wi << 16) | he); 1296 } 1297 1298 static void 1299 voyagerfb_cursor(void *cookie, int on, int row, int col) 1300 { 1301 struct rasops_info *ri = cookie; 1302 struct vcons_screen *scr = ri->ri_hw; 1303 struct voyagerfb_softc *sc = scr->scr_cookie; 1304 int x, y, wi, he; 1305 1306 wi = ri->ri_font->fontwidth; 1307 he = ri->ri_font->fontheight; 1308 1309 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { 1310 x = ri->ri_ccol * wi + ri->ri_xorigin; 1311 y = ri->ri_crow * he + ri->ri_yorigin; 1312 if (ri->ri_flg & RI_CURSOR) { 1313 voyagerfb_bitblt(sc, x, y, x, y, wi, he, ROP_INVERT); 1314 ri->ri_flg &= ~RI_CURSOR; 1315 } 1316 ri->ri_crow = row; 1317 ri->ri_ccol = col; 1318 if (on) { 1319 x = ri->ri_ccol * wi + ri->ri_xorigin; 1320 y = ri->ri_crow * he + ri->ri_yorigin; 1321 voyagerfb_bitblt(sc, x, y, x, y, wi, he, ROP_INVERT); 1322 ri->ri_flg |= RI_CURSOR; 1323 } 1324 } else { 1325 scr->scr_ri.ri_crow = row; 1326 scr->scr_ri.ri_ccol = col; 1327 scr->scr_ri.ri_flg &= ~RI_CURSOR; 1328 } 1329 1330 } 1331 1332 static inline void 1333 voyagerfb_feed8(struct voyagerfb_softc *sc, uint8_t *data, int len) 1334 { 1335 uint32_t *port = (uint32_t *)sc->sc_dataport; 1336 int i; 1337 1338 for (i = 0; i < ((len + 3) & 0xfffc); i++) { 1339 *port = htole32(*data); 1340 data++; 1341 } 1342 } 1343 1344 static inline void 1345 voyagerfb_feed16(struct voyagerfb_softc *sc, uint16_t *data, int len) 1346 { 1347 uint32_t *port = (uint32_t *)sc->sc_dataport; 1348 uint8_t *d8 = (uint8_t *)data; 1349 int i; 1350 1351 len = len << 1; 1352 for (i = 0; i < ((len + 1) & 0xfffe); i++) { 1353 *port = htole32(le16dec(d8)); 1354 d8 += 2; 1355 } 1356 } 1357 1358 static void 1359 voyagerfb_putchar_mono(void *cookie, int row, int col, u_int c, long attr) 1360 { 1361 struct rasops_info *ri = cookie; 1362 struct wsdisplay_font *font = PICK_FONT(ri, c); 1363 struct vcons_screen *scr = ri->ri_hw; 1364 struct voyagerfb_softc *sc = scr->scr_cookie; 1365 uint32_t cmd; 1366 int fg, bg; 1367 uint8_t *data; 1368 int x, y, wi, he; 1369 1370 if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL) 1371 return; 1372 1373 if (!CHAR_IN_FONT(c, font)) 1374 return; 1375 1376 wi = font->fontwidth; 1377 he = font->fontheight; 1378 1379 bg = ri->ri_devcmap[(attr >> 16) & 0x0f]; 1380 fg = ri->ri_devcmap[(attr >> 24) & 0x0f]; 1381 x = ri->ri_xorigin + col * wi; 1382 y = ri->ri_yorigin + row * he; 1383 if (c == 0x20) { 1384 voyagerfb_rectfill(sc, x, y, wi, he, bg); 1385 return; 1386 } 1387 1388 data = WSFONT_GLYPH(c, font); 1389 1390 cmd = ROP_COPY | 1391 SM502_CTRL_USE_ROP2 | 1392 SM502_CTRL_CMD_HOSTWRT | 1393 SM502_CTRL_HOSTBLT_MONO | 1394 SM502_CTRL_QUICKSTART_E | 1395 SM502_CTRL_MONO_PACK_32BIT; 1396 voyagerfb_ready(sc); 1397 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd); 1398 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_FOREGROUND, fg); 1399 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_BACKGROUND, bg); 1400 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 0); 1401 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, (x << 16) | y); 1402 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1403 SM502_DIMENSION, (wi << 16) | he); 1404 /* now feed the data, padded to 32bit */ 1405 switch (ri->ri_font->stride) { 1406 case 1: 1407 voyagerfb_feed8(sc, data, ri->ri_fontscale); 1408 break; 1409 case 2: 1410 voyagerfb_feed16(sc, (uint16_t *)data, 1411 ri->ri_fontscale); 1412 break; 1413 } 1414 } 1415 1416 static void 1417 voyagerfb_putchar_aa32(void *cookie, int row, int col, u_int c, long attr) 1418 { 1419 struct rasops_info *ri = cookie; 1420 struct wsdisplay_font *font = PICK_FONT(ri, c); 1421 struct vcons_screen *scr = ri->ri_hw; 1422 struct voyagerfb_softc *sc = scr->scr_cookie; 1423 uint32_t cmd; 1424 int fg, bg; 1425 uint8_t *data; 1426 int x, y, wi, he; 1427 int i, j, r, g, b, aval, pad; 1428 int rf, gf, bf, rb, gb, bb; 1429 uint32_t pixel; 1430 int rv; 1431 1432 if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL) 1433 return; 1434 1435 if (!CHAR_IN_FONT(c, font)) 1436 return; 1437 1438 wi = font->fontwidth; 1439 he = font->fontheight; 1440 1441 bg = ri->ri_devcmap[(attr >> 16) & 0x0f]; 1442 fg = ri->ri_devcmap[(attr >> 24) & 0x0f]; 1443 x = ri->ri_xorigin + col * wi; 1444 y = ri->ri_yorigin + row * he; 1445 if (c == 0x20) { 1446 voyagerfb_rectfill(sc, x, y, wi, he, bg); 1447 return; 1448 } 1449 1450 data = WSFONT_GLYPH(c, font); 1451 /* 1452 * we can't accelerate the actual alpha blending but 1453 * we can at least use a host blit to go through the 1454 * pipeline instead of having to sync the engine 1455 */ 1456 1457 rv = glyphcache_try(&sc->sc_gc, c, x, y, attr); 1458 if (rv == GC_OK) 1459 return; 1460 1461 cmd = ROP_COPY | 1462 SM502_CTRL_USE_ROP2 | 1463 SM502_CTRL_CMD_HOSTWRT | 1464 SM502_CTRL_QUICKSTART_E; 1465 voyagerfb_ready(sc); 1466 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd); 1467 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 0); 1468 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, (x << 16) | y); 1469 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION, 1470 (wi << 16) | he); 1471 rf = (fg >> 16) & 0xff; 1472 rb = (bg >> 16) & 0xff; 1473 gf = (fg >> 8) & 0xff; 1474 gb = (bg >> 8) & 0xff; 1475 bf = fg & 0xff; 1476 bb = bg & 0xff; 1477 pad = wi & 1; 1478 for (i = 0; i < he; i++) { 1479 for (j = 0; j < wi; j++) { 1480 aval = *data; 1481 data++; 1482 if (aval == 0) { 1483 pixel = bg; 1484 } else if (aval == 255) { 1485 pixel = fg; 1486 } else { 1487 r = aval * rf + (255 - aval) * rb; 1488 g = aval * gf + (255 - aval) * gb; 1489 b = aval * bf + (255 - aval) * bb; 1490 pixel = (r & 0xff00) << 8 | 1491 (g & 0xff00) | 1492 (b & 0xff00) >> 8; 1493 } 1494 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1495 SM502_DATAPORT, pixel); 1496 } 1497 if (pad) 1498 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1499 SM502_DATAPORT, 0); 1500 } 1501 if (rv == GC_ADD) { 1502 glyphcache_add(&sc->sc_gc, c, x, y); 1503 } 1504 } 1505 1506 static void 1507 voyagerfb_putchar_aa8(void *cookie, int row, int col, u_int c, long attr) 1508 { 1509 struct rasops_info *ri = cookie; 1510 struct wsdisplay_font *font = PICK_FONT(ri, c); 1511 struct vcons_screen *scr = ri->ri_hw; 1512 struct voyagerfb_softc *sc = scr->scr_cookie; 1513 uint32_t cmd; 1514 int bg; 1515 uint8_t *data; 1516 int x, y, wi, he; 1517 int i, j, r, g, b, aval, pad; 1518 int r1, g1, b1, r0, g0, b0, fgo, bgo; 1519 uint32_t pixel = 0, latch = 0, bg8, fg8; 1520 int rv; 1521 1522 if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL) 1523 return; 1524 1525 if (!CHAR_IN_FONT(c, font)) 1526 return; 1527 1528 wi = font->fontwidth; 1529 he = font->fontheight; 1530 1531 bg = ri->ri_devcmap[(attr >> 16) & 0x0f]; 1532 x = ri->ri_xorigin + col * wi; 1533 y = ri->ri_yorigin + row * he; 1534 if (c == 0x20) { 1535 voyagerfb_rectfill(sc, x, y, wi, he, bg); 1536 return; 1537 } 1538 1539 data = WSFONT_GLYPH(c, font); 1540 /* 1541 * we can't accelerate the actual alpha blending but 1542 * we can at least use a host blit to go through the 1543 * pipeline instead of having to sync the engine 1544 */ 1545 1546 rv = glyphcache_try(&sc->sc_gc, c, x, y, attr); 1547 if (rv == GC_OK) 1548 return; 1549 1550 cmd = ROP_COPY | 1551 SM502_CTRL_USE_ROP2 | 1552 SM502_CTRL_CMD_HOSTWRT | 1553 SM502_CTRL_QUICKSTART_E; 1554 voyagerfb_ready(sc); 1555 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd); 1556 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 0); 1557 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, (x << 16) | y); 1558 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION, 1559 (wi << 16) | he); 1560 1561 /* 1562 * we need the RGB colours here, so get offsets into rasops_cmap 1563 */ 1564 fgo = ((attr >> 24) & 0xf) * 3; 1565 bgo = ((attr >> 16) & 0xf) * 3; 1566 1567 r0 = rasops_cmap[bgo]; 1568 r1 = rasops_cmap[fgo]; 1569 g0 = rasops_cmap[bgo + 1]; 1570 g1 = rasops_cmap[fgo + 1]; 1571 b0 = rasops_cmap[bgo + 2]; 1572 b1 = rasops_cmap[fgo + 2]; 1573 #define R3G3B2(r, g, b) ((r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)) 1574 bg8 = R3G3B2(r0, g0, b0); 1575 fg8 = R3G3B2(r1, g1, b1); 1576 1577 pad = wi & 4; 1578 for (i = 0; i < he; i++) { 1579 for (j = 0; j < wi; j++) { 1580 aval = *data; 1581 data++; 1582 if (aval == 0) { 1583 pixel = bg8; 1584 } else if (aval == 255) { 1585 pixel = fg8; 1586 } else { 1587 r = aval * r1 + (255 - aval) * r0; 1588 g = aval * g1 + (255 - aval) * g0; 1589 b = aval * b1 + (255 - aval) * b0; 1590 pixel = ((r & 0xe000) >> 8) | 1591 ((g & 0xe000) >> 11) | 1592 ((b & 0xc000) >> 14); 1593 } 1594 latch = (latch << 8) | pixel; 1595 /* write in 32bit chunks */ 1596 if ((j & 3) == 3) { 1597 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1598 SM502_DATAPORT, bswap32(latch)); 1599 latch = 0; 1600 } 1601 } 1602 /* if we have pixels left in latch write them out */ 1603 if ((j & 3) != 0) { 1604 latch = latch << ((4 - (i & 3)) << 3); 1605 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1606 SM502_DATAPORT, bswap32(latch)); 1607 } 1608 if (pad) 1609 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1610 SM502_DATAPORT, 0); 1611 } 1612 if (rv == GC_ADD) { 1613 glyphcache_add(&sc->sc_gc, c, x, y); 1614 } 1615 } 1616 1617 static void 1618 voyagerfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 1619 { 1620 struct rasops_info *ri = cookie; 1621 struct vcons_screen *scr = ri->ri_hw; 1622 struct voyagerfb_softc *sc = scr->scr_cookie; 1623 int32_t xs, xd, y, width, height; 1624 1625 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { 1626 xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol; 1627 xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol; 1628 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 1629 width = ri->ri_font->fontwidth * ncols; 1630 height = ri->ri_font->fontheight; 1631 voyagerfb_bitblt(sc, xs, y, xd, y, width, height, ROP_COPY); 1632 } 1633 } 1634 1635 static void 1636 voyagerfb_erasecols(void *cookie, int row, int startcol, int ncols, 1637 long fillattr) 1638 { 1639 struct rasops_info *ri = cookie; 1640 struct vcons_screen *scr = ri->ri_hw; 1641 struct voyagerfb_softc *sc = scr->scr_cookie; 1642 int32_t x, y, width, height, fg, bg, ul; 1643 1644 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { 1645 x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol; 1646 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 1647 width = ri->ri_font->fontwidth * ncols; 1648 height = ri->ri_font->fontheight; 1649 rasops_unpack_attr(fillattr, &fg, &bg, &ul); 1650 1651 voyagerfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]); 1652 } 1653 } 1654 1655 static void 1656 voyagerfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 1657 { 1658 struct rasops_info *ri = cookie; 1659 struct vcons_screen *scr = ri->ri_hw; 1660 struct voyagerfb_softc *sc = scr->scr_cookie; 1661 int32_t x, ys, yd, width, height; 1662 int i; 1663 1664 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { 1665 x = ri->ri_xorigin; 1666 ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow; 1667 yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow; 1668 width = ri->ri_emuwidth; 1669 height = ri->ri_font->fontheight * nrows; 1670 if ((nrows > 1) && (dstrow > srcrow)) { 1671 /* 1672 * the blitter can't do bottom-up copies so we have 1673 * to copy line by line here 1674 * should probably use a command sequence 1675 */ 1676 ys += (height - ri->ri_font->fontheight); 1677 yd += (height - ri->ri_font->fontheight); 1678 for (i = 0; i < nrows; i++) { 1679 voyagerfb_bitblt(sc, x, ys, x, yd, width, 1680 ri->ri_font->fontheight, ROP_COPY); 1681 ys -= ri->ri_font->fontheight; 1682 yd -= ri->ri_font->fontheight; 1683 } 1684 } else 1685 voyagerfb_bitblt(sc, x, ys, x, yd, width, height, 1686 ROP_COPY); 1687 } 1688 } 1689 1690 static void 1691 voyagerfb_eraserows(void *cookie, int row, int nrows, long fillattr) 1692 { 1693 struct rasops_info *ri = cookie; 1694 struct vcons_screen *scr = ri->ri_hw; 1695 struct voyagerfb_softc *sc = scr->scr_cookie; 1696 int32_t x, y, width, height, fg, bg, ul; 1697 1698 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { 1699 x = ri->ri_xorigin; 1700 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 1701 width = ri->ri_emuwidth; 1702 height = ri->ri_font->fontheight * nrows; 1703 rasops_unpack_attr(fillattr, &fg, &bg, &ul); 1704 1705 voyagerfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]); 1706 } 1707 } 1708 1709 /* backlight control */ 1710 static void 1711 voyagerfb_setup_backlight(struct voyagerfb_softc *sc) 1712 { 1713 /* switch the pin to gpio mode if it isn't already */ 1714 voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0); 1715 /* turn it on */ 1716 voyager_write_gpio(sc->sc_gpio_cookie, 0xffffffff, GPIO_BACKLIGHT); 1717 sc->sc_bl_on = 1; 1718 sc->sc_bl_level = 255; 1719 pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP, 1720 voyagerfb_brightness_up, TRUE); 1721 pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN, 1722 voyagerfb_brightness_down, TRUE); 1723 } 1724 1725 static void 1726 voyagerfb_set_backlight(struct voyagerfb_softc *sc, int level) 1727 { 1728 1729 /* 1730 * should we do nothing when backlight is off, should we just store the 1731 * level and use it when turning back on or should we just flip sc_bl_on 1732 * and turn the backlight on? 1733 * For now turn it on so a crashed screensaver can't get the user stuck 1734 * with a dark screen as long as hotkeys work 1735 */ 1736 if (level > 255) level = 255; 1737 if (level < 0) level = 0; 1738 if (level == sc->sc_bl_level) 1739 return; 1740 sc->sc_bl_level = level; 1741 if (sc->sc_bl_on == 0) 1742 sc->sc_bl_on = 1; 1743 /* and here we would actually muck with the hardware */ 1744 if ((level == 0) || (level == 255)) { 1745 /* in these cases bypass the PWM and use the gpio */ 1746 voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0); 1747 if (level == 0) { 1748 voyager_write_gpio(sc->sc_gpio_cookie, 1749 ~GPIO_BACKLIGHT, 0); 1750 } else { 1751 voyager_write_gpio(sc->sc_gpio_cookie, 1752 0xffffffff, GPIO_BACKLIGHT); 1753 } 1754 } else { 1755 uint32_t pwm; 1756 1757 pwm = voyager_set_pwm(20000, level * 1000 / 256); 1758 pwm |= SM502_PWM_ENABLE; 1759 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM0, pwm); 1760 1761 /* let the PWM take over */ 1762 voyager_control_gpio(sc->sc_gpio_cookie, 1763 0xffffffff, GPIO_BACKLIGHT); 1764 } 1765 } 1766 1767 static void 1768 voyagerfb_switch_backlight(struct voyagerfb_softc *sc, int on) 1769 { 1770 1771 if (on == sc->sc_bl_on) 1772 return; 1773 sc->sc_bl_on = on; 1774 if (on) { 1775 int level = sc->sc_bl_level; 1776 1777 sc->sc_bl_level = -1; 1778 voyagerfb_set_backlight(sc, level); 1779 } else { 1780 voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0); 1781 voyager_write_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0); 1782 } 1783 } 1784 1785 1786 static void 1787 voyagerfb_brightness_up(device_t dev) 1788 { 1789 struct voyagerfb_softc *sc = device_private(dev); 1790 1791 voyagerfb_set_backlight(sc, sc->sc_bl_level + 8); 1792 } 1793 1794 static void 1795 voyagerfb_brightness_down(device_t dev) 1796 { 1797 struct voyagerfb_softc *sc = device_private(dev); 1798 1799 voyagerfb_set_backlight(sc, sc->sc_bl_level - 8); 1800 } 1801 1802 static int 1803 voyagerfb_set_curpos(struct voyagerfb_softc *sc, int x, int y) 1804 { 1805 uint32_t val; 1806 int xx, yy; 1807 1808 sc->sc_cur_x = x; 1809 sc->sc_cur_y = y; 1810 1811 xx = x - sc->sc_hot_x; 1812 yy = y - sc->sc_hot_y; 1813 1814 if (xx < 0) xx = abs(xx) | 0x800; 1815 if (yy < 0) yy = abs(yy) | 0x800; 1816 1817 val = (xx & 0xffff) | (yy << 16); 1818 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_XY, val); 1819 1820 return 0; 1821 } 1822 1823 static int 1824 voyagerfb_gcursor(struct voyagerfb_softc *sc, struct wsdisplay_cursor *cur) 1825 { 1826 /* do nothing for now */ 1827 return 0; 1828 } 1829 1830 static int 1831 voyagerfb_scursor(struct voyagerfb_softc *sc, struct wsdisplay_cursor *cur) 1832 { 1833 if (cur->which & WSDISPLAY_CURSOR_DOCUR) { 1834 1835 bus_space_write_4(sc->sc_memt, sc->sc_regh, 1836 SM502_PANEL_CRSR_ADDR, 1837 sc->sc_cursor_addr | (cur->enable ? SM502_CRSR_ENABLE : 0)); 1838 DPRINTF("%s: %08x\n", __func__, sc->sc_cursor_addr); 1839 } 1840 if (cur->which & WSDISPLAY_CURSOR_DOHOT) { 1841 1842 sc->sc_hot_x = cur->hot.x; 1843 sc->sc_hot_y = cur->hot.y; 1844 } 1845 if (cur->which & WSDISPLAY_CURSOR_DOPOS) { 1846 1847 voyagerfb_set_curpos(sc, cur->pos.x, cur->pos.y); 1848 } 1849 if (cur->which & WSDISPLAY_CURSOR_DOCMAP) { 1850 int i, idx; 1851 uint32_t val; 1852 1853 for (i = 0; i < cur->cmap.count; i++) { 1854 val = ((cur->cmap.red[i] & 0xf8) << 8) | 1855 ((cur->cmap.green[i] & 0xfc) << 3) | 1856 ((cur->cmap.blue[i] & 0xf8) >> 3); 1857 idx = i + cur->cmap.index; 1858 bus_space_write_2(sc->sc_memt, sc->sc_regh, 1859 SM502_PANEL_CRSR_COL12 + (idx << 1), 1860 val); 1861 /* 1862 * if userland doesn't try to set the 3rd colour we 1863 * assume it expects an X11-style 2 colour cursor 1864 * X should be our main user anyway 1865 */ 1866 if ((idx == 1) && 1867 ((cur->cmap.count + cur->cmap.index) < 3)) { 1868 bus_space_write_2(sc->sc_memt, sc->sc_regh, 1869 SM502_PANEL_CRSR_COL3, 1870 val); 1871 } 1872 DPRINTF("%s: %d %04x\n", __func__, i + cur->cmap.index, 1873 val); 1874 } 1875 } 1876 if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) { 1877 1878 int i, j, cnt = 0; 1879 uint32_t latch = 0, omask; 1880 uint8_t imask; 1881 DPRINTF("%s: %d %d\n", __func__, cur->size.x, cur->size.y); 1882 for (i = 0; i < 256; i++) { 1883 omask = 0x00000001; 1884 imask = 0x01; 1885 cur->image[cnt] &= cur->mask[cnt]; 1886 for (j = 0; j < 8; j++) { 1887 if (cur->mask[cnt] & imask) 1888 latch |= omask; 1889 omask <<= 1; 1890 if (cur->image[cnt] & imask) 1891 latch |= omask; 1892 omask <<= 1; 1893 imask <<= 1; 1894 } 1895 cnt++; 1896 imask = 0x01; 1897 cur->image[cnt] &= cur->mask[cnt]; 1898 for (j = 0; j < 8; j++) { 1899 if (cur->mask[cnt] & imask) 1900 latch |= omask; 1901 omask <<= 1; 1902 if (cur->image[cnt] & imask) 1903 latch |= omask; 1904 omask <<= 1; 1905 imask <<= 1; 1906 } 1907 cnt++; 1908 sc->sc_cursor[i] = latch; 1909 latch = 0; 1910 } 1911 } 1912 return 0; 1913 } 1914