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

Commit e831d80b authored by Russell King's avatar Russell King
Browse files

ALSA: AACI: fix number of channels for record



AC'97 codecs only support two channels for recording, so we shouldn't
advertize that there are up to six channels available.  Limit the
selection of 4 and 6 channel audio to playback only.

As this adds additional SNDRV_PCM_STREAM_PLAYBACK conditionals, we can
combine some resulting in the elimination of __aaci_pcm_open() entirely,
and making the code easier to read.

Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent b60fb519
Loading
Loading
Loading
Loading
+52 −62
Original line number Diff line number Diff line
@@ -357,7 +357,7 @@ static struct snd_pcm_hardware aaci_hw_info = {

	/* rates are setup from the AC'97 codec */
	.channels_min		= 2,
	.channels_max		= 6,
	.channels_max		= 2,
	.buffer_bytes_max	= 64 * 1024,
	.period_bytes_min	= 256,
	.period_bytes_max	= PAGE_SIZE,
@@ -365,22 +365,67 @@ static struct snd_pcm_hardware aaci_hw_info = {
	.periods_max		= PAGE_SIZE / 16,
};

static int __aaci_pcm_open(struct aaci *aaci,
			   struct snd_pcm_substream *substream,
			   struct aaci_runtime *aacirun)
/*
 * We can support two and four channel audio.  Unfortunately
 * six channel audio requires a non-standard channel ordering:
 *   2 -> FL(3), FR(4)
 *   4 -> FL(3), FR(4), SL(7), SR(8)
 *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
 *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
 * This requires an ALSA configuration file to correct.
 */
static int aaci_rule_channels(struct snd_pcm_hw_params *p,
	struct snd_pcm_hw_rule *rule)
{
	static unsigned int channel_list[] = { 2, 4, 6 };
	struct aaci *aaci = rule->private;
	unsigned int mask = 1 << 0, slots;

	/* pcms[0] is the our 5.1 PCM instance. */
	slots = aaci->ac97_bus->pcms[0].r[0].slots;
	if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
		mask |= 1 << 1;
		if (slots & (1 << AC97_SLOT_LFE))
			mask |= 1 << 2;
	}

	return snd_interval_list(hw_param_interval(p, rule->var),
				 ARRAY_SIZE(channel_list), channel_list, mask);
}

static int aaci_pcm_open(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct aaci *aaci = substream->private_data;
	struct aaci_runtime *aacirun;
	int ret = 0;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		aacirun = &aaci->playback;
	} else {
		aacirun = &aaci->capture;
	}

	aacirun->substream = substream;
	runtime->private_data = aacirun;
	runtime->hw = aaci_hw_info;
	runtime->hw.rates = aacirun->pcm->rates;
	snd_pcm_limit_hw_rates(runtime);

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
	    aacirun->pcm->r[1].slots)
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		runtime->hw.channels_max = 6;

		/* Add rule describing channel dependency. */
		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
					  SNDRV_PCM_HW_PARAM_CHANNELS,
					  aaci_rule_channels, aaci,
					  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
		if (ret)
			return ret;

		if (aacirun->pcm->r[1].slots)
			snd_ac97_pcm_double_rate_rules(runtime);
	}

	/*
	 * FIXME: ALSA specifies fifo_size in bytes.  If we're in normal
@@ -512,61 +557,6 @@ static const u32 channels_to_txmask[] = {
	[6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
};

/*
 * We can support two and four channel audio.  Unfortunately
 * six channel audio requires a non-standard channel ordering:
 *   2 -> FL(3), FR(4)
 *   4 -> FL(3), FR(4), SL(7), SR(8)
 *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
 *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
 * This requires an ALSA configuration file to correct.
 */
static unsigned int channel_list[] = { 2, 4, 6 };

static int
aaci_rule_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
{
	struct aaci *aaci = rule->private;
	unsigned int chan_mask = 1 << 0, slots;

	/*
	 * pcms[0] is the our 5.1 PCM instance.
	 */
	slots = aaci->ac97_bus->pcms[0].r[0].slots;
	if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
		chan_mask |= 1 << 1;
		if (slots & (1 << AC97_SLOT_LFE))
			chan_mask |= 1 << 2;
	}

	return snd_interval_list(hw_param_interval(p, rule->var),
				 ARRAY_SIZE(channel_list), channel_list,
				 chan_mask);
}

static int aaci_pcm_open(struct snd_pcm_substream *substream)
{
	struct aaci *aaci = substream->private_data;
	int ret;

	/*
	 * Add rule describing channel dependency.
	 */
	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
				  SNDRV_PCM_HW_PARAM_CHANNELS,
				  aaci_rule_channels, aaci,
				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
	if (ret)
		return ret;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		ret = __aaci_pcm_open(aaci, substream, &aaci->playback);
	} else {
		ret = __aaci_pcm_open(aaci, substream, &aaci->capture);
	}
	return ret;
}

static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
				       struct snd_pcm_hw_params *params)
{