Home | History | Annotate | Line # | Download | only in dev
j6x0lcd.c revision 1.12
      1 /*	$NetBSD: j6x0lcd.c,v 1.12 2008/03/27 03:34:41 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.12 2008/03/27 03:34:41 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 	sc = device_private(self);
    184 	sc->sc_dev = self;
    185 
    186 	/*
    187 	 * Brightness is controlled by DAC channel 0.
    188 	 */
    189 	dcr = DAC_(CR);
    190 	dcr &= ~SH7709_DACR_DAE; /* want to control each channel separately */
    191 	dcr |= SH7709_DACR_DAOE0; /* enable channel 0 */
    192 	DAC_(CR) = dcr;
    193 
    194 	ddr = DAC_(DR0);
    195 	sc->sc_brightness = J6X0LCD_DA_TO_BRIGHTNESS(ddr);
    196 
    197 	/*
    198 	 * Contrast and power are controlled by HD64461 GPIO port B.
    199 	 */
    200 	bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
    201 	bdr = hd64461_reg_read_2(HD64461_GPBDR_REG16);
    202 
    203 	/*
    204 	 * Make sure LCD is turned on.
    205 	 */
    206 	bcr &= HD64461_GPBCR_J6X0_LCD_OFF_MASK;
    207 	bcr |= HD64461_GPBCR_J6X0_LCD_OFF_BITS; /* output mode */
    208 
    209 	bdr &= ~HD64461_GPBDR_J6X0_LCD_OFF;
    210 
    211 	/*
    212 	 * 620LX and 680 have different contrast control.
    213 	 */
    214 	if (platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)) {
    215 		bdr |= HD64461_GPBDR_J680_CONTRAST_BITS;
    216 
    217 		sc->sc_contrast_mask =
    218 			HD64461_GPBCR_J680_CONTRAST_MASK;
    219 		sc->sc_contrast_control_bits =
    220 			j6x0lcd_contrast680_control_bits;
    221 		sc->sc_contrast_max =
    222 			arraysize(j6x0lcd_contrast680_control_bits) - 1;
    223 
    224 		sc->sc_contrast = sc->sc_contrast_max
    225 			- j6x0lcd_contrast_raw(bcr,
    226 				arraysize(j6x0lcd_contrast680_pins),
    227 				j6x0lcd_contrast680_pins);
    228 	} else {
    229 		bdr &= ~HD64461_GPBDR_J620LX_CONTRAST_BITS;
    230 
    231 		sc->sc_contrast_mask =
    232 			HD64461_GPBCR_J620LX_CONTRAST_MASK;
    233 		sc->sc_contrast_control_bits =
    234 			j6x0lcd_contrast620lx_control_bits;
    235 		sc->sc_contrast_max =
    236 			arraysize(j6x0lcd_contrast620lx_control_bits) - 1;
    237 
    238 		sc->sc_contrast =
    239 			j6x0lcd_contrast_raw(bcr,
    240 				arraysize(j6x0lcd_contrast620lx_pins),
    241 				j6x0lcd_contrast620lx_pins);
    242 	}
    243 
    244 	hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
    245 	hd64461_reg_write_2(HD64461_GPBDR_REG16, bdr);
    246 
    247 	printf(": brightness %d, contrast %d\n",
    248 	       sc->sc_brightness, sc->sc_contrast);
    249 
    250 
    251 	/* LCD brightness hooks */
    252 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS_MAX,
    253 		    CONFIG_HOOK_SHARE,
    254 		    j6x0lcd_param, sc);
    255 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS,
    256 		    CONFIG_HOOK_SHARE,
    257 		    j6x0lcd_param, sc);
    258 	config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS,
    259 		    CONFIG_HOOK_SHARE,
    260 		    j6x0lcd_param, sc);
    261 
    262 	/* LCD contrast hooks */
    263 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST_MAX,
    264 		    CONFIG_HOOK_SHARE,
    265 		    j6x0lcd_param, sc);
    266 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST,
    267 		    CONFIG_HOOK_SHARE,
    268 		    j6x0lcd_param, sc);
    269 	config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST,
    270 		    CONFIG_HOOK_SHARE,
    271 		    j6x0lcd_param, sc);
    272 
    273 	/* LCD on/off hook */
    274 	config_hook(CONFIG_HOOK_POWERCONTROL,
    275 		    CONFIG_HOOK_POWERCONTROL_LCD,
    276 		    CONFIG_HOOK_SHARE,
    277 		    j6x0lcd_power, sc);
    278 }
    279 
    280 
    281 /*
    282  * Get raw contrast value programmed in GPIO port B control register.
    283  * Used only at attach time to get initial contrast.
    284  */
    285 static int
    286 j6x0lcd_contrast_raw(uint16_t bcr, int width, const uint8_t *pin)
    287 {
    288 	int contrast;
    289 	int bit;
    290 
    291 	contrast = 0;
    292 	for (bit = 0; bit < width; ++bit) {
    293 		unsigned int c;
    294 
    295 		c = (bcr >> (pin[bit] << 1)) & 0x3;
    296 		if (c == 1)	/* pin in output mode? */
    297 			contrast |= (1 << bit);
    298 	}
    299 
    300 	return contrast;
    301 }
    302 
    303 
    304 /*
    305  * Set contrast by programming GPIO port B control register.
    306  * Data register has been initialized at attach time.
    307  */
    308 static void
    309 j6x0lcd_contrast_set(struct j6x0lcd_softc *sc, int contrast)
    310 {
    311 	uint16_t bcr;
    312 
    313 	sc->sc_contrast = contrast;
    314 
    315 	bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
    316 
    317 	bcr &= sc->sc_contrast_mask;
    318 	bcr |= sc->sc_contrast_control_bits[contrast];
    319 
    320 	hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
    321 }
    322 
    323 
    324 static int
    325 j6x0lcd_param(void *ctx, int type, long id, void *msg)
    326 {
    327 	struct j6x0lcd_softc *sc = ctx;
    328 	int value;
    329 	uint8_t dr;
    330 
    331 	switch (type) {
    332 	case CONFIG_HOOK_GET:
    333 		switch (id) {
    334 		case CONFIG_HOOK_CONTRAST:
    335 			*(int *)msg = sc->sc_contrast;
    336 			return (0);
    337 
    338 		case CONFIG_HOOK_CONTRAST_MAX:
    339 			*(int *)msg = sc->sc_contrast_max;
    340 			return (0);
    341 
    342 		case CONFIG_HOOK_BRIGHTNESS:
    343 			*(int *)msg = sc->sc_brightness;
    344 			return (0);
    345 
    346 		case CONFIG_HOOK_BRIGHTNESS_MAX:
    347 			*(int *)msg = J6X0LCD_BRIGHTNESS_MAX;
    348 			return (0);
    349 		}
    350 		break;
    351 
    352 	case CONFIG_HOOK_SET:
    353 		value = *(int *)msg;
    354 		if (value < 0)
    355 			value = 0;
    356 
    357 		switch (id) {
    358 		case CONFIG_HOOK_CONTRAST:
    359 			if (value > sc->sc_contrast_max)
    360 				value = sc->sc_contrast_max;
    361 			j6x0lcd_contrast_set(sc, value);
    362 			return (0);
    363 
    364 		case CONFIG_HOOK_BRIGHTNESS:
    365 			if (value > J6X0LCD_BRIGHTNESS_MAX)
    366 				value = J6X0LCD_BRIGHTNESS_MAX;
    367 			sc->sc_brightness = value;
    368 
    369 			dr = J6X0LCD_BRIGHTNESS_TO_DA(value);
    370 			DAC_(DR0) = dr;
    371 			return (0);
    372 		}
    373 		break;
    374 	}
    375 
    376 	return (EINVAL);
    377 }
    378 
    379 
    380 static int
    381 j6x0lcd_power(void *ctx, int type, long id, void *msg)
    382 {
    383 	int on;
    384 	uint16_t r;
    385 
    386 	if (type != CONFIG_HOOK_POWERCONTROL
    387 	    || id != CONFIG_HOOK_POWERCONTROL_LCD)
    388 		return (EINVAL);
    389 
    390 	on = (int)msg;
    391 
    392 	r = hd64461_reg_read_2(HD64461_GPBDR_REG16);
    393 	if (on)
    394 		r &= ~HD64461_GPBDR_J6X0_LCD_OFF;
    395 	else
    396 		r |= HD64461_GPBDR_J6X0_LCD_OFF;
    397 	hd64461_reg_write_2(HD64461_GPBDR_REG16, r);
    398 
    399 	return (0);
    400 }
    401