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