static struct dentry *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);
+
+static int snd_soc_register_card(struct snd_soc_card *card);
+static int snd_soc_unregister_card(struct snd_soc_card *card);
+
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
* It can be used to eliminate pops between different playback streams, e.g.
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->suspend && !cpu_dai->ac97_control)
- cpu_dai->suspend(pdev, cpu_dai);
+ cpu_dai->suspend(cpu_dai);
if (platform->suspend)
- platform->suspend(pdev, cpu_dai);
+ platform->suspend(cpu_dai);
}
/* close any waiting streams and save state */
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->suspend && cpu_dai->ac97_control)
- cpu_dai->suspend(pdev, cpu_dai);
+ cpu_dai->suspend(cpu_dai);
}
if (card->suspend_post)
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->resume && cpu_dai->ac97_control)
- cpu_dai->resume(pdev, cpu_dai);
+ cpu_dai->resume(cpu_dai);
}
if (codec_dev->resume)
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->resume && !cpu_dai->ac97_control)
- cpu_dai->resume(pdev, cpu_dai);
+ cpu_dai->resume(cpu_dai);
if (platform->resume)
- platform->resume(pdev, cpu_dai);
+ platform->resume(cpu_dai);
}
if (card->resume_post)
#define soc_resume NULL
#endif
-/* probes a new socdev */
-static int soc_probe(struct platform_device *pdev)
+static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
- int ret = 0, i;
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_platform *platform = card->platform;
- struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct platform_device *pdev = container_of(card->dev,
+ struct platform_device,
+ dev);
+ struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+ struct snd_soc_platform *platform;
+ struct snd_soc_dai *dai;
+ int i, found, ret, ac97;
+
+ if (card->instantiated)
+ return;
+
+ found = 0;
+ list_for_each_entry(platform, &platform_list, list)
+ if (card->platform == platform) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ dev_dbg(card->dev, "Platform %s not registered\n",
+ card->platform->name);
+ return;
+ }
- /* Bodge while we push things out of socdev */
- card->socdev = socdev;
+ ac97 = 0;
+ for (i = 0; i < card->num_links; i++) {
+ found = 0;
+ list_for_each_entry(dai, &dai_list, list)
+ if (card->dai_link[i].cpu_dai == dai) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ dev_dbg(card->dev, "DAI %s not registered\n",
+ card->dai_link[i].cpu_dai->name);
+ return;
+ }
+
+ if (card->dai_link[i].cpu_dai->ac97_control)
+ ac97 = 1;
+ }
+
+ /* If we have AC97 in the system then don't wait for the
+ * codec. This will need revisiting if we have to handle
+ * systems with mixed AC97 and non-AC97 parts. Only check for
+ * DAIs currently; we can't do this per link since some AC97
+ * codecs have non-AC97 DAIs.
+ */
+ if (!ac97)
+ for (i = 0; i < card->num_links; i++) {
+ found = 0;
+ list_for_each_entry(dai, &dai_list, list)
+ if (card->dai_link[i].codec_dai == dai) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ dev_dbg(card->dev, "DAI %s not registered\n",
+ card->dai_link[i].codec_dai->name);
+ return;
+ }
+ }
+
+ /* Note that we do not current check for codec components */
+
+ dev_dbg(card->dev, "All components present, instantiating\n");
+ /* Found everything, bring it up */
if (card->probe) {
ret = card->probe(pdev);
if (ret < 0)
- return ret;
+ return;
}
for (i = 0; i < card->num_links; i++) {
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
- return 0;
+ card->instantiated = 1;
+
+ return;
platform_err:
if (codec_dev->remove)
if (card->remove)
card->remove(pdev);
+}
- return ret;
+/*
+ * Attempt to initialise any uninitalised 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);
+}
+
+/* probes a new socdev */
+static int soc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_card *card = socdev->card;
+
+ /* Bodge while we push things out of socdev */
+ card->socdev = socdev;
+
+ /* Bodge while we unpick instantiation */
+ card->dev = &pdev->dev;
+ ret = snd_soc_register_card(card);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register card\n");
+ return ret;
+ }
+
+ return 0;
}
/* removes a socdev */
if (card->remove)
card->remove(pdev);
+ snd_soc_unregister_card(card);
+
return 0;
}
/**
* snd_soc_new_pcms - create new sound card and pcms
* @socdev: the SoC audio device
+ * @idx: ALSA card index
+ * @xid: card identification
*
* Create a new sound card based upon the codec and interface pcms.
*
mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
- if (ac97) {
+ /* Only instantiate AC97 if not already done by the adaptor
+ * for the generic AC97 subsystem.
+ */
+ if (ac97 && strcmp(codec->name, "AC97") != 0) {
ret = soc_ac97_dev_register(codec);
if (ret < 0) {
printk(KERN_ERR "asoc: AC97 device register failed\n");
* snd_soc_cnew - create new control
* @_template: control template
* @data: control private data
- * @lnng_name: control long name
+ * @long_name: control long name
*
* Create a new mixer control from a template control.
*
/**
* snd_soc_get_enum_double - enumerated double mixer get callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to get the value of a double enumerated mixer.
*
/**
* snd_soc_put_enum_double - enumerated double mixer put callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a double enumerated mixer.
*
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
/**
+ * snd_soc_get_value_enum_double - semi enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double semi enumerated mixer.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short reg_val, val, mux;
+
+ reg_val = snd_soc_read(codec, e->reg);
+ val = (reg_val >> e->shift_l) & e->mask;
+ for (mux = 0; mux < e->max; mux++) {
+ if (val == e->values[mux])
+ break;
+ }
+ ucontrol->value.enumerated.item[0] = mux;
+ if (e->shift_l != e->shift_r) {
+ val = (reg_val >> e->shift_r) & e->mask;
+ for (mux = 0; mux < e->max; mux++) {
+ if (val == e->values[mux])
+ break;
+ }
+ ucontrol->value.enumerated.item[1] = mux;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double);
+
+/**
+ * snd_soc_put_value_enum_double - semi enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double semi enumerated mixer.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short val;
+ unsigned short mask;
+
+ if (ucontrol->value.enumerated.item[0] > e->max - 1)
+ return -EINVAL;
+ val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
+ mask = e->mask << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ if (ucontrol->value.enumerated.item[1] > e->max - 1)
+ return -EINVAL;
+ val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
+ mask |= e->mask << e->shift_r;
+ }
+
+ return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
+
+/**
* snd_soc_info_enum_ext - external enumerated single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to get the value of a single mixer control.
*
/**
* snd_soc_put_volsw - single mixer put callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a single mixer control.
*
/**
* snd_soc_get_volsw_2r - double mixer get callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to get the value of a double mixer control that spans 2 registers.
*
/**
* snd_soc_put_volsw_2r - double mixer set callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
/**
* snd_soc_get_volsw_s8 - signed mixer get callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to get the value of a signed mixer control.
*
/**
* snd_soc_put_volsw_sgn - signed mixer put callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a signed mixer control.
*
/**
* snd_soc_dai_set_clkdiv - configure DAI clock dividers.
* @dai: DAI
- * @clk_id: DAI specific clock divider ID
+ * @div_id: DAI specific clock divider ID
* @div: new clock divisor.
*
* Configures the clock dividers. This is used to derive the best DAI bit and
}
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
-static int __devinit snd_soc_init(void)
+/**
+ * snd_soc_register_card - Register a card with the ASoC core
+ *
+ * @card: Card to register
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_register_card(struct snd_soc_card *card)
+{
+ if (!card->name || !card->dev)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&card->list);
+ card->instantiated = 0;
+
+ 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);
+
+ return 0;
+}
+
+/**
+ * snd_soc_unregister_card - Unregister a card with the ASoC core
+ *
+ * @card: Card to unregister
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_unregister_card(struct snd_soc_card *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;
+}
+
+/**
+ * snd_soc_register_dai - Register a DAI with the ASoC core
+ *
+ * @dai: DAI to register
+ */
+int snd_soc_register_dai(struct snd_soc_dai *dai)
+{
+ if (!dai->name)
+ return -EINVAL;
+
+ /* The device should become mandatory over time */
+ if (!dai->dev)
+ printk(KERN_WARNING "No device for DAI %s\n", dai->name);
+
+ INIT_LIST_HEAD(&dai->list);
+
+ mutex_lock(&client_mutex);
+ list_add(&dai->list, &dai_list);
+ snd_soc_instantiate_cards();
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Registered DAI '%s'\n", dai->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dai);
+
+/**
+ * snd_soc_unregister_dai - Unregister a DAI from the ASoC core
+ *
+ * @dai: DAI to unregister
+ */
+void snd_soc_unregister_dai(struct snd_soc_dai *dai)
+{
+ mutex_lock(&client_mutex);
+ list_del(&dai->list);
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Unregistered DAI '%s'\n", dai->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
+
+/**
+ * snd_soc_register_dais - Register multiple DAIs with the ASoC core
+ *
+ * @dai: Array of DAIs to register
+ * @count: Number of DAIs
+ */
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count)
+{
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ ret = snd_soc_register_dai(&dai[i]);
+ if (ret != 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ for (i--; i >= 0; i--)
+ snd_soc_unregister_dai(&dai[i]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dais);
+
+/**
+ * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
+ *
+ * @dai: Array of DAIs to unregister
+ * @count: Number of DAIs
+ */
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ snd_soc_unregister_dai(&dai[i]);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);
+
+/**
+ * snd_soc_register_platform - Register a platform with the ASoC core
+ *
+ * @platform: platform to register
+ */
+int snd_soc_register_platform(struct snd_soc_platform *platform)
+{
+ if (!platform->name)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&platform->list);
+
+ 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);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+
+/**
+ * snd_soc_unregister_platform - Unregister a platform from the ASoC core
+ *
+ * @platform: platform to unregister
+ */
+void snd_soc_unregister_platform(struct snd_soc_platform *platform)
+{
+ mutex_lock(&client_mutex);
+ list_del(&platform->list);
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Unregistered platform '%s'\n", platform->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
+
+/**
+ * snd_soc_register_codec - Register a codec with the ASoC core
+ *
+ * @codec: codec to register
+ */
+int snd_soc_register_codec(struct snd_soc_codec *codec)
+{
+ if (!codec->name)
+ return -EINVAL;
+
+ /* The device should become mandatory over time */
+ if (!codec->dev)
+ printk(KERN_WARNING "No device for codec %s\n", codec->name);
+
+ INIT_LIST_HEAD(&codec->list);
+
+ 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;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_codec);
+
+/**
+ * snd_soc_unregister_codec - Unregister a codec from the ASoC core
+ *
+ * @codec: codec to unregister
+ */
+void snd_soc_unregister_codec(struct snd_soc_codec *codec)
+{
+ mutex_lock(&client_mutex);
+ list_del(&codec->list);
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Unregistered codec '%s'\n", codec->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
+
+static int __init snd_soc_init(void)
{
#ifdef CONFIG_DEBUG_FS
debugfs_root = debugfs_create_dir("asoc", NULL);