@@ -167,6 +167,7 @@ common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
common-obj-y += usb/
common-obj-$(CONFIG_PTIMER) += ptimer.o
common-obj-$(CONFIG_MAX7310) += max7310.o
+common-obj-y += audio_codec.o
common-obj-$(CONFIG_WM8750) += wm8750.o
common-obj-$(CONFIG_TWL92230) += twl92230.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
@@ -24,6 +24,7 @@
#include "ui/console.h"
#include "block/block.h"
#include "audio/audio.h"
+#include "hw/audio_codec.h"
#include "hw/boards.h"
#include "sysemu/blockdev.h"
#include "hw/sysbus.h"
@@ -739,9 +740,11 @@ static void spitz_i2c_setup(PXA2xxState *cpu)
qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]);
/* .. and to the sound interface. */
cpu->i2s->opaque = wm;
- cpu->i2s->codec_out = wm8750_dac_dat;
- cpu->i2s->codec_in = wm8750_adc_dat;
- wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
+ cpu->i2s->codec_out = audio_codec_dac_dat;
+ cpu->i2s->codec_in = audio_codec_adc_dat;
+ audio_codec_data_req_set(DEVICE(wm),
+ cpu->i2s->data_req,
+ cpu->i2s);
}
static void spitz_akita_i2c_setup(PXA2xxState *cpu)
@@ -23,6 +23,7 @@
#include "sysemu/blockdev.h"
#include "ui/console.h"
#include "audio/audio.h"
+#include "hw/audio_codec.h"
#include "exec/address-spaces.h"
#ifdef DEBUG_Z2
@@ -353,9 +354,11 @@ static void z2_init(QEMUMachineInitArgs *args)
i2c_create_slave(bus, "aer915", 0x55);
wm = i2c_create_slave(bus, "wm8750", 0x1b);
mpu->i2s->opaque = wm;
- mpu->i2s->codec_out = wm8750_dac_dat;
- mpu->i2s->codec_in = wm8750_adc_dat;
- wm8750_data_req_set(wm, mpu->i2s->data_req, mpu->i2s);
+ mpu->i2s->codec_out = audio_codec_dac_dat;
+ mpu->i2s->codec_in = audio_codec_adc_dat;
+ audio_codec_data_req_set(DEVICE(wm),
+ mpu->i2s->data_req,
+ mpu->i2s);
qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS,
qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]);
new file mode 100644
@@ -0,0 +1,81 @@
+/*
+ * Audio Codec Class
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "qdev.h"
+#include "i2c.h"
+#include "audio_codec.h"
+
+void audio_codec_data_req_set(DeviceState *dev,
+ void (*data_req)(void *, int, int),
+ void *opaque)
+{
+ AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(dev);
+ if (k->data_req_set) {
+ k->data_req_set(dev, data_req, opaque);
+ }
+}
+
+void audio_codec_dac_dat(void *opaque, uint32_t sample)
+{
+ AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+ if (k->dac_dat) {
+ k->dac_dat(opaque, sample);
+ }
+}
+
+uint32_t audio_codec_adc_dat(void *opaque)
+{
+ AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+ uint32_t ret = 0;
+ if (k->adc_dat) {
+ ret = k->adc_dat(opaque);
+ }
+ return ret;
+}
+
+void *audio_codec_dac_buffer(void *opaque, int samples)
+{
+ AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+ void *ret = NULL;
+ if (k->dac_buffer) {
+ ret = k->dac_buffer(opaque, samples);
+ }
+ return ret;
+}
+
+void audio_codec_dac_commit(void *opaque)
+{
+ AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+ if (k->dac_commit) {
+ k->dac_commit(opaque);
+ }
+}
+
+void audio_codec_set_bclk_in(void *opaque, int new_hz)
+{
+ AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+ if (k->set_bclk_in) {
+ k->set_bclk_in(opaque, new_hz);
+ }
+}
+
+static const TypeInfo audio_codec_info = {
+ .name = TYPE_AUDIO_CODEC,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(AudioCodecState),
+ .abstract = true,
+ .class_size = sizeof(AudioCodecClass),
+};
+
+static void audio_codec_register_types(void)
+{
+ type_register_static(&audio_codec_info);
+}
+
+type_init(audio_codec_register_types)
new file mode 100644
@@ -0,0 +1,56 @@
+/*
+ * Audio Codec Class
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef QEMU_AUDIO_CODEC_H
+#define QEMU_AUDIO_CODEC_H
+
+#include "qdev.h"
+#include "i2c.h"
+
+typedef I2CSlave AudioCodecState;
+
+#define TYPE_AUDIO_CODEC "audio-codec"
+#define AUDIO_CODEC(obj) \
+ OBJECT_CHECK(AudioCodecState, (obj), TYPE_AUDIO_CODEC)
+
+typedef struct AudioCodecClass {
+ /*< private >*/
+ I2CSlaveClass parent;
+
+ /*< public >*/
+ void (*data_req_set)(DeviceState *dev,
+ void (*data_req)(void *, int, int),
+ void *opaque);
+ void (*dac_dat)(void *opaque, uint32_t sample);
+ uint32_t (*adc_dat)(void *opaque);
+ void *(*dac_buffer)(void *opaque, int samples);
+ void (*dac_commit)(void *opaque);
+ void (*set_bclk_in)(void *opaque, int new_hz);
+} AudioCodecClass;
+
+#define AUDIO_CODEC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AudioCodecClass, (klass), TYPE_AUDIO_CODEC)
+#define AUDIO_CODEC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AudioCodecClass, (obj), TYPE_AUDIO_CODEC)
+
+void audio_codec_data_req_set(DeviceState *dev,
+ void (*data_req)(void *, int, int),
+ void *opaque);
+
+void audio_codec_dac_dat(void *opaque, uint32_t sample);
+
+uint32_t audio_codec_adc_dat(void *opaque);
+
+void *audio_codec_dac_buffer(void *opaque, int samples);
+
+void audio_codec_dac_commit(void *opaque);
+
+void audio_codec_set_bclk_in(void *opaque, int new_hz);
+
+#endif
@@ -63,15 +63,6 @@ int i2c_recv(i2c_bus *bus);
DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr);
-/* wm8750.c */
-void wm8750_data_req_set(DeviceState *dev,
- void (*data_req)(void *, int, int), void *opaque);
-void wm8750_dac_dat(void *opaque, uint32_t sample);
-uint32_t wm8750_adc_dat(void *opaque);
-void *wm8750_dac_buffer(void *opaque, int samples);
-void wm8750_dac_commit(void *opaque);
-void wm8750_set_bclk_in(void *opaque, int new_hz);
-
/* lm832x.c */
void lm832x_key_event(DeviceState *dev, int key, int state);
@@ -14,6 +14,7 @@
#include "hw/i2c.h"
#include "hw/sysbus.h"
#include "audio/audio.h"
+#include "audio_codec.h"
#define MP_AUDIO_SIZE 0x00001000
@@ -82,32 +83,36 @@ static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in)
mem_buffer = buf;
if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {
if (s->playback_mode & MP_AUDIO_MONO) {
- codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
+ codec_buffer = audio_codec_dac_buffer(AUDIO_CODEC(s->wm),
+ block_size >> 1);
for (pos = 0; pos < block_size; pos += 2) {
*codec_buffer++ = *(int16_t *)mem_buffer;
*codec_buffer++ = *(int16_t *)mem_buffer;
mem_buffer += 2;
}
} else {
- memcpy(wm8750_dac_buffer(s->wm, block_size >> 2),
- (uint32_t *)mem_buffer, block_size);
+ memcpy(audio_codec_dac_buffer(AUDIO_CODEC(s->wm),
+ block_size >> 2),
+ (uint32_t *)mem_buffer,
+ block_size);
}
} else {
if (s->playback_mode & MP_AUDIO_MONO) {
- codec_buffer = wm8750_dac_buffer(s->wm, block_size);
+ codec_buffer = audio_codec_dac_buffer(s->wm, block_size);
for (pos = 0; pos < block_size; pos++) {
*codec_buffer++ = cpu_to_le16(256 * *mem_buffer);
*codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
}
} else {
- codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
+ codec_buffer = audio_codec_dac_buffer(AUDIO_CODEC(s->wm),
+ block_size >> 1);
for (pos = 0; pos < block_size; pos += 2) {
*codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
*codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
}
}
}
- wm8750_dac_commit(s->wm);
+ audio_codec_dac_commit(AUDIO_CODEC(s->wm));
s->last_free = free_out - block_size;
@@ -135,7 +140,7 @@ static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s)
}
rate /= ((s->clock_div >> 8) & 0xff) + 1;
- wm8750_set_bclk_in(s->wm, rate);
+ audio_codec_set_bclk_in(AUDIO_CODEC(s->wm), rate);
}
static uint64_t mv88w8618_audio_read(void *opaque, hwaddr offset,
@@ -244,7 +249,9 @@ static int mv88w8618_audio_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->irq);
- wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s);
+ audio_codec_data_req_set(DEVICE(s->wm),
+ mv88w8618_audio_callback,
+ s);
memory_region_init_io(&s->iomem, &mv88w8618_audio_ops, s,
"audio", MP_AUDIO_SIZE);
@@ -10,11 +10,12 @@
#include "hw/hw.h"
#include "hw/i2c.h"
#include "audio/audio.h"
+#include "audio_codec.h"
#define IN_PORT_N 3
#define OUT_PORT_N 3
-#define CODEC "wm8750"
+#define TYPE_WM8750 "wm8750"
typedef struct {
int adc;
@@ -24,7 +25,7 @@ typedef struct {
} WMRate;
typedef struct {
- I2CSlave i2c;
+ AudioCodecState parent;
uint8_t i2c_data[2];
int i2c_len;
QEMUSoundCard card;
@@ -50,6 +51,9 @@ typedef struct {
int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
} WM8750State;
+#define WM8750(obj) \
+ OBJECT_CHECK(WM8750State, obj, TYPE_WM8750)
+
/* pow(10.0, -i / 20.0) * 255, i = 0..42 */
static const uint8_t wm8750_vol_db_table[] = {
255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45,
@@ -80,14 +84,14 @@ static inline void wm8750_out_flush(WM8750State *s)
static void wm8750_audio_in_cb(void *opaque, int avail_b)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
s->req_in = avail_b;
s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2);
}
static void wm8750_audio_out_cb(void *opaque, int free_b)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
if (s->idx_out >= free_b) {
s->idx_out = free_b;
@@ -200,11 +204,11 @@ static void wm8750_set_format(WM8750State *s)
in_fmt.fmt = AUD_FMT_S16;
s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
- CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
+ TYPE_WM8750 ".input1", s, wm8750_audio_in_cb, &in_fmt);
s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1],
- CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt);
+ TYPE_WM8750 ".input2", s, wm8750_audio_in_cb, &in_fmt);
s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2],
- CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
+ TYPE_WM8750 ".input3", s, wm8750_audio_in_cb, &in_fmt);
/* Setup output */
out_fmt.endianness = 0;
@@ -213,12 +217,12 @@ static void wm8750_set_format(WM8750State *s)
out_fmt.fmt = AUD_FMT_S16;
s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
- CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
+ TYPE_WM8750 ".speaker", s, wm8750_audio_out_cb, &out_fmt);
s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1],
- CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt);
+ TYPE_WM8750 ".headphone", s, wm8750_audio_out_cb, &out_fmt);
/* MONOMIX is also in stereo for simplicity */
s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2],
- CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt);
+ TYPE_WM8750 ".monomix", s, wm8750_audio_out_cb, &out_fmt);
/* no sense emulating OUT3 which is a mix of other outputs */
wm8750_vol_update(s);
@@ -256,7 +260,7 @@ static void wm8750_clk_update(WM8750State *s, int ext)
static void wm8750_reset(I2CSlave *i2c)
{
- WM8750State *s = (WM8750State *) i2c;
+ WM8750State *s = WM8750(i2c);
s->rate = &wm_rate_table[0];
s->enable = 0;
wm8750_clk_update(s, 1);
@@ -299,7 +303,7 @@ static void wm8750_reset(I2CSlave *i2c)
static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
{
- WM8750State *s = (WM8750State *) i2c;
+ WM8750State *s = WM8750(i2c);
switch (event) {
case I2C_START_SEND:
@@ -356,7 +360,7 @@ static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
static int wm8750_tx(I2CSlave *i2c, uint8_t data)
{
- WM8750State *s = (WM8750State *) i2c;
+ WM8750State *s = WM8750(i2c);
uint8_t cmd;
uint16_t value;
@@ -542,7 +546,7 @@ static int wm8750_tx(I2CSlave *i2c, uint8_t data)
break;
case WM8750_RESET: /* Reset */
- wm8750_reset(&s->i2c);
+ wm8750_reset(I2C_SLAVE(&s->parent));
break;
#ifdef VERBOSE
@@ -561,21 +565,21 @@ static int wm8750_rx(I2CSlave *i2c)
static void wm8750_pre_save(void *opaque)
{
- WM8750State *s = opaque;
+ WM8750State *s = WM8750(opaque);
s->rate_vmstate = s->rate - wm_rate_table;
}
static int wm8750_post_load(void *opaque, int version_id)
{
- WM8750State *s = opaque;
+ WM8750State *s = WM8750(opaque);
s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
return 0;
}
static const VMStateDescription vmstate_wm8750 = {
- .name = CODEC,
+ .name = TYPE_WM8750,
.version_id = 0,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
@@ -604,42 +608,31 @@ static const VMStateDescription vmstate_wm8750 = {
VMSTATE_UINT8(format, WM8750State),
VMSTATE_UINT8(power, WM8750State),
VMSTATE_UINT8(rate_vmstate, WM8750State),
- VMSTATE_I2C_SLAVE(i2c, WM8750State),
VMSTATE_END_OF_LIST()
}
};
static int wm8750_init(I2CSlave *i2c)
{
- WM8750State *s = FROM_I2C_SLAVE(WM8750State, i2c);
+ WM8750State *s = WM8750(i2c);
- AUD_register_card(CODEC, &s->card);
- wm8750_reset(&s->i2c);
+ AUD_register_card(TYPE_WM8750, &s->card);
+ wm8750_reset(I2C_SLAVE(&s->parent));
return 0;
}
-#if 0
-static void wm8750_fini(I2CSlave *i2c)
-{
- WM8750State *s = (WM8750State *) i2c;
- wm8750_reset(&s->i2c);
- AUD_remove_card(&s->card);
- g_free(s);
-}
-#endif
-
-void wm8750_data_req_set(DeviceState *dev,
+static void wm8750_data_req_set(DeviceState *dev,
void (*data_req)(void *, int, int), void *opaque)
{
- WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev));
+ WM8750State *s = WM8750(dev);
s->data_req = data_req;
s->opaque = opaque;
}
-void wm8750_dac_dat(void *opaque, uint32_t sample)
+static void wm8750_dac_dat(void *opaque, uint32_t sample)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
*(uint32_t *) &s->data_out[s->idx_out] = sample;
s->req_out -= 4;
@@ -648,9 +641,9 @@ void wm8750_dac_dat(void *opaque, uint32_t sample)
wm8750_out_flush(s);
}
-void *wm8750_dac_buffer(void *opaque, int samples)
+static void *wm8750_dac_buffer(void *opaque, int samples)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
/* XXX: Should check if there are <i>samples</i> free samples available */
void *ret = s->data_out + s->idx_out;
@@ -659,16 +652,16 @@ void *wm8750_dac_buffer(void *opaque, int samples)
return ret;
}
-void wm8750_dac_commit(void *opaque)
+static void wm8750_dac_commit(void *opaque)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
wm8750_out_flush(s);
}
-uint32_t wm8750_adc_dat(void *opaque)
+static uint32_t wm8750_adc_dat(void *opaque)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
uint32_t *data;
if (s->idx_in >= sizeof(s->data_in))
@@ -680,9 +673,9 @@ uint32_t wm8750_adc_dat(void *opaque)
return *data;
}
-void wm8750_set_bclk_in(void *opaque, int new_hz)
+static void wm8750_set_bclk_in(void *opaque, int new_hz)
{
- WM8750State *s = (WM8750State *) opaque;
+ WM8750State *s = WM8750(opaque);
s->ext_adc_hz = new_hz;
s->ext_dac_hz = new_hz;
@@ -693,6 +686,14 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+ AudioCodecClass *ac = AUDIO_CODEC_CLASS(klass);
+
+ ac->data_req_set = wm8750_data_req_set;
+ ac->dac_dat = wm8750_dac_dat;
+ ac->adc_dat = wm8750_adc_dat;
+ ac->dac_buffer = wm8750_dac_buffer;
+ ac->dac_commit = wm8750_dac_commit;
+ ac->set_bclk_in = wm8750_set_bclk_in;
sc->init = wm8750_init;
sc->event = wm8750_event;
@@ -702,8 +703,8 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo wm8750_info = {
- .name = "wm8750",
- .parent = TYPE_I2C_SLAVE,
+ .name = TYPE_WM8750,
+ .parent = TYPE_AUDIO_CODEC,
.instance_size = sizeof(WM8750State),
.class_init = wm8750_class_init,
};