/* $NetBSD$ */

/*-
 * Copyright (c) 2006 The NetBSD Foundation, Inc.
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the NetBSD
 *	Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");

#include <sys/param.h>
#include <sys/conf.h>

#include <dev/ofw/openfirm.h>

#include <machine/autoconf.h>

#include <macppc/dev/akbdvar.h>
#include <macppc/dev/adbvar.h>

#define ADB_BRT_ID 31


/*      $OpenBSD: ofw_machdep.h,v 1.3 2003/06/02 16:16:27 miod Exp $    */
/*
 * For some reason, setting the brightness under 0x29 from OF switches the
 * backlight off, and it won't be switched on again until you set the
 * brightness above 0x33.  All hail hysteresis! -- miod
 */

#define MIN_BRIGHTNESS          0x34
#define MAX_BRIGHTNESS          0xff
#define STEP_BRIGHTNESS         8
#define DEFAULT_BRIGHTNESS      0x80


/*	$NetBSD: abtn.c,v 1.11 2005/12/11 12:18:03 christos Exp $	*/

#define BUTTON_BRIGHTER	0x09
#define BUTTON_DIMMER	0x0a


struct ofbrt_softc {
	struct device sc_dev;

	int sc_ih;		/* OFW device handle */

	int origaddr;		/* ADB device type */
	int adbaddr;		/* current ADB address */
	int handler_id;

	int brightness;		/* backlight brightness */

};


static int	ofbrt_match(struct device *, struct cfdata *, void *);
static void	ofbrt_attach(struct device *, struct device *, void *);

CFATTACH_DECL(ofbrt, sizeof(struct ofbrt_softc),
    ofbrt_match, ofbrt_attach, NULL, NULL);

static int	of_has_brt(void);
static void	of_set_brightness(int, int);
static int	of_get_brightness(int);

static void ofbrt_adbcomplete(caddr_t, caddr_t, int);


static int
ofbrt_match(struct device *parent, struct cfdata *match, void *aux)
{
	struct adb_attach_args *aa = aux;

	/* If we're the right driver, and the machine has OFW support
	 * for brightness control, and the console output is on the
	 * panel. 
	 */


	if (aa->origaddr == ADBADDR_MISC &&
	    aa->handler_id == ADB_BRT_ID &&
	    of_has_brt() && ofb_is_console())
		return 1;


	return 0;
}

static void
ofbrt_attach(struct device *parent, struct device *self, void *aux)
{
	struct ofbrt_softc *sc =
		(struct ofbrt_softc *)self;
	struct adb_attach_args *aa = aux;
	ADBSetInfoBlock adbinfo;
	int bright;

	int chosen, stdout;



	/* Get handle to display device handle . */

	/* Home brewed OF node locator */
	chosen = OF_finddevice("/chosen");
	if ( chosen == -1 )
		printf("Couldn't find /chosen \n"); /* XXX: reprobe
						       PCI */
	if (OF_getprop(chosen, "stdout", &stdout, 4) == -1)
		printf(" couldn't find property /chosen: stdout \n");

	
	sc->sc_ih = stdout;

	sc->brightness = bright = of_get_brightness(sc->sc_ih);
	of_set_brightness(sc->sc_ih, bright);

	sc->origaddr = aa->origaddr;
	sc->adbaddr = aa->adbaddr;
	sc->handler_id = aa->handler_id;

	adbinfo.siServiceRtPtr = (Ptr)ofbrt_adbcomplete;
	adbinfo.siDataAreaAddr = (caddr_t)sc;

	SetADBInfo(&adbinfo, sc->adbaddr);
	printf("Open Firmware brightness control \n");
}



int
of_has_brt()
{
	int ofnode;
	char type[16];
	

	/* Look for the backlight node */

	ofnode = getnodebyname(0, "backlight");
	if (ofnode == -1)
		return 0;

	/* Ok. This is a bit paranoid. */

	OF_getprop(ofnode, "device_type", type, sizeof(type));
	if (strcmp(type, "backlight") == 0)
		return 1;

	return 0;

}

void
of_set_brightness(int ihandle, int brightness)
{
	if (brightness < MIN_BRIGHTNESS)
		brightness = MIN_BRIGHTNESS;
	else if (brightness > MAX_BRIGHTNESS)
		brightness = MAX_BRIGHTNESS;

	/*
         * The OF method is called "set-contrast" but affects brightness.
	 * Don't ask.
	 */

	OF_call_method_1("set-contrast", ihandle, 1, brightness);
}

int
of_get_brightness(int ihandle)
{
	return 0;
}

extern struct cfdriver akbd_cd;

/* adb keyboard driver hook. Process brightness hotkeys. */

void
ofbrt_adbcomplete(buffer, data, adb_command)
	caddr_t buffer, data;
	int adb_command;
{
	struct ofbrt_softc *sc = (struct ofbrt_softc *)data;
	u_int cmd;

	cmd = buffer[1];


	switch (cmd) {
	case BUTTON_DIMMER:
		sc->brightness -= 8;
		if (sc->brightness < MIN_BRIGHTNESS)
			sc->brightness = MIN_BRIGHTNESS;
		of_set_brightness(sc->sc_ih, sc->brightness);
		printf("ofbrt: BUTTON_DIMMER pressed \n");
		break;

	case BUTTON_BRIGHTER:
		sc->brightness += 8;
		if (sc->brightness > MAX_BRIGHTNESS)
			sc->brightness = MAX_BRIGHTNESS;
		of_set_brightness(sc->sc_ih, sc->brightness);
		printf("ofbrt: BUTTON_BRIGHTER pressed \n");
		break;


	default:
		break;
	}


	/* Pass up the keystroke in any case. */

//	kbd_passup(akbd_cd.cd_devs[0],cmd);

}

