From: Kuo-Jung Su <dant...@faraday-tech.com> Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- hw/Makefile.objs | 1 + hw/arm/spitz.c | 9 +++-- hw/arm/z2.c | 9 +++-- hw/audio.c | 81 +++++++++++++++++++++++++++++++++++++++ hw/audio.h | 56 +++++++++++++++++++++++++++ hw/i2c.h | 9 ----- hw/marvell_88w8618_audio.c | 23 +++++++---- hw/wm8750.c | 91 ++++++++++++++++++++++---------------------- 8 files changed, 211 insertions(+), 68 deletions(-) create mode 100644 hw/audio.c create mode 100644 hw/audio.h
diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 11812c6..808e2d0 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -168,6 +168,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.o common-obj-$(CONFIG_WM8750) += wm8750.o common-obj-$(CONFIG_TWL92230) += twl92230.o common-obj-$(CONFIG_TSC2005) += tsc2005.o diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index f5832be..a07dfe3 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -24,6 +24,7 @@ #include "ui/console.h" #include "block/block.h" #include "audio/audio.h" +#include "hw/audio.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) diff --git a/hw/arm/z2.c b/hw/arm/z2.c index cbb6d80..1fecdc5 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -23,6 +23,7 @@ #include "sysemu/blockdev.h" #include "ui/console.h" #include "audio/audio.h" +#include "hw/audio.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]); diff --git a/hw/audio.c b/hw/audio.c new file mode 100644 index 0000000..35f99b9 --- /dev/null +++ b/hw/audio.c @@ -0,0 +1,81 @@ +/* + * Audio Codec Class + * + * Copyright (c) 2013 Faraday Technology + * Written by Dante Su <dant...@faraday-tech.com> + * + * This file is licensed under GNU GPL v2+. + */ + +#include "hw/qdev.h" +#include "hw/i2c.h" +#include "hw/audio.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) diff --git a/hw/audio.h b/hw/audio.h new file mode 100644 index 0000000..25a2743 --- /dev/null +++ b/hw/audio.h @@ -0,0 +1,56 @@ +/* + * Audio Codec Class + * + * Copyright (c) 2013 Faraday Technology + * Written by Dante Su <dant...@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 diff --git a/hw/i2c.h b/hw/i2c.h index 461392f..39566b5 100644 --- a/hw/i2c.h +++ b/hw/i2c.h @@ -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); diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c index e042046..f6b9d4e 100644 --- a/hw/marvell_88w8618_audio.c +++ b/hw/marvell_88w8618_audio.c @@ -13,6 +13,7 @@ #include "hw/hw.h" #include "hw/i2c.h" #include "hw/sysbus.h" +#include "hw/audio.h" #include "audio/audio.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); diff --git a/hw/wm8750.c b/hw/wm8750.c index 0904cf4..1fffe55 100644 --- a/hw/wm8750.c +++ b/hw/wm8750.c @@ -9,12 +9,13 @@ #include "hw/hw.h" #include "hw/i2c.h" +#include "hw/audio.h" #include "audio/audio.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, }; -- 1.7.9.5