Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: lcdctl.c,v 1.3 2012/01/29 10:12:41 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.3 2012/01/29 10:12:41 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 	if (ZAURUS_ISC1000 || ZAURUS_ISC3000) {
    119 		sc->sc_nbacklighttbl = __arraycount(lcdctl_backlight_c3000);
    120 		sc->sc_backlighttbl = lcdctl_backlight_c3000;
    121 	} else {
    122 		/* XXX: Is this okay for C7x0/860? */
    123 		sc->sc_nbacklighttbl = __arraycount(lcdctl_backlight_c3000);
    124 		sc->sc_backlighttbl = lcdctl_backlight_c3000;
    125 	}
    126 
    127 	/* Start with approximately 40% of full brightness. */
    128 	lcdctl_set_brightness(sc, 3);
    129 
    130 	lcdctl_sc = sc;
    131 
    132 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
    133 	    lcdctl_brightness_up, true))
    134 		aprint_error_dev(self, "couldn't register event handler\n");
    135 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
    136 	    lcdctl_brightness_down, true))
    137 		aprint_error_dev(self, "couldn't register event handler\n");
    138 	if (!pmf_event_register(self, PMFE_DISPLAY_ON,
    139 	    lcdctl_display_on, true))
    140 		aprint_error_dev(self, "couldn't register event handler\n");
    141 	if (!pmf_event_register(self, PMFE_DISPLAY_OFF,
    142 	    lcdctl_display_off, true))
    143 		aprint_error_dev(self, "couldn't register event handler\n");
    144 }
    145 
    146 static void
    147 lcdctl_brightness_up(device_t dv)
    148 {
    149 	struct lcdctl_softc *sc = device_private(dv);
    150 
    151 	lcdctl_set_brightness(sc, sc->sc_brightness + 1);
    152 }
    153 
    154 static void
    155 lcdctl_brightness_down(device_t dv)
    156 {
    157 	struct lcdctl_softc *sc = device_private(dv);
    158 
    159 	lcdctl_set_brightness(sc, sc->sc_brightness - 1);
    160 }
    161 
    162 static void
    163 lcdctl_display_on(device_t dv)
    164 {
    165 	struct lcdctl_softc *sc = device_private(dv);
    166 
    167 	lcdctl_set_blank(sc, false);
    168 	lcdctl_set_backlight(sc, true);
    169 }
    170 
    171 static void
    172 lcdctl_display_off(device_t dv)
    173 {
    174 	struct lcdctl_softc *sc = device_private(dv);
    175 
    176 	lcdctl_set_backlight(sc, false);
    177 	lcdctl_set_blank(sc, true);
    178 }
    179 
    180 static __inline void
    181 set_backlight(const struct lcdctl_backlight *bl)
    182 {
    183 
    184 	(void) zssp_ic_send(ZSSP_IC_LZ9JG18, bl->duty);
    185 	if (ZAURUS_ISC1000)
    186 		ioexp_set_backlight(bl->on, bl->cont);
    187 	else
    188 		scoop_set_backlight(bl->on, bl->cont);
    189 }
    190 
    191 static void
    192 lcdctl_set_brightness_internal(struct lcdctl_softc *sc, int newval)
    193 {
    194 	int i;
    195 
    196 	/*
    197 	 * It appears that the C3000 backlight can draw too much power if we
    198 	 * switch it from a low to a high brightness.  Increasing brightness
    199 	 * in steps avoids this issue.
    200 	 */
    201 	if (newval > sc->sc_brightnesscurval) {
    202 		for (i = sc->sc_brightnesscurval + 1; i <= newval; i++) {
    203 			set_backlight(&sc->sc_backlighttbl[i]);
    204 			delay(5000);
    205 		}
    206 	} else
    207 		set_backlight(&sc->sc_backlighttbl[newval]);
    208 
    209 	sc->sc_brightnesscurval = newval;
    210 }
    211 
    212 static void
    213 lcdctl_set_brightness(struct lcdctl_softc *sc, int newval)
    214 {
    215 	int maxval = sc->sc_nbacklighttbl - 1;
    216 
    217 	if (newval < 0)
    218 		newval = 0;
    219 	else if (newval > maxval)
    220 		newval = maxval;
    221 
    222 	if (sc->sc_islit && !sc->sc_isblank)
    223 		lcdctl_set_brightness_internal(sc, newval);
    224 
    225 	if (newval > 0)
    226 		sc->sc_brightness = newval;
    227 }
    228 
    229 static void
    230 lcdctl_set_backlight(struct lcdctl_softc *sc, bool onoff)
    231 {
    232 
    233 	if (!onoff) {
    234 		lcdctl_set_brightness(sc, 0);
    235 		sc->sc_islit = false;
    236 	} else {
    237 		sc->sc_islit = true;
    238 		lcdctl_set_brightness(sc, sc->sc_brightness);
    239 	}
    240 }
    241 
    242 static void
    243 lcdctl_set_blank(struct lcdctl_softc *sc, bool isblank)
    244 {
    245 
    246 	if (isblank) {
    247 		lcdctl_set_brightness(sc, 0);
    248 		sc->sc_isblank = true;
    249 	} else {
    250 		sc->sc_isblank = false;
    251 		lcdctl_set_brightness(sc, sc->sc_brightness);
    252 	}
    253 }
    254 
    255 static void
    256 lcdctl_set_onoff(struct lcdctl_softc *sc, bool onoff)
    257 {
    258 
    259 	if (!onoff) {
    260 		lcdctl_set_brightness(sc, 0);
    261 	} else {
    262 		lcdctl_set_brightness(sc, sc->sc_brightness);
    263 	}
    264 }
    265 
    266 static int
    267 lcdctl_param_ioctl(struct lcdctl_softc *sc, u_long cmd, struct wsdisplay_param *dp)
    268 {
    269 	int rv = EINVAL;
    270 
    271 	switch (dp->param) {
    272 	case WSDISPLAYIO_PARAM_BACKLIGHT:
    273 		if (cmd == WSDISPLAYIO_GETPARAM) {
    274 			dp->min = 0;
    275 			dp->max = 1;
    276 			dp->curval = sc->sc_islit ? 1 : 0;
    277 			rv = 0;
    278 		} else if (cmd == WSDISPLAYIO_SETPARAM) {
    279 			lcdctl_set_backlight(sc, dp->curval != 0);
    280 			rv = 0;
    281 		}
    282 		break;
    283 
    284 	case WSDISPLAYIO_PARAM_CONTRAST:
    285 		/* unsupported */
    286 		rv = ENOTTY;
    287 		break;
    288 
    289 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
    290 		if (cmd == WSDISPLAYIO_GETPARAM) {
    291 			dp->min = 1;
    292 			dp->max = sc->sc_nbacklighttbl - 1;
    293 			dp->curval = sc->sc_brightness;
    294 			rv = 0;
    295 		} else if (cmd == WSDISPLAYIO_SETPARAM) {
    296 			lcdctl_set_brightness(sc, dp->curval);
    297 			rv = 0;
    298 		}
    299 		break;
    300 	}
    301 	return rv;
    302 }
    303 
    304 void
    305 lcdctl_brightness(int newval)
    306 {
    307 
    308 	if (__predict_true(lcdctl_sc != NULL))
    309 		lcdctl_set_brightness(lcdctl_sc, newval);
    310 }
    311 
    312 void
    313 lcdctl_blank(bool isblank)
    314 {
    315 
    316 	if (__predict_true(lcdctl_sc != NULL))
    317 		lcdctl_set_blank(lcdctl_sc, isblank);
    318 }
    319 
    320 void
    321 lcdctl_onoff(bool onoff)
    322 {
    323 
    324 	if (__predict_true(lcdctl_sc != NULL))
    325 		lcdctl_set_onoff(lcdctl_sc, onoff);
    326 }
    327 
    328 int
    329 lcdctl_param(u_long cmd, struct wsdisplay_param *dp)
    330 {
    331 
    332 	if (__predict_true(lcdctl_sc != NULL))
    333 		return lcdctl_param_ioctl(lcdctl_sc, cmd, dp);
    334 	return ENOTTY;	/* unsupported */
    335 }
    336