lcdctl.c revision 1.2 1 /* $NetBSD: lcdctl.c,v 1.2 2012/01/27 14:48:22 tsutsui Exp $ */
2
3 /*-
4 * Copyright (C) 2012 NONAKA Kimihiro <nonaka (at) netbsd.org>
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: lcdctl.c,v 1.2 2012/01/27 14:48:22 tsutsui Exp $");
30
31 #include "ioexp.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/bus.h>
37
38 #include <dev/wscons/wsconsio.h>
39 #include <dev/wscons/wsdisplayvar.h>
40
41 #include <zaurus/zaurus/zaurus_var.h>
42 #include <zaurus/dev/lcdctlvar.h>
43 #include <zaurus/dev/zsspvar.h>
44 #include <zaurus/dev/scoopvar.h>
45 #include <zaurus/dev/ioexpvar.h>
46
47 struct lcdctl_backlight {
48 int duty; /* LZ9JG18 DAC value */
49 int cont; /* BACKLIGHT_CONT signal */
50 int on; /* BACKLIGHT_ON signal */
51 };
52
53 struct lcdctl_softc {
54 device_t sc_dev;
55
56 int sc_brightness;
57 int sc_brightnesscurval;
58 bool sc_islit;
59 bool sc_isblank;
60
61 int sc_nbacklighttbl;
62 const struct lcdctl_backlight *sc_backlighttbl;
63 };
64
65 /* for SL-C1000/SL-C3x00 */
66 static const struct lcdctl_backlight lcdctl_backlight_c3000[] = {
67 { 0x00, 0, 0 }, /* 0: Off */
68 { 0x00, 0, 1 }, /* 1: 0% */
69 { 0x01, 0, 1 }, /* 2: 20% */
70 { 0x07, 0, 1 }, /* 3: 40% */
71 { 0x01, 1, 1 }, /* 4: 60% */
72 { 0x07, 1, 1 }, /* 5: 80% */
73 { 0x11, 1, 1 } /* 6: 100% */
74 };
75
76 static int lcdctl_match(device_t, cfdata_t, void *);
77 static void lcdctl_attach(device_t, device_t, void *);
78
79 CFATTACH_DECL_NEW(lcdctl, sizeof(struct lcdctl_softc),
80 lcdctl_match, lcdctl_attach, NULL, NULL);
81
82 static void lcdctl_brightness_up(device_t);
83 static void lcdctl_brightness_down(device_t);
84 static void lcdctl_display_on(device_t);
85 static void lcdctl_display_off(device_t);
86
87 static void lcdctl_set_brightness(struct lcdctl_softc *, int);
88 static void lcdctl_set_blank(struct lcdctl_softc *, bool);
89 static void lcdctl_set_backlight(struct lcdctl_softc *, bool);
90
91 static struct lcdctl_softc *lcdctl_sc;
92
93 static int
94 lcdctl_match(device_t parent, cfdata_t cf, void *aux)
95 {
96 struct zssp_attach_args *aa = aux;
97
98 if (strcmp("lcdctl", aa->zaa_name))
99 return 0;
100
101 return 1;
102 }
103
104 static void
105 lcdctl_attach(device_t parent, device_t self, void *aux)
106 {
107 struct lcdctl_softc *sc = device_private(self);
108
109 sc->sc_dev = self;
110
111 aprint_normal("\n");
112 aprint_naive("\n");
113
114 sc->sc_brightness = sc->sc_brightnesscurval = 1;
115 sc->sc_islit = true;
116 sc->sc_isblank = false;
117
118 sc->sc_nbacklighttbl = __arraycount(lcdctl_backlight_c3000);
119 sc->sc_backlighttbl = lcdctl_backlight_c3000;
120
121 /* Start with approximately 40% of full brightness. */
122 lcdctl_set_brightness(sc, 3);
123
124 lcdctl_sc = sc;
125
126 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
127 lcdctl_brightness_up, true))
128 aprint_error_dev(self, "couldn't register event handler\n");
129 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
130 lcdctl_brightness_down, true))
131 aprint_error_dev(self, "couldn't register event handler\n");
132 if (!pmf_event_register(self, PMFE_DISPLAY_ON,
133 lcdctl_display_on, true))
134 aprint_error_dev(self, "couldn't register event handler\n");
135 if (!pmf_event_register(self, PMFE_DISPLAY_OFF,
136 lcdctl_display_off, true))
137 aprint_error_dev(self, "couldn't register event handler\n");
138 }
139
140 static void
141 lcdctl_brightness_up(device_t dv)
142 {
143 struct lcdctl_softc *sc = device_private(dv);
144
145 lcdctl_set_brightness(sc, sc->sc_brightness + 1);
146 }
147
148 static void
149 lcdctl_brightness_down(device_t dv)
150 {
151 struct lcdctl_softc *sc = device_private(dv);
152
153 lcdctl_set_brightness(sc, sc->sc_brightness - 1);
154 }
155
156 static void
157 lcdctl_display_on(device_t dv)
158 {
159 struct lcdctl_softc *sc = device_private(dv);
160
161 lcdctl_set_blank(sc, false);
162 lcdctl_set_backlight(sc, true);
163 }
164
165 static void
166 lcdctl_display_off(device_t dv)
167 {
168 struct lcdctl_softc *sc = device_private(dv);
169
170 lcdctl_set_backlight(sc, false);
171 lcdctl_set_blank(sc, true);
172 }
173
174 static __inline void
175 set_backlight(const struct lcdctl_backlight *bl)
176 {
177
178 (void) zssp_ic_send(ZSSP_IC_LZ9JG18, bl->duty);
179 if (ZAURUS_ISC1000)
180 ioexp_set_backlight(bl->on, bl->cont);
181 else
182 scoop_set_backlight(bl->on, bl->cont);
183 }
184
185 static void
186 lcdctl_set_brightness_internal(struct lcdctl_softc *sc, int newval)
187 {
188 int i;
189
190 /*
191 * It appears that the C3000 backlight can draw too much power if we
192 * switch it from a low to a high brightness. Increasing brightness
193 * in steps avoids this issue.
194 */
195 if (newval > sc->sc_brightnesscurval) {
196 for (i = sc->sc_brightnesscurval + 1; i <= newval; i++) {
197 set_backlight(&sc->sc_backlighttbl[i]);
198 delay(5000);
199 }
200 } else
201 set_backlight(&sc->sc_backlighttbl[newval]);
202
203 sc->sc_brightnesscurval = newval;
204 }
205
206 static void
207 lcdctl_set_brightness(struct lcdctl_softc *sc, int newval)
208 {
209 int maxval = sc->sc_nbacklighttbl - 1;
210
211 if (newval < 0)
212 newval = 0;
213 else if (newval > maxval)
214 newval = maxval;
215
216 if (sc->sc_islit && !sc->sc_isblank)
217 lcdctl_set_brightness_internal(sc, newval);
218
219 if (newval > 0)
220 sc->sc_brightness = newval;
221 }
222
223 static void
224 lcdctl_set_backlight(struct lcdctl_softc *sc, bool onoff)
225 {
226
227 if (!onoff) {
228 lcdctl_set_brightness(sc, 0);
229 sc->sc_islit = false;
230 } else {
231 sc->sc_islit = true;
232 lcdctl_set_brightness(sc, sc->sc_brightness);
233 }
234 }
235
236 static void
237 lcdctl_set_blank(struct lcdctl_softc *sc, bool isblank)
238 {
239
240 if (isblank) {
241 lcdctl_set_brightness(sc, 0);
242 sc->sc_isblank = true;
243 } else {
244 sc->sc_isblank = false;
245 lcdctl_set_brightness(sc, sc->sc_brightness);
246 }
247 }
248
249 static void
250 lcdctl_set_onoff(struct lcdctl_softc *sc, bool onoff)
251 {
252
253 if (!onoff) {
254 lcdctl_set_brightness(sc, 0);
255 } else {
256 lcdctl_set_brightness(sc, sc->sc_brightness);
257 }
258 }
259
260 static int
261 lcdctl_param_ioctl(struct lcdctl_softc *sc, u_long cmd, struct wsdisplay_param *dp)
262 {
263 int rv = EINVAL;
264
265 switch (dp->param) {
266 case WSDISPLAYIO_PARAM_BACKLIGHT:
267 if (cmd == WSDISPLAYIO_GETPARAM) {
268 dp->min = 0;
269 dp->max = 1;
270 dp->curval = sc->sc_islit ? 1 : 0;
271 rv = 0;
272 } else if (cmd == WSDISPLAYIO_SETPARAM) {
273 lcdctl_set_backlight(sc, dp->curval != 0);
274 rv = 0;
275 }
276 break;
277
278 case WSDISPLAYIO_PARAM_CONTRAST:
279 /* unsupported */
280 rv = ENOTTY;
281 break;
282
283 case WSDISPLAYIO_PARAM_BRIGHTNESS:
284 if (cmd == WSDISPLAYIO_GETPARAM) {
285 dp->min = 1;
286 dp->max = sc->sc_nbacklighttbl - 1;
287 dp->curval = sc->sc_brightness;
288 rv = 0;
289 } else if (cmd == WSDISPLAYIO_SETPARAM) {
290 lcdctl_set_brightness(sc, dp->curval);
291 rv = 0;
292 }
293 break;
294 }
295 return rv;
296 }
297
298 void
299 lcdctl_brightness(int newval)
300 {
301
302 if (__predict_true(lcdctl_sc != NULL))
303 lcdctl_set_brightness(lcdctl_sc, newval);
304 }
305
306 void
307 lcdctl_blank(bool isblank)
308 {
309
310 if (__predict_true(lcdctl_sc != NULL))
311 lcdctl_set_blank(lcdctl_sc, isblank);
312 }
313
314 void
315 lcdctl_onoff(bool onoff)
316 {
317
318 if (__predict_true(lcdctl_sc != NULL))
319 lcdctl_set_onoff(lcdctl_sc, onoff);
320 }
321
322 int
323 lcdctl_param(u_long cmd, struct wsdisplay_param *dp)
324 {
325
326 if (__predict_true(lcdctl_sc != NULL))
327 return lcdctl_param_ioctl(lcdctl_sc, cmd, dp);
328 return ENOTTY; /* unsupported */
329 }
330