Commit adb86c372e1596c07437682ff7aa71c905dbc14f
1 parent
3f582262
Add WM8750 and MAX7310 chips (I2C slaves).
Wolfson Microsystems WM8750 audio chip and Maxim MAX7310 gpio expander chip are used in the Spitz. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2854 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
5 changed files
with
805 additions
and
2 deletions
Makefile.target
... | ... | @@ -459,8 +459,8 @@ VL_OBJS+= versatile_pci.o sd.o ptimer.o |
459 | 459 | VL_OBJS+= arm_gic.o realview.o arm_sysctl.o |
460 | 460 | VL_OBJS+= arm-semi.o |
461 | 461 | VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o |
462 | -VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o | |
463 | -VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV) | |
462 | +VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o | |
463 | +VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV) wm8750.o | |
464 | 464 | CPPFLAGS += -DHAS_AUDIO |
465 | 465 | endif |
466 | 466 | ifeq ($(TARGET_BASE_ARCH), sh4) | ... | ... |
hw/i2c.h
... | ... | @@ -46,4 +46,18 @@ void i2c_nack(i2c_bus *bus); |
46 | 46 | int i2c_send(i2c_bus *bus, uint8_t data); |
47 | 47 | int i2c_recv(i2c_bus *bus); |
48 | 48 | |
49 | +/* max7310.c */ | |
50 | +i2c_slave *max7310_init(i2c_bus *bus); | |
51 | +void max7310_reset(i2c_slave *i2c); | |
52 | +qemu_irq *max7310_gpio_in_get(i2c_slave *i2c); | |
53 | +void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler); | |
54 | + | |
55 | +/* wm8750.c */ | |
56 | +i2c_slave *wm8750_init(i2c_bus *bus, AudioState *audio); | |
57 | +void wm8750_reset(i2c_slave *i2c); | |
58 | +void wm8750_data_req_set(i2c_slave *i2c, | |
59 | + void (*data_req)(void *, int, int), void *opaque); | |
60 | +void wm8750_dac_dat(void *opaque, uint32_t sample); | |
61 | +uint32_t wm8750_adc_dat(void *opaque); | |
62 | + | |
49 | 63 | #endif | ... | ... |
hw/max7310.c
0 → 100644
1 | +/* | |
2 | + * MAX7310 8-port GPIO expansion chip. | |
3 | + * | |
4 | + * Copyright (c) 2006 Openedhand Ltd. | |
5 | + * Written by Andrzej Zaborowski <balrog@zabor.org> | |
6 | + * | |
7 | + * This file is licensed under GNU GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | + | |
12 | +struct max7310_s { | |
13 | + i2c_slave i2c; | |
14 | + int i2c_command_byte; | |
15 | + int len; | |
16 | + | |
17 | + uint8_t level; | |
18 | + uint8_t direction; | |
19 | + uint8_t polarity; | |
20 | + uint8_t status; | |
21 | + uint8_t command; | |
22 | + qemu_irq handler[8]; | |
23 | + qemu_irq *gpio_in; | |
24 | +}; | |
25 | + | |
26 | +void max7310_reset(i2c_slave *i2c) | |
27 | +{ | |
28 | + struct max7310_s *s = (struct max7310_s *) i2c; | |
29 | + s->level &= s->direction; | |
30 | + s->direction = 0xff; | |
31 | + s->polarity = 0xf0; | |
32 | + s->status = 0x01; | |
33 | + s->command = 0x00; | |
34 | +} | |
35 | + | |
36 | +static int max7310_rx(i2c_slave *i2c) | |
37 | +{ | |
38 | + struct max7310_s *s = (struct max7310_s *) i2c; | |
39 | + | |
40 | + switch (s->command) { | |
41 | + case 0x00: /* Input port */ | |
42 | + return s->level ^ s->polarity; | |
43 | + break; | |
44 | + | |
45 | + case 0x01: /* Output port */ | |
46 | + return s->level & ~s->direction; | |
47 | + break; | |
48 | + | |
49 | + case 0x02: /* Polarity inversion */ | |
50 | + return s->polarity; | |
51 | + | |
52 | + case 0x03: /* Configuration */ | |
53 | + return s->direction; | |
54 | + | |
55 | + case 0x04: /* Timeout */ | |
56 | + return s->status; | |
57 | + break; | |
58 | + | |
59 | + case 0xff: /* Reserved */ | |
60 | + return 0xff; | |
61 | + | |
62 | + default: | |
63 | +#ifdef VERBOSE | |
64 | + printf("%s: unknown register %02x\n", __FUNCTION__, s->command); | |
65 | +#endif | |
66 | + break; | |
67 | + } | |
68 | + return 0xff; | |
69 | +} | |
70 | + | |
71 | +static int max7310_tx(i2c_slave *i2c, uint8_t data) | |
72 | +{ | |
73 | + struct max7310_s *s = (struct max7310_s *) i2c; | |
74 | + uint8_t diff; | |
75 | + int line; | |
76 | + | |
77 | + if (s->len ++ > 1) { | |
78 | +#ifdef VERBOSE | |
79 | + printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len); | |
80 | +#endif | |
81 | + return 1; | |
82 | + } | |
83 | + | |
84 | + if (s->i2c_command_byte) { | |
85 | + s->command = data; | |
86 | + s->i2c_command_byte = 0; | |
87 | + return 0; | |
88 | + } | |
89 | + | |
90 | + switch (s->command) { | |
91 | + case 0x01: /* Output port */ | |
92 | + for (diff = (data ^ s->level) & ~s->direction; diff; | |
93 | + diff &= ~(1 << line)) { | |
94 | + line = ffs(diff) - 1; | |
95 | + if (s->handler[line]) | |
96 | + qemu_set_irq(s->handler[line], (data >> line) & 1); | |
97 | + } | |
98 | + s->level = (s->level & s->direction) | (data & ~s->direction); | |
99 | + break; | |
100 | + | |
101 | + case 0x02: /* Polarity inversion */ | |
102 | + s->polarity = data; | |
103 | + break; | |
104 | + | |
105 | + case 0x03: /* Configuration */ | |
106 | + s->level &= ~(s->direction ^ data); | |
107 | + s->direction = data; | |
108 | + break; | |
109 | + | |
110 | + case 0x04: /* Timeout */ | |
111 | + s->status = data; | |
112 | + break; | |
113 | + | |
114 | + case 0x00: /* Input port - ignore writes */ | |
115 | + break; | |
116 | + default: | |
117 | +#ifdef VERBOSE | |
118 | + printf("%s: unknown register %02x\n", __FUNCTION__, s->command); | |
119 | +#endif | |
120 | + return 1; | |
121 | + } | |
122 | + | |
123 | + return 0; | |
124 | +} | |
125 | + | |
126 | +static void max7310_event(i2c_slave *i2c, enum i2c_event event) | |
127 | +{ | |
128 | + struct max7310_s *s = (struct max7310_s *) i2c; | |
129 | + s->len = 0; | |
130 | + | |
131 | + switch (event) { | |
132 | + case I2C_START_SEND: | |
133 | + s->i2c_command_byte = 1; | |
134 | + break; | |
135 | + case I2C_FINISH: | |
136 | + if (s->len == 1) | |
137 | +#ifdef VERBOSE | |
138 | + printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len); | |
139 | +#endif | |
140 | + break; | |
141 | + default: | |
142 | + break; | |
143 | + } | |
144 | +} | |
145 | + | |
146 | +static void max7310_gpio_set(void *opaque, int line, int level) | |
147 | +{ | |
148 | + struct max7310_s *s = (struct max7310_s *) opaque; | |
149 | + if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0) | |
150 | + cpu_abort(cpu_single_env, "bad GPIO line"); | |
151 | + | |
152 | + if (level) | |
153 | + s->level |= s->direction & (1 << line); | |
154 | + else | |
155 | + s->level &= ~(s->direction & (1 << line)); | |
156 | +} | |
157 | + | |
158 | +/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols), | |
159 | + * but also accepts sequences that are not SMBus so return an I2C device. */ | |
160 | +struct i2c_slave *max7310_init(i2c_bus *bus) | |
161 | +{ | |
162 | + struct max7310_s *s = (struct max7310_s *) | |
163 | + i2c_slave_init(bus, 0, sizeof(struct max7310_s)); | |
164 | + s->i2c.event = max7310_event; | |
165 | + s->i2c.recv = max7310_rx; | |
166 | + s->i2c.send = max7310_tx; | |
167 | + s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s, | |
168 | + sizeof(s->handler) / sizeof(*s->handler)); | |
169 | + | |
170 | + max7310_reset(&s->i2c); | |
171 | + | |
172 | + return &s->i2c; | |
173 | +} | |
174 | + | |
175 | +qemu_irq *max7310_gpio_in_get(i2c_slave *i2c) | |
176 | +{ | |
177 | + struct max7310_s *s = (struct max7310_s *) i2c; | |
178 | + return s->gpio_in; | |
179 | +} | |
180 | + | |
181 | +void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler) | |
182 | +{ | |
183 | + struct max7310_s *s = (struct max7310_s *) i2c; | |
184 | + if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0) | |
185 | + cpu_abort(cpu_single_env, "bad GPIO line"); | |
186 | + | |
187 | + s->handler[line] = handler; | |
188 | +} | ... | ... |
hw/spitz.c
... | ... | @@ -801,6 +801,57 @@ static void spitz_microdrive_attach(struct pxa2xx_state_s *cpu) |
801 | 801 | } |
802 | 802 | } |
803 | 803 | |
804 | +/* Wm8750 and Max7310 on I2C */ | |
805 | + | |
806 | +#define AKITA_MAX_ADDR 0x18 | |
807 | +#define SPITZ_WM_ADDRL 0x1a | |
808 | +#define SPITZ_WM_ADDRH 0x1b | |
809 | + | |
810 | +#define SPITZ_GPIO_WM 5 | |
811 | + | |
812 | +#ifdef HAS_AUDIO | |
813 | +static void spitz_wm8750_addr(int line, int level, void *opaque) | |
814 | +{ | |
815 | + i2c_slave *wm = (i2c_slave *) opaque; | |
816 | + if (level) | |
817 | + i2c_set_slave_address(wm, SPITZ_WM_ADDRH); | |
818 | + else | |
819 | + i2c_set_slave_address(wm, SPITZ_WM_ADDRL); | |
820 | +} | |
821 | +#endif | |
822 | + | |
823 | +static void spitz_i2c_setup(struct pxa2xx_state_s *cpu) | |
824 | +{ | |
825 | + /* Attach the CPU on one end of our I2C bus. */ | |
826 | + i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); | |
827 | + | |
828 | +#ifdef HAS_AUDIO | |
829 | + AudioState *audio; | |
830 | + i2c_slave *wm; | |
831 | + | |
832 | + audio = AUD_init(); | |
833 | + if (!audio) | |
834 | + return; | |
835 | + /* Attach a WM8750 to the bus */ | |
836 | + wm = wm8750_init(bus, audio); | |
837 | + | |
838 | + spitz_wm8750_addr(0, 0, wm); | |
839 | + pxa2xx_gpio_handler_set(cpu->gpio, SPITZ_GPIO_WM, spitz_wm8750_addr, wm); | |
840 | + /* .. and to the sound interface. */ | |
841 | + cpu->i2s->opaque = wm; | |
842 | + cpu->i2s->codec_out = wm8750_dac_dat; | |
843 | + cpu->i2s->codec_in = wm8750_adc_dat; | |
844 | + wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); | |
845 | +#endif | |
846 | +} | |
847 | + | |
848 | +static void spitz_akita_i2c_setup(struct pxa2xx_state_s *cpu) | |
849 | +{ | |
850 | + /* Attach a Max7310 to Akita I2C bus. */ | |
851 | + i2c_set_slave_address(max7310_init(pxa2xx_i2c_bus(cpu->i2c[0])), | |
852 | + AKITA_MAX_ADDR); | |
853 | +} | |
854 | + | |
804 | 855 | /* Other peripherals */ |
805 | 856 | |
806 | 857 | static void spitz_charge_switch(int line, int level, void *opaque) |
... | ... | @@ -1026,6 +1077,11 @@ static void spitz_common_init(int ram_size, int vga_ram_size, |
1026 | 1077 | |
1027 | 1078 | spitz_gpio_setup(cpu, (model == akita) ? 1 : 2); |
1028 | 1079 | |
1080 | + spitz_i2c_setup(cpu); | |
1081 | + | |
1082 | + if (model == akita) | |
1083 | + spitz_akita_i2c_setup(cpu); | |
1084 | + | |
1029 | 1085 | if (model == terrier) |
1030 | 1086 | /* A 6.0 GB microdrive is permanently sitting in CF slot 0. */ |
1031 | 1087 | spitz_microdrive_attach(cpu); | ... | ... |
hw/wm8750.c
0 → 100644
1 | +/* | |
2 | + * WM8750 audio CODEC. | |
3 | + * | |
4 | + * Copyright (c) 2006 Openedhand Ltd. | |
5 | + * Written by Andrzej Zaborowski <balrog@zabor.org> | |
6 | + * | |
7 | + * This file is licensed under GNU GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | + | |
12 | +#define IN_PORT_N 3 | |
13 | +#define OUT_PORT_N 3 | |
14 | + | |
15 | +#define CODEC "wm8750" | |
16 | + | |
17 | +struct wm_rate_s; | |
18 | +struct wm8750_s { | |
19 | + i2c_slave i2c; | |
20 | + uint8_t i2c_data[2]; | |
21 | + int i2c_len; | |
22 | + QEMUSoundCard card; | |
23 | + SWVoiceIn *adc_voice[IN_PORT_N]; | |
24 | + SWVoiceOut *dac_voice[OUT_PORT_N]; | |
25 | + int enable; | |
26 | + void (*data_req)(void *, int, int); | |
27 | + void *opaque; | |
28 | + uint8_t data_in[4096]; | |
29 | + uint8_t data_out[4096]; | |
30 | + int idx_in, req_in; | |
31 | + int idx_out, req_out; | |
32 | + | |
33 | + SWVoiceOut **out[2]; | |
34 | + uint8_t outvol[7], outmute[2]; | |
35 | + SWVoiceIn **in[2]; | |
36 | + uint8_t invol[4], inmute[2]; | |
37 | + | |
38 | + uint8_t diff[2], pol, ds, monomix[2], alc, mute; | |
39 | + uint8_t path[4], mpath[2], power, format; | |
40 | + uint32_t inmask, outmask; | |
41 | + const struct wm_rate_s *rate; | |
42 | +}; | |
43 | + | |
44 | +static inline void wm8750_in_load(struct wm8750_s *s) | |
45 | +{ | |
46 | + int acquired; | |
47 | + if (s->idx_in + s->req_in <= sizeof(s->data_in)) | |
48 | + return; | |
49 | + s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in); | |
50 | + acquired = AUD_read(*s->in[0], s->data_in + s->idx_in, | |
51 | + sizeof(s->data_in) - s->idx_in); | |
52 | +} | |
53 | + | |
54 | +static inline void wm8750_out_flush(struct wm8750_s *s) | |
55 | +{ | |
56 | + int sent; | |
57 | + if (!s->idx_out) | |
58 | + return; | |
59 | + sent = AUD_write(*s->out[0], s->data_out, s->idx_out); | |
60 | + s->idx_out = 0; | |
61 | +} | |
62 | + | |
63 | +static void wm8750_audio_in_cb(void *opaque, int avail_b) | |
64 | +{ | |
65 | + struct wm8750_s *s = (struct wm8750_s *) opaque; | |
66 | + s->req_in = avail_b; | |
67 | + s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2); | |
68 | + | |
69 | +#if 0 | |
70 | + wm8750_in_load(s); | |
71 | +#endif | |
72 | +} | |
73 | + | |
74 | +static void wm8750_audio_out_cb(void *opaque, int free_b) | |
75 | +{ | |
76 | + struct wm8750_s *s = (struct wm8750_s *) opaque; | |
77 | + wm8750_out_flush(s); | |
78 | + | |
79 | + s->req_out = free_b; | |
80 | + s->data_req(s->opaque, free_b >> 2, s->req_in >> 2); | |
81 | +} | |
82 | + | |
83 | +struct wm_rate_s { | |
84 | + int adc; | |
85 | + int adc_hz; | |
86 | + int dac; | |
87 | + int dac_hz; | |
88 | +}; | |
89 | + | |
90 | +static const struct wm_rate_s wm_rate_table[] = { | |
91 | + { 256, 48000, 256, 48000 }, /* SR: 00000 */ | |
92 | + { 384, 48000, 384, 48000 }, /* SR: 00001 */ | |
93 | + { 256, 48000, 1536, 8000 }, /* SR: 00010 */ | |
94 | + { 384, 48000, 2304, 8000 }, /* SR: 00011 */ | |
95 | + { 1536, 8000, 256, 48000 }, /* SR: 00100 */ | |
96 | + { 2304, 8000, 384, 48000 }, /* SR: 00101 */ | |
97 | + { 1536, 8000, 1536, 8000 }, /* SR: 00110 */ | |
98 | + { 2304, 8000, 2304, 8000 }, /* SR: 00111 */ | |
99 | + { 1024, 12000, 1024, 12000 }, /* SR: 01000 */ | |
100 | + { 1526, 12000, 1536, 12000 }, /* SR: 01001 */ | |
101 | + { 768, 16000, 768, 16000 }, /* SR: 01010 */ | |
102 | + { 1152, 16000, 1152, 16000 }, /* SR: 01011 */ | |
103 | + { 384, 32000, 384, 32000 }, /* SR: 01100 */ | |
104 | + { 576, 32000, 576, 32000 }, /* SR: 01101 */ | |
105 | + { 128, 96000, 128, 96000 }, /* SR: 01110 */ | |
106 | + { 192, 96000, 192, 96000 }, /* SR: 01111 */ | |
107 | + { 256, 44100, 256, 44100 }, /* SR: 10000 */ | |
108 | + { 384, 44100, 384, 44100 }, /* SR: 10001 */ | |
109 | + { 256, 44100, 1408, 8018 }, /* SR: 10010 */ | |
110 | + { 384, 44100, 2112, 8018 }, /* SR: 10011 */ | |
111 | + { 1408, 8018, 256, 44100 }, /* SR: 10100 */ | |
112 | + { 2112, 8018, 384, 44100 }, /* SR: 10101 */ | |
113 | + { 1408, 8018, 1408, 8018 }, /* SR: 10110 */ | |
114 | + { 2112, 8018, 2112, 8018 }, /* SR: 10111 */ | |
115 | + { 1024, 11025, 1024, 11025 }, /* SR: 11000 */ | |
116 | + { 1536, 11025, 1536, 11025 }, /* SR: 11001 */ | |
117 | + { 512, 22050, 512, 22050 }, /* SR: 11010 */ | |
118 | + { 768, 22050, 768, 22050 }, /* SR: 11011 */ | |
119 | + { 512, 24000, 512, 24000 }, /* SR: 11100 */ | |
120 | + { 768, 24000, 768, 24000 }, /* SR: 11101 */ | |
121 | + { 128, 88200, 128, 88200 }, /* SR: 11110 */ | |
122 | + { 192, 88200, 128, 88200 }, /* SR: 11111 */ | |
123 | +}; | |
124 | + | |
125 | +void wm8750_set_format(struct wm8750_s *s) | |
126 | +{ | |
127 | + int i; | |
128 | + audsettings_t in_fmt; | |
129 | + audsettings_t out_fmt; | |
130 | + audsettings_t monoout_fmt; | |
131 | + | |
132 | + wm8750_out_flush(s); | |
133 | + | |
134 | + if (s->in[0] && *s->in[0]) | |
135 | + AUD_set_active_in(*s->in[0], 0); | |
136 | + if (s->out[0] && *s->out[0]) | |
137 | + AUD_set_active_out(*s->out[0], 0); | |
138 | + | |
139 | + for (i = 0; i < IN_PORT_N; i ++) | |
140 | + if (s->adc_voice[i]) { | |
141 | + AUD_close_in(&s->card, s->adc_voice[i]); | |
142 | + s->adc_voice[i] = 0; | |
143 | + } | |
144 | + for (i = 0; i < OUT_PORT_N; i ++) | |
145 | + if (s->dac_voice[i]) { | |
146 | + AUD_close_out(&s->card, s->dac_voice[i]); | |
147 | + s->dac_voice[i] = 0; | |
148 | + } | |
149 | + | |
150 | + if (!s->enable) | |
151 | + return; | |
152 | + | |
153 | + /* Setup input */ | |
154 | + in_fmt.endianness = 0; | |
155 | + in_fmt.nchannels = 2; | |
156 | + in_fmt.freq = s->rate->adc_hz; | |
157 | + in_fmt.fmt = AUD_FMT_S16; | |
158 | + | |
159 | + s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0], | |
160 | + CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt); | |
161 | + s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1], | |
162 | + CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt); | |
163 | + s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2], | |
164 | + CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt); | |
165 | + | |
166 | + /* Setup output */ | |
167 | + out_fmt.endianness = 0; | |
168 | + out_fmt.nchannels = 2; | |
169 | + out_fmt.freq = s->rate->dac_hz; | |
170 | + out_fmt.fmt = AUD_FMT_S16; | |
171 | + monoout_fmt.endianness = 0; | |
172 | + monoout_fmt.nchannels = 1; | |
173 | + monoout_fmt.freq = s->rate->dac_hz; | |
174 | + monoout_fmt.fmt = AUD_FMT_S16; | |
175 | + | |
176 | + s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], | |
177 | + CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt); | |
178 | + s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1], | |
179 | + CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt); | |
180 | + /* MONOMIX is also in stereo for simplicity */ | |
181 | + s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2], | |
182 | + CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt); | |
183 | + /* no sense emulating OUT3 which is a mix of other outputs */ | |
184 | + | |
185 | + /* We should connect the left and right channels to their | |
186 | + * respective inputs/outputs but we have completely no need | |
187 | + * for mixing or combining paths to different ports, so we | |
188 | + * connect both channels to where the left channel is routed. */ | |
189 | + if (s->in[0] && *s->in[0]) | |
190 | + AUD_set_active_in(*s->in[0], 1); | |
191 | + if (s->out[0] && *s->out[0]) | |
192 | + AUD_set_active_out(*s->out[0], 1); | |
193 | +} | |
194 | + | |
195 | +void inline wm8750_mask_update(struct wm8750_s *s) | |
196 | +{ | |
197 | +#define R_ONLY 0x0000ffff | |
198 | +#define L_ONLY 0xffff0000 | |
199 | +#define BOTH (R_ONLY | L_ONLY) | |
200 | +#define NONE (R_ONLY & L_ONLY) | |
201 | + s->inmask = | |
202 | + (s->inmute[0] ? R_ONLY : BOTH) & | |
203 | + (s->inmute[1] ? L_ONLY : BOTH) & | |
204 | + (s->mute ? NONE : BOTH); | |
205 | + s->outmask = | |
206 | + (s->outmute[0] ? R_ONLY : BOTH) & | |
207 | + (s->outmute[1] ? L_ONLY : BOTH) & | |
208 | + (s->mute ? NONE : BOTH); | |
209 | +} | |
210 | + | |
211 | +void wm8750_reset(i2c_slave *i2c) | |
212 | +{ | |
213 | + struct wm8750_s *s = (struct wm8750_s *) i2c; | |
214 | + s->enable = 0; | |
215 | + wm8750_set_format(s); | |
216 | + s->diff[0] = 0; | |
217 | + s->diff[1] = 0; | |
218 | + s->ds = 0; | |
219 | + s->alc = 0; | |
220 | + s->in[0] = &s->adc_voice[0]; | |
221 | + s->invol[0] = 0x17; | |
222 | + s->invol[1] = 0x17; | |
223 | + s->invol[2] = 0xc3; | |
224 | + s->invol[3] = 0xc3; | |
225 | + s->out[0] = &s->dac_voice[0]; | |
226 | + s->outvol[0] = 0xff; | |
227 | + s->outvol[1] = 0xff; | |
228 | + s->outvol[2] = 0x79; | |
229 | + s->outvol[3] = 0x79; | |
230 | + s->outvol[4] = 0x79; | |
231 | + s->outvol[5] = 0x79; | |
232 | + s->inmute[0] = 0; | |
233 | + s->inmute[1] = 0; | |
234 | + s->outmute[0] = 0; | |
235 | + s->outmute[1] = 0; | |
236 | + s->mute = 1; | |
237 | + s->path[0] = 0; | |
238 | + s->path[1] = 0; | |
239 | + s->path[2] = 0; | |
240 | + s->path[3] = 0; | |
241 | + s->mpath[0] = 0; | |
242 | + s->mpath[1] = 0; | |
243 | + s->format = 0x0a; | |
244 | + s->idx_in = sizeof(s->data_in); | |
245 | + s->req_in = 0; | |
246 | + s->idx_out = 0; | |
247 | + s->req_out = 0; | |
248 | + wm8750_mask_update(s); | |
249 | + s->i2c_len = 0; | |
250 | +} | |
251 | + | |
252 | +static void wm8750_event(i2c_slave *i2c, enum i2c_event event) | |
253 | +{ | |
254 | + struct wm8750_s *s = (struct wm8750_s *) i2c; | |
255 | + | |
256 | + switch (event) { | |
257 | + case I2C_START_SEND: | |
258 | + s->i2c_len = 0; | |
259 | + break; | |
260 | + case I2C_FINISH: | |
261 | +#ifdef VERBOSE | |
262 | + if (s->i2c_len < 2) | |
263 | + printf("%s: message too short (%i bytes)\n", | |
264 | + __FUNCTION__, s->i2c_len); | |
265 | +#endif | |
266 | + break; | |
267 | + default: | |
268 | + break; | |
269 | + } | |
270 | +} | |
271 | + | |
272 | +#define WM8750_LINVOL 0x00 | |
273 | +#define WM8750_RINVOL 0x01 | |
274 | +#define WM8750_LOUT1V 0x02 | |
275 | +#define WM8750_ROUT1V 0x03 | |
276 | +#define WM8750_ADCDAC 0x05 | |
277 | +#define WM8750_IFACE 0x07 | |
278 | +#define WM8750_SRATE 0x08 | |
279 | +#define WM8750_LDAC 0x0a | |
280 | +#define WM8750_RDAC 0x0b | |
281 | +#define WM8750_BASS 0x0c | |
282 | +#define WM8750_TREBLE 0x0d | |
283 | +#define WM8750_RESET 0x0f | |
284 | +#define WM8750_3D 0x10 | |
285 | +#define WM8750_ALC1 0x11 | |
286 | +#define WM8750_ALC2 0x12 | |
287 | +#define WM8750_ALC3 0x13 | |
288 | +#define WM8750_NGATE 0x14 | |
289 | +#define WM8750_LADC 0x15 | |
290 | +#define WM8750_RADC 0x16 | |
291 | +#define WM8750_ADCTL1 0x17 | |
292 | +#define WM8750_ADCTL2 0x18 | |
293 | +#define WM8750_PWR1 0x19 | |
294 | +#define WM8750_PWR2 0x1a | |
295 | +#define WM8750_ADCTL3 0x1b | |
296 | +#define WM8750_ADCIN 0x1f | |
297 | +#define WM8750_LADCIN 0x20 | |
298 | +#define WM8750_RADCIN 0x21 | |
299 | +#define WM8750_LOUTM1 0x22 | |
300 | +#define WM8750_LOUTM2 0x23 | |
301 | +#define WM8750_ROUTM1 0x24 | |
302 | +#define WM8750_ROUTM2 0x25 | |
303 | +#define WM8750_MOUTM1 0x26 | |
304 | +#define WM8750_MOUTM2 0x27 | |
305 | +#define WM8750_LOUT2V 0x28 | |
306 | +#define WM8750_ROUT2V 0x29 | |
307 | +#define WM8750_MOUTV 0x2a | |
308 | + | |
309 | +static int wm8750_tx(i2c_slave *i2c, uint8_t data) | |
310 | +{ | |
311 | + struct wm8750_s *s = (struct wm8750_s *) i2c; | |
312 | + uint8_t cmd; | |
313 | + uint16_t value; | |
314 | + | |
315 | + if (s->i2c_len >= 2) { | |
316 | + printf("%s: long message (%i bytes)\n", __FUNCTION__, s->i2c_len); | |
317 | +#ifdef VERBOSE | |
318 | + return 1; | |
319 | +#endif | |
320 | + } | |
321 | + s->i2c_data[s->i2c_len ++] = data; | |
322 | + if (s->i2c_len != 2) | |
323 | + return 0; | |
324 | + | |
325 | + cmd = s->i2c_data[0] >> 1; | |
326 | + value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff; | |
327 | + | |
328 | + switch (cmd) { | |
329 | + case WM8750_LADCIN: /* ADC Signal Path Control (Left) */ | |
330 | + s->diff[0] = (((value >> 6) & 3) == 3); /* LINSEL */ | |
331 | + if (s->diff[0]) | |
332 | + s->in[0] = &s->adc_voice[0 + s->ds * 1]; | |
333 | + else | |
334 | + s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0]; | |
335 | + break; | |
336 | + | |
337 | + case WM8750_RADCIN: /* ADC Signal Path Control (Right) */ | |
338 | + s->diff[1] = (((value >> 6) & 3) == 3); /* RINSEL */ | |
339 | + if (s->diff[1]) | |
340 | + s->in[1] = &s->adc_voice[0 + s->ds * 1]; | |
341 | + else | |
342 | + s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0]; | |
343 | + break; | |
344 | + | |
345 | + case WM8750_ADCIN: /* ADC Input Mode */ | |
346 | + s->ds = (value >> 8) & 1; /* DS */ | |
347 | + if (s->diff[0]) | |
348 | + s->in[0] = &s->adc_voice[0 + s->ds * 1]; | |
349 | + if (s->diff[1]) | |
350 | + s->in[1] = &s->adc_voice[0 + s->ds * 1]; | |
351 | + s->monomix[0] = (value >> 6) & 3; /* MONOMIX */ | |
352 | + break; | |
353 | + | |
354 | + case WM8750_ADCTL1: /* Additional Control (1) */ | |
355 | + s->monomix[1] = (value >> 1) & 1; /* DMONOMIX */ | |
356 | + break; | |
357 | + | |
358 | + case WM8750_PWR1: /* Power Management (1) */ | |
359 | + s->enable = ((value >> 6) & 7) == 3; /* VMIDSEL, VREF */ | |
360 | + wm8750_set_format(s); | |
361 | + break; | |
362 | + | |
363 | + case WM8750_LINVOL: /* Left Channel PGA */ | |
364 | + s->invol[0] = value & 0x3f; /* LINVOL */ | |
365 | + s->inmute[0] = (value >> 7) & 1; /* LINMUTE */ | |
366 | + wm8750_mask_update(s); | |
367 | + break; | |
368 | + | |
369 | + case WM8750_RINVOL: /* Right Channel PGA */ | |
370 | + s->invol[1] = value & 0x3f; /* RINVOL */ | |
371 | + s->inmute[1] = (value >> 7) & 1; /* RINMUTE */ | |
372 | + wm8750_mask_update(s); | |
373 | + break; | |
374 | + | |
375 | + case WM8750_ADCDAC: /* ADC and DAC Control */ | |
376 | + s->pol = (value >> 5) & 3; /* ADCPOL */ | |
377 | + s->mute = (value >> 3) & 1; /* DACMU */ | |
378 | + wm8750_mask_update(s); | |
379 | + break; | |
380 | + | |
381 | + case WM8750_ADCTL3: /* Additional Control (3) */ | |
382 | + break; | |
383 | + | |
384 | + case WM8750_LADC: /* Left ADC Digital Volume */ | |
385 | + s->invol[2] = value & 0xff; /* LADCVOL */ | |
386 | + break; | |
387 | + | |
388 | + case WM8750_RADC: /* Right ADC Digital Volume */ | |
389 | + s->invol[3] = value & 0xff; /* RADCVOL */ | |
390 | + break; | |
391 | + | |
392 | + case WM8750_ALC1: /* ALC Control (1) */ | |
393 | + s->alc = (value >> 7) & 3; /* ALCSEL */ | |
394 | + break; | |
395 | + | |
396 | + case WM8750_NGATE: /* Noise Gate Control */ | |
397 | + case WM8750_3D: /* 3D enhance */ | |
398 | + break; | |
399 | + | |
400 | + case WM8750_LDAC: /* Left Channel Digital Volume */ | |
401 | + s->outvol[0] = value & 0xff; /* LDACVOL */ | |
402 | + break; | |
403 | + | |
404 | + case WM8750_RDAC: /* Right Channel Digital Volume */ | |
405 | + s->outvol[1] = value & 0xff; /* RDACVOL */ | |
406 | + break; | |
407 | + | |
408 | + case WM8750_BASS: /* Bass Control */ | |
409 | + break; | |
410 | + | |
411 | + case WM8750_LOUTM1: /* Left Mixer Control (1) */ | |
412 | + s->path[0] = (value >> 8) & 1; /* LD2LO */ | |
413 | + break; | |
414 | + | |
415 | + case WM8750_LOUTM2: /* Left Mixer Control (2) */ | |
416 | + s->path[1] = (value >> 8) & 1; /* RD2LO */ | |
417 | + break; | |
418 | + | |
419 | + case WM8750_ROUTM1: /* Right Mixer Control (1) */ | |
420 | + s->path[2] = (value >> 8) & 1; /* LD2RO */ | |
421 | + break; | |
422 | + | |
423 | + case WM8750_ROUTM2: /* Right Mixer Control (2) */ | |
424 | + s->path[3] = (value >> 8) & 1; /* RD2RO */ | |
425 | + break; | |
426 | + | |
427 | + case WM8750_MOUTM1: /* Mono Mixer Control (1) */ | |
428 | + s->mpath[0] = (value >> 8) & 1; /* LD2MO */ | |
429 | + break; | |
430 | + | |
431 | + case WM8750_MOUTM2: /* Mono Mixer Control (2) */ | |
432 | + s->mpath[1] = (value >> 8) & 1; /* RD2MO */ | |
433 | + break; | |
434 | + | |
435 | + case WM8750_LOUT1V: /* LOUT1 Volume */ | |
436 | + s->outvol[2] = value & 0x7f; /* LOUT2VOL */ | |
437 | + break; | |
438 | + | |
439 | + case WM8750_LOUT2V: /* LOUT2 Volume */ | |
440 | + s->outvol[4] = value & 0x7f; /* LOUT2VOL */ | |
441 | + break; | |
442 | + | |
443 | + case WM8750_ROUT1V: /* ROUT1 Volume */ | |
444 | + s->outvol[3] = value & 0x7f; /* ROUT2VOL */ | |
445 | + break; | |
446 | + | |
447 | + case WM8750_ROUT2V: /* ROUT2 Volume */ | |
448 | + s->outvol[5] = value & 0x7f; /* ROUT2VOL */ | |
449 | + break; | |
450 | + | |
451 | + case WM8750_MOUTV: /* MONOOUT Volume */ | |
452 | + s->outvol[6] = value & 0x7f; /* MONOOUTVOL */ | |
453 | + break; | |
454 | + | |
455 | + case WM8750_ADCTL2: /* Additional Control (2) */ | |
456 | + break; | |
457 | + | |
458 | + case WM8750_PWR2: /* Power Management (2) */ | |
459 | + s->power = value & 0x7e; | |
460 | + break; | |
461 | + | |
462 | + case WM8750_IFACE: /* Digital Audio Interface Format */ | |
463 | +#ifdef VERBOSE | |
464 | + if (value & 0x40) /* MS */ | |
465 | + printf("%s: attempt to enable Master Mode\n", __FUNCTION__); | |
466 | +#endif | |
467 | + s->format = value; | |
468 | + wm8750_set_format(s); | |
469 | + break; | |
470 | + | |
471 | + case WM8750_SRATE: /* Clocking and Sample Rate Control */ | |
472 | + s->rate = &wm_rate_table[(value >> 1) & 0x1f]; | |
473 | + wm8750_set_format(s); | |
474 | + break; | |
475 | + | |
476 | + case WM8750_RESET: /* Reset */ | |
477 | + wm8750_reset(&s->i2c); | |
478 | + break; | |
479 | + | |
480 | +#ifdef VERBOSE | |
481 | + default: | |
482 | + printf("%s: unknown register %02x\n", __FUNCTION__, cmd); | |
483 | +#endif | |
484 | + } | |
485 | + | |
486 | + return 0; | |
487 | +} | |
488 | + | |
489 | +static int wm8750_rx(i2c_slave *i2c) | |
490 | +{ | |
491 | + return 0x00; | |
492 | +} | |
493 | + | |
494 | +i2c_slave *wm8750_init(i2c_bus *bus, AudioState *audio) | |
495 | +{ | |
496 | + struct wm8750_s *s = (struct wm8750_s *) | |
497 | + i2c_slave_init(bus, 0, sizeof(struct wm8750_s)); | |
498 | + s->i2c.event = wm8750_event; | |
499 | + s->i2c.recv = wm8750_rx; | |
500 | + s->i2c.send = wm8750_tx; | |
501 | + | |
502 | + AUD_register_card(audio, CODEC, &s->card); | |
503 | + wm8750_reset(&s->i2c); | |
504 | + | |
505 | + return &s->i2c; | |
506 | +} | |
507 | + | |
508 | +void wm8750_fini(i2c_slave *i2c) | |
509 | +{ | |
510 | + struct wm8750_s *s = (struct wm8750_s *) i2c; | |
511 | + wm8750_reset(&s->i2c); | |
512 | + AUD_remove_card(&s->card); | |
513 | + qemu_free(s); | |
514 | +} | |
515 | + | |
516 | +void wm8750_data_req_set(i2c_slave *i2c, | |
517 | + void (*data_req)(void *, int, int), void *opaque) | |
518 | +{ | |
519 | + struct wm8750_s *s = (struct wm8750_s *) i2c; | |
520 | + s->data_req = data_req; | |
521 | + s->opaque = opaque; | |
522 | +} | |
523 | + | |
524 | +void wm8750_dac_dat(void *opaque, uint32_t sample) | |
525 | +{ | |
526 | + struct wm8750_s *s = (struct wm8750_s *) opaque; | |
527 | + uint32_t *data = (uint32_t *) &s->data_out[s->idx_out]; | |
528 | + *data = sample & s->outmask; | |
529 | + s->req_out -= 4; | |
530 | + s->idx_out += 4; | |
531 | + if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) | |
532 | + wm8750_out_flush(s); | |
533 | +} | |
534 | + | |
535 | +uint32_t wm8750_adc_dat(void *opaque) | |
536 | +{ | |
537 | + struct wm8750_s *s = (struct wm8750_s *) opaque; | |
538 | + uint32_t *data; | |
539 | + if (s->idx_in >= sizeof(s->data_in)) | |
540 | + wm8750_in_load(s); | |
541 | + data = (uint32_t *) &s->data_in[s->idx_in]; | |
542 | + s->req_in -= 4; | |
543 | + s->idx_in += 4; | |
544 | + return *data & s->inmask; | |
545 | +} | ... | ... |