1 /* $NetBSD: zlcd.c,v 1.15 2011/07/19 15:11:49 dyoung Exp $ */ 2 /* $OpenBSD: zaurus_lcd.c,v 1.20 2006/06/02 20:50:14 miod Exp $ */ 3 /* NetBSD: lubbock_lcd.c,v 1.1 2003/08/09 19:38:53 bsh Exp */ 4 5 /* 6 * Copyright (c) 2002, 2003 Genetec Corporation. All rights reserved. 7 * Written by Hiroyuki Bessho for Genetec Corporation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Genetec Corporation may not be used to endorse or 18 * promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * LCD driver for Sharp Zaurus (based on the Intel Lubbock driver). 36 * 37 * Controlling LCD is almost completely done through PXA2X0's 38 * integrated LCD controller. Codes for it is arm/xscale/pxa2x0_lcd.c. 39 * 40 * Codes in this file provide platform specific things including: 41 * LCD on/off switch and backlight brightness 42 * LCD panel geometry 43 */ 44 45 #include <sys/cdefs.h> 46 __KERNEL_RCSID(0, "$NetBSD: zlcd.c,v 1.15 2011/07/19 15:11:49 dyoung Exp $"); 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/conf.h> 51 #include <sys/uio.h> 52 #include <sys/malloc.h> 53 #include <sys/bus.h> 54 55 #include <dev/cons.h> 56 #include <dev/wscons/wsconsio.h> 57 #include <dev/wscons/wsdisplayvar.h> 58 #include <dev/wscons/wscons_callbacks.h> 59 60 #include <dev/hpc/hpcfbio.h> 61 62 #include <arm/xscale/pxa2x0var.h> 63 #include <arm/xscale/pxa2x0reg.h> 64 #include <arm/xscale/pxa2x0_lcd.h> 65 66 #include <zaurus/zaurus/zaurus_var.h> 67 #include <zaurus/dev/zlcdvar.h> 68 #include <zaurus/dev/zsspvar.h> 69 #include <zaurus/dev/scoopvar.h> 70 #include <zaurus/dev/ioexpvar.h> 71 72 /* 73 * wsdisplay glue 74 */ 75 static struct pxa2x0_wsscreen_descr lcd_std_screen = { 76 .c = { 77 .name = "std", 78 .textops = &pxa2x0_lcd_emulops, 79 .fontwidth = 8, 80 .fontheight = 16, 81 .capabilities = WSSCREEN_WSCOLORS, 82 }, 83 .depth = 16, /* bits per pixel */ 84 .flags = RI_ROTATE_CW, /* quarter clockwise rotation */ 85 }; 86 87 static const struct wsscreen_descr *lcd_scr_descr[] = { 88 &lcd_std_screen.c 89 }; 90 91 static const struct wsscreen_list lcd_screen_list = { 92 .nscreens = __arraycount(lcd_scr_descr), 93 .screens = lcd_scr_descr, 94 }; 95 96 static int lcd_ioctl(void *, void *, u_long, void *, int, struct lwp *); 97 static int lcd_param(struct pxa2x0_lcd_softc *, u_long, 98 struct wsdisplay_param *); 99 static int lcd_show_screen(void *, void *, int, 100 void (*)(void *, int, int), void *); 101 102 struct wsdisplay_accessops lcd_accessops = { 103 lcd_ioctl, 104 pxa2x0_lcd_mmap, 105 pxa2x0_lcd_alloc_screen, 106 pxa2x0_lcd_free_screen, 107 lcd_show_screen, 108 NULL, 109 NULL, 110 NULL, 111 }; 112 113 #define CURRENT_DISPLAY &sharp_zaurus_C3000 114 115 const struct lcd_panel_geometry sharp_zaurus_C3000 = 116 { 117 480, /* Width */ 118 640, /* Height */ 119 0, /* No extra lines */ 120 121 LCDPANEL_ACTIVE | LCDPANEL_VSP | LCDPANEL_HSP, 122 1, /* clock divider */ 123 0, /* AC bias pin freq */ 124 125 0x28, /* horizontal sync pulse width */ 126 0x2e, /* BLW */ 127 0x7d, /* ELW */ 128 129 2, /* vertical sync pulse width */ 130 1, /* BFW */ 131 0, /* EFW */ 132 }; 133 134 struct sharp_lcd_backlight { 135 int duty; /* LZ9JG18 DAC value */ 136 int cont; /* BACKLIGHT_CONT signal */ 137 int on; /* BACKLIGHT_ON signal */ 138 }; 139 140 #define CURRENT_BACKLIGHT sharp_zaurus_C3000_bl 141 142 const struct sharp_lcd_backlight sharp_zaurus_C3000_bl[] = { 143 { 0x00, 0, 0 }, /* 0: Off */ 144 { 0x00, 0, 1 }, /* 1: 0% */ 145 { 0x01, 0, 1 }, /* 2: 20% */ 146 { 0x07, 0, 1 }, /* 3: 40% */ 147 { 0x01, 1, 1 }, /* 4: 60% */ 148 { 0x07, 1, 1 }, /* 5: 80% */ 149 { 0x11, 1, 1 }, /* 6: 100% */ 150 { -1, -1, -1 }, /* 7: Invalid */ 151 }; 152 153 static int lcd_match(device_t, cfdata_t, void *); 154 static void lcd_attach(device_t, device_t, void *); 155 156 CFATTACH_DECL_NEW(zlcd, sizeof(struct pxa2x0_lcd_softc), 157 lcd_match, lcd_attach, NULL, NULL); 158 159 static bool lcd_suspend(device_t, const pmf_qual_t *); 160 static bool lcd_resume(device_t, const pmf_qual_t *); 161 static void lcd_brightness_up(device_t); 162 static void lcd_brightness_down(device_t); 163 static void lcd_display_on(device_t); 164 static void lcd_display_off(device_t); 165 166 static int lcd_max_brightness(void); 167 static int lcd_get_brightness(void); 168 static void lcd_set_brightness(int); 169 static void lcd_set_brightness_internal(int); 170 static int lcd_get_backlight(void); 171 static void lcd_set_backlight(int); 172 173 static int 174 lcd_match(device_t parent, cfdata_t cf, void *aux) 175 { 176 177 if (ZAURUS_ISC1000 || ZAURUS_ISC3000) 178 return 1; 179 return 0; 180 } 181 182 static void 183 lcd_attach(device_t parent, device_t self, void *aux) 184 { 185 struct pxa2x0_lcd_softc *sc = device_private(self); 186 struct wsemuldisplaydev_attach_args aa; 187 188 sc->dev = self; 189 190 pxa2x0_lcd_attach_sub(sc, aux, CURRENT_DISPLAY); 191 192 aa.console = glass_console; 193 aa.scrdata = &lcd_screen_list; 194 aa.accessops = &lcd_accessops; 195 aa.accesscookie = sc; 196 197 (void) config_found(self, &aa, wsemuldisplaydevprint); 198 199 /* Start with approximately 40% of full brightness. */ 200 lcd_set_brightness(3); 201 202 if (!pmf_device_register(self, lcd_suspend, lcd_resume)) 203 aprint_error_dev(self, "couldn't establish power handler\n"); 204 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 205 lcd_brightness_up, true)) 206 aprint_error_dev(self, "couldn't register event handler\n"); 207 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 208 lcd_brightness_down, true)) 209 aprint_error_dev(self, "couldn't register event handler\n"); 210 if (!pmf_event_register(self, PMFE_DISPLAY_ON, 211 lcd_display_on, true)) 212 aprint_error_dev(self, "couldn't register event handler\n"); 213 if (!pmf_event_register(self, PMFE_DISPLAY_OFF, 214 lcd_display_off, true)) 215 aprint_error_dev(self, "couldn't register event handler\n"); 216 } 217 218 void 219 lcd_cnattach(void) 220 { 221 222 pxa2x0_lcd_cnattach(&lcd_std_screen, CURRENT_DISPLAY); 223 } 224 225 /* 226 * power management 227 */ 228 static bool 229 lcd_suspend(device_t dv, const pmf_qual_t *qual) 230 { 231 struct pxa2x0_lcd_softc *sc = device_private(dv); 232 233 lcd_set_brightness(0); 234 pxa2x0_lcd_suspend(sc); 235 236 return true; 237 } 238 239 static bool 240 lcd_resume(device_t dv, const pmf_qual_t *qual) 241 { 242 struct pxa2x0_lcd_softc *sc = device_private(dv); 243 244 pxa2x0_lcd_resume(sc); 245 lcd_set_brightness(lcd_get_brightness()); 246 247 return true; 248 } 249 250 static void 251 lcd_brightness_up(device_t dv) 252 { 253 254 lcd_set_brightness(lcd_get_brightness() + 1); 255 } 256 257 static void 258 lcd_brightness_down(device_t dv) 259 { 260 261 lcd_set_brightness(lcd_get_brightness() - 1); 262 } 263 264 static void 265 lcd_display_on(device_t dv) 266 { 267 268 lcd_blank(0); 269 lcd_set_backlight(1); 270 } 271 272 static void 273 lcd_display_off(device_t dv) 274 { 275 276 lcd_set_backlight(0); 277 lcd_blank(1); 278 } 279 280 /* 281 * wsdisplay accessops overrides 282 */ 283 static int 284 lcd_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) 285 { 286 struct pxa2x0_lcd_softc *sc = (struct pxa2x0_lcd_softc *)v; 287 struct hpcfb_fbconf *fbconf; 288 struct hpcfb_dspconf *dspconf; 289 int res = EINVAL; 290 291 switch (cmd) { 292 case WSDISPLAYIO_GETPARAM: 293 case WSDISPLAYIO_SETPARAM: 294 res = lcd_param(sc, cmd, (struct wsdisplay_param *)data); 295 break; 296 297 case HPCFBIO_GCONF: 298 fbconf = (struct hpcfb_fbconf *)data; 299 if (fbconf->hf_conf_index != 0 && 300 fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) { 301 break; 302 } 303 304 fbconf->hf_conf_index = 0; 305 fbconf->hf_nconfs = 1; 306 fbconf->hf_class = HPCFB_CLASS_RGBCOLOR; 307 strlcpy(fbconf->hf_name, "Sharp Zaurus frame buffer", 308 sizeof(fbconf->hf_name)); 309 strlcpy(fbconf->hf_conf_name, "default", 310 sizeof(fbconf->hf_conf_name)); 311 fbconf->hf_width = sc->geometry->panel_width; 312 fbconf->hf_height = sc->geometry->panel_height; 313 fbconf->hf_baseaddr = (u_long)sc->active->buf_va; 314 fbconf->hf_offset = 0; 315 fbconf->hf_bytes_per_line = sc->geometry->panel_width * 316 sc->active->depth / 8; 317 fbconf->hf_nplanes = 1; 318 fbconf->hf_bytes_per_plane = sc->geometry->panel_width * 319 sc->geometry->panel_height * sc->active->depth / 8; 320 fbconf->hf_pack_width = sc->active->depth; 321 fbconf->hf_pixels_per_pack = 1; 322 fbconf->hf_pixel_width = sc->active->depth; 323 fbconf->hf_access_flags = (0 324 | HPCFB_ACCESS_BYTE 325 | HPCFB_ACCESS_WORD 326 | HPCFB_ACCESS_DWORD); 327 fbconf->hf_order_flags = 0; 328 fbconf->hf_reg_offset = 0; 329 330 fbconf->hf_class_data_length = sizeof(struct hf_rgb_tag); 331 fbconf->hf_u.hf_rgb.hf_flags = 0; 332 fbconf->hf_u.hf_rgb.hf_red_width = 5; 333 fbconf->hf_u.hf_rgb.hf_red_shift = 11; 334 fbconf->hf_u.hf_rgb.hf_green_width = 6; 335 fbconf->hf_u.hf_rgb.hf_green_shift = 5; 336 fbconf->hf_u.hf_rgb.hf_blue_width = 5; 337 fbconf->hf_u.hf_rgb.hf_blue_shift = 0; 338 fbconf->hf_u.hf_rgb.hf_alpha_width = 0; 339 fbconf->hf_u.hf_rgb.hf_alpha_shift = 0; 340 341 fbconf->hf_ext_size = 0; 342 fbconf->hf_ext_data = NULL; 343 344 res = 0; 345 break; 346 347 case HPCFBIO_SCONF: 348 fbconf = (struct hpcfb_fbconf *)data; 349 if (fbconf->hf_conf_index != 0 && 350 fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) { 351 break; 352 } 353 /* nothing to do because we have only one configuration */ 354 res = 0; 355 break; 356 357 case HPCFBIO_GDSPCONF: 358 dspconf = (struct hpcfb_dspconf *)data; 359 if ((dspconf->hd_unit_index != 0 && 360 dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) || 361 (dspconf->hd_conf_index != 0 && 362 dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) { 363 break; 364 } 365 366 dspconf->hd_unit_index = 0; 367 dspconf->hd_nunits = 1; 368 dspconf->hd_class = HPCFB_DSP_CLASS_COLORLCD; 369 strlcpy(dspconf->hd_name, "Sharp Zaurus LCD", 370 sizeof(dspconf->hd_name)); 371 dspconf->hd_op_flags = 0; 372 dspconf->hd_conf_index = 0; 373 dspconf->hd_nconfs = 1; 374 strlcpy(dspconf->hd_conf_name, "default", 375 sizeof(dspconf->hd_conf_name)); 376 dspconf->hd_width = sc->geometry->panel_width; 377 dspconf->hd_height = sc->geometry->panel_height; 378 dspconf->hd_xdpi = HPCFB_DSP_DPI_UNKNOWN; 379 dspconf->hd_ydpi = HPCFB_DSP_DPI_UNKNOWN; 380 381 res = 0; 382 break; 383 384 case HPCFBIO_SDSPCONF: 385 dspconf = (struct hpcfb_dspconf *)data; 386 if ((dspconf->hd_unit_index != 0 && 387 dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) || 388 (dspconf->hd_conf_index != 0 && 389 dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) { 390 break; 391 } 392 /* 393 * nothing to do 394 * because we have only one unit and one configuration 395 */ 396 res = 0; 397 break; 398 399 case HPCFBIO_GOP: 400 case HPCFBIO_SOP: 401 /* curently not implemented... */ 402 break; 403 } 404 405 if (res == EINVAL) 406 res = pxa2x0_lcd_ioctl(v, vs, cmd, data, flag, l); 407 return res; 408 } 409 410 static int 411 lcd_show_screen(void *v, void *cookie, int waitok, 412 void (*cb_func)(void *, int, int), void *cb_arg) 413 { 414 int error; 415 416 error = pxa2x0_lcd_show_screen(v, cookie, waitok, cb_func, cb_arg); 417 if (error) 418 return (error); 419 420 /* Turn on LCD */ 421 lcd_set_brightness(lcd_get_brightness()); 422 423 return 0; 424 } 425 426 /* 427 * wsdisplay I/O controls 428 */ 429 static int 430 lcd_param(struct pxa2x0_lcd_softc *sc, u_long cmd, struct wsdisplay_param *dp) 431 { 432 int res = EINVAL; 433 434 switch (dp->param) { 435 case WSDISPLAYIO_PARAM_BACKLIGHT: 436 if (cmd == WSDISPLAYIO_GETPARAM) { 437 dp->min = 0; 438 dp->max = 1; 439 dp->curval = lcd_get_backlight(); 440 res = 0; 441 } else if (cmd == WSDISPLAYIO_SETPARAM) { 442 lcd_set_backlight(dp->curval); 443 res = 0; 444 } 445 break; 446 447 case WSDISPLAYIO_PARAM_CONTRAST: 448 /* unsupported */ 449 res = ENOTTY; 450 break; 451 452 case WSDISPLAYIO_PARAM_BRIGHTNESS: 453 if (cmd == WSDISPLAYIO_GETPARAM) { 454 dp->min = 1; 455 dp->max = lcd_max_brightness(); 456 dp->curval = lcd_get_brightness(); 457 res = 0; 458 } else if (cmd == WSDISPLAYIO_SETPARAM) { 459 lcd_set_brightness(dp->curval); 460 res = 0; 461 } 462 break; 463 } 464 465 return res; 466 } 467 468 /* 469 * LCD backlight 470 */ 471 472 static int lcdbrightnesscurval = 1; 473 static int lcdislit = 1; 474 static int lcdisblank = 0; 475 476 static int 477 lcd_max_brightness(void) 478 { 479 int i; 480 481 for (i = 0; CURRENT_BACKLIGHT[i].duty != -1; i++) 482 continue; 483 return i - 1; 484 } 485 486 static int 487 lcd_get_brightness(void) 488 { 489 490 return lcdbrightnesscurval; 491 } 492 493 static void 494 lcd_set_brightness(int newval) 495 { 496 int maxval; 497 498 maxval = lcd_max_brightness(); 499 if (newval < 0) 500 newval = 0; 501 else if (newval > maxval) 502 newval = maxval; 503 504 if (lcd_get_backlight() && !lcdisblank) 505 lcd_set_brightness_internal(newval); 506 507 if (newval > 0) 508 lcdbrightnesscurval = newval; 509 } 510 511 static void 512 lcd_set_brightness_internal(int newval) 513 { 514 static int curval = 1; 515 int i; 516 517 /* 518 * It appears that the C3000 backlight can draw too much power if we 519 * switch it from a low to a high brightness. Increasing brightness 520 * in steps avoids this issue. 521 */ 522 if (newval > curval) { 523 for (i = curval + 1; i <= newval; i++) { 524 (void)zssp_ic_send(ZSSP_IC_LZ9JG18, 525 CURRENT_BACKLIGHT[i].duty); 526 if (ZAURUS_ISC1000) 527 ioexp_set_backlight(CURRENT_BACKLIGHT[i].on, 528 CURRENT_BACKLIGHT[i].cont); 529 else 530 scoop_set_backlight(CURRENT_BACKLIGHT[i].on, 531 CURRENT_BACKLIGHT[i].cont); 532 delay(5000); 533 } 534 } else { 535 (void)zssp_ic_send(ZSSP_IC_LZ9JG18, 536 CURRENT_BACKLIGHT[newval].duty); 537 if (ZAURUS_ISC1000) 538 ioexp_set_backlight(CURRENT_BACKLIGHT[newval].on, 539 CURRENT_BACKLIGHT[newval].cont); 540 else 541 scoop_set_backlight(CURRENT_BACKLIGHT[newval].on, 542 CURRENT_BACKLIGHT[newval].cont); 543 } 544 545 curval = newval; 546 } 547 548 static int 549 lcd_get_backlight(void) 550 { 551 552 return lcdislit; 553 } 554 555 static void 556 lcd_set_backlight(int onoff) 557 { 558 559 if (!onoff) { 560 lcd_set_brightness(0); 561 lcdislit = 0; 562 } else { 563 lcdislit = 1; 564 lcd_set_brightness(lcd_get_brightness()); 565 } 566 } 567 568 void 569 lcd_blank(int blank) 570 { 571 572 if (blank) { 573 lcd_set_brightness(0); 574 lcdisblank = 1; 575 } else { 576 lcdisblank = 0; 577 lcd_set_brightness(lcd_get_brightness()); 578 } 579 } 580