1 /* $NetBSD: j6x0lcd.c,v 1.14 2009/04/05 02:23:00 uwe Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2005 Valeriy E. Ushakov 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: j6x0lcd.c,v 1.14 2009/04/05 02:23:00 uwe Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/systm.h> 37 38 #include <machine/platid.h> 39 #include <machine/platid_mask.h> 40 41 #include <machine/config_hook.h> 42 43 #include <sh3/dacreg.h> 44 #include <hpcsh/dev/hd64461/hd64461var.h> /* XXX: for hd64461_reg_read_2 &c */ 45 #include <hpcsh/dev/hd64461/hd64461reg.h> 46 #include <hpcsh/dev/hd64461/hd64461gpioreg.h> 47 48 #define arraysize(ary) (sizeof(ary) / sizeof(ary[0])) 49 50 51 /* 52 * LCD power: controlled by pin 0 in HD64461 GPIO port B. 53 * 0 - power on 54 * 1 - power off 55 */ 56 #define HD64461_GPBDR_J6X0_LCD_OFF 0x01 57 58 #define HD64461_GPBCR_J6X0_LCD_OFF_MASK 0xfffc 59 #define HD64461_GPBCR_J6X0_LCD_OFF_BITS 0x0001 60 61 62 /* 63 * LCD brightness: controlled by DAC channel 0. Larger channel values 64 * mean dimmer. Values smaller (i.e. brighter) then 0x5e seems to 65 * result in no visible changes. 66 */ 67 #define J6X0LCD_BRIGHTNESS_DA_MAX 0x5e 68 #define J6X0LCD_BRIGHTNESS_DA_MIN 0xff 69 70 #define J6X0LCD_DA_TO_BRIGHTNESS(da) \ 71 (J6X0LCD_BRIGHTNESS_DA_MIN - (da)) 72 73 #define J6X0LCD_BRIGHTNESS_TO_DA(br) \ 74 (J6X0LCD_BRIGHTNESS_DA_MIN - (br)) 75 76 #define J6X0LCD_BRIGHTNESS_MAX \ 77 J6X0LCD_DA_TO_BRIGHTNESS(J6X0LCD_BRIGHTNESS_DA_MAX) 78 79 /* convenience macro to accesses DAC registers */ 80 #define DAC_(x) (*((volatile uint8_t *)SH7709_DA ## x)) 81 82 83 /* 84 * LCD contrast in 680 is controlled by pins 6..3 of HD64461 GPIO 85 * port B. 6th pin is the least significant bit, 3rd pin is the most 86 * significant. The bits are inverted: 0 = .1111...; 1 = .0111...; 87 * etc. Larger values mean "blacker". 88 * 89 * The contrast value is programmed by setting bits in the data 90 * register to all ones, and changing the mode of the pins in the 91 * control register, setting logical "ones" to GPIO output mode (1), 92 * and switching "zeroes" to input mode (3). 93 */ 94 #define HD64461_GPBDR_J680_CONTRAST_BITS 0x78 /* set */ 95 #define HD64461_GPBCR_J680_CONTRAST_MASK 0xc03f 96 97 static const uint8_t j6x0lcd_contrast680_pins[] = { 6, 5, 4, 3 }; 98 99 static const uint16_t j6x0lcd_contrast680_control_bits[] = { 100 0x1540, 0x3540, 0x1d40, 0x3d40, 0x1740, 0x3740, 0x1f40, 0x3f40, 101 0x15c0, 0x35c0, 0x1dc0, 0x3dc0, 0x17c0, 0x37c0, 0x1fc0, 0x3fc0 102 }; 103 104 105 /* 106 * LCD contrast in 620lx is controlled by pins 7,6,3,4,5 of HD64461 107 * GPIO port B (in the order from the least significant to the most 108 * significant). The bits are inverted: 0 = 11111...; 5 = 01110...; 109 * etc. Larger values mean "whiter". 110 * 111 * The contrast value is programmed by setting bits in the data 112 * register to all zeroes, and changing the mode of the pins in the 113 * control register, setting logical "ones" to GPIO output mode (1), 114 * and switching "zeroes" to input mode (3). 115 */ 116 #define HD64461_GPBDR_J620LX_CONTRAST_BITS 0xf8 /* clear */ 117 #define HD64461_GPBCR_J620LX_CONTRAST_MASK 0x003f 118 119 static const uint8_t j6x0lcd_contrast620lx_pins[] = { 7, 6, 3, 4, 5 }; 120 121 static const uint16_t j6x0lcd_contrast620lx_control_bits[] = { 122 0xffc0, 0x7fc0, 0xdfc0, 0x5fc0, 0xff40, 0x7f40, 0xdf40, 0x5f40, 123 0xfdc0, 0x7dc0, 0xddc0, 0x5dc0, 0xfd40, 0x7d40, 0xdd40, 0x5d40, 124 0xf7c0, 0x77c0, 0xd7c0, 0x57c0, 0xf740, 0x7740, 0xd740, 0x5740, 125 0xf5c0, 0x75c0, 0xd5c0, 0x55c0, 0xf540, 0x7540, 0xd540, 0x5540 126 }; 127 128 129 130 struct j6x0lcd_softc { 131 device_t sc_dev; 132 133 int sc_brightness; 134 int sc_contrast; 135 136 int sc_contrast_max; 137 uint16_t sc_contrast_mask; 138 const uint16_t *sc_contrast_control_bits; 139 }; 140 141 static int j6x0lcd_match(device_t, cfdata_t, void *); 142 static void j6x0lcd_attach(device_t, device_t, void *); 143 144 CFATTACH_DECL_NEW(j6x0lcd, sizeof(struct j6x0lcd_softc), 145 j6x0lcd_match, j6x0lcd_attach, NULL, NULL); 146 147 148 static int j6x0lcd_param(void *, int, long, void *); 149 static int j6x0lcd_power(void *, int, long, void *); 150 151 static int j6x0lcd_contrast_raw(uint16_t, int, const uint8_t *); 152 static void j6x0lcd_contrast_set(struct j6x0lcd_softc *, int); 153 154 155 156 static int 157 j6x0lcd_match(device_t parent, cfdata_t cf, void *aux) 158 { 159 160 /* 161 * XXX: platid_mask_MACH_HP_LX also matches 360LX. It's not 162 * confirmed whether touch panel in 360LX is connected this 163 * way. We may need to regroup platid masks. 164 */ 165 if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX) 166 && !platid_match(&platid, &platid_mask_MACH_HP_LX)) 167 return (0); 168 169 if (strcmp(cf->cf_name, "j6x0lcd") != 0) 170 return (0); 171 172 return (1); 173 } 174 175 176 static void 177 j6x0lcd_attach(device_t parent, device_t self, void *aux) 178 { 179 struct j6x0lcd_softc *sc; 180 uint16_t bcr, bdr; 181 uint8_t dcr, ddr; 182 183 aprint_naive("\n"); 184 185 sc = device_private(self); 186 sc->sc_dev = self; 187 188 /* 189 * Brightness is controlled by DAC channel 0. 190 */ 191 dcr = DAC_(CR); 192 dcr &= ~SH7709_DACR_DAE; /* want to control each channel separately */ 193 dcr |= SH7709_DACR_DAOE0; /* enable channel 0 */ 194 DAC_(CR) = dcr; 195 196 ddr = DAC_(DR0); 197 sc->sc_brightness = J6X0LCD_DA_TO_BRIGHTNESS(ddr); 198 199 /* 200 * Contrast and power are controlled by HD64461 GPIO port B. 201 */ 202 bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16); 203 bdr = hd64461_reg_read_2(HD64461_GPBDR_REG16); 204 205 /* 206 * Make sure LCD is turned on. 207 */ 208 bcr &= HD64461_GPBCR_J6X0_LCD_OFF_MASK; 209 bcr |= HD64461_GPBCR_J6X0_LCD_OFF_BITS; /* output mode */ 210 211 bdr &= ~HD64461_GPBDR_J6X0_LCD_OFF; 212 213 /* 214 * 620LX and 680 have different contrast control. 215 */ 216 if (platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)) { 217 bdr |= HD64461_GPBDR_J680_CONTRAST_BITS; 218 219 sc->sc_contrast_mask = 220 HD64461_GPBCR_J680_CONTRAST_MASK; 221 sc->sc_contrast_control_bits = 222 j6x0lcd_contrast680_control_bits; 223 sc->sc_contrast_max = 224 arraysize(j6x0lcd_contrast680_control_bits) - 1; 225 226 sc->sc_contrast = sc->sc_contrast_max 227 - j6x0lcd_contrast_raw(bcr, 228 arraysize(j6x0lcd_contrast680_pins), 229 j6x0lcd_contrast680_pins); 230 } else { 231 bdr &= ~HD64461_GPBDR_J620LX_CONTRAST_BITS; 232 233 sc->sc_contrast_mask = 234 HD64461_GPBCR_J620LX_CONTRAST_MASK; 235 sc->sc_contrast_control_bits = 236 j6x0lcd_contrast620lx_control_bits; 237 sc->sc_contrast_max = 238 arraysize(j6x0lcd_contrast620lx_control_bits) - 1; 239 240 sc->sc_contrast = 241 j6x0lcd_contrast_raw(bcr, 242 arraysize(j6x0lcd_contrast620lx_pins), 243 j6x0lcd_contrast620lx_pins); 244 } 245 246 hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr); 247 hd64461_reg_write_2(HD64461_GPBDR_REG16, bdr); 248 249 aprint_normal(": brightness %d, contrast %d\n", 250 sc->sc_brightness, sc->sc_contrast); 251 252 253 /* LCD brightness hooks */ 254 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS_MAX, 255 CONFIG_HOOK_SHARE, 256 j6x0lcd_param, sc); 257 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS, 258 CONFIG_HOOK_SHARE, 259 j6x0lcd_param, sc); 260 config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, 261 CONFIG_HOOK_SHARE, 262 j6x0lcd_param, sc); 263 264 /* LCD contrast hooks */ 265 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST_MAX, 266 CONFIG_HOOK_SHARE, 267 j6x0lcd_param, sc); 268 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST, 269 CONFIG_HOOK_SHARE, 270 j6x0lcd_param, sc); 271 config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, 272 CONFIG_HOOK_SHARE, 273 j6x0lcd_param, sc); 274 275 /* LCD on/off hook */ 276 config_hook(CONFIG_HOOK_POWERCONTROL, 277 CONFIG_HOOK_POWERCONTROL_LCD, 278 CONFIG_HOOK_SHARE, 279 j6x0lcd_power, sc); 280 281 /* XXX: TODO: don't rely on CONFIG_HOOK_POWERCONTROL_LCD */ 282 if (!pmf_device_register(self, NULL, NULL)) 283 aprint_error_dev(self, "unable to establish power handler\n"); 284 } 285 286 287 /* 288 * Get raw contrast value programmed in GPIO port B control register. 289 * Used only at attach time to get initial contrast. 290 */ 291 static int 292 j6x0lcd_contrast_raw(uint16_t bcr, int width, const uint8_t *pin) 293 { 294 int contrast; 295 int bit; 296 297 contrast = 0; 298 for (bit = 0; bit < width; ++bit) { 299 unsigned int c; 300 301 c = (bcr >> (pin[bit] << 1)) & 0x3; 302 if (c == 1) /* pin in output mode? */ 303 contrast |= (1 << bit); 304 } 305 306 return contrast; 307 } 308 309 310 /* 311 * Set contrast by programming GPIO port B control register. 312 * Data register has been initialized at attach time. 313 */ 314 static void 315 j6x0lcd_contrast_set(struct j6x0lcd_softc *sc, int contrast) 316 { 317 uint16_t bcr; 318 319 sc->sc_contrast = contrast; 320 321 bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16); 322 323 bcr &= sc->sc_contrast_mask; 324 bcr |= sc->sc_contrast_control_bits[contrast]; 325 326 hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr); 327 } 328 329 330 static int 331 j6x0lcd_param(void *ctx, int type, long id, void *msg) 332 { 333 struct j6x0lcd_softc *sc = ctx; 334 int value; 335 uint8_t dr; 336 337 switch (type) { 338 case CONFIG_HOOK_GET: 339 switch (id) { 340 case CONFIG_HOOK_CONTRAST: 341 *(int *)msg = sc->sc_contrast; 342 return (0); 343 344 case CONFIG_HOOK_CONTRAST_MAX: 345 *(int *)msg = sc->sc_contrast_max; 346 return (0); 347 348 case CONFIG_HOOK_BRIGHTNESS: 349 *(int *)msg = sc->sc_brightness; 350 return (0); 351 352 case CONFIG_HOOK_BRIGHTNESS_MAX: 353 *(int *)msg = J6X0LCD_BRIGHTNESS_MAX; 354 return (0); 355 } 356 break; 357 358 case CONFIG_HOOK_SET: 359 value = *(int *)msg; 360 if (value < 0) 361 value = 0; 362 363 switch (id) { 364 case CONFIG_HOOK_CONTRAST: 365 if (value > sc->sc_contrast_max) 366 value = sc->sc_contrast_max; 367 j6x0lcd_contrast_set(sc, value); 368 return (0); 369 370 case CONFIG_HOOK_BRIGHTNESS: 371 if (value > J6X0LCD_BRIGHTNESS_MAX) 372 value = J6X0LCD_BRIGHTNESS_MAX; 373 sc->sc_brightness = value; 374 375 dr = J6X0LCD_BRIGHTNESS_TO_DA(value); 376 DAC_(DR0) = dr; 377 return (0); 378 } 379 break; 380 } 381 382 return (EINVAL); 383 } 384 385 386 static int 387 j6x0lcd_power(void *ctx, int type, long id, void *msg) 388 { 389 int on; 390 uint16_t r; 391 392 if (type != CONFIG_HOOK_POWERCONTROL 393 || id != CONFIG_HOOK_POWERCONTROL_LCD) 394 return (EINVAL); 395 396 on = (int)msg; 397 398 r = hd64461_reg_read_2(HD64461_GPBDR_REG16); 399 if (on) 400 r &= ~HD64461_GPBDR_J6X0_LCD_OFF; 401 else 402 r |= HD64461_GPBDR_J6X0_LCD_OFF; 403 hd64461_reg_write_2(HD64461_GPBDR_REG16, r); 404 405 return (0); 406 } 407