#include <sys/param.h>
#include <sys/audioio.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <dev/audio_if.h>
#include <dev/ofw/openfirm.h>
#include <macppc/dev/dbdma.h>
#include <machine/autoconf.h>
#include <macppc/dev/i2svar.h>
#ifdef TUMBLER_DEBUG
# define DPRINTF printf
#else
# define DPRINTF while (0) printf
#endif
#define tumbler_softc i2s_softc
int kiic_write(struct device *, int, int, const void *, int);
int kiic_writereg(struct device *, int, u_int);
void tumbler_init(struct tumbler_softc *);
int tumbler_match(struct device *, void *, void *);
void tumbler_attach(struct device *, struct device *, void *);
void tumbler_defer(struct device *);
void tumbler_set_volume(struct tumbler_softc *, int, int);
void tumbler_set_bass(struct tumbler_softc *, int);
void tumbler_set_treble(struct tumbler_softc *, int);
int tas3001_write(struct tumbler_softc *, u_int, const void *);
int tas3001_init(struct tumbler_softc *);
const struct cfattach tumbler_ca = {
sizeof(struct tumbler_softc), tumbler_match, tumbler_attach
};
struct cfdriver tumbler_cd = {
NULL, "tumbler", DV_DULL
};
const struct audio_hw_if tumbler_hw_if = {
.open = i2s_open,
.close = i2s_close,
.set_params = i2s_set_params,
.round_blocksize = i2s_round_blocksize,
.halt_output = i2s_halt_output,
.halt_input = i2s_halt_input,
.set_port = i2s_set_port,
.get_port = i2s_get_port,
.query_devinfo = i2s_query_devinfo,
.allocm = i2s_allocm,
.round_buffersize = i2s_round_buffersize,
.trigger_output = i2s_trigger_output,
.trigger_input = i2s_trigger_input,
};
const uint8_t tumbler_trebletab[] = {
0x96,
0x94,
0x92,
0x90,
0x8e,
0x8c,
0x8a,
0x88,
0x86,
0x84,
0x82,
0x80,
0x7e,
0x7c,
0x7a,
0x78,
0x76,
0x74,
0x72,
0x70,
0x6d,
0x6b,
0x68,
0x65,
0x62,
0x5e,
0x59,
0x5a,
0x4f,
0x49,
0x42,
0x3a,
0x32,
0x28,
0x1c,
0x10,
0x01,
};
const uint8_t tumbler_basstab[] = {
0x86,
0x7f,
0x7a,
0x76,
0x72,
0x6e,
0x6b,
0x66,
0x61,
0x5d,
0x5a,
0x58,
0x55,
0x53,
0x4f,
0x4b,
0x46,
0x42,
0x3e,
0x3b,
0x38,
0x35,
0x31,
0x2e,
0x2b,
0x28,
0x25,
0x21,
0x1c,
0x18,
0x16,
0x13,
0x10,
0x0d,
0x0a,
0x06,
0x01,
};
#define DEQ_MCR 0x01
#define DEQ_DRC 0x02
#define DEQ_VOLUME 0x04
#define DEQ_TREBLE 0x05
#define DEQ_BASS 0x06
#define DEQ_MIXER1 0x07
#define DEQ_MIXER2 0x08
#define DEQ_LB0 0x0a
#define DEQ_LB1 0x0b
#define DEQ_LB2 0x0c
#define DEQ_LB3 0x0d
#define DEQ_LB4 0x0e
#define DEQ_LB5 0x0f
#define DEQ_RB0 0x13
#define DEQ_RB1 0x14
#define DEQ_RB2 0x15
#define DEQ_RB3 0x16
#define DEQ_RB4 0x17
#define DEQ_RB5 0x18
#define DEQ_MCR_FL 0x80
#define DEQ_MCR_SC 0x40
#define DEQ_MCR_SC_32 0x00
#define DEQ_MCR_SC_64 0x40
#define DEQ_MCR_OM 0x30
#define DEQ_MCR_OM_L 0x00
#define DEQ_MCR_OM_R 0x10
#define DEQ_MCR_OM_I2S 0x20
#define DEQ_MCR_IM 0x0c
#define DEQ_MCR_IM_L 0x00
#define DEQ_MCR_IM_R 0x04
#define DEQ_MCR_IM_I2S 0x08
#define DEQ_MCR_W 0x03
#define DEQ_MCR_W_16 0x00
#define DEQ_MCR_W_18 0x01
#define DEQ_MCR_W_20 0x02
#define DEQ_DRC_CR 0xc0
#define DEQ_DRC_CR_31 0xc0
#define DEQ_DRC_EN 0x01
#define DEQ_MCR_I2S (DEQ_MCR_OM_I2S | DEQ_MCR_IM_I2S)
struct tas3001_reg {
u_char MCR[1];
u_char DRC[2];
u_char VOLUME[6];
u_char TREBLE[1];
u_char BASS[1];
u_char MIXER1[3];
u_char MIXER2[3];
u_char LB0[15];
u_char LB1[15];
u_char LB2[15];
u_char LB3[15];
u_char LB4[15];
u_char LB5[15];
u_char RB0[15];
u_char RB1[15];
u_char RB2[15];
u_char RB3[15];
u_char RB4[15];
u_char RB5[15];
};
int
tumbler_match(struct device *parent, void *match, void *aux)
{
struct confargs *ca = aux;
int soundbus, soundchip;
char compat[32];
if (strcmp(ca->ca_name, "i2s") != 0)
return (0);
if ((soundbus = OF_child(ca->ca_node)) == 0 ||
(soundchip = OF_child(soundbus)) == 0)
return (0);
bzero(compat, sizeof compat);
OF_getprop(soundchip, "compatible", compat, sizeof compat);
if (strcmp(compat, "tumbler") != 0)
return (0);
return (1);
}
void
tumbler_attach(struct device *parent, struct device *self, void *aux)
{
struct tumbler_softc *sc = (struct tumbler_softc *)self;
sc->sc_setvolume = tumbler_set_volume;
sc->sc_setbass = tumbler_set_bass;
sc->sc_settreble = tumbler_set_treble;
i2s_attach(parent, sc, aux);
config_defer(self, tumbler_defer);
}
void
tumbler_defer(struct device *dev)
{
struct tumbler_softc *sc = (struct tumbler_softc *)dev;
struct device *dv;
TAILQ_FOREACH(dv, &alldevs, dv_list)
if (strcmp(dv->dv_cfdata->cf_driver->cd_name, "kiic") == 0 &&
strcmp(dv->dv_parent->dv_cfdata->cf_driver->cd_name, "macobio") == 0)
sc->sc_i2c = dv;
if (sc->sc_i2c == NULL) {
printf("%s: unable to find i2c\n", sc->sc_dev.dv_xname);
return;
}
audio_attach_mi(&tumbler_hw_if, sc, NULL, &sc->sc_dev);
tumbler_init(sc);
}
void
tumbler_set_volume(struct tumbler_softc *sc, int left, int right)
{
u_char vol[6];
sc->sc_vol_l = left;
sc->sc_vol_r = right;
left <<= 6;
right <<= 6;
vol[0] = left >> 16;
vol[1] = left >> 8;
vol[2] = left;
vol[3] = right >> 16;
vol[4] = right >> 8;
vol[5] = right;
tas3001_write(sc, DEQ_VOLUME, vol);
}
void
tumbler_set_treble(struct tumbler_softc *sc, int value)
{
uint8_t reg;
if ((value >= 0) && (value <= 255) && (value != sc->sc_treble)) {
reg = tumbler_trebletab[(value >> 3) + 2];
if (tas3001_write(sc, DEQ_TREBLE, ®) < 0)
return;
sc->sc_treble = value;
}
}
void
tumbler_set_bass(struct tumbler_softc *sc, int value)
{
uint8_t reg;
if ((value >= 0) && (value <= 255) && (value != sc->sc_bass)) {
reg = tumbler_basstab[(value >> 3) + 2];
if (tas3001_write(sc, DEQ_BASS, ®) < 0)
return;
sc->sc_bass = value;
}
}
const struct tas3001_reg tas3001_initdata = {
{ DEQ_MCR_SC_64 | DEQ_MCR_I2S | DEQ_MCR_W_20 },
{ DEQ_DRC_CR_31, 0xa0 },
{ 0, 0, 0, 0, 0, 0 },
{ 0x72 },
{ 0x3e },
{ 0x10, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
const char tas3001_regsize[] = {
0,
sizeof tas3001_initdata.MCR,
sizeof tas3001_initdata.DRC,
0,
sizeof tas3001_initdata.VOLUME,
sizeof tas3001_initdata.TREBLE,
sizeof tas3001_initdata.BASS,
sizeof tas3001_initdata.MIXER1,
sizeof tas3001_initdata.MIXER2,
0,
sizeof tas3001_initdata.LB0,
sizeof tas3001_initdata.LB1,
sizeof tas3001_initdata.LB2,
sizeof tas3001_initdata.LB3,
sizeof tas3001_initdata.LB4,
sizeof tas3001_initdata.LB5,
0,
0,
0,
sizeof tas3001_initdata.RB0,
sizeof tas3001_initdata.RB1,
sizeof tas3001_initdata.RB2,
sizeof tas3001_initdata.RB3,
sizeof tas3001_initdata.RB4,
sizeof tas3001_initdata.RB5
};
#define DEQaddr 0x68
int
tas3001_write(struct tumbler_softc *sc, u_int reg, const void *data)
{
int size;
KASSERT(reg < sizeof tas3001_regsize);
size = tas3001_regsize[reg];
KASSERT(size > 0);
if (kiic_write(sc->sc_i2c, DEQaddr, reg, data, size))
return (-1);
return (0);
}
#define DEQ_WRITE(sc, reg, addr) \
if (tas3001_write(sc, reg, addr)) goto err
int
tas3001_init(struct tumbler_softc *sc)
{
deq_reset(sc);
DEQ_WRITE(sc, DEQ_LB0, tas3001_initdata.LB0);
DEQ_WRITE(sc, DEQ_LB1, tas3001_initdata.LB1);
DEQ_WRITE(sc, DEQ_LB2, tas3001_initdata.LB2);
DEQ_WRITE(sc, DEQ_LB3, tas3001_initdata.LB3);
DEQ_WRITE(sc, DEQ_LB4, tas3001_initdata.LB4);
DEQ_WRITE(sc, DEQ_LB5, tas3001_initdata.LB5);
DEQ_WRITE(sc, DEQ_RB0, tas3001_initdata.RB0);
DEQ_WRITE(sc, DEQ_RB1, tas3001_initdata.RB1);
DEQ_WRITE(sc, DEQ_RB1, tas3001_initdata.RB1);
DEQ_WRITE(sc, DEQ_RB2, tas3001_initdata.RB2);
DEQ_WRITE(sc, DEQ_RB3, tas3001_initdata.RB3);
DEQ_WRITE(sc, DEQ_RB4, tas3001_initdata.RB4);
DEQ_WRITE(sc, DEQ_MCR, tas3001_initdata.MCR);
DEQ_WRITE(sc, DEQ_DRC, tas3001_initdata.DRC);
DEQ_WRITE(sc, DEQ_VOLUME, tas3001_initdata.VOLUME);
DEQ_WRITE(sc, DEQ_TREBLE, tas3001_initdata.TREBLE);
DEQ_WRITE(sc, DEQ_BASS, tas3001_initdata.BASS);
DEQ_WRITE(sc, DEQ_MIXER1, tas3001_initdata.MIXER1);
DEQ_WRITE(sc, DEQ_MIXER2, tas3001_initdata.MIXER2);
return (0);
err:
printf("%s: tas3001_init: error\n", sc->sc_dev.dv_xname);
return (-1);
}
void
tumbler_init(struct tumbler_softc *sc)
{
i2s_set_rate(sc, 44100);
#if 1
#define IER 4
#define I2C_INT_DATA 0x01
#define I2C_INT_ADDR 0x02
#define I2C_INT_STOP 0x04
kiic_writereg(sc->sc_i2c, IER,I2C_INT_DATA|I2C_INT_ADDR|I2C_INT_STOP);
#endif
if (tas3001_init(sc))
return;
tumbler_set_volume(sc, 80, 80);
}