#include <sys/types.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>

#include <machine/bus.h>
#include <machine/resource.h>

#include <machine/pci_cfgreg.h>
#include <sys/rman.h>

#include <dev/sound/pci/xkodi.h>

struct xkodi_softc
{
	bus_space_tag_t		bst;
	bus_space_handle_t	bsh;
	device_t		dev;
	struct resource		*res;
	int			rid;
};

static u_int32_t
xrd(struct xkodi_softc *sc, int reg, int size)
{
	switch (size) {
	case 1:
		return bus_space_read_1(sc->bst, sc->bsh, reg);
	case 2:
		return bus_space_read_2(sc->bst, sc->bsh, reg);
	case 3:
		return bus_space_read_4(sc->bst, sc->bsh, reg);
	default:
		return 0xffffffff;
	}
}

static void
xwr(struct xkodi_softc *sc, int reg, u_int32_t data, int size)
{
	switch (size) {
	case 1:
		bus_space_write_1(sc->bst, sc->bsh, reg, data);
		break;
	case 2:
		bus_space_write_2(sc->bst, sc->bsh, reg, data);
		break;
	case 4:
		bus_space_write_4(sc->bst, sc->bsh, reg, data);
		break;
	}
}

static int
xkodi_rdi2c(struct xkodi_softc *sc, u_int32_t dev, u_int32_t addr)
{
	u_int32_t data;
	int i;

	for (i = 0; i < 1000; i++) {
		data = xrd(sc, ENVY24_CCS13, 1);
		if ((data & 0x01) == 0)
			break;
		DELAY(32); /* 31.25kHz */
	}
	if (i == 1000) {
		return -1;
	}
	xwr(sc, ENVY24_CCS11, addr, 1);
	xwr(sc, ENVY24_CCS10, (dev & 0xfe) | 0x00, 1);
	for (i = 0; i < 1000; i++) {
                data = xrd(sc, ENVY24_CCS13, 1);
                if ((data & 0x01) == 0)
                        break;
                DELAY(32); /* 31.25kHz */
        }
        if (i == 1000) {
                return -1;
        }
	data = xrd(sc, ENVY24_CCS12, 1);
	
	return (int) data;
}

static int
xkodi_probe(device_t dev)
{
	char *s = NULL;

	if (pci_get_device(dev) == ENVY24_PCI_ID)
		s = "ENVY24";

	if (s)
		device_set_desc(dev, s);
	return s? 0 : ENXIO;
}

static int
xkodi_attach(device_t dev)
{
	struct xkodi_softc *sc;
	u_int32_t               data;
	u_int32_t		e2prom;
	int k;

	sc = (struct xkodi_softc *) device_get_softc(dev);
	
	/*if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) {
		device_printf(dev, "cannot allocate softc\n");
		return ENXIO;
	}*/

	sc->dev = dev;

        /* initialize PCI interface */
        data = pci_read_config(dev, PCIR_COMMAND, 2);
        data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
        pci_write_config(dev, PCIR_COMMAND, data, 2);

	sc->rid = PCIR_CCS;
	sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, 0, ~0, 1,
		RF_ACTIVE);
	if (sc->res == NULL)
		return ENXIO;
	sc->bst = rman_get_bustag(sc->res);
	sc->bsh = rman_get_bushandle(sc->res);
	e2prom = xrd(sc, ENVY24_CCS13, 1);
	if ((e2prom & 0x80) == 0) {
		device_printf(sc->dev, "E2PROM not presented\n");
	}
	else {
	device_printf(sc->dev, "E2PROM DUMP:\n");
	printf("    | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C OD 0E 0F \n");
	printf("----+-------------------------------------------------\n");
	for (k = 0; k < 256; k++) {
	e2prom = xkodi_rdi2c(sc, 0xa0, k); 
	if ((k % 16) == 0) {
	if (k==0) printf(" %02d |", (k/16));
	else
	printf("\n %02d |", (k/16));
	}
	printf(" %02x", e2prom);
	}
	}
	return 0; 
}

static int
xkodi_detach(device_t dev)
{
	struct xkodi_softc *sc;

        sc = (struct xkodi_softc *) device_get_softc(dev);
	bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->res);
	return 0;
}

static device_method_t xkodi_methods[] = {
	/* Device interface */
	DEVMETHOD(device_probe,		xkodi_probe),
	DEVMETHOD(device_attach,	xkodi_attach),
	DEVMETHOD(device_detach,	xkodi_detach),

	{0,0}
};

static driver_t xkodi_driver = {
"xkodi",
xkodi_methods,
sizeof(struct xkodi_softc),
};

static devclass_t xkodi_devclass;

DRIVER_MODULE(xkodi, pci, xkodi_driver, xkodi_devclass, 0, 0);
