j6x0lcd.c revision 1.6.4.2 1 /* $NetBSD: j6x0lcd.c,v 1.6.4.2 2006/12/30 20:46:03 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.6.4.2 2006/12/30 20:46:03 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