summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2014-12-08 13:11:44 +0000
committerMark Brown <broonie@kernel.org>2014-12-08 13:11:44 +0000
commit941725f5fade7b35394f497c4d684d64a0e05965 (patch)
treef18b21c23255446f4ae8e5783b2637af9b6fb98f /sound/soc
parent3ee3f454632547a8299c16156c3fefcc2a05726c (diff)
parentc362effe5cda4df02aa7670d58636ea73979e304 (diff)
downloadkernel-941725f5fade7b35394f497c4d684d64a0e05965.tar.gz
Merge remote-tracking branch 'asoc/topic/core' into asoc-next
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/soc-core.c1079
-rw-r--r--sound/soc/soc-dapm.c755
-rw-r--r--sound/soc/soc-ops.c952
-rw-r--r--sound/soc/soc-pcm.c3
-rw-r--r--sound/soc/txx9/txx9aclc.c2
6 files changed, 1371 insertions, 1422 deletions
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index d88edfced8c4..2a043804a2bc 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +1,5 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 78a94fc82446..397d00aee43f 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -589,17 +589,12 @@ int snd_soc_suspend(struct device *dev)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_platform *platform = card->rtd[i].platform;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
cpu_dai->driver->suspend(cpu_dai);
- if (platform->driver->suspend && !platform->suspended) {
- platform->driver->suspend(cpu_dai);
- platform->suspended = 1;
- }
}
/* close any waiting streams and save state */
@@ -626,8 +621,8 @@ int snd_soc_suspend(struct device *dev)
SND_SOC_DAPM_STREAM_SUSPEND);
}
- /* Recheck all analogue paths too */
- dapm_mark_io_dirty(&card->dapm);
+ /* Recheck all endpoints too, their state is affected by suspend */
+ dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* suspend all CODECs */
@@ -771,17 +766,12 @@ static void soc_resume_deferred(struct work_struct *work)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_platform *platform = card->rtd[i].platform;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
cpu_dai->driver->resume(cpu_dai);
- if (platform->driver->resume && platform->suspended) {
- platform->driver->resume(cpu_dai);
- platform->suspended = 0;
- }
}
if (card->resume_post)
@@ -792,8 +782,8 @@ static void soc_resume_deferred(struct work_struct *work)
/* userspace can access us now we are back as we were before */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
- /* Recheck all analogue paths too */
- dapm_mark_io_dirty(&card->dapm);
+ /* Recheck all endpoints too, their state is affected by suspend */
+ dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
}
@@ -1247,25 +1237,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
return 0;
}
-static int soc_probe_codec_dai(struct snd_soc_card *card,
- struct snd_soc_dai *codec_dai,
- int order)
+static int soc_probe_dai(struct snd_soc_dai *dai, int order)
{
int ret;
- if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
- if (codec_dai->driver->probe) {
- ret = codec_dai->driver->probe(codec_dai);
+ if (!dai->probed && dai->driver->probe_order == order) {
+ if (dai->driver->probe) {
+ ret = dai->driver->probe(dai);
if (ret < 0) {
- dev_err(codec_dai->dev,
- "ASoC: failed to probe CODEC DAI %s: %d\n",
- codec_dai->name, ret);
+ dev_err(dai->dev,
+ "ASoC: failed to probe DAI %s: %d\n",
+ dai->name, ret);
return ret;
}
}
- /* mark codec_dai as probed and add to card dai list */
- codec_dai->probed = 1;
+ dai->probed = 1;
}
return 0;
@@ -1315,40 +1302,22 @@ 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];
- struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
card->name, num, order);
- /* config components */
- cpu_dai->platform = platform;
- cpu_dai->card = card;
- for (i = 0; i < rtd->num_codecs; i++)
- rtd->codec_dais[i]->card = card;
-
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
- /* probe the cpu_dai */
- if (!cpu_dai->probed &&
- cpu_dai->driver->probe_order == order) {
- if (cpu_dai->driver->probe) {
- ret = cpu_dai->driver->probe(cpu_dai);
- if (ret < 0) {
- dev_err(cpu_dai->dev,
- "ASoC: failed to probe CPU DAI %s: %d\n",
- cpu_dai->name, ret);
- return ret;
- }
- }
- cpu_dai->probed = 1;
- }
+ ret = soc_probe_dai(cpu_dai, order);
+ if (ret)
+ return ret;
/* probe the CODEC DAI */
for (i = 0; i < rtd->num_codecs; i++) {
- ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+ ret = soc_probe_dai(rtd->codec_dais[i], order);
if (ret)
return ret;
}
@@ -2322,7 +2291,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
const struct snd_kcontrol_new *controls, int num_controls)
{
- struct snd_card *card = dai->card->snd_card;
+ struct snd_card *card = dai->component->card->snd_card;
return snd_soc_add_controls(card, dai->dev, controls, num_controls,
NULL, dai);
@@ -2330,1020 +2299,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
/**
- * snd_soc_info_enum_double - enumerated double mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a double enumerated
- * mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
- uinfo->value.enumerated.items = e->items;
-
- if (uinfo->value.enumerated.item >= e->items)
- uinfo->value.enumerated.item = e->items - 1;
- strlcpy(uinfo->value.enumerated.name,
- e->texts[uinfo->value.enumerated.item],
- sizeof(uinfo->value.enumerated.name));
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
-
-/**
- * snd_soc_get_enum_double - enumerated double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double enumerated mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, item;
- unsigned int reg_val;
- int ret;
-
- ret = snd_soc_component_read(component, e->reg, &reg_val);
- if (ret)
- return ret;
- val = (reg_val >> e->shift_l) & e->mask;
- item = snd_soc_enum_val_to_item(e, val);
- ucontrol->value.enumerated.item[0] = item;
- if (e->shift_l != e->shift_r) {
- val = (reg_val >> e->shift_l) & e->mask;
- item = snd_soc_enum_val_to_item(e, val);
- ucontrol->value.enumerated.item[1] = item;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
-
-/**
- * snd_soc_put_enum_double - enumerated double mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double enumerated mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int *item = ucontrol->value.enumerated.item;
- unsigned int val;
- unsigned int mask;
-
- if (item[0] >= e->items)
- return -EINVAL;
- val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
- mask = e->mask << e->shift_l;
- if (e->shift_l != e->shift_r) {
- if (item[1] >= e->items)
- return -EINVAL;
- val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
- mask |= e->mask << e->shift_r;
- }
-
- return snd_soc_component_update_bits(component, e->reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
-
-/**
- * snd_soc_read_signed - Read a codec register and interprete as signed value
- * @component: component
- * @reg: Register to read
- * @mask: Mask to use after shifting the register value
- * @shift: Right shift of register value
- * @sign_bit: Bit that describes if a number is negative or not.
- * @signed_val: Pointer to where the read value should be stored
- *
- * This functions reads a codec register. The register value is shifted right
- * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
- * the given registervalue into a signed integer if sign_bit is non-zero.
- *
- * Returns 0 on sucess, otherwise an error value
- */
-static int snd_soc_read_signed(struct snd_soc_component *component,
- unsigned int reg, unsigned int mask, unsigned int shift,
- unsigned int sign_bit, int *signed_val)
-{
- int ret;
- unsigned int val;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret < 0)
- return ret;
-
- val = (val >> shift) & mask;
-
- if (!sign_bit) {
- *signed_val = val;
- return 0;
- }
-
- /* non-negative number */
- if (!(val & BIT(sign_bit))) {
- *signed_val = val;
- return 0;
- }
-
- ret = val;
-
- /*
- * The register most probably does not contain a full-sized int.
- * Instead we have an arbitrary number of bits in a signed
- * representation which has to be translated into a full-sized int.
- * This is done by filling up all bits above the sign-bit.
- */
- ret |= ~((int)(BIT(sign_bit) - 1));
-
- *signed_val = ret;
-
- return 0;
-}
-
-/**
- * snd_soc_info_volsw - single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single mixer control, or a double
- * mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw(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;
-
- if (!mc->platform_max)
- mc->platform_max = mc->max;
- platform_max = mc->platform_max;
-
- if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- else
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
- uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = platform_max - mc->min;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
-
-/**
- * snd_soc_get_volsw - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- int sign_bit = mc->sign_bit;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- int val;
- int ret;
-
- if (sign_bit)
- mask = BIT(sign_bit + 1) - 1;
-
- ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] = val - min;
- if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
-
- if (snd_soc_volsw_is_stereo(mc)) {
- if (reg == reg2)
- ret = snd_soc_read_signed(component, reg, mask, rshift,
- sign_bit, &val);
- else
- ret = snd_soc_read_signed(component, reg2, mask, shift,
- sign_bit, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[1] = val - min;
- if (invert)
- ucontrol->value.integer.value[1] =
- max - ucontrol->value.integer.value[1];
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
-
-/**
- * snd_soc_put_volsw - single mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- unsigned int sign_bit = mc->sign_bit;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- int err;
- bool type_2r = false;
- unsigned int val2 = 0;
- unsigned int val, val_mask;
-
- if (sign_bit)
- mask = BIT(sign_bit + 1) - 1;
-
- val = ((ucontrol->value.integer.value[0] + min) & mask);
- if (invert)
- val = max - val;
- val_mask = mask << shift;
- val = val << shift;
- if (snd_soc_volsw_is_stereo(mc)) {
- val2 = ((ucontrol->value.integer.value[1] + min) & mask);
- if (invert)
- val2 = max - val2;
- if (reg == reg2) {
- val_mask |= mask << rshift;
- val |= val2 << rshift;
- } else {
- val2 = val2 << shift;
- type_2r = true;
- }
- }
- err = snd_soc_component_update_bits(component, reg, val_mask, val);
- if (err < 0)
- return err;
-
- if (type_2r)
- err = snd_soc_component_update_bits(component, reg2, val_mask,
- val2);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
-
-/**
- * snd_soc_get_volsw_sx - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
- unsigned int val;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret < 0)
- return ret;
-
- ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- ret = snd_soc_component_read(component, reg2, &val);
- if (ret < 0)
- return ret;
-
- val = ((val >> rshift) - min) & mask;
- ucontrol->value.integer.value[1] = val;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
-
-/**
- * snd_soc_put_volsw_sx - double mixer set callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
-
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
- int err = 0;
- unsigned int val, val_mask, val2 = 0;
-
- val_mask = mask << shift;
- val = (ucontrol->value.integer.value[0] + min) & mask;
- val = val << shift;
-
- err = snd_soc_component_update_bits(component, reg, val_mask, val);
- if (err < 0)
- return err;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- val_mask = mask << rshift;
- val2 = (ucontrol->value.integer.value[1] + min) & mask;
- val2 = val2 << rshift;
-
- err = snd_soc_component_update_bits(component, reg2, val_mask,
- val2);
- }
- return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
-
-/**
- * snd_soc_info_volsw_s8 - signed mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_s8(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 = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = platform_max - min;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
-
-/**
- * snd_soc_get_volsw_s8 - signed mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_s8(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_component *component = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- unsigned int val;
- int min = mc->min;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] =
- ((signed char)(val & 0xff))-min;
- ucontrol->value.integer.value[1] =
- ((signed char)((val >> 8) & 0xff))-min;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
-
-/**
- * snd_soc_put_volsw_sgn - signed mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_s8(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_component *component = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- int min = mc->min;
- unsigned int val;
-
- val = (ucontrol->value.integer.value[0]+min) & 0xff;
- val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
-
- return snd_soc_component_update_bits(component, reg, 0xffff, val);
-}
-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 = snd_soc_volsw_is_stereo(mc) ? 2 : 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_component *component = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- unsigned int rreg = mc->rreg;
- 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;
- int ret;
-
- if (invert)
- val = (max - ucontrol->value.integer.value[0]) & mask;
- else
- val = ((ucontrol->value.integer.value[0] + min) & mask);
- val_mask = mask << shift;
- val = val << shift;
-
- ret = snd_soc_component_update_bits(component, reg, val_mask, val);
- if (ret < 0)
- return ret;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- if (invert)
- val = (max - ucontrol->value.integer.value[1]) & mask;
- else
- val = ((ucontrol->value.integer.value[1] + min) & mask);
- val_mask = mask << shift;
- val = val << shift;
-
- ret = snd_soc_component_update_bits(component, rreg, val_mask,
- val);
- }
-
- return ret;
-}
-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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int rreg = mc->rreg;
- 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;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] = (val >> shift) & mask;
- if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
- else
- ucontrol->value.integer.value[0] =
- ucontrol->value.integer.value[0] - min;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- ret = snd_soc_component_read(component, rreg, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[1] = (val >> shift) & mask;
- if (invert)
- ucontrol->value.integer.value[1] =
- max - ucontrol->value.integer.value[1];
- else
- ucontrol->value.integer.value[1] =
- ucontrol->value.integer.value[1] - 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
- * @name: Name of the control
- * @max: new maximum limit
- *
- * Return 0 for success, else error.
- */
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
- const char *name, int max)
-{
- struct snd_card *card = codec->component.card->snd_card;
- struct snd_kcontrol *kctl;
- struct soc_mixer_control *mc;
- int found = 0;
- int ret = -EINVAL;
-
- /* Sanity check for name and max */
- if (unlikely(!name || max <= 0))
- return -EINVAL;
-
- list_for_each_entry(kctl, &card->controls, list) {
- if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
- found = 1;
- break;
- }
- }
- if (found) {
- mc = (struct soc_mixer_control *)kctl->private_value;
- if (max <= mc->max) {
- mc->platform_max = max;
- ret = 0;
- }
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-
-int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_bytes *params = (void *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = params->num_regs * component->val_bytes;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
-
-int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_bytes *params = (void *)kcontrol->private_value;
- int ret;
-
- if (component->regmap)
- ret = regmap_raw_read(component->regmap, params->base,
- ucontrol->value.bytes.data,
- params->num_regs * component->val_bytes);
- else
- ret = -EINVAL;
-
- /* Hide any masked bytes to ensure consistent data reporting */
- if (ret == 0 && params->mask) {
- switch (component->val_bytes) {
- case 1:
- ucontrol->value.bytes.data[0] &= ~params->mask;
- break;
- case 2:
- ((u16 *)(&ucontrol->value.bytes.data))[0]
- &= cpu_to_be16(~params->mask);
- break;
- case 4:
- ((u32 *)(&ucontrol->value.bytes.data))[0]
- &= cpu_to_be32(~params->mask);
- break;
- default:
- return -EINVAL;
- }
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
-
-int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_bytes *params = (void *)kcontrol->private_value;
- int ret, len;
- unsigned int val, mask;
- void *data;
-
- if (!component->regmap || !params->num_regs)
- return -EINVAL;
-
- len = params->num_regs * component->val_bytes;
-
- data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
- if (!data)
- return -ENOMEM;
-
- /*
- * If we've got a mask then we need to preserve the register
- * bits. We shouldn't modify the incoming data so take a
- * copy.
- */
- if (params->mask) {
- ret = regmap_read(component->regmap, params->base, &val);
- if (ret != 0)
- goto out;
-
- val &= params->mask;
-
- switch (component->val_bytes) {
- case 1:
- ((u8 *)data)[0] &= ~params->mask;
- ((u8 *)data)[0] |= val;
- break;
- case 2:
- mask = ~params->mask;
- ret = regmap_parse_val(component->regmap,
- &mask, &mask);
- if (ret != 0)
- goto out;
-
- ((u16 *)data)[0] &= mask;
-
- ret = regmap_parse_val(component->regmap,
- &val, &val);
- if (ret != 0)
- goto out;
-
- ((u16 *)data)[0] |= val;
- break;
- case 4:
- mask = ~params->mask;
- ret = regmap_parse_val(component->regmap,
- &mask, &mask);
- if (ret != 0)
- goto out;
-
- ((u32 *)data)[0] &= mask;
-
- ret = regmap_parse_val(component->regmap,
- &val, &val);
- if (ret != 0)
- goto out;
-
- ((u32 *)data)[0] |= val;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
- }
-
- ret = regmap_raw_write(component->regmap, params->base,
- data, len);
-
-out:
- kfree(data);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
-
-int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *ucontrol)
-{
- struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-
- ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- ucontrol->count = params->max;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
-
-int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct soc_bytes_ext *params = (void *)kcontrol->private_value;
- unsigned int count = size < params->max ? size : params->max;
- int ret = -ENXIO;
-
- switch (op_flag) {
- case SNDRV_CTL_TLV_OP_READ:
- if (params->get)
- ret = params->get(tlv, count);
- break;
- case SNDRV_CTL_TLV_OP_WRITE:
- if (params->put)
- ret = params->put(tlv, count);
- break;
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
-
-/**
- * snd_soc_info_xr_sx - signed multi register info callback
- * @kcontrol: mreg control
- * @uinfo: control element information
- *
- * Callback to provide information of a control that can
- * span multiple codec registers which together
- * forms a single signed value in a MSB/LSB manner.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = mc->min;
- uinfo->value.integer.max = mc->max;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
-
-/**
- * snd_soc_get_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to get the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- unsigned int regbase = mc->regbase;
- unsigned int regcount = mc->regcount;
- unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
- unsigned int regwmask = (1<<regwshift)-1;
- unsigned int invert = mc->invert;
- unsigned long mask = (1UL<<mc->nbits)-1;
- long min = mc->min;
- long max = mc->max;
- long val = 0;
- unsigned int regval;
- unsigned int i;
- int ret;
-
- for (i = 0; i < regcount; i++) {
- ret = snd_soc_component_read(component, regbase+i, &regval);
- if (ret)
- return ret;
- val |= (regval & regwmask) << (regwshift*(regcount-i-1));
- }
- val &= mask;
- if (min < 0 && val > max)
- val |= ~mask;
- if (invert)
- val = max - val;
- ucontrol->value.integer.value[0] = val;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
-
-/**
- * snd_soc_put_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to set the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- unsigned int regbase = mc->regbase;
- unsigned int regcount = mc->regcount;
- unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
- unsigned int regwmask = (1<<regwshift)-1;
- unsigned int invert = mc->invert;
- unsigned long mask = (1UL<<mc->nbits)-1;
- long max = mc->max;
- long val = ucontrol->value.integer.value[0];
- unsigned int i, regval, regmask;
- int err;
-
- if (invert)
- val = max - val;
- val &= mask;
- for (i = 0; i < regcount; i++) {
- regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
- regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
- err = snd_soc_component_update_bits(component, regbase+i,
- regmask, regval);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
-
-/**
- * snd_soc_get_strobe - strobe get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback get the value of a strobe mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = 1 << shift;
- unsigned int invert = mc->invert != 0;
- unsigned int val;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret)
- return ret;
-
- val &= mask;
-
- if (shift != 0 && val != 0)
- val = val >> shift;
- ucontrol->value.enumerated.item[0] = val ^ invert;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
-
-/**
- * snd_soc_put_strobe - strobe put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback strobe a register bit to high then low (or the inverse)
- * in one pass of a single mixer enum control.
- *
- * Returns 1 for success.
- */
-int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = 1 << shift;
- unsigned int invert = mc->invert != 0;
- unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
- unsigned int val1 = (strobe ^ invert) ? mask : 0;
- unsigned int val2 = (strobe ^ invert) ? 0 : mask;
- int err;
-
- err = snd_soc_component_update_bits(component, reg, mask, val1);
- if (err < 0)
- return err;
-
- return snd_soc_component_update_bits(component, reg, mask, val2);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
-
-/**
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
* @dai: DAI
* @clk_id: DAI specific clock ID
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c61cb9cedbcd..c5136bb1f982 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
}
}
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
+/*
+ * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
+ * paths
+ * @w: The widget for which to invalidate the cached number of input paths
+ *
+ * The function resets the cached number of inputs for the specified widget and
+ * all widgets that can be reached via outgoing paths from the widget.
+ *
+ * This function must be called if the number of input paths for a widget might
+ * have changed. E.g. if the source state of a widget changes or a path is added
+ * or activated with the widget as the sink.
+ */
+static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget *sink;
+ struct snd_soc_dapm_path *p;
+ LIST_HEAD(list);
+
+ dapm_assert_locked(w->dapm);
+
+ if (w->inputs == -1)
+ return;
+
+ w->inputs = -1;
+ list_add_tail(&w->work_list, &list);
+
+ list_for_each_entry(w, &list, work_list) {
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->is_supply || p->weak || !p->connect)
+ continue;
+ sink = p->sink;
+ if (sink->inputs != -1) {
+ sink->inputs = -1;
+ list_add_tail(&sink->work_list, &list);
+ }
+ }
+ }
+}
+
+/*
+ * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
+ * output paths
+ * @w: The widget for which to invalidate the cached number of output paths
+ *
+ * Resets the cached number of outputs for the specified widget and all widgets
+ * that can be reached via incoming paths from the widget.
+ *
+ * This function must be called if the number of output paths for a widget might
+ * have changed. E.g. if the sink state of a widget changes or a path is added
+ * or activated with the widget as the source.
+ */
+static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget *source;
+ struct snd_soc_dapm_path *p;
+ LIST_HEAD(list);
+
+ dapm_assert_locked(w->dapm);
+
+ if (w->outputs == -1)
+ return;
+
+ w->outputs = -1;
+ list_add_tail(&w->work_list, &list);
+
+ list_for_each_entry(w, &list, work_list) {
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->is_supply || p->weak || !p->connect)
+ continue;
+ source = p->source;
+ if (source->outputs != -1) {
+ source->outputs = -1;
+ list_add_tail(&source->work_list, &list);
+ }
+ }
+ }
+}
+
+/*
+ * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
+ * for the widgets connected to a path
+ * @p: The path to invalidate
+ *
+ * Resets the cached number of inputs for the sink of the path and the cached
+ * number of outputs for the source of the path.
+ *
+ * This function must be called when a path is added, removed or the connected
+ * state changes.
+ */
+static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
+{
+ /*
+ * Weak paths or supply paths do not influence the number of input or
+ * output paths of their neighbors.
+ */
+ if (p->weak || p->is_supply)
+ return;
+
+ /*
+ * The number of connected endpoints is the sum of the number of
+ * connected endpoints of all neighbors. If a node with 0 connected
+ * endpoints is either connected or disconnected that sum won't change,
+ * so there is no need to re-check the path.
+ */
+ if (p->source->inputs != 0)
+ dapm_widget_invalidate_input_paths(p->sink);
+ if (p->sink->outputs != 0)
+ dapm_widget_invalidate_output_paths(p->source);
+}
+
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
{
- struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
mutex_lock(&card->dapm_mutex);
list_for_each_entry(w, &card->widgets, list) {
- switch (w->id) {
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- dapm_mark_dirty(w, "Rechecking inputs and outputs");
- break;
- default:
- break;
+ if (w->is_sink || w->is_source) {
+ dapm_mark_dirty(w, "Rechecking endpoints");
+ if (w->is_sink)
+ dapm_widget_invalidate_output_paths(w);
+ if (w->is_source)
+ dapm_widget_invalidate_input_paths(w);
}
}
mutex_unlock(&card->dapm_mutex);
}
-EXPORT_SYMBOL_GPL(dapm_mark_io_dirty);
+EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
list_for_each_entry(w, &card->widgets, list) {
w->new_power = w->power;
w->power_checked = false;
- w->inputs = -1;
- w->outputs = -1;
}
}
@@ -469,10 +575,9 @@ out:
/* connect mux widget to its interconnecting audio paths */
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
- struct snd_soc_dapm_path *path, const char *control_name,
- const struct snd_kcontrol_new *kcontrol)
+ struct snd_soc_dapm_path *path, const char *control_name)
{
+ const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
int i;
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
for (i = 0; i < e->items; i++) {
if (!(strcmp(control_name, e->texts[i]))) {
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &dest->sources);
- list_add(&path->list_source, &src->sinks);
- path->name = (char*)e->texts[i];
+ path->name = e->texts[i];
if (i == item)
path->connect = 1;
else
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
}
/* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
- struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)
- w->kcontrol_news[i].private_value;
+ p->sink->kcontrol_news[i].private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int max = mc->max;
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
unsigned int val;
if (reg != SND_SOC_NOPM) {
- soc_dapm_read(w->dapm, reg, &val);
+ soc_dapm_read(p->sink->dapm, reg, &val);
val = (val >> shift) & mask;
if (invert)
val = max - val;
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
/* connect mixer widget to its interconnecting audio paths */
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
struct snd_soc_dapm_path *path, const char *control_name)
{
int i;
/* search for mixer kcontrol */
- for (i = 0; i < dest->num_kcontrols; i++) {
- if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &dest->sources);
- list_add(&path->list_source, &src->sinks);
- path->name = dest->kcontrol_news[i].name;
- dapm_set_mixer_path_status(dest, path, i);
+ for (i = 0; i < path->sink->num_kcontrols; i++) {
+ if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
+ path->name = path->sink->kcontrol_news[i].name;
+ dapm_set_mixer_path_status(path, i);
return 0;
}
}
@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
if (ret < 0)
return ret;
- list_for_each_entry(path, &w->sources, list_sink)
- dapm_kcontrol_add_path(w->kcontrols[0], path);
+ list_for_each_entry(path, &w->sources, list_sink) {
+ if (path->name)
+ dapm_kcontrol_add_path(w->kcontrols[0], path);
+ }
return 0;
}
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
return 0;
}
-/* reset 'walked' bit for each dapm path */
-static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
- struct list_head *sink)
-{
- struct snd_soc_dapm_path *p;
-
- list_for_each_entry(p, sink, list_source) {
- if (p->walked) {
- p->walked = 0;
- dapm_clear_walk_output(dapm, &p->sink->sinks);
- }
- }
-}
-
-static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
- struct list_head *source)
-{
- struct snd_soc_dapm_path *p;
-
- list_for_each_entry(p, source, list_sink) {
- if (p->walked) {
- p->walked = 0;
- dapm_clear_walk_input(dapm, &p->source->sources);
- }
- }
-}
-
-
/* We implement power down on suspend by checking the power state of
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT(widget, path_checks);
- switch (widget->id) {
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_kcontrol:
- return 0;
- default:
- break;
- }
-
- switch (widget->id) {
- case snd_soc_dapm_adc:
- case snd_soc_dapm_aif_out:
- case snd_soc_dapm_dai_out:
- if (widget->active) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
- }
- default:
- break;
- }
-
- if (widget->connected) {
- /* connected pin ? */
- if (widget->id == snd_soc_dapm_output && !widget->ext) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
- }
-
- /* connected jack or spk ? */
- if (widget->id == snd_soc_dapm_hp ||
- widget->id == snd_soc_dapm_spk ||
- (widget->id == snd_soc_dapm_line &&
- !list_empty(&widget->sources))) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
- }
+ if (widget->is_sink && widget->connected) {
+ widget->outputs = snd_soc_dapm_suspend_check(widget);
+ return widget->outputs;
}
list_for_each_entry(path, &widget->sinks, list_source) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
- if (path->weak)
+ if (path->weak || path->is_supply)
continue;
if (path->walking)
return 1;
- if (path->walked)
- continue;
-
trace_snd_soc_dapm_output_path(widget, path);
- if (path->sink && path->connect) {
- path->walked = 1;
+ if (path->connect) {
path->walking = 1;
/* do we need to add this widget to the list ? */
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT(widget, path_checks);
- switch (widget->id) {
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_kcontrol:
- return 0;
- default:
- break;
- }
-
- /* active stream ? */
- switch (widget->id) {
- case snd_soc_dapm_dac:
- case snd_soc_dapm_aif_in:
- case snd_soc_dapm_dai_in:
- if (widget->active) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
- default:
- break;
- }
-
- if (widget->connected) {
- /* connected pin ? */
- if (widget->id == snd_soc_dapm_input && !widget->ext) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- /* connected VMID/Bias for lower pops */
- if (widget->id == snd_soc_dapm_vmid) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- /* connected jack ? */
- if (widget->id == snd_soc_dapm_mic ||
- (widget->id == snd_soc_dapm_line &&
- !list_empty(&widget->sinks))) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- /* signal generator */
- if (widget->id == snd_soc_dapm_siggen) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
+ if (widget->is_source && widget->connected) {
+ widget->inputs = snd_soc_dapm_suspend_check(widget);
+ return widget->inputs;
}
list_for_each_entry(path, &widget->sources, list_sink) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
- if (path->weak)
+ if (path->weak || path->is_supply)
continue;
if (path->walking)
return 1;
- if (path->walked)
- continue;
-
trace_snd_soc_dapm_input_path(widget, path);
- if (path->source && path->connect) {
- path->walked = 1;
+ if (path->connect) {
path->walking = 1;
/* do we need to add this widget to the list ? */
@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list)
{
- struct snd_soc_card *card = dai->card;
+ struct snd_soc_card *card = dai->component->card;
+ struct snd_soc_dapm_widget *w;
int paths;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- dapm_reset(card);
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * For is_connected_{output,input}_ep fully discover the graph we need
+ * to reset the cached number of inputs and outputs.
+ */
+ list_for_each_entry(w, &card->widgets, list) {
+ w->inputs = -1;
+ w->outputs = -1;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
paths = is_connected_output_ep(dai->playback_widget, list);
- dapm_clear_walk_output(&card->dapm,
- &dai->playback_widget->sinks);
- } else {
+ else
paths = is_connected_input_ep(dai->capture_widget, list);
- dapm_clear_walk_input(&card->dapm,
- &dai->capture_widget->sources);
- }
trace_snd_soc_dapm_connected(paths, stream);
mutex_unlock(&card->dapm_mutex);
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
in = is_connected_input_ep(w, NULL);
- dapm_clear_walk_input(w->dapm, &w->sources);
out = is_connected_output_ep(w, NULL);
- dapm_clear_walk_output(w->dapm, &w->sinks);
return out != 0 && in != 0;
}
-/* Check to see if an ADC has power */
-static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
-{
- int in;
-
- DAPM_UPDATE_STAT(w, power_checks);
-
- if (w->active) {
- in = is_connected_input_ep(w, NULL);
- dapm_clear_walk_input(w->dapm, &w->sources);
- return in != 0;
- } else {
- return dapm_generic_check_power(w);
- }
-}
-
-/* Check to see if a DAC has power */
-static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
-{
- int out;
-
- DAPM_UPDATE_STAT(w, power_checks);
-
- if (w->active) {
- out = is_connected_output_ep(w, NULL);
- dapm_clear_walk_output(w->dapm, &w->sinks);
- return out != 0;
- } else {
- return dapm_generic_check_power(w);
- }
-}
-
/* Check to see if a power supply is needed */
static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
{
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
!path->connected(path->source, path->sink))
continue;
- if (!path->sink)
- continue;
-
if (dapm_widget_power_check(path->sink))
return 1;
}
@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
/* If we changed our power state perhaps our neigbours changed
* also.
*/
- list_for_each_entry(path, &w->sources, list_sink) {
- if (path->source) {
- dapm_widget_set_peer_power(path->source, power,
+ list_for_each_entry(path, &w->sources, list_sink)
+ dapm_widget_set_peer_power(path->source, power, path->connect);
+
+ /* Supplies can't affect their outputs, only their inputs */
+ if (!w->is_supply) {
+ list_for_each_entry(path, &w->sinks, list_source)
+ dapm_widget_set_peer_power(path->sink, power,
path->connect);
- }
- }
- switch (w->id) {
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_kcontrol:
- /* Supplies can't affect their outputs, only their inputs */
- break;
- default:
- list_for_each_entry(path, &w->sinks, list_source) {
- if (path->sink) {
- dapm_widget_set_peer_power(path->sink, power,
- path->connect);
- }
- }
- break;
}
if (power)
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (!buf)
return -ENOMEM;
- in = is_connected_input_ep(w, NULL);
- dapm_clear_walk_input(w->dapm, &w->sources);
- out = is_connected_output_ep(w, NULL);
- dapm_clear_walk_output(w->dapm, &w->sinks);
+ /* Supply widgets are not handled by is_connected_{input,output}_ep() */
+ if (w->is_supply) {
+ in = 0;
+ out = 0;
+ } else {
+ in = is_connected_input_ep(w, NULL);
+ out = is_connected_output_ep(w, NULL);
+ }
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
w->name, w->power ? "On" : "Off",
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
+/*
+ * soc_dapm_connect_path() - Connects or disconnects a path
+ * @path: The path to update
+ * @connect: The new connect state of the path. True if the path is connected,
+ * false if it is disconneted.
+ * @reason: The reason why the path changed (for debugging only)
+ */
+static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+ bool connect, const char *reason)
+{
+ if (path->connect == connect)
+ return;
+
+ path->connect = connect;
+ dapm_mark_dirty(path->source, reason);
+ dapm_mark_dirty(path->sink, reason);
+ dapm_path_invalidate(path);
+}
+
/* test and update the power status of a mux widget */
static int soc_dapm_mux_update_power(struct snd_soc_card *card,
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
+ bool connect;
lockdep_assert_held(&card->dapm_mutex);
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) {
- if (!path->name || !e->texts[mux])
- continue;
-
found = 1;
/* we now need to match the string in the enum to the path */
- if (!(strcmp(path->name, e->texts[mux]))) {
- path->connect = 1; /* new connection */
- dapm_mark_dirty(path->source, "mux connection");
- } else {
- if (path->connect)
- dapm_mark_dirty(path->source,
- "mux disconnection");
- path->connect = 0; /* old connection must be powered down */
- }
- dapm_mark_dirty(path->sink, "mux change");
+ if (!(strcmp(path->name, e->texts[mux])))
+ connect = true;
+ else
+ connect = false;
+
+ soc_dapm_connect_path(path, connect, "mux update");
}
if (found)
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) {
found = 1;
- path->connect = connect;
- dapm_mark_dirty(path->source, "mixer connection");
- dapm_mark_dirty(path->sink, "mixer update");
+ soc_dapm_connect_path(path, connect, "mixer update");
}
if (found)
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}
- if (w->connected != status)
+ if (w->connected != status) {
dapm_mark_dirty(w, "pin configuration");
+ dapm_widget_invalidate_input_paths(w);
+ dapm_widget_invalidate_output_paths(w);
+ }
w->connected = status;
if (status == 0)
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
+/*
+ * dapm_update_widget_flags() - Re-compute widget sink and source flags
+ * @w: The widget for which to update the flags
+ *
+ * Some widgets have a dynamic category which depends on which neighbors they
+ * are connected to. This function update the category for these widgets.
+ *
+ * This function must be called whenever a path is added or removed to a widget.
+ */
+static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+
+ switch (w->id) {
+ case snd_soc_dapm_input:
+ w->is_source = 1;
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->source->id == snd_soc_dapm_micbias ||
+ p->source->id == snd_soc_dapm_mic ||
+ p->source->id == snd_soc_dapm_line ||
+ p->source->id == snd_soc_dapm_output) {
+ w->is_source = 0;
+ break;
+ }
+ }
+ break;
+ case snd_soc_dapm_output:
+ w->is_sink = 1;
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->sink->id == snd_soc_dapm_spk ||
+ p->sink->id == snd_soc_dapm_hp ||
+ p->sink->id == snd_soc_dapm_line ||
+ p->sink->id == snd_soc_dapm_input) {
+ w->is_sink = 0;
+ break;
+ }
+ }
+ break;
+ case snd_soc_dapm_line:
+ w->is_sink = !list_empty(&w->sources);
+ w->is_source = !list_empty(&w->sinks);
+ break;
+ default:
+ break;
+ }
+}
+
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path;
int ret;
+ if (wsink->is_supply && !wsource->is_supply) {
+ dev_err(dapm->dev,
+ "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ if (connected && !wsource->is_supply) {
+ dev_err(dapm->dev,
+ "connected() callback only supported for supply widgets (%s -> %s)\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ if (wsource->is_supply && control) {
+ dev_err(dapm->dev,
+ "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
+ wsource->name, control, wsink->name);
+ return -EINVAL;
+ }
+
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path)
return -ENOMEM;
@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
- /* check for external widgets */
- if (wsink->id == snd_soc_dapm_input) {
- if (wsource->id == snd_soc_dapm_micbias ||
- wsource->id == snd_soc_dapm_mic ||
- wsource->id == snd_soc_dapm_line ||
- wsource->id == snd_soc_dapm_output)
- wsink->ext = 1;
- }
- if (wsource->id == snd_soc_dapm_output) {
- if (wsink->id == snd_soc_dapm_spk ||
- wsink->id == snd_soc_dapm_hp ||
- wsink->id == snd_soc_dapm_line ||
- wsink->id == snd_soc_dapm_input)
- wsource->ext = 1;
- }
-
- dapm_mark_dirty(wsource, "Route added");
- dapm_mark_dirty(wsink, "Route added");
+ if (wsource->is_supply || wsink->is_supply)
+ path->is_supply = 1;
/* connect static paths */
if (control == NULL) {
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
- return 0;
- }
-
- /* connect dynamic paths */
- switch (wsink->id) {
- case snd_soc_dapm_adc:
- case snd_soc_dapm_dac:
- case snd_soc_dapm_pga:
- case snd_soc_dapm_out_drv:
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_siggen:
- case snd_soc_dapm_micbias:
- case snd_soc_dapm_vmid:
- case snd_soc_dapm_pre:
- case snd_soc_dapm_post:
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_aif_in:
- case snd_soc_dapm_aif_out:
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- case snd_soc_dapm_dai_link:
- case snd_soc_dapm_kcontrol:
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
- path->connect = 1;
- return 0;
- case snd_soc_dapm_mux:
- ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
- &wsink->kcontrol_news[0]);
- if (ret != 0)
- goto err;
- break;
- case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer:
- case snd_soc_dapm_mixer_named_ctl:
- ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
- if (ret != 0)
+ } else {
+ /* connect dynamic paths */
+ switch (wsink->id) {
+ case snd_soc_dapm_mux:
+ ret = dapm_connect_mux(dapm, path, control);
+ if (ret != 0)
+ goto err;
+ break;
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ ret = dapm_connect_mixer(dapm, path, control);
+ if (ret != 0)
+ goto err;
+ break;
+ default:
+ dev_err(dapm->dev,
+ "Control not supported for path %s -> [%s] -> %s\n",
+ wsource->name, control, wsink->name);
+ ret = -EINVAL;
goto err;
- break;
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
- case snd_soc_dapm_line:
- case snd_soc_dapm_spk:
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
- path->connect = 0;
- return 0;
+ }
}
+ list_add(&path->list, &dapm->card->paths);
+ list_add(&path->list_sink, &wsink->sources);
+ list_add(&path->list_source, &wsource->sinks);
+
+ dapm_update_widget_flags(wsource);
+ dapm_update_widget_flags(wsink);
+
+ dapm_mark_dirty(wsource, "Route added");
+ dapm_mark_dirty(wsink, "Route added");
+
+ if (dapm->card->instantiated && path->connect)
+ dapm_path_invalidate(path);
+
return 0;
err:
kfree(path);
@@ -2489,6 +2476,7 @@ err:
static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
+ struct snd_soc_dapm_widget *wsource, *wsink;
struct snd_soc_dapm_path *path, *p;
const char *sink;
const char *source;
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
}
if (path) {
- dapm_mark_dirty(path->source, "Route removed");
- dapm_mark_dirty(path->sink, "Route removed");
+ wsource = path->source;
+ wsink = path->sink;
+
+ dapm_mark_dirty(wsource, "Route removed");
+ dapm_mark_dirty(wsink, "Route removed");
+ if (path->connect)
+ dapm_path_invalidate(path);
dapm_free_path(path);
+
+ /* Update any path related flags */
+ dapm_update_widget_flags(wsource);
+ dapm_update_widget_flags(wsink);
} else {
dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
source, sink);
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
}
switch (w->id) {
- case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer:
- case snd_soc_dapm_mixer_named_ctl:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_input:
+ w->is_source = 1;
w->power_check = dapm_generic_check_power;
break;
- case snd_soc_dapm_mux:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_output:
+ w->is_sink = 1;
w->power_check = dapm_generic_check_power;
break;
- case snd_soc_dapm_dai_out:
- w->power_check = dapm_adc_check_power;
- break;
- case snd_soc_dapm_dai_in:
- w->power_check = dapm_dac_check_power;
+ case snd_soc_dapm_vmid:
+ case snd_soc_dapm_siggen:
+ w->is_source = 1;
+ w->power_check = dapm_always_on_check_power;
break;
+ case snd_soc_dapm_mux:
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
- case snd_soc_dapm_spk:
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_dai_out:
+ case snd_soc_dapm_dai_in:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
+ w->is_supply = 1;
w->power_check = dapm_supply_check_power;
break;
default:
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets);
+ w->inputs = -1;
+ w->outputs = -1;
+
/* machine layer set ups unconnected pins and insertions */
w->connected = 1;
return w;
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
break;
}
+
+ if (w->id == snd_soc_dapm_dai_in) {
+ w->is_source = w->active;
+ dapm_widget_invalidate_input_paths(w);
+ } else {
+ w->is_sink = w->active;
+ dapm_widget_invalidate_output_paths(w);
+ }
}
}
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
}
dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
- w->connected = 1;
+ if (!w->connected) {
+ /*
+ * w->force does not affect the number of input or output paths,
+ * so we only have to recheck if w->connected is changed
+ */
+ dapm_widget_invalidate_input_paths(w);
+ dapm_widget_invalidate_output_paths(w);
+ w->connected = 1;
+ }
w->force = 1;
dapm_mark_dirty(w, "force enable");
@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
+/**
+ * dapm_is_external_path() - Checks if a path is a external path
+ * @card: The card the path belongs to
+ * @path: The path to check
+ *
+ * Returns true if the path is either between two different DAPM contexts or
+ * between two external pins of the same DAPM context. Otherwise returns
+ * false.
+ */
+static bool dapm_is_external_path(struct snd_soc_card *card,
+ struct snd_soc_dapm_path *path)
+{
+ dev_dbg(card->dev,
+ "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
+ path->source->name, path->source->id, path->source->dapm,
+ path->sink->name, path->sink->id, path->sink->dapm);
+
+ /* Connection between two different DAPM contexts */
+ if (path->source->dapm != path->sink->dapm)
+ return true;
+
+ /* Loopback connection from external pin to external pin */
+ if (path->sink->id == snd_soc_dapm_input) {
+ switch (path->source->id) {
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_micbias:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p;
- list_for_each_entry(p, &card->paths, list) {
- if ((p->source == w) || (p->sink == w)) {
- dev_dbg(card->dev,
- "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
- p->source->name, p->source->id, p->source->dapm,
- p->sink->name, p->sink->id, p->sink->dapm);
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (dapm_is_external_path(card, p))
+ return true;
+ }
- /* Connected to something other than the codec */
- if (p->source->dapm != p->sink->dapm)
- return true;
- /*
- * Loopback connection from codec external pin to
- * codec external pin
- */
- if (p->sink->id == snd_soc_dapm_input) {
- switch (p->source->id) {
- case snd_soc_dapm_output:
- case snd_soc_dapm_micbias:
- return true;
- default:
- break;
- }
- }
- }
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (dapm_is_external_path(card, p))
+ return true;
}
return false;
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
new file mode 100644
index 000000000000..100d92b5b77e
--- /dev/null
+++ b/sound/soc/soc-ops.c
@@ -0,0 +1,952 @@
+/*
+ * soc-ops.c -- Generic ASoC operations
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * with code, comments and ideas from :-
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+#include <sound/initval.h>
+
+/**
+ * snd_soc_info_enum_double - enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double enumerated
+ * mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+ e->items, e->texts);
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+
+/**
+ * snd_soc_get_enum_double - enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val, item;
+ unsigned int reg_val;
+ int ret;
+
+ ret = snd_soc_component_read(component, e->reg, &reg_val);
+ if (ret)
+ return ret;
+ val = (reg_val >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[0] = item;
+ if (e->shift_l != e->shift_r) {
+ val = (reg_val >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[1] = item;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+
+/**
+ * snd_soc_put_enum_double - enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val;
+ unsigned int mask;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ mask = e->mask << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ if (item[1] >= e->items)
+ return -EINVAL;
+ val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+ mask |= e->mask << e->shift_r;
+ }
+
+ return snd_soc_component_update_bits(component, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+
+/**
+ * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * @component: component
+ * @reg: Register to read
+ * @mask: Mask to use after shifting the register value
+ * @shift: Right shift of register value
+ * @sign_bit: Bit that describes if a number is negative or not.
+ * @signed_val: Pointer to where the read value should be stored
+ *
+ * This functions reads a codec register. The register value is shifted right
+ * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
+ * the given registervalue into a signed integer if sign_bit is non-zero.
+ *
+ * Returns 0 on sucess, otherwise an error value
+ */
+static int snd_soc_read_signed(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int shift,
+ unsigned int sign_bit, int *signed_val)
+{
+ int ret;
+ unsigned int val;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val = (val >> shift) & mask;
+
+ if (!sign_bit) {
+ *signed_val = val;
+ return 0;
+ }
+
+ /* non-negative number */
+ if (!(val & BIT(sign_bit))) {
+ *signed_val = val;
+ return 0;
+ }
+
+ ret = val;
+
+ /*
+ * The register most probably does not contain a full-sized int.
+ * Instead we have an arbitrary number of bits in a signed
+ * representation which has to be translated into a full-sized int.
+ * This is done by filling up all bits above the sign-bit.
+ */
+ ret |= ~((int)(BIT(sign_bit) - 1));
+
+ *signed_val = ret;
+
+ return 0;
+}
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw(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;
+
+ if (!mc->platform_max)
+ mc->platform_max = mc->max;
+ platform_max = mc->platform_max;
+
+ if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = platform_max - mc->min;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+
+/**
+ * snd_soc_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int sign_bit = mc->sign_bit;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int val;
+ int ret;
+
+ if (sign_bit)
+ mask = BIT(sign_bit + 1) - 1;
+
+ ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = val - min;
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ if (reg == reg2)
+ ret = snd_soc_read_signed(component, reg, mask, rshift,
+ sign_bit, &val);
+ else
+ ret = snd_soc_read_signed(component, reg2, mask, shift,
+ sign_bit, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[1] = val - min;
+ if (invert)
+ ucontrol->value.integer.value[1] =
+ max - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+
+/**
+ * snd_soc_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ unsigned int sign_bit = mc->sign_bit;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int err;
+ bool type_2r = false;
+ unsigned int val2 = 0;
+ unsigned int val, val_mask;
+
+ if (sign_bit)
+ mask = BIT(sign_bit + 1) - 1;
+
+ val = ((ucontrol->value.integer.value[0] + min) & mask);
+ if (invert)
+ val = max - val;
+ val_mask = mask << shift;
+ val = val << shift;
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val2 = ((ucontrol->value.integer.value[1] + min) & mask);
+ if (invert)
+ val2 = max - val2;
+ if (reg == reg2) {
+ val_mask |= mask << rshift;
+ val |= val2 << rshift;
+ } else {
+ val2 = val2 << shift;
+ type_2r = true;
+ }
+ }
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ if (type_2r)
+ err = snd_soc_component_update_bits(component, reg2, val_mask,
+ val2);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+
+/**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int mask = (1 << (fls(min + max) - 1)) - 1;
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ ret = snd_soc_component_read(component, reg2, &val);
+ if (ret < 0)
+ return ret;
+
+ val = ((val >> rshift) - min) & mask;
+ ucontrol->value.integer.value[1] = val;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int mask = (1 << (fls(min + max) - 1)) - 1;
+ int err = 0;
+ unsigned int val, val_mask, val2 = 0;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] + min) & mask;
+ val = val << shift;
+
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val_mask = mask << rshift;
+ val2 = (ucontrol->value.integer.value[1] + min) & mask;
+ val2 = val2 << rshift;
+
+ err = snd_soc_component_update_bits(component, reg2, val_mask,
+ val2);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
+ * 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 = snd_soc_volsw_is_stereo(mc) ? 2 : 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_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int rreg = mc->rreg;
+ 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;
+ int ret;
+
+ if (invert)
+ val = (max - ucontrol->value.integer.value[0]) & mask;
+ else
+ val = ((ucontrol->value.integer.value[0] + min) & mask);
+ val_mask = mask << shift;
+ val = val << shift;
+
+ ret = snd_soc_component_update_bits(component, reg, val_mask, val);
+ if (ret < 0)
+ return ret;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ if (invert)
+ val = (max - ucontrol->value.integer.value[1]) & mask;
+ else
+ val = ((ucontrol->value.integer.value[1] + min) & mask);
+ val_mask = mask << shift;
+ val = val << shift;
+
+ ret = snd_soc_component_update_bits(component, rreg, val_mask,
+ val);
+ }
+
+ return ret;
+}
+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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int rreg = mc->rreg;
+ 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;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = (val >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ else
+ ucontrol->value.integer.value[0] =
+ ucontrol->value.integer.value[0] - min;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ ret = snd_soc_component_read(component, rreg, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[1] = (val >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[1] =
+ max - ucontrol->value.integer.value[1];
+ else
+ ucontrol->value.integer.value[1] =
+ ucontrol->value.integer.value[1] - 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
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+ const char *name, int max)
+{
+ struct snd_card *card = codec->component.card->snd_card;
+ struct snd_kcontrol *kctl;
+ struct soc_mixer_control *mc;
+ int found = 0;
+ int ret = -EINVAL;
+
+ /* Sanity check for name and max */
+ if (unlikely(!name || max <= 0))
+ return -EINVAL;
+
+ list_for_each_entry(kctl, &card->controls, list) {
+ if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ mc = (struct soc_mixer_control *)kctl->private_value;
+ if (max <= mc->max) {
+ mc->platform_max = max;
+ ret = 0;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = params->num_regs * component->val_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
+
+int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_raw_read(component->regmap, params->base,
+ ucontrol->value.bytes.data,
+ params->num_regs * component->val_bytes);
+ else
+ ret = -EINVAL;
+
+ /* Hide any masked bytes to ensure consistent data reporting */
+ if (ret == 0 && params->mask) {
+ switch (component->val_bytes) {
+ case 1:
+ ucontrol->value.bytes.data[0] &= ~params->mask;
+ break;
+ case 2:
+ ((u16 *)(&ucontrol->value.bytes.data))[0]
+ &= cpu_to_be16(~params->mask);
+ break;
+ case 4:
+ ((u32 *)(&ucontrol->value.bytes.data))[0]
+ &= cpu_to_be32(~params->mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
+
+int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ int ret, len;
+ unsigned int val, mask;
+ void *data;
+
+ if (!component->regmap || !params->num_regs)
+ return -EINVAL;
+
+ len = params->num_regs * component->val_bytes;
+
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * If we've got a mask then we need to preserve the register
+ * bits. We shouldn't modify the incoming data so take a
+ * copy.
+ */
+ if (params->mask) {
+ ret = regmap_read(component->regmap, params->base, &val);
+ if (ret != 0)
+ goto out;
+
+ val &= params->mask;
+
+ switch (component->val_bytes) {
+ case 1:
+ ((u8 *)data)[0] &= ~params->mask;
+ ((u8 *)data)[0] |= val;
+ break;
+ case 2:
+ mask = ~params->mask;
+ ret = regmap_parse_val(component->regmap,
+ &mask, &mask);
+ if (ret != 0)
+ goto out;
+
+ ((u16 *)data)[0] &= mask;
+
+ ret = regmap_parse_val(component->regmap,
+ &val, &val);
+ if (ret != 0)
+ goto out;
+
+ ((u16 *)data)[0] |= val;
+ break;
+ case 4:
+ mask = ~params->mask;
+ ret = regmap_parse_val(component->regmap,
+ &mask, &mask);
+ if (ret != 0)
+ goto out;
+
+ ((u32 *)data)[0] &= mask;
+
+ ret = regmap_parse_val(component->regmap,
+ &val, &val);
+ if (ret != 0)
+ goto out;
+
+ ((u32 *)data)[0] |= val;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ ret = regmap_raw_write(component->regmap, params->base,
+ data, len);
+
+out:
+ kfree(data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
+
+int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ unsigned int count = size < params->max ? size : params->max;
+ int ret = -ENXIO;
+
+ switch (op_flag) {
+ case SNDRV_CTL_TLV_OP_READ:
+ if (params->get)
+ ret = params->get(tlv, count);
+ break;
+ case SNDRV_CTL_TLV_OP_WRITE:
+ if (params->put)
+ ret = params->put(tlv, count);
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
+
+/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = mc->min;
+ uinfo->value.integer.max = mc->max;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+ unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int invert = mc->invert;
+ unsigned long mask = (1UL<<mc->nbits)-1;
+ long min = mc->min;
+ long max = mc->max;
+ long val = 0;
+ unsigned int regval;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < regcount; i++) {
+ ret = snd_soc_component_read(component, regbase+i, &regval);
+ if (ret)
+ return ret;
+ val |= (regval & regwmask) << (regwshift*(regcount-i-1));
+ }
+ val &= mask;
+ if (min < 0 && val > max)
+ val |= ~mask;
+ if (invert)
+ val = max - val;
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+ unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int invert = mc->invert;
+ unsigned long mask = (1UL<<mc->nbits)-1;
+ long max = mc->max;
+ long val = ucontrol->value.integer.value[0];
+ unsigned int i, regval, regmask;
+ int err;
+
+ if (invert)
+ val = max - val;
+ val &= mask;
+ for (i = 0; i < regcount; i++) {
+ regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+ regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+ err = snd_soc_component_update_bits(component, regbase+i,
+ regmask, regval);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret)
+ return ret;
+
+ val &= mask;
+
+ if (shift != 0 && val != 0)
+ val = val >> shift;
+ ucontrol->value.enumerated.item[0] = val ^ invert;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+ unsigned int val1 = (strobe ^ invert) ? mask : 0;
+ unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+ int err;
+
+ err = snd_soc_component_update_bits(component, reg, mask, val1);
+ if (err < 0)
+ return err;
+
+ return snd_soc_component_update_bits(component, reg, mask, val2);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 70e8088a5468..5de2440ff25c 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
codec_dai->rate = 0;
}
+ snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
for (i = 0; i < rtd->num_codecs; i++)
snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
substream->stream);
+ snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out:
mutex_unlock(&rtd->pcm_mutex);
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index cd71fd889d8b..00b7e2d02690 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
- struct platform_device *pdev = to_platform_device(dai->platform->dev);
+ struct platform_device *pdev = to_platform_device(rtd->platform->dev);
struct txx9aclc_soc_device *dev;
struct resource *r;
int i;