[PARISC] Update harmony from parisc tree
Stuart Brady [Sat, 22 Oct 2005 02:42:38 +0000 (22:42 -0400)]
o Added a control for the input source (which can be either
  "line" or "mic")

o Mute the speaker/line-out/headphone outputs by default.

o Increased the buffer size from 10 pages to 16.

Signed-off-by: Stuart Brady <sdb@parisc-linux.org>

ALSA Harmony was resetting the capture position when
preparing the capture substream, which it shouldn't do.
This should fix the problem.

Signed-off-by: Stuart Brady <sdb@parisc-linux.org>

ALSA Harmony should no longer play junk (left in the buffers
from a previous stream) at the start of a new stream.

Implement the monitor mixer channel for ALSA Harmony.

Also prevent snd_harmony_volume_get from returning negative values.

Signed-off-by: Stuart Brady <sdb@parisc-linux.org>

Use the graveyard/silence buffers in ALSA Harmony.

Signed-off-by: Stuart Brady <sdb@parisc-linux.org>

Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>

sound/parisc/harmony.c
sound/parisc/harmony.h

index 8b3ea26..d833349 100644 (file)
@@ -197,7 +197,7 @@ snd_harmony_interrupt(int irq, void *dev, struct pt_regs *regs)
        spin_unlock(&h->lock);
 
        if (dstatus & HARMONY_DSTATUS_PN) {
-               if (h->psubs) {
+               if (h->psubs && h->st.playing) {
                        spin_lock(&h->lock);
                        h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
                        h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
@@ -216,7 +216,7 @@ snd_harmony_interrupt(int irq, void *dev, struct pt_regs *regs)
        }
 
        if (dstatus & HARMONY_DSTATUS_RN) {
-               if (h->csubs) {
+               if (h->csubs && h->st.capturing) {
                        spin_lock(&h->lock);
                        h->cbuf.buf += h->cbuf.count;
                        h->cbuf.buf %= h->cbuf.size;
@@ -316,6 +316,7 @@ snd_harmony_playback_trigger(snd_pcm_substream_t *ss, int cmd)
        case SNDRV_PCM_TRIGGER_STOP:
                h->st.playing = 0;
                harmony_mute(h);
+               harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
                harmony_disable_interrupts(h);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -351,8 +352,9 @@ snd_harmony_capture_trigger(snd_pcm_substream_t *ss, int cmd)
                break;
         case SNDRV_PCM_TRIGGER_STOP:
                h->st.capturing = 0;
-                harmony_mute(h);
-                harmony_disable_interrupts(h);
+               harmony_mute(h);
+               harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
+               harmony_disable_interrupts(h);
                break;
         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -408,7 +410,8 @@ snd_harmony_playback_prepare(snd_pcm_substream_t *ss)
        
        h->pbuf.size = snd_pcm_lib_buffer_bytes(ss);
        h->pbuf.count = snd_pcm_lib_period_bytes(ss);
-       h->pbuf.buf = 0;
+       if (h->pbuf.buf >= h->pbuf.size)
+               h->pbuf.buf = 0;
        h->st.playing = 0;
 
        h->st.rate = snd_harmony_rate_bits(rt->rate);
@@ -437,7 +440,8 @@ snd_harmony_capture_prepare(snd_pcm_substream_t *ss)
 
         h->cbuf.size = snd_pcm_lib_buffer_bytes(ss);
         h->cbuf.count = snd_pcm_lib_period_bytes(ss);
-        h->cbuf.buf = 0;
+       if (h->cbuf.buf >= h->cbuf.size)
+               h->cbuf.buf = 0;
        h->st.capturing = 0;
 
         h->st.rate = snd_harmony_rate_bits(rt->rate);
@@ -712,13 +716,14 @@ snd_harmony_volume_get(snd_kcontrol_t *kc,
 
        left = (h->st.gain >> shift_left) & mask;
        right = (h->st.gain >> shift_right) & mask;
-
        if (invert) {
                left = mask - left;
                right = mask - right;
        }
+       
        ucontrol->value.integer.value[0] = left;
-       ucontrol->value.integer.value[1] = right;
+       if (shift_left != shift_right)
+               ucontrol->value.integer.value[1] = right;
 
        spin_unlock_irqrestore(&h->mixer_lock, flags);
 
@@ -738,22 +743,82 @@ snd_harmony_volume_put(snd_kcontrol_t *kc,
        int old_gain = h->st.gain;
        unsigned long flags;
        
+       spin_lock_irqsave(&h->mixer_lock, flags);
+
        left = ucontrol->value.integer.value[0] & mask;
-       right = ucontrol->value.integer.value[1] & mask;
-       if (invert) {
+       if (invert)
                left = mask - left;
-               right = mask - right;
+       h->st.gain &= ~( (mask << shift_left ) );
+       h->st.gain |= (left << shift_left);
+
+       if (shift_left != shift_right) {
+               right = ucontrol->value.integer.value[1] & mask;
+               if (invert)
+                       right = mask - right;
+               h->st.gain &= ~( (mask << shift_right) );
+               h->st.gain |= (right << shift_right);
        }
+
+       snd_harmony_set_new_gain(h);
+
+       spin_unlock_irqrestore(&h->mixer_lock, flags);
+       
+       return h->st.gain != old_gain;
+}
+
+static int 
+snd_harmony_captureroute_info(snd_kcontrol_t *kc, 
+                             snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[2] = { "Line", "Mic" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int 
+snd_harmony_captureroute_get(snd_kcontrol_t *kc, 
+                            snd_ctl_elem_value_t *ucontrol)
+{
+       harmony_t *h = snd_kcontrol_chip(kc);
+       int value;
+       unsigned long flags;
+       
+       spin_lock_irqsave(&h->mixer_lock, flags);
+
+       value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
+       ucontrol->value.enumerated.item[0] = value;
+
+       spin_unlock_irqrestore(&h->mixer_lock, flags);
+
+       return 0;
+}  
+
+static int 
+snd_harmony_captureroute_put(snd_kcontrol_t *kc, 
+                            snd_ctl_elem_value_t *ucontrol)
+{
+       harmony_t *h = snd_kcontrol_chip(kc);
+       int value;
+       int old_gain = h->st.gain;
+       unsigned long flags;
        
        spin_lock_irqsave(&h->mixer_lock, flags);
 
-       h->st.gain &= ~( (mask << shift_right) | (mask << shift_left) );
-       h->st.gain |=  ( (left << shift_left) | (right << shift_right) );
+       value = ucontrol->value.enumerated.item[0] & 1;
+       h->st.gain &= ~HARMONY_GAIN_IS_MASK;
+       h->st.gain |= value << HARMONY_GAIN_IS_SHIFT;
+
        snd_harmony_set_new_gain(h);
 
        spin_unlock_irqrestore(&h->mixer_lock, flags);
        
-       return (old_gain - h->st.gain);
+       return h->st.gain != old_gain;
 }
 
 #define HARMONY_CONTROLS (sizeof(snd_harmony_controls)/ \
@@ -767,10 +832,25 @@ snd_harmony_volume_put(snd_kcontrol_t *kc,
                    ((mask) << 16) | ((invert) << 24)) }
 
 static snd_kcontrol_new_t snd_harmony_controls[] = {
-       HARMONY_VOLUME("Playback Volume", HARMONY_GAIN_LO_SHIFT, 
+       HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT, 
                       HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
        HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
                       HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0),
+       HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT,
+                      HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Input Route",
+               .info = snd_harmony_captureroute_info,
+               .get = snd_harmony_captureroute_get,
+               .put = snd_harmony_captureroute_put
+       },
+       HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT,
+                      HARMONY_GAIN_SE_SHIFT, 1, 0),
+       HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT,
+                      HARMONY_GAIN_LE_SHIFT, 1, 0),
+       HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT,
+                      HARMONY_GAIN_HE_SHIFT, 1, 0),
 };
 
 static void __init 
index ef77f9a..526c523 100644 (file)
@@ -61,7 +61,7 @@ typedef struct snd_card_harmony {
 #define HARMONY_SIZE       64
 
 #define BUF_SIZE     PAGE_SIZE
-#define MAX_BUFS     10
+#define MAX_BUFS     16
 #define MAX_BUF_SIZE (MAX_BUFS * BUF_SIZE)
 
 #define PLAYBACK_BUFS    MAX_BUFS
@@ -101,28 +101,31 @@ typedef struct snd_card_harmony {
 #define HARMONY_SS_MONO         0x00000000
 #define HARMONY_SS_STEREO       0x00000001
 
-#define HARMONY_GAIN_SILENCE    0x00F00FFF
-#define HARMONY_GAIN_DEFAULT    0x0FF00000
+#define HARMONY_GAIN_SILENCE    0x01F00FFF
+#define HARMONY_GAIN_DEFAULT    0x01F00FFF
 
-#define HARMONY_GAIN_HE_SHIFT   27
+#define HARMONY_GAIN_HE_SHIFT   27 /* headphones enabled */
 #define HARMONY_GAIN_HE_MASK    (1 << HARMONY_GAIN_HE_SHIFT)
-#define HARMONY_GAIN_LE_SHIFT   26
+#define HARMONY_GAIN_LE_SHIFT   26 /* line-out enabled */
 #define HARMONY_GAIN_LE_MASK    (1 << HARMONY_GAIN_LE_SHIFT)
-#define HARMONY_GAIN_SE_SHIFT   25
+#define HARMONY_GAIN_SE_SHIFT   25 /* internal-speaker enabled */
 #define HARMONY_GAIN_SE_MASK    (1 << HARMONY_GAIN_SE_SHIFT)
-#define HARMONY_GAIN_IS_SHIFT   24
+#define HARMONY_GAIN_IS_SHIFT   24 /* input select - 0 for line, 1 for mic */
 #define HARMONY_GAIN_IS_MASK    (1 << HARMONY_GAIN_IS_SHIFT)
 
+/* monitor attenuation */
 #define HARMONY_GAIN_MA         0x0f
 #define HARMONY_GAIN_MA_SHIFT   20
 #define HARMONY_GAIN_MA_MASK    (HARMONY_GAIN_MA << HARMONY_GAIN_MA_SHIFT)
 
+/* input gain */
 #define HARMONY_GAIN_IN         0x0f
 #define HARMONY_GAIN_LI_SHIFT   16
 #define HARMONY_GAIN_LI_MASK    (HARMONY_GAIN_IN << HARMONY_GAIN_LI_SHIFT)
 #define HARMONY_GAIN_RI_SHIFT   12
 #define HARMONY_GAIN_RI_MASK    (HARMONY_GAIN_IN << HARMONY_GAIN_RI_SHIFT)
 
+/* output gain (master volume) */
 #define HARMONY_GAIN_OUT        0x3f
 #define HARMONY_GAIN_LO_SHIFT   6
 #define HARMONY_GAIN_LO_MASK    (HARMONY_GAIN_OUT << HARMONY_GAIN_LO_SHIFT)