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.
|
# subsystems should select the appropriate symbols.
|
||||||
|
|
||||||
config REGMAP
|
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_COMPRESS
|
||||||
select LZO_DECOMPRESS
|
select LZO_DECOMPRESS
|
||||||
select IRQ_DOMAIN if REGMAP_IRQ
|
select IRQ_DOMAIN if REGMAP_IRQ
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config REGMAP_AC97
|
||||||
|
tristate
|
||||||
|
|
||||||
config REGMAP_I2C
|
config REGMAP_I2C
|
||||||
tristate
|
tristate
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
|
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
|
||||||
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
|
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
|
||||||
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.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_I2C) += regmap-i2c.o
|
||||||
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
||||||
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.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;
|
||||||
struct regmap_range_cfg;
|
struct regmap_range_cfg;
|
||||||
struct regmap_field;
|
struct regmap_field;
|
||||||
|
struct snd_ac97;
|
||||||
|
|
||||||
/* An enum of all the supported cache types */
|
/* An enum of all the supported cache types */
|
||||||
enum regcache_type {
|
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,
|
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||||
void __iomem *regs,
|
void __iomem *regs,
|
||||||
const struct regmap_config *config);
|
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,
|
struct regmap *devm_regmap_init(struct device *dev,
|
||||||
const struct regmap_bus *bus,
|
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,
|
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||||
void __iomem *regs,
|
void __iomem *regs,
|
||||||
const struct regmap_config *config);
|
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
|
* regmap_init_mmio(): Initialise register map
|
||||||
|
@ -206,7 +206,6 @@ struct snd_soc_dai_driver {
|
|||||||
/* DAI description */
|
/* DAI description */
|
||||||
const char *name;
|
const char *name;
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
int ac97_control;
|
|
||||||
unsigned int base;
|
unsigned int base;
|
||||||
|
|
||||||
/* DAI driver callbacks */
|
/* DAI driver callbacks */
|
||||||
@ -216,6 +215,8 @@ struct snd_soc_dai_driver {
|
|||||||
int (*resume)(struct snd_soc_dai *dai);
|
int (*resume)(struct snd_soc_dai *dai);
|
||||||
/* compress dai */
|
/* compress dai */
|
||||||
bool compress_dai;
|
bool compress_dai;
|
||||||
|
/* DAI is also used for the control bus */
|
||||||
|
bool bus_control;
|
||||||
|
|
||||||
/* ops */
|
/* ops */
|
||||||
const struct snd_soc_dai_ops *ops;
|
const struct snd_soc_dai_ops *ops;
|
||||||
@ -241,7 +242,6 @@ struct snd_soc_dai {
|
|||||||
const char *name;
|
const char *name;
|
||||||
int id;
|
int id;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void *ac97_pdata; /* platform_data for the ac97 codec */
|
|
||||||
|
|
||||||
/* driver ops */
|
/* driver ops */
|
||||||
struct snd_soc_dai_driver *driver;
|
struct snd_soc_dai_driver *driver;
|
||||||
|
@ -369,8 +369,6 @@ struct snd_soc_jack_gpio;
|
|||||||
|
|
||||||
typedef int (*hw_write_t)(void *,const char* ,int);
|
typedef int (*hw_write_t)(void *,const char* ,int);
|
||||||
|
|
||||||
extern struct snd_ac97_bus_ops *soc_ac97_ops;
|
|
||||||
|
|
||||||
enum snd_soc_pcm_subclass {
|
enum snd_soc_pcm_subclass {
|
||||||
SND_SOC_PCM_CLASS_PCM = 0,
|
SND_SOC_PCM_CLASS_PCM = 0,
|
||||||
SND_SOC_PCM_CLASS_BE = 1,
|
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,
|
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
unsigned int mask, unsigned int value);
|
unsigned int mask, unsigned int value);
|
||||||
|
|
||||||
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
#ifdef CONFIG_SND_SOC_AC97_BUS
|
||||||
struct snd_ac97_bus_ops *ops, int num);
|
struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec);
|
||||||
void snd_soc_free_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(struct snd_ac97_bus_ops *ops);
|
||||||
int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
|
int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
|
||||||
struct platform_device *pdev);
|
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
|
*Controls
|
||||||
*/
|
*/
|
||||||
@ -778,11 +790,8 @@ struct snd_soc_codec {
|
|||||||
struct list_head card_list;
|
struct list_head card_list;
|
||||||
|
|
||||||
/* runtime */
|
/* runtime */
|
||||||
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
|
|
||||||
unsigned int cache_bypass:1; /* Suppress access to the cache */
|
unsigned int cache_bypass:1; /* Suppress access to the cache */
|
||||||
unsigned int suspended:1; /* Codec is in suspend PM state */
|
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 */
|
unsigned int cache_init:1; /* codec cache has been initialized */
|
||||||
|
|
||||||
/* codec IO */
|
/* 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,
|
int snd_soc_component_test_bits(struct snd_soc_component *component,
|
||||||
unsigned int reg, unsigned int mask, unsigned int value);
|
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 */
|
/* device driver data */
|
||||||
|
|
||||||
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
|
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
|
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
|
||||||
endif
|
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) += snd-soc-core.o
|
||||||
obj-$(CONFIG_SND_SOC) += codecs/
|
obj-$(CONFIG_SND_SOC) += codecs/
|
||||||
obj-$(CONFIG_SND_SOC) += generic/
|
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 = {
|
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
|
||||||
.name = "alchemy-ac97c",
|
.name = "alchemy-ac97c",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.probe = au1xac97c_dai_probe,
|
.probe = au1xac97c_dai_probe,
|
||||||
.playback = {
|
.playback = {
|
||||||
.rates = AC97_RATES,
|
.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 = {
|
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.probe = au1xpsc_ac97_probe,
|
.probe = au1xpsc_ac97_probe,
|
||||||
.playback = {
|
.playback = {
|
||||||
.rates = AC97_RATES,
|
.rates = AC97_RATES,
|
||||||
|
@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct snd_soc_dai_driver bfin_ac97_dai = {
|
static struct snd_soc_dai_driver bfin_ac97_dai = {
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.suspend = bf5xx_ac97_suspend,
|
.suspend = bf5xx_ac97_suspend,
|
||||||
.resume = bf5xx_ac97_resume,
|
.resume = bf5xx_ac97_resume,
|
||||||
.playback = {
|
.playback = {
|
||||||
|
@ -46,8 +46,6 @@
|
|||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <asm/portmux.h>
|
#include <asm/portmux.h>
|
||||||
|
|
||||||
#include "../codecs/ad1980.h"
|
|
||||||
|
|
||||||
#include "bf5xx-ac97.h"
|
#include "bf5xx-ac97.h"
|
||||||
|
|
||||||
static struct snd_soc_card bf5xx_board;
|
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 = {
|
static struct snd_soc_dai_driver ep93xx_ac97_dai = {
|
||||||
.name = "ep93xx-ac97",
|
.name = "ep93xx-ac97",
|
||||||
.id = 0,
|
.id = 0,
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.probe = ep93xx_ac97_dai_probe,
|
.probe = ep93xx_ac97_dai_probe,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
|
@ -223,6 +223,7 @@ config SND_SOC_AD193X_I2C
|
|||||||
select SND_SOC_AD193X
|
select SND_SOC_AD193X
|
||||||
|
|
||||||
config SND_SOC_AD1980
|
config SND_SOC_AD1980
|
||||||
|
select REGMAP_AC97
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
config SND_SOC_AD73311
|
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_dai *dai)
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = dai->codec;
|
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) ?
|
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
|
||||||
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
|
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 |\
|
#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 = {
|
static struct snd_soc_dai_driver ac97_dai = {
|
||||||
.name = "ac97-hifi",
|
.name = "ac97-hifi",
|
||||||
.ac97_control = 1,
|
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
.channels_min = 1,
|
.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)
|
static int ac97_soc_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97;
|
||||||
struct snd_ac97_bus *ac97_bus;
|
struct snd_ac97_bus *ac97_bus;
|
||||||
struct snd_ac97_template ac97_template;
|
struct snd_ac97_template ac97_template;
|
||||||
int ret;
|
int ret;
|
||||||
@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
snd_soc_codec_set_drvdata(codec, ac97);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int ac97_soc_suspend(struct snd_soc_codec *codec)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ac97_soc_resume(struct snd_soc_codec *codec)
|
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;
|
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[] = {
|
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_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("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("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_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
|
||||||
|
SND_SOC_DAPM_VMID("VMID"),
|
||||||
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
|
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
|
||||||
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
|
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
|
||||||
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
|
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[] = {
|
static const struct snd_soc_dapm_route audio_paths[] = {
|
||||||
{ "DAC", NULL, "SYSCLK" },
|
{ "DAC", NULL, "SYSCLK" },
|
||||||
|
{ "DAC Output", NULL, "DAC" },
|
||||||
|
{ "DAC Output", NULL, "VMID" },
|
||||||
{ "ADC", NULL, "SYSCLK" },
|
{ "ADC", NULL, "SYSCLK" },
|
||||||
{ "DAC", NULL, "ADC_PWR" },
|
{ "DAC", NULL, "ADC_PWR" },
|
||||||
{ "ADC", NULL, "ADC_PWR" },
|
{ "ADC", NULL, "ADC_PWR" },
|
||||||
{ "DAC1OUT", NULL, "DAC" },
|
{ "DAC1OUT", NULL, "DAC Output" },
|
||||||
{ "DAC2OUT", NULL, "DAC" },
|
{ "DAC2OUT", NULL, "DAC Output" },
|
||||||
{ "DAC3OUT", NULL, "DAC" },
|
{ "DAC3OUT", NULL, "DAC Output" },
|
||||||
{ "DAC4OUT", NULL, "DAC" },
|
{ "DAC4OUT", NULL, "DAC Output" },
|
||||||
{ "ADC", NULL, "ADC1IN" },
|
{ "ADC", NULL, "ADC1IN" },
|
||||||
{ "ADC", NULL, "ADC2IN" },
|
{ "ADC", NULL, "ADC2IN" },
|
||||||
{ "SYSCLK", NULL, "PLL_PWR" },
|
{ "SYSCLK", NULL, "PLL_PWR" },
|
||||||
|
@ -24,34 +24,86 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/ac97_codec.h>
|
#include <sound/ac97_codec.h>
|
||||||
#include <sound/initval.h>
|
#include <sound/initval.h>
|
||||||
#include <sound/soc.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 },
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
|
||||||
* AD1980 register cache
|
{
|
||||||
*/
|
switch (reg) {
|
||||||
static const u16 ad1980_reg[] = {
|
case AC97_RESET ... AC97_MASTER_MONO:
|
||||||
0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */
|
case AC97_PHONE ... AC97_CD:
|
||||||
0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */
|
case AC97_AUX ... AC97_GENERAL_PURPOSE:
|
||||||
0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
|
case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
|
||||||
0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
|
case AC97_SPDIF:
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
|
case AC97_CODEC_CLASS_REV:
|
||||||
0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
|
case AC97_PCI_SVID:
|
||||||
0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
|
case AC97_AD_CODEC_CFG:
|
||||||
0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
|
case AC97_AD_JACK_SPDIF:
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
case AC97_AD_SERIAL_CFG:
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
case AC97_VENDOR_ID1:
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
case AC97_VENDOR_ID2:
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
return true;
|
||||||
0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
|
default:
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
|
return false;
|
||||||
0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
|
}
|
||||||
0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */
|
}
|
||||||
|
|
||||||
|
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",
|
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" },
|
{ "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 = {
|
static struct snd_soc_dai_driver ad1980_dai = {
|
||||||
.name = "ad1980-hifi",
|
.name = "ad1980-hifi",
|
||||||
.ac97_control = 1,
|
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "Playback",
|
.stream_name = "Playback",
|
||||||
.channels_min = 2,
|
.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)
|
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;
|
unsigned int retry_cnt = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (try_warm && soc_ac97_ops->warm_reset) {
|
if (try_warm && soc_ac97_ops->warm_reset) {
|
||||||
soc_ac97_ops->warm_reset(codec->ac97);
|
soc_ac97_ops->warm_reset(ac97);
|
||||||
if (ac97_read(codec, AC97_RESET) == 0x0090)
|
if (snd_soc_read(codec, AC97_RESET) == 0x0090)
|
||||||
return 1;
|
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
|
* Set bit 16slot in register 74h, then every slot will has only
|
||||||
* 16 bits. This command is sent out in 20bit mode, in which
|
* 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
|
* case the first nibble of data is eaten by the addr. (Tag is
|
||||||
* always 16 bit)
|
* 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;
|
return 0;
|
||||||
} while (retry_cnt++ < 10);
|
} 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;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ad1980_soc_probe(struct snd_soc_codec *codec)
|
static int ad1980_soc_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97;
|
||||||
|
struct regmap *regmap;
|
||||||
int ret;
|
int ret;
|
||||||
u16 vendor_id2;
|
u16 vendor_id2;
|
||||||
u16 ext_status;
|
u16 ext_status;
|
||||||
|
|
||||||
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
|
ac97 = snd_soc_new_ac97_codec(codec);
|
||||||
|
if (IS_ERR(ac97)) {
|
||||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
ret = PTR_ERR(ac97);
|
||||||
if (ret < 0) {
|
dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
|
||||||
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ad1980_reset(codec, 0);
|
regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
|
||||||
if (ret < 0) {
|
if (IS_ERR(regmap)) {
|
||||||
printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
|
ret = PTR_ERR(regmap);
|
||||||
goto reset_err;
|
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 */
|
/* 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;
|
ret = -ENODEV;
|
||||||
goto reset_err;
|
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 != 0x5370) {
|
||||||
if (vendor_id2 != 0x5374) {
|
if (vendor_id2 != 0x5374) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto reset_err;
|
goto reset_err;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_WARNING "ad1980: "
|
dev_warn(codec->dev,
|
||||||
"Found AD1981 - only 2/2 IN/OUT Channels "
|
"Found AD1981 - only 2/2 IN/OUT Channels supported\n");
|
||||||
"supported\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unmute captures and playbacks volume */
|
/* unmute captures and playbacks volume */
|
||||||
ac97_write(codec, AC97_MASTER, 0x0000);
|
snd_soc_write(codec, AC97_MASTER, 0x0000);
|
||||||
ac97_write(codec, AC97_PCM, 0x0000);
|
snd_soc_write(codec, AC97_PCM, 0x0000);
|
||||||
ac97_write(codec, AC97_REC_GAIN, 0x0000);
|
snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
|
||||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
|
snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
|
||||||
ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
|
snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
|
||||||
|
|
||||||
/*power on LFE/CENTER/Surround DACs*/
|
/*power on LFE/CENTER/Surround DACs*/
|
||||||
ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
|
ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
|
||||||
ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
|
snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
|
||||||
|
|
||||||
snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
|
|
||||||
ARRAY_SIZE(ad1980_snd_ac97_controls));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
reset_err:
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ad1980_soc_remove(struct snd_soc_codec *codec)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
|
static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
|
||||||
.probe = ad1980_soc_probe,
|
.probe = ad1980_soc_probe,
|
||||||
.remove = ad1980_soc_remove,
|
.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,
|
.dapm_widgets = ad1980_dapm_widgets,
|
||||||
.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
|
||||||
.dapm_routes = ad1980_dapm_routes,
|
.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,
|
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event)
|
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);
|
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int pll_id = w->name[3] - '1';
|
unsigned int pll_id = w->name[3] - '1';
|
||||||
unsigned int val;
|
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,
|
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
|
||||||
struct snd_soc_dapm_widget *sink)
|
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);
|
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int dai;
|
unsigned int dai;
|
||||||
const char *clk;
|
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,
|
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
|
||||||
struct snd_soc_dapm_widget *sink)
|
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);
|
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int dai;
|
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,
|
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event)
|
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
|
/* After any power changes have been made the dejitter circuit
|
||||||
* has to be reinitialized. */
|
* 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,
|
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event)
|
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);
|
struct adau *adau = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
/* After any power changes have been made the dejitter circuit
|
/* 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,
|
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event)
|
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;
|
int ret;
|
||||||
|
|
||||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
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,
|
static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
if (reg > AC97_STAC_PAGE0) {
|
if (reg > AC97_STAC_PAGE0) {
|
||||||
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
|
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);
|
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
|
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
soc_ac97_ops->write(ac97, reg, val);
|
||||||
cache[reg / 2] = val;
|
cache[reg / 2] = val;
|
||||||
return 0;
|
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,
|
static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
|
||||||
unsigned int reg)
|
unsigned int reg)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 val = 0, *cache = codec->reg_cache;
|
u16 val = 0, *cache = codec->reg_cache;
|
||||||
|
|
||||||
if (reg > AC97_STAC_PAGE0) {
|
if (reg > AC97_STAC_PAGE0) {
|
||||||
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
|
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);
|
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
|
||||||
return val;
|
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_INT_PAGING || reg == AC97_VENDOR_ID1 ||
|
||||||
reg == AC97_VENDOR_ID2) {
|
reg == AC97_VENDOR_ID2) {
|
||||||
|
|
||||||
val = soc_ac97_ops->read(codec->ac97, reg);
|
val = soc_ac97_ops->read(ac97, reg);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
return cache[reg / 2];
|
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)
|
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) {
|
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])
|
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
soc_ac97_ops->reset(codec->ac97);
|
soc_ac97_ops->reset(ac97);
|
||||||
if (soc_ac97_ops->warm_reset)
|
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])
|
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
|
||||||
return -EIO;
|
return -EIO;
|
||||||
return 0;
|
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)
|
static int stac9766_codec_resume(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 id, reset;
|
u16 id, reset;
|
||||||
|
|
||||||
reset = 0;
|
reset = 0;
|
||||||
@ -271,8 +276,8 @@ reset:
|
|||||||
printk(KERN_ERR "stac9766 failed to resume");
|
printk(KERN_ERR "stac9766 failed to resume");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
codec->ac97->bus->ops->warm_reset(codec->ac97);
|
ac97->bus->ops->warm_reset(ac97);
|
||||||
id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
|
id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
|
||||||
if (id != 0x4c13) {
|
if (id != 0x4c13) {
|
||||||
stac9766_reset(codec, 0);
|
stac9766_reset(codec, 0);
|
||||||
reset++;
|
reset++;
|
||||||
@ -294,7 +299,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
|
|||||||
static struct snd_soc_dai_driver stac9766_dai[] = {
|
static struct snd_soc_dai_driver stac9766_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "stac9766-hifi-analog",
|
.name = "stac9766-hifi-analog",
|
||||||
.ac97_control = 1,
|
|
||||||
|
|
||||||
/* stream cababilities */
|
/* stream cababilities */
|
||||||
.playback = {
|
.playback = {
|
||||||
@ -316,7 +320,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "stac9766-hifi-IEC958",
|
.name = "stac9766-hifi-IEC958",
|
||||||
.ac97_control = 1,
|
|
||||||
|
|
||||||
/* stream cababilities */
|
/* stream cababilities */
|
||||||
.playback = {
|
.playback = {
|
||||||
@ -334,11 +337,14 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
|
|||||||
|
|
||||||
static int stac9766_codec_probe(struct snd_soc_codec *codec)
|
static int stac9766_codec_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
ac97 = snd_soc_new_ac97_codec(codec);
|
||||||
if (ret < 0)
|
if (IS_ERR(ac97))
|
||||||
goto codec_err;
|
return PTR_ERR(ac97);
|
||||||
|
|
||||||
|
snd_soc_codec_set_drvdata(codec, ac97);
|
||||||
|
|
||||||
/* do a cold reset for the controller and then try
|
/* do a cold reset for the controller and then try
|
||||||
* a warm reset followed by an optional cold reset for codec */
|
* 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;
|
return 0;
|
||||||
|
|
||||||
codec_err:
|
codec_err:
|
||||||
snd_soc_free_ac97_codec(codec);
|
snd_soc_free_ac97_codec(ac97);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stac9766_codec_remove(struct snd_soc_codec *codec)
|
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;
|
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. */
|
/* We use a register cache to enhance read performance. */
|
||||||
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
|
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;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case AC97_RESET:
|
case AC97_RESET:
|
||||||
case AC97_VENDOR_ID1:
|
case AC97_VENDOR_ID1:
|
||||||
case AC97_VENDOR_ID2:
|
case AC97_VENDOR_ID2:
|
||||||
return soc_ac97_ops->read(codec->ac97, reg);
|
return soc_ac97_ops->read(ac97, reg);
|
||||||
default:
|
default:
|
||||||
reg = reg >> 1;
|
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,
|
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
soc_ac97_ops->write(ac97, reg, val);
|
||||||
reg = reg >> 1;
|
reg = reg >> 1;
|
||||||
if (reg < (ARRAY_SIZE(wm9705_reg)))
|
if (reg < (ARRAY_SIZE(wm9705_reg)))
|
||||||
cache[reg] = val;
|
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[] = {
|
static struct snd_soc_dai_driver wm9705_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "wm9705-hifi",
|
.name = "wm9705-hifi",
|
||||||
.ac97_control = 1,
|
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "HiFi Playback",
|
.stream_name = "HiFi Playback",
|
||||||
.channels_min = 1,
|
.channels_min = 1,
|
||||||
@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
|
|||||||
|
|
||||||
static int wm9705_reset(struct snd_soc_codec *codec)
|
static int wm9705_reset(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
if (soc_ac97_ops->reset) {
|
if (soc_ac97_ops->reset) {
|
||||||
soc_ac97_ops->reset(codec->ac97);
|
soc_ac97_ops->reset(ac97);
|
||||||
if (ac97_read(codec, 0) == wm9705_reg[0])
|
if (ac97_read(codec, 0) == wm9705_reg[0])
|
||||||
return 0; /* Success */
|
return 0; /* Success */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||||
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm9705_soc_resume(struct snd_soc_codec *codec)
|
static int wm9705_soc_resume(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||||
int i, ret;
|
int i, ret;
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
ret = wm9705_reset(codec);
|
ret = wm9705_reset(codec);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
|
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;
|
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)
|
static int wm9705_soc_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct snd_ac97 *ac97;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
ac97 = snd_soc_new_ac97_codec(codec);
|
||||||
if (ret < 0) {
|
if (IS_ERR(ac97)) {
|
||||||
printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
|
ret = PTR_ERR(ac97);
|
||||||
|
dev_err(codec->dev, "Failed to register AC97 codec\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snd_soc_codec_set_drvdata(codec, ac97);
|
||||||
|
|
||||||
ret = wm9705_reset(codec);
|
ret = wm9705_reset(codec);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto reset_err;
|
goto reset_err;
|
||||||
|
|
||||||
snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
|
|
||||||
ARRAY_SIZE(wm9705_snd_ac97_controls));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
reset_err:
|
reset_err:
|
||||||
snd_soc_free_ac97_codec(codec);
|
snd_soc_free_ac97_codec(ac97);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm9705_soc_remove(struct snd_soc_codec *codec)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
|
|||||||
.reg_word_size = sizeof(u16),
|
.reg_word_size = sizeof(u16),
|
||||||
.reg_cache_step = 2,
|
.reg_cache_step = 2,
|
||||||
.reg_cache_default = wm9705_reg,
|
.reg_cache_default = wm9705_reg,
|
||||||
|
|
||||||
|
.controls = wm9705_snd_ac97_controls,
|
||||||
|
.num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
|
||||||
.dapm_widgets = wm9705_dapm_widgets,
|
.dapm_widgets = wm9705_dapm_widgets,
|
||||||
.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
|
||||||
.dapm_routes = wm9705_audio_map,
|
.dapm_routes = wm9705_audio_map,
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
#include <sound/tlv.h>
|
#include <sound/tlv.h>
|
||||||
#include "wm9712.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,
|
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||||
unsigned int reg);
|
unsigned int reg);
|
||||||
static int ac97_write(struct snd_soc_codec *codec,
|
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, 0x0000, /* 6e */
|
||||||
0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
|
0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
|
||||||
0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
|
0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
|
||||||
0x0000, 0x0000 /* virtual hp mixers */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* virtual HP mixers regs */
|
#define HPL_MIXER 0x0
|
||||||
#define HPL_MIXER 0x80
|
#define HPR_MIXER 0x1
|
||||||
#define HPR_MIXER 0x82
|
|
||||||
|
|
||||||
static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
|
static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
|
||||||
static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
|
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),
|
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
|
/* 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.
|
* the codec only has a single control that is shared by both channels.
|
||||||
* This makes it impossible to determine the audio path.
|
* This makes it impossible to determine the audio path.
|
||||||
*/
|
*/
|
||||||
static int mixer_event(struct snd_soc_dapm_widget *w,
|
static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_kcontrol *k, int event)
|
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);
|
mixer = mc->shift >> 8;
|
||||||
r = ac97_read(w->codec, HPR_MIXER);
|
shift = mc->shift & 0xff;
|
||||||
beep = ac97_read(w->codec, AC97_PC_BEEP);
|
mask = 1 << shift;
|
||||||
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);
|
|
||||||
|
|
||||||
if (l & 0x1 || r & 0x1)
|
mutex_lock(&wm9712->lock);
|
||||||
ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
|
old = wm9712->hp_mixer[mixer];
|
||||||
|
if (ucontrol->value.enumerated.item[0])
|
||||||
|
wm9712->hp_mixer[mixer] |= mask;
|
||||||
else
|
else
|
||||||
ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
|
wm9712->hp_mixer[mixer] &= ~mask;
|
||||||
|
|
||||||
if (l & 0x2 || r & 0x2)
|
change = old != wm9712->hp_mixer[mixer];
|
||||||
ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
|
if (change) {
|
||||||
else
|
update.kcontrol = kcontrol;
|
||||||
ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
|
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)
|
snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
|
||||||
ac97_write(w->codec, AC97_LINE, line & 0x7fff);
|
&update);
|
||||||
else
|
}
|
||||||
ac97_write(w->codec, AC97_LINE, line | 0x8000);
|
|
||||||
|
|
||||||
if (l & 0x8 || r & 0x8)
|
mutex_unlock(&wm9712->lock);
|
||||||
ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
|
|
||||||
else
|
|
||||||
ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
|
|
||||||
|
|
||||||
if (l & 0x10 || r & 0x10)
|
return change;
|
||||||
ac97_write(w->codec, AC97_CD, aux & 0x7fff);
|
}
|
||||||
else
|
|
||||||
ac97_write(w->codec, AC97_CD, aux | 0x8000);
|
|
||||||
|
|
||||||
if (l & 0x20 || r & 0x20)
|
static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
|
||||||
ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
else
|
{
|
||||||
ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
|
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;
|
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 */
|
/* Left Headphone Mixers */
|
||||||
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
|
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
|
||||||
SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
|
WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
|
||||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
|
WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
|
||||||
SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
|
WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
|
||||||
SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
|
WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
|
||||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
|
WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
|
||||||
SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
|
WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Right Headphone Mixers */
|
/* Right Headphone Mixers */
|
||||||
static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
|
static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
|
||||||
SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
|
WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
|
||||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
|
WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
|
||||||
SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
|
WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
|
||||||
SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
|
WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
|
||||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
|
WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
|
||||||
SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
|
WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Speaker Mixer */
|
/* 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,
|
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
|
||||||
&wm9712_diff_sel_controls),
|
&wm9712_diff_sel_controls),
|
||||||
SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
|
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,
|
SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
|
||||||
&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
|
&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
|
||||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
|
||||||
SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
|
&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
|
||||||
&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
|
|
||||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
|
||||||
SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
|
SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
|
||||||
&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
|
&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
|
||||||
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
|
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,
|
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||||
unsigned int reg)
|
unsigned int reg)
|
||||||
{
|
{
|
||||||
|
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
||||||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
||||||
reg == AC97_REC_GAIN)
|
reg == AC97_REC_GAIN)
|
||||||
return soc_ac97_ops->read(codec->ac97, reg);
|
return soc_ac97_ops->read(wm9712->ac97, reg);
|
||||||
else {
|
else {
|
||||||
reg = reg >> 1;
|
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,
|
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
|
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
if (reg < 0x7c)
|
soc_ac97_ops->write(wm9712->ac97, reg, val);
|
||||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
|
||||||
reg = reg >> 1;
|
reg = reg >> 1;
|
||||||
if (reg < (ARRAY_SIZE(wm9712_reg)))
|
if (reg < (ARRAY_SIZE(wm9712_reg)))
|
||||||
cache[reg] = val;
|
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[] = {
|
static struct snd_soc_dai_driver wm9712_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "wm9712-hifi",
|
.name = "wm9712-hifi",
|
||||||
.ac97_control = 1,
|
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "HiFi Playback",
|
.stream_name = "HiFi Playback",
|
||||||
.channels_min = 1,
|
.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)
|
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) {
|
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])
|
if (ac97_read(codec, 0) == wm9712_reg[0])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
soc_ac97_ops->reset(codec->ac97);
|
soc_ac97_ops->reset(wm9712->ac97);
|
||||||
if (soc_ac97_ops->warm_reset)
|
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])
|
if (ac97_read(codec, 0) != wm9712_reg[0])
|
||||||
goto err;
|
goto err;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
printk(KERN_ERR "WM9712 AC97 reset failed\n");
|
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||||
return -EIO;
|
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)
|
static int wm9712_soc_resume(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||||
int i, ret;
|
int i, ret;
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
ret = wm9712_reset(codec, 1);
|
ret = wm9712_reset(codec, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
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 ||
|
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
|
||||||
(i > 0x58 && i != 0x5c))
|
(i > 0x58 && i != 0x5c))
|
||||||
continue;
|
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)
|
static int wm9712_soc_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
wm9712->ac97 = snd_soc_new_ac97_codec(codec);
|
||||||
if (ret < 0) {
|
if (IS_ERR(wm9712->ac97)) {
|
||||||
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
|
ret = PTR_ERR(wm9712->ac97);
|
||||||
|
dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = wm9712_reset(codec, 0);
|
ret = wm9712_reset(codec, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
|
|
||||||
goto reset_err;
|
goto reset_err;
|
||||||
}
|
|
||||||
|
|
||||||
/* set alc mux to none */
|
/* set alc mux to none */
|
||||||
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
|
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
|
||||||
|
|
||||||
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
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;
|
return 0;
|
||||||
|
|
||||||
reset_err:
|
reset_err:
|
||||||
snd_soc_free_ac97_codec(codec);
|
snd_soc_free_ac97_codec(wm9712->ac97);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm9712_soc_remove(struct snd_soc_codec *codec)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,6 +715,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
|
|||||||
.reg_word_size = sizeof(u16),
|
.reg_word_size = sizeof(u16),
|
||||||
.reg_cache_step = 2,
|
.reg_cache_step = 2,
|
||||||
.reg_cache_default = wm9712_reg,
|
.reg_cache_default = wm9712_reg,
|
||||||
|
|
||||||
|
.controls = wm9712_snd_ac97_controls,
|
||||||
|
.num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
|
||||||
.dapm_widgets = wm9712_dapm_widgets,
|
.dapm_widgets = wm9712_dapm_widgets,
|
||||||
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
|
||||||
.dapm_routes = wm9712_audio_map,
|
.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)
|
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,
|
return snd_soc_register_codec(&pdev->dev,
|
||||||
&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
|
&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,10 @@
|
|||||||
#include "wm9713.h"
|
#include "wm9713.h"
|
||||||
|
|
||||||
struct wm9713_priv {
|
struct wm9713_priv {
|
||||||
|
struct snd_ac97 *ac97;
|
||||||
u32 pll_in; /* PLL input frequency */
|
u32 pll_in; /* PLL input frequency */
|
||||||
|
unsigned int hp_mixer[2];
|
||||||
|
struct mutex lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
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, 0x0000,
|
||||||
0x0000, 0x0000, 0x0000, 0x0006,
|
0x0000, 0x0000, 0x0000, 0x0006,
|
||||||
0x0001, 0x0000, 0x574d, 0x4c13,
|
0x0001, 0x0000, 0x574d, 0x4c13,
|
||||||
0x0000, 0x0000, 0x0000
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* virtual HP mixers regs */
|
#define HPL_MIXER 0
|
||||||
#define HPL_MIXER 0x80
|
#define HPR_MIXER 1
|
||||||
#define HPR_MIXER 0x82
|
|
||||||
#define MICB_MUX 0x82
|
|
||||||
|
|
||||||
static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
|
static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
|
||||||
static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "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_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_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(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);
|
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;
|
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
|
/* 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.
|
* 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
|
* register map, thus we add a new (virtual) register to help determine the
|
||||||
* audio route within the device.
|
* audio route within the device.
|
||||||
*/
|
*/
|
||||||
static int mixer_event(struct snd_soc_dapm_widget *w,
|
static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_kcontrol *kcontrol, int event)
|
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);
|
mixer = mc->shift >> 8;
|
||||||
r = ac97_read(w->codec, HPR_MIXER);
|
shift = mc->shift & 0xff;
|
||||||
beep = ac97_read(w->codec, AC97_PC_BEEP);
|
mask = (1 << shift);
|
||||||
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);
|
|
||||||
|
|
||||||
if (event & SND_SOC_DAPM_PRE_REG)
|
mutex_lock(&wm9713->lock);
|
||||||
return 0;
|
old = wm9713->hp_mixer[mixer];
|
||||||
if ((l & 0x1) || (r & 0x1))
|
if (ucontrol->value.enumerated.item[0])
|
||||||
ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
|
wm9713->hp_mixer[mixer] |= mask;
|
||||||
else
|
else
|
||||||
ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
|
wm9713->hp_mixer[mixer] &= ~mask;
|
||||||
|
|
||||||
if ((l & 0x2) || (r & 0x2))
|
change = old != wm9713->hp_mixer[mixer];
|
||||||
ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
|
if (change) {
|
||||||
else
|
update.kcontrol = kcontrol;
|
||||||
ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
|
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))
|
snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
|
||||||
ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
|
&update);
|
||||||
else
|
}
|
||||||
ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
|
|
||||||
|
|
||||||
if ((l & 0x8) || (r & 0x8))
|
mutex_unlock(&wm9713->lock);
|
||||||
ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
|
|
||||||
else
|
|
||||||
ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
|
|
||||||
|
|
||||||
if ((l & 0x10) || (r & 0x10))
|
return change;
|
||||||
ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
|
}
|
||||||
else
|
|
||||||
ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
|
|
||||||
|
|
||||||
if ((l & 0x20) || (r & 0x20))
|
static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
|
||||||
ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
else
|
{
|
||||||
ac97_write(w->codec, AC97_AUX, aux | 0x8000);
|
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;
|
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 */
|
/* Left Headphone Mixers */
|
||||||
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
|
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
|
||||||
SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
|
WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
|
||||||
SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
|
WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
|
||||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
|
WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
|
||||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
|
WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
|
||||||
SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
|
WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
|
||||||
SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
|
WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Right Headphone Mixers */
|
/* Right Headphone Mixers */
|
||||||
static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
|
static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
|
||||||
SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
|
WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
|
||||||
SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
|
WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
|
||||||
SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
|
WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
|
||||||
SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
|
WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
|
||||||
SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
|
WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
|
||||||
SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
|
WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* headphone capture mux */
|
/* headphone capture mux */
|
||||||
@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
|
|||||||
&wm9713_mic_sel_mux_controls),
|
&wm9713_mic_sel_mux_controls),
|
||||||
SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
|
SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
|
||||||
&wm9713_micb_sel_mux_controls),
|
&wm9713_micb_sel_mux_controls),
|
||||||
SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
|
SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
|
||||||
&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
|
&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
|
||||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
|
||||||
SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
|
&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
|
||||||
&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
|
|
||||||
mixer_event, SND_SOC_DAPM_POST_REG),
|
|
||||||
SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
|
SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
|
||||||
&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
|
&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
|
||||||
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
|
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,
|
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||||
unsigned int reg)
|
unsigned int reg)
|
||||||
{
|
{
|
||||||
|
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
||||||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
||||||
reg == AC97_CD)
|
reg == AC97_CD)
|
||||||
return soc_ac97_ops->read(codec->ac97, reg);
|
return soc_ac97_ops->read(wm9713->ac97, reg);
|
||||||
else {
|
else {
|
||||||
reg = reg >> 1;
|
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,
|
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
|
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
if (reg < 0x7c)
|
soc_ac97_ops->write(wm9713->ac97, reg, val);
|
||||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
|
||||||
reg = reg >> 1;
|
reg = reg >> 1;
|
||||||
if (reg < (ARRAY_SIZE(wm9713_reg)))
|
if (reg < (ARRAY_SIZE(wm9713_reg)))
|
||||||
cache[reg] = val;
|
cache[reg] = val;
|
||||||
@ -689,7 +719,8 @@ struct _pll_div {
|
|||||||
* to allow rounding later */
|
* to allow rounding later */
|
||||||
#define FIXED_PLL_SIZE ((1 << 22) * 10)
|
#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;
|
u64 Kpart;
|
||||||
unsigned int K, Ndiv, Nmod, target;
|
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;
|
Ndiv = target / source;
|
||||||
if ((Ndiv < 5) || (Ndiv > 12))
|
if ((Ndiv < 5) || (Ndiv > 12))
|
||||||
printk(KERN_WARNING
|
dev_warn(codec->dev,
|
||||||
"WM9713 PLL N value %u out of recommended range!\n",
|
"WM9713 PLL N value %u out of recommended range!\n",
|
||||||
Ndiv);
|
Ndiv);
|
||||||
|
|
||||||
@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pll_factors(&pll_div, freq_in);
|
pll_factors(codec, &pll_div, freq_in);
|
||||||
|
|
||||||
if (pll_div.k == 0) {
|
if (pll_div.k == 0) {
|
||||||
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
|
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[] = {
|
static struct snd_soc_dai_driver wm9713_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "wm9713-hifi",
|
.name = "wm9713-hifi",
|
||||||
.ac97_control = 1,
|
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "HiFi Playback",
|
.stream_name = "HiFi Playback",
|
||||||
.channels_min = 1,
|
.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)
|
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) {
|
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])
|
if (ac97_read(codec, 0) == wm9713_reg[0])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
soc_ac97_ops->reset(codec->ac97);
|
soc_ac97_ops->reset(wm9713->ac97);
|
||||||
if (soc_ac97_ops->warm_reset)
|
if (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])
|
if (ac97_read(codec, 0) != wm9713_reg[0]) {
|
||||||
|
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm9713_reset);
|
EXPORT_SYMBOL_GPL(wm9713_reset);
|
||||||
@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
|||||||
u16 *cache = codec->reg_cache;
|
u16 *cache = codec->reg_cache;
|
||||||
|
|
||||||
ret = wm9713_reset(codec, 1);
|
ret = wm9713_reset(codec, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
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 ||
|
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
|
||||||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
|
i == AC97_EXTENDED_MSTATUS || i > 0x66)
|
||||||
continue;
|
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)
|
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;
|
int ret = 0, reg;
|
||||||
|
|
||||||
wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
|
wm9713->ac97 = snd_soc_new_ac97_codec(codec);
|
||||||
if (wm9713 == NULL)
|
if (IS_ERR(wm9713->ac97))
|
||||||
return -ENOMEM;
|
return PTR_ERR(wm9713->ac97);
|
||||||
snd_soc_codec_set_drvdata(codec, wm9713);
|
|
||||||
|
|
||||||
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
goto codec_err;
|
|
||||||
|
|
||||||
/* do a cold reset for the controller and then try
|
/* do a cold reset for the controller and then try
|
||||||
* a warm reset followed by an optional cold reset for codec */
|
* a warm reset followed by an optional cold reset for codec */
|
||||||
wm9713_reset(codec, 0);
|
wm9713_reset(codec, 0);
|
||||||
ret = wm9713_reset(codec, 1);
|
ret = wm9713_reset(codec, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
|
|
||||||
goto reset_err;
|
goto reset_err;
|
||||||
}
|
|
||||||
|
|
||||||
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
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;
|
reg = ac97_read(codec, AC97_CD) & 0x7fff;
|
||||||
ac97_write(codec, AC97_CD, reg);
|
ac97_write(codec, AC97_CD, reg);
|
||||||
|
|
||||||
snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
|
|
||||||
ARRAY_SIZE(wm9713_snd_ac97_controls));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
reset_err:
|
reset_err:
|
||||||
snd_soc_free_ac97_codec(codec);
|
snd_soc_free_ac97_codec(wm9713->ac97);
|
||||||
codec_err:
|
|
||||||
kfree(wm9713);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm9713_soc_remove(struct snd_soc_codec *codec)
|
static int wm9713_soc_remove(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1248,6 +1269,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
|
|||||||
.reg_word_size = sizeof(u16),
|
.reg_word_size = sizeof(u16),
|
||||||
.reg_cache_step = 2,
|
.reg_cache_step = 2,
|
||||||
.reg_cache_default = wm9713_reg,
|
.reg_cache_default = wm9713_reg,
|
||||||
|
|
||||||
|
.controls = wm9713_snd_ac97_controls,
|
||||||
|
.num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
|
||||||
.dapm_widgets = wm9713_dapm_widgets,
|
.dapm_widgets = wm9713_dapm_widgets,
|
||||||
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
|
||||||
.dapm_routes = wm9713_audio_map,
|
.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)
|
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,
|
return snd_soc_register_codec(&pdev->dev,
|
||||||
&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
|
&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 = {
|
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
.channels_min = 2,
|
.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 = {
|
static struct snd_soc_dai_driver imx_ac97_dai = {
|
||||||
.probe = imx_ssi_dai_probe,
|
.probe = imx_ssi_dai_probe,
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
|
@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
|
|||||||
goto capture_alloc_err;
|
goto capture_alloc_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtd->codec->ac97)
|
|
||||||
rtd->codec->ac97->private_data = psc_dma;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
capture_alloc_err:
|
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[] = {
|
static struct snd_soc_dai_driver psc_ac97_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "mpc5200-psc-ac97.0",
|
.name = "mpc5200-psc-ac97.0",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.probe = psc_ac97_probe,
|
.probe = psc_ac97_probe,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "mpc5200-psc-ac97.1",
|
.name = "mpc5200-psc-ac97.1",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 SPDIF",
|
.stream_name = "AC97 SPDIF",
|
||||||
.channels_min = 1,
|
.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)
|
static int psc_ac97_of_probe(struct platform_device *op)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct snd_ac97 ac97;
|
|
||||||
struct mpc52xx_psc __iomem *regs;
|
struct mpc52xx_psc __iomem *regs;
|
||||||
|
|
||||||
rc = mpc5200_audio_dma_create(op);
|
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);
|
psc_dma = dev_get_drvdata(&op->dev);
|
||||||
regs = psc_dma->psc_regs;
|
regs = psc_dma->psc_regs;
|
||||||
ac97.private_data = psc_dma;
|
|
||||||
|
|
||||||
psc_dma->imr = 0;
|
psc_dma->imr = 0;
|
||||||
out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
|
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 = {
|
static struct snd_soc_dai_driver nuc900_ac97_dai = {
|
||||||
.probe = nuc900_ac97_probe,
|
.probe = nuc900_ac97_probe,
|
||||||
.remove = nuc900_ac97_remove,
|
.remove = nuc900_ac97_remove,
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
.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[] = {
|
static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
||||||
{
|
{
|
||||||
.name = "pxa2xx-ac97",
|
.name = "pxa2xx-ac97",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "pxa2xx-ac97-aux",
|
.name = "pxa2xx-ac97-aux",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Aux Playback",
|
.stream_name = "AC97 Aux Playback",
|
||||||
.channels_min = 1,
|
.channels_min = 1,
|
||||||
@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "pxa2xx-ac97-mic",
|
.name = "pxa2xx-ac97-mic",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.capture = {
|
.capture = {
|
||||||
.stream_name = "AC97 Mic Capture",
|
.stream_name = "AC97 Mic Capture",
|
||||||
.channels_min = 1,
|
.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[] = {
|
static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
||||||
[S3C_AC97_DAI_PCM] = {
|
[S3C_AC97_DAI_PCM] = {
|
||||||
.name = "samsung-ac97",
|
.name = "samsung-ac97",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "AC97 Playback",
|
.stream_name = "AC97 Playback",
|
||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
|||||||
},
|
},
|
||||||
[S3C_AC97_DAI_MIC] = {
|
[S3C_AC97_DAI_MIC] = {
|
||||||
.name = "samsung-ac97-mic",
|
.name = "samsung-ac97-mic",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.capture = {
|
.capture = {
|
||||||
.stream_name = "AC97 Mic Capture",
|
.stream_name = "AC97 Mic Capture",
|
||||||
.channels_min = 1,
|
.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[] = {
|
static struct snd_soc_dai_driver sh4_hac_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "hac-dai.0",
|
.name = "hac-dai.0",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.playback = {
|
.playback = {
|
||||||
.rates = AC97_RATES,
|
.rates = AC97_RATES,
|
||||||
.formats = AC97_FMTS,
|
.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/ctype.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of.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/core.h>
|
||||||
#include <sound/jack.h>
|
#include <sound/jack.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
@ -69,16 +66,6 @@ static int pmdown_time = 5000;
|
|||||||
module_param(pmdown_time, int, 0);
|
module_param(pmdown_time, int, 0);
|
||||||
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
|
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
|
/* returns the minimum number of bytes needed to represent
|
||||||
* a particular given value */
|
* a particular given value */
|
||||||
static int min_bytes_needed(unsigned long val)
|
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);
|
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)
|
static void codec2codec_close_delayed_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
/* Currently nothing to do for c2c links
|
/* 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)
|
if (card->rtd[i].dai_link->ignore_suspend)
|
||||||
continue;
|
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);
|
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)
|
if (card->rtd[i].dai_link->ignore_suspend)
|
||||||
continue;
|
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);
|
cpu_dai->driver->suspend(cpu_dai);
|
||||||
|
|
||||||
/* deactivate pins to sleep state */
|
/* deactivate pins to sleep state */
|
||||||
@ -703,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
|
|||||||
if (card->resume_pre)
|
if (card->resume_pre)
|
||||||
card->resume_pre(card);
|
card->resume_pre(card);
|
||||||
|
|
||||||
/* resume AC97 DAIs */
|
/* resume control bus DAIs */
|
||||||
for (i = 0; i < card->num_rtd; i++) {
|
for (i = 0; i < card->num_rtd; i++) {
|
||||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||||
|
|
||||||
if (card->rtd[i].dai_link->ignore_suspend)
|
if (card->rtd[i].dai_link->ignore_suspend)
|
||||||
continue;
|
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);
|
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)
|
if (card->rtd[i].dai_link->ignore_suspend)
|
||||||
continue;
|
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);
|
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)
|
int snd_soc_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct snd_soc_card *card = dev_get_drvdata(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 the card is not initialized yet there is nothing to do */
|
||||||
if (!card->instantiated)
|
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
|
* DAIs that also act as the control bus master might have other drivers
|
||||||
* problem and may take a substantial amount of time to resume
|
* 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.
|
* due to I/O costs and anti-pop so handle them out of line.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < card->num_rtd; i++) {
|
for (i = 0; i < card->num_rtd; i++) {
|
||||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
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) {
|
if (bus_control) {
|
||||||
dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
|
dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
|
||||||
soc_resume_deferred(&card->deferred_resume_work);
|
soc_resume_deferred(&card->deferred_resume_work);
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(dev, "ASoC: Scheduling resume work\n");
|
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;
|
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)
|
static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[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;
|
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;
|
card->instantiated = 1;
|
||||||
snd_soc_dapm_sync(&card->dapm);
|
snd_soc_dapm_sync(&card->dapm);
|
||||||
mutex_unlock(&card->mutex);
|
mutex_unlock(&card->mutex);
|
||||||
@ -1913,216 +1779,6 @@ static struct platform_driver soc_driver = {
|
|||||||
.remove = soc_remove,
|
.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
|
* snd_soc_cnew - create new control
|
||||||
* @_template: control template
|
* @_template: control template
|
||||||
@ -2947,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
|
|||||||
return 0;
|
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)
|
int val_bytes = regmap_get_val_bytes(component->regmap);
|
||||||
component->regmap = dev_get_regmap(component->dev, NULL);
|
|
||||||
if (component->regmap) {
|
/* Errors are legitimate for non-integer byte multiples */
|
||||||
int val_bytes = regmap_get_val_bytes(component->regmap);
|
if (val_bytes > 0)
|
||||||
/* Errors are legitimate for non-integer byte multiples */
|
component->val_bytes = val_bytes;
|
||||||
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)
|
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
|
||||||
{
|
{
|
||||||
if (!component->write && !component->read)
|
if (!component->write && !component->read) {
|
||||||
snd_soc_component_init_regmap(component);
|
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);
|
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 = {
|
static struct snd_soc_dai_driver tegra20_ac97_dai = {
|
||||||
.name = "tegra-ac97-pcm",
|
.name = "tegra-ac97-pcm",
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.probe = tegra20_ac97_probe,
|
.probe = tegra20_ac97_probe,
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "PCM 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 = {
|
static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
|
||||||
.ac97_control = 1,
|
.bus_control = true,
|
||||||
.probe = txx9aclc_ac97_probe,
|
.probe = txx9aclc_ac97_probe,
|
||||||
.remove = txx9aclc_ac97_remove,
|
.remove = txx9aclc_ac97_remove,
|
||||||
.playback = {
|
.playback = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user