|
@@ -61,15 +61,25 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
|
|
|
"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 */
|
|
|
struct aic3x_priv {
|
|
|
+ struct snd_soc_codec *codec;
|
|
|
struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
|
|
|
+ struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
|
|
|
enum snd_soc_control_type control_type;
|
|
|
struct aic3x_setup_data *setup;
|
|
|
void *control_data;
|
|
|
unsigned int sysclk;
|
|
|
int master;
|
|
|
int gpio_reset;
|
|
|
+ int power;
|
|
|
#define AIC3X_MODEL_3X 0
|
|
|
#define AIC3X_MODEL_33 1
|
|
|
#define AIC3X_MODEL_3007 2
|
|
@@ -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,
|
|
|
- unsigned int reg)
|
|
|
+static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
|
|
|
+ u8 *value)
|
|
|
{
|
|
|
u8 *cache = codec->reg_cache;
|
|
|
- if (reg >= AIC3X_CACHEREGNUM)
|
|
|
- return -1;
|
|
|
- return cache[reg];
|
|
|
-}
|
|
|
|
|
|
-/*
|
|
|
- * write aic3x register cache
|
|
|
- */
|
|
|
-static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec,
|
|
|
- u8 reg, u8 value)
|
|
|
-{
|
|
|
- u8 *cache = codec->reg_cache;
|
|
|
+ if (codec->cache_only)
|
|
|
+ return -EINVAL;
|
|
|
if (reg >= AIC3X_CACHEREGNUM)
|
|
|
- return;
|
|
|
- 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;
|
|
|
+ return -1;
|
|
|
|
|
|
- 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;
|
|
|
}
|
|
|
|
|
@@ -646,6 +617,14 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
|
|
|
SND_SOC_DAPM_INPUT("LINE1R"),
|
|
|
SND_SOC_DAPM_INPUT("LINE2L"),
|
|
|
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[] = {
|
|
@@ -839,8 +818,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
|
|
int clk;
|
|
|
|
|
|
/* select data word length */
|
|
|
- data =
|
|
|
- aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
|
|
|
+ data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
|
|
|
switch (params_format(params)) {
|
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
|
break;
|
|
@@ -854,7 +832,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
|
|
data |= (0x03 << 4);
|
|
|
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 = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
|
|
@@ -869,17 +847,17 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
|
|
if (bypass_pll) {
|
|
|
pll_q &= 0xf;
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
|
|
|
- aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
|
|
|
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
|
|
|
+ snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
|
|
|
/* disable PLL if it is bypassed */
|
|
|
- reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE);
|
|
|
+ reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
|
|
|
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE);
|
|
|
|
|
|
} 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 */
|
|
|
- reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE);
|
|
|
+ reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
|
|
|
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE);
|
|
|
}
|
|
|
|
|
|
/* Route Left DAC to left channel input and
|
|
@@ -888,7 +866,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
|
|
data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
|
|
|
if (params_rate(params) >= 64000)
|
|
|
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 */
|
|
|
data = (fsref * 20) / params_rate(params);
|
|
@@ -897,7 +875,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
|
|
data /= 5;
|
|
|
data -= 2;
|
|
|
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)
|
|
|
return 0;
|
|
@@ -966,13 +944,16 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
|
|
}
|
|
|
|
|
|
found:
|
|
|
- data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
|
|
|
- aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGD_REG,
|
|
|
- (pll_d & 0x3F) << PLLD_LSB_SHIFT);
|
|
|
+ data = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
|
|
|
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
|
|
|
+ data | (pll_p << PLLP_SHIFT));
|
|
|
+ snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,
|
|
|
+ pll_r << PLLR_SHIFT);
|
|
|
+ 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);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -980,15 +961,15 @@ found:
|
|
|
static int aic3x_mute(struct snd_soc_dai *dai, int mute)
|
|
|
{
|
|
|
struct snd_soc_codec *codec = dai->codec;
|
|
|
- u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
|
|
|
- u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON;
|
|
|
+ u8 ldac_reg = snd_soc_read(codec, LDAC_VOL) & ~MUTE_ON;
|
|
|
+ u8 rdac_reg = snd_soc_read(codec, RDAC_VOL) & ~MUTE_ON;
|
|
|
|
|
|
if (mute) {
|
|
|
- aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
|
|
|
- aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
|
|
|
+ snd_soc_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
|
|
|
+ snd_soc_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
|
|
|
} else {
|
|
|
- aic3x_write(codec, LDAC_VOL, ldac_reg);
|
|
|
- aic3x_write(codec, RDAC_VOL, rdac_reg);
|
|
|
+ snd_soc_write(codec, LDAC_VOL, ldac_reg);
|
|
|
+ snd_soc_write(codec, RDAC_VOL, rdac_reg);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -1012,8 +993,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|
|
u8 iface_areg, iface_breg;
|
|
|
int delay = 0;
|
|
|
|
|
|
- iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
|
|
|
- iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
|
|
|
+ iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
|
|
|
+ iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
|
|
|
|
|
|
/* set master/slave audio interface */
|
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
@@ -1052,13 +1033,98 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|
|
}
|
|
|
|
|
|
/* set iface */
|
|
|
- aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
|
|
|
- aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
|
|
|
- aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
|
|
|
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
|
|
|
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
+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,
|
|
|
enum snd_soc_bias_level level)
|
|
|
{
|
|
@@ -1069,23 +1135,29 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
|
|
|
case SND_SOC_BIAS_ON:
|
|
|
break;
|
|
|
case SND_SOC_BIAS_PREPARE:
|
|
|
- if (aic3x->master) {
|
|
|
+ if (codec->bias_level == SND_SOC_BIAS_STANDBY &&
|
|
|
+ aic3x->master) {
|
|
|
/* enable pll */
|
|
|
- reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGA_REG,
|
|
|
- reg | PLL_ENABLE);
|
|
|
+ reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
|
|
|
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
|
|
|
+ reg | PLL_ENABLE);
|
|
|
}
|
|
|
break;
|
|
|
case SND_SOC_BIAS_STANDBY:
|
|
|
- /* fall through and disable pll */
|
|
|
- case SND_SOC_BIAS_OFF:
|
|
|
- if (aic3x->master) {
|
|
|
+ if (!aic3x->power)
|
|
|
+ aic3x_set_power(codec, 1);
|
|
|
+ if (codec->bias_level == SND_SOC_BIAS_PREPARE &&
|
|
|
+ aic3x->master) {
|
|
|
/* disable pll */
|
|
|
- reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
|
|
|
- aic3x_write(codec, AIC3X_PLL_PROGA_REG,
|
|
|
- reg & ~PLL_ENABLE);
|
|
|
+ reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
|
|
|
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
|
|
|
+ reg & ~PLL_ENABLE);
|
|
|
}
|
|
|
break;
|
|
|
+ case SND_SOC_BIAS_OFF:
|
|
|
+ if (aic3x->power)
|
|
|
+ aic3x_set_power(codec, 0);
|
|
|
+ break;
|
|
|
}
|
|
|
codec->bias_level = level;
|
|
|
|
|
@@ -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 bit = gpio ? 3: 0;
|
|
|
- u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
|
|
|
- aic3x_write(codec, reg, val | (!!state << bit));
|
|
|
+ u8 val = snd_soc_read(codec, reg) & ~(1 << bit);
|
|
|
+ snd_soc_write(codec, reg, val | (!!state << bit));
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(aic3x_set_gpio);
|
|
|
|
|
@@ -1126,7 +1198,7 @@ void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
|
|
|
if (detect & AIC3X_HEADSET_DETECT_MASK)
|
|
|
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);
|
|
|
|
|
@@ -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)
|
|
|
{
|
|
|
- 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);
|
|
|
|
|
|
return 0;
|
|
@@ -1209,104 +1270,133 @@ static int aic3x_init(struct snd_soc_codec *codec)
|
|
|
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
|
|
|
int reg;
|
|
|
|
|
|
- aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
|
|
|
- aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
|
|
|
+ snd_soc_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
|
|
|
+ snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
|
|
|
|
|
|
/* DAC default volume and mute */
|
|
|
- aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
|
|
|
- aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
|
|
|
+ snd_soc_write(codec, LDAC_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 */
|
|
|
- aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
|
|
|
- aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
|
|
|
- aic3x_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, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
|
|
|
+ snd_soc_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
|
|
|
+ snd_soc_write(codec, DACL1_2_HPLCOM_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 */
|
|
|
- aic3x_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, DACL1_2_LLOPM_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 */
|
|
|
- aic3x_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, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
|
|
|
+ snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
|
|
|
|
|
|
/* unmute all outputs */
|
|
|
- reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
|
|
|
- aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE);
|
|
|
- reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
|
|
|
- aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE);
|
|
|
- reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
|
|
|
- aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
|
|
|
- reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
|
|
|
- aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE);
|
|
|
- reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
|
|
|
- aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE);
|
|
|
- reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
|
|
|
- aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE);
|
|
|
- reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
|
|
|
- aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, LLOPM_CTRL);
|
|
|
+ snd_soc_write(codec, LLOPM_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, RLOPM_CTRL);
|
|
|
+ snd_soc_write(codec, RLOPM_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, MONOLOPM_CTRL);
|
|
|
+ snd_soc_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, HPLOUT_CTRL);
|
|
|
+ snd_soc_write(codec, HPLOUT_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, HPROUT_CTRL);
|
|
|
+ snd_soc_write(codec, HPROUT_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, HPLCOM_CTRL);
|
|
|
+ snd_soc_write(codec, HPLCOM_CTRL, reg | UNMUTE);
|
|
|
+ reg = snd_soc_read(codec, HPRCOM_CTRL);
|
|
|
+ snd_soc_write(codec, HPRCOM_CTRL, reg | UNMUTE);
|
|
|
|
|
|
/* ADC default volume and unmute */
|
|
|
- aic3x_write(codec, LADC_VOL, DEFAULT_GAIN);
|
|
|
- aic3x_write(codec, RADC_VOL, DEFAULT_GAIN);
|
|
|
+ snd_soc_write(codec, LADC_VOL, DEFAULT_GAIN);
|
|
|
+ snd_soc_write(codec, RADC_VOL, DEFAULT_GAIN);
|
|
|
/* By default route Line1 to ADC PGA mixer */
|
|
|
- aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0);
|
|
|
- aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0);
|
|
|
+ snd_soc_write(codec, LINE1L_2_LADC_CTRL, 0x0);
|
|
|
+ snd_soc_write(codec, LINE1R_2_RADC_CTRL, 0x0);
|
|
|
|
|
|
/* PGA to HP Bypass default volume, disconnect from Output Mixer */
|
|
|
- aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
|
|
|
/* PGA to Line Out default volume, disconnect from Output Mixer */
|
|
|
- aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
|
|
|
/* PGA to Mono Line Out default volume, disconnect from Output Mixer */
|
|
|
- aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, PGAL_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 */
|
|
|
- aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
|
|
|
/* Line2 Line Out default volume, disconnect from Output Mixer */
|
|
|
- aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
|
|
|
/* Line2 to Mono Out default volume, disconnect from Output Mixer */
|
|
|
- aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
|
|
|
- aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
|
|
|
+ snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
|
|
|
|
|
|
if (aic3x->model == AIC3X_MODEL_3007) {
|
|
|
- /* Class-D speaker driver init; datasheet p. 46 */
|
|
|
- aic3x_write(codec, AIC3X_PAGE_SELECT, 0x0D);
|
|
|
- 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);
|
|
|
+ aic3x_init_3007(codec);
|
|
|
+ snd_soc_write(codec, CLASSD_CTRL, 0);
|
|
|
}
|
|
|
|
|
|
- /* off, with power on */
|
|
|
- aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int aic3x_probe(struct snd_soc_codec *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;
|
|
|
+ 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);
|
|
|
|
|
|
if (aic3x->setup) {
|
|
|
/* setup GPIO functions */
|
|
|
- aic3x_write(codec, AIC3X_GPIO1_REG,
|
|
|
- (aic3x->setup->gpio_func[0] & 0xf) << 4);
|
|
|
- aic3x_write(codec, AIC3X_GPIO2_REG,
|
|
|
- (aic3x->setup->gpio_func[1] & 0xf) << 4);
|
|
|
+ snd_soc_write(codec, AIC3X_GPIO1_REG,
|
|
|
+ (aic3x->setup->gpio_func[0] & 0xf) << 4);
|
|
|
+ snd_soc_write(codec, AIC3X_GPIO2_REG,
|
|
|
+ (aic3x->setup->gpio_func[1] & 0xf) << 4);
|
|
|
}
|
|
|
|
|
|
snd_soc_add_controls(codec, aic3x_snd_controls,
|
|
@@ -1317,17 +1407,39 @@ static int aic3x_probe(struct snd_soc_codec *codec)
|
|
|
aic3x_add_widgets(codec);
|
|
|
|
|
|
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)
|
|
|
{
|
|
|
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
|
|
|
+ int i;
|
|
|
+
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
.reg_cache_size = ARRAY_SIZE(aic3x_reg),
|
|
|
.reg_word_size = sizeof(u8),
|
|
@@ -1361,7 +1473,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
|
|
|
{
|
|
|
struct aic3x_pdata *pdata = i2c->dev.platform_data;
|
|
|
struct aic3x_priv *aic3x;
|
|
|
- int ret, i;
|
|
|
+ int ret;
|
|
|
const struct i2c_device_id *tbl;
|
|
|
|
|
|
aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
|
|
@@ -1371,6 +1483,8 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
|
|
|
}
|
|
|
|
|
|
aic3x->control_data = i2c;
|
|
|
+ aic3x->control_type = SND_SOC_I2C;
|
|
|
+
|
|
|
i2c_set_clientdata(i2c, aic3x);
|
|
|
if (pdata) {
|
|
|
aic3x->gpio_reset = pdata->gpio_reset;
|
|
@@ -1379,68 +1493,21 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
|
|
|
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++) {
|
|
|
if (!strcmp(tbl->name, id->name))
|
|
|
break;
|
|
|
}
|
|
|
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,
|
|
|
&soc_codec_dev_aic3x, &aic3x_dai, 1);
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
kfree(i2c_get_clientdata(client));
|
|
|
return 0;
|