xref: /netbsd-src/sys/arch/zaurus/dev/lcdctl.c (revision 533071c4dd860f30eaf5ea090a8f9ea80de94634)
1*533071c4Stsutsui /*	$NetBSD: lcdctl.c,v 1.3 2012/01/29 10:12:41 tsutsui Exp $	*/
26145ba81Stsutsui 
36145ba81Stsutsui /*-
46145ba81Stsutsui  * Copyright (C) 2012 NONAKA Kimihiro <nonaka@netbsd.org>
56145ba81Stsutsui  * All rights reserved.
66145ba81Stsutsui  *
76145ba81Stsutsui  * Redistribution and use in source and binary forms, with or without
86145ba81Stsutsui  * modification, are permitted provided that the following conditions
96145ba81Stsutsui  * are met:
106145ba81Stsutsui  * 1. Redistributions of source code must retain the above copyright
116145ba81Stsutsui  *    notice, this list of conditions and the following disclaimer.
126145ba81Stsutsui  * 2. Redistributions in binary form must reproduce the above copyright
136145ba81Stsutsui  *    notice, this list of conditions and the following disclaimer in the
146145ba81Stsutsui  *    documentation and/or other materials provided with the distribution.
156145ba81Stsutsui  *
166145ba81Stsutsui  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
176145ba81Stsutsui  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
186145ba81Stsutsui  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
196145ba81Stsutsui  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
206145ba81Stsutsui  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
216145ba81Stsutsui  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
226145ba81Stsutsui  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
236145ba81Stsutsui  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
246145ba81Stsutsui  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
256145ba81Stsutsui  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
266145ba81Stsutsui  */
276145ba81Stsutsui 
286145ba81Stsutsui #include <sys/cdefs.h>
29*533071c4Stsutsui __KERNEL_RCSID(0, "$NetBSD: lcdctl.c,v 1.3 2012/01/29 10:12:41 tsutsui Exp $");
306145ba81Stsutsui 
316145ba81Stsutsui #include "ioexp.h"
326145ba81Stsutsui 
336145ba81Stsutsui #include <sys/param.h>
346145ba81Stsutsui #include <sys/systm.h>
356145ba81Stsutsui #include <sys/device.h>
366145ba81Stsutsui #include <sys/bus.h>
376145ba81Stsutsui 
386145ba81Stsutsui #include <dev/wscons/wsconsio.h>
396145ba81Stsutsui #include <dev/wscons/wsdisplayvar.h>
406145ba81Stsutsui 
416145ba81Stsutsui #include <zaurus/zaurus/zaurus_var.h>
426145ba81Stsutsui #include <zaurus/dev/lcdctlvar.h>
436145ba81Stsutsui #include <zaurus/dev/zsspvar.h>
446145ba81Stsutsui #include <zaurus/dev/scoopvar.h>
456145ba81Stsutsui #include <zaurus/dev/ioexpvar.h>
466145ba81Stsutsui 
476145ba81Stsutsui struct lcdctl_backlight {
486145ba81Stsutsui 	int	duty;		/* LZ9JG18 DAC value */
496145ba81Stsutsui 	int	cont;		/* BACKLIGHT_CONT signal */
506145ba81Stsutsui 	int	on;		/* BACKLIGHT_ON signal */
516145ba81Stsutsui };
526145ba81Stsutsui 
536145ba81Stsutsui struct lcdctl_softc {
546145ba81Stsutsui 	device_t	sc_dev;
556145ba81Stsutsui 
566145ba81Stsutsui 	int		sc_brightness;
576145ba81Stsutsui 	int		sc_brightnesscurval;
586145ba81Stsutsui 	bool		sc_islit;
596145ba81Stsutsui 	bool		sc_isblank;
606145ba81Stsutsui 
616145ba81Stsutsui 	int		sc_nbacklighttbl;
626145ba81Stsutsui 	const struct lcdctl_backlight *sc_backlighttbl;
636145ba81Stsutsui };
646145ba81Stsutsui 
656145ba81Stsutsui /* for SL-C1000/SL-C3x00 */
666145ba81Stsutsui static const struct lcdctl_backlight lcdctl_backlight_c3000[] = {
676145ba81Stsutsui 	{ 0x00, 0,  0 },	/* 0:  Off */
686145ba81Stsutsui 	{ 0x00, 0,  1 },	/* 1:   0% */
696145ba81Stsutsui 	{ 0x01, 0,  1 },	/* 2:  20% */
706145ba81Stsutsui 	{ 0x07, 0,  1 },	/* 3:  40% */
716145ba81Stsutsui 	{ 0x01, 1,  1 },	/* 4:  60% */
726145ba81Stsutsui 	{ 0x07, 1,  1 },	/* 5:  80% */
736145ba81Stsutsui 	{ 0x11, 1,  1 }		/* 6: 100% */
746145ba81Stsutsui };
756145ba81Stsutsui 
766145ba81Stsutsui static int	lcdctl_match(device_t, cfdata_t, void *);
776145ba81Stsutsui static void	lcdctl_attach(device_t, device_t, void *);
786145ba81Stsutsui 
796145ba81Stsutsui CFATTACH_DECL_NEW(lcdctl, sizeof(struct lcdctl_softc),
806145ba81Stsutsui 	lcdctl_match, lcdctl_attach, NULL, NULL);
816145ba81Stsutsui 
826145ba81Stsutsui static void lcdctl_brightness_up(device_t);
836145ba81Stsutsui static void lcdctl_brightness_down(device_t);
846145ba81Stsutsui static void lcdctl_display_on(device_t);
856145ba81Stsutsui static void lcdctl_display_off(device_t);
866145ba81Stsutsui 
876145ba81Stsutsui static void lcdctl_set_brightness(struct lcdctl_softc *, int);
886145ba81Stsutsui static void lcdctl_set_blank(struct lcdctl_softc *, bool);
896145ba81Stsutsui static void lcdctl_set_backlight(struct lcdctl_softc *, bool);
906145ba81Stsutsui 
916145ba81Stsutsui static struct lcdctl_softc *lcdctl_sc;
926145ba81Stsutsui 
936145ba81Stsutsui static int
lcdctl_match(device_t parent,cfdata_t cf,void * aux)946145ba81Stsutsui lcdctl_match(device_t parent, cfdata_t cf, void *aux)
956145ba81Stsutsui {
966145ba81Stsutsui 	struct zssp_attach_args *aa = aux;
976145ba81Stsutsui 
986145ba81Stsutsui 	if (strcmp("lcdctl", aa->zaa_name))
996145ba81Stsutsui 		return 0;
1006145ba81Stsutsui 
1016145ba81Stsutsui 	return 1;
1026145ba81Stsutsui }
1036145ba81Stsutsui 
1046145ba81Stsutsui static void
lcdctl_attach(device_t parent,device_t self,void * aux)1056145ba81Stsutsui lcdctl_attach(device_t parent, device_t self, void *aux)
1066145ba81Stsutsui {
1076145ba81Stsutsui 	struct lcdctl_softc *sc = device_private(self);
1086145ba81Stsutsui 
1096145ba81Stsutsui 	sc->sc_dev = self;
1106145ba81Stsutsui 
1116145ba81Stsutsui 	aprint_normal("\n");
1126145ba81Stsutsui 	aprint_naive("\n");
1136145ba81Stsutsui 
1146145ba81Stsutsui 	sc->sc_brightness = sc->sc_brightnesscurval = 1;
1156145ba81Stsutsui 	sc->sc_islit = true;
1166145ba81Stsutsui 	sc->sc_isblank = false;
1176145ba81Stsutsui 
118*533071c4Stsutsui 	if (ZAURUS_ISC1000 || ZAURUS_ISC3000) {
1196145ba81Stsutsui 		sc->sc_nbacklighttbl = __arraycount(lcdctl_backlight_c3000);
1206145ba81Stsutsui 		sc->sc_backlighttbl = lcdctl_backlight_c3000;
121*533071c4Stsutsui 	} else {
122*533071c4Stsutsui 		/* XXX: Is this okay for C7x0/860? */
123*533071c4Stsutsui 		sc->sc_nbacklighttbl = __arraycount(lcdctl_backlight_c3000);
124*533071c4Stsutsui 		sc->sc_backlighttbl = lcdctl_backlight_c3000;
125*533071c4Stsutsui 	}
1266145ba81Stsutsui 
12763752ed2Stsutsui 	/* Start with approximately 40% of full brightness. */
12863752ed2Stsutsui 	lcdctl_set_brightness(sc, 3);
12963752ed2Stsutsui 
13063752ed2Stsutsui 	lcdctl_sc = sc;
1316145ba81Stsutsui 
1326145ba81Stsutsui 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
1336145ba81Stsutsui 	    lcdctl_brightness_up, true))
1346145ba81Stsutsui 		aprint_error_dev(self, "couldn't register event handler\n");
1356145ba81Stsutsui 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
1366145ba81Stsutsui 	    lcdctl_brightness_down, true))
1376145ba81Stsutsui 		aprint_error_dev(self, "couldn't register event handler\n");
1386145ba81Stsutsui 	if (!pmf_event_register(self, PMFE_DISPLAY_ON,
1396145ba81Stsutsui 	    lcdctl_display_on, true))
1406145ba81Stsutsui 		aprint_error_dev(self, "couldn't register event handler\n");
1416145ba81Stsutsui 	if (!pmf_event_register(self, PMFE_DISPLAY_OFF,
1426145ba81Stsutsui 	    lcdctl_display_off, true))
1436145ba81Stsutsui 		aprint_error_dev(self, "couldn't register event handler\n");
1446145ba81Stsutsui }
1456145ba81Stsutsui 
1466145ba81Stsutsui static void
lcdctl_brightness_up(device_t dv)1476145ba81Stsutsui lcdctl_brightness_up(device_t dv)
1486145ba81Stsutsui {
1496145ba81Stsutsui 	struct lcdctl_softc *sc = device_private(dv);
1506145ba81Stsutsui 
1516145ba81Stsutsui 	lcdctl_set_brightness(sc, sc->sc_brightness + 1);
1526145ba81Stsutsui }
1536145ba81Stsutsui 
1546145ba81Stsutsui static void
lcdctl_brightness_down(device_t dv)1556145ba81Stsutsui lcdctl_brightness_down(device_t dv)
1566145ba81Stsutsui {
1576145ba81Stsutsui 	struct lcdctl_softc *sc = device_private(dv);
1586145ba81Stsutsui 
1596145ba81Stsutsui 	lcdctl_set_brightness(sc, sc->sc_brightness - 1);
1606145ba81Stsutsui }
1616145ba81Stsutsui 
1626145ba81Stsutsui static void
lcdctl_display_on(device_t dv)1636145ba81Stsutsui lcdctl_display_on(device_t dv)
1646145ba81Stsutsui {
1656145ba81Stsutsui 	struct lcdctl_softc *sc = device_private(dv);
1666145ba81Stsutsui 
1676145ba81Stsutsui 	lcdctl_set_blank(sc, false);
1686145ba81Stsutsui 	lcdctl_set_backlight(sc, true);
1696145ba81Stsutsui }
1706145ba81Stsutsui 
1716145ba81Stsutsui static void
lcdctl_display_off(device_t dv)1726145ba81Stsutsui lcdctl_display_off(device_t dv)
1736145ba81Stsutsui {
1746145ba81Stsutsui 	struct lcdctl_softc *sc = device_private(dv);
1756145ba81Stsutsui 
1766145ba81Stsutsui 	lcdctl_set_backlight(sc, false);
1776145ba81Stsutsui 	lcdctl_set_blank(sc, true);
1786145ba81Stsutsui }
1796145ba81Stsutsui 
1806145ba81Stsutsui static __inline void
set_backlight(const struct lcdctl_backlight * bl)1816145ba81Stsutsui set_backlight(const struct lcdctl_backlight *bl)
1826145ba81Stsutsui {
1836145ba81Stsutsui 
1846145ba81Stsutsui 	(void) zssp_ic_send(ZSSP_IC_LZ9JG18, bl->duty);
1856145ba81Stsutsui 	if (ZAURUS_ISC1000)
1866145ba81Stsutsui 		ioexp_set_backlight(bl->on, bl->cont);
1876145ba81Stsutsui 	else
1886145ba81Stsutsui 		scoop_set_backlight(bl->on, bl->cont);
1896145ba81Stsutsui }
1906145ba81Stsutsui 
1916145ba81Stsutsui static void
lcdctl_set_brightness_internal(struct lcdctl_softc * sc,int newval)1926145ba81Stsutsui lcdctl_set_brightness_internal(struct lcdctl_softc *sc, int newval)
1936145ba81Stsutsui {
1946145ba81Stsutsui 	int i;
1956145ba81Stsutsui 
1966145ba81Stsutsui 	/*
1976145ba81Stsutsui 	 * It appears that the C3000 backlight can draw too much power if we
1986145ba81Stsutsui 	 * switch it from a low to a high brightness.  Increasing brightness
1996145ba81Stsutsui 	 * in steps avoids this issue.
2006145ba81Stsutsui 	 */
2016145ba81Stsutsui 	if (newval > sc->sc_brightnesscurval) {
2026145ba81Stsutsui 		for (i = sc->sc_brightnesscurval + 1; i <= newval; i++) {
2036145ba81Stsutsui 			set_backlight(&sc->sc_backlighttbl[i]);
2046145ba81Stsutsui 			delay(5000);
2056145ba81Stsutsui 		}
2066145ba81Stsutsui 	} else
2076145ba81Stsutsui 		set_backlight(&sc->sc_backlighttbl[newval]);
2086145ba81Stsutsui 
2096145ba81Stsutsui 	sc->sc_brightnesscurval = newval;
2106145ba81Stsutsui }
2116145ba81Stsutsui 
2126145ba81Stsutsui static void
lcdctl_set_brightness(struct lcdctl_softc * sc,int newval)2136145ba81Stsutsui lcdctl_set_brightness(struct lcdctl_softc *sc, int newval)
2146145ba81Stsutsui {
2156145ba81Stsutsui 	int maxval = sc->sc_nbacklighttbl - 1;
2166145ba81Stsutsui 
2176145ba81Stsutsui 	if (newval < 0)
2186145ba81Stsutsui 		newval = 0;
2196145ba81Stsutsui 	else if (newval > maxval)
2206145ba81Stsutsui 		newval = maxval;
2216145ba81Stsutsui 
2226145ba81Stsutsui 	if (sc->sc_islit && !sc->sc_isblank)
2236145ba81Stsutsui 		lcdctl_set_brightness_internal(sc, newval);
2246145ba81Stsutsui 
2256145ba81Stsutsui 	if (newval > 0)
2266145ba81Stsutsui 		sc->sc_brightness = newval;
2276145ba81Stsutsui }
2286145ba81Stsutsui 
2296145ba81Stsutsui static void
lcdctl_set_backlight(struct lcdctl_softc * sc,bool onoff)2306145ba81Stsutsui lcdctl_set_backlight(struct lcdctl_softc *sc, bool onoff)
2316145ba81Stsutsui {
2326145ba81Stsutsui 
2336145ba81Stsutsui 	if (!onoff) {
2346145ba81Stsutsui 		lcdctl_set_brightness(sc, 0);
2356145ba81Stsutsui 		sc->sc_islit = false;
2366145ba81Stsutsui 	} else {
2376145ba81Stsutsui 		sc->sc_islit = true;
2386145ba81Stsutsui 		lcdctl_set_brightness(sc, sc->sc_brightness);
2396145ba81Stsutsui 	}
2406145ba81Stsutsui }
2416145ba81Stsutsui 
2426145ba81Stsutsui static void
lcdctl_set_blank(struct lcdctl_softc * sc,bool isblank)2436145ba81Stsutsui lcdctl_set_blank(struct lcdctl_softc *sc, bool isblank)
2446145ba81Stsutsui {
2456145ba81Stsutsui 
2466145ba81Stsutsui 	if (isblank) {
2476145ba81Stsutsui 		lcdctl_set_brightness(sc, 0);
2486145ba81Stsutsui 		sc->sc_isblank = true;
2496145ba81Stsutsui 	} else {
2506145ba81Stsutsui 		sc->sc_isblank = false;
2516145ba81Stsutsui 		lcdctl_set_brightness(sc, sc->sc_brightness);
2526145ba81Stsutsui 	}
2536145ba81Stsutsui }
2546145ba81Stsutsui 
2556145ba81Stsutsui static void
lcdctl_set_onoff(struct lcdctl_softc * sc,bool onoff)2566145ba81Stsutsui lcdctl_set_onoff(struct lcdctl_softc *sc, bool onoff)
2576145ba81Stsutsui {
2586145ba81Stsutsui 
2596145ba81Stsutsui 	if (!onoff) {
2606145ba81Stsutsui 		lcdctl_set_brightness(sc, 0);
2616145ba81Stsutsui 	} else {
2626145ba81Stsutsui 		lcdctl_set_brightness(sc, sc->sc_brightness);
2636145ba81Stsutsui 	}
2646145ba81Stsutsui }
2656145ba81Stsutsui 
2666145ba81Stsutsui static int
lcdctl_param_ioctl(struct lcdctl_softc * sc,u_long cmd,struct wsdisplay_param * dp)2676145ba81Stsutsui lcdctl_param_ioctl(struct lcdctl_softc *sc, u_long cmd, struct wsdisplay_param *dp)
2686145ba81Stsutsui {
2696145ba81Stsutsui 	int rv = EINVAL;
2706145ba81Stsutsui 
2716145ba81Stsutsui 	switch (dp->param) {
2726145ba81Stsutsui 	case WSDISPLAYIO_PARAM_BACKLIGHT:
2736145ba81Stsutsui 		if (cmd == WSDISPLAYIO_GETPARAM) {
2746145ba81Stsutsui 			dp->min = 0;
2756145ba81Stsutsui 			dp->max = 1;
2766145ba81Stsutsui 			dp->curval = sc->sc_islit ? 1 : 0;
2776145ba81Stsutsui 			rv = 0;
2786145ba81Stsutsui 		} else if (cmd == WSDISPLAYIO_SETPARAM) {
2796145ba81Stsutsui 			lcdctl_set_backlight(sc, dp->curval != 0);
2806145ba81Stsutsui 			rv = 0;
2816145ba81Stsutsui 		}
2826145ba81Stsutsui 		break;
2836145ba81Stsutsui 
2846145ba81Stsutsui 	case WSDISPLAYIO_PARAM_CONTRAST:
2856145ba81Stsutsui 		/* unsupported */
2866145ba81Stsutsui 		rv = ENOTTY;
2876145ba81Stsutsui 		break;
2886145ba81Stsutsui 
2896145ba81Stsutsui 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
2906145ba81Stsutsui 		if (cmd == WSDISPLAYIO_GETPARAM) {
2916145ba81Stsutsui 			dp->min = 1;
2926145ba81Stsutsui 			dp->max = sc->sc_nbacklighttbl - 1;
2936145ba81Stsutsui 			dp->curval = sc->sc_brightness;
2946145ba81Stsutsui 			rv = 0;
2956145ba81Stsutsui 		} else if (cmd == WSDISPLAYIO_SETPARAM) {
2966145ba81Stsutsui 			lcdctl_set_brightness(sc, dp->curval);
2976145ba81Stsutsui 			rv = 0;
2986145ba81Stsutsui 		}
2996145ba81Stsutsui 		break;
3006145ba81Stsutsui 	}
3016145ba81Stsutsui 	return rv;
3026145ba81Stsutsui }
3036145ba81Stsutsui 
3046145ba81Stsutsui void
lcdctl_brightness(int newval)3056145ba81Stsutsui lcdctl_brightness(int newval)
3066145ba81Stsutsui {
3076145ba81Stsutsui 
3086145ba81Stsutsui 	if (__predict_true(lcdctl_sc != NULL))
3096145ba81Stsutsui 		lcdctl_set_brightness(lcdctl_sc, newval);
3106145ba81Stsutsui }
3116145ba81Stsutsui 
3126145ba81Stsutsui void
lcdctl_blank(bool isblank)3136145ba81Stsutsui lcdctl_blank(bool isblank)
3146145ba81Stsutsui {
3156145ba81Stsutsui 
3166145ba81Stsutsui 	if (__predict_true(lcdctl_sc != NULL))
3176145ba81Stsutsui 		lcdctl_set_blank(lcdctl_sc, isblank);
3186145ba81Stsutsui }
3196145ba81Stsutsui 
3206145ba81Stsutsui void
lcdctl_onoff(bool onoff)3216145ba81Stsutsui lcdctl_onoff(bool onoff)
3226145ba81Stsutsui {
3236145ba81Stsutsui 
3246145ba81Stsutsui 	if (__predict_true(lcdctl_sc != NULL))
3256145ba81Stsutsui 		lcdctl_set_onoff(lcdctl_sc, onoff);
3266145ba81Stsutsui }
3276145ba81Stsutsui 
3286145ba81Stsutsui int
lcdctl_param(u_long cmd,struct wsdisplay_param * dp)3296145ba81Stsutsui lcdctl_param(u_long cmd, struct wsdisplay_param *dp)
3306145ba81Stsutsui {
3316145ba81Stsutsui 
3326145ba81Stsutsui 	if (__predict_true(lcdctl_sc != NULL))
3336145ba81Stsutsui 		return lcdctl_param_ioctl(lcdctl_sc, cmd, dp);
3346145ba81Stsutsui 	return ENOTTY;	/* unsupported */
3356145ba81Stsutsui }
336