Commit 683efdcbdb83a45f8a6ff52831855d06a56df469
1 parent
39454628
First cut at WM8750 volume control (Jan Kiszka).
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4321 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
70 additions
and
32 deletions
audio/audio.c
... | ... | @@ -117,8 +117,8 @@ volume_t nominal_volume = { |
117 | 117 | 1.0, |
118 | 118 | 1.0 |
119 | 119 | #else |
120 | - UINT_MAX, | |
121 | - UINT_MAX | |
120 | + 1ULL << 32, | |
121 | + 1ULL << 32 | |
122 | 122 | #endif |
123 | 123 | }; |
124 | 124 | |
... | ... | @@ -1955,3 +1955,21 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) |
1955 | 1955 | } |
1956 | 1956 | } |
1957 | 1957 | } |
1958 | + | |
1959 | +void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol) | |
1960 | +{ | |
1961 | + if (sw) { | |
1962 | + sw->vol.mute = mute; | |
1963 | + sw->vol.l = nominal_volume.l * lvol / 255; | |
1964 | + sw->vol.r = nominal_volume.r * rvol / 255; | |
1965 | + } | |
1966 | +} | |
1967 | + | |
1968 | +void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol) | |
1969 | +{ | |
1970 | + if (sw) { | |
1971 | + sw->vol.mute = mute; | |
1972 | + sw->vol.l = nominal_volume.l * lvol / 255; | |
1973 | + sw->vol.r = nominal_volume.r * rvol / 255; | |
1974 | + } | |
1975 | +} | ... | ... |
audio/audio.h
... | ... | @@ -124,6 +124,9 @@ int AUD_is_active_out (SWVoiceOut *sw); |
124 | 124 | void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); |
125 | 125 | uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); |
126 | 126 | |
127 | +void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol); | |
128 | +void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol); | |
129 | + | |
127 | 130 | SWVoiceIn *AUD_open_in ( |
128 | 131 | QEMUSoundCard *card, |
129 | 132 | SWVoiceIn *sw, | ... | ... |
hw/wm8750.c
... | ... | @@ -39,10 +39,18 @@ struct wm8750_s { |
39 | 39 | |
40 | 40 | uint8_t diff[2], pol, ds, monomix[2], alc, mute; |
41 | 41 | uint8_t path[4], mpath[2], power, format; |
42 | - uint32_t inmask, outmask; | |
43 | 42 | const struct wm_rate_s *rate; |
44 | 43 | }; |
45 | 44 | |
45 | +/* pow(10.0, -i / 20.0), i = 0..42 */ | |
46 | +static const uint8_t wm8750_vol_db_table[] = { | |
47 | + 255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45, | |
48 | + 40, 36, 32, 29, 26, 23, 20, 18, 16, 14, 13, 11, 10, 9, 8, 7, 6, 6, 5, 5, | |
49 | + 4, 4, 3, 3, 3, 2, 2 | |
50 | +}; | |
51 | + | |
52 | +#define WM8750_VOL_TRANSFORM(x) wm8750_vol_db_table[(0x7f - x) / 3] | |
53 | + | |
46 | 54 | static inline void wm8750_in_load(struct wm8750_s *s) |
47 | 55 | { |
48 | 56 | int acquired; |
... | ... | @@ -125,6 +133,32 @@ static const struct wm_rate_s wm_rate_table[] = { |
125 | 133 | { 192, 88200, 192, 88200 }, /* SR: 11111 */ |
126 | 134 | }; |
127 | 135 | |
136 | +static void wm8750_vol_update(struct wm8750_s *s) | |
137 | +{ | |
138 | + /* FIXME: multiply all volumes by s->invol[2], s->invol[3] */ | |
139 | + | |
140 | + AUD_set_volume_in(*s->in[0], s->mute, | |
141 | + s->inmute[0] ? 0 : 0xff, | |
142 | + s->inmute[1] ? 0 : 0xff); | |
143 | + | |
144 | + /* FIXME: multiply all volumes by s->outvol[0], s->outvol[1] */ | |
145 | + | |
146 | + /* Speaker: LOUT2VOL ROUT2VOL */ | |
147 | + AUD_set_volume_out(s->dac_voice[0], s->mute, | |
148 | + s->outmute[0] ? 0 : WM8750_VOL_TRANSFORM(s->outvol[4]), | |
149 | + s->outmute[1] ? 0 : WM8750_VOL_TRANSFORM(s->outvol[5])); | |
150 | + | |
151 | + /* Headphone: LOUT2VOL ROUT2VOL */ | |
152 | + AUD_set_volume_out(s->dac_voice[1], s->mute, | |
153 | + s->outmute[0] ? 0 : WM8750_VOL_TRANSFORM(s->outvol[2]), | |
154 | + s->outmute[1] ? 0 : WM8750_VOL_TRANSFORM(s->outvol[3])); | |
155 | + | |
156 | + /* MONOOUT: MONOVOL MONOVOL */ | |
157 | + AUD_set_volume_out(s->dac_voice[2], s->mute, | |
158 | + s->outmute[0] ? 0 : WM8750_VOL_TRANSFORM(s->outvol[6]), | |
159 | + s->outmute[1] ? 0 : WM8750_VOL_TRANSFORM(s->outvol[6])); | |
160 | +} | |
161 | + | |
128 | 162 | static void wm8750_set_format(struct wm8750_s *s) |
129 | 163 | { |
130 | 164 | int i; |
... | ... | @@ -185,6 +219,8 @@ static void wm8750_set_format(struct wm8750_s *s) |
185 | 219 | CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt); |
186 | 220 | /* no sense emulating OUT3 which is a mix of other outputs */ |
187 | 221 | |
222 | + wm8750_vol_update(s); | |
223 | + | |
188 | 224 | /* We should connect the left and right channels to their |
189 | 225 | * respective inputs/outputs but we have completely no need |
190 | 226 | * for mixing or combining paths to different ports, so we |
... | ... | @@ -195,22 +231,6 @@ static void wm8750_set_format(struct wm8750_s *s) |
195 | 231 | AUD_set_active_out(*s->out[0], 1); |
196 | 232 | } |
197 | 233 | |
198 | -static void inline wm8750_mask_update(struct wm8750_s *s) | |
199 | -{ | |
200 | -#define R_ONLY 0x0000ffff | |
201 | -#define L_ONLY 0xffff0000 | |
202 | -#define BOTH (R_ONLY | L_ONLY) | |
203 | -#define NONE (R_ONLY & L_ONLY) | |
204 | - s->inmask = | |
205 | - (s->inmute[0] ? R_ONLY : BOTH) & | |
206 | - (s->inmute[1] ? L_ONLY : BOTH) & | |
207 | - (s->mute ? NONE : BOTH); | |
208 | - s->outmask = | |
209 | - (s->outmute[0] ? R_ONLY : BOTH) & | |
210 | - (s->outmute[1] ? L_ONLY : BOTH) & | |
211 | - (s->mute ? NONE : BOTH); | |
212 | -} | |
213 | - | |
214 | 234 | void wm8750_reset(i2c_slave *i2c) |
215 | 235 | { |
216 | 236 | struct wm8750_s *s = (struct wm8750_s *) i2c; |
... | ... | @@ -249,7 +269,7 @@ void wm8750_reset(i2c_slave *i2c) |
249 | 269 | s->req_in = 0; |
250 | 270 | s->idx_out = 0; |
251 | 271 | s->req_out = 0; |
252 | - wm8750_mask_update(s); | |
272 | + wm8750_vol_update(s); | |
253 | 273 | s->i2c_len = 0; |
254 | 274 | } |
255 | 275 | |
... | ... | @@ -367,19 +387,19 @@ static int wm8750_tx(i2c_slave *i2c, uint8_t data) |
367 | 387 | case WM8750_LINVOL: /* Left Channel PGA */ |
368 | 388 | s->invol[0] = value & 0x3f; /* LINVOL */ |
369 | 389 | s->inmute[0] = (value >> 7) & 1; /* LINMUTE */ |
370 | - wm8750_mask_update(s); | |
390 | + wm8750_vol_update(s); | |
371 | 391 | break; |
372 | 392 | |
373 | 393 | case WM8750_RINVOL: /* Right Channel PGA */ |
374 | 394 | s->invol[1] = value & 0x3f; /* RINVOL */ |
375 | 395 | s->inmute[1] = (value >> 7) & 1; /* RINMUTE */ |
376 | - wm8750_mask_update(s); | |
396 | + wm8750_vol_update(s); | |
377 | 397 | break; |
378 | 398 | |
379 | 399 | case WM8750_ADCDAC: /* ADC and DAC Control */ |
380 | 400 | s->pol = (value >> 5) & 3; /* ADCPOL */ |
381 | 401 | s->mute = (value >> 3) & 1; /* DACMU */ |
382 | - wm8750_mask_update(s); | |
402 | + wm8750_vol_update(s); | |
383 | 403 | break; |
384 | 404 | |
385 | 405 | case WM8750_ADCTL3: /* Additional Control (3) */ |
... | ... | @@ -437,19 +457,21 @@ static int wm8750_tx(i2c_slave *i2c, uint8_t data) |
437 | 457 | break; |
438 | 458 | |
439 | 459 | case WM8750_LOUT1V: /* LOUT1 Volume */ |
440 | - s->outvol[2] = value & 0x7f; /* LOUT2VOL */ | |
460 | + s->outvol[2] = value & 0x7f; /* LOUT1VOL */ | |
441 | 461 | break; |
442 | 462 | |
443 | 463 | case WM8750_LOUT2V: /* LOUT2 Volume */ |
444 | 464 | s->outvol[4] = value & 0x7f; /* LOUT2VOL */ |
465 | + wm8750_vol_update(s); | |
445 | 466 | break; |
446 | 467 | |
447 | 468 | case WM8750_ROUT1V: /* ROUT1 Volume */ |
448 | - s->outvol[3] = value & 0x7f; /* ROUT2VOL */ | |
469 | + s->outvol[3] = value & 0x7f; /* ROUT1VOL */ | |
449 | 470 | break; |
450 | 471 | |
451 | 472 | case WM8750_ROUT2V: /* ROUT2 Volume */ |
452 | 473 | s->outvol[5] = value & 0x7f; /* ROUT2VOL */ |
474 | + wm8750_vol_update(s); | |
453 | 475 | break; |
454 | 476 | |
455 | 477 | case WM8750_MOUTV: /* MONOOUT Volume */ |
... | ... | @@ -531,8 +553,6 @@ static void wm8750_save(QEMUFile *f, void *opaque) |
531 | 553 | qemu_put_8s(f, &s->mpath[i]); |
532 | 554 | qemu_put_8s(f, &s->format); |
533 | 555 | qemu_put_8s(f, &s->power); |
534 | - qemu_put_be32s(f, &s->inmask); | |
535 | - qemu_put_be32s(f, &s->outmask); | |
536 | 556 | qemu_put_byte(f, (s->rate - wm_rate_table) / sizeof(*s->rate)); |
537 | 557 | i2c_slave_save(f, &s->i2c); |
538 | 558 | } |
... | ... | @@ -573,8 +593,6 @@ static int wm8750_load(QEMUFile *f, void *opaque, int version_id) |
573 | 593 | qemu_get_8s(f, &s->mpath[i]); |
574 | 594 | qemu_get_8s(f, &s->format); |
575 | 595 | qemu_get_8s(f, &s->power); |
576 | - qemu_get_be32s(f, &s->inmask); | |
577 | - qemu_get_be32s(f, &s->outmask); | |
578 | 596 | s->rate = &wm_rate_table[(uint8_t) qemu_get_byte(f) & 0x1f]; |
579 | 597 | i2c_slave_load(f, &s->i2c); |
580 | 598 | return 0; |
... | ... | @@ -619,8 +637,7 @@ void wm8750_data_req_set(i2c_slave *i2c, |
619 | 637 | void wm8750_dac_dat(void *opaque, uint32_t sample) |
620 | 638 | { |
621 | 639 | struct wm8750_s *s = (struct wm8750_s *) opaque; |
622 | - uint32_t *data = (uint32_t *) &s->data_out[s->idx_out]; | |
623 | - *data = sample & s->outmask; | |
640 | + *(uint32_t *) &s->data_out[s->idx_out] = sample; | |
624 | 641 | s->req_out -= 4; |
625 | 642 | s->idx_out += 4; |
626 | 643 | if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) |
... | ... | @@ -654,5 +671,5 @@ uint32_t wm8750_adc_dat(void *opaque) |
654 | 671 | data = (uint32_t *) &s->data_in[s->idx_in]; |
655 | 672 | s->req_in -= 4; |
656 | 673 | s->idx_in += 4; |
657 | - return *data & s->inmask; | |
674 | + return *data; | |
658 | 675 | } | ... | ... |