Commit 0704129f authored by Mark Brown's avatar Mark Brown

Merge branch 'for-2.6.37' of...

Merge branch 'for-2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc-2.6 into for-2.6.37
parents d71b3576 7d1be0a6
...@@ -61,15 +61,25 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { ...@@ -61,15 +61,25 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
"DRVDD", /* ADC Analog and Output Driver Voltage */ "DRVDD", /* ADC Analog and Output Driver Voltage */
}; };
struct aic3x_priv;
struct aic3x_disable_nb {
struct notifier_block nb;
struct aic3x_priv *aic3x;
};
/* codec private data */ /* codec private data */
struct aic3x_priv { struct aic3x_priv {
struct snd_soc_codec *codec;
struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
enum snd_soc_control_type control_type; enum snd_soc_control_type control_type;
struct aic3x_setup_data *setup; struct aic3x_setup_data *setup;
void *control_data; void *control_data;
unsigned int sysclk; unsigned int sysclk;
int master; int master;
int gpio_reset; int gpio_reset;
int power;
#define AIC3X_MODEL_3X 0 #define AIC3X_MODEL_3X 0
#define AIC3X_MODEL_33 1 #define AIC3X_MODEL_33 1
#define AIC3X_MODEL_3007 2 #define AIC3X_MODEL_3007 2
...@@ -112,62 +122,23 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { ...@@ -112,62 +122,23 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
}; };
/* /*
* read aic3x register cache * read from the aic3x register space. Only use for this function is if
* wanting to read volatile bits from those registers that has both read-only
* and read/write bits. All other cases should use snd_soc_read.
*/ */
static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec, static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
unsigned int reg) u8 *value)
{ {
u8 *cache = codec->reg_cache; u8 *cache = codec->reg_cache;
if (reg >= AIC3X_CACHEREGNUM)
return -1;
return cache[reg];
}
/* if (codec->cache_only)
* write aic3x register cache return -EINVAL;
*/
static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec,
u8 reg, u8 value)
{
u8 *cache = codec->reg_cache;
if (reg >= AIC3X_CACHEREGNUM) if (reg >= AIC3X_CACHEREGNUM)
return; return -1;
cache[reg] = value;
}
/*
* write to the aic3x register space
*/
static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
/* data is
* D15..D8 aic3x register offset
* D7...D0 register data
*/
data[0] = reg & 0xff;
data[1] = value & 0xff;
aic3x_write_reg_cache(codec, data[0], data[1]);
if (codec->hw_write(codec->control_data, data, 2) == 2)
return 0;
else
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;
value[0] = i2c_smbus_read_byte_data(codec->control_data, value[0]); *value = codec->hw_read(codec, reg);
cache[reg] = *value;
aic3x_write_reg_cache(codec, reg, *value);
return 0; return 0;
} }
...@@ -646,6 +617,14 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { ...@@ -646,6 +617,14 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINE1R"), SND_SOC_DAPM_INPUT("LINE1R"),
SND_SOC_DAPM_INPUT("LINE2L"), SND_SOC_DAPM_INPUT("LINE2L"),
SND_SOC_DAPM_INPUT("LINE2R"), SND_SOC_DAPM_INPUT("LINE2R"),
/*
* Virtual output pin to detection block inside codec. This can be
* used to keep codec bias on if gpio or detection features are needed.
* Force pin on or construct a path with an input jack and mic bias
* widgets.
*/
SND_SOC_DAPM_OUTPUT("Detection"),
}; };
static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = { static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = {
...@@ -839,8 +818,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -839,8 +818,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
int clk; int clk;
/* select data word length */ /* select data word length */
data = data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
break; break;
...@@ -854,7 +832,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -854,7 +832,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
data |= (0x03 << 4); data |= (0x03 << 4);
break; break;
} }
aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data); snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, data);
/* Fsref can be 44100 or 48000 */ /* Fsref can be 44100 or 48000 */
fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
...@@ -869,17 +847,17 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -869,17 +847,17 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
if (bypass_pll) { if (bypass_pll) {
pll_q &= 0xf; pll_q &= 0xf;
aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT); snd_soc_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV); snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
/* disable PLL if it is bypassed */ /* disable PLL if it is bypassed */
reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE); snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE);
} else { } else {
aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV); snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
/* enable PLL when it is used */ /* enable PLL when it is used */
reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE); snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE);
} }
/* Route Left DAC to left channel input and /* Route Left DAC to left channel input and
...@@ -888,7 +866,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -888,7 +866,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
if (params_rate(params) >= 64000) if (params_rate(params) >= 64000)
data |= DUAL_RATE_MODE; data |= DUAL_RATE_MODE;
aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data); snd_soc_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */ /* codec sample rate select */
data = (fsref * 20) / params_rate(params); data = (fsref * 20) / params_rate(params);
...@@ -897,7 +875,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -897,7 +875,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
data /= 5; data /= 5;
data -= 2; data -= 2;
data |= (data << 4); data |= (data << 4);
aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data); snd_soc_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
if (bypass_pll) if (bypass_pll)
return 0; return 0;
...@@ -966,12 +944,15 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -966,12 +944,15 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
} }
found: found:
data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); data = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT); data | (pll_p << PLLP_SHIFT));
aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT); snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,
aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT); pll_r << PLLR_SHIFT);
aic3x_write(codec, AIC3X_PLL_PROGD_REG, snd_soc_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
snd_soc_write(codec, AIC3X_PLL_PROGC_REG,
(pll_d >> 6) << PLLD_MSB_SHIFT);
snd_soc_write(codec, AIC3X_PLL_PROGD_REG,
(pll_d & 0x3F) << PLLD_LSB_SHIFT); (pll_d & 0x3F) << PLLD_LSB_SHIFT);
return 0; return 0;
...@@ -980,15 +961,15 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -980,15 +961,15 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
static int aic3x_mute(struct snd_soc_dai *dai, int mute) static int aic3x_mute(struct snd_soc_dai *dai, int mute)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON; u8 ldac_reg = snd_soc_read(codec, LDAC_VOL) & ~MUTE_ON;
u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON; u8 rdac_reg = snd_soc_read(codec, RDAC_VOL) & ~MUTE_ON;
if (mute) { if (mute) {
aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON); snd_soc_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON); snd_soc_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
} else { } else {
aic3x_write(codec, LDAC_VOL, ldac_reg); snd_soc_write(codec, LDAC_VOL, ldac_reg);
aic3x_write(codec, RDAC_VOL, rdac_reg); snd_soc_write(codec, RDAC_VOL, rdac_reg);
} }
return 0; return 0;
...@@ -1012,8 +993,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -1012,8 +993,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
u8 iface_areg, iface_breg; u8 iface_areg, iface_breg;
int delay = 0; int delay = 0;
iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f; iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f; iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
/* set master/slave audio interface */ /* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
...@@ -1052,13 +1033,98 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -1052,13 +1033,98 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
} }
/* set iface */ /* set iface */
aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg); snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg); snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay); snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
return 0;
}
static int aic3x_init_3007(struct snd_soc_codec *codec)
{
u8 tmp1, tmp2, *cache = codec->reg_cache;
/*
* There is no need to cache writes to undocumented page 0xD but
* respective page 0 register cache entries must be preserved
*/
tmp1 = cache[0xD];
tmp2 = cache[0x8];
/* Class-D speaker driver init; datasheet p. 46 */
snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x0D);
snd_soc_write(codec, 0xD, 0x0D);
snd_soc_write(codec, 0x8, 0x5C);
snd_soc_write(codec, 0x8, 0x5D);
snd_soc_write(codec, 0x8, 0x5C);
snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x00);
cache[0xD] = tmp1;
cache[0x8] = tmp2;
return 0; return 0;
} }
static int aic3x_regulator_event(struct notifier_block *nb,
unsigned long event, void *data)
{
struct aic3x_disable_nb *disable_nb =
container_of(nb, struct aic3x_disable_nb, nb);
struct aic3x_priv *aic3x = disable_nb->aic3x;
if (event & REGULATOR_EVENT_DISABLE) {
/*
* Put codec to reset and require cache sync as at least one
* of the supplies was disabled
*/
if (aic3x->gpio_reset >= 0)
gpio_set_value(aic3x->gpio_reset, 0);
aic3x->codec->cache_sync = 1;
}
return 0;
}
static int aic3x_set_power(struct snd_soc_codec *codec, int power)
{
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int i, ret;
u8 *cache = codec->reg_cache;
if (power) {
ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
if (ret)
goto out;
aic3x->power = 1;
/*
* Reset release and cache sync is necessary only if some
* supply was off or if there were cached writes
*/
if (!codec->cache_sync)
goto out;
if (aic3x->gpio_reset >= 0) {
udelay(1);
gpio_set_value(aic3x->gpio_reset, 1);
}
/* Sync reg_cache with the hardware */
codec->cache_only = 0;
for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++)
snd_soc_write(codec, i, cache[i]);
if (aic3x->model == AIC3X_MODEL_3007)
aic3x_init_3007(codec);
codec->cache_sync = 0;
} else {
aic3x->power = 0;
/* HW writes are needless when bias is off */
codec->cache_only = 1;
ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
}
out:
return ret;
}
static int aic3x_set_bias_level(struct snd_soc_codec *codec, static int aic3x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
...@@ -1069,23 +1135,29 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, ...@@ -1069,23 +1135,29 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
break; break;
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
if (aic3x->master) { if (codec->bias_level == SND_SOC_BIAS_STANDBY &&
aic3x->master) {
/* enable pll */ /* enable pll */
reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
reg | PLL_ENABLE); reg | PLL_ENABLE);
} }
break; break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
/* fall through and disable pll */ if (!aic3x->power)
case SND_SOC_BIAS_OFF: aic3x_set_power(codec, 1);
if (aic3x->master) { if (codec->bias_level == SND_SOC_BIAS_PREPARE &&
aic3x->master) {
/* disable pll */ /* disable pll */
reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
reg & ~PLL_ENABLE); reg & ~PLL_ENABLE);
} }
break; break;
case SND_SOC_BIAS_OFF:
if (aic3x->power)
aic3x_set_power(codec, 0);
break;
} }
codec->bias_level = level; codec->bias_level = level;
...@@ -1096,8 +1168,8 @@ void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state) ...@@ -1096,8 +1168,8 @@ void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
{ {
u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG; u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
u8 bit = gpio ? 3: 0; u8 bit = gpio ? 3: 0;
u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit); u8 val = snd_soc_read(codec, reg) & ~(1 << bit);
aic3x_write(codec, reg, val | (!!state << bit)); snd_soc_write(codec, reg, val | (!!state << bit));
} }
EXPORT_SYMBOL_GPL(aic3x_set_gpio); EXPORT_SYMBOL_GPL(aic3x_set_gpio);
...@@ -1126,7 +1198,7 @@ void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect, ...@@ -1126,7 +1198,7 @@ void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
if (detect & AIC3X_HEADSET_DETECT_MASK) if (detect & AIC3X_HEADSET_DETECT_MASK)
val |= AIC3X_HEADSET_DETECT_ENABLED; val |= AIC3X_HEADSET_DETECT_ENABLED;
aic3x_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val); snd_soc_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
} }
EXPORT_SYMBOL_GPL(aic3x_set_headset_detection); EXPORT_SYMBOL_GPL(aic3x_set_headset_detection);
...@@ -1184,17 +1256,6 @@ static int aic3x_suspend(struct snd_soc_codec *codec, pm_message_t state) ...@@ -1184,17 +1256,6 @@ static int aic3x_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int aic3x_resume(struct snd_soc_codec *codec) static int aic3x_resume(struct snd_soc_codec *codec)
{ {
int i;
u8 data[2];
u8 *cache = codec->reg_cache;
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) {
data[0] = i;
data[1] = cache[i];
codec->hw_write(codec->control_data, data, 2);
}
aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; return 0;
...@@ -1209,103 +1270,132 @@ static int aic3x_init(struct snd_soc_codec *codec) ...@@ -1209,103 +1270,132 @@ static int aic3x_init(struct snd_soc_codec *codec)
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int reg; int reg;
aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); snd_soc_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
aic3x_write(codec, AIC3X_RESET, SOFT_RESET); snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
/* DAC default volume and mute */ /* DAC default volume and mute */
aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON); snd_soc_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON); snd_soc_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
/* DAC to HP default volume and route to Output mixer */ /* DAC to HP default volume and route to Output mixer */
aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
aic3x_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
aic3x_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
/* DAC to Line Out default volume and route to Output mixer */ /* DAC to Line Out default volume and route to Output mixer */
aic3x_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
aic3x_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
/* DAC to Mono Line Out default volume and route to Output mixer */ /* DAC to Mono Line Out default volume and route to Output mixer */
aic3x_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
aic3x_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
/* unmute all outputs */ /* unmute all outputs */
reg = aic3x_read_reg_cache(codec, LLOPM_CTRL); reg = snd_soc_read(codec, LLOPM_CTRL);
aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE); snd_soc_write(codec, LLOPM_CTRL, reg | UNMUTE);
reg = aic3x_read_reg_cache(codec, RLOPM_CTRL); reg = snd_soc_read(codec, RLOPM_CTRL);
aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE); snd_soc_write(codec, RLOPM_CTRL, reg | UNMUTE);
reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL); reg = snd_soc_read(codec, MONOLOPM_CTRL);
aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE); snd_soc_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL); reg = snd_soc_read(codec, HPLOUT_CTRL);
aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE); snd_soc_write(codec, HPLOUT_CTRL, reg | UNMUTE);
reg = aic3x_read_reg_cache(codec, HPROUT_CTRL); reg = snd_soc_read(codec, HPROUT_CTRL);
aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE); snd_soc_write(codec, HPROUT_CTRL, reg | UNMUTE);
reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL); reg = snd_soc_read(codec, HPLCOM_CTRL);
aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE); snd_soc_write(codec, HPLCOM_CTRL, reg | UNMUTE);
reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL); reg = snd_soc_read(codec, HPRCOM_CTRL);
aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE); snd_soc_write(codec, HPRCOM_CTRL, reg | UNMUTE);
/* ADC default volume and unmute */ /* ADC default volume and unmute */
aic3x_write(codec, LADC_VOL, DEFAULT_GAIN); snd_soc_write(codec, LADC_VOL, DEFAULT_GAIN);
aic3x_write(codec, RADC_VOL, DEFAULT_GAIN); snd_soc_write(codec, RADC_VOL, DEFAULT_GAIN);
/* By default route Line1 to ADC PGA mixer */ /* By default route Line1 to ADC PGA mixer */
aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0); snd_soc_write(codec, LINE1L_2_LADC_CTRL, 0x0);
aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0); snd_soc_write(codec, LINE1R_2_RADC_CTRL, 0x0);
/* PGA to HP Bypass default volume, disconnect from Output Mixer */ /* PGA to HP Bypass default volume, disconnect from Output Mixer */
aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
/* PGA to Line Out default volume, disconnect from Output Mixer */ /* PGA to Line Out default volume, disconnect from Output Mixer */
aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
/* PGA to Mono Line Out default volume, disconnect from Output Mixer */ /* PGA to Mono Line Out default volume, disconnect from Output Mixer */
aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
/* Line2 to HP Bypass default volume, disconnect from Output Mixer */ /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
/* Line2 Line Out default volume, disconnect from Output Mixer */ /* Line2 Line Out default volume, disconnect from Output Mixer */
aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
/* Line2 to Mono Out default volume, disconnect from Output Mixer */ /* Line2 to Mono Out default volume, disconnect from Output Mixer */
aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
if (aic3x->model == AIC3X_MODEL_3007) { if (aic3x->model == AIC3X_MODEL_3007) {
/* Class-D speaker driver init; datasheet p. 46 */ aic3x_init_3007(codec);
aic3x_write(codec, AIC3X_PAGE_SELECT, 0x0D); snd_soc_write(codec, CLASSD_CTRL, 0);
aic3x_write(codec, 0xD, 0x0D);
aic3x_write(codec, 0x8, 0x5C);
aic3x_write(codec, 0x8, 0x5D);
aic3x_write(codec, 0x8, 0x5C);
aic3x_write(codec, AIC3X_PAGE_SELECT, 0x00);
aic3x_write(codec, CLASSD_CTRL, 0);
} }
/* off, with power on */
aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; return 0;
} }
static int aic3x_probe(struct snd_soc_codec *codec) static int aic3x_probe(struct snd_soc_codec *codec)
{ {
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int ret, i;
codec->hw_write = (hw_write_t) i2c_master_send;
codec->control_data = aic3x->control_data; codec->control_data = aic3x->control_data;
aic3x->codec = codec;
codec->idle_bias_off = 1;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
if (aic3x->gpio_reset >= 0) {
ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
if (ret != 0)
goto err_gpio;
gpio_direction_output(aic3x->gpio_reset, 0);
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err_get;
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
aic3x->disable_nb[i].aic3x = aic3x;
ret = regulator_register_notifier(aic3x->supplies[i].consumer,
&aic3x->disable_nb[i].nb);
if (ret) {
dev_err(codec->dev,
"Failed to request regulator notifier: %d\n",
ret);
goto err_notif;
}
}
codec->cache_only = 1;
aic3x_init(codec); aic3x_init(codec);
if (aic3x->setup) { if (aic3x->setup) {
/* setup GPIO functions */ /* setup GPIO functions */
aic3x_write(codec, AIC3X_GPIO1_REG, snd_soc_write(codec, AIC3X_GPIO1_REG,
(aic3x->setup->gpio_func[0] & 0xf) << 4); (aic3x->setup->gpio_func[0] & 0xf) << 4);
aic3x_write(codec, AIC3X_GPIO2_REG, snd_soc_write(codec, AIC3X_GPIO2_REG,
(aic3x->setup->gpio_func[1] & 0xf) << 4); (aic3x->setup->gpio_func[1] & 0xf) << 4);
} }
...@@ -1317,17 +1407,39 @@ static int aic3x_probe(struct snd_soc_codec *codec) ...@@ -1317,17 +1407,39 @@ static int aic3x_probe(struct snd_soc_codec *codec)
aic3x_add_widgets(codec); aic3x_add_widgets(codec);
return 0; return 0;
err_notif:
while (i--)
regulator_unregister_notifier(aic3x->supplies[i].consumer,
&aic3x->disable_nb[i].nb);
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
err_get:
if (aic3x->gpio_reset >= 0)
gpio_free(aic3x->gpio_reset);
err_gpio:
kfree(aic3x);
return ret;
} }
static int aic3x_remove(struct snd_soc_codec *codec) static int aic3x_remove(struct snd_soc_codec *codec)
{ {
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int i;
aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
if (aic3x->gpio_reset >= 0) {
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
regulator_unregister_notifier(aic3x->supplies[i].consumer,
&aic3x->disable_nb[i].nb);
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
return 0; return 0;
} }
static struct snd_soc_codec_driver soc_codec_dev_aic3x = { static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
.read = aic3x_read_reg_cache,
.write = aic3x_write,
.set_bias_level = aic3x_set_bias_level, .set_bias_level = aic3x_set_bias_level,
.reg_cache_size = ARRAY_SIZE(aic3x_reg), .reg_cache_size = ARRAY_SIZE(aic3x_reg),
.reg_word_size = sizeof(u8), .reg_word_size = sizeof(u8),
...@@ -1361,7 +1473,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, ...@@ -1361,7 +1473,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
{ {
struct aic3x_pdata *pdata = i2c->dev.platform_data; struct aic3x_pdata *pdata = i2c->dev.platform_data;
struct aic3x_priv *aic3x; struct aic3x_priv *aic3x;
int ret, i; int ret;
const struct i2c_device_id *tbl; const struct i2c_device_id *tbl;
aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
...@@ -1371,6 +1483,8 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, ...@@ -1371,6 +1483,8 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
} }
aic3x->control_data = i2c; aic3x->control_data = i2c;
aic3x->control_type = SND_SOC_I2C;
i2c_set_clientdata(i2c, aic3x); i2c_set_clientdata(i2c, aic3x);
if (pdata) { if (pdata) {
aic3x->gpio_reset = pdata->gpio_reset; aic3x->gpio_reset = pdata->gpio_reset;
...@@ -1379,68 +1493,21 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, ...@@ -1379,68 +1493,21 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
aic3x->gpio_reset = -1; aic3x->gpio_reset = -1;
} }
if (aic3x->gpio_reset >= 0) {
ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
if (ret != 0)
goto err_gpio;
gpio_direction_output(aic3x->gpio_reset, 0);
}
for (tbl = aic3x_i2c_id; tbl->name[0]; tbl++) { for (tbl = aic3x_i2c_id; tbl->name[0]; tbl++) {
if (!strcmp(tbl->name, id->name)) if (!strcmp(tbl->name, id->name))
break; break;
} }
aic3x->model = tbl - aic3x_i2c_id; aic3x->model = tbl - aic3x_i2c_id;
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i];
ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
goto err_get;
}
ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
goto err_enable;
}
if (aic3x->gpio_reset >= 0) {
udelay(1);
gpio_set_value(aic3x->gpio_reset, 1);
}
ret = snd_soc_register_codec(&i2c->dev, ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_aic3x, &aic3x_dai, 1); &soc_codec_dev_aic3x, &aic3x_dai, 1);
if (ret < 0) if (ret < 0)
goto err_enable;
return ret;
err_enable:
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
err_get:
if (aic3x->gpio_reset >= 0)
gpio_free(aic3x->gpio_reset);
err_gpio:
kfree(aic3x); kfree(aic3x);
return ret; return ret;
} }
static int aic3x_i2c_remove(struct i2c_client *client) static int aic3x_i2c_remove(struct i2c_client *client)
{ {
struct aic3x_priv *aic3x = i2c_get_clientdata(client);
if (aic3x->gpio_reset >= 0) {
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
snd_soc_unregister_codec(&client->dev); snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client)); kfree(i2c_get_clientdata(client));
return 0; return 0;
......
...@@ -193,6 +193,9 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, ...@@ -193,6 +193,9 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
if (wl1273->mode == ucontrol->value.integer.value[0])
return 0;
/* Do not allow changes while stream is running */ /* Do not allow changes while stream is running */
if (codec->active) if (codec->active)
return -EPERM; return -EPERM;
......
...@@ -759,7 +759,7 @@ static struct snd_soc_dai_driver omap_mcbsp_dai = ...@@ -759,7 +759,7 @@ static struct snd_soc_dai_driver omap_mcbsp_dai =
.ops = &mcbsp_dai_ops, .ops = &mcbsp_dai_ops,
}; };
int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
struct soc_mixer_control *mc = struct soc_mixer_control *mc =
......
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