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 | } | ... | ... |