Home | History | Annotate | Line # | Download | only in dev
j6x0lcd.c revision 1.3.2.6
      1 /*	$NetBSD: j6x0lcd.c,v 1.3.2.6 2005/11/10 13:56:31 skrll 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.3.2.6 2005/11/10 13:56:31 skrll Exp $");
     32 
     33 #include <sys/param.h>
     34 #include <sys/kernel.h>
     35 #include <sys/device.h>
     36 #include <sys/systm.h>
     37 #ifdef GPROF
     38 #include <sys/gmon.h>
     39 #endif
     40 
     41 #include <machine/platid.h>
     42 #include <machine/platid_mask.h>
     43 
     44 #include <machine/config_hook.h>
     45 
     46 #include <sh3/dacreg.h>
     47 #include <hpcsh/dev/hd64461/hd64461var.h> /* XXX: for hd64461_reg_read_2 &c */
     48 #include <hpcsh/dev/hd64461/hd64461reg.h>
     49 #include <hpcsh/dev/hd64461/hd64461gpioreg.h>
     50 
     51 #define arraysize(ary) (sizeof(ary) / sizeof(ary[0]))
     52 
     53 
     54 /*
     55  * LCD power: controlled by pin 0 in HD64461 GPIO port B.
     56  *   0 - power on
     57  *   1 - power off
     58  */
     59 #define HD64461_GPBDR_J6X0_LCD_OFF	0x01
     60 
     61 #define HD64461_GPBCR_J6X0_LCD_OFF_MASK	0xfffc
     62 #define HD64461_GPBCR_J6X0_LCD_OFF_BITS	0x0001
     63 
     64 
     65 /*
     66  * LCD brightness: controlled by DAC channel 0.  Larger channel values
     67  * mean dimmer.  Values smaller (i.e. brighter) then 0x5e seems to
     68  * result in no visible changes.
     69  */
     70 #define J6X0LCD_BRIGHTNESS_DA_MAX	0x5e
     71 #define J6X0LCD_BRIGHTNESS_DA_MIN	0xff
     72 
     73 #define J6X0LCD_DA_TO_BRIGHTNESS(da) \
     74 	(J6X0LCD_BRIGHTNESS_DA_MIN - (da))
     75 
     76 #define J6X0LCD_BRIGHTNESS_TO_DA(br) \
     77 	(J6X0LCD_BRIGHTNESS_DA_MIN - (br))
     78 
     79 #define J6X0LCD_BRIGHTNESS_MAX \
     80 	J6X0LCD_DA_TO_BRIGHTNESS(J6X0LCD_BRIGHTNESS_DA_MAX)
     81 
     82 /* convenience macro to accesses DAC registers */
     83 #define DAC_(x)    (*((volatile uint8_t *)SH7709_DA ## x))
     84 
     85 
     86 /*
     87  * LCD contrast in 680 is controlled by pins 6..3 of HD64461 GPIO
     88  * port B.  6th pin is the least significant bit, 3rd pin is the most
     89  * significant.  The bits are inverted: 0 = .1111...; 1 = .0111...;
     90  * etc.  Larger values mean "blacker".
     91  *
     92  * The contrast value is programmed by setting bits in the data
     93  * register to all ones, and changing the mode of the pins in the
     94  * control register, setting logical "ones" to GPIO output mode (1),
     95  * and switching "zeroes" to input mode (3).
     96  */
     97 #define HD64461_GPBDR_J680_CONTRAST_BITS	0x78	/* set */
     98 #define HD64461_GPBCR_J680_CONTRAST_MASK	0xc03f
     99 
    100 static const uint8_t j6x0lcd_contrast680_pins[] = { 6, 5, 4, 3 };
    101 
    102 static const uint16_t j6x0lcd_contrast680_control_bits[] = {
    103 	0x1540, 0x3540, 0x1d40, 0x3d40, 0x1740, 0x3740, 0x1f40, 0x3f40,
    104 	0x15c0, 0x35c0, 0x1dc0, 0x3dc0, 0x17c0, 0x37c0, 0x1fc0, 0x3fc0
    105 };
    106 
    107 
    108 /*
    109  * LCD contrast in 620lx is controlled by pins 7,6,3,4,5 of HD64461
    110  * GPIO port B (in the order from the least significant to the most
    111  * significant).  The bits are inverted: 0 = 11111...; 5 = 01110...;
    112  * etc.  Larger values mean "whiter".
    113  *
    114  * The contrast value is programmed by setting bits in the data
    115  * register to all zeroes, and changing the mode of the pins in the
    116  * control register, setting logical "ones" to GPIO output mode (1),
    117  * and switching "zeroes" to input mode (3).
    118  */
    119 #define HD64461_GPBDR_J620LX_CONTRAST_BITS	0xf8	/* clear */
    120 #define HD64461_GPBCR_J620LX_CONTRAST_MASK	0x003f
    121 
    122 static const uint8_t j6x0lcd_contrast620lx_pins[] = { 7, 6, 3, 4, 5 };
    123 
    124 static const uint16_t j6x0lcd_contrast620lx_control_bits[] = {
    125 	0xffc0, 0x7fc0, 0xdfc0, 0x5fc0, 0xff40, 0x7f40, 0xdf40, 0x5f40,
    126 	0xfdc0, 0x7dc0, 0xddc0, 0x5dc0, 0xfd40, 0x7d40, 0xdd40, 0x5d40,
    127 	0xf7c0, 0x77c0, 0xd7c0, 0x57c0, 0xf740, 0x7740, 0xd740, 0x5740,
    128 	0xf5c0, 0x75c0, 0xd5c0, 0x55c0, 0xf540, 0x7540, 0xd540, 0x5540
    129 };
    130 
    131 
    132 
    133 struct j6x0lcd_softc {
    134 	struct device sc_dev;
    135 	int sc_brightness;
    136 	int sc_contrast;
    137 
    138 	int sc_contrast_max;
    139 	uint16_t sc_contrast_mask;
    140 	const uint16_t *sc_contrast_control_bits;
    141 };
    142 
    143 static int	j6x0lcd_match(struct device *, struct cfdata *, void *);
    144 static void	j6x0lcd_attach(struct device *, struct device *, void *);
    145 
    146 CFATTACH_DECL(j6x0lcd, sizeof(struct j6x0lcd_softc),
    147     j6x0lcd_match, j6x0lcd_attach, NULL, NULL);
    148 
    149 
    150 static int	j6x0lcd_param(void *, int, long, void *);
    151 static int	j6x0lcd_power(void *, int, long, void *);
    152 
    153 static int	j6x0lcd_contrast_raw(uint16_t, int, const uint8_t *);
    154 static void	j6x0lcd_contrast_set(struct j6x0lcd_softc *, int);
    155 
    156 
    157 
    158 static int
    159 j6x0lcd_match(struct device *parent, struct cfdata *cfp, void *aux)
    160 {
    161 
    162 	/*
    163 	 * XXX: platid_mask_MACH_HP_LX also matches 360LX.  It's not
    164 	 * confirmed whether touch panel in 360LX is connected this
    165 	 * way.  We may need to regroup platid masks.
    166 	 */
    167 	if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)
    168 	    && !platid_match(&platid, &platid_mask_MACH_HP_LX))
    169 		return (0);
    170 
    171 	if (strcmp(cfp->cf_name, "j6x0lcd") != 0)
    172 		return (0);
    173 
    174 	return (1);
    175 }
    176 
    177 
    178 static void
    179 j6x0lcd_attach(struct device *parent, struct device *self, void *aux)
    180 {
    181 	struct j6x0lcd_softc *sc = (struct j6x0lcd_softc *)self;
    182 	uint16_t bcr, bdr;
    183 	uint8_t dcr, ddr;
    184 
    185 	/*
    186 	 * Brightness is controlled by DAC channel 0.
    187 	 */
    188 	dcr = DAC_(CR);
    189 	dcr &= ~SH7709_DACR_DAE; /* want to control each channel separately */
    190 	dcr |= SH7709_DACR_DAOE0; /* enable channel 0 */
    191 	DAC_(CR) = dcr;
    192 
    193 	ddr = DAC_(DR0);
    194 	sc->sc_brightness = J6X0LCD_DA_TO_BRIGHTNESS(ddr);
    195 
    196 	/*
    197 	 * Contrast and power are controlled by HD64461 GPIO port B.
    198 	 */
    199 	bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
    200 	bdr = hd64461_reg_read_2(HD64461_GPBDR_REG16);
    201 
    202 	/*
    203 	 * Make sure LCD is turned on.
    204 	 */
    205 	bcr &= HD64461_GPBCR_J6X0_LCD_OFF_MASK;
    206 	bcr |= HD64461_GPBCR_J6X0_LCD_OFF_BITS; /* output mode */
    207 
    208 	bdr &= ~HD64461_GPBDR_J6X0_LCD_OFF;
    209 
    210 	/*
    211 	 * 620LX and 680 have different contrast control.
    212 	 */
    213 	if (platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)) {
    214 		bdr |= HD64461_GPBDR_J680_CONTRAST_BITS;
    215 
    216 		sc->sc_contrast_mask =
    217 			HD64461_GPBCR_J680_CONTRAST_MASK;
    218 		sc->sc_contrast_control_bits =
    219 			j6x0lcd_contrast680_control_bits;
    220 		sc->sc_contrast_max =
    221 			arraysize(j6x0lcd_contrast680_control_bits) - 1;
    222 
    223 		sc->sc_contrast = sc->sc_contrast_max
    224 			- j6x0lcd_contrast_raw(bcr,
    225 				arraysize(j6x0lcd_contrast680_pins),
    226 				j6x0lcd_contrast680_pins);
    227 	} else {
    228 		bdr &= ~HD64461_GPBDR_J620LX_CONTRAST_BITS;
    229 
    230 		sc->sc_contrast_mask =
    231 			HD64461_GPBCR_J620LX_CONTRAST_MASK;
    232 		sc->sc_contrast_control_bits =
    233 			j6x0lcd_contrast620lx_control_bits;
    234 		sc->sc_contrast_max =
    235 			arraysize(j6x0lcd_contrast620lx_control_bits) - 1;
    236 
    237 		sc->sc_contrast =
    238 			j6x0lcd_contrast_raw(bcr,
    239 				arraysize(j6x0lcd_contrast620lx_pins),
    240 				j6x0lcd_contrast620lx_pins);
    241 	}
    242 
    243 	hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
    244 	hd64461_reg_write_2(HD64461_GPBDR_REG16, bdr);
    245 
    246 	printf(": brightness %d, contrast %d\n",
    247 	       sc->sc_brightness, sc->sc_contrast);
    248 
    249 
    250 	/* LCD brightness hooks */
    251 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS_MAX,
    252 		    CONFIG_HOOK_SHARE,
    253 		    j6x0lcd_param, sc);
    254 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS,
    255 		    CONFIG_HOOK_SHARE,
    256 		    j6x0lcd_param, sc);
    257 	config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS,
    258 		    CONFIG_HOOK_SHARE,
    259 		    j6x0lcd_param, sc);
    260 
    261 	/* LCD contrast hooks */
    262 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST_MAX,
    263 		    CONFIG_HOOK_SHARE,
    264 		    j6x0lcd_param, sc);
    265 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CONTRAST,
    266 		    CONFIG_HOOK_SHARE,
    267 		    j6x0lcd_param, sc);
    268 	config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST,
    269 		    CONFIG_HOOK_SHARE,
    270 		    j6x0lcd_param, sc);
    271 
    272 	/* LCD on/off hook */
    273 	config_hook(CONFIG_HOOK_POWERCONTROL,
    274 		    CONFIG_HOOK_POWERCONTROL_LCD,
    275 		    CONFIG_HOOK_SHARE,
    276 		    j6x0lcd_power, sc);
    277 }
    278 
    279 
    280 /*
    281  * Get raw contrast value programmed in GPIO port B control register.
    282  * Used only at attach time to get initial contrast.
    283  */
    284 static int
    285 j6x0lcd_contrast_raw(uint16_t bcr, int width, const uint8_t *pin)
    286 {
    287 	int contrast;
    288 	int bit;
    289 
    290 	contrast = 0;
    291 	for (bit = 0; bit < width; ++bit) {
    292 		unsigned int c, v;
    293 
    294 		c = (bcr >> (pin[bit] << 1)) & 0x3;
    295 		if (c == 1)	/* output mode? */
    296 			v = 1;
    297 		else
    298 			v = 0;
    299 		contrast |= (v << bit);
    300 	}
    301 
    302 	return contrast;
    303 }
    304 
    305 
    306 /*
    307  * Set contrast by programming GPIO port B control register.
    308  * Data register has been initialized at attach time.
    309  */
    310 static void
    311 j6x0lcd_contrast_set(struct j6x0lcd_softc *sc, int contrast)
    312 {
    313 	uint16_t bcr;
    314 
    315 	sc->sc_contrast = contrast;
    316 
    317 	bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
    318 
    319 	bcr &= sc->sc_contrast_mask;
    320 	bcr |= sc->sc_contrast_control_bits[contrast];
    321 
    322 	hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
    323 }
    324 
    325 
    326 static int
    327 j6x0lcd_param(ctx, type, id, msg)
    328 	void *ctx;
    329 	int type;
    330 	long id;
    331 	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(ctx, type, id, msg)
    388 	void *ctx;
    389 	int type;
    390 	long id;
    391 	void *msg;
    392 {
    393 	int on;
    394 	uint16_t r;
    395 
    396 	if (type != CONFIG_HOOK_POWERCONTROL
    397 	    || id != CONFIG_HOOK_POWERCONTROL_LCD)
    398 		return (EINVAL);
    399 
    400 	on = (int)msg;
    401 
    402 	r = hd64461_reg_read_2(HD64461_GPBDR_REG16);
    403 	if (on)
    404 		r &= ~HD64461_GPBDR_J6X0_LCD_OFF;
    405 	else
    406 		r |= HD64461_GPBDR_J6X0_LCD_OFF;
    407 	hd64461_reg_write_2(HD64461_GPBDR_REG16, r);
    408 
    409 	return (0);
    410 }
    411