1/* $NetBSD: mq200.c,v 1.36 2022/05/28 15:57:18 andvar Exp $ */ 2 3/*- 4 * Copyright (c) 2000, 2001 TAKEMURA Shin 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 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: mq200.c,v 1.36 2022/05/28 15:57:18 andvar Exp $"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/device.h> 38#include <sys/kernel.h> 39#include <sys/systm.h> 40#include <sys/reboot.h> 41 42#include <uvm/uvm_extern.h> 43 44#include <dev/wscons/wsconsio.h> 45 46#include <mips/locore.h> 47 48#include <machine/bootinfo.h> 49#include <machine/autoconf.h> 50#include <machine/config_hook.h> 51#include <machine/platid.h> 52#include <machine/platid_mask.h> 53 54#include "opt_mq200.h" 55#include <hpcmips/dev/mq200reg.h> 56#include <hpcmips/dev/mq200var.h> 57#include <hpcmips/dev/mq200priv.h> 58 59#include "bivideo.h" 60#if NBIVIDEO > 0 61#include <dev/hpc/bivideovar.h> 62#endif 63 64/* 65 * function prototypes 66 */ 67static void mq200_power(int, void *); 68static int mq200_hardpower(void *, int, long, void *); 69static int mq200_fbinit(struct hpcfb_fbconf *); 70static int mq200_ioctl(void *, u_long, void *, int, struct lwp *); 71static paddr_t mq200_mmap(void *, off_t offset, int); 72static void mq200_update_powerstate(struct mq200_softc *, int); 73void mq200_init_backlight(struct mq200_softc *, int); 74void mq200_init_brightness(struct mq200_softc *, int); 75void mq200_init_contrast(struct mq200_softc *, int); 76void mq200_set_brightness(struct mq200_softc *, int); 77void mq200_set_contrast(struct mq200_softc *, int); 78 79/* 80 * static variables 81 */ 82struct hpcfb_accessops mq200_ha = { 83 mq200_ioctl, mq200_mmap 84}; 85 86#ifdef MQ200_DEBUG 87int mq200_debug = MQ200DEBUG_CONF; 88#endif 89 90int 91mq200_probe(bus_space_tag_t iot, bus_space_handle_t ioh) 92{ 93 unsigned long regval; 94 95#if NBIVIDEO > 0 96 if (bivideo_dont_attach) /* some video driver already attached */ 97 return (0); 98#endif /* NBIVIDEO > 0 */ 99 100 regval = bus_space_read_4(iot, ioh, MQ200_PC00R); 101 VPRINTF("probe: vendor id=%04lx product id=%04lx\n", 102 regval & 0xffff, (regval >> 16) & 0xffff); 103 if (regval != ((MQ200_PRODUCT_ID << 16) | MQ200_VENDOR_ID)) 104 return (0); 105 106 return (1); 107} 108 109void 110mq200_attach(struct mq200_softc *sc) 111{ 112 unsigned long regval; 113 struct hpcfb_attach_args ha; 114 int console = (bootinfo->bi_cnuse & BI_CNUSE_SERIAL) ? 0 : 1; 115 116 printf(": "); 117 if (mq200_fbinit(&sc->sc_fbconf) != 0) { 118 /* just return so that hpcfb will not be attached */ 119 return; 120 } 121 122 sc->sc_fbconf.hf_baseaddr = (u_long)bootinfo->fb_addr; 123 sc->sc_fbconf.hf_offset = (u_long)sc->sc_fbconf.hf_baseaddr - 124 MIPS_PHYS_TO_KSEG1(mips_ptob(mips_btop(sc->sc_baseaddr))); 125 DPRINTF("hf_baseaddr=%lx\n", sc->sc_fbconf.hf_baseaddr); 126 DPRINTF("hf_offset=%lx\n", sc->sc_fbconf.hf_offset); 127 128 regval = mq200_read(sc, MQ200_PC08R); 129 printf("MQ200 Rev.%02lx video controller", regval & 0xff); 130 if (console) { 131 printf(", console"); 132 } 133 printf("\n"); 134 printf("%s: framebuffer address: 0x%08lx\n", 135 device_xname(sc->sc_dev), (u_long)bootinfo->fb_addr); 136 137 /* 138 * setup registers 139 */ 140 sc->sc_flags = 0; 141 sc->sc_baseclock = 12288; /* 12.288 MHz */ 142#ifdef MQ200_DEBUG 143 if (bootverbose) { 144 /* dump current setting */ 145 mq200_dump_all(sc); 146 mq200_dump_pll(sc); 147 } 148#endif 149 mq200_setup_regctx(sc); 150 mq200_mdsetup(sc); 151 if (sc->sc_md) { 152 int mode; 153 154 switch (sc->sc_fbconf.hf_pixel_width) { 155 case 1: mode = MQ200_GCC_1BPP; break; 156 case 2: mode = MQ200_GCC_2BPP; break; 157 case 4: mode = MQ200_GCC_4BPP; break; 158 case 8: mode = MQ200_GCC_8BPP; break; 159 case 16: mode = MQ200_GCC_16BPP_DIRECT; break; 160 default: 161 printf("%s: %dbpp isn't supported\n", 162 device_xname(sc->sc_dev), sc->sc_fbconf.hf_pixel_width); 163 return; 164 } 165 166 if (sc->sc_md->md_flags & MQ200_MD_HAVEFP) { 167 sc->sc_flags |= MQ200_SC_GC2_ENABLE; /* FP */ 168 } 169#if MQ200_USECRT 170 if (sc->sc_md->md_flags & MQ200_MD_HAVECRT) { 171 int i; 172 sc->sc_flags |= MQ200_SC_GC1_ENABLE; /* CRT */ 173 for (i = 0; i < mq200_crt_nparams; i++) { 174 sc->sc_crt = &mq200_crt_params[i]; 175 if (sc->sc_md->md_fp_width <= 176 mq200_crt_params[i].width && 177 sc->sc_md->md_fp_height <= 178 mq200_crt_params[i].height) 179 break; 180 } 181 } 182#endif 183 mq200_setup(sc); 184 185 if (sc->sc_flags & MQ200_SC_GC2_ENABLE) /* FP */ 186 mq200_win_enable(sc, MQ200_GC2, mode, 187 sc->sc_fbconf.hf_baseaddr, 188 sc->sc_fbconf.hf_width, sc->sc_fbconf.hf_height, 189 sc->sc_fbconf.hf_bytes_per_plane); 190 if (sc->sc_flags & MQ200_SC_GC1_ENABLE) /* CRT */ 191 mq200_win_enable(sc, MQ200_GC1, mode, 192 sc->sc_fbconf.hf_baseaddr, 193 sc->sc_fbconf.hf_width, sc->sc_fbconf.hf_height, 194 sc->sc_fbconf.hf_bytes_per_plane); 195 } 196#ifdef MQ200_DEBUG 197 if (sc->sc_md == NULL || bootverbose) { 198 mq200_dump_pll(sc); 199 } 200#endif 201 202 /* Add a power hook to power saving */ 203 sc->sc_mq200pwstate = MQ200_POWERSTATE_D0; 204 sc->sc_powerhook = powerhook_establish(device_xname(sc->sc_dev), 205 mq200_power, sc); 206 if (sc->sc_powerhook == NULL) 207 printf("%s: WARNING: unable to establish power hook\n", 208 device_xname(sc->sc_dev)); 209 210 /* Add a hard power hook to power saving */ 211 sc->sc_hardpowerhook = config_hook(CONFIG_HOOK_PMEVENT, 212 CONFIG_HOOK_PMEVENT_HARDPOWER, 213 CONFIG_HOOK_SHARE, 214 mq200_hardpower, sc); 215 if (sc->sc_hardpowerhook == NULL) 216 printf("%s: WARNING: unable to establish hard power hook\n", 217 device_xname(sc->sc_dev)); 218 219 /* initialize backlight brightness and lcd contrast */ 220 sc->sc_lcd_inited = 0; 221 mq200_init_brightness(sc, 1); 222 mq200_init_contrast(sc, 1); 223 mq200_init_backlight(sc, 1); 224 225 if (console && hpcfb_cnattach(&sc->sc_fbconf) != 0) { 226 panic("mq200_attach: can't init fb console"); 227 } 228 229 ha.ha_console = console; 230 ha.ha_accessops = &mq200_ha; 231 ha.ha_accessctx = sc; 232 ha.ha_curfbconf = 0; 233 ha.ha_nfbconf = 1; 234 ha.ha_fbconflist = &sc->sc_fbconf; 235 ha.ha_curdspconf = 0; 236 ha.ha_ndspconf = 1; 237 ha.ha_dspconflist = &sc->sc_dspconf; 238 239 config_found(sc->sc_dev, &ha, hpcfbprint, CFARGS_NONE); 240 241#if NBIVIDEO > 0 242 /* 243 * bivideo is no longer need 244 */ 245 bivideo_dont_attach = 1; 246#endif /* NBIVIDEO > 0 */ 247} 248 249static void 250mq200_update_powerstate(struct mq200_softc *sc, int updates) 251{ 252 253 if (updates & PWRSTAT_LCD) 254 config_hook_call(CONFIG_HOOK_POWERCONTROL, 255 CONFIG_HOOK_POWERCONTROL_LCD, 256 (void*)!(sc->sc_powerstate & 257 (PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND))); 258 259 if (updates & PWRSTAT_BACKLIGHT) 260 config_hook_call(CONFIG_HOOK_POWERCONTROL, 261 CONFIG_HOOK_POWERCONTROL_LCDLIGHT, 262 (void*)(!(sc->sc_powerstate & 263 (PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)) && 264 (sc->sc_powerstate & PWRSTAT_BACKLIGHT))); 265} 266 267static void 268mq200_power(int why, void *arg) 269{ 270 struct mq200_softc *sc = arg; 271 272 switch (why) { 273 case PWR_SUSPEND: 274 sc->sc_powerstate |= PWRSTAT_SUSPEND; 275 mq200_update_powerstate(sc, PWRSTAT_ALL); 276 break; 277 case PWR_STANDBY: 278 sc->sc_powerstate |= PWRSTAT_SUSPEND; 279 mq200_update_powerstate(sc, PWRSTAT_ALL); 280 break; 281 case PWR_RESUME: 282 sc->sc_powerstate &= ~PWRSTAT_SUSPEND; 283 mq200_update_powerstate(sc, PWRSTAT_ALL); 284 break; 285 } 286} 287 288static int 289mq200_hardpower(void *ctx, int type, long id, void *msg) 290{ 291 struct mq200_softc *sc = ctx; 292 int why = (int)msg; 293 294 switch (why) { 295 case PWR_SUSPEND: 296 sc->sc_mq200pwstate = MQ200_POWERSTATE_D2; 297 break; 298 case PWR_STANDBY: 299 sc->sc_mq200pwstate = MQ200_POWERSTATE_D3; 300 break; 301 case PWR_RESUME: 302 sc->sc_mq200pwstate = MQ200_POWERSTATE_D0; 303 break; 304 } 305 306 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 307 MQ200_PMCSR, sc->sc_mq200pwstate); 308 309 /* 310 * you should wait until the 311 * power state transit sequence will end. 312 */ 313 { 314 unsigned long tmp; 315 do { 316 tmp = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 317 MQ200_PMCSR); 318 } while ((tmp & 0x3) != (sc->sc_mq200pwstate & 0x3)); 319 delay(100000); /* XXX */ 320 } 321 322 return (0); 323} 324 325 326static int 327mq200_fbinit(struct hpcfb_fbconf *fb) 328{ 329 330 /* 331 * get fb settings from bootinfo 332 */ 333 if (bootinfo == NULL || 334 bootinfo->fb_addr == 0 || 335 bootinfo->fb_line_bytes == 0 || 336 bootinfo->fb_width == 0 || 337 bootinfo->fb_height == 0) { 338 printf("no frame buffer information.\n"); 339 return (-1); 340 } 341 342 /* zero fill */ 343 memset(fb, 0, sizeof(*fb)); 344 345 fb->hf_conf_index = 0; /* configuration index */ 346 fb->hf_nconfs = 1; /* how many configurations */ 347 strcpy(fb->hf_name, "built-in video"); 348 /* frame buffer name */ 349 strcpy(fb->hf_conf_name, "default"); 350 /* configuration name */ 351 fb->hf_height = bootinfo->fb_height; 352 fb->hf_width = bootinfo->fb_width; 353 fb->hf_baseaddr = mips_ptob(mips_btop(bootinfo->fb_addr)); 354 fb->hf_offset = (u_long)bootinfo->fb_addr - fb->hf_baseaddr; 355 /* frame buffer start offset */ 356 fb->hf_bytes_per_line = bootinfo->fb_line_bytes; 357 fb->hf_nplanes = 1; 358 fb->hf_bytes_per_plane = bootinfo->fb_height * 359 bootinfo->fb_line_bytes; 360 361 fb->hf_access_flags |= HPCFB_ACCESS_BYTE; 362 fb->hf_access_flags |= HPCFB_ACCESS_WORD; 363 fb->hf_access_flags |= HPCFB_ACCESS_DWORD; 364 365 switch (bootinfo->fb_type) { 366 /* 367 * monochrome 368 */ 369 case BIFB_D1_M2L_1: 370 fb->hf_access_flags |= HPCFB_ACCESS_REVERSE; 371 /* fall through */ 372 case BIFB_D1_M2L_0: 373 fb->hf_class = HPCFB_CLASS_GRAYSCALE; 374 fb->hf_access_flags |= HPCFB_ACCESS_STATIC; 375 fb->hf_pack_width = 8; 376 fb->hf_pixels_per_pack = 8; 377 fb->hf_pixel_width = 1; 378 fb->hf_class_data_length = sizeof(struct hf_gray_tag); 379 fb->hf_u.hf_gray.hf_flags = 0; /* reserved for future use */ 380 break; 381 382 /* 383 * gray scale 384 */ 385 case BIFB_D2_M2L_3: 386 case BIFB_D2_M2L_3x2: 387 fb->hf_access_flags |= HPCFB_ACCESS_REVERSE; 388 /* fall through */ 389 case BIFB_D2_M2L_0: 390 case BIFB_D2_M2L_0x2: 391 fb->hf_class = HPCFB_CLASS_GRAYSCALE; 392 fb->hf_access_flags |= HPCFB_ACCESS_STATIC; 393 fb->hf_pack_width = 8; 394 fb->hf_pixels_per_pack = 4; 395 fb->hf_pixel_width = 2; 396 fb->hf_class_data_length = sizeof(struct hf_gray_tag); 397 fb->hf_u.hf_gray.hf_flags = 0; /* reserved for future use */ 398 break; 399 400 case BIFB_D4_M2L_F: 401 case BIFB_D4_M2L_Fx2: 402 fb->hf_access_flags |= HPCFB_ACCESS_REVERSE; 403 /* fall through */ 404 case BIFB_D4_M2L_0: 405 case BIFB_D4_M2L_0x2: 406 fb->hf_class = HPCFB_CLASS_GRAYSCALE; 407 fb->hf_access_flags |= HPCFB_ACCESS_STATIC; 408 fb->hf_pack_width = 8; 409 fb->hf_pixels_per_pack = 2; 410 fb->hf_pixel_width = 4; 411 fb->hf_class_data_length = sizeof(struct hf_gray_tag); 412 fb->hf_u.hf_gray.hf_flags = 0; /* reserved for future use */ 413 break; 414 415 /* 416 * indexed color 417 */ 418 case BIFB_D8_FF: 419 fb->hf_access_flags |= HPCFB_ACCESS_REVERSE; 420 /* fall through */ 421 case BIFB_D8_00: 422 fb->hf_class = HPCFB_CLASS_INDEXCOLOR; 423 fb->hf_access_flags |= HPCFB_ACCESS_STATIC; 424 fb->hf_pack_width = 8; 425 fb->hf_pixels_per_pack = 1; 426 fb->hf_pixel_width = 8; 427 fb->hf_class_data_length = sizeof(struct hf_indexed_tag); 428 fb->hf_u.hf_indexed.hf_flags = 0; /* reserved for future use */ 429 break; 430 431 /* 432 * RGB color 433 */ 434 case BIFB_D16_FFFF: 435 fb->hf_access_flags |= HPCFB_ACCESS_REVERSE; 436 /* fall through */ 437 case BIFB_D16_0000: 438 fb->hf_class = HPCFB_CLASS_RGBCOLOR; 439 fb->hf_access_flags |= HPCFB_ACCESS_STATIC; 440 fb->hf_order_flags = HPCFB_REVORDER_BYTE; 441 fb->hf_pack_width = 16; 442 fb->hf_pixels_per_pack = 1; 443 fb->hf_pixel_width = 16; 444 445 fb->hf_class_data_length = sizeof(struct hf_rgb_tag); 446 fb->hf_u.hf_rgb.hf_flags = 0; /* reserved for future use */ 447 448 fb->hf_u.hf_rgb.hf_red_width = 5; 449 fb->hf_u.hf_rgb.hf_red_shift = 11; 450 fb->hf_u.hf_rgb.hf_green_width = 6; 451 fb->hf_u.hf_rgb.hf_green_shift = 5; 452 fb->hf_u.hf_rgb.hf_blue_width = 5; 453 fb->hf_u.hf_rgb.hf_blue_shift = 0; 454 fb->hf_u.hf_rgb.hf_alpha_width = 0; 455 fb->hf_u.hf_rgb.hf_alpha_shift = 0; 456 break; 457 458 default: 459 printf("unknown type (=%d).\n", bootinfo->fb_type); 460 return (-1); 461 break; 462 } 463 464 return (0); /* no error */ 465} 466 467int 468mq200_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 469{ 470 struct mq200_softc *sc = (struct mq200_softc *)v; 471 struct hpcfb_fbconf *fbconf; 472 struct hpcfb_dspconf *dspconf; 473 struct wsdisplay_cmap *cmap; 474 struct wsdisplay_param *dispparam; 475 476 switch (cmd) { 477 case WSDISPLAYIO_GETCMAP: 478 cmap = (struct wsdisplay_cmap *)data; 479 480 if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR || 481 sc->sc_fbconf.hf_pack_width != 8 || 482 256 <= cmap->index || 483 256 - cmap->index < cmap->count) 484 return (EINVAL); 485 486 /* 487 * This driver can't get color map. 488 */ 489 return (EINVAL); 490 491 case WSDISPLAYIO_PUTCMAP: 492 /* 493 * This driver can't set color map. 494 */ 495 return (EINVAL); 496 497 case WSDISPLAYIO_SVIDEO: 498 if (*(int *)data == WSDISPLAYIO_VIDEO_OFF) 499 sc->sc_powerstate |= PWRSTAT_VIDEOOFF; 500 else 501 sc->sc_powerstate &= ~PWRSTAT_VIDEOOFF; 502 mq200_update_powerstate(sc, PWRSTAT_ALL); 503 return 0; 504 505 case WSDISPLAYIO_GVIDEO: 506 *(int *)data = (sc->sc_powerstate&PWRSTAT_VIDEOOFF) ? 507 WSDISPLAYIO_VIDEO_OFF:WSDISPLAYIO_VIDEO_ON; 508 return 0; 509 510 case WSDISPLAYIO_GETPARAM: 511 dispparam = (struct wsdisplay_param*)data; 512 switch (dispparam->param) { 513 case WSDISPLAYIO_PARAM_BACKLIGHT: 514 VPRINTF("ioctl: GET:BACKLIGHT\n"); 515 mq200_init_brightness(sc, 0); 516 mq200_init_backlight(sc, 0); 517 VPRINTF("ioctl: GET:(real)BACKLIGHT %d\n", 518 (sc->sc_powerstate&PWRSTAT_BACKLIGHT)? 1: 0); 519 dispparam->min = 0; 520 dispparam->max = 1; 521 if (sc->sc_max_brightness > 0) 522 dispparam->curval = sc->sc_brightness > 0 523 ? 1: 0; 524 else 525 dispparam->curval = 526 (sc->sc_powerstate&PWRSTAT_BACKLIGHT) 527 ? 1: 0; 528 VPRINTF("ioctl: GET:BACKLIGHT:%d(%s)\n", 529 dispparam->curval, 530 sc->sc_max_brightness > 0? "brightness": "light"); 531 return 0; 532 break; 533 case WSDISPLAYIO_PARAM_CONTRAST: 534 VPRINTF("ioctl: GET:CONTRAST\n"); 535 mq200_init_contrast(sc, 0); 536 if (sc->sc_max_contrast > 0) { 537 dispparam->min = 0; 538 dispparam->max = sc->sc_max_contrast; 539 dispparam->curval = sc->sc_contrast; 540 VPRINTF("ioctl: GET:CONTRAST" 541 " max=%d, current=%d\n", 542 sc->sc_max_contrast, sc->sc_contrast); 543 return 0; 544 } else { 545 VPRINTF("ioctl: GET:CONTRAST EINVAL\n"); 546 return (EINVAL); 547 } 548 break; 549 case WSDISPLAYIO_PARAM_BRIGHTNESS: 550 VPRINTF("ioctl: GET:BRIGHTNESS\n"); 551 mq200_init_brightness(sc, 0); 552 if (sc->sc_max_brightness > 0) { 553 dispparam->min = 0; 554 dispparam->max = sc->sc_max_brightness; 555 dispparam->curval = sc->sc_brightness; 556 VPRINTF("ioctl: GET:BRIGHTNESS" 557 " max=%d, current=%d\n", 558 sc->sc_max_brightness, sc->sc_brightness); 559 return 0; 560 } else { 561 VPRINTF("ioctl: GET:BRIGHTNESS EINVAL\n"); 562 return (EINVAL); 563 } 564 return (EINVAL); 565 default: 566 return (EINVAL); 567 } 568 return (0); 569 570 case WSDISPLAYIO_SETPARAM: 571 dispparam = (struct wsdisplay_param*)data; 572 switch (dispparam->param) { 573 case WSDISPLAYIO_PARAM_BACKLIGHT: 574 VPRINTF("ioctl: SET:BACKLIGHT\n"); 575 if (dispparam->curval < 0 || 576 1 < dispparam->curval) 577 return (EINVAL); 578 mq200_init_brightness(sc, 0); 579 VPRINTF("ioctl: SET:max brightness=%d\n", 580 sc->sc_max_brightness); 581 if (sc->sc_max_brightness > 0) { /* dimmer */ 582 if (dispparam->curval == 0){ 583 sc->sc_brightness_save = 584 sc->sc_brightness; 585 mq200_set_brightness(sc, 0); /* min */ 586 } else { 587 if (sc->sc_brightness_save == 0) 588 sc->sc_brightness_save = 589 sc->sc_max_brightness; 590 mq200_set_brightness(sc, 591 sc->sc_brightness_save); 592 } 593 VPRINTF("ioctl: SET:BACKLIGHT:" 594 " brightness=%d\n", sc->sc_brightness); 595 } else { /* off */ 596 if (dispparam->curval == 0) 597 sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT; 598 else 599 sc->sc_powerstate |= PWRSTAT_BACKLIGHT; 600 VPRINTF("ioctl: SET:BACKLIGHT:" 601 " powerstate %d\n", 602 (sc->sc_powerstate & PWRSTAT_BACKLIGHT) 603 ? 1 : 0); 604 mq200_update_powerstate(sc, PWRSTAT_BACKLIGHT); 605 VPRINTF("ioctl: SET:BACKLIGHT:%d\n", 606 (sc->sc_powerstate & PWRSTAT_BACKLIGHT) 607 ? 1 : 0); 608 } 609 return 0; 610 break; 611 case WSDISPLAYIO_PARAM_CONTRAST: 612 VPRINTF("ioctl: SET:CONTRAST\n"); 613 mq200_init_contrast(sc, 0); 614 if (dispparam->curval < 0 || 615 sc->sc_max_contrast < dispparam->curval) 616 return (EINVAL); 617 if (sc->sc_max_contrast > 0) { 618 int org = sc->sc_contrast; 619 mq200_set_contrast(sc, dispparam->curval); 620 VPRINTF("ioctl: SET:CONTRAST" 621 " org=%d, current=%d\n", org, 622 sc->sc_contrast); 623 VPRINTF("ioctl: SETPARAM:" 624 " CONTRAST org=%d, current=%d\n", org, 625 sc->sc_contrast); 626 return 0; 627 } else { 628 VPRINTF("ioctl: SET:CONTRAST EINVAL\n"); 629 return (EINVAL); 630 } 631 break; 632 case WSDISPLAYIO_PARAM_BRIGHTNESS: 633 VPRINTF("ioctl: SET:BRIGHTNESS\n"); 634 mq200_init_brightness(sc, 0); 635 if (dispparam->curval < 0 || 636 sc->sc_max_brightness < dispparam->curval) 637 return (EINVAL); 638 if (sc->sc_max_brightness > 0) { 639 int org = sc->sc_brightness; 640 mq200_set_brightness(sc, dispparam->curval); 641 VPRINTF("ioctl: SET:BRIGHTNESS" 642 " org=%d, current=%d\n", org, 643 sc->sc_brightness); 644 return 0; 645 } else { 646 VPRINTF("ioctl: SET:BRIGHTNESS EINVAL\n"); 647 return (EINVAL); 648 } 649 break; 650 default: 651 return (EINVAL); 652 } 653 return (0); 654 655 case HPCFBIO_GCONF: 656 fbconf = (struct hpcfb_fbconf *)data; 657 if (fbconf->hf_conf_index != 0 && 658 fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) { 659 return (EINVAL); 660 } 661 *fbconf = sc->sc_fbconf; /* structure assignment */ 662 return (0); 663 case HPCFBIO_SCONF: 664 fbconf = (struct hpcfb_fbconf *)data; 665 if (fbconf->hf_conf_index != 0 && 666 fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) { 667 return (EINVAL); 668 } 669 /* 670 * nothing to do because we have only one configuration 671 */ 672 return (0); 673 case HPCFBIO_GDSPCONF: 674 dspconf = (struct hpcfb_dspconf *)data; 675 if ((dspconf->hd_unit_index != 0 && 676 dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) || 677 (dspconf->hd_conf_index != 0 && 678 dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) { 679 return (EINVAL); 680 } 681 *dspconf = sc->sc_dspconf; /* structure assignment */ 682 return (0); 683 case HPCFBIO_SDSPCONF: 684 dspconf = (struct hpcfb_dspconf *)data; 685 if ((dspconf->hd_unit_index != 0 && 686 dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) || 687 (dspconf->hd_conf_index != 0 && 688 dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) { 689 return (EINVAL); 690 } 691 /* 692 * nothing to do 693 * because we have only one unit and one configuration 694 */ 695 return (0); 696 case HPCFBIO_GOP: 697 case HPCFBIO_SOP: 698 /* 699 * currently not implemented... 700 */ 701 return (EINVAL); 702 } 703 704 return (EPASSTHROUGH); 705} 706 707paddr_t 708mq200_mmap(void *ctx, off_t offset, int prot) 709{ 710 struct mq200_softc *sc = (struct mq200_softc *)ctx; 711 712 if (offset < 0 || MQ200_MAPSIZE <= offset) 713 return -1; 714 715 return mips_btop(sc->sc_baseaddr + offset); 716} 717 718 719void 720mq200_init_backlight(struct mq200_softc *sc, int inattach) 721{ 722 int val = -1; 723 724 if (sc->sc_lcd_inited&BACKLIGHT_INITED) 725 return; 726 727 if (config_hook_call(CONFIG_HOOK_GET, 728 CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) { 729 /* we can get real light state */ 730 VPRINTF("init_backlight: real backlight=%d\n", val); 731 if (val == 0) 732 sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT; 733 else 734 sc->sc_powerstate |= PWRSTAT_BACKLIGHT; 735 sc->sc_lcd_inited |= BACKLIGHT_INITED; 736 } else if (inattach) { 737 /* 738 we cannot get real light state in attach time 739 because light device not yet attached. 740 we will retry in !inattach. 741 temporary assume light is on. 742 */ 743 sc->sc_powerstate |= PWRSTAT_BACKLIGHT; 744 } else { 745 /* we cannot get real light state, so work by myself state */ 746 sc->sc_lcd_inited |= BACKLIGHT_INITED; 747 } 748} 749 750void 751mq200_init_brightness(struct mq200_softc *sc, int inattach) 752{ 753 int val = -1; 754 755 if (sc->sc_lcd_inited&BRIGHTNESS_INITED) 756 return; 757 758 VPRINTF("init_brightness\n"); 759 if (config_hook_call(CONFIG_HOOK_GET, 760 CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) { 761 /* we can get real brightness max */ 762 VPRINTF("init_brightness: real brightness max=%d\n", val); 763 sc->sc_max_brightness = val; 764 val = -1; 765 if (config_hook_call(CONFIG_HOOK_GET, 766 CONFIG_HOOK_BRIGHTNESS, &val) != -1) { 767 /* we can get real brightness */ 768 VPRINTF("init_brightness: real brightness=%d\n", val); 769 sc->sc_brightness_save = sc->sc_brightness = val; 770 } else { 771 sc->sc_brightness_save = 772 sc->sc_brightness = sc->sc_max_brightness; 773 } 774 sc->sc_lcd_inited |= BRIGHTNESS_INITED; 775 } else if (inattach) { 776 /* 777 we cannot get real brightness in attach time 778 because brightness device not yet attached. 779 we will retry in !inattach. 780 */ 781 sc->sc_max_brightness = -1; 782 sc->sc_brightness = -1; 783 sc->sc_brightness_save = -1; 784 } else { 785 /* we cannot get real brightness */ 786 sc->sc_lcd_inited |= BRIGHTNESS_INITED; 787 } 788 789 return; 790} 791 792 793void 794mq200_init_contrast(struct mq200_softc *sc, int inattach) 795{ 796 int val = -1; 797 798 if (sc->sc_lcd_inited&CONTRAST_INITED) 799 return; 800 801 VPRINTF("init_contrast\n"); 802 if (config_hook_call(CONFIG_HOOK_GET, 803 CONFIG_HOOK_CONTRAST_MAX, &val) != -1) { 804 /* we can get real contrast max */ 805 VPRINTF("init_contrast: real contrast max=%d\n", val); 806 sc->sc_max_contrast = val; 807 val = -1; 808 if (config_hook_call(CONFIG_HOOK_GET, 809 CONFIG_HOOK_CONTRAST, &val) != -1) { 810 /* we can get real contrast */ 811 VPRINTF("init_contrast: real contrast=%d\n", val); 812 sc->sc_contrast = val; 813 } else { 814 sc->sc_contrast = sc->sc_max_contrast; 815 } 816 sc->sc_lcd_inited |= CONTRAST_INITED; 817 } else if (inattach) { 818 /* 819 we cannot get real contrast in attach time 820 because contrast device not yet attached. 821 we will retry in !inattach. 822 */ 823 sc->sc_max_contrast = -1; 824 sc->sc_contrast = -1; 825 } else { 826 /* we cannot get real contrast */ 827 sc->sc_lcd_inited |= CONTRAST_INITED; 828 } 829 830 return; 831} 832 833 834void 835mq200_set_brightness(struct mq200_softc *sc, int val) 836{ 837 sc->sc_brightness = val; 838 839 config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, &val); 840 if (config_hook_call(CONFIG_HOOK_GET, 841 CONFIG_HOOK_BRIGHTNESS, &val) != -1) { 842 sc->sc_brightness = val; 843 } 844} 845 846void 847mq200_set_contrast(struct mq200_softc *sc, int val) 848{ 849 sc->sc_contrast = val; 850 851 config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, &val); 852 if (config_hook_call(CONFIG_HOOK_GET, 853 CONFIG_HOOK_CONTRAST, &val) != -1) { 854 sc->sc_contrast = val; 855 } 856} 857