Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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;
+22 −8
Original line number Diff line number Diff line
@@ -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);
+66 −17
Original line number Diff line number Diff line
@@ -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);

	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);

	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_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
	},
};