diff options
Diffstat (limited to 'sound/soc/soc-core.c')
| -rw-r--r-- | sound/soc/soc-core.c | 165 | 
1 files changed, 104 insertions, 61 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1cd149b9ce6..3f44150d8e3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -113,6 +113,35 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)  }  #endif +static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_card *card = socdev->card; +	struct snd_soc_dai_link *machine = rtd->dai; +	struct snd_soc_dai *cpu_dai = machine->cpu_dai; +	struct snd_soc_dai *codec_dai = machine->codec_dai; +	int ret; + +	if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates || +	    machine->symmetric_rates) { +		dev_dbg(card->dev, "Symmetry forces %dHz rate\n",  +			machine->rate); + +		ret = snd_pcm_hw_constraint_minmax(substream->runtime, +						   SNDRV_PCM_HW_PARAM_RATE, +						   machine->rate, +						   machine->rate); +		if (ret < 0) { +			dev_err(card->dev, +				"Unable to apply rate symmetry constraint: %d\n", ret); +			return ret; +		} +	} + +	return 0; +} +  /*   * Called by ALSA when a PCM substream is opened, the runtime->hw record is   * then initialized and any private data can be allocated. This also calls @@ -221,6 +250,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)  		goto machine_err;  	} +	/* Symmetry only applies if we've already got an active stream. */ +	if (cpu_dai->active || codec_dai->active) { +		ret = soc_pcm_apply_symmetry(substream); +		if (ret != 0) +			goto machine_err; +	} +  	pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);  	pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);  	pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, @@ -263,7 +299,6 @@ static void close_delayed_work(struct work_struct *work)  {  	struct snd_soc_card *card = container_of(work, struct snd_soc_card,  						 delayed_work.work); -	struct snd_soc_device *socdev = card->socdev;  	struct snd_soc_codec *codec = card->codec;  	struct snd_soc_dai *codec_dai;  	int i; @@ -279,27 +314,10 @@ static void close_delayed_work(struct work_struct *work)  		/* are we waiting on this codec DAI stream */  		if (codec_dai->pop_wait == 1) { - -			/* Reduce power if no longer active */ -			if (codec->active == 0) { -				pr_debug("pop wq D1 %s %s\n", codec->name, -					 codec_dai->playback.stream_name); -				snd_soc_dapm_set_bias_level(socdev, -					SND_SOC_BIAS_PREPARE); -			} -  			codec_dai->pop_wait = 0;  			snd_soc_dapm_stream_event(codec,  				codec_dai->playback.stream_name,  				SND_SOC_DAPM_STREAM_STOP); - -			/* Fall into standby if no longer active */ -			if (codec->active == 0) { -				pr_debug("pop wq D3 %s %s\n", codec->name, -					 codec_dai->playback.stream_name); -				snd_soc_dapm_set_bias_level(socdev, -					SND_SOC_BIAS_STANDBY); -			}  		}  	}  	mutex_unlock(&pcm_mutex); @@ -363,10 +381,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream)  		snd_soc_dapm_stream_event(codec,  			codec_dai->capture.stream_name,  			SND_SOC_DAPM_STREAM_STOP); - -		if (codec->active == 0 && codec_dai->pop_wait == 0) -			snd_soc_dapm_set_bias_level(socdev, -						SND_SOC_BIAS_STANDBY);  	}  	mutex_unlock(&pcm_mutex); @@ -431,36 +445,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)  		cancel_delayed_work(&card->delayed_work);  	} -	/* do we need to power up codec */ -	if (codec->bias_level != SND_SOC_BIAS_ON) { -		snd_soc_dapm_set_bias_level(socdev, -					    SND_SOC_BIAS_PREPARE); - -		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -			snd_soc_dapm_stream_event(codec, -					codec_dai->playback.stream_name, -					SND_SOC_DAPM_STREAM_START); -		else -			snd_soc_dapm_stream_event(codec, -					codec_dai->capture.stream_name, -					SND_SOC_DAPM_STREAM_START); - -		snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON); -		snd_soc_dai_digital_mute(codec_dai, 0); - -	} else { -		/* codec already powered - power on widgets */ -		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -			snd_soc_dapm_stream_event(codec, -					codec_dai->playback.stream_name, -					SND_SOC_DAPM_STREAM_START); -		else -			snd_soc_dapm_stream_event(codec, -					codec_dai->capture.stream_name, -					SND_SOC_DAPM_STREAM_START); +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		snd_soc_dapm_stream_event(codec, +					  codec_dai->playback.stream_name, +					  SND_SOC_DAPM_STREAM_START); +	else +		snd_soc_dapm_stream_event(codec, +					  codec_dai->capture.stream_name, +					  SND_SOC_DAPM_STREAM_START); -		snd_soc_dai_digital_mute(codec_dai, 0); -	} +	snd_soc_dai_digital_mute(codec_dai, 0);  out:  	mutex_unlock(&pcm_mutex); @@ -521,6 +515,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,  		}  	} +	machine->rate = params_rate(params); +  out:  	mutex_unlock(&pcm_mutex);  	return ret; @@ -632,6 +628,12 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)  	struct snd_soc_codec *codec = card->codec;  	int i; +	/* If the initialization of this soc device failed, there is no codec +	 * associated with it. Just bail out in this case. +	 */ +	if (!codec) +		return 0; +  	/* Due to the resume being scheduled into a workqueue we could  	* suspend before that's finished - wait for it to complete.  	 */ @@ -1334,6 +1336,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)  		return ret;  	} +	codec->socdev = socdev;  	codec->card->dev = socdev->dev;  	codec->card->private_data = codec;  	strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver)); @@ -1744,7 +1747,7 @@ int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,  {  	int max = kcontrol->private_value; -	if (max == 1) +	if (max == 1 && !strstr(kcontrol->id.name, " Volume"))  		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;  	else  		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -1774,7 +1777,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,  	unsigned int shift = mc->shift;  	unsigned int rshift = mc->rshift; -	if (max == 1) +	if (max == 1 && !strstr(kcontrol->id.name, " Volume"))  		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;  	else  		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -1881,7 +1884,7 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,  		(struct soc_mixer_control *)kcontrol->private_value;  	int max = mc->max; -	if (max == 1) +	if (max == 1 && !strstr(kcontrol->id.name, " Volume"))  		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;  	else  		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -2065,7 +2068,7 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);  int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,  	unsigned int freq, int dir)  { -	if (dai->ops->set_sysclk) +	if (dai->ops && dai->ops->set_sysclk)  		return dai->ops->set_sysclk(dai, clk_id, freq, dir);  	else  		return -EINVAL; @@ -2085,7 +2088,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);  int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,  	int div_id, int div)  { -	if (dai->ops->set_clkdiv) +	if (dai->ops && dai->ops->set_clkdiv)  		return dai->ops->set_clkdiv(dai, div_id, div);  	else  		return -EINVAL; @@ -2104,7 +2107,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);  int snd_soc_dai_set_pll(struct snd_soc_dai *dai,  	int pll_id, unsigned int freq_in, unsigned int freq_out)  { -	if (dai->ops->set_pll) +	if (dai->ops && dai->ops->set_pll)  		return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);  	else  		return -EINVAL; @@ -2120,7 +2123,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);   */  int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)  { -	if (dai->ops->set_fmt) +	if (dai->ops && dai->ops->set_fmt)  		return dai->ops->set_fmt(dai, fmt);  	else  		return -EINVAL; @@ -2139,7 +2142,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);  int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,  	unsigned int mask, int slots)  { -	if (dai->ops->set_sysclk) +	if (dai->ops && dai->ops->set_tdm_slot)  		return dai->ops->set_tdm_slot(dai, mask, slots);  	else  		return -EINVAL; @@ -2155,7 +2158,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);   */  int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)  { -	if (dai->ops->set_sysclk) +	if (dai->ops && dai->ops->set_tristate)  		return dai->ops->set_tristate(dai, tristate);  	else  		return -EINVAL; @@ -2171,7 +2174,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);   */  int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)  { -	if (dai->ops->digital_mute) +	if (dai->ops && dai->ops->digital_mute)  		return dai->ops->digital_mute(dai, mute);  	else  		return -EINVAL; @@ -2352,6 +2355,39 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform)  }  EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); +static u64 codec_format_map[] = { +	SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE, +	SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE, +	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE, +	SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE, +	SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, +	SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE, +	SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, +	SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, +	SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE, +	SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE, +	SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE, +	SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE, +	SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE, +	SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE, +	SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE +	| SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, +}; + +/* Fix up the DAI formats for endianness: codecs don't actually see + * the endianness of the data but we're using the CPU format + * definitions which do need to include endianness so we ensure that + * codec DAIs always have both big and little endian variants set. + */ +static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(codec_format_map); i++) +		if (stream->formats & codec_format_map[i]) +			stream->formats |= codec_format_map[i]; +} +  /**   * snd_soc_register_codec - Register a codec with the ASoC core   * @@ -2359,6 +2395,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);   */  int snd_soc_register_codec(struct snd_soc_codec *codec)  { +	int i; +  	if (!codec->name)  		return -EINVAL; @@ -2368,6 +2406,11 @@ int snd_soc_register_codec(struct snd_soc_codec *codec)  	INIT_LIST_HEAD(&codec->list); +	for (i = 0; i < codec->num_dai; i++) { +		fixup_codec_formats(&codec->dai[i].playback); +		fixup_codec_formats(&codec->dai[i].capture); +	} +  	mutex_lock(&client_mutex);  	list_add(&codec->list, &codec_list);  	snd_soc_instantiate_cards();  |