149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * WM8750 audio CODEC.
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * Copyright (c) 2006 Openedhand Ltd.
549ab747fSPaolo Bonzini * Written by Andrzej Zaborowski <balrog@zabor.org>
649ab747fSPaolo Bonzini *
749ab747fSPaolo Bonzini * This file is licensed under GNU GPL.
849ab747fSPaolo Bonzini */
949ab747fSPaolo Bonzini
106086a565SPeter Maydell #include "qemu/osdep.h"
1149ab747fSPaolo Bonzini #include "hw/i2c/i2c.h"
12d6454270SMarkus Armbruster #include "migration/vmstate.h"
130b8fa32fSMarkus Armbruster #include "qemu/module.h"
147ab14c5aSPhilippe Mathieu-Daudé #include "hw/audio/wm8750.h"
1549ab747fSPaolo Bonzini #include "audio/audio.h"
16db1015e9SEduardo Habkost #include "qom/object.h"
1749ab747fSPaolo Bonzini
1849ab747fSPaolo Bonzini #define IN_PORT_N 3
1949ab747fSPaolo Bonzini #define OUT_PORT_N 3
2049ab747fSPaolo Bonzini
2149ab747fSPaolo Bonzini #define CODEC "wm8750"
2249ab747fSPaolo Bonzini
2349ab747fSPaolo Bonzini typedef struct {
2449ab747fSPaolo Bonzini int adc;
2549ab747fSPaolo Bonzini int adc_hz;
2649ab747fSPaolo Bonzini int dac;
2749ab747fSPaolo Bonzini int dac_hz;
2849ab747fSPaolo Bonzini } WMRate;
2949ab747fSPaolo Bonzini
308063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(WM8750State, WM8750)
31bc229b0fSAndreas Färber
32db1015e9SEduardo Habkost struct WM8750State {
33bc229b0fSAndreas Färber I2CSlave parent_obj;
34bc229b0fSAndreas Färber
3549ab747fSPaolo Bonzini uint8_t i2c_data[2];
3649ab747fSPaolo Bonzini int i2c_len;
3749ab747fSPaolo Bonzini QEMUSoundCard card;
3849ab747fSPaolo Bonzini SWVoiceIn *adc_voice[IN_PORT_N];
3949ab747fSPaolo Bonzini SWVoiceOut *dac_voice[OUT_PORT_N];
4049ab747fSPaolo Bonzini int enable;
4149ab747fSPaolo Bonzini void (*data_req)(void *, int, int);
4249ab747fSPaolo Bonzini void *opaque;
4349ab747fSPaolo Bonzini uint8_t data_in[4096];
4449ab747fSPaolo Bonzini uint8_t data_out[4096];
4549ab747fSPaolo Bonzini int idx_in, req_in;
4649ab747fSPaolo Bonzini int idx_out, req_out;
4749ab747fSPaolo Bonzini
4849ab747fSPaolo Bonzini SWVoiceOut **out[2];
4949ab747fSPaolo Bonzini uint8_t outvol[7], outmute[2];
5049ab747fSPaolo Bonzini SWVoiceIn **in[2];
5149ab747fSPaolo Bonzini uint8_t invol[4], inmute[2];
5249ab747fSPaolo Bonzini
5349ab747fSPaolo Bonzini uint8_t diff[2], pol, ds, monomix[2], alc, mute;
5449ab747fSPaolo Bonzini uint8_t path[4], mpath[2], power, format;
5549ab747fSPaolo Bonzini const WMRate *rate;
5649ab747fSPaolo Bonzini uint8_t rate_vmstate;
5749ab747fSPaolo Bonzini int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
58db1015e9SEduardo Habkost };
5949ab747fSPaolo Bonzini
6049ab747fSPaolo Bonzini /* pow(10.0, -i / 20.0) * 255, i = 0..42 */
6149ab747fSPaolo Bonzini static const uint8_t wm8750_vol_db_table[] = {
6249ab747fSPaolo Bonzini 255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45,
6349ab747fSPaolo Bonzini 40, 36, 32, 29, 26, 23, 20, 18, 16, 14, 13, 11, 10, 9, 8, 7, 6, 6, 5, 5,
6449ab747fSPaolo Bonzini 4, 4, 3, 3, 3, 2, 2
6549ab747fSPaolo Bonzini };
6649ab747fSPaolo Bonzini
6749ab747fSPaolo Bonzini #define WM8750_OUTVOL_TRANSFORM(x) wm8750_vol_db_table[(0x7f - x) / 3]
6849ab747fSPaolo Bonzini #define WM8750_INVOL_TRANSFORM(x) (x << 2)
6949ab747fSPaolo Bonzini
wm8750_in_load(WM8750State * s)7049ab747fSPaolo Bonzini static inline void wm8750_in_load(WM8750State *s)
7149ab747fSPaolo Bonzini {
7249ab747fSPaolo Bonzini if (s->idx_in + s->req_in <= sizeof(s->data_in))
7349ab747fSPaolo Bonzini return;
7458935915SKővágó, Zoltán s->idx_in = MAX(0, (int) sizeof(s->data_in) - s->req_in);
7549ab747fSPaolo Bonzini AUD_read(*s->in[0], s->data_in + s->idx_in,
7649ab747fSPaolo Bonzini sizeof(s->data_in) - s->idx_in);
7749ab747fSPaolo Bonzini }
7849ab747fSPaolo Bonzini
wm8750_out_flush(WM8750State * s)7949ab747fSPaolo Bonzini static inline void wm8750_out_flush(WM8750State *s)
8049ab747fSPaolo Bonzini {
8149ab747fSPaolo Bonzini int sent = 0;
8249ab747fSPaolo Bonzini while (sent < s->idx_out)
8349ab747fSPaolo Bonzini sent += AUD_write(*s->out[0], s->data_out + sent, s->idx_out - sent)
8449ab747fSPaolo Bonzini ?: s->idx_out;
8549ab747fSPaolo Bonzini s->idx_out = 0;
8649ab747fSPaolo Bonzini }
8749ab747fSPaolo Bonzini
wm8750_audio_in_cb(void * opaque,int avail_b)8849ab747fSPaolo Bonzini static void wm8750_audio_in_cb(void *opaque, int avail_b)
8949ab747fSPaolo Bonzini {
9049ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
9149ab747fSPaolo Bonzini s->req_in = avail_b;
9249ab747fSPaolo Bonzini s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2);
9349ab747fSPaolo Bonzini }
9449ab747fSPaolo Bonzini
wm8750_audio_out_cb(void * opaque,int free_b)9549ab747fSPaolo Bonzini static void wm8750_audio_out_cb(void *opaque, int free_b)
9649ab747fSPaolo Bonzini {
9749ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
9849ab747fSPaolo Bonzini
9949ab747fSPaolo Bonzini if (s->idx_out >= free_b) {
10049ab747fSPaolo Bonzini s->idx_out = free_b;
10149ab747fSPaolo Bonzini s->req_out = 0;
10249ab747fSPaolo Bonzini wm8750_out_flush(s);
10349ab747fSPaolo Bonzini } else
10449ab747fSPaolo Bonzini s->req_out = free_b - s->idx_out;
10549ab747fSPaolo Bonzini
10649ab747fSPaolo Bonzini s->data_req(s->opaque, s->req_out >> 2, s->req_in >> 2);
10749ab747fSPaolo Bonzini }
10849ab747fSPaolo Bonzini
10949ab747fSPaolo Bonzini static const WMRate wm_rate_table[] = {
11049ab747fSPaolo Bonzini { 256, 48000, 256, 48000 }, /* SR: 00000 */
11149ab747fSPaolo Bonzini { 384, 48000, 384, 48000 }, /* SR: 00001 */
11249ab747fSPaolo Bonzini { 256, 48000, 1536, 8000 }, /* SR: 00010 */
11349ab747fSPaolo Bonzini { 384, 48000, 2304, 8000 }, /* SR: 00011 */
11449ab747fSPaolo Bonzini { 1536, 8000, 256, 48000 }, /* SR: 00100 */
11549ab747fSPaolo Bonzini { 2304, 8000, 384, 48000 }, /* SR: 00101 */
11649ab747fSPaolo Bonzini { 1536, 8000, 1536, 8000 }, /* SR: 00110 */
11749ab747fSPaolo Bonzini { 2304, 8000, 2304, 8000 }, /* SR: 00111 */
11849ab747fSPaolo Bonzini { 1024, 12000, 1024, 12000 }, /* SR: 01000 */
11949ab747fSPaolo Bonzini { 1526, 12000, 1536, 12000 }, /* SR: 01001 */
12049ab747fSPaolo Bonzini { 768, 16000, 768, 16000 }, /* SR: 01010 */
12149ab747fSPaolo Bonzini { 1152, 16000, 1152, 16000 }, /* SR: 01011 */
12249ab747fSPaolo Bonzini { 384, 32000, 384, 32000 }, /* SR: 01100 */
12349ab747fSPaolo Bonzini { 576, 32000, 576, 32000 }, /* SR: 01101 */
12449ab747fSPaolo Bonzini { 128, 96000, 128, 96000 }, /* SR: 01110 */
12549ab747fSPaolo Bonzini { 192, 96000, 192, 96000 }, /* SR: 01111 */
12649ab747fSPaolo Bonzini { 256, 44100, 256, 44100 }, /* SR: 10000 */
12749ab747fSPaolo Bonzini { 384, 44100, 384, 44100 }, /* SR: 10001 */
12849ab747fSPaolo Bonzini { 256, 44100, 1408, 8018 }, /* SR: 10010 */
12949ab747fSPaolo Bonzini { 384, 44100, 2112, 8018 }, /* SR: 10011 */
13049ab747fSPaolo Bonzini { 1408, 8018, 256, 44100 }, /* SR: 10100 */
13149ab747fSPaolo Bonzini { 2112, 8018, 384, 44100 }, /* SR: 10101 */
13249ab747fSPaolo Bonzini { 1408, 8018, 1408, 8018 }, /* SR: 10110 */
13349ab747fSPaolo Bonzini { 2112, 8018, 2112, 8018 }, /* SR: 10111 */
13449ab747fSPaolo Bonzini { 1024, 11025, 1024, 11025 }, /* SR: 11000 */
13549ab747fSPaolo Bonzini { 1536, 11025, 1536, 11025 }, /* SR: 11001 */
13649ab747fSPaolo Bonzini { 512, 22050, 512, 22050 }, /* SR: 11010 */
13749ab747fSPaolo Bonzini { 768, 22050, 768, 22050 }, /* SR: 11011 */
13849ab747fSPaolo Bonzini { 512, 24000, 512, 24000 }, /* SR: 11100 */
13949ab747fSPaolo Bonzini { 768, 24000, 768, 24000 }, /* SR: 11101 */
14049ab747fSPaolo Bonzini { 128, 88200, 128, 88200 }, /* SR: 11110 */
14149ab747fSPaolo Bonzini { 192, 88200, 192, 88200 }, /* SR: 11111 */
14249ab747fSPaolo Bonzini };
14349ab747fSPaolo Bonzini
wm8750_vol_update(WM8750State * s)14449ab747fSPaolo Bonzini static void wm8750_vol_update(WM8750State *s)
14549ab747fSPaolo Bonzini {
14649ab747fSPaolo Bonzini /* FIXME: multiply all volumes by s->invol[2], s->invol[3] */
14749ab747fSPaolo Bonzini
14849ab747fSPaolo Bonzini AUD_set_volume_in(s->adc_voice[0], s->mute,
14949ab747fSPaolo Bonzini s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
15049ab747fSPaolo Bonzini s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
15149ab747fSPaolo Bonzini AUD_set_volume_in(s->adc_voice[1], s->mute,
15249ab747fSPaolo Bonzini s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
15349ab747fSPaolo Bonzini s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
15449ab747fSPaolo Bonzini AUD_set_volume_in(s->adc_voice[2], s->mute,
15549ab747fSPaolo Bonzini s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
15649ab747fSPaolo Bonzini s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
15749ab747fSPaolo Bonzini
15849ab747fSPaolo Bonzini /* FIXME: multiply all volumes by s->outvol[0], s->outvol[1] */
15949ab747fSPaolo Bonzini
16049ab747fSPaolo Bonzini /* Speaker: LOUT2VOL ROUT2VOL */
16149ab747fSPaolo Bonzini AUD_set_volume_out(s->dac_voice[0], s->mute,
16249ab747fSPaolo Bonzini s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[4]),
16349ab747fSPaolo Bonzini s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[5]));
16449ab747fSPaolo Bonzini
16549ab747fSPaolo Bonzini /* Headphone: LOUT1VOL ROUT1VOL */
16649ab747fSPaolo Bonzini AUD_set_volume_out(s->dac_voice[1], s->mute,
16749ab747fSPaolo Bonzini s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[2]),
16849ab747fSPaolo Bonzini s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[3]));
16949ab747fSPaolo Bonzini
17049ab747fSPaolo Bonzini /* MONOOUT: MONOVOL MONOVOL */
17149ab747fSPaolo Bonzini AUD_set_volume_out(s->dac_voice[2], s->mute,
17249ab747fSPaolo Bonzini s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]),
17349ab747fSPaolo Bonzini s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]));
17449ab747fSPaolo Bonzini }
17549ab747fSPaolo Bonzini
wm8750_set_format(WM8750State * s)17649ab747fSPaolo Bonzini static void wm8750_set_format(WM8750State *s)
17749ab747fSPaolo Bonzini {
17849ab747fSPaolo Bonzini int i;
17949ab747fSPaolo Bonzini struct audsettings in_fmt;
18049ab747fSPaolo Bonzini struct audsettings out_fmt;
18149ab747fSPaolo Bonzini
18249ab747fSPaolo Bonzini wm8750_out_flush(s);
18349ab747fSPaolo Bonzini
18449ab747fSPaolo Bonzini if (s->in[0] && *s->in[0])
18549ab747fSPaolo Bonzini AUD_set_active_in(*s->in[0], 0);
18649ab747fSPaolo Bonzini if (s->out[0] && *s->out[0])
18749ab747fSPaolo Bonzini AUD_set_active_out(*s->out[0], 0);
18849ab747fSPaolo Bonzini
18949ab747fSPaolo Bonzini for (i = 0; i < IN_PORT_N; i ++)
19049ab747fSPaolo Bonzini if (s->adc_voice[i]) {
19149ab747fSPaolo Bonzini AUD_close_in(&s->card, s->adc_voice[i]);
19249ab747fSPaolo Bonzini s->adc_voice[i] = NULL;
19349ab747fSPaolo Bonzini }
19449ab747fSPaolo Bonzini for (i = 0; i < OUT_PORT_N; i ++)
19549ab747fSPaolo Bonzini if (s->dac_voice[i]) {
19649ab747fSPaolo Bonzini AUD_close_out(&s->card, s->dac_voice[i]);
19749ab747fSPaolo Bonzini s->dac_voice[i] = NULL;
19849ab747fSPaolo Bonzini }
19949ab747fSPaolo Bonzini
20049ab747fSPaolo Bonzini if (!s->enable)
20149ab747fSPaolo Bonzini return;
20249ab747fSPaolo Bonzini
20349ab747fSPaolo Bonzini /* Setup input */
20449ab747fSPaolo Bonzini in_fmt.endianness = 0;
20549ab747fSPaolo Bonzini in_fmt.nchannels = 2;
20649ab747fSPaolo Bonzini in_fmt.freq = s->adc_hz;
20785bc5852SKővágó, Zoltán in_fmt.fmt = AUDIO_FORMAT_S16;
20849ab747fSPaolo Bonzini
20949ab747fSPaolo Bonzini s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
21049ab747fSPaolo Bonzini CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
21149ab747fSPaolo Bonzini s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1],
21249ab747fSPaolo Bonzini CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt);
21349ab747fSPaolo Bonzini s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2],
21449ab747fSPaolo Bonzini CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
21549ab747fSPaolo Bonzini
21649ab747fSPaolo Bonzini /* Setup output */
21749ab747fSPaolo Bonzini out_fmt.endianness = 0;
21849ab747fSPaolo Bonzini out_fmt.nchannels = 2;
21949ab747fSPaolo Bonzini out_fmt.freq = s->dac_hz;
22085bc5852SKővágó, Zoltán out_fmt.fmt = AUDIO_FORMAT_S16;
22149ab747fSPaolo Bonzini
22249ab747fSPaolo Bonzini s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
22349ab747fSPaolo Bonzini CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
22449ab747fSPaolo Bonzini s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1],
22549ab747fSPaolo Bonzini CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt);
22649ab747fSPaolo Bonzini /* MONOMIX is also in stereo for simplicity */
22749ab747fSPaolo Bonzini s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2],
22849ab747fSPaolo Bonzini CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt);
22949ab747fSPaolo Bonzini /* no sense emulating OUT3 which is a mix of other outputs */
23049ab747fSPaolo Bonzini
23149ab747fSPaolo Bonzini wm8750_vol_update(s);
23249ab747fSPaolo Bonzini
23349ab747fSPaolo Bonzini /* We should connect the left and right channels to their
23449ab747fSPaolo Bonzini * respective inputs/outputs but we have completely no need
23549ab747fSPaolo Bonzini * for mixing or combining paths to different ports, so we
23649ab747fSPaolo Bonzini * connect both channels to where the left channel is routed. */
23749ab747fSPaolo Bonzini if (s->in[0] && *s->in[0])
23849ab747fSPaolo Bonzini AUD_set_active_in(*s->in[0], 1);
23949ab747fSPaolo Bonzini if (s->out[0] && *s->out[0])
24049ab747fSPaolo Bonzini AUD_set_active_out(*s->out[0], 1);
24149ab747fSPaolo Bonzini }
24249ab747fSPaolo Bonzini
wm8750_clk_update(WM8750State * s,int ext)24349ab747fSPaolo Bonzini static void wm8750_clk_update(WM8750State *s, int ext)
24449ab747fSPaolo Bonzini {
24549ab747fSPaolo Bonzini if (s->master || !s->ext_dac_hz)
24649ab747fSPaolo Bonzini s->dac_hz = s->rate->dac_hz;
24749ab747fSPaolo Bonzini else
24849ab747fSPaolo Bonzini s->dac_hz = s->ext_dac_hz;
24949ab747fSPaolo Bonzini
25049ab747fSPaolo Bonzini if (s->master || !s->ext_adc_hz)
25149ab747fSPaolo Bonzini s->adc_hz = s->rate->adc_hz;
25249ab747fSPaolo Bonzini else
25349ab747fSPaolo Bonzini s->adc_hz = s->ext_adc_hz;
25449ab747fSPaolo Bonzini
25549ab747fSPaolo Bonzini if (s->master || (!s->ext_dac_hz && !s->ext_adc_hz)) {
25649ab747fSPaolo Bonzini if (!ext)
25749ab747fSPaolo Bonzini wm8750_set_format(s);
25849ab747fSPaolo Bonzini } else {
25949ab747fSPaolo Bonzini if (ext)
26049ab747fSPaolo Bonzini wm8750_set_format(s);
26149ab747fSPaolo Bonzini }
26249ab747fSPaolo Bonzini }
26349ab747fSPaolo Bonzini
wm8750_reset(I2CSlave * i2c)26449ab747fSPaolo Bonzini static void wm8750_reset(I2CSlave *i2c)
26549ab747fSPaolo Bonzini {
266bc229b0fSAndreas Färber WM8750State *s = WM8750(i2c);
267bc229b0fSAndreas Färber
26849ab747fSPaolo Bonzini s->rate = &wm_rate_table[0];
26949ab747fSPaolo Bonzini s->enable = 0;
27049ab747fSPaolo Bonzini wm8750_clk_update(s, 1);
27149ab747fSPaolo Bonzini s->diff[0] = 0;
27249ab747fSPaolo Bonzini s->diff[1] = 0;
27349ab747fSPaolo Bonzini s->ds = 0;
27449ab747fSPaolo Bonzini s->alc = 0;
27549ab747fSPaolo Bonzini s->in[0] = &s->adc_voice[0];
27649ab747fSPaolo Bonzini s->invol[0] = 0x17;
27749ab747fSPaolo Bonzini s->invol[1] = 0x17;
27849ab747fSPaolo Bonzini s->invol[2] = 0xc3;
27949ab747fSPaolo Bonzini s->invol[3] = 0xc3;
28049ab747fSPaolo Bonzini s->out[0] = &s->dac_voice[0];
28149ab747fSPaolo Bonzini s->outvol[0] = 0xff;
28249ab747fSPaolo Bonzini s->outvol[1] = 0xff;
28349ab747fSPaolo Bonzini s->outvol[2] = 0x79;
28449ab747fSPaolo Bonzini s->outvol[3] = 0x79;
28549ab747fSPaolo Bonzini s->outvol[4] = 0x79;
28649ab747fSPaolo Bonzini s->outvol[5] = 0x79;
28749ab747fSPaolo Bonzini s->outvol[6] = 0x79;
28849ab747fSPaolo Bonzini s->inmute[0] = 0;
28949ab747fSPaolo Bonzini s->inmute[1] = 0;
29049ab747fSPaolo Bonzini s->outmute[0] = 0;
29149ab747fSPaolo Bonzini s->outmute[1] = 0;
29249ab747fSPaolo Bonzini s->mute = 1;
29349ab747fSPaolo Bonzini s->path[0] = 0;
29449ab747fSPaolo Bonzini s->path[1] = 0;
29549ab747fSPaolo Bonzini s->path[2] = 0;
29649ab747fSPaolo Bonzini s->path[3] = 0;
29749ab747fSPaolo Bonzini s->mpath[0] = 0;
29849ab747fSPaolo Bonzini s->mpath[1] = 0;
29949ab747fSPaolo Bonzini s->format = 0x0a;
30049ab747fSPaolo Bonzini s->idx_in = sizeof(s->data_in);
30149ab747fSPaolo Bonzini s->req_in = 0;
30249ab747fSPaolo Bonzini s->idx_out = 0;
30349ab747fSPaolo Bonzini s->req_out = 0;
30449ab747fSPaolo Bonzini wm8750_vol_update(s);
30549ab747fSPaolo Bonzini s->i2c_len = 0;
30649ab747fSPaolo Bonzini }
30749ab747fSPaolo Bonzini
wm8750_event(I2CSlave * i2c,enum i2c_event event)308d307c28cSCorey Minyard static int wm8750_event(I2CSlave *i2c, enum i2c_event event)
30949ab747fSPaolo Bonzini {
310bc229b0fSAndreas Färber WM8750State *s = WM8750(i2c);
31149ab747fSPaolo Bonzini
31249ab747fSPaolo Bonzini switch (event) {
31349ab747fSPaolo Bonzini case I2C_START_SEND:
31449ab747fSPaolo Bonzini s->i2c_len = 0;
31549ab747fSPaolo Bonzini break;
31649ab747fSPaolo Bonzini case I2C_FINISH:
31749ab747fSPaolo Bonzini #ifdef VERBOSE
31849ab747fSPaolo Bonzini if (s->i2c_len < 2)
31949ab747fSPaolo Bonzini printf("%s: message too short (%i bytes)\n",
320a89f364aSAlistair Francis __func__, s->i2c_len);
32149ab747fSPaolo Bonzini #endif
32249ab747fSPaolo Bonzini break;
32349ab747fSPaolo Bonzini default:
32449ab747fSPaolo Bonzini break;
32549ab747fSPaolo Bonzini }
326d307c28cSCorey Minyard
327d307c28cSCorey Minyard return 0;
32849ab747fSPaolo Bonzini }
32949ab747fSPaolo Bonzini
33049ab747fSPaolo Bonzini #define WM8750_LINVOL 0x00
33149ab747fSPaolo Bonzini #define WM8750_RINVOL 0x01
33249ab747fSPaolo Bonzini #define WM8750_LOUT1V 0x02
33349ab747fSPaolo Bonzini #define WM8750_ROUT1V 0x03
33449ab747fSPaolo Bonzini #define WM8750_ADCDAC 0x05
33549ab747fSPaolo Bonzini #define WM8750_IFACE 0x07
33649ab747fSPaolo Bonzini #define WM8750_SRATE 0x08
33749ab747fSPaolo Bonzini #define WM8750_LDAC 0x0a
33849ab747fSPaolo Bonzini #define WM8750_RDAC 0x0b
33949ab747fSPaolo Bonzini #define WM8750_BASS 0x0c
34049ab747fSPaolo Bonzini #define WM8750_TREBLE 0x0d
34149ab747fSPaolo Bonzini #define WM8750_RESET 0x0f
34249ab747fSPaolo Bonzini #define WM8750_3D 0x10
34349ab747fSPaolo Bonzini #define WM8750_ALC1 0x11
34449ab747fSPaolo Bonzini #define WM8750_ALC2 0x12
34549ab747fSPaolo Bonzini #define WM8750_ALC3 0x13
34649ab747fSPaolo Bonzini #define WM8750_NGATE 0x14
34749ab747fSPaolo Bonzini #define WM8750_LADC 0x15
34849ab747fSPaolo Bonzini #define WM8750_RADC 0x16
34949ab747fSPaolo Bonzini #define WM8750_ADCTL1 0x17
35049ab747fSPaolo Bonzini #define WM8750_ADCTL2 0x18
35149ab747fSPaolo Bonzini #define WM8750_PWR1 0x19
35249ab747fSPaolo Bonzini #define WM8750_PWR2 0x1a
35349ab747fSPaolo Bonzini #define WM8750_ADCTL3 0x1b
35449ab747fSPaolo Bonzini #define WM8750_ADCIN 0x1f
35549ab747fSPaolo Bonzini #define WM8750_LADCIN 0x20
35649ab747fSPaolo Bonzini #define WM8750_RADCIN 0x21
35749ab747fSPaolo Bonzini #define WM8750_LOUTM1 0x22
35849ab747fSPaolo Bonzini #define WM8750_LOUTM2 0x23
35949ab747fSPaolo Bonzini #define WM8750_ROUTM1 0x24
36049ab747fSPaolo Bonzini #define WM8750_ROUTM2 0x25
36149ab747fSPaolo Bonzini #define WM8750_MOUTM1 0x26
36249ab747fSPaolo Bonzini #define WM8750_MOUTM2 0x27
36349ab747fSPaolo Bonzini #define WM8750_LOUT2V 0x28
36449ab747fSPaolo Bonzini #define WM8750_ROUT2V 0x29
36549ab747fSPaolo Bonzini #define WM8750_MOUTV 0x2a
36649ab747fSPaolo Bonzini
wm8750_tx(I2CSlave * i2c,uint8_t data)36749ab747fSPaolo Bonzini static int wm8750_tx(I2CSlave *i2c, uint8_t data)
36849ab747fSPaolo Bonzini {
369bc229b0fSAndreas Färber WM8750State *s = WM8750(i2c);
37049ab747fSPaolo Bonzini uint8_t cmd;
37149ab747fSPaolo Bonzini uint16_t value;
37249ab747fSPaolo Bonzini
37349ab747fSPaolo Bonzini if (s->i2c_len >= 2) {
37449ab747fSPaolo Bonzini #ifdef VERBOSE
37549ab747fSPaolo Bonzini printf("%s: long message (%i bytes)\n", __func__, s->i2c_len);
37649ab747fSPaolo Bonzini #endif
37749ab747fSPaolo Bonzini return 1;
37849ab747fSPaolo Bonzini }
37949ab747fSPaolo Bonzini s->i2c_data[s->i2c_len ++] = data;
38049ab747fSPaolo Bonzini if (s->i2c_len != 2)
38149ab747fSPaolo Bonzini return 0;
38249ab747fSPaolo Bonzini
38349ab747fSPaolo Bonzini cmd = s->i2c_data[0] >> 1;
38449ab747fSPaolo Bonzini value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff;
38549ab747fSPaolo Bonzini
38649ab747fSPaolo Bonzini switch (cmd) {
38749ab747fSPaolo Bonzini case WM8750_LADCIN: /* ADC Signal Path Control (Left) */
38849ab747fSPaolo Bonzini s->diff[0] = (((value >> 6) & 3) == 3); /* LINSEL */
38949ab747fSPaolo Bonzini if (s->diff[0])
39049ab747fSPaolo Bonzini s->in[0] = &s->adc_voice[0 + s->ds * 1];
39149ab747fSPaolo Bonzini else
39249ab747fSPaolo Bonzini s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
39349ab747fSPaolo Bonzini break;
39449ab747fSPaolo Bonzini
39549ab747fSPaolo Bonzini case WM8750_RADCIN: /* ADC Signal Path Control (Right) */
39649ab747fSPaolo Bonzini s->diff[1] = (((value >> 6) & 3) == 3); /* RINSEL */
39749ab747fSPaolo Bonzini if (s->diff[1])
39849ab747fSPaolo Bonzini s->in[1] = &s->adc_voice[0 + s->ds * 1];
39949ab747fSPaolo Bonzini else
40049ab747fSPaolo Bonzini s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
40149ab747fSPaolo Bonzini break;
40249ab747fSPaolo Bonzini
40349ab747fSPaolo Bonzini case WM8750_ADCIN: /* ADC Input Mode */
40449ab747fSPaolo Bonzini s->ds = (value >> 8) & 1; /* DS */
40549ab747fSPaolo Bonzini if (s->diff[0])
40649ab747fSPaolo Bonzini s->in[0] = &s->adc_voice[0 + s->ds * 1];
40749ab747fSPaolo Bonzini if (s->diff[1])
40849ab747fSPaolo Bonzini s->in[1] = &s->adc_voice[0 + s->ds * 1];
40949ab747fSPaolo Bonzini s->monomix[0] = (value >> 6) & 3; /* MONOMIX */
41049ab747fSPaolo Bonzini break;
41149ab747fSPaolo Bonzini
41249ab747fSPaolo Bonzini case WM8750_ADCTL1: /* Additional Control (1) */
41349ab747fSPaolo Bonzini s->monomix[1] = (value >> 1) & 1; /* DMONOMIX */
41449ab747fSPaolo Bonzini break;
41549ab747fSPaolo Bonzini
41649ab747fSPaolo Bonzini case WM8750_PWR1: /* Power Management (1) */
41749ab747fSPaolo Bonzini s->enable = ((value >> 6) & 7) == 3; /* VMIDSEL, VREF */
41849ab747fSPaolo Bonzini wm8750_set_format(s);
41949ab747fSPaolo Bonzini break;
42049ab747fSPaolo Bonzini
42149ab747fSPaolo Bonzini case WM8750_LINVOL: /* Left Channel PGA */
42249ab747fSPaolo Bonzini s->invol[0] = value & 0x3f; /* LINVOL */
42349ab747fSPaolo Bonzini s->inmute[0] = (value >> 7) & 1; /* LINMUTE */
42449ab747fSPaolo Bonzini wm8750_vol_update(s);
42549ab747fSPaolo Bonzini break;
42649ab747fSPaolo Bonzini
42749ab747fSPaolo Bonzini case WM8750_RINVOL: /* Right Channel PGA */
42849ab747fSPaolo Bonzini s->invol[1] = value & 0x3f; /* RINVOL */
42949ab747fSPaolo Bonzini s->inmute[1] = (value >> 7) & 1; /* RINMUTE */
43049ab747fSPaolo Bonzini wm8750_vol_update(s);
43149ab747fSPaolo Bonzini break;
43249ab747fSPaolo Bonzini
43349ab747fSPaolo Bonzini case WM8750_ADCDAC: /* ADC and DAC Control */
43449ab747fSPaolo Bonzini s->pol = (value >> 5) & 3; /* ADCPOL */
43549ab747fSPaolo Bonzini s->mute = (value >> 3) & 1; /* DACMU */
43649ab747fSPaolo Bonzini wm8750_vol_update(s);
43749ab747fSPaolo Bonzini break;
43849ab747fSPaolo Bonzini
43949ab747fSPaolo Bonzini case WM8750_ADCTL3: /* Additional Control (3) */
44049ab747fSPaolo Bonzini break;
44149ab747fSPaolo Bonzini
44249ab747fSPaolo Bonzini case WM8750_LADC: /* Left ADC Digital Volume */
44349ab747fSPaolo Bonzini s->invol[2] = value & 0xff; /* LADCVOL */
44449ab747fSPaolo Bonzini wm8750_vol_update(s);
44549ab747fSPaolo Bonzini break;
44649ab747fSPaolo Bonzini
44749ab747fSPaolo Bonzini case WM8750_RADC: /* Right ADC Digital Volume */
44849ab747fSPaolo Bonzini s->invol[3] = value & 0xff; /* RADCVOL */
44949ab747fSPaolo Bonzini wm8750_vol_update(s);
45049ab747fSPaolo Bonzini break;
45149ab747fSPaolo Bonzini
45249ab747fSPaolo Bonzini case WM8750_ALC1: /* ALC Control (1) */
45349ab747fSPaolo Bonzini s->alc = (value >> 7) & 3; /* ALCSEL */
45449ab747fSPaolo Bonzini break;
45549ab747fSPaolo Bonzini
45649ab747fSPaolo Bonzini case WM8750_NGATE: /* Noise Gate Control */
45749ab747fSPaolo Bonzini case WM8750_3D: /* 3D enhance */
45849ab747fSPaolo Bonzini break;
45949ab747fSPaolo Bonzini
46049ab747fSPaolo Bonzini case WM8750_LDAC: /* Left Channel Digital Volume */
46149ab747fSPaolo Bonzini s->outvol[0] = value & 0xff; /* LDACVOL */
46249ab747fSPaolo Bonzini wm8750_vol_update(s);
46349ab747fSPaolo Bonzini break;
46449ab747fSPaolo Bonzini
46549ab747fSPaolo Bonzini case WM8750_RDAC: /* Right Channel Digital Volume */
46649ab747fSPaolo Bonzini s->outvol[1] = value & 0xff; /* RDACVOL */
46749ab747fSPaolo Bonzini wm8750_vol_update(s);
46849ab747fSPaolo Bonzini break;
46949ab747fSPaolo Bonzini
47049ab747fSPaolo Bonzini case WM8750_BASS: /* Bass Control */
47149ab747fSPaolo Bonzini break;
47249ab747fSPaolo Bonzini
47349ab747fSPaolo Bonzini case WM8750_LOUTM1: /* Left Mixer Control (1) */
47449ab747fSPaolo Bonzini s->path[0] = (value >> 8) & 1; /* LD2LO */
47549ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
47649ab747fSPaolo Bonzini wm8750_vol_update(s);
47749ab747fSPaolo Bonzini break;
47849ab747fSPaolo Bonzini
47949ab747fSPaolo Bonzini case WM8750_LOUTM2: /* Left Mixer Control (2) */
48049ab747fSPaolo Bonzini s->path[1] = (value >> 8) & 1; /* RD2LO */
48149ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
48249ab747fSPaolo Bonzini wm8750_vol_update(s);
48349ab747fSPaolo Bonzini break;
48449ab747fSPaolo Bonzini
48549ab747fSPaolo Bonzini case WM8750_ROUTM1: /* Right Mixer Control (1) */
48649ab747fSPaolo Bonzini s->path[2] = (value >> 8) & 1; /* LD2RO */
48749ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
48849ab747fSPaolo Bonzini wm8750_vol_update(s);
48949ab747fSPaolo Bonzini break;
49049ab747fSPaolo Bonzini
49149ab747fSPaolo Bonzini case WM8750_ROUTM2: /* Right Mixer Control (2) */
49249ab747fSPaolo Bonzini s->path[3] = (value >> 8) & 1; /* RD2RO */
49349ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
49449ab747fSPaolo Bonzini wm8750_vol_update(s);
49549ab747fSPaolo Bonzini break;
49649ab747fSPaolo Bonzini
49749ab747fSPaolo Bonzini case WM8750_MOUTM1: /* Mono Mixer Control (1) */
49849ab747fSPaolo Bonzini s->mpath[0] = (value >> 8) & 1; /* LD2MO */
49949ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
50049ab747fSPaolo Bonzini wm8750_vol_update(s);
50149ab747fSPaolo Bonzini break;
50249ab747fSPaolo Bonzini
50349ab747fSPaolo Bonzini case WM8750_MOUTM2: /* Mono Mixer Control (2) */
50449ab747fSPaolo Bonzini s->mpath[1] = (value >> 8) & 1; /* RD2MO */
50549ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
50649ab747fSPaolo Bonzini wm8750_vol_update(s);
50749ab747fSPaolo Bonzini break;
50849ab747fSPaolo Bonzini
50949ab747fSPaolo Bonzini case WM8750_LOUT1V: /* LOUT1 Volume */
51049ab747fSPaolo Bonzini s->outvol[2] = value & 0x7f; /* LOUT1VOL */
51149ab747fSPaolo Bonzini wm8750_vol_update(s);
51249ab747fSPaolo Bonzini break;
51349ab747fSPaolo Bonzini
51449ab747fSPaolo Bonzini case WM8750_LOUT2V: /* LOUT2 Volume */
51549ab747fSPaolo Bonzini s->outvol[4] = value & 0x7f; /* LOUT2VOL */
51649ab747fSPaolo Bonzini wm8750_vol_update(s);
51749ab747fSPaolo Bonzini break;
51849ab747fSPaolo Bonzini
51949ab747fSPaolo Bonzini case WM8750_ROUT1V: /* ROUT1 Volume */
52049ab747fSPaolo Bonzini s->outvol[3] = value & 0x7f; /* ROUT1VOL */
52149ab747fSPaolo Bonzini wm8750_vol_update(s);
52249ab747fSPaolo Bonzini break;
52349ab747fSPaolo Bonzini
52449ab747fSPaolo Bonzini case WM8750_ROUT2V: /* ROUT2 Volume */
52549ab747fSPaolo Bonzini s->outvol[5] = value & 0x7f; /* ROUT2VOL */
52649ab747fSPaolo Bonzini wm8750_vol_update(s);
52749ab747fSPaolo Bonzini break;
52849ab747fSPaolo Bonzini
52949ab747fSPaolo Bonzini case WM8750_MOUTV: /* MONOOUT Volume */
53049ab747fSPaolo Bonzini s->outvol[6] = value & 0x7f; /* MONOOUTVOL */
53149ab747fSPaolo Bonzini wm8750_vol_update(s);
53249ab747fSPaolo Bonzini break;
53349ab747fSPaolo Bonzini
53449ab747fSPaolo Bonzini case WM8750_ADCTL2: /* Additional Control (2) */
53549ab747fSPaolo Bonzini break;
53649ab747fSPaolo Bonzini
53749ab747fSPaolo Bonzini case WM8750_PWR2: /* Power Management (2) */
53849ab747fSPaolo Bonzini s->power = value & 0x7e;
53949ab747fSPaolo Bonzini /* TODO: mute/unmute respective paths */
54049ab747fSPaolo Bonzini wm8750_vol_update(s);
54149ab747fSPaolo Bonzini break;
54249ab747fSPaolo Bonzini
54349ab747fSPaolo Bonzini case WM8750_IFACE: /* Digital Audio Interface Format */
54449ab747fSPaolo Bonzini s->format = value;
54549ab747fSPaolo Bonzini s->master = (value >> 6) & 1; /* MS */
54649ab747fSPaolo Bonzini wm8750_clk_update(s, s->master);
54749ab747fSPaolo Bonzini break;
54849ab747fSPaolo Bonzini
54949ab747fSPaolo Bonzini case WM8750_SRATE: /* Clocking and Sample Rate Control */
55049ab747fSPaolo Bonzini s->rate = &wm_rate_table[(value >> 1) & 0x1f];
55149ab747fSPaolo Bonzini wm8750_clk_update(s, 0);
55249ab747fSPaolo Bonzini break;
55349ab747fSPaolo Bonzini
55449ab747fSPaolo Bonzini case WM8750_RESET: /* Reset */
555bc229b0fSAndreas Färber wm8750_reset(I2C_SLAVE(s));
55649ab747fSPaolo Bonzini break;
55749ab747fSPaolo Bonzini
55849ab747fSPaolo Bonzini #ifdef VERBOSE
55949ab747fSPaolo Bonzini default:
560a89f364aSAlistair Francis printf("%s: unknown register %02x\n", __func__, cmd);
56149ab747fSPaolo Bonzini #endif
56249ab747fSPaolo Bonzini }
56349ab747fSPaolo Bonzini
56449ab747fSPaolo Bonzini return 0;
56549ab747fSPaolo Bonzini }
56649ab747fSPaolo Bonzini
wm8750_rx(I2CSlave * i2c)5672ac4c5f4SCorey Minyard static uint8_t wm8750_rx(I2CSlave *i2c)
56849ab747fSPaolo Bonzini {
56949ab747fSPaolo Bonzini return 0x00;
57049ab747fSPaolo Bonzini }
57149ab747fSPaolo Bonzini
wm8750_pre_save(void * opaque)57244b1ff31SDr. David Alan Gilbert static int wm8750_pre_save(void *opaque)
57349ab747fSPaolo Bonzini {
57449ab747fSPaolo Bonzini WM8750State *s = opaque;
57549ab747fSPaolo Bonzini
57649ab747fSPaolo Bonzini s->rate_vmstate = s->rate - wm_rate_table;
57744b1ff31SDr. David Alan Gilbert
57844b1ff31SDr. David Alan Gilbert return 0;
57949ab747fSPaolo Bonzini }
58049ab747fSPaolo Bonzini
wm8750_post_load(void * opaque,int version_id)58149ab747fSPaolo Bonzini static int wm8750_post_load(void *opaque, int version_id)
58249ab747fSPaolo Bonzini {
58349ab747fSPaolo Bonzini WM8750State *s = opaque;
58449ab747fSPaolo Bonzini
58549ab747fSPaolo Bonzini s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
58649ab747fSPaolo Bonzini return 0;
58749ab747fSPaolo Bonzini }
58849ab747fSPaolo Bonzini
58949ab747fSPaolo Bonzini static const VMStateDescription vmstate_wm8750 = {
59049ab747fSPaolo Bonzini .name = CODEC,
59149ab747fSPaolo Bonzini .version_id = 0,
59249ab747fSPaolo Bonzini .minimum_version_id = 0,
59349ab747fSPaolo Bonzini .pre_save = wm8750_pre_save,
59449ab747fSPaolo Bonzini .post_load = wm8750_post_load,
595*856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
59649ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(i2c_data, WM8750State, 2),
59749ab747fSPaolo Bonzini VMSTATE_INT32(i2c_len, WM8750State),
59849ab747fSPaolo Bonzini VMSTATE_INT32(enable, WM8750State),
59949ab747fSPaolo Bonzini VMSTATE_INT32(idx_in, WM8750State),
60049ab747fSPaolo Bonzini VMSTATE_INT32(req_in, WM8750State),
60149ab747fSPaolo Bonzini VMSTATE_INT32(idx_out, WM8750State),
60249ab747fSPaolo Bonzini VMSTATE_INT32(req_out, WM8750State),
60349ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(outvol, WM8750State, 7),
60449ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(outmute, WM8750State, 2),
60549ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(invol, WM8750State, 4),
60649ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(inmute, WM8750State, 2),
60749ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(diff, WM8750State, 2),
60849ab747fSPaolo Bonzini VMSTATE_UINT8(pol, WM8750State),
60949ab747fSPaolo Bonzini VMSTATE_UINT8(ds, WM8750State),
61049ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(monomix, WM8750State, 2),
61149ab747fSPaolo Bonzini VMSTATE_UINT8(alc, WM8750State),
61249ab747fSPaolo Bonzini VMSTATE_UINT8(mute, WM8750State),
61349ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(path, WM8750State, 4),
61449ab747fSPaolo Bonzini VMSTATE_UINT8_ARRAY(mpath, WM8750State, 2),
61549ab747fSPaolo Bonzini VMSTATE_UINT8(format, WM8750State),
61649ab747fSPaolo Bonzini VMSTATE_UINT8(power, WM8750State),
61749ab747fSPaolo Bonzini VMSTATE_UINT8(rate_vmstate, WM8750State),
618bc229b0fSAndreas Färber VMSTATE_I2C_SLAVE(parent_obj, WM8750State),
61949ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
62049ab747fSPaolo Bonzini }
62149ab747fSPaolo Bonzini };
62249ab747fSPaolo Bonzini
wm8750_realize(DeviceState * dev,Error ** errp)623c8c9e103SPhilippe Mathieu-Daudé static void wm8750_realize(DeviceState *dev, Error **errp)
62449ab747fSPaolo Bonzini {
625c8c9e103SPhilippe Mathieu-Daudé WM8750State *s = WM8750(dev);
62649ab747fSPaolo Bonzini
627cb94ff5fSMartin Kletzander if (!AUD_register_card(CODEC, &s->card, errp)) {
628cb94ff5fSMartin Kletzander return;
629cb94ff5fSMartin Kletzander }
630cb94ff5fSMartin Kletzander
631bc229b0fSAndreas Färber wm8750_reset(I2C_SLAVE(s));
63249ab747fSPaolo Bonzini }
63349ab747fSPaolo Bonzini
63449ab747fSPaolo Bonzini #if 0
63549ab747fSPaolo Bonzini static void wm8750_fini(I2CSlave *i2c)
63649ab747fSPaolo Bonzini {
637bc229b0fSAndreas Färber WM8750State *s = WM8750(i2c);
638bc229b0fSAndreas Färber
639bc229b0fSAndreas Färber wm8750_reset(I2C_SLAVE(s));
64049ab747fSPaolo Bonzini AUD_remove_card(&s->card);
64149ab747fSPaolo Bonzini g_free(s);
64249ab747fSPaolo Bonzini }
64349ab747fSPaolo Bonzini #endif
64449ab747fSPaolo Bonzini
wm8750_data_req_set(DeviceState * dev,data_req_cb * data_req,void * opaque)6457ab14c5aSPhilippe Mathieu-Daudé void wm8750_data_req_set(DeviceState *dev, data_req_cb *data_req, void *opaque)
64649ab747fSPaolo Bonzini {
647bc229b0fSAndreas Färber WM8750State *s = WM8750(dev);
648bc229b0fSAndreas Färber
64949ab747fSPaolo Bonzini s->data_req = data_req;
65049ab747fSPaolo Bonzini s->opaque = opaque;
65149ab747fSPaolo Bonzini }
65249ab747fSPaolo Bonzini
wm8750_dac_dat(void * opaque,uint32_t sample)65349ab747fSPaolo Bonzini void wm8750_dac_dat(void *opaque, uint32_t sample)
65449ab747fSPaolo Bonzini {
65549ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
65649ab747fSPaolo Bonzini
65749ab747fSPaolo Bonzini *(uint32_t *) &s->data_out[s->idx_out] = sample;
65849ab747fSPaolo Bonzini s->req_out -= 4;
65949ab747fSPaolo Bonzini s->idx_out += 4;
66049ab747fSPaolo Bonzini if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0)
66149ab747fSPaolo Bonzini wm8750_out_flush(s);
66249ab747fSPaolo Bonzini }
66349ab747fSPaolo Bonzini
wm8750_dac_buffer(void * opaque,int samples)66449ab747fSPaolo Bonzini void *wm8750_dac_buffer(void *opaque, int samples)
66549ab747fSPaolo Bonzini {
66649ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
66749ab747fSPaolo Bonzini /* XXX: Should check if there are <i>samples</i> free samples available */
66849ab747fSPaolo Bonzini void *ret = s->data_out + s->idx_out;
66949ab747fSPaolo Bonzini
67049ab747fSPaolo Bonzini s->idx_out += samples << 2;
67149ab747fSPaolo Bonzini s->req_out -= samples << 2;
67249ab747fSPaolo Bonzini return ret;
67349ab747fSPaolo Bonzini }
67449ab747fSPaolo Bonzini
wm8750_dac_commit(void * opaque)67549ab747fSPaolo Bonzini void wm8750_dac_commit(void *opaque)
67649ab747fSPaolo Bonzini {
67749ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
67849ab747fSPaolo Bonzini
67949ab747fSPaolo Bonzini wm8750_out_flush(s);
68049ab747fSPaolo Bonzini }
68149ab747fSPaolo Bonzini
wm8750_adc_dat(void * opaque)68249ab747fSPaolo Bonzini uint32_t wm8750_adc_dat(void *opaque)
68349ab747fSPaolo Bonzini {
68449ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
68549ab747fSPaolo Bonzini uint32_t *data;
68649ab747fSPaolo Bonzini
6874bb38939SGerd Hoffmann if (s->idx_in >= sizeof(s->data_in)) {
68849ab747fSPaolo Bonzini wm8750_in_load(s);
6894bb38939SGerd Hoffmann if (s->idx_in >= sizeof(s->data_in)) {
69085bc5852SKővágó, Zoltán return 0x80008000; /* silence in AUDIO_FORMAT_S16 sample format */
6914bb38939SGerd Hoffmann }
6924bb38939SGerd Hoffmann }
69349ab747fSPaolo Bonzini
69449ab747fSPaolo Bonzini data = (uint32_t *) &s->data_in[s->idx_in];
69549ab747fSPaolo Bonzini s->req_in -= 4;
69649ab747fSPaolo Bonzini s->idx_in += 4;
69749ab747fSPaolo Bonzini return *data;
69849ab747fSPaolo Bonzini }
69949ab747fSPaolo Bonzini
wm8750_set_bclk_in(void * opaque,int new_hz)70049ab747fSPaolo Bonzini void wm8750_set_bclk_in(void *opaque, int new_hz)
70149ab747fSPaolo Bonzini {
70249ab747fSPaolo Bonzini WM8750State *s = (WM8750State *) opaque;
70349ab747fSPaolo Bonzini
70449ab747fSPaolo Bonzini s->ext_adc_hz = new_hz;
70549ab747fSPaolo Bonzini s->ext_dac_hz = new_hz;
70649ab747fSPaolo Bonzini wm8750_clk_update(s, 1);
70749ab747fSPaolo Bonzini }
70849ab747fSPaolo Bonzini
70988e47b9aSKővágó, Zoltán static Property wm8750_properties[] = {
71088e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(WM8750State, card),
71188e47b9aSKővágó, Zoltán DEFINE_PROP_END_OF_LIST(),
71288e47b9aSKővágó, Zoltán };
71388e47b9aSKővágó, Zoltán
wm8750_class_init(ObjectClass * klass,void * data)71449ab747fSPaolo Bonzini static void wm8750_class_init(ObjectClass *klass, void *data)
71549ab747fSPaolo Bonzini {
71649ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
71749ab747fSPaolo Bonzini I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
71849ab747fSPaolo Bonzini
719c8c9e103SPhilippe Mathieu-Daudé dc->realize = wm8750_realize;
72049ab747fSPaolo Bonzini sc->event = wm8750_event;
72149ab747fSPaolo Bonzini sc->recv = wm8750_rx;
72249ab747fSPaolo Bonzini sc->send = wm8750_tx;
72349ab747fSPaolo Bonzini dc->vmsd = &vmstate_wm8750;
7244f67d30bSMarc-André Lureau device_class_set_props(dc, wm8750_properties);
72549ab747fSPaolo Bonzini }
72649ab747fSPaolo Bonzini
72749ab747fSPaolo Bonzini static const TypeInfo wm8750_info = {
728bc229b0fSAndreas Färber .name = TYPE_WM8750,
72949ab747fSPaolo Bonzini .parent = TYPE_I2C_SLAVE,
73049ab747fSPaolo Bonzini .instance_size = sizeof(WM8750State),
73149ab747fSPaolo Bonzini .class_init = wm8750_class_init,
73249ab747fSPaolo Bonzini };
73349ab747fSPaolo Bonzini
wm8750_register_types(void)73449ab747fSPaolo Bonzini static void wm8750_register_types(void)
73549ab747fSPaolo Bonzini {
73649ab747fSPaolo Bonzini type_register_static(&wm8750_info);
73749ab747fSPaolo Bonzini }
73849ab747fSPaolo Bonzini
73949ab747fSPaolo Bonzini type_init(wm8750_register_types)
740