Merge tag 'asoc-v3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v3.12 - DAPM is now mandatory for CODEC drivers in order to avoid the repeated regressions in the special cases for non-DAPM CODECs and make it easier to integrate with other components on boards. All existing drivers have had some level of DAPM support added. - A lot of cleanups in DAPM plus support for maintaining controls in a specific state while a DAPM widget all contributed by Lars-Peter Clausen. - Core helpers for bitbanged AC'97 reset from Markus Pargmann. - New drivers and support for Analog Devices ADAU1702 and ADAU1401(a), Asahi Kasei Microdevices AK4554, Atmel AT91ASM9x5 and WM8904 based machines, Freescale S/PDIF and SSI AC'97, Renesas R-Car SoCs, Samsung Exynos5420 SoCs, Texas Instruments PCM1681 and PCM1792A and Wolfson Microelectronics WM8997. - Support for building drivers that can support it cross-platform for compile test.
This commit is contained in:
@@ -30,9 +30,12 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
@@ -47,8 +50,6 @@
|
||||
|
||||
#define NAME_SIZE 32
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *snd_soc_debugfs_root;
|
||||
EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
|
||||
@@ -69,6 +70,16 @@ static int pmdown_time = 5000;
|
||||
module_param(pmdown_time, int, 0);
|
||||
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
|
||||
|
||||
struct snd_ac97_reset_cfg {
|
||||
struct pinctrl *pctl;
|
||||
struct pinctrl_state *pstate_reset;
|
||||
struct pinctrl_state *pstate_warm_reset;
|
||||
struct pinctrl_state *pstate_run;
|
||||
int gpio_sdata;
|
||||
int gpio_sync;
|
||||
int gpio_reset;
|
||||
};
|
||||
|
||||
/* returns the minimum number of bytes needed to represent
|
||||
* a particular given value */
|
||||
static int min_bytes_needed(unsigned long val)
|
||||
@@ -532,6 +543,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void codec2codec_close_delayed_work(struct work_struct *work)
|
||||
{
|
||||
/* Currently nothing to do for c2c links
|
||||
* Since c2c links are internal nodes in the DAPM graph and
|
||||
* don't interface with the outside world or application layer
|
||||
* we don't have to do any special handling on close.
|
||||
*/
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/* powers down audio subsystem for suspend */
|
||||
int snd_soc_suspend(struct device *dev)
|
||||
@@ -1430,6 +1450,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
INIT_DELAYED_WORK(&rtd->delayed_work,
|
||||
codec2codec_close_delayed_work);
|
||||
|
||||
/* link the DAI widgets */
|
||||
play_w = codec_dai->playback_widget;
|
||||
capture_w = cpu_dai->capture_widget;
|
||||
@@ -2082,6 +2105,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
|
||||
|
||||
static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
|
||||
|
||||
static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
|
||||
|
||||
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
|
||||
|
||||
gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
|
||||
|
||||
udelay(10);
|
||||
|
||||
gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
|
||||
|
||||
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
|
||||
msleep(2);
|
||||
}
|
||||
|
||||
static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
|
||||
|
||||
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
|
||||
|
||||
gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
|
||||
gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
|
||||
gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
|
||||
|
||||
udelay(10);
|
||||
|
||||
gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
|
||||
|
||||
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
|
||||
msleep(2);
|
||||
}
|
||||
|
||||
static int snd_soc_ac97_parse_pinctl(struct device *dev,
|
||||
struct snd_ac97_reset_cfg *cfg)
|
||||
{
|
||||
struct pinctrl *p;
|
||||
struct pinctrl_state *state;
|
||||
int gpio;
|
||||
int ret;
|
||||
|
||||
p = devm_pinctrl_get(dev);
|
||||
if (IS_ERR(p)) {
|
||||
dev_err(dev, "Failed to get pinctrl\n");
|
||||
return PTR_RET(p);
|
||||
}
|
||||
cfg->pctl = p;
|
||||
|
||||
state = pinctrl_lookup_state(p, "ac97-reset");
|
||||
if (IS_ERR(state)) {
|
||||
dev_err(dev, "Can't find pinctrl state ac97-reset\n");
|
||||
return PTR_RET(state);
|
||||
}
|
||||
cfg->pstate_reset = state;
|
||||
|
||||
state = pinctrl_lookup_state(p, "ac97-warm-reset");
|
||||
if (IS_ERR(state)) {
|
||||
dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
|
||||
return PTR_RET(state);
|
||||
}
|
||||
cfg->pstate_warm_reset = state;
|
||||
|
||||
state = pinctrl_lookup_state(p, "ac97-running");
|
||||
if (IS_ERR(state)) {
|
||||
dev_err(dev, "Can't find pinctrl state ac97-running\n");
|
||||
return PTR_RET(state);
|
||||
}
|
||||
cfg->pstate_run = state;
|
||||
|
||||
gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
|
||||
if (gpio < 0) {
|
||||
dev_err(dev, "Can't find ac97-sync gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
ret = devm_gpio_request(dev, gpio, "AC97 link sync");
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed requesting ac97-sync gpio\n");
|
||||
return ret;
|
||||
}
|
||||
cfg->gpio_sync = gpio;
|
||||
|
||||
gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
|
||||
if (gpio < 0) {
|
||||
dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
|
||||
return gpio;
|
||||
}
|
||||
ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed requesting ac97-sdata gpio\n");
|
||||
return ret;
|
||||
}
|
||||
cfg->gpio_sdata = gpio;
|
||||
|
||||
gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
|
||||
if (gpio < 0) {
|
||||
dev_err(dev, "Can't find ac97-reset gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
ret = devm_gpio_request(dev, gpio, "AC97 link reset");
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed requesting ac97-reset gpio\n");
|
||||
return ret;
|
||||
}
|
||||
cfg->gpio_reset = gpio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_ac97_bus_ops *soc_ac97_ops;
|
||||
EXPORT_SYMBOL_GPL(soc_ac97_ops);
|
||||
|
||||
@@ -2099,6 +2233,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
|
||||
|
||||
/**
|
||||
* snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
|
||||
*
|
||||
* This function sets the reset and warm_reset properties of ops and parses
|
||||
* the device node of pdev to get pinctrl states and gpio numbers to use.
|
||||
*/
|
||||
int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct snd_ac97_reset_cfg cfg;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_set_ac97_ops(ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ops->warm_reset = snd_soc_ac97_warm_reset;
|
||||
ops->reset = snd_soc_ac97_reset;
|
||||
|
||||
snd_ac97_rst_cfg = cfg;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
|
||||
|
||||
/**
|
||||
* snd_soc_free_ac97_codec - free AC97 codec device
|
||||
* @codec: audio codec
|
||||
@@ -2301,6 +2464,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
|
||||
const char *name)
|
||||
{
|
||||
struct snd_card *card = soc_card->snd_card;
|
||||
struct snd_kcontrol *kctl;
|
||||
|
||||
if (unlikely(!name))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(kctl, &card->controls, list)
|
||||
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
|
||||
return kctl;
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
|
||||
|
||||
/**
|
||||
* snd_soc_add_codec_controls - add an array of controls to a codec.
|
||||
* Convenience function to add a list of controls. Many codecs were
|
||||
@@ -2542,59 +2721,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
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
|
||||
*
|
||||
* Callback to provide information about an external enumerated
|
||||
* single mixer.
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*/
|
||||
int snd_soc_info_enum_ext(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 = 1;
|
||||
uinfo->value.enumerated.items = e->max;
|
||||
|
||||
if (uinfo->value.enumerated.item > e->max - 1)
|
||||
uinfo->value.enumerated.item = e->max - 1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
e->texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
|
||||
|
||||
/**
|
||||
* snd_soc_info_volsw_ext - external single mixer info callback
|
||||
* @kcontrol: mixer control
|
||||
* @uinfo: control element information
|
||||
*
|
||||
* Callback to provide information about a single external mixer control.
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*/
|
||||
int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
int max = kcontrol->private_value;
|
||||
|
||||
if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
else
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = max;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
|
||||
|
||||
/**
|
||||
* snd_soc_info_volsw - single mixer info callback
|
||||
* @kcontrol: mixer control
|
||||
@@ -3910,10 +4036,8 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
|
||||
{
|
||||
/* create platform component name */
|
||||
platform->name = fmt_single_name(dev, &platform->id);
|
||||
if (platform->name == NULL) {
|
||||
kfree(platform);
|
||||
if (platform->name == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform->dev = dev;
|
||||
platform->driver = platform_drv;
|
||||
|
Reference in New Issue
Block a user