1 /*        $NetBSD: j6x0lcd.c,v 1.14 2009/04/05 02:23:00 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.14 2009/04/05 02:23:00 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
j6x0lcd_match(device_t parent,cfdata_t cf,void * aux)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
j6x0lcd_attach(device_t parent,device_t self,void * aux)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           /* XXX: TODO: don't rely on CONFIG_HOOK_POWERCONTROL_LCD */
282           if (!pmf_device_register(self, NULL, NULL))
283                     aprint_error_dev(self, "unable to establish power handler\n");
284 }
285 
286 
287 /*
288  * Get raw contrast value programmed in GPIO port B control register.
289  * Used only at attach time to get initial contrast.
290  */
291 static int
j6x0lcd_contrast_raw(uint16_t bcr,int width,const uint8_t * pin)292 j6x0lcd_contrast_raw(uint16_t bcr, int width, const uint8_t *pin)
293 {
294           int contrast;
295           int bit;
296 
297           contrast = 0;
298           for (bit = 0; bit < width; ++bit) {
299                     unsigned int c;
300 
301                     c = (bcr >> (pin[bit] << 1)) & 0x3;
302                     if (c == 1)         /* pin in output mode? */
303                               contrast |= (1 << bit);
304           }
305 
306           return contrast;
307 }
308 
309 
310 /*
311  * Set contrast by programming GPIO port B control register.
312  * Data register has been initialized at attach time.
313  */
314 static void
j6x0lcd_contrast_set(struct j6x0lcd_softc * sc,int contrast)315 j6x0lcd_contrast_set(struct j6x0lcd_softc *sc, int contrast)
316 {
317           uint16_t bcr;
318 
319           sc->sc_contrast = contrast;
320 
321           bcr = hd64461_reg_read_2(HD64461_GPBCR_REG16);
322 
323           bcr &= sc->sc_contrast_mask;
324           bcr |= sc->sc_contrast_control_bits[contrast];
325 
326           hd64461_reg_write_2(HD64461_GPBCR_REG16, bcr);
327 }
328 
329 
330 static int
j6x0lcd_param(void * ctx,int type,long id,void * msg)331 j6x0lcd_param(void *ctx, int type, long id, 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
j6x0lcd_power(void * ctx,int type,long id,void * msg)387 j6x0lcd_power(void *ctx, int type, long id, void *msg)
388 {
389           int on;
390           uint16_t r;
391 
392           if (type != CONFIG_HOOK_POWERCONTROL
393               || id != CONFIG_HOOK_POWERCONTROL_LCD)
394                     return (EINVAL);
395 
396           on = (int)msg;
397 
398           r = hd64461_reg_read_2(HD64461_GPBDR_REG16);
399           if (on)
400                     r &= ~HD64461_GPBDR_J6X0_LCD_OFF;
401           else
402                     r |= HD64461_GPBDR_J6X0_LCD_OFF;
403           hd64461_reg_write_2(HD64461_GPBDR_REG16, r);
404 
405           return (0);
406 }
407