1*9d8bb20eSuwe /* $NetBSD: j6x0lcd.c,v 1.2 2004/03/15 23:38:16 uwe Exp $ */ 2ef7c8200Suwe 3ef7c8200Suwe /* 4ef7c8200Suwe * Copyright (c) 2004 Valeriy E. Ushakov 5ef7c8200Suwe * All rights reserved. 6ef7c8200Suwe * 7ef7c8200Suwe * Redistribution and use in source and binary forms, with or without 8ef7c8200Suwe * modification, are permitted provided that the following conditions 9ef7c8200Suwe * are met: 10ef7c8200Suwe * 1. Redistributions of source code must retain the above copyright 11ef7c8200Suwe * notice, this list of conditions and the following disclaimer. 12ef7c8200Suwe * 2. Redistributions in binary form must reproduce the above copyright 13ef7c8200Suwe * notice, this list of conditions and the following disclaimer in the 14ef7c8200Suwe * documentation and/or other materials provided with the distribution. 15ef7c8200Suwe * 3. The name of the author may not be used to endorse or promote products 16ef7c8200Suwe * derived from this software without specific prior written permission 17ef7c8200Suwe * 18ef7c8200Suwe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19ef7c8200Suwe * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20ef7c8200Suwe * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21ef7c8200Suwe * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22ef7c8200Suwe * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23ef7c8200Suwe * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24ef7c8200Suwe * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25ef7c8200Suwe * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26ef7c8200Suwe * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27ef7c8200Suwe * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28ef7c8200Suwe */ 29ef7c8200Suwe 30ef7c8200Suwe #include <sys/cdefs.h> 31*9d8bb20eSuwe __KERNEL_RCSID(0, "$NetBSD: j6x0lcd.c,v 1.2 2004/03/15 23:38:16 uwe Exp $"); 32ef7c8200Suwe 33ef7c8200Suwe #include <sys/param.h> 34ef7c8200Suwe #include <sys/kernel.h> 35ef7c8200Suwe #include <sys/device.h> 36ef7c8200Suwe #include <sys/malloc.h> 37ef7c8200Suwe #include <sys/systm.h> 38ef7c8200Suwe #include <sys/callout.h> 39ef7c8200Suwe #ifdef GPROF 40ef7c8200Suwe #include <sys/gmon.h> 41ef7c8200Suwe #endif 42ef7c8200Suwe 43ef7c8200Suwe #include <machine/platid.h> 44ef7c8200Suwe #include <machine/platid_mask.h> 45ef7c8200Suwe 46ef7c8200Suwe #include <machine/config_hook.h> 47ef7c8200Suwe 48ef7c8200Suwe #include <sh3/dacreg.h> 49ef7c8200Suwe #include <hpcsh/dev/hd64461/hd64461var.h> /* XXX: for hd64461_reg_read_2 &c */ 50ef7c8200Suwe #include <hpcsh/dev/hd64461/hd64461reg.h> 51ef7c8200Suwe #include <hpcsh/dev/hd64461/hd64461gpioreg.h> 52ef7c8200Suwe 53ef7c8200Suwe 54ef7c8200Suwe /* 55ef7c8200Suwe * LCD power: controlled by pin 0 in HD64461 GPIO port B. 56ef7c8200Suwe * 0 - power on 57ef7c8200Suwe * 1 - power off 58ef7c8200Suwe */ 59ef7c8200Suwe #define HD64461_GPBDR_J6X0LCD_OFF 0x01 60ef7c8200Suwe 61ef7c8200Suwe #define HD64461_GPBCR_J6X0LCD_OFF_MASK 0xfffc 62ef7c8200Suwe #define HD64461_GPBCR_J6X0LCD_OFF_BITS 0x0001 63ef7c8200Suwe 64ef7c8200Suwe 65ef7c8200Suwe /* 66ef7c8200Suwe * LCD brightness: controlled by DAC channel 0. Larger channel values 67ef7c8200Suwe * mean dimmer. Values smaller (i.e. brighter) then 0x5e seems to 68ef7c8200Suwe * result in no visible changes. 69ef7c8200Suwe */ 70ef7c8200Suwe #define J6X0LCD_BRIGHTNESS_DA_MAX 0x5e 71ef7c8200Suwe #define J6X0LCD_BRIGHTNESS_DA_MIN 0xff 72ef7c8200Suwe 73ef7c8200Suwe #define J6X0LCD_DA_TO_BRIGHTNESS(da) \ 74ef7c8200Suwe (J6X0LCD_BRIGHTNESS_DA_MIN - (da)) 75ef7c8200Suwe 76ef7c8200Suwe #define J6X0LCD_BRIGHTNESS_TO_DA(br) \ 77ef7c8200Suwe (J6X0LCD_BRIGHTNESS_DA_MIN - (br)) 78ef7c8200Suwe 79ef7c8200Suwe #define J6X0LCD_BRIGHTNESS_MAX \ 80ef7c8200Suwe J6X0LCD_DA_TO_BRIGHTNESS(J6X0LCD_BRIGHTNESS_DA_MAX) 81ef7c8200Suwe 82ef7c8200Suwe /* convenience macro to accesses DAC registers */ 83ef7c8200Suwe #define DAC_(x) (*((volatile uint8_t *)SH7709_DA ## x)) 84ef7c8200Suwe 85ef7c8200Suwe 86ef7c8200Suwe /* 87ef7c8200Suwe * LCD contrast: controlled by pins 6,5,4,3 HD64461 GPIO port B. 88ef7c8200Suwe * 6th is the most significant bit, 3rd is the least significant. 89ef7c8200Suwe * The bits are inverted: .1111... = 0, .0111... = 1, etc. 90ef7c8200Suwe * 91ef7c8200Suwe * We control the contrast value by setting bits in the data register 92ef7c8200Suwe * to all ones, and changing the mode of the bits in the control 93ef7c8200Suwe * register, keeping "ones" in gpio output mode (1), and switching 94ef7c8200Suwe * "zeros" to input mode (3). This is what WinCE also does. 95ef7c8200Suwe * 96ef7c8200Suwe * Alternative method is to set the mode of all bits to gpio output 97ef7c8200Suwe * mode and then change the bits in the data register. This method 98ef7c8200Suwe * results in significantly less contrast screen for the same values. 99ef7c8200Suwe * E.g., WinCE default contrast value is 11 (in our numbering, 23 in 100ef7c8200Suwe * WinCE's) - which is fine in the "tweak the control register" 101ef7c8200Suwe * method, but is very blurry in the "tweak the data register" method. 102ef7c8200Suwe * Subjectively, 15 in the "tweak control" method looks like 7 in the 103ef7c8200Suwe * "tweak data" method. 104ef7c8200Suwe * 105ef7c8200Suwe * May be it's possible to control a wider range of contrast by 106ef7c8200Suwe * combining the two methods, but values above 7 in the "tweak data" 107ef7c8200Suwe * method are so blurry that they are next to unusable in practice. 108ef7c8200Suwe */ 109ef7c8200Suwe #define J6X0LCD_CONTRAST_MAX 15 110ef7c8200Suwe 111ef7c8200Suwe #define HD64461_GPBDR_J6X0LCD_CONTRAST_MASK 0x87 112ef7c8200Suwe #define HD64461_GPBDR_J6X0LCD_CONTRAST_BITS 0x78 113ef7c8200Suwe 114ef7c8200Suwe #if 0 115ef7c8200Suwe /* "tweak data" */ 116ef7c8200Suwe static uint8_t j6x0lcd_contrast_data_bits[] = { 117ef7c8200Suwe 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, 118ef7c8200Suwe 0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00 119ef7c8200Suwe }; 120ef7c8200Suwe #endif 121ef7c8200Suwe 122ef7c8200Suwe #define HD64461_GPBCR_J6X0LCD_CONTRAST_MASK 0xc03f 123ef7c8200Suwe #define HD64461_GPBCR_J6X0LCD_CONTRAST_BITS 0x1540 124ef7c8200Suwe 125ef7c8200Suwe /* "tweak control" */ 126ef7c8200Suwe static uint16_t j6x0lcd_contrast_control_bits[] = { 127ef7c8200Suwe 0x1540, 0x3540, 0x1d40, 0x3d40, 0x1740, 0x3740, 0x1f40, 0x3f40, 128ef7c8200Suwe 0x15c0, 0x35c0, 0x1dc0, 0x3dc0, 0x17c0, 0x37c0, 0x1fc0, 0x3fc0 129ef7c8200Suwe }; 130ef7c8200Suwe 131ef7c8200Suwe 132ef7c8200Suwe struct j6x0lcd_softc { 133ef7c8200Suwe struct device sc_dev; 134ef7c8200Suwe int sc_brightness; 135ef7c8200Suwe int sc_contrast; 136ef7c8200Suwe }; 137ef7c8200Suwe 138ef7c8200Suwe static int j6x0lcd_match(struct device *, struct cfdata *, void *); 139ef7c8200Suwe static void j6x0lcd_attach(struct device *, struct device *, void *); 140ef7c8200Suwe 141ef7c8200Suwe CFATTACH_DECL(j6x0lcd, sizeof(struct j6x0lcd_softc), 142ef7c8200Suwe j6x0lcd_match, j6x0lcd_attach, NULL, NULL); 143ef7c8200Suwe 144ef7c8200Suwe 145ef7c8200Suwe static int j6x0lcd_param(void *, int, long, void *); 146ef7c8200Suwe static int j6x0lcd_power(void *, int, long, void *); 147ef7c8200Suwe 148ef7c8200Suwe 149ef7c8200Suwe static int 150ef7c8200Suwe j6x0lcd_match(struct device *parent, struct cfdata *cfp, void *aux) 151ef7c8200Suwe { 152ef7c8200Suwe 153ef7c8200Suwe /* 154ef7c8200Suwe * XXX: does platid_mask_MACH_HP_LX matches _JORNADA_6XX too? 155ef7c8200Suwe * Is 620 wired similarly? 156ef7c8200Suwe */ 157ef7c8200Suwe if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)) 158ef7c8200Suwe return (0); 159ef7c8200Suwe 160ef7c8200Suwe if (strcmp(cfp->cf_name, "j6x0lcd") != 0) 161ef7c8200Suwe return (0); 162ef7c8200Suwe 163ef7c8200Suwe return (1); 164ef7c8200Suwe } 165ef7c8200Suwe 166ef7c8200Suwe 167ef7c8200Suwe static void 168ef7c8200Suwe j6x0lcd_attach(struct device *parent, struct device *self, void *aux) 169ef7c8200Suwe { 170ef7c8200Suwe struct j6x0lcd_softc *sc = (struct j6x0lcd_softc *)self; 171ef7c8200Suwe int contrast, i; 172ef7c8200Suwe uint16_t bcr, bdr; 173ef7c8200Suwe uint8_t dcr, ddr; 174ef7c8200Suwe 175ef7c8200Suwe /* 176ef7c8200Suwe * Brightness is controlled by DAC channel 0. 177ef7c8200Suwe */ 178ef7c8200Suwe dcr = DAC_(CR); 179ef7c8200Suwe dcr &= ~SH7709_DACR_DAE; /* want to control each channel separately */ 180ef7c8200Suwe dcr |= SH7709_DACR_DAOE0; /* enable channel 0 */ 181ef7c8200Suwe DAC_(CR) = dcr; 182ef7c8200Suwe 183ef7c8200Suwe ddr = DAC_(DR0); 184ef7c8200Suwe sc->sc_brightness = J6X0LCD_DA_TO_BRIGHTNESS(ddr); 185ef7c8200Suwe 186ef7c8200Suwe 187ef7c8200Suwe /* 188ef7c8200Suwe * Contrast and power are controlled by HD64461 GPIO port B. 189ef7c8200Suwe */ 190ef7c8200Suwe bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16); 191ef7c8200Suwe bdr = hd64461_reg_read_2(HD64461_GPBDR_REG16); 192ef7c8200Suwe 193ef7c8200Suwe contrast = 0xf; /* bits are inverted */ 194ef7c8200Suwe for (i = 0; i < 4; ++i) { 195ef7c8200Suwe unsigned int c, v; 196ef7c8200Suwe c = (bcr >> ((6 - i) << 1)) & 0x3; 197ef7c8200Suwe if (c == 1) /* gpio mode? */ 198ef7c8200Suwe v = 1; 199ef7c8200Suwe else 200ef7c8200Suwe v = 0; 201ef7c8200Suwe contrast &= ~(v << i); 202ef7c8200Suwe } 203ef7c8200Suwe 204ef7c8200Suwe sc->sc_contrast = contrast; 205ef7c8200Suwe 206ef7c8200Suwe bdr &= ~HD64461_GPBDR_J6X0LCD_OFF; 207ef7c8200Suwe bdr |= HD64461_GPBDR_J6X0LCD_CONTRAST_BITS; 208ef7c8200Suwe hd64461_reg_write_2(HD64461_GPBDR_REG16, bdr); 209ef7c8200Suwe 210ef7c8200Suwe bcr &= HD64461_GPBCR_J6X0LCD_OFF_MASK 211ef7c8200Suwe & HD64461_GPBCR_J6X0LCD_CONTRAST_MASK; 212ef7c8200Suwe bcr |= HD64461_GPBCR_J6X0LCD_OFF_BITS 213ef7c8200Suwe | j6x0lcd_contrast_control_bits[contrast]; 214ef7c8200Suwe hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr); 215ef7c8200Suwe 216ef7c8200Suwe printf(": brightness %d, contrast %d\n", 217ef7c8200Suwe sc->sc_brightness, sc->sc_contrast); 218ef7c8200Suwe 219ef7c8200Suwe 220ef7c8200Suwe /* LCD brightness hooks */ 221ef7c8200Suwe config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS_MAX, 222ef7c8200Suwe CONFIG_HOOK_SHARE, 223ef7c8200Suwe j6x0lcd_param, sc); 224ef7c8200Suwe config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS, 225ef7c8200Suwe CONFIG_HOOK_SHARE, 226ef7c8200Suwe j6x0lcd_param, sc); 227ef7c8200Suwe config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, 228ef7c8200Suwe CONFIG_HOOK_SHARE, 229ef7c8200Suwe j6x0lcd_param, sc); 230ef7c8200Suwe 231ef7c8200Suwe /* LCD contrast hooks */ 232ef7c8200Suwe config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST_MAX, 233ef7c8200Suwe CONFIG_HOOK_SHARE, 234ef7c8200Suwe j6x0lcd_param, sc); 235ef7c8200Suwe config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST, 236ef7c8200Suwe CONFIG_HOOK_SHARE, 237ef7c8200Suwe j6x0lcd_param, sc); 238ef7c8200Suwe config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, 239ef7c8200Suwe CONFIG_HOOK_SHARE, 240ef7c8200Suwe j6x0lcd_param, sc); 241ef7c8200Suwe 242ef7c8200Suwe /* LCD on/off hook */ 243ef7c8200Suwe config_hook(CONFIG_HOOK_POWERCONTROL, 244*9d8bb20eSuwe CONFIG_HOOK_POWERCONTROL_LCD, 245ef7c8200Suwe CONFIG_HOOK_SHARE, 246ef7c8200Suwe j6x0lcd_power, sc); 247ef7c8200Suwe } 248ef7c8200Suwe 249ef7c8200Suwe 250ef7c8200Suwe static int 251ef7c8200Suwe j6x0lcd_param(ctx, type, id, msg) 252ef7c8200Suwe void *ctx; 253ef7c8200Suwe int type; 254ef7c8200Suwe long id; 255ef7c8200Suwe void *msg; 256ef7c8200Suwe { 257ef7c8200Suwe struct j6x0lcd_softc *sc = ctx; 258ef7c8200Suwe int value; 259ef7c8200Suwe uint16_t bcr; 260ef7c8200Suwe uint8_t dr; 261ef7c8200Suwe 262ef7c8200Suwe switch (type) { 263ef7c8200Suwe case CONFIG_HOOK_GET: 264ef7c8200Suwe switch (id) { 265ef7c8200Suwe case CONFIG_HOOK_CONTRAST: 266ef7c8200Suwe *(int *)msg = sc->sc_contrast; 267ef7c8200Suwe return (0); 268ef7c8200Suwe 269ef7c8200Suwe case CONFIG_HOOK_CONTRAST_MAX: 270ef7c8200Suwe *(int *)msg = J6X0LCD_CONTRAST_MAX; 271ef7c8200Suwe return (0); 272ef7c8200Suwe 273ef7c8200Suwe case CONFIG_HOOK_BRIGHTNESS: 274ef7c8200Suwe *(int *)msg = sc->sc_brightness; 275ef7c8200Suwe return (0); 276ef7c8200Suwe 277ef7c8200Suwe case CONFIG_HOOK_BRIGHTNESS_MAX: 278ef7c8200Suwe *(int *)msg = J6X0LCD_BRIGHTNESS_MAX; 279ef7c8200Suwe return (0); 280ef7c8200Suwe } 281ef7c8200Suwe break; 282ef7c8200Suwe 283ef7c8200Suwe case CONFIG_HOOK_SET: 284ef7c8200Suwe value = *(int *)msg; 285ef7c8200Suwe if (value < 0) 286ef7c8200Suwe value = 0; 287ef7c8200Suwe 288ef7c8200Suwe switch (id) { 289ef7c8200Suwe case CONFIG_HOOK_CONTRAST: 290ef7c8200Suwe if (value > J6X0LCD_CONTRAST_MAX) 291ef7c8200Suwe value = J6X0LCD_CONTRAST_MAX; 292ef7c8200Suwe sc->sc_contrast = value; 293ef7c8200Suwe 294ef7c8200Suwe bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16); 295ef7c8200Suwe bcr &= HD64461_GPBCR_J6X0LCD_CONTRAST_MASK; 296ef7c8200Suwe bcr |= j6x0lcd_contrast_control_bits[value]; 297ef7c8200Suwe hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr); 298ef7c8200Suwe return (0); 299ef7c8200Suwe 300ef7c8200Suwe case CONFIG_HOOK_BRIGHTNESS: 301ef7c8200Suwe if (value > J6X0LCD_BRIGHTNESS_MAX) 302ef7c8200Suwe value = J6X0LCD_BRIGHTNESS_MAX; 303ef7c8200Suwe sc->sc_brightness = value; 304ef7c8200Suwe 305ef7c8200Suwe dr = J6X0LCD_BRIGHTNESS_TO_DA(value); 306ef7c8200Suwe DAC_(DR0) = dr; 307ef7c8200Suwe return (0); 308ef7c8200Suwe } 309ef7c8200Suwe break; 310ef7c8200Suwe } 311ef7c8200Suwe 312ef7c8200Suwe return (EINVAL); 313ef7c8200Suwe } 314ef7c8200Suwe 315ef7c8200Suwe 316ef7c8200Suwe static int 317ef7c8200Suwe j6x0lcd_power(ctx, type, id, msg) 318ef7c8200Suwe void *ctx; 319ef7c8200Suwe int type; 320ef7c8200Suwe long id; 321ef7c8200Suwe void *msg; 322ef7c8200Suwe { 323ef7c8200Suwe int on; 324ef7c8200Suwe uint16_t r; 325ef7c8200Suwe 326ef7c8200Suwe if (type != CONFIG_HOOK_POWERCONTROL 327*9d8bb20eSuwe || id != CONFIG_HOOK_POWERCONTROL_LCD) 328ef7c8200Suwe return (EINVAL); 329ef7c8200Suwe 330ef7c8200Suwe on = (int)msg; 331ef7c8200Suwe 332ef7c8200Suwe r = hd64461_reg_read_2(HD64461_GPBDR_REG16); 333ef7c8200Suwe if (on) 334ef7c8200Suwe r &= ~HD64461_GPBDR_J6X0LCD_OFF; 335ef7c8200Suwe else 336ef7c8200Suwe r |= HD64461_GPBDR_J6X0LCD_OFF; 337ef7c8200Suwe hd64461_reg_write_2(HD64461_GPBDR_REG16, r); 338ef7c8200Suwe 339ef7c8200Suwe return (0); 340ef7c8200Suwe } 341