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