aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/soc-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-core.c')
-rw-r--r--sound/soc/soc-core.c690
1 files changed, 487 insertions, 203 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c88d9741b9e..b37ee8077ed 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -39,6 +39,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
#include <sound/initval.h>
#define CREATE_TRACE_POINTS
@@ -54,7 +55,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
#endif
static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(card_list);
static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
@@ -465,6 +465,35 @@ static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
}
#endif
+struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
+ const char *dai_link, int stream)
+{
+ int i;
+
+ for (i = 0; i < card->num_links; i++) {
+ if (card->rtd[i].dai_link->no_pcm &&
+ !strcmp(card->rtd[i].dai_link->name, dai_link))
+ return card->rtd[i].pcm->streams[stream].substream;
+ }
+ dev_dbg(card->dev, "failed to find dai link %s\n", dai_link);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
+
+struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
+ const char *dai_link)
+{
+ int i;
+
+ for (i = 0; i < card->num_links; i++) {
+ if (!strcmp(card->rtd[i].dai_link->name, dai_link))
+ return &card->rtd[i];
+ }
+ dev_dbg(card->dev, "failed to find rtd %s\n", dai_link);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
+
#ifdef CONFIG_SND_SOC_AC97_BUS
/* unregister ac97 codec */
static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -567,19 +596,16 @@ int snd_soc_suspend(struct device *dev)
}
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_PLAYBACK,
- codec_dai,
SND_SOC_DAPM_STREAM_SUSPEND);
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_CAPTURE,
- codec_dai,
SND_SOC_DAPM_STREAM_SUSPEND);
}
@@ -683,17 +709,16 @@ static void soc_resume_deferred(struct work_struct *work)
}
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
snd_soc_dapm_stream_event(&card->rtd[i],
- SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
+ SNDRV_PCM_STREAM_PLAYBACK,
SND_SOC_DAPM_STREAM_RESUME);
snd_soc_dapm_stream_event(&card->rtd[i],
- SNDRV_PCM_STREAM_CAPTURE, codec_dai,
+ SNDRV_PCM_STREAM_CAPTURE,
SND_SOC_DAPM_STREAM_RESUME);
}
@@ -783,15 +808,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_dai *codec_dai, *cpu_dai;
const char *platform_name;
- if (rtd->complete)
- return 1;
dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);
- /* do we already have the CPU DAI for this link ? */
- if (rtd->cpu_dai) {
- goto find_codec;
- }
- /* no, then find CPU DAI from registered DAIs*/
+ /* 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)
@@ -802,18 +821,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
}
rtd->cpu_dai = cpu_dai;
- goto find_codec;
}
- dev_dbg(card->dev, "CPU DAI %s not registered\n",
- dai_link->cpu_dai_name);
-find_codec:
- /* do we already have the CODEC for this link ? */
- if (rtd->codec) {
- goto find_platform;
+ if (!rtd->cpu_dai) {
+ dev_dbg(card->dev, "CPU DAI %s not registered\n",
+ dai_link->cpu_dai_name);
+ return -EPROBE_DEFER;
}
- /* no, then find CODEC from registered CODECs*/
+ /* Find CODEC from registered CODECs */
list_for_each_entry(codec, &codec_list, list) {
if (dai_link->codec_of_node) {
if (codec->dev->of_node != dai_link->codec_of_node)
@@ -835,28 +851,28 @@ find_codec:
dai_link->codec_dai_name)) {
rtd->codec_dai = codec_dai;
- goto find_platform;
}
}
- dev_dbg(card->dev, "CODEC DAI %s not registered\n",
- dai_link->codec_dai_name);
- goto find_platform;
+ if (!rtd->codec_dai) {
+ dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+ dai_link->codec_dai_name);
+ return -EPROBE_DEFER;
+ }
}
- dev_dbg(card->dev, "CODEC %s not registered\n",
- dai_link->codec_name);
-find_platform:
- /* do we need a platform? */
- if (rtd->platform)
- goto out;
+ if (!rtd->codec) {
+ dev_dbg(card->dev, "CODEC %s not registered\n",
+ dai_link->codec_name);
+ return -EPROBE_DEFER;
+ }
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
if (!platform_name && !dai_link->platform_of_node)
platform_name = "snd-soc-dummy";
- /* no, then find one from the set of registered platforms */
+ /* find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
if (dai_link->platform_of_node) {
if (platform->dev->of_node !=
@@ -868,20 +884,16 @@ find_platform:
}
rtd->platform = platform;
- goto out;
}
-
- dev_dbg(card->dev, "platform %s not registered\n",
+ if (!rtd->platform) {
+ dev_dbg(card->dev, "platform %s not registered\n",
dai_link->platform_name);
- return 0;
-
-out:
- /* mark rtd as complete if we found all 4 of our client devices */
- if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {
- rtd->complete = 1;
- card->num_rtd++;
+ return -EPROBE_DEFER;
}
- return 1;
+
+ card->num_rtd++;
+
+ return 0;
}
static void soc_remove_codec(struct snd_soc_codec *codec)
@@ -1068,6 +1080,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
{
int ret = 0;
const struct snd_soc_platform_driver *driver = platform->driver;
+ struct snd_soc_dai *dai;
platform->card = card;
platform->dapm.card = card;
@@ -1081,6 +1094,14 @@ static int soc_probe_platform(struct snd_soc_card *card,
snd_soc_dapm_new_controls(&platform->dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
+ /* Create DAPM widgets for each DAI stream */
+ list_for_each_entry(dai, &dai_list, list) {
+ if (dai->dev != platform->dev)
+ continue;
+
+ snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+ }
+
platform->dapm.idle_bias_off = 1;
if (driver->probe) {
@@ -1170,6 +1191,10 @@ static int soc_post_component_init(struct snd_soc_card *card,
rtd->dev->init_name = name;
dev_set_drvdata(rtd->dev, rtd);
mutex_init(&rtd->pcm_mutex);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
ret = device_add(rtd->dev);
if (ret < 0) {
dev_err(card->dev,
@@ -1191,6 +1216,17 @@ static int soc_post_component_init(struct snd_soc_card *card,
dev_err(codec->dev,
"asoc: failed to add codec sysfs files: %d\n", ret);
+#ifdef CONFIG_DEBUG_FS
+ /* add DPCM sysfs entries */
+ if (!dailess && !dai_link->dynamic)
+ goto out;
+
+ ret = soc_dpcm_debugfs_add(rtd);
+ if (ret < 0)
+ dev_err(rtd->dev, "asoc: failed to add dpcm sysfs entries: %d\n", ret);
+
+out:
+#endif
return 0;
}
@@ -1200,14 +1236,15 @@ static int soc_probe_dai_link(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;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dapm_widget *play_w, *capture_w;
int ret;
dev_dbg(card->dev, "probe %s dai link %d late %d\n",
card->name, num, order);
/* config components */
- codec_dai->codec = codec;
cpu_dai->platform = platform;
codec_dai->card = card;
cpu_dai->card = card;
@@ -1218,9 +1255,12 @@ 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;
+ snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
+
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
if (ret < 0) {
@@ -1279,12 +1319,39 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
if (ret < 0)
pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
- /* create the pcm */
- ret = soc_new_pcm(rtd, num);
- if (ret < 0) {
- pr_err("asoc: can't create pcm %s :%d\n",
- dai_link->stream_name, ret);
- return ret;
+ if (!dai_link->params) {
+ /* create the pcm */
+ ret = soc_new_pcm(rtd, num);
+ if (ret < 0) {
+ pr_err("asoc: can't create pcm %s :%d\n",
+ dai_link->stream_name, ret);
+ return ret;
+ }
+ } else {
+ /* link the DAI widgets */
+ play_w = codec_dai->playback_widget;
+ capture_w = cpu_dai->capture_widget;
+ if (play_w && capture_w) {
+ ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+ capture_w, play_w);
+ if (ret != 0) {
+ dev_err(card->dev, "Can't link %s to %s: %d\n",
+ play_w->name, capture_w->name, ret);
+ return ret;
+ }
+ }
+
+ play_w = cpu_dai->playback_widget;
+ capture_w = codec_dai->capture_widget;
+ if (play_w && capture_w) {
+ ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+ capture_w, play_w);
+ if (ret != 0) {
+ dev_err(card->dev, "Can't link %s to %s: %d\n",
+ play_w->name, capture_w->name, ret);
+ return ret;
+ }
+ }
}
/* add platform data for AC97 devices */
@@ -1334,6 +1401,20 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec)
}
#endif
+static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+{
+ struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+ struct snd_soc_codec *codec;
+
+ /* find CODEC from registered CODECs*/
+ list_for_each_entry(codec, &codec_list, list) {
+ if (!strcmp(codec->name, aux_dev->codec_name))
+ return 0;
+ }
+
+ return -EPROBE_DEFER;
+}
+
static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
{
struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
@@ -1354,7 +1435,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
}
/* codec not found */
dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name);
- goto out;
+ return -EPROBE_DEFER;
found:
ret = soc_probe_codec(card, codec);
@@ -1404,29 +1485,28 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec,
return 0;
}
-static void snd_soc_instantiate_card(struct snd_soc_card *card)
+static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
struct snd_soc_codec_conf *codec_conf;
enum snd_soc_compress_type compress_type;
struct snd_soc_dai_link *dai_link;
- int ret, i, order;
+ int ret, i, order, dai_fmt;
- mutex_lock(&card->mutex);
-
- if (card->instantiated) {
- mutex_unlock(&card->mutex);
- return;
- }
+ mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
/* bind DAIs */
- for (i = 0; i < card->num_links; i++)
- soc_bind_dai_link(card, i);
+ for (i = 0; i < card->num_links; i++) {
+ ret = soc_bind_dai_link(card, i);
+ if (ret != 0)
+ goto base_error;
+ }
- /* bind completed ? */
- if (card->num_rtd != card->num_links) {
- mutex_unlock(&card->mutex);
- return;
+ /* check aux_devs too */
+ for (i = 0; i < card->num_aux_devs; i++) {
+ ret = soc_check_aux_dev(card, i);
+ if (ret != 0)
+ goto base_error;
}
/* initialize the register cache for each available codec */
@@ -1446,10 +1526,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
ret = snd_soc_init_codec_cache(codec, compress_type);
- if (ret < 0) {
- mutex_unlock(&card->mutex);
- return;
- }
+ if (ret < 0)
+ goto base_error;
}
/* card bind complete so register a sound card */
@@ -1458,8 +1536,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
if (ret < 0) {
pr_err("asoc: can't create sound card for card %s: %d\n",
card->name, ret);
- mutex_unlock(&card->mutex);
- return;
+ goto base_error;
}
card->snd_card->dev = card->dev;
@@ -1523,17 +1600,47 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
for (i = 0; i < card->num_links; i++) {
dai_link = &card->dai_link[i];
+ dai_fmt = dai_link->dai_fmt;
- if (dai_link->dai_fmt) {
+ if (dai_fmt) {
ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
- dai_link->dai_fmt);
+ dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].codec_dai->dev,
"Failed to set DAI format: %d\n",
ret);
+ }
+ /* If this is a regular CPU link there will be a platform */
+ if (dai_fmt &&
+ (dai_link->platform_name || dai_link->platform_of_node)) {
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
- dai_link->dai_fmt);
+ dai_fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(card->rtd[i].cpu_dai->dev,
+ "Failed to set DAI format: %d\n",
+ ret);
+ } else if (dai_fmt) {
+ /* Flip the polarity for the "CPU" end */
+ dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+ switch (dai_link->dai_fmt &
+ SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ }
+
+ ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
+ dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"Failed to set DAI format: %d\n",
@@ -1599,7 +1706,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
- return;
+
+ return 0;
probe_aux_dev_err:
for (i = 0; i < card->num_aux_devs; i++)
@@ -1614,18 +1722,10 @@ card_probe_error:
snd_card_free(card->snd_card);
+base_error:
mutex_unlock(&card->mutex);
-}
-/*
- * Attempt to initialise any uninitialised cards. Must be called with
- * client_mutex.
- */
-static void snd_soc_instantiate_cards(void)
-{
- struct snd_soc_card *card;
- list_for_each_entry(card, &card_list, list)
- snd_soc_instantiate_card(card);
+ return ret;
}
/* probes a new socdev */
@@ -2527,6 +2627,87 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
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_codec *codec = 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;
+
+ ucontrol->value.integer.value[0] =
+ ((snd_soc_read(codec, reg) >> shift) - min) & mask;
+
+ if (snd_soc_volsw_is_stereo(mc))
+ ucontrol->value.integer.value[1] =
+ ((snd_soc_read(codec, reg2) >> rshift) - min) & mask;
+
+ 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_codec *codec = 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 short val, val_mask, val2 = 0;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] + min) & mask;
+ val = val << shift;
+
+ if (snd_soc_update_bits_locked(codec, reg, val_mask, val))
+ return err;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val_mask = mask << rshift;
+ val2 = (ucontrol->value.integer.value[1] + min) & mask;
+ val2 = val2 << rshift;
+
+ if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2))
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
* snd_soc_info_volsw_s8 - signed mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
@@ -2647,99 +2828,6 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-/**
- * snd_soc_info_volsw_2r_sx - double with tlv and variable data size
- * mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int max = mc->max;
- int min = mc->min;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = max-min;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx);
-
-/**
- * snd_soc_get_volsw_2r_sx - double with tlv and variable data size
- * mixer get callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_2r_sx(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 mask = (1<<mc->shift)-1;
- int min = mc->min;
- int val = snd_soc_read(codec, mc->reg) & mask;
- int valr = snd_soc_read(codec, mc->rreg) & mask;
-
- ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask;
- ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
-
-/**
- * snd_soc_put_volsw_2r_sx - double with tlv and variable data size
- * mixer put callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_2r_sx(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 mask = (1<<mc->shift)-1;
- int min = mc->min;
- int ret;
- unsigned int val, valr, oval, ovalr;
-
- val = ((ucontrol->value.integer.value[0]+min) & 0xff);
- val &= mask;
- valr = ((ucontrol->value.integer.value[1]+min) & 0xff);
- valr &= mask;
-
- oval = snd_soc_read(codec, mc->reg) & mask;
- ovalr = snd_soc_read(codec, mc->rreg) & mask;
-
- ret = 0;
- if (oval != val) {
- ret = snd_soc_write(codec, mc->reg, val);
- if (ret < 0)
- return ret;
- }
- if (ovalr != valr) {
- ret = snd_soc_write(codec, mc->rreg, valr);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
-
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -2850,6 +2938,186 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
/**
+ * 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 soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = codec->driver->reg_word_size * 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 long regval;
+ unsigned int i;
+
+ for (i = 0; i < regcount; i++) {
+ regval = snd_soc_read(codec, regbase+i) & regwmask;
+ val |= regval << (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 soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = codec->driver->reg_word_size * 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_update_bits_locked(codec, 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 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;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int val = snd_soc_read(codec, reg) & 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 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;
+ 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_update_bits_locked(codec, reg, mask, val1);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_update_bits_locked(codec, reg, mask, val2);
+ return err;
+}
+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
@@ -3048,7 +3316,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
if (dai->driver && dai->driver->ops->digital_mute)
return dai->driver->ops->digital_mute(dai, mute);
else
- return -EINVAL;
+ return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
@@ -3060,7 +3328,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
- int i;
+ int i, ret;
if (!card->name || !card->dev)
return -EINVAL;
@@ -3123,15 +3391,13 @@ int snd_soc_register_card(struct snd_soc_card *card)
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = 0;
mutex_init(&card->mutex);
+ mutex_init(&card->dapm_mutex);
- mutex_lock(&client_mutex);
- list_add(&card->list, &card_list);
- snd_soc_instantiate_cards();
- mutex_unlock(&client_mutex);
-
- dev_dbg(card->dev, "Registered card '%s'\n", card->name);
+ ret = snd_soc_instantiate_card(card);
+ if (ret != 0)
+ soc_cleanup_card_debugfs(card);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
@@ -3145,9 +3411,6 @@ int snd_soc_unregister_card(struct snd_soc_card *card)
{
if (card->instantiated)
soc_cleanup_card_resources(card);
- mutex_lock(&client_mutex);
- list_del(&card->list);
- mutex_unlock(&client_mutex);
dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
return 0;
@@ -3221,6 +3484,7 @@ static inline char *fmt_multiple_name(struct device *dev,
int snd_soc_register_dai(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
{
+ struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
dev_dbg(dev, "dai register %s\n", dev_name(dev));
@@ -3238,12 +3502,23 @@ int snd_soc_register_dai(struct device *dev,
dai->dev = dev;
dai->driver = dai_drv;
+ dai->dapm.dev = dev;
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
mutex_lock(&client_mutex);
+
+ list_for_each_entry(codec, &codec_list, list) {
+ if (codec->dev == dev) {
+ dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+ dai->name, codec->name);
+ dai->codec = codec;
+ break;
+ }
+ }
+
list_add(&dai->list, &dai_list);
- snd_soc_instantiate_cards();
+
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
@@ -3287,6 +3562,7 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
int snd_soc_register_dais(struct device *dev,
struct snd_soc_dai_driver *dai_drv, size_t count)
{
+ struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
int i, ret = 0;
@@ -3314,19 +3590,28 @@ int snd_soc_register_dais(struct device *dev,
dai->id = dai->driver->id;
else
dai->id = i;
+ dai->dapm.dev = dev;
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
mutex_lock(&client_mutex);
+
+ list_for_each_entry(codec, &codec_list, list) {
+ if (codec->dev == dev) {
+ dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+ dai->name, codec->name);
+ dai->codec = codec;
+ break;
+ }
+ }
+
list_add(&dai->list, &dai_list);
+
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
}
- mutex_lock(&client_mutex);
- snd_soc_instantiate_cards();
- mutex_unlock(&client_mutex);
return 0;
err:
@@ -3384,7 +3669,6 @@ int snd_soc_register_platform(struct device *dev,
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
- snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
@@ -3534,18 +3818,18 @@ int snd_soc_register_codec(struct device *dev,
fixup_codec_formats(&dai_drv[i].capture);
}
+ mutex_lock(&client_mutex);
+ list_add(&codec->list, &codec_list);
+ mutex_unlock(&client_mutex);
+
/* register any DAIs */
if (num_dai) {
ret = snd_soc_register_dais(dev, dai_drv, num_dai);
if (ret < 0)
- goto fail;
+ dev_err(codec->dev, "Failed to regster DAIs: %d\n",
+ ret);
}
- mutex_lock(&client_mutex);
- list_add(&codec->list, &codec_list);
- snd_soc_instantiate_cards();
- mutex_unlock(&client_mutex);
-
pr_debug("Registered codec '%s'\n", codec->name);
return 0;