Home | History | Annotate | Line # | Download | only in dev
j6x0lcd.c revision 1.10.22.1
      1 /*	$NetBSD: j6x0lcd.c,v 1.10.22.1 2006/12/10 07:16:01 yamt 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.10.22.1 2006/12/10 07:16:01 yamt 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 	struct device sc_dev;
    132 	int sc_brightness;
    133 	int sc_contrast;
    134 
    135 	int sc_contrast_max;
    136 	uint16_t sc_contrast_mask;
    137 	const uint16_t *sc_contrast_control_bits;
    138 };
    139 
    140 static int	j6x0lcd_match(struct device *, struct cfdata *, void *);
    141 static void	j6x0lcd_attach(struct device *, struct device *, void *);
    142 
    143 CFATTACH_DECL(j6x0lcd, sizeof(struct j6x0lcd_softc),
    144     j6x0lcd_match, j6x0lcd_attach, NULL, NULL);
    145 
    146 
    147 static int	j6x0lcd_param(void *, int, long, void *);
    148 static int	j6x0lcd_power(void *, int, long, void *);
    149 
    150 static int	j6x0lcd_contrast_raw(uint16_t, int, const uint8_t *);
    151 static void	j6x0lcd_contrast_set(struct j6x0lcd_softc *, int);
    152 
    153 
    154 
    155 static int
    156 j6x0lcd_match(struct device *parent, struct cfdata *cfp, void *aux)
    157 {
    158 
    159 	/*
    160 	 * XXX: platid_mask_MACH_HP_LX also matches 360LX.  It's not
    161 	 * confirmed whether touch panel in 360LX is connected this
    162 	 * way.  We may need to regroup platid masks.
    163 	 */
    164 	if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)
    165 	    && !platid_match(&platid, &platid_mask_MACH_HP_LX))
    166 		return (0);
    167 
    168 	if (strcmp(cfp->cf_name, "j6x0lcd") != 0)
    169 		return (0);
    170 
    171 	return (1);
    172 }
    173 
    174 
    175 static void
    176 j6x0lcd_attach(struct device *parent, struct device *self, void *aux)
    177 {
    178 	struct j6x0lcd_softc *sc = (struct j6x0lcd_softc *)self;
    179 	uint16_t bcr, bdr;
    180 	uint8_t dcr, ddr;
    181 
    182 	/*
    183 	 * Brightness is controlled by DAC channel 0.
    184 	 */
    185 	dcr = DAC_(CR);
    186 	dcr &= ~SH7709_DACR_DAE; /* want to control each channel separately */
    187 	dcr |= SH7709_DACR_DAOE0; /* enable channel 0 */
    188 	DAC_(CR) = dcr;
    189 
    190 	ddr = DAC_(DR0);
    191 	sc->sc_brightness = J6X0LCD_DA_TO_BRIGHTNESS(ddr);
    192 
    193 	/*
    194 	 * Contrast and power are controlled by HD64461 GPIO port B.
    195 	 */
    196 	bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
    197 	bdr = hd64461_reg_read_2(HD64461_GPBDR_REG16);
    198 
    199 	/*
    200 	 * Make sure LCD is turned on.
    201 	 */
    202 	bcr &= HD64461_GPBCR_J6X0_LCD_OFF_MASK;
    203 	bcr |= HD64461_GPBCR_J6X0_LCD_OFF_BITS; /* output mode */
    204 
    205 	bdr &= ~HD64461_GPBDR_J6X0_LCD_OFF;
    206 
    207 	/*
    208 	 * 620LX and 680 have different contrast control.
    209 	 */
    210 	if (platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)) {
    211 		bdr |= HD64461_GPBDR_J680_CONTRAST_BITS;
    212 
    213 		sc->sc_contrast_mask =
    214 			HD64461_GPBCR_J680_CONTRAST_MASK;
    215 		sc->sc_contrast_control_bits =
    216 			j6x0lcd_contrast680_control_bits;
    217 		sc->sc_contrast_max =
    218 			arraysize(j6x0lcd_contrast680_control_bits) - 1;
    219 
    220 		sc->sc_contrast = sc->sc_contrast_max
    221 			- j6x0lcd_contrast_raw(bcr,
    222 				arraysize(j6x0lcd_contrast680_pins),
    223 				j6x0lcd_contrast680_pins);
    224 	} else {
    225 		bdr &= ~HD64461_GPBDR_J620LX_CONTRAST_BITS;
    226 
    227 		sc->sc_contrast_mask =
    228 			HD64461_GPBCR_J620LX_CONTRAST_MASK;
    229 		sc->sc_contrast_control_bits =
    230 			j6x0lcd_contrast620lx_control_bits;
    231 		sc->sc_contrast_max =
    232 			arraysize(j6x0lcd_contrast620lx_control_bits) - 1;
    233 
    234 		sc->sc_contrast =
    235 			j6x0lcd_contrast_raw(bcr,
    236 				arraysize(j6x0lcd_contrast620lx_pins),
    237 				j6x0lcd_contrast620lx_pins);
    238 	}
    239 
    240 	hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
    241 	hd64461_reg_write_2(HD64461_GPBDR_REG16, bdr);
    242 
    243 	printf(": brightness %d, contrast %d\n",
    244 	       sc->sc_brightness, sc->sc_contrast);
    245 
    246 
    247 	/* LCD brightness hooks */
    248 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS_MAX,
    249 		    CONFIG_HOOK_SHARE,
    250 		    j6x0lcd_param, sc);
    251 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS,
    252 		    CONFIG_HOOK_SHARE,
    253 		    j6x0lcd_param, sc);
    254 	config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS,
    255 		    CONFIG_HOOK_SHARE,
    256 		    j6x0lcd_param, sc);
    257 
    258 	/* LCD contrast hooks */
    259 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST_MAX,
    260 		    CONFIG_HOOK_SHARE,
    261 		    j6x0lcd_param, sc);
    262 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST,
    263 		    CONFIG_HOOK_SHARE,
    264 		    j6x0lcd_param, sc);
    265 	config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST,
    266 		    CONFIG_HOOK_SHARE,
    267 		    j6x0lcd_param, sc);
    268 
    269 	/* LCD on/off hook */
    270 	config_hook(CONFIG_HOOK_POWERCONTROL,
    271 		    CONFIG_HOOK_POWERCONTROL_LCD,
    272 		    CONFIG_HOOK_SHARE,
    273 		    j6x0lcd_power, sc);
    274 }
    275 
    276 
    277 /*
    278  * Get raw contrast value programmed in GPIO port B control register.
    279  * Used only at attach time to get initial contrast.
    280  */
    281 static int
    282 j6x0lcd_contrast_raw(uint16_t bcr, int width, const uint8_t *pin)
    283 {
    284 	int contrast;
    285 	int bit;
    286 
    287 	contrast = 0;
    288 	for (bit = 0; bit < width; ++bit) {
    289 		unsigned int c;
    290 
    291 		c = (bcr >> (pin[bit] << 1)) & 0x3;
    292 		if (c == 1)	/* pin in output mode? */
    293 			contrast |= (1 << bit);
    294 	}
    295 
    296 	return contrast;
    297 }
    298 
    299 
    300 /*
    301  * Set contrast by programming GPIO port B control register.
    302  * Data register has been initialized at attach time.
    303  */
    304 static void
    305 j6x0lcd_contrast_set(struct j6x0lcd_softc *sc, int contrast)
    306 {
    307 	uint16_t bcr;
    308 
    309 	sc->sc_contrast = contrast;
    310 
    311 	bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
    312 
    313 	bcr &= sc->sc_contrast_mask;
    314 	bcr |= sc->sc_contrast_control_bits[contrast];
    315 
    316 	hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
    317 }
    318 
    319 
    320 static int
    321 j6x0lcd_param(void *ctx, int type, long id, void *msg)
    322 {
    323 	struct j6x0lcd_softc *sc = ctx;
    324 	int value;
    325 	uint8_t dr;
    326 
    327 	switch (type) {
    328 	case CONFIG_HOOK_GET:
    329 		switch (id) {
    330 		case CONFIG_HOOK_CONTRAST:
    331 			*(int *)msg = sc->sc_contrast;
    332 			return (0);
    333 
    334 		case CONFIG_HOOK_CONTRAST_MAX:
    335 			*(int *)msg = sc->sc_contrast_max;
    336 			return (0);
    337 
    338 		case CONFIG_HOOK_BRIGHTNESS:
    339 			*(int *)msg = sc->sc_brightness;
    340 			return (0);
    341 
    342 		case CONFIG_HOOK_BRIGHTNESS_MAX:
    343 			*(int *)msg = J6X0LCD_BRIGHTNESS_MAX;
    344 			return (0);
    345 		}
    346 		break;
    347 
    348 	case CONFIG_HOOK_SET:
    349 		value = *(int *)msg;
    350 		if (value < 0)
    351 			value = 0;
    352 
    353 		switch (id) {
    354 		case CONFIG_HOOK_CONTRAST:
    355 			if (value > sc->sc_contrast_max)
    356 				value = sc->sc_contrast_max;
    357 			j6x0lcd_contrast_set(sc, value);
    358 			return (0);
    359 
    360 		case CONFIG_HOOK_BRIGHTNESS:
    361 			if (value > J6X0LCD_BRIGHTNESS_MAX)
    362 				value = J6X0LCD_BRIGHTNESS_MAX;
    363 			sc->sc_brightness = value;
    364 
    365 			dr = J6X0LCD_BRIGHTNESS_TO_DA(value);
    366 			DAC_(DR0) = dr;
    367 			return (0);
    368 		}
    369 		break;
    370 	}
    371 
    372 	return (EINVAL);
    373 }
    374 
    375 
    376 static int
    377 j6x0lcd_power(void *ctx, int type, long id, void *msg)
    378 {
    379 	int on;
    380 	uint16_t r;
    381 
    382 	if (type != CONFIG_HOOK_POWERCONTROL
    383 	    || id != CONFIG_HOOK_POWERCONTROL_LCD)
    384 		return (EINVAL);
    385 
    386 	on = (int)msg;
    387 
    388 	r = hd64461_reg_read_2(HD64461_GPBDR_REG16);
    389 	if (on)
    390 		r &= ~HD64461_GPBDR_J6X0_LCD_OFF;
    391 	else
    392 		r |= HD64461_GPBDR_J6X0_LCD_OFF;
    393 	hd64461_reg_write_2(HD64461_GPBDR_REG16, r);
    394 
    395 	return (0);
    396 }
    397