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