Home | History | Annotate | Line # | Download | only in dev
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