#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/audioio.h>
#include <sys/bus.h>
#include <dev/audio/audio_if.h>
#include <dev/ic/uda1341var.h>
#include <arch/arm/s3c2xx0/s3c2440reg.h>
#include <arch/arm/s3c2xx0/s3c2440var.h>
#include <arch/arm/s3c2xx0/s3c2440_dma.h>
#include <arch/arm/s3c2xx0/s3c2440_i2s.h>
#ifdef AUDIO_MINI2440_DEBUG
#define DPRINTF(x) do {printf x; } while (0)
#else
#define DPRINTF(s) do {} while (0)
#endif
struct uda_softc {
device_t sc_dev;
kmutex_t sc_lock;
kmutex_t sc_intr_lock;
struct uda1341_softc sc_uda1341;
s3c2440_i2s_buf_t sc_play_buf;
s3c2440_i2s_buf_t sc_rec_buf;
void *sc_i2s_handle;
};
int uda_ssio_open(void *, int);
void uda_ssio_close(void *);
int uda_ssio_query_format(void *, audio_format_query_t *);
int uda_ssio_set_format(void *, int,
const audio_params_t *, const audio_params_t *,
audio_filter_reg_t *, audio_filter_reg_t *);
int uda_ssio_start_output(void *, void *, int, void (*)(void *),
void *);
int uda_ssio_start_input(void *, void *, int, void (*)(void *),
void *);
int uda_ssio_halt_output(void *);
int uda_ssio_halt_input(void *);
int uda_ssio_getdev(void *, struct audio_device *ret);
void* uda_ssio_allocm(void *, int, size_t);
void uda_ssio_freem(void *, void *, size_t);
size_t uda_ssio_round_buffersize(void *, int, size_t);
int uda_ssio_get_props(void *);
void uda_ssio_get_locks(void *, kmutex_t**, kmutex_t**);
struct audio_hw_if uda1341_hw_if = {
.open = uda_ssio_open,
.close = uda_ssio_close,
.query_format = uda_ssio_query_format,
.set_format = uda_ssio_set_format,
.start_output = uda_ssio_start_output,
.start_input = uda_ssio_start_input,
.halt_output = uda_ssio_halt_output,
.halt_input = uda_ssio_halt_input,
.getdev = uda_ssio_getdev,
.set_port = uda1341_set_port,
.get_port = uda1341_get_port,
.query_devinfo = uda1341_query_devinfo,
.allocm = uda_ssio_allocm,
.freem = uda_ssio_freem,
.round_buffersize = uda_ssio_round_buffersize,
.get_props = uda_ssio_get_props,
.get_locks = uda_ssio_get_locks
};
static struct audio_device uda1341_device = {
"MINI2240-UDA1341",
"0.1",
"uda_ssio"
};
static const struct audio_format uda_ssio_formats[] =
{
{
.mode = AUMODE_PLAY | AUMODE_RECORD,
.encoding = AUDIO_ENCODING_SLINEAR_LE,
.validbits = 16,
.precision = 16,
.channels = 2,
.channel_mask = AUFMT_STEREO,
.frequency_type = 6,
.frequency = { 8000, 11025, 22050, 32000, 44100, 48000 },
}
};
#define UDA_SSIO_NFORMATS __arraycount(uda_ssio_formats)
void uda_ssio_l3_write(void *,int mode, int value);
int uda_ssio_match(device_t, cfdata_t, void*);
void uda_ssio_attach(device_t, device_t, void*);
CFATTACH_DECL_NEW(udassio, sizeof(struct uda_softc),
uda_ssio_match, uda_ssio_attach, NULL, NULL);
int
uda_ssio_match(device_t parent, cfdata_t match, void *aux)
{
DPRINTF(("%s\n", __func__));
return 1;
}
void
uda_ssio_attach(device_t parent, device_t self, void *aux)
{
struct uda_softc *sc = device_private(self);
struct s3c2xx0_softc *s3sc = s3c2xx0_softc;
struct s3c2440_i2s_attach_args *aa = aux;
uint32_t reg;
sc->sc_dev = self;
sc->sc_play_buf = NULL;
sc->sc_i2s_handle = aa->i2sa_handle;
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
s3c2440_i2s_set_intr_lock(aa->i2sa_handle, &sc->sc_intr_lock);
reg = bus_space_read_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBCON);
reg = GPIO_SET_FUNC(reg, 2, 1);
reg = GPIO_SET_FUNC(reg, 3, 1);
reg = GPIO_SET_FUNC(reg, 4, 1);
bus_space_write_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBCON, reg);
reg = bus_space_read_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT);
reg = GPIO_SET_DATA(reg, 4, 1);
reg = GPIO_SET_DATA(reg, 3, 0);
reg = GPIO_SET_DATA(reg, 2, 1);
bus_space_write_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT, reg);
printf("\n");
uda1341_attach(&sc->sc_uda1341);
sc->sc_uda1341.parent = sc;
sc->sc_uda1341.sc_l3_write = uda_ssio_l3_write;
sc->sc_uda1341.sc_bus_format = UDA1341_BUS_MSB;
s3c2440_i2s_set_bus_format(sc->sc_i2s_handle, S3C2440_I2S_BUS_MSB);
audio_attach_mi(&uda1341_hw_if, &sc->sc_uda1341, self);
}
int
uda_ssio_open(void *handle, int flags)
{
int retval;
DPRINTF(("%s\n", __func__));
if (!(flags & FREAD) && !(flags & FWRITE))
return EINVAL;
retval = uda1341_open(handle, flags);
if (retval != 0) {
return retval;
}
return 0;
}
void
uda_ssio_close(void *handle)
{
DPRINTF(("%s\n", __func__));
uda1341_close(handle);
}
int
uda_ssio_query_format(void *handle, audio_format_query_t *afp)
{
return audio_query_format(uda_ssio_formats, UDA_SSIO_NFORMATS, afp);
}
int
uda_ssio_set_format(void *handle, int setmode,
const audio_params_t *play, const audio_params_t *rec,
audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
int retval;
DPRINTF(("%s: setmode: %d\n", __func__, setmode));
DPRINTF(("%s: %dHz, encoding: %d, precision: %d, channels: %d\n",
__func__, play->sample_rate, play->encoding, play->precision,
play->channels));
if (setmode == AUMODE_PLAY) {
s3c2440_i2s_set_direction(sc->sc_i2s_handle,
S3C2440_I2S_TRANSMIT);
} else {
s3c2440_i2s_set_direction(sc->sc_i2s_handle,
S3C2440_I2S_RECEIVE);
}
s3c2440_i2s_set_sample_rate(sc->sc_i2s_handle, play->sample_rate);
s3c2440_i2s_set_sample_width(sc->sc_i2s_handle, 16);
switch (s3c2440_i2s_get_master_clock(sc->sc_i2s_handle)) {
case 384:
uc->sc_system_clock = UDA1341_CLOCK_384;
break;
case 256:
uc->sc_system_clock = UDA1341_CLOCK_256;
break;
default:
return EINVAL;
}
retval = uda1341_set_format(handle, setmode, play, rec, pfil, rfil);
if (retval != 0) {
return retval;
}
retval = s3c2440_i2s_commit(sc->sc_i2s_handle);
if (retval != 0) {
printf("Failed to setup I2S controller\n");
return retval;
}
return 0;
}
int
uda_ssio_start_output(void *handle, void *block, int bsize,
void (*intr)(void *), void *intrarg)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
return s3c2440_i2s_output(sc->sc_play_buf, block, bsize, intr, intrarg);
}
int
uda_ssio_start_input(void *handle, void *block, int bsize,
void (*intr)(void *), void *intrarg)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
return s3c2440_i2s_input(sc->sc_rec_buf, block, bsize, intr, intrarg);
}
int
uda_ssio_halt_output(void *handle)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
return s3c2440_i2s_halt_output(sc->sc_play_buf);
}
int
uda_ssio_halt_input(void *handle)
{
DPRINTF(("%s\n", __func__));
return 0;
}
int
uda_ssio_getdev(void *handle, struct audio_device *ret)
{
*ret = uda1341_device;
return 0;
}
void *
uda_ssio_allocm(void *handle, int direction, size_t size)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
void *retval = NULL;
DPRINTF(("%s\n", __func__));
if (direction == AUMODE_PLAY ) {
if (sc->sc_play_buf != NULL)
return NULL;
s3c2440_i2s_alloc(sc->sc_i2s_handle, direction, size, 0x00, &sc->sc_play_buf);
DPRINTF(("%s: addr of ring buffer: %p\n", __func__, sc->sc_play_buf->i2b_addr));
retval = sc->sc_play_buf->i2b_addr;
} else if (direction == AUMODE_RECORD) {
if (sc->sc_rec_buf != NULL)
return NULL;
s3c2440_i2s_alloc(sc->sc_i2s_handle, direction, size, 0x00, &sc->sc_rec_buf);
DPRINTF(("%s: addr of ring buffer: %p\n", __func__, sc->sc_rec_buf->i2b_addr));
retval = sc->sc_rec_buf->i2b_addr;
}
DPRINTF(("buffer: %p", retval));
return retval;
}
void
uda_ssio_freem(void *handle, void *ptr, size_t size)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
DPRINTF(("%s\n", __func__));
if (ptr == sc->sc_play_buf->i2b_addr)
s3c2440_i2s_free(sc->sc_play_buf);
else if (ptr == sc->sc_rec_buf->i2b_addr)
s3c2440_i2s_free(sc->sc_rec_buf);
}
size_t
uda_ssio_round_buffersize(void *handle, int direction, size_t bufsize)
{
DPRINTF(("%s: %d\n", __func__, (int)bufsize));
return bufsize;
}
int
uda_ssio_get_props(void *handle)
{
return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE;
}
void
uda_ssio_get_locks(void *handle, kmutex_t **intr, kmutex_t **thread)
{
struct uda1341_softc *uc = handle;
struct uda_softc *sc = uc->parent;
*intr = &sc->sc_intr_lock;
*thread = &sc->sc_lock;
}
void
uda_ssio_l3_write(void *cookie, int mode, int value)
{
struct s3c2xx0_softc *s3sc = s3c2xx0_softc;
uint32_t reg;
#define L3MODE 2
#define L3DATA 3
#define L3CLOCK 4
#define READ_GPIO() bus_space_read_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT)
#define WRITE_GPIO(val) bus_space_write_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT, val)
#define DELAY_TIME 1
reg = READ_GPIO();
reg = GPIO_SET_DATA(reg, L3CLOCK, 1);
reg = GPIO_SET_DATA(reg, L3MODE, mode);
reg = GPIO_SET_DATA(reg, L3DATA, 0);
WRITE_GPIO(reg);
if (mode == 1 ) {
reg = READ_GPIO();
reg = GPIO_SET_DATA(reg, L3MODE, 1);
WRITE_GPIO(reg);
}
DELAY(1);
for(int i = 0; i<8; i++) {
char bval = (value >> i) & 0x1;
reg = READ_GPIO();
reg = GPIO_SET_DATA(reg, L3CLOCK, 0);
reg = GPIO_SET_DATA(reg, L3DATA, bval);
WRITE_GPIO(reg);
DELAY(DELAY_TIME);
reg = READ_GPIO();
reg = GPIO_SET_DATA(reg, L3CLOCK, 1);
reg = GPIO_SET_DATA(reg, L3DATA, bval);
WRITE_GPIO(reg);
DELAY(DELAY_TIME);
}
reg = READ_GPIO();
reg = GPIO_SET_DATA(reg, L3MODE, 1);
reg = GPIO_SET_DATA(reg, L3CLOCK, 1);
reg = GPIO_SET_DATA(reg, L3DATA, 0);
WRITE_GPIO(reg);
#undef L3MODE
#undef L3DATA
#undef L3CLOCK
#undef DELAY_TIME
#undef READ_GPIO
#undef WRITE_GPIO
}