qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] wm8750: Soft-volume control for OUT2


From: Jan Kiszka
Subject: [Qemu-devel] [PATCH] wm8750: Soft-volume control for OUT2
Date: Sat, 19 Apr 2008 21:59:35 +0200
User-agent: Thunderbird 2.0.0.12 (X11/20080226)

This adds basic soft-volume control to the Wolfson 8750. Only OUT2 is
considered so far. The critical path of the calculation is built on
scaled math, thus should be fairly efficient (though, given the low
sound data rates, one will have a hard time to notice this on todays
hardware :)). I twisted my brain several times, but I can't guarantee I
got endianness right (if you are on big endian, be warned, keep your
volume low :)).

Note that I ported the volume mapping from the MusicPal patch. For that
device, the mapping allows to play with the volume while being able to
hear an effect even if the host volume is not at its limit. Please check
if this is not due to some MusicPal properties, in which case the
mapping should become tunable. Avoiding positive gains should
unconditionally make sense, though.

Signed-off-by: Jan Kiszka <address@hidden> 
---
 hw/wm8750.c |   33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

Index: b/hw/wm8750.c
===================================================================
--- a/hw/wm8750.c
+++ b/hw/wm8750.c
@@ -7,6 +7,8 @@
  * This file is licensed under GNU GPL.
  */
 
+#include <math.h>
+
 #include "hw.h"
 #include "i2c.h"
 #include "audio/audio.h"
@@ -34,6 +36,7 @@ struct wm8750_s {
 
     SWVoiceOut **out[2];
     uint8_t outvol[7], outmute[2];
+    int out2vol_mult[2];
     SWVoiceIn **in[2];
     uint8_t invol[4], inmute[2];
 
@@ -166,11 +169,11 @@ static void wm8750_set_format(struct wm8
                     CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
 
     /* Setup output */
-    out_fmt.endianness = 0;
+    out_fmt.endianness = AUDIO_HOST_ENDIANNESS;
     out_fmt.nchannels = 2;
     out_fmt.freq = s->rate->dac_hz;
     out_fmt.fmt = AUD_FMT_S16;
-    monoout_fmt.endianness = 0;
+    monoout_fmt.endianness = AUDIO_HOST_ENDIANNESS;
     monoout_fmt.nchannels = 1;
     monoout_fmt.freq = s->rate->dac_hz;
     monoout_fmt.fmt = AUD_FMT_S16;
@@ -314,6 +317,7 @@ static int wm8750_tx(i2c_slave *i2c, uin
     struct wm8750_s *s = (struct wm8750_s *) i2c;
     uint8_t cmd;
     uint16_t value;
+    int vol;
 
     if (s->i2c_len >= 2) {
         printf("%s: long message (%i bytes)\n", __FUNCTION__, s->i2c_len);
@@ -440,7 +444,12 @@ static int wm8750_tx(i2c_slave *i2c, uin
         break;
 
     case WM8750_LOUT2V:        /* LOUT2 Volume */
-        s->outvol[4] = value & 0x7f;           /* LOUT2VOL */
+        s->outvol[4] = vol = value & 0x7f;     /* LOUT2VOL */
+        /* Map volume:
+         *  - no positive gain (avoid clipping)
+         *  - smaller range */
+        vol = (vol - 0x7F) / 3;
+        s->out2vol_mult[0] = pow(10.0, vol/20.0) * 65536.0;
         break;
 
     case WM8750_ROUT1V:        /* ROUT1 Volume */
@@ -448,7 +457,12 @@ static int wm8750_tx(i2c_slave *i2c, uin
         break;
 
     case WM8750_ROUT2V:        /* ROUT2 Volume */
-        s->outvol[5] = value & 0x7f;           /* ROUT2VOL */
+        s->outvol[5] = vol = value & 0x7f;     /* ROUT2VOL */
+        /* Map volume:
+         *  - no positive gain (avoid clipping)
+         *  - smaller range */
+        vol = (vol - 0x7F) / 3;
+        s->out2vol_mult[1] = pow(10.0, vol/20.0) * 65536.0;
         break;
 
     case WM8750_MOUTV: /* MONOOUT Volume */
@@ -616,8 +630,15 @@ void wm8750_data_req_set(i2c_slave *i2c,
 void wm8750_dac_dat(void *opaque, uint32_t sample)
 {
     struct wm8750_s *s = (struct wm8750_s *) opaque;
-    uint32_t *data = (uint32_t *) &s->data_out[s->idx_out];
-    *data = sample & s->outmask;
+    uint16_t *data = (uint16_t *) &s->data_out[s->idx_out];
+    int32_t tmp;
+
+    tmp = ((int16_t)le16_to_cpu((sample & s->outmask) & 0xFFFF) *
+            s->out2vol_mult[1]) >> 16;
+    *data++ = tmp;
+    tmp = ((int16_t)le16_to_cpu((sample & s->outmask) >> 16) *
+            s->out2vol_mult[0]) >> 16;
+    *data = tmp;
     s->req_out -= 4;
     s->idx_out += 4;
     if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]