forked from Minki/linux
Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/ad193x', 'asoc/topic/adau1373' and 'asoc/topic/adau17x1' into asoc-next
This commit is contained in:
commit
0a7e4ca1aa
@ -3,12 +3,15 @@
|
||||
# subsystems should select the appropriate symbols.
|
||||
|
||||
config REGMAP
|
||||
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
|
||||
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
select IRQ_DOMAIN if REGMAP_IRQ
|
||||
bool
|
||||
|
||||
config REGMAP_AC97
|
||||
tristate
|
||||
|
||||
config REGMAP_I2C
|
||||
tristate
|
||||
depends on I2C
|
||||
|
@ -1,6 +1,7 @@
|
||||
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
|
||||
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
|
||||
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
|
||||
obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
|
||||
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
|
||||
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
||||
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
|
||||
|
114
drivers/base/regmap/regmap-ac97.c
Normal file
114
drivers/base/regmap/regmap-ac97.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Register map access API - AC'97 support
|
||||
*
|
||||
* Copyright 2013 Linaro Ltd. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case AC97_RESET:
|
||||
case AC97_POWERDOWN:
|
||||
case AC97_INT_PAGING:
|
||||
case AC97_EXTENDED_ID:
|
||||
case AC97_EXTENDED_STATUS:
|
||||
case AC97_EXTENDED_MID:
|
||||
case AC97_EXTENDED_MSTATUS:
|
||||
case AC97_GPIO_STATUS:
|
||||
case AC97_MISC_AFE:
|
||||
case AC97_VENDOR_ID1:
|
||||
case AC97_VENDOR_ID2:
|
||||
case AC97_CODEC_CLASS_REV:
|
||||
case AC97_PCI_SVID:
|
||||
case AC97_PCI_SID:
|
||||
case AC97_FUNC_SELECT:
|
||||
case AC97_FUNC_INFO:
|
||||
case AC97_SENSE_INFO:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
|
||||
|
||||
static int regmap_ac97_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct snd_ac97 *ac97 = context;
|
||||
|
||||
*val = ac97->bus->ops->read(ac97, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_ac97_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct snd_ac97 *ac97 = context;
|
||||
|
||||
ac97->bus->ops->write(ac97, reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_bus ac97_regmap_bus = {
|
||||
.reg_write = regmap_ac97_reg_write,
|
||||
.reg_read = regmap_ac97_reg_read,
|
||||
};
|
||||
|
||||
/**
|
||||
* regmap_init_ac97(): Initialise AC'97 register map
|
||||
*
|
||||
* @ac97: Device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
* a struct regmap.
|
||||
*/
|
||||
struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_ac97);
|
||||
|
||||
/**
|
||||
* devm_regmap_init_ac97(): Initialise AC'97 register map
|
||||
*
|
||||
* @ac97: Device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
* to a struct regmap. The regmap will be automatically freed by the
|
||||
* device management code.
|
||||
*/
|
||||
struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
@ -27,6 +27,7 @@ struct spmi_device;
|
||||
struct regmap;
|
||||
struct regmap_range_cfg;
|
||||
struct regmap_field;
|
||||
struct snd_ac97;
|
||||
|
||||
/* An enum of all the supported cache types */
|
||||
enum regcache_type {
|
||||
@ -340,6 +341,8 @@ struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
|
||||
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
|
||||
const struct regmap_config *config);
|
||||
|
||||
struct regmap *devm_regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
@ -356,6 +359,10 @@ struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
|
||||
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
|
||||
const struct regmap_config *config);
|
||||
|
||||
bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
|
||||
|
||||
/**
|
||||
* regmap_init_mmio(): Initialise register map
|
||||
|
@ -206,7 +206,6 @@ struct snd_soc_dai_driver {
|
||||
/* DAI description */
|
||||
const char *name;
|
||||
unsigned int id;
|
||||
int ac97_control;
|
||||
unsigned int base;
|
||||
|
||||
/* DAI driver callbacks */
|
||||
@ -216,6 +215,8 @@ struct snd_soc_dai_driver {
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
/* compress dai */
|
||||
bool compress_dai;
|
||||
/* DAI is also used for the control bus */
|
||||
bool bus_control;
|
||||
|
||||
/* ops */
|
||||
const struct snd_soc_dai_ops *ops;
|
||||
@ -241,7 +242,6 @@ struct snd_soc_dai {
|
||||
const char *name;
|
||||
int id;
|
||||
struct device *dev;
|
||||
void *ac97_pdata; /* platform_data for the ac97 codec */
|
||||
|
||||
/* driver ops */
|
||||
struct snd_soc_dai_driver *driver;
|
||||
|
@ -369,8 +369,6 @@ struct snd_soc_jack_gpio;
|
||||
|
||||
typedef int (*hw_write_t)(void *,const char* ,int);
|
||||
|
||||
extern struct snd_ac97_bus_ops *soc_ac97_ops;
|
||||
|
||||
enum snd_soc_pcm_subclass {
|
||||
SND_SOC_PCM_CLASS_PCM = 0,
|
||||
SND_SOC_PCM_CLASS_BE = 1,
|
||||
@ -499,14 +497,28 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
|
||||
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int mask, unsigned int value);
|
||||
|
||||
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
||||
struct snd_ac97_bus_ops *ops, int num);
|
||||
void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
|
||||
#ifdef CONFIG_SND_SOC_AC97_BUS
|
||||
struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec);
|
||||
void snd_soc_free_ac97_codec(struct snd_ac97 *ac97);
|
||||
|
||||
int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
|
||||
int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
|
||||
struct platform_device *pdev);
|
||||
|
||||
extern struct snd_ac97_bus_ops *soc_ac97_ops;
|
||||
#else
|
||||
static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*Controls
|
||||
*/
|
||||
@ -778,11 +790,8 @@ struct snd_soc_codec {
|
||||
struct list_head card_list;
|
||||
|
||||
/* runtime */
|
||||
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
|
||||
unsigned int cache_bypass:1; /* Suppress access to the cache */
|
||||
unsigned int suspended:1; /* Codec is in suspend PM state */
|
||||
unsigned int ac97_registered:1; /* Codec has been AC97 registered */
|
||||
unsigned int ac97_created:1; /* Codec has been created by SoC */
|
||||
unsigned int cache_init:1; /* codec cache has been initialized */
|
||||
|
||||
/* codec IO */
|
||||
@ -1275,6 +1284,45 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
|
||||
int snd_soc_component_test_bits(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask, unsigned int value);
|
||||
|
||||
#ifdef CONFIG_REGMAP
|
||||
|
||||
void snd_soc_component_init_regmap(struct snd_soc_component *component,
|
||||
struct regmap *regmap);
|
||||
void snd_soc_component_exit_regmap(struct snd_soc_component *component);
|
||||
|
||||
/**
|
||||
* snd_soc_codec_init_regmap() - Initialize regmap instance for the CODEC
|
||||
* @codec: The CODEC for which to initialize the regmap instance
|
||||
* @regmap: The regmap instance that should be used by the CODEC
|
||||
*
|
||||
* This function allows deferred assignment of the regmap instance that is
|
||||
* associated with the CODEC. Only use this if the regmap instance is not yet
|
||||
* ready when the CODEC is registered. The function must also be called before
|
||||
* the first IO attempt of the CODEC.
|
||||
*/
|
||||
static inline void snd_soc_codec_init_regmap(struct snd_soc_codec *codec,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
snd_soc_component_init_regmap(&codec->component, regmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_codec_exit_regmap() - De-initialize regmap instance for the CODEC
|
||||
* @codec: The CODEC for which to de-initialize the regmap instance
|
||||
*
|
||||
* Calls regmap_exit() on the regmap instance associated to the CODEC and
|
||||
* removes the regmap instance from the CODEC.
|
||||
*
|
||||
* This function should only be used if snd_soc_codec_init_regmap() was used to
|
||||
* initialize the regmap instance.
|
||||
*/
|
||||
static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_component_exit_regmap(&codec->component);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* device driver data */
|
||||
|
||||
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
|
||||
|
@ -5,6 +5,10 @@ ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
|
||||
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
|
||||
snd-soc-core-objs += soc-ac97.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
|
||||
obj-$(CONFIG_SND_SOC) += codecs/
|
||||
obj-$(CONFIG_SND_SOC) += generic/
|
||||
|
@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
|
||||
|
||||
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
|
||||
.name = "alchemy-ac97c",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.probe = au1xac97c_dai_probe,
|
||||
.playback = {
|
||||
.rates = AC97_RATES,
|
||||
|
@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.probe = au1xpsc_ac97_probe,
|
||||
.playback = {
|
||||
.rates = AC97_RATES,
|
||||
|
@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
|
||||
#endif
|
||||
|
||||
static struct snd_soc_dai_driver bfin_ac97_dai = {
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.suspend = bf5xx_ac97_suspend,
|
||||
.resume = bf5xx_ac97_resume,
|
||||
.playback = {
|
||||
|
@ -46,8 +46,6 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad1980.h"
|
||||
|
||||
#include "bf5xx-ac97.h"
|
||||
|
||||
static struct snd_soc_card bf5xx_board;
|
||||
|
@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
|
||||
static struct snd_soc_dai_driver ep93xx_ac97_dai = {
|
||||
.name = "ep93xx-ac97",
|
||||
.id = 0,
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.probe = ep93xx_ac97_dai_probe,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
|
@ -223,6 +223,7 @@ config SND_SOC_AD193X_I2C
|
||||
select SND_SOC_AD193X
|
||||
|
||||
config SND_SOC_AD1980
|
||||
select REGMAP_AC97
|
||||
tristate
|
||||
|
||||
config SND_SOC_AD73311
|
||||
|
@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
|
||||
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
|
||||
return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
|
||||
return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
|
||||
}
|
||||
|
||||
#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
@ -53,7 +54,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = {
|
||||
|
||||
static struct snd_soc_dai_driver ac97_dai = {
|
||||
.name = "ac97-hifi",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 1,
|
||||
@ -71,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = {
|
||||
|
||||
static int ac97_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97;
|
||||
struct snd_ac97_bus *ac97_bus;
|
||||
struct snd_ac97_template ac97_template;
|
||||
int ret;
|
||||
@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
|
||||
return ret;
|
||||
|
||||
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
|
||||
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
|
||||
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
snd_soc_codec_set_drvdata(codec, ac97);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ac97_soc_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_ac97_suspend(codec->ac97);
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_ac97_suspend(ac97);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ac97_soc_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_ac97_resume(codec->ac97);
|
||||
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_ac97_resume(ac97);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
|
||||
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
|
||||
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
|
||||
SND_SOC_DAPM_VMID("VMID"),
|
||||
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
|
||||
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
|
||||
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
|
||||
@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
|
||||
|
||||
static const struct snd_soc_dapm_route audio_paths[] = {
|
||||
{ "DAC", NULL, "SYSCLK" },
|
||||
{ "DAC Output", NULL, "DAC" },
|
||||
{ "DAC Output", NULL, "VMID" },
|
||||
{ "ADC", NULL, "SYSCLK" },
|
||||
{ "DAC", NULL, "ADC_PWR" },
|
||||
{ "ADC", NULL, "ADC_PWR" },
|
||||
{ "DAC1OUT", NULL, "DAC" },
|
||||
{ "DAC2OUT", NULL, "DAC" },
|
||||
{ "DAC3OUT", NULL, "DAC" },
|
||||
{ "DAC4OUT", NULL, "DAC" },
|
||||
{ "DAC1OUT", NULL, "DAC Output" },
|
||||
{ "DAC2OUT", NULL, "DAC Output" },
|
||||
{ "DAC3OUT", NULL, "DAC Output" },
|
||||
{ "DAC4OUT", NULL, "DAC Output" },
|
||||
{ "ADC", NULL, "ADC1IN" },
|
||||
{ "ADC", NULL, "ADC2IN" },
|
||||
{ "SYSCLK", NULL, "PLL_PWR" },
|
||||
|
@ -24,34 +24,86 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "ad1980.h"
|
||||
static const struct reg_default ad1980_reg_defaults[] = {
|
||||
{ 0x02, 0x8000 },
|
||||
{ 0x04, 0x8000 },
|
||||
{ 0x06, 0x8000 },
|
||||
{ 0x0c, 0x8008 },
|
||||
{ 0x0e, 0x8008 },
|
||||
{ 0x10, 0x8808 },
|
||||
{ 0x12, 0x8808 },
|
||||
{ 0x16, 0x8808 },
|
||||
{ 0x18, 0x8808 },
|
||||
{ 0x1a, 0x0000 },
|
||||
{ 0x1c, 0x8000 },
|
||||
{ 0x20, 0x0000 },
|
||||
{ 0x28, 0x03c7 },
|
||||
{ 0x2c, 0xbb80 },
|
||||
{ 0x2e, 0xbb80 },
|
||||
{ 0x30, 0xbb80 },
|
||||
{ 0x32, 0xbb80 },
|
||||
{ 0x36, 0x8080 },
|
||||
{ 0x38, 0x8080 },
|
||||
{ 0x3a, 0x2000 },
|
||||
{ 0x60, 0x0000 },
|
||||
{ 0x62, 0x0000 },
|
||||
{ 0x72, 0x0000 },
|
||||
{ 0x74, 0x1001 },
|
||||
{ 0x76, 0x0000 },
|
||||
};
|
||||
|
||||
/*
|
||||
* AD1980 register cache
|
||||
*/
|
||||
static const u16 ad1980_reg[] = {
|
||||
0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */
|
||||
0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */
|
||||
0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
|
||||
0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
|
||||
0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
|
||||
0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
|
||||
0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
||||
0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
||||
0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
|
||||
0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */
|
||||
static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case AC97_RESET ... AC97_MASTER_MONO:
|
||||
case AC97_PHONE ... AC97_CD:
|
||||
case AC97_AUX ... AC97_GENERAL_PURPOSE:
|
||||
case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
|
||||
case AC97_SPDIF:
|
||||
case AC97_CODEC_CLASS_REV:
|
||||
case AC97_PCI_SVID:
|
||||
case AC97_AD_CODEC_CFG:
|
||||
case AC97_AD_JACK_SPDIF:
|
||||
case AC97_AD_SERIAL_CFG:
|
||||
case AC97_VENDOR_ID1:
|
||||
case AC97_VENDOR_ID2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case AC97_VENDOR_ID1:
|
||||
case AC97_VENDOR_ID2:
|
||||
return false;
|
||||
default:
|
||||
return ad1980_readable_reg(dev, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config ad1980_regmap_config = {
|
||||
.reg_bits = 16,
|
||||
.reg_stride = 2,
|
||||
.val_bits = 16,
|
||||
.max_register = 0x7e,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.volatile_reg = regmap_ac97_default_volatile,
|
||||
.readable_reg = ad1980_readable_reg,
|
||||
.writeable_reg = ad1980_writeable_reg,
|
||||
|
||||
.reg_defaults = ad1980_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
|
||||
};
|
||||
|
||||
static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
|
||||
@ -134,45 +186,8 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
|
||||
{ "HP_OUT_R", NULL, "Playback" },
|
||||
};
|
||||
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
switch (reg) {
|
||||
case AC97_RESET:
|
||||
case AC97_INT_PAGING:
|
||||
case AC97_POWERDOWN:
|
||||
case AC97_EXTENDED_STATUS:
|
||||
case AC97_VENDOR_ID1:
|
||||
case AC97_VENDOR_ID2:
|
||||
return soc_ac97_ops->read(codec->ac97, reg);
|
||||
default:
|
||||
reg = reg >> 1;
|
||||
|
||||
if (reg >= ARRAY_SIZE(ad1980_reg))
|
||||
return -EINVAL;
|
||||
|
||||
return cache[reg];
|
||||
}
|
||||
}
|
||||
|
||||
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
reg = reg >> 1;
|
||||
if (reg < ARRAY_SIZE(ad1980_reg))
|
||||
cache[reg] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver ad1980_dai = {
|
||||
.name = "ad1980-hifi",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
@ -189,108 +204,115 @@ static struct snd_soc_dai_driver ad1980_dai = {
|
||||
|
||||
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int retry_cnt = 0;
|
||||
|
||||
do {
|
||||
if (try_warm && soc_ac97_ops->warm_reset) {
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
if (ac97_read(codec, AC97_RESET) == 0x0090)
|
||||
soc_ac97_ops->warm_reset(ac97);
|
||||
if (snd_soc_read(codec, AC97_RESET) == 0x0090)
|
||||
return 1;
|
||||
}
|
||||
|
||||
soc_ac97_ops->reset(codec->ac97);
|
||||
soc_ac97_ops->reset(ac97);
|
||||
/*
|
||||
* Set bit 16slot in register 74h, then every slot will has only
|
||||
* 16 bits. This command is sent out in 20bit mode, in which
|
||||
* case the first nibble of data is eaten by the addr. (Tag is
|
||||
* always 16 bit)
|
||||
*/
|
||||
ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
|
||||
snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
|
||||
|
||||
if (ac97_read(codec, AC97_RESET) == 0x0090)
|
||||
if (snd_soc_read(codec, AC97_RESET) == 0x0090)
|
||||
return 0;
|
||||
} while (retry_cnt++ < 10);
|
||||
|
||||
printk(KERN_ERR "AD1980 AC97 reset failed\n");
|
||||
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int ad1980_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
u16 vendor_id2;
|
||||
u16 ext_status;
|
||||
|
||||
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
|
||||
|
||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
|
||||
ac97 = snd_soc_new_ac97_codec(codec);
|
||||
if (IS_ERR(ac97)) {
|
||||
ret = PTR_ERR(ac97);
|
||||
dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ad1980_reset(codec, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
|
||||
goto reset_err;
|
||||
regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
goto err_free_ac97;
|
||||
}
|
||||
|
||||
snd_soc_codec_init_regmap(codec, regmap);
|
||||
snd_soc_codec_set_drvdata(codec, ac97);
|
||||
|
||||
ret = ad1980_reset(codec, 0);
|
||||
if (ret < 0)
|
||||
goto reset_err;
|
||||
|
||||
/* Read out vendor ID to make sure it is ad1980 */
|
||||
if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
|
||||
if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
|
||||
ret = -ENODEV;
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
|
||||
vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
|
||||
|
||||
if (vendor_id2 != 0x5370) {
|
||||
if (vendor_id2 != 0x5374) {
|
||||
ret = -ENODEV;
|
||||
goto reset_err;
|
||||
} else {
|
||||
printk(KERN_WARNING "ad1980: "
|
||||
"Found AD1981 - only 2/2 IN/OUT Channels "
|
||||
"supported\n");
|
||||
dev_warn(codec->dev,
|
||||
"Found AD1981 - only 2/2 IN/OUT Channels supported\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* unmute captures and playbacks volume */
|
||||
ac97_write(codec, AC97_MASTER, 0x0000);
|
||||
ac97_write(codec, AC97_PCM, 0x0000);
|
||||
ac97_write(codec, AC97_REC_GAIN, 0x0000);
|
||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
|
||||
ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
|
||||
snd_soc_write(codec, AC97_MASTER, 0x0000);
|
||||
snd_soc_write(codec, AC97_PCM, 0x0000);
|
||||
snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
|
||||
snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
|
||||
snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
|
||||
|
||||
/*power on LFE/CENTER/Surround DACs*/
|
||||
ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
|
||||
ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
|
||||
|
||||
snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
|
||||
ARRAY_SIZE(ad1980_snd_ac97_controls));
|
||||
ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
|
||||
snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
|
||||
|
||||
return 0;
|
||||
|
||||
reset_err:
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
snd_soc_codec_exit_regmap(codec);
|
||||
err_free_ac97:
|
||||
snd_soc_free_ac97_codec(ac97);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad1980_soc_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_codec_exit_regmap(codec);
|
||||
snd_soc_free_ac97_codec(ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
|
||||
.probe = ad1980_soc_probe,
|
||||
.remove = ad1980_soc_remove,
|
||||
.reg_cache_size = ARRAY_SIZE(ad1980_reg),
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_default = ad1980_reg,
|
||||
.reg_cache_step = 2,
|
||||
.write = ac97_write,
|
||||
.read = ac97_read,
|
||||
|
||||
.controls = ad1980_snd_ac97_controls,
|
||||
.num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
|
||||
.dapm_widgets = ad1980_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
|
||||
.dapm_routes = ad1980_dapm_routes,
|
||||
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* ad1980.h -- ad1980 Soc Audio driver
|
||||
*
|
||||
* WARNING:
|
||||
*
|
||||
* Because Analog Devices Inc. discontinued the ad1980 sound chip since
|
||||
* Sep. 2009, this ad1980 driver is not maintained, tested and supported
|
||||
* by ADI now.
|
||||
*/
|
||||
|
||||
#ifndef _AD1980_H
|
||||
#define _AD1980_H
|
||||
/* Bit definition of Power-Down Control/Status Register */
|
||||
#define ADC 0x0001
|
||||
#define DAC 0x0002
|
||||
#define ANL 0x0004
|
||||
#define REF 0x0008
|
||||
#define PR0 0x0100
|
||||
#define PR1 0x0200
|
||||
#define PR2 0x0400
|
||||
#define PR3 0x0800
|
||||
#define PR4 0x1000
|
||||
#define PR5 0x2000
|
||||
#define PR6 0x4000
|
||||
|
||||
#endif
|
@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
|
||||
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int pll_id = w->name[3] - '1';
|
||||
unsigned int val;
|
||||
@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
|
||||
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_codec *codec = source->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
|
||||
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int dai;
|
||||
const char *clk;
|
||||
@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
|
||||
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_codec *codec = source->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
|
||||
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int dai;
|
||||
|
||||
|
@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
|
||||
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
struct adau *adau = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
/* After any power changes have been made the dejitter circuit
|
||||
* has to be reinitialized. */
|
||||
|
@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
|
||||
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
struct adau *adau = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
/* After any power changes have been made the dejitter circuit
|
||||
|
@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
|
||||
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
struct adau *adau = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
|
@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
|
||||
static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
if (reg > AC97_STAC_PAGE0) {
|
||||
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
soc_ac97_ops->write(ac97, reg, val);
|
||||
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
|
||||
return 0;
|
||||
}
|
||||
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
|
||||
return -EIO;
|
||||
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
soc_ac97_ops->write(ac97, reg, val);
|
||||
cache[reg / 2] = val;
|
||||
return 0;
|
||||
}
|
||||
@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 val = 0, *cache = codec->reg_cache;
|
||||
|
||||
if (reg > AC97_STAC_PAGE0) {
|
||||
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
|
||||
val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
|
||||
val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
|
||||
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
|
||||
return val;
|
||||
}
|
||||
@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
|
||||
reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
|
||||
reg == AC97_VENDOR_ID2) {
|
||||
|
||||
val = soc_ac97_ops->read(codec->ac97, reg);
|
||||
val = soc_ac97_ops->read(ac97, reg);
|
||||
return val;
|
||||
}
|
||||
return cache[reg / 2];
|
||||
@ -240,15 +242,17 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
|
||||
|
||||
static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (try_warm && soc_ac97_ops->warm_reset) {
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
soc_ac97_ops->warm_reset(ac97);
|
||||
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
|
||||
return 1;
|
||||
}
|
||||
|
||||
soc_ac97_ops->reset(codec->ac97);
|
||||
soc_ac97_ops->reset(ac97);
|
||||
if (soc_ac97_ops->warm_reset)
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
soc_ac97_ops->warm_reset(ac97);
|
||||
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
|
||||
return -EIO;
|
||||
return 0;
|
||||
@ -262,6 +266,7 @@ static int stac9766_codec_suspend(struct snd_soc_codec *codec)
|
||||
|
||||
static int stac9766_codec_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 id, reset;
|
||||
|
||||
reset = 0;
|
||||
@ -271,8 +276,8 @@ reset:
|
||||
printk(KERN_ERR "stac9766 failed to resume");
|
||||
return -EIO;
|
||||
}
|
||||
codec->ac97->bus->ops->warm_reset(codec->ac97);
|
||||
id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
|
||||
ac97->bus->ops->warm_reset(ac97);
|
||||
id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
|
||||
if (id != 0x4c13) {
|
||||
stac9766_reset(codec, 0);
|
||||
reset++;
|
||||
@ -294,7 +299,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
|
||||
static struct snd_soc_dai_driver stac9766_dai[] = {
|
||||
{
|
||||
.name = "stac9766-hifi-analog",
|
||||
.ac97_control = 1,
|
||||
|
||||
/* stream cababilities */
|
||||
.playback = {
|
||||
@ -316,7 +320,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
|
||||
},
|
||||
{
|
||||
.name = "stac9766-hifi-IEC958",
|
||||
.ac97_control = 1,
|
||||
|
||||
/* stream cababilities */
|
||||
.playback = {
|
||||
@ -334,11 +337,14 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
|
||||
|
||||
static int stac9766_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97;
|
||||
int ret = 0;
|
||||
|
||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
||||
if (ret < 0)
|
||||
goto codec_err;
|
||||
ac97 = snd_soc_new_ac97_codec(codec);
|
||||
if (IS_ERR(ac97))
|
||||
return PTR_ERR(ac97);
|
||||
|
||||
snd_soc_codec_set_drvdata(codec, ac97);
|
||||
|
||||
/* do a cold reset for the controller and then try
|
||||
* a warm reset followed by an optional cold reset for codec */
|
||||
@ -357,13 +363,15 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
|
||||
return 0;
|
||||
|
||||
codec_err:
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
snd_soc_free_ac97_codec(ac97);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stac9766_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_free_ac97_codec(ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
|
||||
/* We use a register cache to enhance read performance. */
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
switch (reg) {
|
||||
case AC97_RESET:
|
||||
case AC97_VENDOR_ID1:
|
||||
case AC97_VENDOR_ID2:
|
||||
return soc_ac97_ops->read(codec->ac97, reg);
|
||||
return soc_ac97_ops->read(ac97, reg);
|
||||
default:
|
||||
reg = reg >> 1;
|
||||
|
||||
@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
|
||||
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
soc_ac97_ops->write(ac97, reg, val);
|
||||
reg = reg >> 1;
|
||||
if (reg < (ARRAY_SIZE(wm9705_reg)))
|
||||
cache[reg] = val;
|
||||
@ -263,7 +265,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = {
|
||||
static struct snd_soc_dai_driver wm9705_dai[] = {
|
||||
{
|
||||
.name = "wm9705-hifi",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 1,
|
||||
@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
|
||||
|
||||
static int wm9705_reset(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (soc_ac97_ops->reset) {
|
||||
soc_ac97_ops->reset(codec->ac97);
|
||||
soc_ac97_ops->reset(ac97);
|
||||
if (ac97_read(codec, 0) == wm9705_reg[0])
|
||||
return 0; /* Success */
|
||||
}
|
||||
|
||||
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm9705_soc_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
int i, ret;
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
ret = wm9705_reset(codec);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
|
||||
soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
|
||||
soc_ac97_ops->write(ac97, i, cache[i>>1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -335,31 +341,34 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
|
||||
|
||||
static int wm9705_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97;
|
||||
int ret = 0;
|
||||
|
||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
|
||||
ac97 = snd_soc_new_ac97_codec(codec);
|
||||
if (IS_ERR(ac97)) {
|
||||
ret = PTR_ERR(ac97);
|
||||
dev_err(codec->dev, "Failed to register AC97 codec\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_soc_codec_set_drvdata(codec, ac97);
|
||||
|
||||
ret = wm9705_reset(codec);
|
||||
if (ret)
|
||||
goto reset_err;
|
||||
|
||||
snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
|
||||
ARRAY_SIZE(wm9705_snd_ac97_controls));
|
||||
|
||||
return 0;
|
||||
|
||||
reset_err:
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
snd_soc_free_ac97_codec(ac97);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm9705_soc_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_free_ac97_codec(ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.reg_cache_default = wm9705_reg,
|
||||
|
||||
.controls = wm9705_snd_ac97_controls,
|
||||
.num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
|
||||
.dapm_widgets = wm9705_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
|
||||
.dapm_routes = wm9705_audio_map,
|
||||
|
@ -23,6 +23,12 @@
|
||||
#include <sound/tlv.h>
|
||||
#include "wm9712.h"
|
||||
|
||||
struct wm9712_priv {
|
||||
struct snd_ac97 *ac97;
|
||||
unsigned int hp_mixer[2];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
static int ac97_write(struct snd_soc_codec *codec,
|
||||
@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
|
||||
0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
|
||||
0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
|
||||
0x0000, 0x0000 /* virtual hp mixers */
|
||||
};
|
||||
|
||||
/* virtual HP mixers regs */
|
||||
#define HPL_MIXER 0x80
|
||||
#define HPR_MIXER 0x82
|
||||
#define HPL_MIXER 0x0
|
||||
#define HPR_MIXER 0x1
|
||||
|
||||
static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
|
||||
static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
|
||||
@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
|
||||
SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
|
||||
};
|
||||
|
||||
static const unsigned int wm9712_mixer_mute_regs[] = {
|
||||
AC97_VIDEO,
|
||||
AC97_PCM,
|
||||
AC97_LINE,
|
||||
AC97_PHONE,
|
||||
AC97_CD,
|
||||
AC97_PC_BEEP,
|
||||
};
|
||||
|
||||
/* We have to create a fake left and right HP mixers because
|
||||
* the codec only has a single control that is shared by both channels.
|
||||
* This makes it impossible to determine the audio path.
|
||||
*/
|
||||
static int mixer_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u16 l, r, beep, line, phone, mic, pcm, aux;
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val = ucontrol->value.enumerated.item[0];
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int mixer, mask, shift, old;
|
||||
struct snd_soc_dapm_update update;
|
||||
bool change;
|
||||
|
||||
l = ac97_read(w->codec, HPL_MIXER);
|
||||
r = ac97_read(w->codec, HPR_MIXER);
|
||||
beep = ac97_read(w->codec, AC97_PC_BEEP);
|
||||
mic = ac97_read(w->codec, AC97_VIDEO);
|
||||
phone = ac97_read(w->codec, AC97_PHONE);
|
||||
line = ac97_read(w->codec, AC97_LINE);
|
||||
pcm = ac97_read(w->codec, AC97_PCM);
|
||||
aux = ac97_read(w->codec, AC97_CD);
|
||||
mixer = mc->shift >> 8;
|
||||
shift = mc->shift & 0xff;
|
||||
mask = 1 << shift;
|
||||
|
||||
if (l & 0x1 || r & 0x1)
|
||||
ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
|
||||
mutex_lock(&wm9712->lock);
|
||||
old = wm9712->hp_mixer[mixer];
|
||||
if (ucontrol->value.enumerated.item[0])
|
||||
wm9712->hp_mixer[mixer] |= mask;
|
||||
else
|
||||
ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
|
||||
wm9712->hp_mixer[mixer] &= ~mask;
|
||||
|
||||
if (l & 0x2 || r & 0x2)
|
||||
ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
|
||||
change = old != wm9712->hp_mixer[mixer];
|
||||
if (change) {
|
||||
update.kcontrol = kcontrol;
|
||||
update.reg = wm9712_mixer_mute_regs[shift];
|
||||
update.mask = 0x8000;
|
||||
if ((wm9712->hp_mixer[0] & mask) ||
|
||||
(wm9712->hp_mixer[1] & mask))
|
||||
update.val = 0x0;
|
||||
else
|
||||
update.val = 0x8000;
|
||||
|
||||
if (l & 0x4 || r & 0x4)
|
||||
ac97_write(w->codec, AC97_LINE, line & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_LINE, line | 0x8000);
|
||||
snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
|
||||
&update);
|
||||
}
|
||||
|
||||
if (l & 0x8 || r & 0x8)
|
||||
ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
|
||||
mutex_unlock(&wm9712->lock);
|
||||
|
||||
if (l & 0x10 || r & 0x10)
|
||||
ac97_write(w->codec, AC97_CD, aux & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_CD, aux | 0x8000);
|
||||
return change;
|
||||
}
|
||||
|
||||
if (l & 0x20 || r & 0x20)
|
||||
ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
|
||||
static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int shift, mixer;
|
||||
|
||||
mixer = mc->shift >> 8;
|
||||
shift = mc->shift & 0xff;
|
||||
|
||||
ucontrol->value.enumerated.item[0] =
|
||||
(wm9712->hp_mixer[mixer] >> shift) & 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
|
||||
.private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
|
||||
(xmixer << 8) | xshift, 1, 0, 0) \
|
||||
}
|
||||
|
||||
/* Left Headphone Mixers */
|
||||
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
|
||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
|
||||
SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
|
||||
WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
|
||||
WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
|
||||
WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
|
||||
WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
|
||||
WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
|
||||
WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
|
||||
};
|
||||
|
||||
/* Right Headphone Mixers */
|
||||
static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
|
||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
|
||||
SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
|
||||
WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
|
||||
WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
|
||||
WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
|
||||
WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
|
||||
WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
|
||||
WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
|
||||
};
|
||||
|
||||
/* Speaker Mixer */
|
||||
@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
|
||||
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm9712_diff_sel_controls),
|
||||
SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
|
||||
&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
|
||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
||||
SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
|
||||
&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
|
||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
||||
SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
|
||||
&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
|
||||
&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
|
||||
&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
|
||||
@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
||||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
||||
reg == AC97_REC_GAIN)
|
||||
return soc_ac97_ops->read(codec->ac97, reg);
|
||||
return soc_ac97_ops->read(wm9712->ac97, reg);
|
||||
else {
|
||||
reg = reg >> 1;
|
||||
|
||||
@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
if (reg < 0x7c)
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
soc_ac97_ops->write(wm9712->ac97, reg, val);
|
||||
reg = reg >> 1;
|
||||
if (reg < (ARRAY_SIZE(wm9712_reg)))
|
||||
cache[reg] = val;
|
||||
@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
|
||||
static struct snd_soc_dai_driver wm9712_dai[] = {
|
||||
{
|
||||
.name = "wm9712-hifi",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 1,
|
||||
@ -581,21 +616,23 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
|
||||
|
||||
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
|
||||
{
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (try_warm && soc_ac97_ops->warm_reset) {
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
soc_ac97_ops->warm_reset(wm9712->ac97);
|
||||
if (ac97_read(codec, 0) == wm9712_reg[0])
|
||||
return 1;
|
||||
}
|
||||
|
||||
soc_ac97_ops->reset(codec->ac97);
|
||||
soc_ac97_ops->reset(wm9712->ac97);
|
||||
if (soc_ac97_ops->warm_reset)
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
soc_ac97_ops->warm_reset(wm9712->ac97);
|
||||
if (ac97_read(codec, 0) != wm9712_reg[0])
|
||||
goto err;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
printk(KERN_ERR "WM9712 AC97 reset failed\n");
|
||||
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -607,14 +644,13 @@ static int wm9712_soc_suspend(struct snd_soc_codec *codec)
|
||||
|
||||
static int wm9712_soc_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
int i, ret;
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
ret = wm9712_reset(codec, 1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
@ -624,7 +660,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
|
||||
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
|
||||
(i > 0x58 && i != 0x5c))
|
||||
continue;
|
||||
soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
|
||||
soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -633,37 +669,37 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
|
||||
|
||||
static int wm9712_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
|
||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
|
||||
wm9712->ac97 = snd_soc_new_ac97_codec(codec);
|
||||
if (IS_ERR(wm9712->ac97)) {
|
||||
ret = PTR_ERR(wm9712->ac97);
|
||||
dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wm9712_reset(codec, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
|
||||
if (ret < 0)
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
/* set alc mux to none */
|
||||
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
|
||||
|
||||
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
|
||||
ARRAY_SIZE(wm9712_snd_ac97_controls));
|
||||
|
||||
return 0;
|
||||
|
||||
reset_err:
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
snd_soc_free_ac97_codec(wm9712->ac97);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm9712_soc_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_free_ac97_codec(wm9712->ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -679,6 +715,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.reg_cache_default = wm9712_reg,
|
||||
|
||||
.controls = wm9712_snd_ac97_controls,
|
||||
.num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
|
||||
.dapm_widgets = wm9712_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
|
||||
.dapm_routes = wm9712_audio_map,
|
||||
@ -687,6 +726,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
|
||||
|
||||
static int wm9712_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm9712_priv *wm9712;
|
||||
|
||||
wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
|
||||
if (wm9712 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&wm9712->lock);
|
||||
|
||||
platform_set_drvdata(pdev, wm9712);
|
||||
|
||||
return snd_soc_register_codec(&pdev->dev,
|
||||
&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
|
||||
}
|
||||
|
@ -30,7 +30,10 @@
|
||||
#include "wm9713.h"
|
||||
|
||||
struct wm9713_priv {
|
||||
struct snd_ac97 *ac97;
|
||||
u32 pll_in; /* PLL input frequency */
|
||||
unsigned int hp_mixer[2];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
@ -59,13 +62,10 @@ static const u16 wm9713_reg[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0006,
|
||||
0x0001, 0x0000, 0x574d, 0x4c13,
|
||||
0x0000, 0x0000, 0x0000
|
||||
};
|
||||
|
||||
/* virtual HP mixers regs */
|
||||
#define HPL_MIXER 0x80
|
||||
#define HPR_MIXER 0x82
|
||||
#define MICB_MUX 0x82
|
||||
#define HPL_MIXER 0
|
||||
#define HPR_MIXER 1
|
||||
|
||||
static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
|
||||
static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
|
||||
@ -110,7 +110,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
|
||||
SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
|
||||
SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
|
||||
SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
|
||||
SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
|
||||
SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
|
||||
@ -234,6 +234,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const unsigned int wm9713_mixer_mute_regs[] = {
|
||||
AC97_PC_BEEP,
|
||||
AC97_MASTER_TONE,
|
||||
AC97_PHONE,
|
||||
AC97_REC_SEL,
|
||||
AC97_PCM,
|
||||
AC97_AUX,
|
||||
};
|
||||
|
||||
/* We have to create a fake left and right HP mixers because
|
||||
* the codec only has a single control that is shared by both channels.
|
||||
@ -241,73 +249,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
|
||||
* register map, thus we add a new (virtual) register to help determine the
|
||||
* audio route within the device.
|
||||
*/
|
||||
static int mixer_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u16 l, r, beep, tone, phone, rec, pcm, aux;
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val = ucontrol->value.enumerated.item[0];
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int mixer, mask, shift, old;
|
||||
struct snd_soc_dapm_update update;
|
||||
bool change;
|
||||
|
||||
l = ac97_read(w->codec, HPL_MIXER);
|
||||
r = ac97_read(w->codec, HPR_MIXER);
|
||||
beep = ac97_read(w->codec, AC97_PC_BEEP);
|
||||
tone = ac97_read(w->codec, AC97_MASTER_TONE);
|
||||
phone = ac97_read(w->codec, AC97_PHONE);
|
||||
rec = ac97_read(w->codec, AC97_REC_SEL);
|
||||
pcm = ac97_read(w->codec, AC97_PCM);
|
||||
aux = ac97_read(w->codec, AC97_AUX);
|
||||
mixer = mc->shift >> 8;
|
||||
shift = mc->shift & 0xff;
|
||||
mask = (1 << shift);
|
||||
|
||||
if (event & SND_SOC_DAPM_PRE_REG)
|
||||
return 0;
|
||||
if ((l & 0x1) || (r & 0x1))
|
||||
ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
|
||||
mutex_lock(&wm9713->lock);
|
||||
old = wm9713->hp_mixer[mixer];
|
||||
if (ucontrol->value.enumerated.item[0])
|
||||
wm9713->hp_mixer[mixer] |= mask;
|
||||
else
|
||||
ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
|
||||
wm9713->hp_mixer[mixer] &= ~mask;
|
||||
|
||||
if ((l & 0x2) || (r & 0x2))
|
||||
ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
|
||||
change = old != wm9713->hp_mixer[mixer];
|
||||
if (change) {
|
||||
update.kcontrol = kcontrol;
|
||||
update.reg = wm9713_mixer_mute_regs[shift];
|
||||
update.mask = 0x8000;
|
||||
if ((wm9713->hp_mixer[0] & mask) ||
|
||||
(wm9713->hp_mixer[1] & mask))
|
||||
update.val = 0x0;
|
||||
else
|
||||
update.val = 0x8000;
|
||||
|
||||
if ((l & 0x4) || (r & 0x4))
|
||||
ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
|
||||
snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
|
||||
&update);
|
||||
}
|
||||
|
||||
if ((l & 0x8) || (r & 0x8))
|
||||
ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
|
||||
mutex_unlock(&wm9713->lock);
|
||||
|
||||
if ((l & 0x10) || (r & 0x10))
|
||||
ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
|
||||
return change;
|
||||
}
|
||||
|
||||
if ((l & 0x20) || (r & 0x20))
|
||||
ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
|
||||
else
|
||||
ac97_write(w->codec, AC97_AUX, aux | 0x8000);
|
||||
static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int mixer, shift;
|
||||
|
||||
mixer = mc->shift >> 8;
|
||||
shift = mc->shift & 0xff;
|
||||
|
||||
ucontrol->value.enumerated.item[0] =
|
||||
(wm9713->hp_mixer[mixer] >> shift) & 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
|
||||
.private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
|
||||
xshift, xmixer, 1, 0, 0) \
|
||||
}
|
||||
|
||||
/* Left Headphone Mixers */
|
||||
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
|
||||
SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
|
||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
|
||||
WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
|
||||
WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
|
||||
WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
|
||||
WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
|
||||
WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
|
||||
WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
|
||||
};
|
||||
|
||||
/* Right Headphone Mixers */
|
||||
static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
|
||||
SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
|
||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
|
||||
WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
|
||||
WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
|
||||
WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
|
||||
WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
|
||||
WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
|
||||
WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
|
||||
};
|
||||
|
||||
/* headphone capture mux */
|
||||
@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm9713_mic_sel_mux_controls),
|
||||
SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm9713_micb_sel_mux_controls),
|
||||
SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
|
||||
&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
|
||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
||||
SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
|
||||
&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
|
||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
||||
SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
|
||||
&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
|
||||
&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
|
||||
&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
|
||||
@ -647,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
||||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
||||
reg == AC97_CD)
|
||||
return soc_ac97_ops->read(codec->ac97, reg);
|
||||
return soc_ac97_ops->read(wm9713->ac97, reg);
|
||||
else {
|
||||
reg = reg >> 1;
|
||||
|
||||
@ -666,9 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
u16 *cache = codec->reg_cache;
|
||||
if (reg < 0x7c)
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
soc_ac97_ops->write(wm9713->ac97, reg, val);
|
||||
reg = reg >> 1;
|
||||
if (reg < (ARRAY_SIZE(wm9713_reg)))
|
||||
cache[reg] = val;
|
||||
@ -689,7 +719,8 @@ struct _pll_div {
|
||||
* to allow rounding later */
|
||||
#define FIXED_PLL_SIZE ((1 << 22) * 10)
|
||||
|
||||
static void pll_factors(struct _pll_div *pll_div, unsigned int source)
|
||||
static void pll_factors(struct snd_soc_codec *codec,
|
||||
struct _pll_div *pll_div, unsigned int source)
|
||||
{
|
||||
u64 Kpart;
|
||||
unsigned int K, Ndiv, Nmod, target;
|
||||
@ -724,7 +755,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
|
||||
|
||||
Ndiv = target / source;
|
||||
if ((Ndiv < 5) || (Ndiv > 12))
|
||||
printk(KERN_WARNING
|
||||
dev_warn(codec->dev,
|
||||
"WM9713 PLL N value %u out of recommended range!\n",
|
||||
Ndiv);
|
||||
|
||||
@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
pll_factors(&pll_div, freq_in);
|
||||
pll_factors(codec, &pll_div, freq_in);
|
||||
|
||||
if (pll_div.k == 0) {
|
||||
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
|
||||
@ -1049,7 +1080,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
|
||||
static struct snd_soc_dai_driver wm9713_dai[] = {
|
||||
{
|
||||
.name = "wm9713-hifi",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 1,
|
||||
@ -1095,17 +1125,22 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
|
||||
|
||||
int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (try_warm && soc_ac97_ops->warm_reset) {
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
soc_ac97_ops->warm_reset(wm9713->ac97);
|
||||
if (ac97_read(codec, 0) == wm9713_reg[0])
|
||||
return 1;
|
||||
}
|
||||
|
||||
soc_ac97_ops->reset(codec->ac97);
|
||||
soc_ac97_ops->reset(wm9713->ac97);
|
||||
if (soc_ac97_ops->warm_reset)
|
||||
soc_ac97_ops->warm_reset(codec->ac97);
|
||||
if (ac97_read(codec, 0) != wm9713_reg[0])
|
||||
soc_ac97_ops->warm_reset(wm9713->ac97);
|
||||
if (ac97_read(codec, 0) != wm9713_reg[0]) {
|
||||
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm9713_reset);
|
||||
@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
ret = wm9713_reset(codec, 1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
@ -1180,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
||||
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
|
||||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
|
||||
continue;
|
||||
soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
|
||||
soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1189,26 +1222,19 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
||||
|
||||
static int wm9713_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm9713_priv *wm9713;
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0, reg;
|
||||
|
||||
wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
|
||||
if (wm9713 == NULL)
|
||||
return -ENOMEM;
|
||||
snd_soc_codec_set_drvdata(codec, wm9713);
|
||||
|
||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
||||
if (ret < 0)
|
||||
goto codec_err;
|
||||
wm9713->ac97 = snd_soc_new_ac97_codec(codec);
|
||||
if (IS_ERR(wm9713->ac97))
|
||||
return PTR_ERR(wm9713->ac97);
|
||||
|
||||
/* do a cold reset for the controller and then try
|
||||
* a warm reset followed by an optional cold reset for codec */
|
||||
wm9713_reset(codec, 0);
|
||||
ret = wm9713_reset(codec, 1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
|
||||
if (ret < 0)
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
@ -1216,23 +1242,18 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
|
||||
reg = ac97_read(codec, AC97_CD) & 0x7fff;
|
||||
ac97_write(codec, AC97_CD, reg);
|
||||
|
||||
snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
|
||||
ARRAY_SIZE(wm9713_snd_ac97_controls));
|
||||
|
||||
return 0;
|
||||
|
||||
reset_err:
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
codec_err:
|
||||
kfree(wm9713);
|
||||
snd_soc_free_ac97_codec(wm9713->ac97);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm9713_soc_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
snd_soc_free_ac97_codec(codec);
|
||||
kfree(wm9713);
|
||||
|
||||
snd_soc_free_ac97_codec(wm9713->ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1248,6 +1269,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.reg_cache_default = wm9713_reg,
|
||||
|
||||
.controls = wm9713_snd_ac97_controls,
|
||||
.num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
|
||||
.dapm_widgets = wm9713_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
|
||||
.dapm_routes = wm9713_audio_map,
|
||||
@ -1256,6 +1280,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
|
||||
|
||||
static int wm9713_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm9713_priv *wm9713;
|
||||
|
||||
wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
|
||||
if (wm9713 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&wm9713->lock);
|
||||
|
||||
platform_set_drvdata(pdev, wm9713);
|
||||
|
||||
return snd_soc_register_codec(&pdev->dev,
|
||||
&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
|
||||
}
|
||||
|
@ -1099,7 +1099,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
|
@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = {
|
||||
|
||||
static struct snd_soc_dai_driver imx_ac97_dai = {
|
||||
.probe = imx_ssi_dai_probe,
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
|
@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
goto capture_alloc_err;
|
||||
}
|
||||
|
||||
if (rtd->codec->ac97)
|
||||
rtd->codec->ac97->private_data = psc_dma;
|
||||
|
||||
return 0;
|
||||
|
||||
capture_alloc_err:
|
||||
|
@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
|
||||
static struct snd_soc_dai_driver psc_ac97_dai[] = {
|
||||
{
|
||||
.name = "mpc5200-psc-ac97.0",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.probe = psc_ac97_probe,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
|
||||
},
|
||||
{
|
||||
.name = "mpc5200-psc-ac97.1",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.stream_name = "AC97 SPDIF",
|
||||
.channels_min = 1,
|
||||
@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = {
|
||||
static int psc_ac97_of_probe(struct platform_device *op)
|
||||
{
|
||||
int rc;
|
||||
struct snd_ac97 ac97;
|
||||
struct mpc52xx_psc __iomem *regs;
|
||||
|
||||
rc = mpc5200_audio_dma_create(op);
|
||||
@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op)
|
||||
|
||||
psc_dma = dev_get_drvdata(&op->dev);
|
||||
regs = psc_dma->psc_regs;
|
||||
ac97.private_data = psc_dma;
|
||||
|
||||
psc_dma->imr = 0;
|
||||
out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
|
||||
|
@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
|
||||
static struct snd_soc_dai_driver nuc900_ac97_dai = {
|
||||
.probe = nuc900_ac97_probe,
|
||||
.remove = nuc900_ac97_remove,
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
|
@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
|
||||
static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
||||
{
|
||||
.name = "pxa2xx-ac97",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
||||
},
|
||||
{
|
||||
.name = "pxa2xx-ac97-aux",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Aux Playback",
|
||||
.channels_min = 1,
|
||||
@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
||||
},
|
||||
{
|
||||
.name = "pxa2xx-ac97-mic",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.capture = {
|
||||
.stream_name = "AC97 Mic Capture",
|
||||
.channels_min = 1,
|
||||
|
@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
|
||||
static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
||||
[S3C_AC97_DAI_PCM] = {
|
||||
.name = "samsung-ac97",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
||||
},
|
||||
[S3C_AC97_DAI_MIC] = {
|
||||
.name = "samsung-ac97-mic",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.capture = {
|
||||
.stream_name = "AC97 Mic Capture",
|
||||
.channels_min = 1,
|
||||
|
@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
|
||||
static struct snd_soc_dai_driver sh4_hac_dai[] = {
|
||||
{
|
||||
.name = "hac-dai.0",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.playback = {
|
||||
.rates = AC97_RATES,
|
||||
.formats = AC97_FMTS,
|
||||
|
256
sound/soc/soc-ac97.c
Normal file
256
sound/soc/soc-ac97.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* soc-ac97.c -- ALSA SoC Audio Layer AC97 support
|
||||
*
|
||||
* 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/ctype.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static struct snd_ac97_bus soc_ac97_bus = {
|
||||
.ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
|
||||
};
|
||||
|
||||
static void soc_ac97_device_release(struct device *dev)
|
||||
{
|
||||
kfree(to_ac97_t(dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_new_ac97_codec - initailise AC97 device
|
||||
* @codec: audio codec
|
||||
*
|
||||
* Initialises AC97 codec resources for use by ad-hoc devices only.
|
||||
*/
|
||||
struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97 *ac97;
|
||||
int ret;
|
||||
|
||||
ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
|
||||
if (ac97 == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ac97->bus = &soc_ac97_bus;
|
||||
ac97->num = 0;
|
||||
|
||||
ac97->dev.bus = &ac97_bus_type;
|
||||
ac97->dev.parent = codec->component.card->dev;
|
||||
ac97->dev.release = soc_ac97_device_release;
|
||||
|
||||
dev_set_name(&ac97->dev, "%d-%d:%s",
|
||||
codec->component.card->snd_card->number, 0,
|
||||
codec->component.name);
|
||||
|
||||
ret = device_register(&ac97->dev);
|
||||
if (ret) {
|
||||
put_device(&ac97->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return ac97;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
|
||||
|
||||
/**
|
||||
* snd_soc_free_ac97_codec - free AC97 codec device
|
||||
* @codec: audio codec
|
||||
*
|
||||
* Frees AC97 codec device resources.
|
||||
*/
|
||||
void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
|
||||
{
|
||||
device_del(&ac97->dev);
|
||||
ac97->bus = NULL;
|
||||
put_device(&ac97->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_free_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_ERR(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_ERR(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_ERR(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_ERR(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);
|
||||
|
||||
int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
|
||||
{
|
||||
if (ops == soc_ac97_ops)
|
||||
return 0;
|
||||
|
||||
if (soc_ac97_ops && ops)
|
||||
return -EBUSY;
|
||||
|
||||
soc_ac97_ops = ops;
|
||||
soc_ac97_bus.ops = ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
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);
|
@ -34,9 +34,6 @@
|
||||
#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>
|
||||
#include <sound/pcm.h>
|
||||
@ -69,16 +66,6 @@ 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)
|
||||
@ -496,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (codec->ac97->dev.bus)
|
||||
device_unregister(&codec->ac97->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stop no dev release warning */
|
||||
static void soc_ac97_device_release(struct device *dev){}
|
||||
|
||||
/* register ac97 codec to bus */
|
||||
static int soc_ac97_dev_register(struct snd_soc_codec *codec)
|
||||
{
|
||||
int err;
|
||||
|
||||
codec->ac97->dev.bus = &ac97_bus_type;
|
||||
codec->ac97->dev.parent = codec->component.card->dev;
|
||||
codec->ac97->dev.release = soc_ac97_device_release;
|
||||
|
||||
dev_set_name(&codec->ac97->dev, "%d-%d:%s",
|
||||
codec->component.card->snd_card->number, 0,
|
||||
codec->component.name);
|
||||
err = device_register(&codec->ac97->dev);
|
||||
if (err < 0) {
|
||||
dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
|
||||
codec->ac97->dev.bus = NULL;
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void codec2codec_close_delayed_work(struct work_struct *work)
|
||||
{
|
||||
/* Currently nothing to do for c2c links
|
||||
@ -593,7 +546,7 @@ int snd_soc_suspend(struct device *dev)
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
|
||||
if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
|
||||
cpu_dai->driver->suspend(cpu_dai);
|
||||
}
|
||||
|
||||
@ -667,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
|
||||
if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
|
||||
cpu_dai->driver->suspend(cpu_dai);
|
||||
|
||||
/* deactivate pins to sleep state */
|
||||
@ -703,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
|
||||
if (card->resume_pre)
|
||||
card->resume_pre(card);
|
||||
|
||||
/* resume AC97 DAIs */
|
||||
/* resume control bus DAIs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
|
||||
if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
|
||||
cpu_dai->driver->resume(cpu_dai);
|
||||
}
|
||||
|
||||
@ -770,7 +723,7 @@ static void soc_resume_deferred(struct work_struct *work)
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
|
||||
if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
|
||||
cpu_dai->driver->resume(cpu_dai);
|
||||
}
|
||||
|
||||
@ -791,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
|
||||
int snd_soc_resume(struct device *dev)
|
||||
{
|
||||
struct snd_soc_card *card = dev_get_drvdata(dev);
|
||||
int i, ac97_control = 0;
|
||||
bool bus_control = false;
|
||||
int i;
|
||||
|
||||
/* If the card is not initialized yet there is nothing to do */
|
||||
if (!card->instantiated)
|
||||
@ -814,17 +768,18 @@ int snd_soc_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
/* AC97 devices might have other drivers hanging off them so
|
||||
* need to resume immediately. Other drivers don't have that
|
||||
* problem and may take a substantial amount of time to resume
|
||||
/*
|
||||
* DAIs that also act as the control bus master might have other drivers
|
||||
* hanging off them so need to resume immediately. Other drivers don't
|
||||
* have that problem and may take a substantial amount of time to resume
|
||||
* due to I/O costs and anti-pop so handle them out of line.
|
||||
*/
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
ac97_control |= cpu_dai->driver->ac97_control;
|
||||
bus_control |= cpu_dai->driver->bus_control;
|
||||
}
|
||||
if (ac97_control) {
|
||||
dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
|
||||
if (bus_control) {
|
||||
dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
|
||||
soc_resume_deferred(&card->deferred_resume_work);
|
||||
} else {
|
||||
dev_dbg(dev, "ASoC: Scheduling resume work\n");
|
||||
@ -1387,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
||||
}
|
||||
}
|
||||
|
||||
/* add platform data for AC97 devices */
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
if (rtd->codec_dais[i]->driver->ac97_control)
|
||||
snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
|
||||
rtd->cpu_dai->ac97_pdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_SOC_AC97_BUS
|
||||
static int soc_register_ac97_codec(struct snd_soc_codec *codec,
|
||||
struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Only instantiate AC97 if not already done by the adaptor
|
||||
* for the generic AC97 subsystem.
|
||||
*/
|
||||
if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
|
||||
/*
|
||||
* It is possible that the AC97 device is already registered to
|
||||
* the device subsystem. This happens when the device is created
|
||||
* via snd_ac97_mixer(). Currently only SoC codec that does so
|
||||
* is the generic AC97 glue but others migh emerge.
|
||||
*
|
||||
* In those cases we don't try to register the device again.
|
||||
*/
|
||||
if (!codec->ac97_created)
|
||||
return 0;
|
||||
|
||||
ret = soc_ac97_dev_register(codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev,
|
||||
"ASoC: AC97 device register failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
codec->ac97_registered = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
|
||||
{
|
||||
if (codec->ac97_registered) {
|
||||
soc_ac97_dev_unregister(codec);
|
||||
codec->ac97_registered = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
|
||||
|
||||
ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
|
||||
if (ret) {
|
||||
while (--i >= 0)
|
||||
soc_unregister_ac97_codec(codec_dai->codec);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < rtd->num_codecs; i++)
|
||||
soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
|
||||
@ -1758,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
||||
goto probe_aux_dev_err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_SOC_AC97_BUS
|
||||
/* register any AC97 codecs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
ret = soc_register_ac97_dai_link(&card->rtd[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: failed to register AC97: %d\n", ret);
|
||||
while (--i >= 0)
|
||||
soc_unregister_ac97_dai_link(&card->rtd[i]);
|
||||
goto probe_aux_dev_err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
card->instantiated = 1;
|
||||
snd_soc_dapm_sync(&card->dapm);
|
||||
mutex_unlock(&card->mutex);
|
||||
@ -1913,216 +1779,6 @@ static struct platform_driver soc_driver = {
|
||||
.remove = soc_remove,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_soc_new_ac97_codec - initailise AC97 device
|
||||
* @codec: audio codec
|
||||
* @ops: AC97 bus operations
|
||||
* @num: AC97 codec number
|
||||
*
|
||||
* Initialises AC97 codec resources for use by ad-hoc devices only.
|
||||
*/
|
||||
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
||||
struct snd_ac97_bus_ops *ops, int num)
|
||||
{
|
||||
codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
|
||||
if (codec->ac97 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
|
||||
if (codec->ac97->bus == NULL) {
|
||||
kfree(codec->ac97);
|
||||
codec->ac97 = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
codec->ac97->bus->ops = ops;
|
||||
codec->ac97->num = num;
|
||||
|
||||
/*
|
||||
* Mark the AC97 device to be created by us. This way we ensure that the
|
||||
* device will be registered with the device subsystem later on.
|
||||
*/
|
||||
codec->ac97_created = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
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_ERR(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_ERR(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_ERR(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_ERR(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);
|
||||
|
||||
int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
|
||||
{
|
||||
if (ops == soc_ac97_ops)
|
||||
return 0;
|
||||
|
||||
if (soc_ac97_ops && ops)
|
||||
return -EBUSY;
|
||||
|
||||
soc_ac97_ops = ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
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
|
||||
*
|
||||
* Frees AC97 codec device resources.
|
||||
*/
|
||||
void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
|
||||
{
|
||||
#ifdef CONFIG_SND_SOC_AC97_BUS
|
||||
soc_unregister_ac97_codec(codec);
|
||||
#endif
|
||||
kfree(codec->ac97->bus);
|
||||
kfree(codec->ac97);
|
||||
codec->ac97 = NULL;
|
||||
codec->ac97_created = 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
|
||||
|
||||
/**
|
||||
* snd_soc_cnew - create new control
|
||||
* @_template: control template
|
||||
@ -2947,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_soc_component_init_regmap(struct snd_soc_component *component)
|
||||
static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
|
||||
{
|
||||
if (!component->regmap)
|
||||
component->regmap = dev_get_regmap(component->dev, NULL);
|
||||
if (component->regmap) {
|
||||
int val_bytes = regmap_get_val_bytes(component->regmap);
|
||||
/* Errors are legitimate for non-integer byte multiples */
|
||||
if (val_bytes > 0)
|
||||
component->val_bytes = val_bytes;
|
||||
}
|
||||
int val_bytes = regmap_get_val_bytes(component->regmap);
|
||||
|
||||
/* Errors are legitimate for non-integer byte multiples */
|
||||
if (val_bytes > 0)
|
||||
component->val_bytes = val_bytes;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGMAP
|
||||
|
||||
/**
|
||||
* snd_soc_component_init_regmap() - Initialize regmap instance for the component
|
||||
* @component: The component for which to initialize the regmap instance
|
||||
* @regmap: The regmap instance that should be used by the component
|
||||
*
|
||||
* This function allows deferred assignment of the regmap instance that is
|
||||
* associated with the component. Only use this if the regmap instance is not
|
||||
* yet ready when the component is registered. The function must also be called
|
||||
* before the first IO attempt of the component.
|
||||
*/
|
||||
void snd_soc_component_init_regmap(struct snd_soc_component *component,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
component->regmap = regmap;
|
||||
snd_soc_component_setup_regmap(component);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
|
||||
|
||||
/**
|
||||
* snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
|
||||
* @component: The component for which to de-initialize the regmap instance
|
||||
*
|
||||
* Calls regmap_exit() on the regmap instance associated to the component and
|
||||
* removes the regmap instance from the component.
|
||||
*
|
||||
* This function should only be used if snd_soc_component_init_regmap() was used
|
||||
* to initialize the regmap instance.
|
||||
*/
|
||||
void snd_soc_component_exit_regmap(struct snd_soc_component *component)
|
||||
{
|
||||
regmap_exit(component->regmap);
|
||||
component->regmap = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
|
||||
|
||||
#endif
|
||||
|
||||
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
|
||||
{
|
||||
if (!component->write && !component->read)
|
||||
snd_soc_component_init_regmap(component);
|
||||
if (!component->write && !component->read) {
|
||||
if (!component->regmap)
|
||||
component->regmap = dev_get_regmap(component->dev, NULL);
|
||||
if (component->regmap)
|
||||
snd_soc_component_setup_regmap(component);
|
||||
}
|
||||
|
||||
list_add(&component->list, &component_list);
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
|
||||
|
||||
static struct snd_soc_dai_driver tegra20_ac97_dai = {
|
||||
.name = "tegra-ac97-pcm",
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.probe = tegra20_ac97_probe,
|
||||
.playback = {
|
||||
.stream_name = "PCM Playback",
|
||||
|
@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
|
||||
.ac97_control = 1,
|
||||
.bus_control = true,
|
||||
.probe = txx9aclc_ac97_probe,
|
||||
.remove = txx9aclc_ac97_remove,
|
||||
.playback = {
|
||||
|
Loading…
Reference in New Issue
Block a user