Commit 3d475829 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: ca0106 - Add IEC958 PCM Stream controls

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: default avatarTakashi Iwai <tiwai@suse.de>
parent 86effd7e
...@@ -690,7 +690,8 @@ struct snd_ca0106 { ...@@ -690,7 +690,8 @@ struct snd_ca0106 {
struct snd_ca0106_channel playback_channels[4]; struct snd_ca0106_channel playback_channels[4];
struct snd_ca0106_channel capture_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 spdif_enable;
int capture_source; int capture_source;
int i2c_capture_source; int i2c_capture_source;
......
...@@ -479,6 +479,15 @@ static const int spi_dacd_bit[] = { ...@@ -479,6 +479,15 @@ static const int spi_dacd_bit[] = {
[PCM_UNKNOWN_CHANNEL] = SPI_DACD1_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 */ /* open_playback callback */
static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream, static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
int channel_id) int channel_id)
...@@ -524,6 +533,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr ...@@ -524,6 +533,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
if (err < 0) if (err < 0)
return err; return err;
} }
restore_spdif_bits(chip, channel_id);
return 0; return 0;
} }
...@@ -535,6 +547,8 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream) ...@@ -535,6 +547,8 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
struct snd_ca0106_pcm *epcm = runtime->private_data; struct snd_ca0106_pcm *epcm = runtime->private_data;
chip->playback_channels[epcm->channel_id].use = 0; 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) { if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
const int reg = spi_dacd_reg[epcm->channel_id]; const int reg = spi_dacd_reg[epcm->channel_id];
...@@ -1330,16 +1344,16 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) ...@@ -1330,16 +1344,16 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_GENERATIONSTATUS | 0x00001200 |
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
if (!resume) { if (!resume) {
chip->spdif_bits[0] = def_bits; chip->spdif_str_bits[0] = chip->spdif_bits[0] = def_bits;
chip->spdif_bits[1] = def_bits; chip->spdif_str_bits[1] = chip->spdif_bits[1] = def_bits;
chip->spdif_bits[2] = def_bits; chip->spdif_str_bits[2] = chip->spdif_bits[2] = def_bits;
chip->spdif_bits[3] = def_bits; chip->spdif_str_bits[3] = chip->spdif_bits[3] = def_bits;
} }
/* Only SPCS1 has been tested */ /* Only SPCS1 has been tested */
snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_bits[1]); snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_str_bits[1]);
snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_bits[0]); snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_str_bits[0]);
snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_bits[2]); snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_str_bits[2]);
snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_bits[3]); 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, PLAYBACK_MUTE, 0, 0x00fc0000);
snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000); snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
......
...@@ -148,7 +148,7 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) ...@@ -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) 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, ...@@ -353,16 +353,33 @@ static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
return 0; 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_ctl_elem_value *ucontrol)
{ {
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; decode_spdif_bits(ucontrol->value.iec958.status,
ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; emu->spdif_bits[idx]);
ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; return 0;
ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; }
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; return 0;
} }
...@@ -376,24 +393,48 @@ static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, ...@@ -376,24 +393,48 @@ static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0; 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_ctl_elem_value *ucontrol)
{ {
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
int change;
unsigned int val; unsigned int val;
val = (ucontrol->value.iec958.status[0] << 0) | val = encode_spdif_bits(ucontrol->value.iec958.status);
(ucontrol->value.iec958.status[1] << 8) | if (val != emu->spdif_bits[idx]) {
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
change = val != emu->spdif_bits[idx];
if (change) {
emu->spdif_bits[idx] = val; 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); 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, static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
...@@ -604,8 +645,16 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { ...@@ -604,8 +645,16 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.count = 4, .count = 4,
.info = snd_ca0106_spdif_info, .info = snd_ca0106_spdif_info,
.get = snd_ca0106_spdif_get, .get = snd_ca0106_spdif_get_default,
.put = snd_ca0106_spdif_put .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
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment