static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
-int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
-
/*
* 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.
static ssize_t codec_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_soc_pcm_runtime *rtd =
- container_of(dev, struct snd_soc_pcm_runtime, dev);
+ struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0);
}
static ssize_t pmdown_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_soc_pcm_runtime *rtd =
- container_of(dev, struct snd_soc_pcm_runtime, dev);
+ struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
return sprintf(buf, "%ld\n", rtd->pmdown_time);
}
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct snd_soc_pcm_runtime *rtd =
- container_of(dev, struct snd_soc_pcm_runtime, dev);
+ struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
int ret;
ret = strict_strtol(buf, 10, &rtd->pmdown_time);
static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
#ifdef CONFIG_DEBUG_FS
-static int codec_reg_open_file(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
}
static const struct file_operations codec_reg_fops = {
- .open = codec_reg_open_file,
+ .open = simple_open,
.read = codec_reg_read_file,
.write = codec_reg_write_file,
.llseek = default_llseek,
codec->debugfs_codec_root = debugfs_create_dir(codec->name,
debugfs_card_root);
if (!codec->debugfs_codec_root) {
- printk(KERN_WARNING
- "ASoC: Failed to create codec debugfs directory\n");
+ dev_warn(codec->dev, "Failed to create codec debugfs directory\n");
return;
}
codec->debugfs_codec_root,
codec, &codec_reg_fops);
if (!codec->debugfs_reg)
- printk(KERN_WARNING
- "ASoC: Failed to create codec register debugfs file\n");
+ dev_warn(codec->dev, "Failed to create codec register debugfs file\n");
snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
}
debugfs_remove_recursive(codec->debugfs_codec_root);
}
+static void soc_init_platform_debugfs(struct snd_soc_platform *platform)
+{
+ struct dentry *debugfs_card_root = platform->card->debugfs_card_root;
+
+ platform->debugfs_platform_root = debugfs_create_dir(platform->name,
+ debugfs_card_root);
+ if (!platform->debugfs_platform_root) {
+ dev_warn(platform->dev,
+ "Failed to create platform debugfs directory\n");
+ return;
+ }
+
+ snd_soc_dapm_debugfs_init(&platform->dapm,
+ platform->debugfs_platform_root);
+}
+
+static void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform)
+{
+ debugfs_remove_recursive(platform->debugfs_platform_root);
+}
+
static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
{
}
+static inline void soc_init_platform_debugfs(struct snd_soc_platform *platform)
+{
+}
+
+static inline void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform)
+{
+}
+
static inline void soc_init_card_debugfs(struct snd_soc_card *card)
{
}
}
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
+ struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (driver->playback.stream_name != NULL)
- snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name,
- SND_SOC_DAPM_STREAM_SUSPEND);
+ snd_soc_dapm_stream_event(&card->rtd[i],
+ SNDRV_PCM_STREAM_PLAYBACK,
+ codec_dai,
+ SND_SOC_DAPM_STREAM_SUSPEND);
- if (driver->capture.stream_name != NULL)
- snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name,
- SND_SOC_DAPM_STREAM_SUSPEND);
+ snd_soc_dapm_stream_event(&card->rtd[i],
+ SNDRV_PCM_STREAM_CAPTURE,
+ codec_dai,
+ SND_SOC_DAPM_STREAM_SUSPEND);
}
/* suspend all CODECs */
if (!codec->suspended && codec->driver->suspend) {
switch (codec->dapm.bias_level) {
case SND_SOC_BIAS_STANDBY:
+ /*
+ * If the CODEC is capable of idle
+ * bias off then being in STANDBY
+ * means it's doing something,
+ * otherwise fall through.
+ */
+ if (codec->dapm.idle_bias_off) {
+ dev_dbg(codec->dev,
+ "idle_bias_off CODEC on over suspend\n");
+ break;
+ }
case SND_SOC_BIAS_OFF:
codec->driver->suspend(codec);
codec->suspended = 1;
}
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
+ struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (driver->playback.stream_name != NULL)
- snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name,
- SND_SOC_DAPM_STREAM_RESUME);
+ snd_soc_dapm_stream_event(&card->rtd[i],
+ SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
+ SND_SOC_DAPM_STREAM_RESUME);
- if (driver->capture.stream_name != NULL)
- snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name,
- SND_SOC_DAPM_STREAM_RESUME);
+ snd_soc_dapm_stream_event(&card->rtd[i],
+ SNDRV_PCM_STREAM_CAPTURE, codec_dai,
+ SND_SOC_DAPM_STREAM_RESUME);
}
/* unmute any active DACs */
}
/* no, then find CPU DAI from registered DAIs*/
list_for_each_entry(cpu_dai, &dai_list, list) {
- if (strcmp(cpu_dai->name, dai_link->cpu_dai_name))
- continue;
+ if (dai_link->cpu_dai_of_node) {
+ if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
+ continue;
+ } else {
+ if (strcmp(cpu_dai->name, dai_link->cpu_dai_name))
+ continue;
+ }
rtd->cpu_dai = cpu_dai;
goto find_codec;
/* no, then find CODEC from registered CODECs*/
list_for_each_entry(codec, &codec_list, list) {
- if (strcmp(codec->name, dai_link->codec_name))
- continue;
+ if (dai_link->codec_of_node) {
+ if (codec->dev->of_node != dai_link->codec_of_node)
+ continue;
+ } else {
+ if (strcmp(codec->name, dai_link->codec_name))
+ continue;
+ }
rtd->codec = codec;
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
- if (!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 */
list_for_each_entry(platform, &platform_list, list) {
- if (strcmp(platform->name, platform_name))
- continue;
+ if (dai_link->platform_of_node) {
+ if (platform->dev->of_node !=
+ dai_link->platform_of_node)
+ continue;
+ } else {
+ if (strcmp(platform->name, platform_name))
+ continue;
+ }
rtd->platform = platform;
goto out;
/* unregister the rtd device */
if (rtd->dev_registered) {
- device_remove_file(&rtd->dev, &dev_attr_pmdown_time);
- device_remove_file(&rtd->dev, &dev_attr_codec_reg);
- device_unregister(&rtd->dev);
+ device_remove_file(rtd->dev, &dev_attr_pmdown_time);
+ device_remove_file(rtd->dev, &dev_attr_codec_reg);
+ device_unregister(rtd->dev);
rtd->dev_registered = 0;
}
if (codec_dai->driver->remove) {
err = codec_dai->driver->remove(codec_dai);
if (err < 0)
- printk(KERN_ERR "asoc: failed to remove %s\n", codec_dai->name);
+ pr_err("asoc: failed to remove %s: %d\n",
+ codec_dai->name, err);
}
codec_dai->probed = 0;
list_del(&codec_dai->card_list);
if (platform->driver->remove) {
err = platform->driver->remove(platform);
if (err < 0)
- printk(KERN_ERR "asoc: failed to remove %s\n", platform->name);
+ pr_err("asoc: failed to remove %s: %d\n",
+ platform->name, err);
}
+
+ /* Make sure all DAPM widgets are freed */
+ snd_soc_dapm_free(&platform->dapm);
+
+ soc_cleanup_platform_debugfs(platform);
platform->probed = 0;
list_del(&platform->card_list);
module_put(platform->dev->driver->owner);
if (cpu_dai->driver->remove) {
err = cpu_dai->driver->remove(cpu_dai);
if (err < 0)
- printk(KERN_ERR "asoc: failed to remove %s\n", cpu_dai->name);
+ pr_err("asoc: failed to remove %s: %d\n",
+ cpu_dai->name, err);
}
cpu_dai->probed = 0;
list_del(&cpu_dai->card_list);
{
int ret = 0;
const struct snd_soc_codec_driver *driver = codec->driver;
+ struct snd_soc_dai *dai;
codec->card = card;
codec->dapm.card = card;
snd_soc_dapm_new_controls(&codec->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 != codec->dev)
+ continue;
+
+ snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
+ }
+
codec->dapm.idle_bias_off = driver->idle_bias_off;
if (driver->probe) {
}
if (driver->controls)
- snd_soc_add_controls(codec, driver->controls,
+ snd_soc_add_codec_controls(codec, driver->controls,
driver->num_controls);
if (driver->dapm_routes)
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
if (!try_module_get(platform->dev->driver->owner))
return -ENODEV;
+ soc_init_platform_debugfs(platform);
+
if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&platform->dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
+ platform->dapm.idle_bias_off = 1;
+
if (driver->probe) {
ret = driver->probe(platform);
if (ret < 0) {
return 0;
err_probe:
+ soc_cleanup_platform_debugfs(platform);
module_put(platform->dev->driver->owner);
return ret;
}
-static void rtd_release(struct device *dev) {}
+static void rtd_release(struct device *dev)
+{
+ kfree(dev);
+}
static int soc_post_component_init(struct snd_soc_card *card,
struct snd_soc_codec *codec,
/* register the rtd device */
rtd->codec = codec;
- rtd->dev.parent = card->dev;
- rtd->dev.release = rtd_release;
- rtd->dev.init_name = name;
+
+ rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!rtd->dev)
+ return -ENOMEM;
+ device_initialize(rtd->dev);
+ rtd->dev->parent = card->dev;
+ rtd->dev->release = rtd_release;
+ rtd->dev->init_name = name;
+ dev_set_drvdata(rtd->dev, rtd);
mutex_init(&rtd->pcm_mutex);
- ret = device_register(&rtd->dev);
+ ret = device_add(rtd->dev);
if (ret < 0) {
dev_err(card->dev,
"asoc: failed to register runtime device: %d\n", ret);
rtd->dev_registered = 1;
/* add DAPM sysfs entries for this codec */
- ret = snd_soc_dapm_sys_add(&rtd->dev);
+ ret = snd_soc_dapm_sys_add(rtd->dev);
if (ret < 0)
dev_err(codec->dev,
"asoc: failed to add codec dapm sysfs entries: %d\n",
ret);
/* add codec sysfs entries */
- ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);
+ ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
if (ret < 0)
dev_err(codec->dev,
"asoc: failed to add codec sysfs files: %d\n", ret);
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
if (ret < 0) {
- printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",
- cpu_dai->name);
+ pr_err("asoc: failed to probe CPU DAI %s: %d\n",
+ cpu_dai->name, ret);
module_put(cpu_dai->dev->driver->owner);
return ret;
}
if (codec_dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai);
if (ret < 0) {
- printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",
- codec_dai->name);
+ pr_err("asoc: failed to probe CODEC DAI %s: %d\n",
+ codec_dai->name, ret);
return ret;
}
}
if (ret)
return ret;
- ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time);
+ ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
if (ret < 0)
- printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");
+ pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
/* create the pcm */
ret = soc_new_pcm(rtd, num);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't create pcm %s\n", dai_link->stream_name);
+ pr_err("asoc: can't create pcm %s :%d\n",
+ dai_link->stream_name, ret);
return ret;
}
ret = soc_ac97_dev_register(rtd->codec);
if (ret < 0) {
- printk(KERN_ERR "asoc: AC97 device register failed\n");
+ pr_err("asoc: AC97 device register failed:%d\n", ret);
return ret;
}
/* unregister the rtd device */
if (rtd->dev_registered) {
- device_remove_file(&rtd->dev, &dev_attr_codec_reg);
- device_unregister(&rtd->dev);
+ device_remove_file(rtd->dev, &dev_attr_codec_reg);
+ device_del(rtd->dev);
rtd->dev_registered = 0;
}
struct snd_soc_dai_link *dai_link;
int ret, i, order;
- mutex_lock(&card->mutex);
+ mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
if (card->instantiated) {
mutex_unlock(&card->mutex);
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't create sound card for card %s\n",
- card->name);
+ pr_err("asoc: can't create sound card for card %s: %d\n",
+ card->name, ret);
mutex_unlock(&card->mutex);
return;
}
}
}
- /* We should have a non-codec control add function but we don't */
+ snd_soc_dapm_link_dai_widgets(card);
+
if (card->controls)
- snd_soc_add_controls(list_first_entry(&card->codec_dev_list,
- struct snd_soc_codec,
- card_list),
- card->controls,
- card->num_controls);
+ snd_soc_add_card_controls(card, card->controls, card->num_controls);
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
if (dai_link->dai_fmt) {
ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
dai_link->dai_fmt);
- if (ret != 0)
+ if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].codec_dai->dev,
"Failed to set DAI format: %d\n",
ret);
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
dai_link->dai_fmt);
- if (ret != 0)
+ if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"Failed to set DAI format: %d\n",
ret);
ret = snd_card_register(card->snd_card);
if (ret < 0) {
- printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
+ pr_err("asoc: failed to register soundcard for %s: %d\n",
+ card->name, ret);
goto probe_aux_dev_err;
}
for (i = 0; i < card->num_rtd; i++) {
ret = soc_register_ac97_dai_link(&card->rtd[i]);
if (ret < 0) {
- printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name);
+ pr_err("asoc: failed to register AC97 %s: %d\n",
+ card->name, ret);
while (--i >= 0)
soc_unregister_ac97_dai_link(card->rtd[i].codec);
goto probe_aux_dev_err;
if (!card)
return -EINVAL;
+ dev_warn(&pdev->dev,
+ "ASoC machine %s should use snd_soc_register_card()\n",
+ card->name);
+
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
snd_soc_dapm_free(&card->dapm);
- kfree(card->rtd);
snd_card_free(card->snd_card);
return 0;
const struct dev_pm_ops snd_soc_pm_ops = {
.suspend = snd_soc_suspend,
.resume = snd_soc_resume,
+ .freeze = snd_soc_suspend,
+ .thaw = snd_soc_resume,
.poweroff = snd_soc_poweroff,
+ .restore = snd_soc_resume,
};
EXPORT_SYMBOL_GPL(snd_soc_pm_ops);
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned int mask, unsigned int value)
{
- int change;
+ bool change;
unsigned int old, new;
int ret;
- ret = snd_soc_read(codec, reg);
- if (ret < 0)
- return ret;
-
- old = ret;
- new = (old & ~mask) | (value & mask);
- change = old != new;
- if (change) {
- ret = snd_soc_write(codec, reg, new);
+ if (codec->using_regmap) {
+ ret = regmap_update_bits_check(codec->control_data, reg,
+ mask, value, &change);
+ } else {
+ ret = snd_soc_read(codec, reg);
if (ret < 0)
return ret;
+
+ old = ret;
+ new = (old & ~mask) | (value & mask);
+ change = old != new;
+ if (change)
+ ret = snd_soc_write(codec, reg, new);
}
+ if (ret < 0)
+ return ret;
+
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_update_bits);
* Returns 0 for success, else error.
*/
struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
- void *data, char *long_name,
+ void *data, const char *long_name,
const char *prefix)
{
struct snd_kcontrol_new template;
}
EXPORT_SYMBOL_GPL(snd_soc_cnew);
+static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
+ const struct snd_kcontrol_new *controls, int num_controls,
+ const char *prefix, void *data)
+{
+ int err, i;
+
+ for (i = 0; i < num_controls; i++) {
+ const struct snd_kcontrol_new *control = &controls[i];
+ err = snd_ctl_add(card, snd_soc_cnew(control, data,
+ control->name, prefix));
+ if (err < 0) {
+ dev_err(dev, "Failed to add %s: %d\n", control->name, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
/**
- * snd_soc_add_controls - add an array of controls to a codec.
- * Convienience function to add a list of controls. Many codecs were
+ * snd_soc_add_codec_controls - add an array of controls to a codec.
+ * Convenience function to add a list of controls. Many codecs were
* duplicating this code.
*
* @codec: codec to add controls to
*
* Return 0 for success, else error.
*/
-int snd_soc_add_controls(struct snd_soc_codec *codec,
+int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
const struct snd_kcontrol_new *controls, int num_controls)
{
struct snd_card *card = codec->card->snd_card;
- int err, i;
-
- for (i = 0; i < num_controls; i++) {
- const struct snd_kcontrol_new *control = &controls[i];
- err = snd_ctl_add(card, snd_soc_cnew(control, codec,
- control->name,
- codec->name_prefix));
- if (err < 0) {
- dev_err(codec->dev, "%s: Failed to add %s: %d\n",
- codec->name, control->name, err);
- return err;
- }
- }
- return 0;
+ return snd_soc_add_controls(card, codec->dev, controls, num_controls,
+ codec->name_prefix, codec);
}
-EXPORT_SYMBOL_GPL(snd_soc_add_controls);
+EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
/**
* snd_soc_add_platform_controls - add an array of controls to a platform.
- * Convienience function to add a list of controls.
+ * Convenience function to add a list of controls.
*
* @platform: platform to add controls to
* @controls: array of controls to add
const struct snd_kcontrol_new *controls, int num_controls)
{
struct snd_card *card = platform->card->snd_card;
- int err, i;
- for (i = 0; i < num_controls; i++) {
- const struct snd_kcontrol_new *control = &controls[i];
- err = snd_ctl_add(card, snd_soc_cnew(control, platform,
- control->name, NULL));
- if (err < 0) {
- dev_err(platform->dev, "Failed to add %s %d\n",control->name, err);
- return err;
- }
- }
-
- return 0;
+ return snd_soc_add_controls(card, platform->dev, controls, num_controls,
+ NULL, platform);
}
EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
+/**
+ * snd_soc_add_card_controls - add an array of controls to a SoC card.
+ * Convenience function to add a list of controls.
+ *
+ * @soc_card: SoC card to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
+ const struct snd_kcontrol_new *controls, int num_controls)
+{
+ struct snd_card *card = soc_card->snd_card;
+
+ return snd_soc_add_controls(card, soc_card->dev, controls, num_controls,
+ NULL, soc_card);
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
+
+/**
+ * snd_soc_add_dai_controls - add an array of controls to a DAI.
+ * Convienience function to add a list of controls.
+ *
+ * @dai: DAI to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+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;
+
+ return snd_soc_add_controls(card, dai->dev, controls, num_controls,
+ NULL, dai);
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
+
/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = params->num_regs * codec->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 soc_bytes *params = (void *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ if (codec->using_regmap)
+ ret = regmap_raw_read(codec->control_data, params->base,
+ ucontrol->value.bytes.data,
+ params->num_regs * codec->val_bytes);
+ else
+ ret = -EINVAL;
+
+ /* Hide any masked bytes to ensure consistent data reporting */
+ if (ret == 0 && params->mask) {
+ switch (codec->val_bytes) {
+ case 1:
+ ucontrol->value.bytes.data[0] &= ~params->mask;
+ break;
+ case 2:
+ ((u16 *)(&ucontrol->value.bytes.data))[0]
+ &= ~params->mask;
+ break;
+ case 4:
+ ((u32 *)(&ucontrol->value.bytes.data))[0]
+ &= ~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 soc_bytes *params = (void *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int ret, len;
+ unsigned int val;
+ void *data;
+
+ if (!codec->using_regmap)
+ return -EINVAL;
+
+ data = ucontrol->value.bytes.data;
+ len = params->num_regs * codec->val_bytes;
+
+ /*
+ * 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(codec->control_data, params->base, &val);
+ if (ret != 0)
+ return ret;
+
+ val &= params->mask;
+
+ data = kmemdup(data, len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ switch (codec->val_bytes) {
+ case 1:
+ ((u8 *)data)[0] &= ~params->mask;
+ ((u8 *)data)[0] |= val;
+ break;
+ case 2:
+ ((u16 *)data)[0] &= cpu_to_be16(~params->mask);
+ ((u16 *)data)[0] |= cpu_to_be16(val);
+ break;
+ case 4:
+ ((u32 *)data)[0] &= cpu_to_be32(~params->mask);
+ ((u32 *)data)[0] |= cpu_to_be32(val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ ret = regmap_raw_write(codec->control_data, params->base,
+ data, len);
+
+ if (params->mask)
+ kfree(data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
/**
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
* @dai: DAI
*/
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- if (dai->driver && dai->driver->ops->set_fmt)
- return dai->driver->ops->set_fmt(dai, fmt);
- else
+ if (dai->driver == NULL)
return -EINVAL;
+ if (dai->driver->ops->set_fmt == NULL)
+ return -ENOTSUPP;
+ return dai->driver->ops->set_fmt(dai, fmt);
}
EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
if (!card->name || !card->dev)
return -EINVAL;
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai_link *link = &card->dai_link[i];
+
+ /*
+ * Codec must be specified by 1 of name or OF node,
+ * not both or neither.
+ */
+ if (!!link->codec_name == !!link->codec_of_node) {
+ dev_err(card->dev,
+ "Neither/both codec name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Platform may be specified by either name or OF node, but
+ * can be left unspecified, and a dummy platform will be used.
+ */
+ if (link->platform_name && link->platform_of_node) {
+ dev_err(card->dev,
+ "Both platform name/of_node are set for %s\n", link->name);
+ return -EINVAL;
+ }
+
+ /*
+ * CPU DAI must be specified by 1 of name or OF node,
+ * not both or neither.
+ */
+ if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) {
+ dev_err(card->dev,
+ "Neither/both cpu_dai name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+ }
+
dev_set_drvdata(card->dev, card);
snd_soc_initialize_card_lists(card);
soc_init_card_debugfs(card);
- card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
- (card->num_links + card->num_aux_devs),
- GFP_KERNEL);
+ card->rtd = devm_kzalloc(card->dev,
+ sizeof(struct snd_soc_pcm_runtime) *
+ (card->num_links + card->num_aux_devs),
+ GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
+ card->num_rtd = 0;
card->rtd_aux = &card->rtd[card->num_links];
for (i = 0; i < card->num_links; i++)
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);
struct snd_soc_dai_driver *dai_drv)
{
if (dai_drv->name == NULL) {
- printk(KERN_ERR "asoc: error - multiple DAI %s registered with no name\n",
+ pr_err("asoc: error - multiple DAI %s registered with no name\n",
dev_name(dev));
return NULL;
}
platform->dapm.dev = dev;
platform->dapm.platform = platform;
platform->dapm.stream_event = platform_drv->stream_event;
+ mutex_init(&platform->mutex);
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
codec->writable_register = codec_drv->writable_register;
+ codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
+int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
+ const char *propname)
+{
+ struct device_node *np = card->dev->of_node;
+ int num_routes;
+ struct snd_soc_dapm_route *routes;
+ int i, ret;
+
+ num_routes = of_property_count_strings(np, propname);
+ if (num_routes < 0 || num_routes & 1) {
+ dev_err(card->dev,
+ "Property '%s' does not exist or its length is not even\n",
+ propname);
+ return -EINVAL;
+ }
+ num_routes /= 2;
+ if (!num_routes) {
+ dev_err(card->dev,
+ "Property '%s's length is zero\n",
+ propname);
+ return -EINVAL;
+ }
+
+ routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+ GFP_KERNEL);
+ if (!routes) {
+ dev_err(card->dev,
+ "Could not allocate DAPM route table\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_routes; i++) {
+ ret = of_property_read_string_index(np, propname,
+ 2 * i, &routes[i].sink);
+ if (ret) {
+ dev_err(card->dev,
+ "Property '%s' index %d could not be read: %d\n",
+ propname, 2 * i, ret);
+ return -EINVAL;
+ }
+ ret = of_property_read_string_index(np, propname,
+ (2 * i) + 1, &routes[i].source);
+ if (ret) {
+ dev_err(card->dev,
+ "Property '%s' index %d could not be read: %d\n",
+ propname, (2 * i) + 1, ret);
+ return -EINVAL;
+ }
+ }
+
+ card->num_dapm_routes = num_routes;
+ card->dapm_routes = routes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
+
static int __init snd_soc_init(void)
{
#ifdef CONFIG_DEBUG_FS
snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
- printk(KERN_WARNING
- "ASoC: Failed to create debugfs directory\n");
+ pr_warn("ASoC: Failed to create debugfs directory\n");
snd_soc_debugfs_root = NULL;
}