/* $NetBSD: vrc4172pwm.c,v 1.22 2012/10/27 17:17:55 chs Exp $ */ /* * Copyright (c) 2000,2001 SATO Kazumi. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: vrc4172pwm.c,v 1.22 2012/10/27 17:17:55 chs Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include "locators.h" #ifdef VRC2PWMDEBUG #ifndef VRC2PWMDEBUG_CONF #define VRC2PWMDEBUG_CONF 0 #endif /* VRC2PWMDEBUG_CONF */ int vrc4172pwmdebug = VRC2PWMDEBUG_CONF; #define DPRINTF(arg) if (vrc4172pwmdebug) printf arg; #define VPRINTF(arg) if (bootverbose||vrc4172pwmdebug) printf arg; #define VDUMPREG(arg) if (bootverbose||vrc4172pwmdebug) vrc4172pwm_dumpreg(arg); #else /* VRC2PWMDEBUG */ #define DPRINTF(arg) #define VPRINTF(arg) if (bootverbose) printf arg; #define VDUMPREG(arg) if (bootverbose) vrc4172pwm_dumpreg(arg); #endif /* VRC2PWMDEBUG */ static int vrc4172pwmprobe(device_t, cfdata_t, void *); static void vrc4172pwmattach(device_t, device_t, void *); static void vrc4172pwm_write(struct vrc4172pwm_softc *, int, unsigned short); static unsigned short vrc4172pwm_read(struct vrc4172pwm_softc *, int); static int vrc4172pwm_event(void *, int, long, void *); static int vrc4172pwm_pmevent(void *, int, long, void *); static void vrc4172pwm_dumpreg(struct vrc4172pwm_softc *); static void vrc4172pwm_init_brightness(struct vrc4172pwm_softc *); void vrc4172pwm_light(struct vrc4172pwm_softc *, int); int vrc4172pwm_get_light(struct vrc4172pwm_softc *); int vrc4172pwm_get_brightness(struct vrc4172pwm_softc *); void vrc4172pwm_set_brightness(struct vrc4172pwm_softc *, int); int vrc4172pwm_rawduty2brightness(struct vrc4172pwm_softc *); int vrc4172pwm_brightness2rawduty(struct vrc4172pwm_softc *); struct vrc4172pwm_param * vrc4172pwm_getparam(void); void vrc4172pwm_dumpreg(struct vrc4172pwm_softc *); CFATTACH_DECL_NEW(vrc4172pwm, sizeof(struct vrc4172pwm_softc), vrc4172pwmprobe, vrc4172pwmattach, NULL, NULL); /* * platform related parameters */ struct vrc4172pwm_param vrc4172pwm_mcr520_param = { 1, /* probe broken */ 8, /* levels */ { 0x16, 0x1f, 0x24, 0x2a, 0x2f, 0x34, 0x3a, 0x3f } }; struct vrc4172pwm_param vrc4172pwm_mcr530_param = { 0, 8, /* levels */ { 0x16, 0x1b, 0x20, 0x25, 0x2a, 0x30, 0x37, 0x3f } }; struct vrc4172pwm_param vrc4172pwm_mcr700_param = { 1, /* probe broken */ 8, /* levels */ { 0x12, 0x15, 0x18, 0x1d, 0x24, 0x2d, 0x38, 0x3f } }; struct vrc4172pwm_param vrc4172pwm_sigmarion_param = { 0, 8, /* levels */ { 0xe, 0x13, 0x18, 0x1c, 0x23, 0x29, 0x32, 0x3f } }; struct platid_data vrc4172pwm_platid_param_table[] = { { &platid_mask_MACH_NEC_MCR_430, &vrc4172pwm_mcr530_param}, { &platid_mask_MACH_NEC_MCR_510, &vrc4172pwm_mcr520_param}, { &platid_mask_MACH_NEC_MCR_520, &vrc4172pwm_mcr520_param}, { &platid_mask_MACH_NEC_MCR_520A, &vrc4172pwm_mcr520_param}, { &platid_mask_MACH_NEC_MCR_530, &vrc4172pwm_mcr530_param}, { &platid_mask_MACH_NEC_MCR_530A, &vrc4172pwm_mcr530_param}, { &platid_mask_MACH_NEC_MCR_SIGMARION, &vrc4172pwm_sigmarion_param}, { &platid_mask_MACH_NEC_MCR_700, &vrc4172pwm_mcr700_param}, { &platid_mask_MACH_NEC_MCR_700A, &vrc4172pwm_mcr700_param}, { &platid_mask_MACH_NEC_MCR_730, &vrc4172pwm_mcr700_param}, { &platid_mask_MACH_NEC_MCR_730A, &vrc4172pwm_mcr700_param}, { NULL, NULL} }; struct vrc4172pwm_softc *this_pwm; static inline void vrc4172pwm_write(struct vrc4172pwm_softc *sc, int port, unsigned short val) { bus_space_write_2(sc->sc_iot, sc->sc_ioh, port, val); } static inline unsigned short vrc4172pwm_read(struct vrc4172pwm_softc *sc, int port) { return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, port)); } static int vrc4172pwmprobe(device_t parent, cfdata_t cf, void *aux) { platid_mask_t mask; struct vrip_attach_args *va = aux; bus_space_handle_t ioh; #ifdef VRC4172PWM_BROKEN_PROBE int probe = 0; #else /* VRC4172PWM_BROKEN_PROBE */ int probe = 1; #endif /* VRC4172PWM_BROKEN_PROBE */ int data; int data2; struct vrc4172pwm_param *param; int ret = 0; if (va->va_addr == VRIPIFCF_ADDR_DEFAULT) return (0); if (cf->cf_loc[VRIPIFCF_PLATFORM] == 0) return (0); if (cf->cf_loc[VRIPIFCF_PLATFORM] != -1) { /* if specify */ mask = PLATID_DEREF(cf->cf_loc[VRIPIFCF_PLATFORM]); VPRINTF(("vrc4172pwmprobe: check platid\n")); if (platid_match(&platid, &mask) == 0) return (0); param = vrc4172pwm_getparam(); if (param != NULL && param->brokenprobe) probe = 0; } if (probe) { if (bus_space_map(va->va_iot, va->va_addr, va->va_size, 0, &ioh)) { return (0); } data = bus_space_read_2(va->va_iot, ioh, VRC2_PWM_LCDDUTYEN); bus_space_write_2(va->va_iot, ioh, VRC2_PWM_LCDDUTYEN, 0xff); if ((data2 = bus_space_read_2(va->va_iot, ioh, VRC2_PWM_LCDDUTYEN)) == VRC2_PWM_LCDEN_MASK) { VPRINTF(("vrc4172pwmprobe:" " VRC2_PWM_LCDDUTYEN found\n")); ret = 1; } else { VPRINTF(("vrc4172pwmprobe: VRC2_PWM_LCDDUTYEN" " not found org=%x, data=%x!=%x\n", data, data2, VRC2_PWM_LCDEN_MASK)); } bus_space_write_2(va->va_iot, ioh, VRC2_PWM_LCDDUTYEN, data); bus_space_unmap(va->va_iot, ioh, va->va_size); } else ret = 1; VPRINTF(("vrc4172pwmprobe: return %d\n", ret)); return (ret); } static void vrc4172pwmattach(device_t parent, device_t self, void *aux) { struct vrc4172pwm_softc *sc = device_private(self); struct vrip_attach_args *va = aux; bus_space_tag_t iot = va->va_iot; bus_space_handle_t ioh; if (bus_space_map(iot, va->va_addr, 1, 0, &ioh)) { printf(": can't map bus space\n"); return; } sc->sc_iot = iot; sc->sc_ioh = ioh; printf("\n"); VDUMPREG(sc); /* basic setup */ sc->sc_pmhook = config_hook(CONFIG_HOOK_PMEVENT, CONFIG_HOOK_PMEVENT_HARDPOWER, CONFIG_HOOK_SHARE, vrc4172pwm_pmevent, sc); sc->sc_lcdhook = config_hook(CONFIG_HOOK_POWERCONTROL, CONFIG_HOOK_POWERCONTROL_LCDLIGHT, CONFIG_HOOK_SHARE, vrc4172pwm_event, sc); sc->sc_getlcdhook = config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_POWER_LCDLIGHT, CONFIG_HOOK_SHARE, vrc4172pwm_event, sc); sc->sc_sethook = config_hook(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, CONFIG_HOOK_SHARE, vrc4172pwm_event, sc); sc->sc_gethook = config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS, CONFIG_HOOK_SHARE, vrc4172pwm_event, sc); sc->sc_getmaxhook = config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BRIGHTNESS_MAX, CONFIG_HOOK_SHARE, vrc4172pwm_event, sc); vrc4172pwm_init_brightness(sc); if (sc->sc_param == NULL) printf("vrc4172pwm: NO parameter found. DISABLE pwm control\n"); this_pwm = sc; } /* * get platform related brightness paramerters */ struct vrc4172pwm_param * vrc4172pwm_getparam(void) { struct platid_data *p; if ((p = platid_search_data(&platid, vrc4172pwm_platid_param_table))) return (p->data); return (NULL); } /* * * Initialize PWM brightness parameters * */ void vrc4172pwm_init_brightness(struct vrc4172pwm_softc *sc) { sc->sc_param = vrc4172pwm_getparam(); sc->sc_raw_freq = vrc4172pwm_read(sc, VRC2_PWM_LCDFREQ); sc->sc_raw_duty = vrc4172pwm_read(sc, VRC2_PWM_LCDDUTY); sc->sc_brightness = vrc4172pwm_rawduty2brightness(sc); sc->sc_light = vrc4172pwm_get_light(sc); DPRINTF(("vrc4172pwm_init_brightness: param=0x%x, freq=0x%x," " duty=0x%x, blightness=%d light=%d\n", (int)sc->sc_param, sc->sc_raw_freq, sc->sc_raw_duty, sc->sc_brightness, sc->sc_light)); } /* * backlight on/off */ void vrc4172pwm_light(struct vrc4172pwm_softc *sc, int on) { int brightness; DPRINTF(("vrc4172pwm_light: %s\n", on?"ON":"OFF")); if (on) { vrc4172pwm_set_brightness(sc, sc->sc_brightness); vrc4172pwm_write(sc, VRC2_PWM_LCDDUTYEN, VRC2_PWM_LCD_EN); } else { brightness = sc->sc_brightness; /* save */ vrc4172pwm_set_brightness(sc, 0); /* need this, break sc->sc_brightness */ sc->sc_brightness = brightness; /* resume */ vrc4172pwm_write(sc, VRC2_PWM_LCDDUTYEN, VRC2_PWM_LCD_DIS); } sc->sc_light = on; } /* * get backlight on/off */ int vrc4172pwm_get_light(struct vrc4172pwm_softc *sc) { return (VRC2_PWM_LCDEN_MASK&vrc4172pwm_read(sc, VRC2_PWM_LCDDUTYEN)); } /* * set brightness */ void vrc4172pwm_set_brightness(struct vrc4172pwm_softc *sc, int val) { int raw; if (sc->sc_param == NULL) return; if (val < 0) val = 0; if (val > VRC2_PWM_MAX_BRIGHTNESS) val = VRC2_PWM_MAX_BRIGHTNESS; if (val > sc->sc_param->n_brightness) val = sc->sc_param->n_brightness; sc->sc_brightness = val; raw = vrc4172pwm_brightness2rawduty(sc); vrc4172pwm_write(sc, VRC2_PWM_LCDDUTY, raw); DPRINTF(("vrc4172pwm_set_brightness: val=%d raw=0x%x\n", val, raw)); } /* * get brightness */ int vrc4172pwm_get_brightness(struct vrc4172pwm_softc *sc) { if (sc->sc_param == NULL) return (VRC2_PWM_MAX_BRIGHTNESS); return (sc->sc_brightness); } /* * PWM duty to brightness */ int vrc4172pwm_rawduty2brightness(struct vrc4172pwm_softc *sc) { int i; if (sc->sc_param == NULL) return (VRC2_PWM_MAX_BRIGHTNESS); for (i = 0; i < sc->sc_param->n_brightness; i++) { if (sc->sc_raw_duty <= sc->sc_param->bvalues[i]) break; } if (i >= sc->sc_param->n_brightness-1) return (sc->sc_param->n_brightness-1); else return (i); } /* * brightness to raw duty */ int vrc4172pwm_brightness2rawduty(struct vrc4172pwm_softc *sc) { if (sc->sc_param == NULL) return (VRC2_PWM_LCDDUTY_MASK); return (sc->sc_param->bvalues[sc->sc_brightness]); } /* * PWM config hook events * */ int vrc4172pwm_event(void *ctx, int type, long id, void *msg) { struct vrc4172pwm_softc *sc = (struct vrc4172pwm_softc *)ctx; int why =(int)msg; if (type == CONFIG_HOOK_POWERCONTROL && id == CONFIG_HOOK_POWERCONTROL_LCDLIGHT) { DPRINTF(("vrc4172pwm:POWERCONTROL_LCDLIGHT: %d\n", why)); vrc4172pwm_light(sc, why); } else if (type == CONFIG_HOOK_GET && id == CONFIG_HOOK_POWER_LCDLIGHT) { *(int *)msg = vrc4172pwm_get_light(sc); DPRINTF(("vrc4172pwm:GET LCDLIGHT: %d\n", *(int *)msg)); } else if (type == CONFIG_HOOK_GET && id == CONFIG_HOOK_BRIGHTNESS) { *(int *)msg = vrc4172pwm_get_brightness(sc); DPRINTF(("vrc4172pwm:GET BRIGHTNESS: %d\n", *(int *)msg)); } else if (type == CONFIG_HOOK_GET && id == CONFIG_HOOK_BRIGHTNESS_MAX) { if (sc->sc_param == NULL) *(int *)msg = VRC2_PWM_MAX_BRIGHTNESS; else *(int *)msg = sc->sc_param->n_brightness-1; DPRINTF(("vrc4172pwm:GET MAX BRIGHTNESS: %d\n", *(int *)msg)); } else if (type == CONFIG_HOOK_SET && id == CONFIG_HOOK_BRIGHTNESS) { DPRINTF(("vrc4172pwm:SET BRIGHTNESS: %d\n", *(int *)msg)); vrc4172pwm_set_brightness(sc, *(int *)msg); } else { DPRINTF(("vrc4172pwm:unknown event: type %d id %ld\n", type, id)); return (1); } return (0); } /* * PWM config hook events * */ int vrc4172pwm_pmevent(void *ctx, int type, long id, void *msg) { struct vrc4172pwm_softc *sc = (struct vrc4172pwm_softc *)ctx; int why =(int)msg; if (type != CONFIG_HOOK_PMEVENT) return (1); switch (why) { case PWR_STANDBY: case PWR_SUSPEND: sc->sc_light_save = sc->sc_light; vrc4172pwm_light(sc, 0); break; case PWR_RESUME: vrc4172pwm_light(sc, sc->sc_light_save); break; default: return (1); } return (0); } /* * dump pwm registers */ void vrc4172pwm_dumpreg(struct vrc4172pwm_softc *sc) { int en, freq, duty; en = vrc4172pwm_read(sc, VRC2_PWM_LCDDUTYEN); freq = vrc4172pwm_read(sc, VRC2_PWM_LCDFREQ); duty = vrc4172pwm_read(sc, VRC2_PWM_LCDDUTY); printf("vrc4172pwm: dumpreg: lightenable = %d," " freq = 0x%x, duty = 0x%x\n", en, freq, duty); } /* end */