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