j6x0lcd.c revision 1.7 1 /* $NetBSD: j6x0lcd.c,v 1.7 2005/08/03 22:25:17 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.7 2005/08/03 22:25:17 uwe 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