ALSA: ca0106 - Add IEC958 PCM Stream controls
authorTakashi Iwai <tiwai@suse.de>
Fri, 19 Dec 2008 11:13:18 +0000 (12:13 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 19 Dec 2008 11:13:18 +0000 (12:13 +0100)
Added "IEC958 PCM Stream" controls for the per-stream IEC958 status
bits.  Using this instead of "IEC958 Default" is safer since the status
bits will be recovered to the default states after closing the PCM
stream.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/ca0106/ca0106.h
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c

index 1c14ff424116e20a734ec074db62398e188ec675..ec0f17ded4e01d7adabaf683b87dd6e25a7de8ca 100644 (file)
@@ -690,7 +690,8 @@ struct snd_ca0106 {
 
        struct snd_ca0106_channel playback_channels[4];
        struct snd_ca0106_channel capture_channels[4];
-       u32 spdif_bits[4];             /* s/pdif out setup */
+       u32 spdif_bits[4];             /* s/pdif out default setup */
+       u32 spdif_str_bits[4];         /* s/pdif out per-stream setup */
        int spdif_enable;
        int capture_source;
        int i2c_capture_source;
index 2c71f9b896cd1c7d860789ac579dec3ef58d8814..c27fd90101d6651ab0ddfe8d0431ad95b9e6e7d0 100644 (file)
@@ -479,6 +479,15 @@ static const int spi_dacd_bit[] = {
        [PCM_UNKNOWN_CHANNEL]   = SPI_DACD1_BIT,
 };
 
+static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
+{
+       if (chip->spdif_str_bits[idx] != chip->spdif_bits[idx]) {
+               chip->spdif_str_bits[idx] = chip->spdif_bits[idx];
+               snd_ca0106_ptr_write(chip, SPCS0 + idx, 0,
+                                    chip->spdif_str_bits[idx]);
+       }
+}
+
 /* open_playback callback */
 static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
                                                int channel_id)
@@ -524,6 +533,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
                if (err < 0)
                        return err;
        }
+
+       restore_spdif_bits(chip, channel_id);
+
        return 0;
 }
 
@@ -535,6 +547,8 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
         struct snd_ca0106_pcm *epcm = runtime->private_data;
        chip->playback_channels[epcm->channel_id].use = 0;
 
+       restore_spdif_bits(chip, epcm->channel_id);
+
        if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
                const int reg = spi_dacd_reg[epcm->channel_id];
 
@@ -1330,16 +1344,16 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
                SPCS_GENERATIONSTATUS | 0x00001200 |
                0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
        if (!resume) {
-               chip->spdif_bits[0] = def_bits;
-               chip->spdif_bits[1] = def_bits;
-               chip->spdif_bits[2] = def_bits;
-               chip->spdif_bits[3] = def_bits;
+               chip->spdif_str_bits[0] = chip->spdif_bits[0] = def_bits;
+               chip->spdif_str_bits[1] = chip->spdif_bits[1] = def_bits;
+               chip->spdif_str_bits[2] = chip->spdif_bits[2] = def_bits;
+               chip->spdif_str_bits[3] = chip->spdif_bits[3] = def_bits;
        }
        /* Only SPCS1 has been tested */
-       snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_bits[1]);
-       snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_bits[0]);
-       snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_bits[2]);
-       snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_bits[3]);
+       snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_str_bits[1]);
+       snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_str_bits[0]);
+       snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_str_bits[2]);
+       snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_str_bits[3]);
 
         snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
         snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
index cccc32cdb943029ff4beee1b938cd11dace16c66..8727881a10b81c05c8fca9fb3e393e98bef1f454 100644 (file)
@@ -148,7 +148,7 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
 
 static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
 {
-       snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_bits[idx]);
+       snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
 }
 
 /*
@@ -353,16 +353,33 @@ static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
+static void decode_spdif_bits(unsigned char *status, unsigned int bits)
+{
+       status[0] = (bits >> 0) & 0xff;
+       status[1] = (bits >> 8) & 0xff;
+       status[2] = (bits >> 16) & 0xff;
+       status[3] = (bits >> 24) & 0xff;
+}
+
+static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
-       ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
-       ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
-       ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
-       ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+       decode_spdif_bits(ucontrol->value.iec958.status,
+                         emu->spdif_bits[idx]);
+        return 0;
+}
+
+static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       decode_spdif_bits(ucontrol->value.iec958.status,
+                         emu->spdif_str_bits[idx]);
         return 0;
 }
 
@@ -376,24 +393,48 @@ static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
         return 0;
 }
 
-static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
+static unsigned int encode_spdif_bits(unsigned char *status)
+{
+       return ((unsigned int)status[0] << 0) |
+               ((unsigned int)status[1] << 8) |
+               ((unsigned int)status[2] << 16) |
+               ((unsigned int)status[3] << 24);
+}
+
+static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       int change;
        unsigned int val;
 
-       val = (ucontrol->value.iec958.status[0] << 0) |
-             (ucontrol->value.iec958.status[1] << 8) |
-             (ucontrol->value.iec958.status[2] << 16) |
-             (ucontrol->value.iec958.status[3] << 24);
-       change = val != emu->spdif_bits[idx];
-       if (change) {
+       val = encode_spdif_bits(ucontrol->value.iec958.status);
+       if (val != emu->spdif_bits[idx]) {
                emu->spdif_bits[idx] = val;
+               /* FIXME: this isn't safe, but needed to keep the compatibility
+                * with older alsa-lib config
+                */
+               emu->spdif_str_bits[idx] = val;
                ca0106_set_spdif_bits(emu, idx);
+               return 1;
        }
-        return change;
+       return 0;
+}
+
+static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       unsigned int val;
+
+       val = encode_spdif_bits(ucontrol->value.iec958.status);
+       if (val != emu->spdif_str_bits[idx]) {
+               emu->spdif_str_bits[idx] = val;
+               ca0106_set_spdif_bits(emu, idx);
+               return 1;
+       }
+        return 0;
 }
 
 static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
@@ -604,8 +645,16 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
                .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
                .count =        4,
                .info =         snd_ca0106_spdif_info,
-               .get =          snd_ca0106_spdif_get,
-               .put =          snd_ca0106_spdif_put
+               .get =          snd_ca0106_spdif_get_default,
+               .put =          snd_ca0106_spdif_put_default
+       },
+       {
+               .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+               .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+               .count =        4,
+               .info =         snd_ca0106_spdif_info,
+               .get =          snd_ca0106_spdif_get_stream,
+               .put =          snd_ca0106_spdif_put_stream
        },
 };