Commit db5a5ee7 authored by Mark Brown's avatar Mark Brown

Merge branches 'topic/ad193x', 'topic/tlv320aic23', 'topic/tlv320aic32x4',...

Merge branches 'topic/ad193x', 'topic/tlv320aic23', 'topic/tlv320aic32x4', 'topic/wm8991', 'fix/si476x' and 'fix/88pm860' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-io
Texas Instruments - tlv320aic32x4 Codec module
The tlv320aic32x4 serial control bus communicates through I2C protocols
Required properties:
- compatible: Should be "ti,tlv320aic32x4"
- reg: I2C slave address
- supply-*: Required supply regulators are:
"iov" - digital IO power supply
"ldoin" - LDO power supply
"dv" - Digital core power supply
"av" - Analog core power supply
If you supply ldoin, dv and av are optional. Otherwise they are required
See regulator/regulator.txt for more information about the detailed binding
format.
Optional properties:
- reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt
- clocks/clock-names: Clock named 'mclk' for the master clock of the codec.
See clock/clock-bindings.txt for information about the detailed format.
Example:
codec: tlv320aic32x4@18 {
compatible = "ti,tlv320aic32x4";
reg = <0x18>;
clocks = <&clks 201>;
clock-names = "mclk";
};
......@@ -45,6 +45,11 @@
((unsigned long)&(struct soc_mixer_control) \
{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
.max = xmax, .platform_max = xmax, .invert = xinvert})
#define SOC_DOUBLE_R_S_VALUE(xlreg, xrreg, xshift, xmin, xmax, xsign_bit, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
.max = xmax, .min = xmin, .platform_max = xmax, .sign_bit = xsign_bit, \
.invert = xinvert})
#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
......@@ -152,6 +157,15 @@
{.reg = xreg, .rreg = xrreg, \
.shift = xshift, .rshift = xshift, \
.max = xmax, .min = xmin} }
#define SOC_DOUBLE_R_S_TLV(xname, reg_left, reg_right, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw, \
.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
.private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \
xmin, xmax, xsign_bit, xinvert) }
#define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
......@@ -1064,6 +1078,7 @@ struct soc_mixer_control {
int min, max, platform_max;
int reg, rreg;
unsigned int shift, rshift;
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
};
......
......@@ -58,6 +58,6 @@ config SND_AT91_SOC_AFEB9260
depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
help
Say Y here to support sound on AFEB9260 board.
......@@ -68,7 +68,8 @@ config SND_BF5XX_SOC_AD193X
tristate "SoC AD193X Audio support for Blackfin"
depends on SND_BF5XX_I2S
select SND_BF5XX_SOC_I2S
select SND_SOC_AD193X
select SND_SOC_AD193X_I2C if I2C
select SND_SOC_AD193X_SPI if SPI_MASTER
help
Say Y if you want to add support for AD193X codec on Blackfin.
This driver supports AD1936, AD1937, AD1938 and AD1939.
......
......@@ -18,7 +18,7 @@ config SND_EP93XX_SOC_SNAPPERCL15
tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
select SND_EP93XX_SOC_I2S
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
help
Say Y or M here if you want to add support for I2S audio on the
Bluewater Systems Snapper CL15 module.
......
......@@ -1328,6 +1328,9 @@ static int pm860x_probe(struct snd_soc_codec *codec)
pm860x->codec = codec;
codec->control_data = pm860x->regmap;
ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
if (ret)
return ret;
for (i = 0; i < 4; i++) {
ret = request_threaded_irq(pm860x->irq[i], NULL,
......
......@@ -16,7 +16,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AB8500_CODEC if ABX500_CORE
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD193X_SPI if SPI_MASTER
select SND_SOC_AD193X_I2C if I2C
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
......@@ -71,7 +72,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_STA529 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TAS5086 if I2C
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC32X4 if I2C
select SND_SOC_TLV320AIC3X if I2C
......@@ -182,6 +184,14 @@ config SND_SOC_AD1836
config SND_SOC_AD193X
tristate
config SND_SOC_AD193X_SPI
tristate
select SND_SOC_AD193X
config SND_SOC_AD193X_I2C
tristate
select SND_SOC_AD193X
config SND_SOC_AD1980
tristate
......@@ -357,6 +367,14 @@ config SND_SOC_TAS5086
config SND_SOC_TLV320AIC23
tristate
config SND_SOC_TLV320AIC23_I2C
tristate
select SND_SOC_TLV320AIC23
config SND_SOC_TLV320AIC23_SPI
tristate
select SND_SOC_TLV320AIC23
config SND_SOC_TLV320AIC26
tristate
depends on SPI
......
......@@ -3,6 +3,8 @@ snd-soc-ab8500-codec-objs := ab8500-codec.o
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
snd-soc-ad193x-spi-objs := ad193x-spi.o
snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-adau1701-objs := adau1701.o
......@@ -63,9 +65,11 @@ snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-tas5086-objs := tas5086.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-twl6040-objs := twl6040.o
......@@ -134,6 +138,8 @@ obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
......@@ -193,9 +199,11 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
......
/*
* AD1936/AD1937 audio driver
*
* Copyright 2014 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
{ "ad1936", 0 },
{ "ad1937", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
static int ad193x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = ad193x_regmap_config;
config.val_bits = 8;
config.reg_bits = 8;
return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
}
static int ad193x_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = ad193x_i2c_remove,
.id_table = ad193x_id,
};
module_i2c_driver(ad193x_i2c_driver);
MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");
/*
* AD1938/AD1939 audio driver
*
* Copyright 2014 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "ad193x.h"
static int ad193x_spi_probe(struct spi_device *spi)
{
struct regmap_config config;
config = ad193x_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
config.read_flag_mask = 0x09;
config.write_flag_mask = 0x08;
return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
static int ad193x_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
.remove = ad193x_spi_remove,
};
module_spi_driver(ad193x_spi_driver);
MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");
......@@ -6,12 +6,10 @@
* Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
......@@ -19,6 +17,7 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "ad193x.h"
/* codec private data */
......@@ -32,8 +31,8 @@ struct ad193x_priv {
*/
static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
static const struct soc_enum ad193x_deemp_enum =
SOC_ENUM_SINGLE(AD193X_DAC_CTRL2, 1, 4, ad193x_deemp);
static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
ad193x_deemp);
static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
......@@ -320,7 +319,7 @@ static struct snd_soc_dai_driver ad193x_dai = {
.ops = &ad193x_dai_ops,
};
static int ad193x_probe(struct snd_soc_codec *codec)
static int ad193x_codec_probe(struct snd_soc_codec *codec)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
int ret;
......@@ -352,7 +351,7 @@ static int ad193x_probe(struct snd_soc_codec *codec)
}
static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
.probe = ad193x_probe,
.probe = ad193x_codec_probe,
.controls = ad193x_snd_controls,
.num_controls = ARRAY_SIZE(ad193x_snd_controls),
.dapm_widgets = ad193x_dapm_widgets,
......@@ -366,140 +365,31 @@ static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
return false;
}
#if defined(CONFIG_SPI_MASTER)
static const struct regmap_config ad193x_spi_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.read_flag_mask = 0x09,
.write_flag_mask = 0x08,
const struct regmap_config ad193x_regmap_config = {
.max_register = AD193X_NUM_REGS - 1,
.volatile_reg = adau193x_reg_volatile,
};
EXPORT_SYMBOL_GPL(ad193x_regmap_config);
static int ad193x_spi_probe(struct spi_device *spi)
int ad193x_probe(struct device *dev, struct regmap *regmap)
{
struct ad193x_priv *ad193x;
ad193x = devm_kzalloc(&spi->dev, sizeof(struct ad193x_priv),
GFP_KERNEL);
if (ad193x == NULL)
return -ENOMEM;
ad193x->regmap = devm_regmap_init_spi(spi, &ad193x_spi_regmap_config);
if (IS_ERR(ad193x->regmap))
return PTR_ERR(ad193x->regmap);
spi_set_drvdata(spi, ad193x);
return snd_soc_register_codec(&spi->dev, &soc_codec_dev_ad193x,
&ad193x_dai, 1);
}
static int ad193x_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
.remove = ad193x_spi_remove,
};
#endif
#if IS_ENABLED(CONFIG_I2C)
static const struct regmap_config ad193x_i2c_regmap_config = {
.val_bits = 8,
.reg_bits = 8,
.max_register = AD193X_NUM_REGS - 1,
.volatile_reg = adau193x_reg_volatile,
};
static const struct i2c_device_id ad193x_id[] = {
{ "ad1936", 0 },
{ "ad1937", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
static int ad193x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ad193x_priv *ad193x;
ad193x = devm_kzalloc(&client->dev, sizeof(struct ad193x_priv),
GFP_KERNEL);
ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL);
if (ad193x == NULL)
return -ENOMEM;
ad193x->regmap = devm_regmap_init_i2c(client, &ad193x_i2c_regmap_config);
if (IS_ERR(ad193x->regmap))
return PTR_ERR(ad193x->regmap);
i2c_set_clientdata(client, ad193x);
return snd_soc_register_codec(&client->dev, &soc_codec_dev_ad193x,
&ad193x_dai, 1);
}
static int ad193x_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
ad193x->regmap = regmap;
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = ad193x_i2c_remove,
.id_table = ad193x_id,
};
#endif
static int __init ad193x_modinit(void)
{
int ret;
#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&ad193x_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n",
ret);
}
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&ad193x_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register AD193X SPI driver: %d\n",
ret);
}
#endif
return ret;
}
module_init(ad193x_modinit);
static void __exit ad193x_modexit(void)
{
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&ad193x_spi_driver);
#endif
dev_set_drvdata(dev, ad193x);
#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&ad193x_i2c_driver);
#endif
return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
&ad193x_dai, 1);
}
module_exit(ad193x_modexit);
EXPORT_SYMBOL_GPL(ad193x_probe);
MODULE_DESCRIPTION("ASoC ad193x driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
......
......@@ -9,6 +9,13 @@
#ifndef __AD193X_H__
#define __AD193X_H__
#include <linux/regmap.h>
struct device;
extern const struct regmap_config ad193x_regmap_config;
int ad193x_probe(struct device *dev, struct regmap *regmap);
#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
#define AD193X_PLL_INPUT_MASK 0x6
......
......@@ -210,7 +210,7 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
static int si476x_codec_probe(struct snd_soc_codec *codec)
{
codec->control_data = dev_get_regmap(codec->dev->parent, NULL);
return 0;
return snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
}
static struct snd_soc_dai_ops si476x_dai_ops = {
......
/*
* ALSA SoC TLV320AIC23 codec driver I2C interface
*
* Author: Arun KS, <arunks@mistralsolutions.com>
* Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
*
* Based on sound/soc/codecs/wm8731.c by Richard Purdie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "tlv320aic23.h"
static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *i2c_id)
{
struct regmap *regmap;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EINVAL;
regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap);
return tlv320aic23_probe(&i2c->dev, regmap);
}
static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static const struct i2c_device_id tlv320aic23_id[] = {
{"tlv320aic23", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
static struct i2c_driver tlv320aic23_i2c_driver = {
.driver = {
.name = "tlv320aic23-codec",
},
.probe = tlv320aic23_i2c_probe,
.remove = __exit_p(tlv320aic23_i2c_remove),
.id_table = tlv320aic23_id,
};
module_i2c_driver(tlv320aic23_i2c_driver);
MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver I2C");
MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
MODULE_LICENSE("GPL");
/*
* ALSA SoC TLV320AIC23 codec driver SPI interface
*
* Author: Arun KS, <arunks@mistralsolutions.com>
* Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
*
* Based on sound/soc/codecs/wm8731.c by Richard Purdie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "tlv320aic23.h"
static int aic23_spi_probe(struct spi_device *spi)
{
int ret;
struct regmap *regmap;
dev_dbg(&spi->dev, "probing tlv320aic23 spi device\n");
spi->bits_per_word = 16;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0)
return ret;
regmap = devm_regmap_init_spi(spi, &tlv320aic23_regmap);
return tlv320aic23_probe(&spi->dev, regmap);
}
static int aic23_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver aic23_spi = {
.driver = {
.name = "tlv320aic23",
.owner = THIS_MODULE,
},
.probe = aic23_spi_probe,
.remove = aic23_spi_remove,
};
module_spi_driver(aic23_spi);
MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver SPI");
MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
MODULE_LICENSE("GPL");
......@@ -23,7 +23,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
......@@ -51,7 +50,7 @@ static const struct reg_default tlv320aic23_reg[] = {
{ 9, 0x0000 },
};
static const struct regmap_config tlv320aic23_regmap = {
const struct regmap_config tlv320aic23_regmap = {
.reg_bits = 7,
.val_bits = 9,
......@@ -60,20 +59,21 @@ static const struct regmap_config tlv320aic23_regmap = {
.num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg),
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL(tlv320aic23_regmap);
static const char *rec_src_text[] = { "Line", "Mic" };
static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"};
static const struct soc_enum rec_src_enum =
SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
static SOC_ENUM_SINGLE_DECL(rec_src_enum,
TLV320AIC23_ANLG, 2, rec_src_text);
static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls =
SOC_DAPM_ENUM("Input Select", rec_src_enum);
static const struct soc_enum tlv320aic23_rec_src =
SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
static const struct soc_enum tlv320aic23_deemph =
SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text);
static SOC_ENUM_SINGLE_DECL(tlv320aic23_rec_src,
TLV320AIC23_ANLG, 2, rec_src_text);
static SOC_ENUM_SINGLE_DECL(tlv320aic23_deemph,
TLV320AIC23_DIGT, 1, deemph_text);
static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0);
static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0);
......@@ -557,7 +557,7 @@ static int tlv320aic23_resume(struct snd_soc_codec *codec)
return 0;
}
static int tlv320aic23_probe(struct snd_soc_codec *codec)
static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
{
int ret;
......@@ -604,7 +604,7 @@ static int tlv320aic23_remove(struct snd_soc_codec *codec)
}
static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
.probe = tlv320aic23_probe,
.probe = tlv320aic23_codec_probe,
.remove = tlv320aic23_remove,
.suspend = tlv320aic23_suspend,
.resume = tlv320aic23_resume,
......@@ -617,56 +617,25 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
.num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
};
/*
* If the i2c layer weren't so broken, we could pass this kind of data
* around
*/
static int tlv320aic23_codec_probe(struct i2c_client *i2c,
const struct i2c_device_id *i2c_id)
int tlv320aic23_probe(struct device *dev, struct regmap *regmap)
{
struct aic23 *aic23;
int ret;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EINVAL;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
aic23 = devm_kzalloc(&i2c->dev, sizeof(struct aic23), GFP_KERNEL);
aic23 = devm_kzalloc(dev, sizeof(struct aic23), GFP_KERNEL);
if (aic23 == NULL)
return -ENOMEM;
aic23->regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap);
if (IS_ERR(aic23->regmap))
return PTR_ERR(aic23->regmap);
aic23->regmap = regmap;
i2c_set_clientdata(i2c, aic23);
dev_set_drvdata(dev, aic23);
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_tlv320aic23, &tlv320aic23_dai, 1);
return ret;
}
static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
return snd_soc_register_codec(dev, &soc_codec_dev_tlv320aic23,
&tlv320aic23_dai, 1);
}
static const struct i2c_device_id tlv320aic23_id[] = {
{"tlv320aic23", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
static struct i2c_driver tlv320aic23_i2c_driver = {
.driver = {
.name = "tlv320aic23-codec",
},
.probe = tlv320aic23_codec_probe,
.remove = __exit_p(tlv320aic23_i2c_remove),
.id_table = tlv320aic23_id,
};
module_i2c_driver(tlv320aic23_i2c_driver);
EXPORT_SYMBOL(tlv320aic23_probe);
MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
......
......@@ -12,6 +12,12 @@
#ifndef _TLV320AIC23_H
#define _TLV320AIC23_H
struct device;
struct regmap_config;
extern const struct regmap_config tlv320aic23_regmap;
int tlv320aic23_probe(struct device *dev, struct regmap *regmap);
/* Codec TLV320AIC23 */
#define TLV320AIC23_LINVOL 0x00
#define TLV320AIC23_RINVOL 0x01
......
......@@ -29,9 +29,12 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <sound/tlv320aic32x4.h>
#include <sound/core.h>
......@@ -66,20 +69,32 @@ struct aic32x4_priv {
u32 micpga_routing;
bool swapdacs;
int rstn_gpio;
struct clk *mclk;
struct regulator *supply_ldo;
struct regulator *supply_iov;
struct regulator *supply_dv;
struct regulator *supply_av;
};
/* 0dB min, 1dB steps */
static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0);
/* 0dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
/* -63.5dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_pcm, -6350, 50, 0);
/* -6dB min, 1dB steps */
static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
/* -12dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);
static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1),
SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1),
SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0,
tlv_driver_gain),
SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0,
tlv_driver_gain),
SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 6, 0x01, 1),
SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
......@@ -90,8 +105,8 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0),
SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0),
SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL,
AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5),
SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL,
AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol),
SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL,
AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5),
......@@ -480,8 +495,18 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
/* Switch on master clock */
ret = clk_prepare_enable(aic32x4->mclk);
if (ret) {
dev_err(codec->dev, "Failed to enable master clock\n");
return ret;
}
/* Switch on PLL */
snd_soc_update_bits(codec, AIC32X4_PLLPR,
AIC32X4_PLLEN, AIC32X4_PLLEN);
......@@ -509,29 +534,32 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* Switch off PLL */
snd_soc_update_bits(codec, AIC32X4_PLLPR,
AIC32X4_PLLEN, 0);
/* Switch off BCLK_N Divider */
snd_soc_update_bits(codec, AIC32X4_BCLKN,
AIC32X4_BCLKEN, 0);
/* Switch off NDAC Divider */
snd_soc_update_bits(codec, AIC32X4_NDAC,
AIC32X4_NDACEN, 0);
/* Switch off MADC Divider */
snd_soc_update_bits(codec, AIC32X4_MADC,
AIC32X4_MADCEN, 0);
/* Switch off NADC Divider */
snd_soc_update_bits(codec, AIC32X4_NADC,
AIC32X4_NADCEN, 0);
/* Switch off MDAC Divider */
snd_soc_update_bits(codec, AIC32X4_MDAC,
AIC32X4_MDACEN, 0);
/* Switch off NADC Divider */
snd_soc_update_bits(codec, AIC32X4_NADC,
AIC32X4_NADCEN, 0);
/* Switch off NDAC Divider */
snd_soc_update_bits(codec, AIC32X4_NDAC,
AIC32X4_NDACEN, 0);
/* Switch off MADC Divider */
snd_soc_update_bits(codec, AIC32X4_MADC,
AIC32X4_MADCEN, 0);
/* Switch off PLL */
snd_soc_update_bits(codec, AIC32X4_PLLPR,
AIC32X4_PLLEN, 0);
/* Switch off BCLK_N Divider */
snd_soc_update_bits(codec, AIC32X4_BCLKN,
AIC32X4_BCLKEN, 0);
/* Switch off master clock */
clk_disable_unprepare(aic32x4->mclk);
break;
case SND_SOC_BIAS_OFF:
break;
......@@ -588,7 +616,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
if (aic32x4->rstn_gpio >= 0) {
if (gpio_is_valid(aic32x4->rstn_gpio)) {
ndelay(10);
gpio_set_value(aic32x4->rstn_gpio, 1);
}
......@@ -663,11 +691,122 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
.num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
};
static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
struct device_node *np)
{
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
return 0;
}
static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4)
{
regulator_disable(aic32x4->supply_iov);
if (!IS_ERR(aic32x4->supply_ldo))
regulator_disable(aic32x4->supply_ldo);
if (!IS_ERR(aic32x4->supply_dv))
regulator_disable(aic32x4->supply_dv);
if (!IS_ERR(aic32x4->supply_av))
regulator_disable(aic32x4->supply_av);
}
static int aic32x4_setup_regulators(struct device *dev,
struct aic32x4_priv *aic32x4)
{
int ret = 0;
aic32x4->supply_ldo = devm_regulator_get_optional(dev, "ldoin");
aic32x4->supply_iov = devm_regulator_get(dev, "iov");
aic32x4->supply_dv = devm_regulator_get_optional(dev, "dv");
aic32x4->supply_av = devm_regulator_get_optional(dev, "av");
/* Check if the regulator requirements are fulfilled */
if (IS_ERR(aic32x4->supply_iov)) {
dev_err(dev, "Missing supply 'iov'\n");
return PTR_ERR(aic32x4->supply_iov);
}
if (IS_ERR(aic32x4->supply_ldo)) {
if (PTR_ERR(aic32x4->supply_ldo) == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (IS_ERR(aic32x4->supply_dv)) {
dev_err(dev, "Missing supply 'dv' or 'ldoin'\n");
return PTR_ERR(aic32x4->supply_dv);
}
if (IS_ERR(aic32x4->supply_av)) {
dev_err(dev, "Missing supply 'av' or 'ldoin'\n");
return PTR_ERR(aic32x4->supply_av);
}
} else {
if (IS_ERR(aic32x4->supply_dv) &&
PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (IS_ERR(aic32x4->supply_av) &&
PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
ret = regulator_enable(aic32x4->supply_iov);
if (ret) {
dev_err(dev, "Failed to enable regulator iov\n");
return ret;
}
if (!IS_ERR(aic32x4->supply_ldo)) {
ret = regulator_enable(aic32x4->supply_ldo);
if (ret) {
dev_err(dev, "Failed to enable regulator ldo\n");
goto error_ldo;
}
}
if (!IS_ERR(aic32x4->supply_dv)) {
ret = regulator_enable(aic32x4->supply_dv);
if (ret) {
dev_err(dev, "Failed to enable regulator dv\n");
goto error_dv;
}
}
if (!IS_ERR(aic32x4->supply_av)) {
ret = regulator_enable(aic32x4->supply_av);
if (ret) {
dev_err(dev, "Failed to enable regulator av\n");
goto error_av;
}
}
if (!IS_ERR(aic32x4->supply_ldo) && IS_ERR(aic32x4->supply_av))
aic32x4->power_cfg |= AIC32X4_PWR_AIC32X4_LDO_ENABLE;
return 0;
error_av:
if (!IS_ERR(aic32x4->supply_dv))
regulator_disable(aic32x4->supply_dv);
error_dv:
if (!IS_ERR(aic32x4->supply_ldo))
regulator_disable(aic32x4->supply_ldo);
error_ldo:
regulator_disable(aic32x4->supply_iov);
return ret;
}
static int aic32x4_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct aic32x4_pdata *pdata = i2c->dev.platform_data;
struct aic32x4_priv *aic32x4;
struct device_node *np = i2c->dev.of_node;
int ret;
aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv),
......@@ -686,6 +825,12 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
aic32x4->swapdacs = pdata->swapdacs;
aic32x4->micpga_routing = pdata->micpga_routing;
aic32x4->rstn_gpio = pdata->rstn_gpio;
} else if (np) {
ret = aic32x4_parse_dt(aic32x4, np);
if (ret) {
dev_err(&i2c->dev, "Failed to parse DT node\n");
return ret;
}
} else {
aic32x4->power_cfg = 0;
aic32x4->swapdacs = false;
......@@ -693,20 +838,44 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
aic32x4->rstn_gpio = -1;
}
if (aic32x4->rstn_gpio >= 0) {
aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk");
if (IS_ERR(aic32x4->mclk)) {
dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
return PTR_ERR(aic32x4->mclk);
}
if (gpio_is_valid(aic32x4->rstn_gpio)) {
ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio,
GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
if (ret != 0)
return ret;
}
ret = aic32x4_setup_regulators(&i2c->dev, aic32x4);
if (ret) {
dev_err(&i2c->dev, "Failed to setup regulators\n");
return ret;
}
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_aic32x4, &aic32x4_dai, 1);
return ret;
if (ret) {
dev_err(&i2c->dev, "Failed to register codec\n");
aic32x4_disable_regulators(aic32x4);
return ret;
}
i2c_set_clientdata(i2c, aic32x4);
return 0;
}
static int aic32x4_i2c_remove(struct i2c_client *client)
{
struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client);
aic32x4_disable_regulators(aic32x4);
snd_soc_unregister_codec(&client->dev);
return 0;
}
......@@ -717,10 +886,17 @@ static const struct i2c_device_id aic32x4_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
static const struct of_device_id aic32x4_of_id[] = {
{ .compatible = "ti,tlv320aic32x4", },
{ /* senitel */ }
};
MODULE_DEVICE_TABLE(of, aic32x4_of_id);
static struct i2c_driver aic32x4_i2c_driver = {
.driver = {
.name = "tlv320aic32x4",
.owner = THIS_MODULE,
.of_match_table = aic32x4_of_id,
},
.probe = aic32x4_i2c_probe,
.remove = aic32x4_i2c_remove,
......
......@@ -171,26 +171,23 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
static const char *wm8991_digital_sidetone[] =
{"None", "Left ADC", "Right ADC", "Reserved"};
static const struct soc_enum wm8991_left_digital_sidetone_enum =
SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
WM8991_ADC_TO_DACL_SHIFT,
WM8991_ADC_TO_DACL_MASK,
wm8991_digital_sidetone);
static const struct soc_enum wm8991_right_digital_sidetone_enum =
SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
WM8991_ADC_TO_DACR_SHIFT,
WM8991_ADC_TO_DACR_MASK,
wm8991_digital_sidetone);
static SOC_ENUM_SINGLE_DECL(wm8991_left_digital_sidetone_enum,
WM8991_DIGITAL_SIDE_TONE,
WM8991_ADC_TO_DACL_SHIFT,
wm8991_digital_sidetone);
static SOC_ENUM_SINGLE_DECL(wm8991_right_digital_sidetone_enum,
WM8991_DIGITAL_SIDE_TONE,
WM8991_ADC_TO_DACR_SHIFT,
wm8991_digital_sidetone);
static const char *wm8991_adcmode[] =
{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
static const struct soc_enum wm8991_right_adcmode_enum =
SOC_ENUM_SINGLE(WM8991_ADC_CTRL,
WM8991_ADC_HPF_CUT_SHIFT,
WM8991_ADC_HPF_CUT_MASK,
wm8991_adcmode);
static SOC_ENUM_SINGLE_DECL(wm8991_right_adcmode_enum,
WM8991_ADC_CTRL,
WM8991_ADC_HPF_CUT_SHIFT,
wm8991_adcmode);
static const struct snd_kcontrol_new wm8991_snd_controls[] = {
/* INMIXL */
......@@ -486,9 +483,9 @@ static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
static const char *wm8991_ainlmux[] =
{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
static const struct soc_enum wm8991_ainlmux_enum =
SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux);
static SOC_ENUM_SINGLE_DECL(wm8991_ainlmux_enum,
WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
wm8991_ainlmux);
static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
......@@ -499,9 +496,9 @@ static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
static const char *wm8991_ainrmux[] =
{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
static const struct soc_enum wm8991_ainrmux_enum =
SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux);
static SOC_ENUM_SINGLE_DECL(wm8991_ainrmux_enum,
WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
wm8991_ainrmux);
static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
......@@ -1251,11 +1248,8 @@ static int wm8991_remove(struct snd_soc_codec *codec)
static int wm8991_probe(struct snd_soc_codec *codec)
{
struct wm8991_priv *wm8991;
int ret;
wm8991 = snd_soc_codec_get_drvdata(codec);
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
......
......@@ -170,7 +170,7 @@ config SND_SOC_EUKREA_TLV320
|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
|| MACH_EUKREA_MBIMXSD51_BASEBOARD
depends on I2C
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
select SND_SOC_IMX_PCM_FIQ
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_SSI
......
......@@ -58,7 +58,7 @@ config SND_OMAP_SOC_OSK5912
tristate "SoC Audio support for omap osk5912"
depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
help
Say Y if you want to add support for SoC audio on osk5912.
......@@ -66,7 +66,7 @@ config SND_OMAP_SOC_AM3517EVM
tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
help
Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
EVM.
......
......@@ -117,7 +117,7 @@ config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23
tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
select SND_S3C24XX_I2S
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
select SND_SOC_SAMSUNG_SIMTEC
config SND_SOC_SAMSUNG_SIMTEC_HERMES
......
......@@ -2715,6 +2715,48 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
/**
* snd_soc_read_signed - Read a codec register and interprete as signed value
* @codec: codec
* @reg: Register to read
* @mask: Mask to use after shifting the register value
* @shift: Right shift of register value
* @sign_bit: Bit that describes if a number is negative or not.
*
* This functions reads a codec register. The register value is shifted right
* by 'shift' bits and masked with the given 'mask'. Afterwards it translates
* the given registervalue into a signed integer if sign_bit is non-zero.
*
* Returns the register value as signed int.
*/
static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg,
unsigned int mask, unsigned int shift, unsigned int sign_bit)
{
int ret;
unsigned int val;
val = (snd_soc_read(codec, reg) >> shift) & mask;
if (!sign_bit)
return val;
/* non-negative number */
if (!(val & BIT(sign_bit)))
return val;
ret = val;
/*
* The register most probably does not contain a full-sized int.
* Instead we have an arbitrary number of bits in a signed
* representation which has to be translated into a full-sized int.
* This is done by filling up all bits above the sign-bit.
*/
ret |= ~((int)(BIT(sign_bit) - 1));
return ret;
}
/**
* snd_soc_info_volsw - single mixer info callback
* @kcontrol: mixer control
......@@ -2743,7 +2785,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max;
uinfo->value.integer.max = platform_max - mc->min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
......@@ -2769,11 +2811,16 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int sign_bit = mc->sign_bit;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
ucontrol->value.integer.value[0] =
(snd_soc_read(codec, reg) >> shift) & mask;
if (sign_bit)
mask = BIT(sign_bit + 1) - 1;
ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask,
shift, sign_bit) - min;
if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
......@@ -2781,10 +2828,12 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
if (snd_soc_volsw_is_stereo(mc)) {
if (reg == reg2)
ucontrol->value.integer.value[1] =
(snd_soc_read(codec, reg) >> rshift) & mask;
snd_soc_read_signed(codec, reg, mask, rshift,
sign_bit) - min;
else
ucontrol->value.integer.value[1] =
(snd_soc_read(codec, reg2) >> shift) & mask;
snd_soc_read_signed(codec, reg2, mask, shift,
sign_bit) - min;
if (invert)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
......@@ -2815,6 +2864,8 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
unsigned int sign_bit = mc->sign_bit;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int err;
......@@ -2822,13 +2873,16 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int val2 = 0;
unsigned int val, val_mask;
val = (ucontrol->value.integer.value[0] & mask);
if (sign_bit)
mask = BIT(sign_bit + 1) - 1;
val = ((ucontrol->value.integer.value[0] + min) & mask);
if (invert)
val = max - val;
val_mask = mask << shift;
val = val << shift;
if (snd_soc_volsw_is_stereo(mc)) {
val2 = (ucontrol->value.integer.value[1] & mask);
val2 = ((ucontrol->value.integer.value[1] + min) & mask);
if (invert)
val2 = max - val2;
if (reg == reg2) {
......
......@@ -105,7 +105,7 @@ config SND_SOC_TEGRA_TRIMSLICE
tristate "SoC Audio support for TrimSlice board"
depends on SND_SOC_TEGRA && I2C
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC23_I2C
help
Say Y or M here if you want to add support for SoC audio on the
TrimSlice platform.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment