Commit 54e7e616 authored by Daniel Mack's avatar Daniel Mack Committed by Jaroslav Kysela

[ALSA] soc - tlv320aic3x - add GPIO support

This patch adds support for AIC3x GPIO lines. They can be configured for
many possible functions as well as be driven manually. I also introduced
i2c read functionality since the GPIO state register has to be read from
hardware every time and can not be served from cache.
Signed-off-by: default avatarDaniel Mack <daniel@caiaq.de>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent 4f9c16cc
...@@ -138,6 +138,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, ...@@ -138,6 +138,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO; return -EIO;
} }
/*
* read from the aic3x register space
*/
static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
u8 *value)
{
*value = reg & 0xff;
if (codec->hw_read(codec->control_data, value, 1) != 1)
return -EIO;
aic3x_write_reg_cache(codec, reg, *value);
return 0;
}
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \ .info = snd_soc_info_volsw, \
...@@ -911,6 +925,33 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) ...@@ -911,6 +925,33 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
return 0; return 0;
} }
void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
{
u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
u8 bit = gpio ? 3: 0;
u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
aic3x_write(codec, reg, val | (!!state << bit));
}
EXPORT_SYMBOL_GPL(aic3x_set_gpio);
int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
{
u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
u8 val, bit = gpio ? 2: 1;
aic3x_read(codec, reg, &val);
return (val >> bit) & 1;
}
EXPORT_SYMBOL_GPL(aic3x_get_gpio);
int aic3x_headset_detected(struct snd_soc_codec *codec)
{
u8 val;
aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
return (val >> 2) & 1;
}
EXPORT_SYMBOL_GPL(aic3x_headset_detected);
#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 #define AIC3X_RATES SNDRV_PCM_RATE_8000_96000
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ #define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
...@@ -977,6 +1018,7 @@ static int aic3x_resume(struct platform_device *pdev) ...@@ -977,6 +1018,7 @@ static int aic3x_resume(struct platform_device *pdev)
static int aic3x_init(struct snd_soc_device *socdev) static int aic3x_init(struct snd_soc_device *socdev)
{ {
struct snd_soc_codec *codec = socdev->codec; struct snd_soc_codec *codec = socdev->codec;
struct aic3x_setup_data *setup = socdev->codec_data;
int reg, ret = 0; int reg, ret = 0;
codec->name = "aic3x"; codec->name = "aic3x";
...@@ -1067,6 +1109,10 @@ static int aic3x_init(struct snd_soc_device *socdev) ...@@ -1067,6 +1109,10 @@ static int aic3x_init(struct snd_soc_device *socdev)
/* off, with power on */ /* off, with power on */
aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot); aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
/* setup GPIO functions */
aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
aic3x_add_controls(codec); aic3x_add_controls(codec);
aic3x_add_widgets(codec); aic3x_add_widgets(codec);
ret = snd_soc_register_card(socdev); ret = snd_soc_register_card(socdev);
...@@ -1174,6 +1220,12 @@ static struct i2c_client client_template = { ...@@ -1174,6 +1220,12 @@ static struct i2c_client client_template = {
.name = "AIC3X", .name = "AIC3X",
.driver = &aic3x_i2c_driver, .driver = &aic3x_i2c_driver,
}; };
static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
{
value[0] = i2c_smbus_read_byte_data(client, value[0]);
return (len == 1);
}
#endif #endif
static int aic3x_probe(struct platform_device *pdev) static int aic3x_probe(struct platform_device *pdev)
...@@ -1208,6 +1260,7 @@ static int aic3x_probe(struct platform_device *pdev) ...@@ -1208,6 +1260,7 @@ static int aic3x_probe(struct platform_device *pdev)
if (setup->i2c_address) { if (setup->i2c_address) {
normal_i2c[0] = setup->i2c_address; normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t) i2c_master_send; codec->hw_write = (hw_write_t) i2c_master_send;
codec->hw_read = (hw_read_t) aic3x_i2c_read;
ret = i2c_add_driver(&aic3x_i2c_driver); ret = i2c_add_driver(&aic3x_i2c_driver);
if (ret != 0) if (ret != 0)
printk(KERN_ERR "can't add i2c driver"); printk(KERN_ERR "can't add i2c driver");
......
...@@ -108,8 +108,14 @@ ...@@ -108,8 +108,14 @@
#define DACR1_2_RLOPM_VOL 92 #define DACR1_2_RLOPM_VOL 92
#define LLOPM_CTRL 86 #define LLOPM_CTRL 86
#define RLOPM_CTRL 93 #define RLOPM_CTRL 93
/* Clock generation control register */ /* GPIO/IRQ registers */
#define AIC3X_STICKY_IRQ_FLAGS_REG 96
#define AIC3X_RT_IRQ_FLAGS_REG 97
#define AIC3X_GPIO1_REG 98
#define AIC3X_GPIO2_REG 99
#define AIC3X_GPIOA_REG 100
#define AIC3X_GPIOB_REG 101 #define AIC3X_GPIOB_REG 101
/* Clock generation control register */
#define AIC3X_CLKGEN_CTRL_REG 102 #define AIC3X_CLKGEN_CTRL_REG 102
/* Page select register bits */ /* Page select register bits */
...@@ -175,8 +181,49 @@ ...@@ -175,8 +181,49 @@
/* Default input volume */ /* Default input volume */
#define DEFAULT_GAIN 0x20 #define DEFAULT_GAIN 0x20
/* GPIO API */
enum {
AIC3X_GPIO1_FUNC_DISABLED = 0,
AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC = 1,
AIC3X_GPIO1_FUNC_CLOCK_MUX = 2,
AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2 = 3,
AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4 = 4,
AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8 = 5,
AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ = 6,
AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ = 7,
AIC3X_GPIO1_FUNC_INPUT = 8,
AIC3X_GPIO1_FUNC_OUTPUT = 9,
AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK = 10,
AIC3X_GPIO1_FUNC_AUDIO_WORDCLK = 11,
AIC3X_GPIO1_FUNC_BUTTON_IRQ = 12,
AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ = 13,
AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 14,
AIC3X_GPIO1_FUNC_ALL_IRQ = 16
};
enum {
AIC3X_GPIO2_FUNC_DISABLED = 0,
AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ = 2,
AIC3X_GPIO2_FUNC_INPUT = 3,
AIC3X_GPIO2_FUNC_OUTPUT = 4,
AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT = 5,
AIC3X_GPIO2_FUNC_AUDIO_BITCLK = 8,
AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9,
AIC3X_GPIO2_FUNC_ALL_IRQ = 10,
AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11,
AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12,
AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ = 13,
AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ = 14,
AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15
};
void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
int aic3x_headset_detected(struct snd_soc_codec *codec);
struct aic3x_setup_data { struct aic3x_setup_data {
unsigned short i2c_address; unsigned short i2c_address;
unsigned int gpio_func[2];
}; };
extern struct snd_soc_codec_dai aic3x_dai; extern struct snd_soc_codec_dai aic3x_dai;
......
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