xref: /openbmc/qemu/hw/audio/wm8750.c (revision d328fef93ae757a0dd65ed786a4086e27952eef3)
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