diff options
Diffstat (limited to 'sound/soc/soc-core.c')
| -rw-r--r-- | sound/soc/soc-core.c | 328 | 
1 files changed, 264 insertions, 64 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b37ee8077ed1..f81c5976b961 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -812,13 +812,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)  	/* Find CPU DAI from registered DAIs*/  	list_for_each_entry(cpu_dai, &dai_list, list) { -		if (dai_link->cpu_dai_of_node) { -			if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node) -				continue; -		} else { -			if (strcmp(cpu_dai->name, dai_link->cpu_dai_name)) -				continue; -		} +		if (dai_link->cpu_of_node && +		    (cpu_dai->dev->of_node != dai_link->cpu_of_node)) +			continue; +		if (dai_link->cpu_name && +		    strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name)) +			continue; +		if (dai_link->cpu_dai_name && +		    strcmp(cpu_dai->name, dai_link->cpu_dai_name)) +			continue;  		rtd->cpu_dai = cpu_dai;  	} @@ -896,6 +898,28 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)  	return 0;  } +static int soc_remove_platform(struct snd_soc_platform *platform) +{ +	int ret; + +	if (platform->driver->remove) { +		ret = platform->driver->remove(platform); +		if (ret < 0) +			pr_err("asoc: failed to remove %s: %d\n", +				platform->name, ret); +	} + +	/* Make sure all DAPM widgets are freed */ +	snd_soc_dapm_free(&platform->dapm); + +	soc_cleanup_platform_debugfs(platform); +	platform->probed = 0; +	list_del(&platform->card_list); +	module_put(platform->dev->driver->owner); + +	return 0; +} +  static void soc_remove_codec(struct snd_soc_codec *codec)  {  	int err; @@ -917,11 +941,9 @@ static void soc_remove_codec(struct snd_soc_codec *codec)  	module_put(codec->dev->driver->owner);  } -static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) +static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)  {  	struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; -	struct snd_soc_codec *codec = rtd->codec; -	struct snd_soc_platform *platform = rtd->platform;  	struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;  	int err; @@ -946,30 +968,6 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)  		list_del(&codec_dai->card_list);  	} -	/* remove the platform */ -	if (platform && platform->probed && -			platform->driver->remove_order == order) { -		if (platform->driver->remove) { -			err = platform->driver->remove(platform); -			if (err < 0) -				pr_err("asoc: failed to remove %s: %d\n", -							platform->name, err); -		} - -		/* Make sure all DAPM widgets are freed */ -		snd_soc_dapm_free(&platform->dapm); - -		soc_cleanup_platform_debugfs(platform); -		platform->probed = 0; -		list_del(&platform->card_list); -		module_put(platform->dev->driver->owner); -	} - -	/* remove the CODEC */ -	if (codec && codec->probed && -			codec->driver->remove_order == order) -		soc_remove_codec(codec); -  	/* remove the cpu_dai */  	if (cpu_dai && cpu_dai->probed &&  			cpu_dai->driver->remove_order == order) { @@ -981,7 +979,43 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)  		}  		cpu_dai->probed = 0;  		list_del(&cpu_dai->card_list); -		module_put(cpu_dai->dev->driver->owner); + +		if (!cpu_dai->codec) { +			snd_soc_dapm_free(&cpu_dai->dapm); +			module_put(cpu_dai->dev->driver->owner); +		} +	} +} + +static void soc_remove_link_components(struct snd_soc_card *card, int num, +				       int order) +{ +	struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	struct snd_soc_dai *codec_dai = rtd->codec_dai; +	struct snd_soc_platform *platform = rtd->platform; +	struct snd_soc_codec *codec; + +	/* remove the platform */ +	if (platform && platform->probed && +	    platform->driver->remove_order == order) { +		soc_remove_platform(platform); +	} + +	/* remove the CODEC-side CODEC */ +	if (codec_dai) { +		codec = codec_dai->codec; +		if (codec && codec->probed && +		    codec->driver->remove_order == order) +			soc_remove_codec(codec); +	} + +	/* remove any CPU-side CODEC */ +	if (cpu_dai) { +		codec = cpu_dai->codec; +		if (codec && codec->probed && +		    codec->driver->remove_order == order) +			soc_remove_codec(codec);  	}  } @@ -992,8 +1026,15 @@ static void soc_remove_dai_links(struct snd_soc_card *card)  	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;  			order++) {  		for (dai = 0; dai < card->num_rtd; dai++) -			soc_remove_dai_link(card, dai, order); +			soc_remove_link_dais(card, dai, order);  	} + +	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; +			order++) { +		for (dai = 0; dai < card->num_rtd; dai++) +			soc_remove_link_components(card, dai, order); +	} +  	card->num_rtd = 0;  } @@ -1054,6 +1095,10 @@ static int soc_probe_codec(struct snd_soc_card *card,  		}  	} +	/* If the driver didn't set I/O up try regmap */ +	if (!codec->write && dev_get_regmap(codec->dev, NULL)) +		snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); +  	if (driver->controls)  		snd_soc_add_codec_controls(codec, driver->controls,  				     driver->num_controls); @@ -1230,7 +1275,44 @@ out:  	return 0;  } -static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) +static int soc_probe_link_components(struct snd_soc_card *card, int num, +				     int order) +{ +	struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	struct snd_soc_dai *codec_dai = rtd->codec_dai; +	struct snd_soc_platform *platform = rtd->platform; +	int ret; + +	/* probe the CPU-side component, if it is a CODEC */ +	if (cpu_dai->codec && +	    !cpu_dai->codec->probed && +	    cpu_dai->codec->driver->probe_order == order) { +		ret = soc_probe_codec(card, cpu_dai->codec); +		if (ret < 0) +			return ret; +	} + +	/* probe the CODEC-side component */ +	if (!codec_dai->codec->probed && +	    codec_dai->codec->driver->probe_order == order) { +		ret = soc_probe_codec(card, codec_dai->codec); +		if (ret < 0) +			return ret; +	} + +	/* probe the platform */ +	if (!platform->probed && +	    platform->driver->probe_order == order) { +		ret = soc_probe_platform(card, platform); +		if (ret < 0) +			return ret; +	} + +	return 0; +} + +static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)  {  	struct snd_soc_dai_link *dai_link = &card->dai_link[num];  	struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; @@ -1255,11 +1337,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)  	/* probe the cpu_dai */  	if (!cpu_dai->probed &&  			cpu_dai->driver->probe_order == order) { -		cpu_dai->dapm.card = card; -		if (!try_module_get(cpu_dai->dev->driver->owner)) -			return -ENODEV; +		if (!cpu_dai->codec) { +			cpu_dai->dapm.card = card; +			if (!try_module_get(cpu_dai->dev->driver->owner)) +				return -ENODEV; -		snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); +			list_add(&cpu_dai->dapm.list, &card->dapm_list); +			snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); +		}  		if (cpu_dai->driver->probe) {  			ret = cpu_dai->driver->probe(cpu_dai); @@ -1275,22 +1360,6 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)  		list_add(&cpu_dai->card_list, &card->dai_dev_list);  	} -	/* probe the CODEC */ -	if (!codec->probed && -			codec->driver->probe_order == order) { -		ret = soc_probe_codec(card, codec); -		if (ret < 0) -			return ret; -	} - -	/* probe the platform */ -	if (!platform->probed && -			platform->driver->probe_order == order) { -		ret = soc_probe_platform(card, platform); -		if (ret < 0) -			return ret; -	} -  	/* probe the CODEC DAI */  	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {  		if (codec_dai->driver->probe) { @@ -1565,14 +1634,27 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)  			goto card_probe_error;  	} -	/* early DAI link probe */ +	/* probe all components used by DAI links on this card */  	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;  			order++) {  		for (i = 0; i < card->num_links; i++) { -			ret = soc_probe_dai_link(card, i, order); +			ret = soc_probe_link_components(card, i, order);  			if (ret < 0) {  				pr_err("asoc: failed to instantiate card %s: %d\n", -			       card->name, ret); +				       card->name, ret); +				goto probe_dai_err; +			} +		} +	} + +	/* probe all DAI links on this card */ +	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; +			order++) { +		for (i = 0; i < card->num_links; i++) { +			ret = soc_probe_link_dais(card, i, order); +			if (ret < 0) { +				pr_err("asoc: failed to instantiate card %s: %d\n", +				       card->name, ret);  				goto probe_dai_err;  			}  		} @@ -2790,6 +2872,104 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,  EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);  /** + * snd_soc_info_volsw_range - single mixer info callback with range. + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information, within a range, about a single + * mixer control. + * + * returns 0 for success. + */ +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_info *uinfo) +{ +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	int platform_max; +	int min = mc->min; + +	if (!mc->platform_max) +		mc->platform_max = mc->max; +	platform_max = mc->platform_max; + +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +	uinfo->count = 1; +	uinfo->value.integer.min = 0; +	uinfo->value.integer.max = platform_max - min; + +	return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); + +/** + * snd_soc_put_volsw_range - single mixer put value callback with range. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value, within a range, for a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +	unsigned int reg = mc->reg; +	unsigned int shift = mc->shift; +	int min = mc->min; +	int max = mc->max; +	unsigned int mask = (1 << fls(max)) - 1; +	unsigned int invert = mc->invert; +	unsigned int val, val_mask; + +	val = ((ucontrol->value.integer.value[0] + min) & mask); +	if (invert) +		val = max - val; +	val_mask = mask << shift; +	val = val << shift; + +	return snd_soc_update_bits_locked(codec, reg, val_mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); + +/** + * snd_soc_get_volsw_range - single mixer get callback with range + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value, within a range, of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +	unsigned int reg = mc->reg; +	unsigned int shift = mc->shift; +	int min = mc->min; +	int max = mc->max; +	unsigned int mask = (1 << fls(max)) - 1; +	unsigned int invert = mc->invert; + +	ucontrol->value.integer.value[0] = +		(snd_soc_read(codec, reg) >> shift) & mask; +	if (invert) +		ucontrol->value.integer.value[0] = +			max - ucontrol->value.integer.value[0]; +	ucontrol->value.integer.value[0] = +		ucontrol->value.integer.value[0] - min; + +	return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); + +/**   * snd_soc_limit_volume - Set new limit to an existing volume control.   *   * @codec: where to look for the control @@ -3346,6 +3526,12 @@ int snd_soc_register_card(struct snd_soc_card *card)  				link->name);  			return -EINVAL;  		} +		/* Codec DAI name must be specified */ +		if (!link->codec_dai_name) { +			dev_err(card->dev, "codec_dai_name not set for %s\n", +				link->name); +			return -EINVAL; +		}  		/*  		 * Platform may be specified by either name or OF node, but @@ -3358,12 +3544,24 @@ int snd_soc_register_card(struct snd_soc_card *card)  		}  		/* -		 * CPU DAI must be specified by 1 of name or OF node, -		 * not both or neither. +		 * CPU device may be specified by either name or OF node, but +		 * can be left unspecified, and will be matched based on DAI +		 * name alone.. +		 */ +		if (link->cpu_name && link->cpu_of_node) { +			dev_err(card->dev, +				"Neither/both cpu name/of_node are set for %s\n", +				link->name); +			return -EINVAL; +		} +		/* +		 * At least one of CPU DAI name or CPU device name/node must be +		 * specified  		 */ -		if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) { +		if (!link->cpu_dai_name && +		    !(link->cpu_name || link->cpu_of_node)) {  			dev_err(card->dev, -				"Neither/both cpu_dai name/of_node are set for %s\n", +				"Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",  				link->name);  			return -EINVAL;  		} @@ -3938,6 +4136,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,  			dev_err(card->dev,  				"Property '%s' index %d could not be read: %d\n",  				propname, 2 * i, ret); +			kfree(routes);  			return -EINVAL;  		}  		ret = of_property_read_string_index(np, propname, @@ -3946,6 +4145,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,  			dev_err(card->dev,  				"Property '%s' index %d could not be read: %d\n",  				propname, (2 * i) + 1, ret); +			kfree(routes);  			return -EINVAL;  		}  	}  | 
