mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-24 02:49:05 +07:00
0bb423f2ea
Regulator notifiers, that were registered during codec driver probing, must be unregistered during driver release, or device managed versions have to be used. This patch fixes codec drivers, that weren't explicitly unregistering notifiers and simplifies those, that did that manually. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
1573 lines
46 KiB
C
1573 lines
46 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* ALSA SoC TLV320AIC31xx CODEC Driver
|
|
*
|
|
* Copyright (C) 2014-2017 Texas Instruments Incorporated - http://www.ti.com/
|
|
* Jyri Sarha <jsarha@ti.com>
|
|
*
|
|
* Based on ground work by: Ajit Kulkarni <x0175765@ti.com>
|
|
*
|
|
* The TLV320AIC31xx series of audio codecs are low-power, highly integrated
|
|
* high performance codecs which provides a stereo DAC, a mono ADC,
|
|
* and mono/stereo Class-D speaker driver.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/init.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/slab.h>
|
|
#include <sound/core.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/initval.h>
|
|
#include <sound/tlv.h>
|
|
#include <dt-bindings/sound/tlv320aic31xx-micbias.h>
|
|
|
|
#include "tlv320aic31xx.h"
|
|
|
|
static const struct reg_default aic31xx_reg_defaults[] = {
|
|
{ AIC31XX_CLKMUX, 0x00 },
|
|
{ AIC31XX_PLLPR, 0x11 },
|
|
{ AIC31XX_PLLJ, 0x04 },
|
|
{ AIC31XX_PLLDMSB, 0x00 },
|
|
{ AIC31XX_PLLDLSB, 0x00 },
|
|
{ AIC31XX_NDAC, 0x01 },
|
|
{ AIC31XX_MDAC, 0x01 },
|
|
{ AIC31XX_DOSRMSB, 0x00 },
|
|
{ AIC31XX_DOSRLSB, 0x80 },
|
|
{ AIC31XX_NADC, 0x01 },
|
|
{ AIC31XX_MADC, 0x01 },
|
|
{ AIC31XX_AOSR, 0x80 },
|
|
{ AIC31XX_IFACE1, 0x00 },
|
|
{ AIC31XX_DATA_OFFSET, 0x00 },
|
|
{ AIC31XX_IFACE2, 0x00 },
|
|
{ AIC31XX_BCLKN, 0x01 },
|
|
{ AIC31XX_DACSETUP, 0x14 },
|
|
{ AIC31XX_DACMUTE, 0x0c },
|
|
{ AIC31XX_LDACVOL, 0x00 },
|
|
{ AIC31XX_RDACVOL, 0x00 },
|
|
{ AIC31XX_ADCSETUP, 0x00 },
|
|
{ AIC31XX_ADCFGA, 0x80 },
|
|
{ AIC31XX_ADCVOL, 0x00 },
|
|
{ AIC31XX_HPDRIVER, 0x04 },
|
|
{ AIC31XX_SPKAMP, 0x06 },
|
|
{ AIC31XX_DACMIXERROUTE, 0x00 },
|
|
{ AIC31XX_LANALOGHPL, 0x7f },
|
|
{ AIC31XX_RANALOGHPR, 0x7f },
|
|
{ AIC31XX_LANALOGSPL, 0x7f },
|
|
{ AIC31XX_RANALOGSPR, 0x7f },
|
|
{ AIC31XX_HPLGAIN, 0x02 },
|
|
{ AIC31XX_HPRGAIN, 0x02 },
|
|
{ AIC31XX_SPLGAIN, 0x00 },
|
|
{ AIC31XX_SPRGAIN, 0x00 },
|
|
{ AIC31XX_MICBIAS, 0x00 },
|
|
{ AIC31XX_MICPGA, 0x80 },
|
|
{ AIC31XX_MICPGAPI, 0x00 },
|
|
{ AIC31XX_MICPGAMI, 0x00 },
|
|
};
|
|
|
|
static bool aic31xx_volatile(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case AIC31XX_PAGECTL: /* regmap implementation requires this */
|
|
case AIC31XX_RESET: /* always clears after write */
|
|
case AIC31XX_OT_FLAG:
|
|
case AIC31XX_ADCFLAG:
|
|
case AIC31XX_DACFLAG1:
|
|
case AIC31XX_DACFLAG2:
|
|
case AIC31XX_OFFLAG: /* Sticky interrupt flags */
|
|
case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */
|
|
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
|
|
case AIC31XX_INTRDACFLAG2:
|
|
case AIC31XX_INTRADCFLAG2:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool aic31xx_writeable(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case AIC31XX_OT_FLAG:
|
|
case AIC31XX_ADCFLAG:
|
|
case AIC31XX_DACFLAG1:
|
|
case AIC31XX_DACFLAG2:
|
|
case AIC31XX_OFFLAG: /* Sticky interrupt flags */
|
|
case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */
|
|
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
|
|
case AIC31XX_INTRDACFLAG2:
|
|
case AIC31XX_INTRADCFLAG2:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static const struct regmap_range_cfg aic31xx_ranges[] = {
|
|
{
|
|
.range_min = 0,
|
|
.range_max = 12 * 128,
|
|
.selector_reg = AIC31XX_PAGECTL,
|
|
.selector_mask = 0xff,
|
|
.selector_shift = 0,
|
|
.window_start = 0,
|
|
.window_len = 128,
|
|
},
|
|
};
|
|
|
|
static const struct regmap_config aic31xx_i2c_regmap = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
.writeable_reg = aic31xx_writeable,
|
|
.volatile_reg = aic31xx_volatile,
|
|
.reg_defaults = aic31xx_reg_defaults,
|
|
.num_reg_defaults = ARRAY_SIZE(aic31xx_reg_defaults),
|
|
.cache_type = REGCACHE_RBTREE,
|
|
.ranges = aic31xx_ranges,
|
|
.num_ranges = ARRAY_SIZE(aic31xx_ranges),
|
|
.max_register = 12 * 128,
|
|
};
|
|
|
|
static const char * const aic31xx_supply_names[] = {
|
|
"HPVDD",
|
|
"SPRVDD",
|
|
"SPLVDD",
|
|
"AVDD",
|
|
"IOVDD",
|
|
"DVDD",
|
|
};
|
|
|
|
#define AIC31XX_NUM_SUPPLIES ARRAY_SIZE(aic31xx_supply_names)
|
|
|
|
struct aic31xx_disable_nb {
|
|
struct notifier_block nb;
|
|
struct aic31xx_priv *aic31xx;
|
|
};
|
|
|
|
struct aic31xx_priv {
|
|
struct snd_soc_component *component;
|
|
u8 i2c_regs_status;
|
|
struct device *dev;
|
|
struct regmap *regmap;
|
|
enum aic31xx_type codec_type;
|
|
struct gpio_desc *gpio_reset;
|
|
int micbias_vg;
|
|
struct aic31xx_pdata pdata;
|
|
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
|
|
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
|
|
unsigned int sysclk;
|
|
u8 p_div;
|
|
int rate_div_line;
|
|
bool master_dapm_route_applied;
|
|
int irq;
|
|
};
|
|
|
|
struct aic31xx_rate_divs {
|
|
u32 mclk_p;
|
|
u32 rate;
|
|
u8 pll_j;
|
|
u16 pll_d;
|
|
u16 dosr;
|
|
u8 ndac;
|
|
u8 mdac;
|
|
u8 aosr;
|
|
u8 nadc;
|
|
u8 madc;
|
|
};
|
|
|
|
/* ADC dividers can be disabled by configuring them to 0 */
|
|
static const struct aic31xx_rate_divs aic31xx_divs[] = {
|
|
/* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
|
|
/* 8k rate */
|
|
{12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2},
|
|
{12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3},
|
|
{12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2},
|
|
/* 11.025k rate */
|
|
{12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2},
|
|
{12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3},
|
|
{12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2},
|
|
/* 16k rate */
|
|
{12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2},
|
|
{12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3},
|
|
{12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2},
|
|
/* 22.05k rate */
|
|
{12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2},
|
|
{12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3},
|
|
{12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2},
|
|
/* 32k rate */
|
|
{12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2},
|
|
{12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3},
|
|
{12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2},
|
|
/* 44.1k rate */
|
|
{12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2},
|
|
{12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3},
|
|
{12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2},
|
|
/* 48k rate */
|
|
{12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2},
|
|
{12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4},
|
|
{12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2},
|
|
/* 88.2k rate */
|
|
{12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2},
|
|
{12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3},
|
|
{12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2},
|
|
/* 96k rate */
|
|
{12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2},
|
|
{12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4},
|
|
{12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2},
|
|
/* 176.4k rate */
|
|
{12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2},
|
|
{12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3},
|
|
{12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2},
|
|
/* 192k rate */
|
|
{12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2},
|
|
{12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4},
|
|
{12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2},
|
|
};
|
|
|
|
static const char * const ldac_in_text[] = {
|
|
"Off", "Left Data", "Right Data", "Mono"
|
|
};
|
|
|
|
static const char * const rdac_in_text[] = {
|
|
"Off", "Right Data", "Left Data", "Mono"
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_DECL(ldac_in_enum, AIC31XX_DACSETUP, 4, ldac_in_text);
|
|
|
|
static SOC_ENUM_SINGLE_DECL(rdac_in_enum, AIC31XX_DACSETUP, 2, rdac_in_text);
|
|
|
|
static const char * const mic_select_text[] = {
|
|
"Off", "FFR 10 Ohm", "FFR 20 Ohm", "FFR 40 Ohm"
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_DECL(mic1lp_p_enum, AIC31XX_MICPGAPI, 6,
|
|
mic_select_text);
|
|
static SOC_ENUM_SINGLE_DECL(mic1rp_p_enum, AIC31XX_MICPGAPI, 4,
|
|
mic_select_text);
|
|
static SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2,
|
|
mic_select_text);
|
|
|
|
static SOC_ENUM_SINGLE_DECL(cm_m_enum, AIC31XX_MICPGAMI, 6, mic_select_text);
|
|
static SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4,
|
|
mic_select_text);
|
|
|
|
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
|
|
static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0);
|
|
static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0);
|
|
static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, 0, 50, 0);
|
|
static const DECLARE_TLV_DB_SCALE(hp_drv_tlv, 0, 100, 0);
|
|
static const DECLARE_TLV_DB_SCALE(class_D_drv_tlv, 600, 600, 0);
|
|
static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -6350, 50, 0);
|
|
static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0);
|
|
|
|
/*
|
|
* controls to be exported to the user space
|
|
*/
|
|
static const struct snd_kcontrol_new common31xx_snd_controls[] = {
|
|
SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
|
|
AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
|
|
|
|
SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
|
|
AIC31XX_HPRGAIN, 2, 1, 0),
|
|
SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
|
|
AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
|
|
|
|
SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
|
|
AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
|
|
SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
|
|
adc_fgain_tlv),
|
|
|
|
SOC_SINGLE("ADC Capture Switch", AIC31XX_ADCFGA, 7, 1, 1),
|
|
SOC_DOUBLE_R_S_TLV("ADC Capture Volume", AIC31XX_ADCVOL, AIC31XX_ADCVOL,
|
|
0, -24, 40, 6, 0, adc_cgain_tlv),
|
|
|
|
SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
|
|
119, 0, mic_pga_tlv),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new aic311x_snd_controls[] = {
|
|
SOC_DOUBLE_R("Speaker Driver Playback Switch", AIC31XX_SPLGAIN,
|
|
AIC31XX_SPRGAIN, 2, 1, 0),
|
|
SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN,
|
|
AIC31XX_SPRGAIN, 3, 3, 0, class_D_drv_tlv),
|
|
|
|
SOC_DOUBLE_R_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL,
|
|
AIC31XX_RANALOGSPR, 0, 0x7F, 1, sp_vol_tlv),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new aic310x_snd_controls[] = {
|
|
SOC_SINGLE("Speaker Driver Playback Switch", AIC31XX_SPLGAIN,
|
|
2, 1, 0),
|
|
SOC_SINGLE_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN,
|
|
3, 3, 0, class_D_drv_tlv),
|
|
|
|
SOC_SINGLE_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL,
|
|
0, 0x7F, 1, sp_vol_tlv),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new ldac_in_control =
|
|
SOC_DAPM_ENUM("DAC Left Input", ldac_in_enum);
|
|
|
|
static const struct snd_kcontrol_new rdac_in_control =
|
|
SOC_DAPM_ENUM("DAC Right Input", rdac_in_enum);
|
|
|
|
static int aic31xx_wait_bits(struct aic31xx_priv *aic31xx, unsigned int reg,
|
|
unsigned int mask, unsigned int wbits, int sleep,
|
|
int count)
|
|
{
|
|
unsigned int bits;
|
|
int counter = count;
|
|
int ret = regmap_read(aic31xx->regmap, reg, &bits);
|
|
|
|
while ((bits & mask) != wbits && counter && !ret) {
|
|
usleep_range(sleep, sleep * 2);
|
|
ret = regmap_read(aic31xx->regmap, reg, &bits);
|
|
counter--;
|
|
}
|
|
if ((bits & mask) != wbits) {
|
|
dev_err(aic31xx->dev,
|
|
"%s: Failed! 0x%x was 0x%x expected 0x%x (%d, 0x%x, %d us)\n",
|
|
__func__, reg, bits, wbits, ret, mask,
|
|
(count - counter) * sleep);
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#define WIDGET_BIT(reg, shift) (((shift) << 8) | (reg))
|
|
|
|
static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
unsigned int reg = AIC31XX_DACFLAG1;
|
|
unsigned int mask;
|
|
|
|
switch (WIDGET_BIT(w->reg, w->shift)) {
|
|
case WIDGET_BIT(AIC31XX_DACSETUP, 7):
|
|
mask = AIC31XX_LDACPWRSTATUS_MASK;
|
|
break;
|
|
case WIDGET_BIT(AIC31XX_DACSETUP, 6):
|
|
mask = AIC31XX_RDACPWRSTATUS_MASK;
|
|
break;
|
|
case WIDGET_BIT(AIC31XX_HPDRIVER, 7):
|
|
mask = AIC31XX_HPLDRVPWRSTATUS_MASK;
|
|
break;
|
|
case WIDGET_BIT(AIC31XX_HPDRIVER, 6):
|
|
mask = AIC31XX_HPRDRVPWRSTATUS_MASK;
|
|
break;
|
|
case WIDGET_BIT(AIC31XX_SPKAMP, 7):
|
|
mask = AIC31XX_SPLDRVPWRSTATUS_MASK;
|
|
break;
|
|
case WIDGET_BIT(AIC31XX_SPKAMP, 6):
|
|
mask = AIC31XX_SPRDRVPWRSTATUS_MASK;
|
|
break;
|
|
case WIDGET_BIT(AIC31XX_ADCSETUP, 7):
|
|
mask = AIC31XX_ADCPWRSTATUS_MASK;
|
|
reg = AIC31XX_ADCFLAG;
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "Unknown widget '%s' calling %s\n",
|
|
w->name, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_POST_PMU:
|
|
return aic31xx_wait_bits(aic31xx, reg, mask, mask, 5000, 100);
|
|
case SND_SOC_DAPM_POST_PMD:
|
|
return aic31xx_wait_bits(aic31xx, reg, mask, 0, 5000, 100);
|
|
default:
|
|
dev_dbg(component->dev,
|
|
"Unhandled dapm widget event %d from %s\n",
|
|
event, w->name);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
|
|
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
|
|
SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
|
|
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
|
|
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
|
|
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
|
|
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
|
|
SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
|
|
SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
|
|
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
|
|
SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new p_term_mic1lp =
|
|
SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
|
|
|
|
static const struct snd_kcontrol_new p_term_mic1rp =
|
|
SOC_DAPM_ENUM("MIC1RP P-Terminal", mic1rp_p_enum);
|
|
|
|
static const struct snd_kcontrol_new p_term_mic1lm =
|
|
SOC_DAPM_ENUM("MIC1LM P-Terminal", mic1lm_p_enum);
|
|
|
|
static const struct snd_kcontrol_new m_term_mic1lm =
|
|
SOC_DAPM_ENUM("MIC1LM M-Terminal", mic1lm_m_enum);
|
|
|
|
static const struct snd_kcontrol_new aic31xx_dapm_hpl_switch =
|
|
SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGHPL, 7, 1, 0);
|
|
|
|
static const struct snd_kcontrol_new aic31xx_dapm_hpr_switch =
|
|
SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGHPR, 7, 1, 0);
|
|
|
|
static const struct snd_kcontrol_new aic31xx_dapm_spl_switch =
|
|
SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGSPL, 7, 1, 0);
|
|
|
|
static const struct snd_kcontrol_new aic31xx_dapm_spr_switch =
|
|
SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGSPR, 7, 1, 0);
|
|
|
|
static int mic_bias_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_POST_PMU:
|
|
/* change mic bias voltage to user defined */
|
|
snd_soc_component_update_bits(component, AIC31XX_MICBIAS,
|
|
AIC31XX_MICBIAS_MASK,
|
|
aic31xx->micbias_vg <<
|
|
AIC31XX_MICBIAS_SHIFT);
|
|
dev_dbg(component->dev, "%s: turned on\n", __func__);
|
|
break;
|
|
case SND_SOC_DAPM_PRE_PMD:
|
|
/* turn mic bias off */
|
|
snd_soc_component_update_bits(component, AIC31XX_MICBIAS,
|
|
AIC31XX_MICBIAS_MASK, 0);
|
|
dev_dbg(component->dev, "%s: turned off\n", __func__);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
|
|
SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
|
|
|
|
SND_SOC_DAPM_MUX("DAC Left Input",
|
|
SND_SOC_NOPM, 0, 0, &ldac_in_control),
|
|
SND_SOC_DAPM_MUX("DAC Right Input",
|
|
SND_SOC_NOPM, 0, 0, &rdac_in_control),
|
|
/* DACs */
|
|
SND_SOC_DAPM_DAC_E("DAC Left", "Left Playback",
|
|
AIC31XX_DACSETUP, 7, 0, aic31xx_dapm_power_event,
|
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_DAC_E("DAC Right", "Right Playback",
|
|
AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
|
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
/* HP */
|
|
SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
|
|
&aic31xx_dapm_hpl_switch),
|
|
SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
|
|
&aic31xx_dapm_hpr_switch),
|
|
|
|
/* Output drivers */
|
|
SND_SOC_DAPM_OUT_DRV_E("HPL Driver", AIC31XX_HPDRIVER, 7, 0,
|
|
NULL, 0, aic31xx_dapm_power_event,
|
|
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
|
|
SND_SOC_DAPM_OUT_DRV_E("HPR Driver", AIC31XX_HPDRIVER, 6, 0,
|
|
NULL, 0, aic31xx_dapm_power_event,
|
|
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
|
|
|
|
/* Mic Bias */
|
|
SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
|
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
|
|
/* Keep BCLK/WCLK enabled even if DAC/ADC is powered down */
|
|
SND_SOC_DAPM_SUPPLY("Activate I2S clocks", AIC31XX_IFACE2, 2, 0,
|
|
NULL, 0),
|
|
|
|
/* Outputs */
|
|
SND_SOC_DAPM_OUTPUT("HPL"),
|
|
SND_SOC_DAPM_OUTPUT("HPR"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
|
|
/* Inputs */
|
|
SND_SOC_DAPM_INPUT("AIN1"),
|
|
SND_SOC_DAPM_INPUT("AIN2"),
|
|
|
|
/* Output Mixers */
|
|
SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
|
|
dac31xx_left_output_switches,
|
|
ARRAY_SIZE(dac31xx_left_output_switches)),
|
|
SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
|
|
dac31xx_right_output_switches,
|
|
ARRAY_SIZE(dac31xx_right_output_switches)),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
|
|
/* Inputs */
|
|
SND_SOC_DAPM_INPUT("MIC1LP"),
|
|
SND_SOC_DAPM_INPUT("MIC1RP"),
|
|
SND_SOC_DAPM_INPUT("MIC1LM"),
|
|
|
|
/* Input Selection to MIC_PGA */
|
|
SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
|
|
&p_term_mic1lp),
|
|
SND_SOC_DAPM_MUX("MIC1RP P-Terminal", SND_SOC_NOPM, 0, 0,
|
|
&p_term_mic1rp),
|
|
SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
|
|
&p_term_mic1lm),
|
|
|
|
/* ADC */
|
|
SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
|
|
aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
|
|
SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
|
|
&m_term_mic1lm),
|
|
|
|
/* Enabling & Disabling MIC Gain Ctl */
|
|
SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
|
|
7, 1, NULL, 0),
|
|
|
|
/* Output Mixers */
|
|
SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
|
|
aic31xx_left_output_switches,
|
|
ARRAY_SIZE(aic31xx_left_output_switches)),
|
|
SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
|
|
aic31xx_right_output_switches,
|
|
ARRAY_SIZE(aic31xx_right_output_switches)),
|
|
|
|
SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
|
|
/* AIC3111 and AIC3110 have stereo class-D amplifier */
|
|
SND_SOC_DAPM_OUT_DRV_E("SPL ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0,
|
|
aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
|
|
SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_OUT_DRV_E("SPR ClassD", AIC31XX_SPKAMP, 6, 0, NULL, 0,
|
|
aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
|
|
SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SWITCH("Speaker Left", SND_SOC_NOPM, 0, 0,
|
|
&aic31xx_dapm_spl_switch),
|
|
SND_SOC_DAPM_SWITCH("Speaker Right", SND_SOC_NOPM, 0, 0,
|
|
&aic31xx_dapm_spr_switch),
|
|
SND_SOC_DAPM_OUTPUT("SPL"),
|
|
SND_SOC_DAPM_OUTPUT("SPR"),
|
|
};
|
|
|
|
/* AIC3100 and AIC3120 have only mono class-D amplifier */
|
|
static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
|
|
SND_SOC_DAPM_OUT_DRV_E("SPK ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0,
|
|
aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
|
|
SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SWITCH("Speaker", SND_SOC_NOPM, 0, 0,
|
|
&aic31xx_dapm_spl_switch),
|
|
SND_SOC_DAPM_OUTPUT("SPK"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route
|
|
common31xx_audio_map[] = {
|
|
/* DAC Input Routing */
|
|
{"DAC Left Input", "Left Data", "AIF IN"},
|
|
{"DAC Left Input", "Right Data", "AIF IN"},
|
|
{"DAC Left Input", "Mono", "AIF IN"},
|
|
{"DAC Right Input", "Left Data", "AIF IN"},
|
|
{"DAC Right Input", "Right Data", "AIF IN"},
|
|
{"DAC Right Input", "Mono", "AIF IN"},
|
|
{"DAC Left", NULL, "DAC Left Input"},
|
|
{"DAC Right", NULL, "DAC Right Input"},
|
|
|
|
/* HPL path */
|
|
{"HP Left", "Switch", "Output Left"},
|
|
{"HPL Driver", NULL, "HP Left"},
|
|
{"HPL", NULL, "HPL Driver"},
|
|
|
|
/* HPR path */
|
|
{"HP Right", "Switch", "Output Right"},
|
|
{"HPR Driver", NULL, "HP Right"},
|
|
{"HPR", NULL, "HPR Driver"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route
|
|
dac31xx_audio_map[] = {
|
|
/* Left Output */
|
|
{"Output Left", "From Left DAC", "DAC Left"},
|
|
{"Output Left", "From AIN1", "AIN1"},
|
|
{"Output Left", "From AIN2", "AIN2"},
|
|
|
|
/* Right Output */
|
|
{"Output Right", "From Right DAC", "DAC Right"},
|
|
{"Output Right", "From AIN2", "AIN2"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route
|
|
aic31xx_audio_map[] = {
|
|
/* Mic input */
|
|
{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
|
|
{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
|
|
{"MIC1LP P-Terminal", "FFR 40 Ohm", "MIC1LP"},
|
|
{"MIC1RP P-Terminal", "FFR 10 Ohm", "MIC1RP"},
|
|
{"MIC1RP P-Terminal", "FFR 20 Ohm", "MIC1RP"},
|
|
{"MIC1RP P-Terminal", "FFR 40 Ohm", "MIC1RP"},
|
|
{"MIC1LM P-Terminal", "FFR 10 Ohm", "MIC1LM"},
|
|
{"MIC1LM P-Terminal", "FFR 20 Ohm", "MIC1LM"},
|
|
{"MIC1LM P-Terminal", "FFR 40 Ohm", "MIC1LM"},
|
|
|
|
{"MIC1LM M-Terminal", "FFR 10 Ohm", "MIC1LM"},
|
|
{"MIC1LM M-Terminal", "FFR 20 Ohm", "MIC1LM"},
|
|
{"MIC1LM M-Terminal", "FFR 40 Ohm", "MIC1LM"},
|
|
|
|
{"MIC_GAIN_CTL", NULL, "MIC1LP P-Terminal"},
|
|
{"MIC_GAIN_CTL", NULL, "MIC1RP P-Terminal"},
|
|
{"MIC_GAIN_CTL", NULL, "MIC1LM P-Terminal"},
|
|
{"MIC_GAIN_CTL", NULL, "MIC1LM M-Terminal"},
|
|
|
|
{"ADC", NULL, "MIC_GAIN_CTL"},
|
|
|
|
{"AIF OUT", NULL, "ADC"},
|
|
|
|
/* Left Output */
|
|
{"Output Left", "From Left DAC", "DAC Left"},
|
|
{"Output Left", "From MIC1LP", "MIC1LP"},
|
|
{"Output Left", "From MIC1RP", "MIC1RP"},
|
|
|
|
/* Right Output */
|
|
{"Output Right", "From Right DAC", "DAC Right"},
|
|
{"Output Right", "From MIC1RP", "MIC1RP"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route
|
|
aic311x_audio_map[] = {
|
|
/* SP L path */
|
|
{"Speaker Left", "Switch", "Output Left"},
|
|
{"SPL ClassD", NULL, "Speaker Left"},
|
|
{"SPL", NULL, "SPL ClassD"},
|
|
|
|
/* SP R path */
|
|
{"Speaker Right", "Switch", "Output Right"},
|
|
{"SPR ClassD", NULL, "Speaker Right"},
|
|
{"SPR", NULL, "SPR ClassD"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route
|
|
aic310x_audio_map[] = {
|
|
/* SP L path */
|
|
{"Speaker", "Switch", "Output Left"},
|
|
{"SPK ClassD", NULL, "Speaker"},
|
|
{"SPK", NULL, "SPK ClassD"},
|
|
};
|
|
|
|
/*
|
|
* Always connected DAPM routes for codec clock master modes.
|
|
* If the codec is the master on the I2S bus, we need to power up components
|
|
* to have valid DAC_CLK.
|
|
*
|
|
* In order to have the I2S clocks on the bus either the DACs/ADC need to be
|
|
* enabled, or the P0/R29/D2 (Keep bclk/wclk in power down) need to be set.
|
|
*
|
|
* Otherwise the codec will not generate clocks on the bus.
|
|
*/
|
|
static const struct snd_soc_dapm_route
|
|
common31xx_cm_audio_map[] = {
|
|
{"HPL", NULL, "AIF IN"},
|
|
{"HPR", NULL, "AIF IN"},
|
|
|
|
{"AIF IN", NULL, "Activate I2S clocks"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route
|
|
aic31xx_cm_audio_map[] = {
|
|
{"AIF OUT", NULL, "MIC1LP"},
|
|
{"AIF OUT", NULL, "MIC1RP"},
|
|
{"AIF OUT", NULL, "MIC1LM"},
|
|
|
|
{"AIF OUT", NULL, "Activate I2S clocks"},
|
|
};
|
|
|
|
static int aic31xx_add_controls(struct snd_soc_component *component)
|
|
{
|
|
int ret = 0;
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
|
|
if (!(aic31xx->codec_type & DAC31XX_BIT))
|
|
ret = snd_soc_add_component_controls(
|
|
component, aic31xx_snd_controls,
|
|
ARRAY_SIZE(aic31xx_snd_controls));
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT)
|
|
ret = snd_soc_add_component_controls(
|
|
component, aic311x_snd_controls,
|
|
ARRAY_SIZE(aic311x_snd_controls));
|
|
else
|
|
ret = snd_soc_add_component_controls(
|
|
component, aic310x_snd_controls,
|
|
ARRAY_SIZE(aic310x_snd_controls));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aic31xx_add_widgets(struct snd_soc_component *component)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
int ret = 0;
|
|
|
|
if (aic31xx->codec_type & DAC31XX_BIT) {
|
|
ret = snd_soc_dapm_new_controls(
|
|
dapm, dac31xx_dapm_widgets,
|
|
ARRAY_SIZE(dac31xx_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
|
|
ARRAY_SIZE(dac31xx_audio_map));
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
ret = snd_soc_dapm_new_controls(
|
|
dapm, aic31xx_dapm_widgets,
|
|
ARRAY_SIZE(aic31xx_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
|
|
ARRAY_SIZE(aic31xx_audio_map));
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
|
|
ret = snd_soc_dapm_new_controls(
|
|
dapm, aic311x_dapm_widgets,
|
|
ARRAY_SIZE(aic311x_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(dapm, aic311x_audio_map,
|
|
ARRAY_SIZE(aic311x_audio_map));
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
ret = snd_soc_dapm_new_controls(
|
|
dapm, aic310x_dapm_widgets,
|
|
ARRAY_SIZE(aic310x_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(dapm, aic310x_audio_map,
|
|
ARRAY_SIZE(aic310x_audio_map));
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_setup_pll(struct snd_soc_component *component,
|
|
struct snd_pcm_hw_params *params)
|
|
{
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
int bclk_score = snd_soc_params_to_frame_size(params);
|
|
int mclk_p;
|
|
int bclk_n = 0;
|
|
int match = -1;
|
|
int i;
|
|
|
|
if (!aic31xx->sysclk || !aic31xx->p_div) {
|
|
dev_err(component->dev, "Master clock not supplied\n");
|
|
return -EINVAL;
|
|
}
|
|
mclk_p = aic31xx->sysclk / aic31xx->p_div;
|
|
|
|
/* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */
|
|
snd_soc_component_update_bits(component, AIC31XX_CLKMUX,
|
|
AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL);
|
|
snd_soc_component_update_bits(component, AIC31XX_IFACE2,
|
|
AIC31XX_BDIVCLK_MASK, AIC31XX_DAC2BCLK);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) {
|
|
if (aic31xx_divs[i].rate == params_rate(params) &&
|
|
aic31xx_divs[i].mclk_p == mclk_p) {
|
|
int s = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) %
|
|
snd_soc_params_to_frame_size(params);
|
|
int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) /
|
|
snd_soc_params_to_frame_size(params);
|
|
if (s < bclk_score && bn > 0) {
|
|
match = i;
|
|
bclk_n = bn;
|
|
bclk_score = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (match == -1) {
|
|
dev_err(component->dev,
|
|
"%s: Sample rate (%u) and format not supported\n",
|
|
__func__, params_rate(params));
|
|
/* See bellow for details how fix this. */
|
|
return -EINVAL;
|
|
}
|
|
if (bclk_score != 0) {
|
|
dev_warn(component->dev, "Can not produce exact bitclock");
|
|
/* This is fine if using dsp format, but if using i2s
|
|
there may be trouble. To fix the issue edit the
|
|
aic31xx_divs table for your mclk and sample
|
|
rate. Details can be found from:
|
|
http://www.ti.com/lit/ds/symlink/tlv320aic3100.pdf
|
|
Section: 5.6 CLOCK Generation and PLL
|
|
*/
|
|
}
|
|
i = match;
|
|
|
|
/* PLL configuration */
|
|
snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
|
|
(aic31xx->p_div << 4) | 0x01);
|
|
snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
|
|
|
|
snd_soc_component_write(component, AIC31XX_PLLDMSB,
|
|
aic31xx_divs[i].pll_d >> 8);
|
|
snd_soc_component_write(component, AIC31XX_PLLDLSB,
|
|
aic31xx_divs[i].pll_d & 0xff);
|
|
|
|
/* DAC dividers configuration */
|
|
snd_soc_component_update_bits(component, AIC31XX_NDAC, AIC31XX_PLL_MASK,
|
|
aic31xx_divs[i].ndac);
|
|
snd_soc_component_update_bits(component, AIC31XX_MDAC, AIC31XX_PLL_MASK,
|
|
aic31xx_divs[i].mdac);
|
|
|
|
snd_soc_component_write(component, AIC31XX_DOSRMSB, aic31xx_divs[i].dosr >> 8);
|
|
snd_soc_component_write(component, AIC31XX_DOSRLSB, aic31xx_divs[i].dosr & 0xff);
|
|
|
|
/* ADC dividers configuration. Write reset value 1 if not used. */
|
|
snd_soc_component_update_bits(component, AIC31XX_NADC, AIC31XX_PLL_MASK,
|
|
aic31xx_divs[i].nadc ? aic31xx_divs[i].nadc : 1);
|
|
snd_soc_component_update_bits(component, AIC31XX_MADC, AIC31XX_PLL_MASK,
|
|
aic31xx_divs[i].madc ? aic31xx_divs[i].madc : 1);
|
|
|
|
snd_soc_component_write(component, AIC31XX_AOSR, aic31xx_divs[i].aosr);
|
|
|
|
/* Bit clock divider configuration. */
|
|
snd_soc_component_update_bits(component, AIC31XX_BCLKN,
|
|
AIC31XX_PLL_MASK, bclk_n);
|
|
|
|
aic31xx->rate_div_line = i;
|
|
|
|
dev_dbg(component->dev,
|
|
"pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
|
|
aic31xx_divs[i].pll_j,
|
|
aic31xx_divs[i].pll_d,
|
|
aic31xx->p_div,
|
|
aic31xx_divs[i].dosr,
|
|
aic31xx_divs[i].ndac,
|
|
aic31xx_divs[i].mdac,
|
|
aic31xx_divs[i].aosr,
|
|
aic31xx_divs[i].nadc,
|
|
aic31xx_divs[i].madc,
|
|
bclk_n
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct snd_soc_component *component = dai->component;
|
|
u8 data = 0;
|
|
|
|
dev_dbg(component->dev, "## %s: width %d rate %d\n",
|
|
__func__, params_width(params),
|
|
params_rate(params));
|
|
|
|
switch (params_width(params)) {
|
|
case 16:
|
|
break;
|
|
case 20:
|
|
data = (AIC31XX_WORD_LEN_20BITS <<
|
|
AIC31XX_IFACE1_DATALEN_SHIFT);
|
|
break;
|
|
case 24:
|
|
data = (AIC31XX_WORD_LEN_24BITS <<
|
|
AIC31XX_IFACE1_DATALEN_SHIFT);
|
|
break;
|
|
case 32:
|
|
data = (AIC31XX_WORD_LEN_32BITS <<
|
|
AIC31XX_IFACE1_DATALEN_SHIFT);
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "%s: Unsupported width %d\n",
|
|
__func__, params_width(params));
|
|
return -EINVAL;
|
|
}
|
|
|
|
snd_soc_component_update_bits(component, AIC31XX_IFACE1,
|
|
AIC31XX_IFACE1_DATALEN_MASK,
|
|
data);
|
|
|
|
return aic31xx_setup_pll(component, params);
|
|
}
|
|
|
|
static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute)
|
|
{
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
|
|
if (mute) {
|
|
snd_soc_component_update_bits(component, AIC31XX_DACMUTE,
|
|
AIC31XX_DACMUTE_MASK,
|
|
AIC31XX_DACMUTE_MASK);
|
|
} else {
|
|
snd_soc_component_update_bits(component, AIC31XX_DACMUTE,
|
|
AIC31XX_DACMUTE_MASK, 0x0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_clock_master_routes(struct snd_soc_component *component,
|
|
unsigned int fmt)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
int ret;
|
|
|
|
fmt &= SND_SOC_DAIFMT_MASTER_MASK;
|
|
if (fmt == SND_SOC_DAIFMT_CBS_CFS &&
|
|
aic31xx->master_dapm_route_applied) {
|
|
/*
|
|
* Remove the DAPM route(s) for codec clock master modes,
|
|
* if applied
|
|
*/
|
|
ret = snd_soc_dapm_del_routes(dapm, common31xx_cm_audio_map,
|
|
ARRAY_SIZE(common31xx_cm_audio_map));
|
|
if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
|
|
ret = snd_soc_dapm_del_routes(dapm,
|
|
aic31xx_cm_audio_map,
|
|
ARRAY_SIZE(aic31xx_cm_audio_map));
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
aic31xx->master_dapm_route_applied = false;
|
|
} else if (fmt != SND_SOC_DAIFMT_CBS_CFS &&
|
|
!aic31xx->master_dapm_route_applied) {
|
|
/*
|
|
* Add the needed DAPM route(s) for codec clock master modes,
|
|
* if it is not done already
|
|
*/
|
|
ret = snd_soc_dapm_add_routes(dapm, common31xx_cm_audio_map,
|
|
ARRAY_SIZE(common31xx_cm_audio_map));
|
|
if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
|
|
ret = snd_soc_dapm_add_routes(dapm,
|
|
aic31xx_cm_audio_map,
|
|
ARRAY_SIZE(aic31xx_cm_audio_map));
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
aic31xx->master_dapm_route_applied = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|
unsigned int fmt)
|
|
{
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
u8 iface_reg1 = 0;
|
|
u8 iface_reg2 = 0;
|
|
u8 dsp_a_val = 0;
|
|
|
|
dev_dbg(component->dev, "## %s: fmt = 0x%x\n", __func__, fmt);
|
|
|
|
/* set master/slave audio interface */
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBS_CFM:
|
|
iface_reg1 |= AIC31XX_WCLK_MASTER;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBM_CFS:
|
|
iface_reg1 |= AIC31XX_BCLK_MASTER;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "Invalid DAI master/slave interface\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* signal polarity */
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
break;
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
iface_reg2 |= AIC31XX_BCLKINV_MASK;
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "Invalid DAI clock signal polarity\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* interface format */
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_I2S:
|
|
break;
|
|
case SND_SOC_DAIFMT_DSP_A:
|
|
dsp_a_val = 0x1; /* fall through */
|
|
case SND_SOC_DAIFMT_DSP_B:
|
|
/*
|
|
* NOTE: This CODEC samples on the falling edge of BCLK in
|
|
* DSP mode, this is inverted compared to what most DAIs
|
|
* expect, so we invert for this mode
|
|
*/
|
|
iface_reg2 ^= AIC31XX_BCLKINV_MASK;
|
|
iface_reg1 |= (AIC31XX_DSP_MODE <<
|
|
AIC31XX_IFACE1_DATATYPE_SHIFT);
|
|
break;
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
iface_reg1 |= (AIC31XX_RIGHT_JUSTIFIED_MODE <<
|
|
AIC31XX_IFACE1_DATATYPE_SHIFT);
|
|
break;
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
iface_reg1 |= (AIC31XX_LEFT_JUSTIFIED_MODE <<
|
|
AIC31XX_IFACE1_DATATYPE_SHIFT);
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "Invalid DAI interface format\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
snd_soc_component_update_bits(component, AIC31XX_IFACE1,
|
|
AIC31XX_IFACE1_DATATYPE_MASK |
|
|
AIC31XX_IFACE1_MASTER_MASK,
|
|
iface_reg1);
|
|
snd_soc_component_update_bits(component, AIC31XX_DATA_OFFSET,
|
|
AIC31XX_DATA_OFFSET_MASK,
|
|
dsp_a_val);
|
|
snd_soc_component_update_bits(component, AIC31XX_IFACE2,
|
|
AIC31XX_BCLKINV_MASK,
|
|
iface_reg2);
|
|
|
|
return aic31xx_clock_master_routes(component, fmt);
|
|
}
|
|
|
|
static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|
int clk_id, unsigned int freq, int dir)
|
|
{
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
int i;
|
|
|
|
dev_dbg(component->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
|
|
__func__, clk_id, freq, dir);
|
|
|
|
for (i = 1; i < 8; i++)
|
|
if (freq / i <= 20000000)
|
|
break;
|
|
if (freq/i > 20000000) {
|
|
dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
|
|
__func__, freq);
|
|
return -EINVAL;
|
|
}
|
|
aic31xx->p_div = i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++)
|
|
if (aic31xx_divs[i].mclk_p == freq / aic31xx->p_div)
|
|
break;
|
|
if (i == ARRAY_SIZE(aic31xx_divs)) {
|
|
dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
|
|
__func__, freq);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set clock on MCLK, BCLK, or GPIO1 as PLL input */
|
|
snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK,
|
|
clk_id << AIC31XX_PLL_CLKIN_SHIFT);
|
|
|
|
aic31xx->sysclk = freq;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_regulator_event(struct notifier_block *nb,
|
|
unsigned long event, void *data)
|
|
{
|
|
struct aic31xx_disable_nb *disable_nb =
|
|
container_of(nb, struct aic31xx_disable_nb, nb);
|
|
struct aic31xx_priv *aic31xx = disable_nb->aic31xx;
|
|
|
|
if (event & REGULATOR_EVENT_DISABLE) {
|
|
/*
|
|
* Put codec to reset and as at least one of the
|
|
* supplies was disabled.
|
|
*/
|
|
if (aic31xx->gpio_reset)
|
|
gpiod_set_value(aic31xx->gpio_reset, 1);
|
|
|
|
regcache_mark_dirty(aic31xx->regmap);
|
|
dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_reset(struct aic31xx_priv *aic31xx)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (aic31xx->gpio_reset) {
|
|
gpiod_set_value(aic31xx->gpio_reset, 1);
|
|
ndelay(10); /* At least 10ns */
|
|
gpiod_set_value(aic31xx->gpio_reset, 0);
|
|
} else {
|
|
ret = regmap_write(aic31xx->regmap, AIC31XX_RESET, 1);
|
|
}
|
|
mdelay(1); /* At least 1ms */
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void aic31xx_clk_on(struct snd_soc_component *component)
|
|
{
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
u8 mask = AIC31XX_PM_MASK;
|
|
u8 on = AIC31XX_PM_MASK;
|
|
|
|
dev_dbg(component->dev, "codec clock -> on (rate %d)\n",
|
|
aic31xx_divs[aic31xx->rate_div_line].rate);
|
|
snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, on);
|
|
mdelay(10);
|
|
snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, on);
|
|
snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, on);
|
|
if (aic31xx_divs[aic31xx->rate_div_line].nadc)
|
|
snd_soc_component_update_bits(component, AIC31XX_NADC, mask, on);
|
|
if (aic31xx_divs[aic31xx->rate_div_line].madc)
|
|
snd_soc_component_update_bits(component, AIC31XX_MADC, mask, on);
|
|
snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, on);
|
|
}
|
|
|
|
static void aic31xx_clk_off(struct snd_soc_component *component)
|
|
{
|
|
u8 mask = AIC31XX_PM_MASK;
|
|
u8 off = 0;
|
|
|
|
dev_dbg(component->dev, "codec clock -> off\n");
|
|
snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, off);
|
|
snd_soc_component_update_bits(component, AIC31XX_MADC, mask, off);
|
|
snd_soc_component_update_bits(component, AIC31XX_NADC, mask, off);
|
|
snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, off);
|
|
snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, off);
|
|
snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, off);
|
|
}
|
|
|
|
static int aic31xx_power_on(struct snd_soc_component *component)
|
|
{
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
int ret;
|
|
|
|
ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies),
|
|
aic31xx->supplies);
|
|
if (ret)
|
|
return ret;
|
|
|
|
regcache_cache_only(aic31xx->regmap, false);
|
|
|
|
/* Reset device registers for a consistent power-on like state */
|
|
ret = aic31xx_reset(aic31xx);
|
|
if (ret < 0)
|
|
dev_err(aic31xx->dev, "Could not reset device: %d\n", ret);
|
|
|
|
ret = regcache_sync(aic31xx->regmap);
|
|
if (ret) {
|
|
dev_err(component->dev,
|
|
"Failed to restore cache: %d\n", ret);
|
|
regcache_cache_only(aic31xx->regmap, true);
|
|
regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
|
|
aic31xx->supplies);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aic31xx_power_off(struct snd_soc_component *component)
|
|
{
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
|
|
regcache_cache_only(aic31xx->regmap, true);
|
|
regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
|
|
aic31xx->supplies);
|
|
}
|
|
|
|
static int aic31xx_set_bias_level(struct snd_soc_component *component,
|
|
enum snd_soc_bias_level level)
|
|
{
|
|
dev_dbg(component->dev, "## %s: %d -> %d\n", __func__,
|
|
snd_soc_component_get_bias_level(component), level);
|
|
|
|
switch (level) {
|
|
case SND_SOC_BIAS_ON:
|
|
break;
|
|
case SND_SOC_BIAS_PREPARE:
|
|
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY)
|
|
aic31xx_clk_on(component);
|
|
break;
|
|
case SND_SOC_BIAS_STANDBY:
|
|
switch (snd_soc_component_get_bias_level(component)) {
|
|
case SND_SOC_BIAS_OFF:
|
|
aic31xx_power_on(component);
|
|
break;
|
|
case SND_SOC_BIAS_PREPARE:
|
|
aic31xx_clk_off(component);
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
break;
|
|
case SND_SOC_BIAS_OFF:
|
|
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY)
|
|
aic31xx_power_off(component);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic31xx_codec_probe(struct snd_soc_component *component)
|
|
{
|
|
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
|
int i, ret;
|
|
|
|
dev_dbg(aic31xx->dev, "## %s\n", __func__);
|
|
|
|
aic31xx->component = component;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
|
|
aic31xx->disable_nb[i].nb.notifier_call =
|
|
aic31xx_regulator_event;
|
|
aic31xx->disable_nb[i].aic31xx = aic31xx;
|
|
ret = devm_regulator_register_notifier(
|
|
aic31xx->supplies[i].consumer,
|
|
&aic31xx->disable_nb[i].nb);
|
|
if (ret) {
|
|
dev_err(component->dev,
|
|
"Failed to request regulator notifier: %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
regcache_cache_only(aic31xx->regmap, true);
|
|
regcache_mark_dirty(aic31xx->regmap);
|
|
|
|
ret = aic31xx_add_controls(component);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = aic31xx_add_widgets(component);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
|
|
.probe = aic31xx_codec_probe,
|
|
.set_bias_level = aic31xx_set_bias_level,
|
|
.controls = common31xx_snd_controls,
|
|
.num_controls = ARRAY_SIZE(common31xx_snd_controls),
|
|
.dapm_widgets = common31xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets),
|
|
.dapm_routes = common31xx_audio_map,
|
|
.num_dapm_routes = ARRAY_SIZE(common31xx_audio_map),
|
|
.suspend_bias_off = 1,
|
|
.idle_bias_on = 1,
|
|
.use_pmdown_time = 1,
|
|
.endianness = 1,
|
|
.non_legacy_dai_naming = 1,
|
|
};
|
|
|
|
static const struct snd_soc_dai_ops aic31xx_dai_ops = {
|
|
.hw_params = aic31xx_hw_params,
|
|
.set_sysclk = aic31xx_set_dai_sysclk,
|
|
.set_fmt = aic31xx_set_dai_fmt,
|
|
.digital_mute = aic31xx_dac_mute,
|
|
};
|
|
|
|
static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
|
|
{
|
|
.name = "tlv320dac31xx-hifi",
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = AIC31XX_RATES,
|
|
.formats = AIC31XX_FORMATS,
|
|
},
|
|
.ops = &aic31xx_dai_ops,
|
|
.symmetric_rates = 1,
|
|
}
|
|
};
|
|
|
|
static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
|
|
{
|
|
.name = "tlv320aic31xx-hifi",
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = AIC31XX_RATES,
|
|
.formats = AIC31XX_FORMATS,
|
|
},
|
|
.capture = {
|
|
.stream_name = "Capture",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = AIC31XX_RATES,
|
|
.formats = AIC31XX_FORMATS,
|
|
},
|
|
.ops = &aic31xx_dai_ops,
|
|
.symmetric_rates = 1,
|
|
}
|
|
};
|
|
|
|
#if defined(CONFIG_OF)
|
|
static const struct of_device_id tlv320aic31xx_of_match[] = {
|
|
{ .compatible = "ti,tlv320aic310x" },
|
|
{ .compatible = "ti,tlv320aic311x" },
|
|
{ .compatible = "ti,tlv320aic3100" },
|
|
{ .compatible = "ti,tlv320aic3110" },
|
|
{ .compatible = "ti,tlv320aic3120" },
|
|
{ .compatible = "ti,tlv320aic3111" },
|
|
{ .compatible = "ti,tlv320dac3100" },
|
|
{ .compatible = "ti,tlv320dac3101" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match);
|
|
#endif /* CONFIG_OF */
|
|
|
|
#ifdef CONFIG_ACPI
|
|
static const struct acpi_device_id aic31xx_acpi_match[] = {
|
|
{ "10TI3100", 0 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
|
|
#endif
|
|
|
|
static irqreturn_t aic31xx_irq(int irq, void *data)
|
|
{
|
|
struct aic31xx_priv *aic31xx = data;
|
|
struct device *dev = aic31xx->dev;
|
|
unsigned int value;
|
|
bool handled = false;
|
|
int ret;
|
|
|
|
ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
|
|
goto exit;
|
|
}
|
|
|
|
if (value)
|
|
handled = true;
|
|
else
|
|
goto read_overflow;
|
|
|
|
if (value & AIC31XX_HPLSCDETECT)
|
|
dev_err(dev, "Short circuit on Left output is detected\n");
|
|
if (value & AIC31XX_HPRSCDETECT)
|
|
dev_err(dev, "Short circuit on Right output is detected\n");
|
|
if (value & ~(AIC31XX_HPLSCDETECT |
|
|
AIC31XX_HPRSCDETECT))
|
|
dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
|
|
|
|
read_overflow:
|
|
ret = regmap_read(aic31xx->regmap, AIC31XX_OFFLAG, &value);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to read overflow flag: %d\n", ret);
|
|
goto exit;
|
|
}
|
|
|
|
if (value)
|
|
handled = true;
|
|
else
|
|
goto exit;
|
|
|
|
if (value & AIC31XX_DAC_OF_LEFT)
|
|
dev_warn(dev, "Left-channel DAC overflow has occurred\n");
|
|
if (value & AIC31XX_DAC_OF_RIGHT)
|
|
dev_warn(dev, "Right-channel DAC overflow has occurred\n");
|
|
if (value & AIC31XX_DAC_OF_SHIFTER)
|
|
dev_warn(dev, "DAC barrel shifter overflow has occurred\n");
|
|
if (value & AIC31XX_ADC_OF)
|
|
dev_warn(dev, "ADC overflow has occurred\n");
|
|
if (value & AIC31XX_ADC_OF_SHIFTER)
|
|
dev_warn(dev, "ADC barrel shifter overflow has occurred\n");
|
|
if (value & ~(AIC31XX_DAC_OF_LEFT |
|
|
AIC31XX_DAC_OF_RIGHT |
|
|
AIC31XX_DAC_OF_SHIFTER |
|
|
AIC31XX_ADC_OF |
|
|
AIC31XX_ADC_OF_SHIFTER))
|
|
dev_warn(dev, "Unknown overflow interrupt flags: 0x%08x\n", value);
|
|
|
|
exit:
|
|
if (handled)
|
|
return IRQ_HANDLED;
|
|
else
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
static int aic31xx_i2c_probe(struct i2c_client *i2c,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct aic31xx_priv *aic31xx;
|
|
unsigned int micbias_value = MICBIAS_2_0V;
|
|
int i, ret;
|
|
|
|
dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
|
|
id->name, (int)id->driver_data);
|
|
|
|
aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
|
|
if (!aic31xx)
|
|
return -ENOMEM;
|
|
|
|
aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap);
|
|
if (IS_ERR(aic31xx->regmap)) {
|
|
ret = PTR_ERR(aic31xx->regmap);
|
|
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
aic31xx->dev = &i2c->dev;
|
|
aic31xx->irq = i2c->irq;
|
|
|
|
aic31xx->codec_type = id->driver_data;
|
|
|
|
dev_set_drvdata(aic31xx->dev, aic31xx);
|
|
|
|
fwnode_property_read_u32(aic31xx->dev->fwnode, "ai31xx-micbias-vg",
|
|
&micbias_value);
|
|
switch (micbias_value) {
|
|
case MICBIAS_2_0V:
|
|
case MICBIAS_2_5V:
|
|
case MICBIAS_AVDDV:
|
|
aic31xx->micbias_vg = micbias_value;
|
|
break;
|
|
default:
|
|
dev_err(aic31xx->dev, "Bad ai31xx-micbias-vg value %d\n",
|
|
micbias_value);
|
|
aic31xx->micbias_vg = MICBIAS_2_0V;
|
|
}
|
|
|
|
if (dev_get_platdata(aic31xx->dev)) {
|
|
memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev), sizeof(aic31xx->pdata));
|
|
aic31xx->codec_type = aic31xx->pdata.codec_type;
|
|
aic31xx->micbias_vg = aic31xx->pdata.micbias_vg;
|
|
}
|
|
|
|
aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset",
|
|
GPIOD_OUT_LOW);
|
|
if (IS_ERR(aic31xx->gpio_reset)) {
|
|
dev_err(aic31xx->dev, "not able to acquire gpio\n");
|
|
return PTR_ERR(aic31xx->gpio_reset);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
|
|
aic31xx->supplies[i].supply = aic31xx_supply_names[i];
|
|
|
|
ret = devm_regulator_bulk_get(aic31xx->dev,
|
|
ARRAY_SIZE(aic31xx->supplies),
|
|
aic31xx->supplies);
|
|
if (ret) {
|
|
dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (aic31xx->irq > 0) {
|
|
regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
|
|
AIC31XX_GPIO1_FUNC_MASK,
|
|
AIC31XX_GPIO1_INT1 <<
|
|
AIC31XX_GPIO1_FUNC_SHIFT);
|
|
|
|
regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
|
|
AIC31XX_SC |
|
|
AIC31XX_ENGINE);
|
|
|
|
ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq,
|
|
NULL, aic31xx_irq,
|
|
IRQF_ONESHOT, "aic31xx-irq",
|
|
aic31xx);
|
|
if (ret) {
|
|
dev_err(aic31xx->dev, "Unable to request IRQ\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (aic31xx->codec_type & DAC31XX_BIT)
|
|
return devm_snd_soc_register_component(&i2c->dev,
|
|
&soc_codec_driver_aic31xx,
|
|
dac31xx_dai_driver,
|
|
ARRAY_SIZE(dac31xx_dai_driver));
|
|
else
|
|
return devm_snd_soc_register_component(&i2c->dev,
|
|
&soc_codec_driver_aic31xx,
|
|
aic31xx_dai_driver,
|
|
ARRAY_SIZE(aic31xx_dai_driver));
|
|
}
|
|
|
|
static const struct i2c_device_id aic31xx_i2c_id[] = {
|
|
{ "tlv320aic310x", AIC3100 },
|
|
{ "tlv320aic311x", AIC3110 },
|
|
{ "tlv320aic3100", AIC3100 },
|
|
{ "tlv320aic3110", AIC3110 },
|
|
{ "tlv320aic3120", AIC3120 },
|
|
{ "tlv320aic3111", AIC3111 },
|
|
{ "tlv320dac3100", DAC3100 },
|
|
{ "tlv320dac3101", DAC3101 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
|
|
|
|
static struct i2c_driver aic31xx_i2c_driver = {
|
|
.driver = {
|
|
.name = "tlv320aic31xx-codec",
|
|
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
|
|
.acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
|
|
},
|
|
.probe = aic31xx_i2c_probe,
|
|
.id_table = aic31xx_i2c_id,
|
|
};
|
|
module_i2c_driver(aic31xx_i2c_driver);
|
|
|
|
MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
|
|
MODULE_DESCRIPTION("ASoC TLV320AIC31xx CODEC Driver");
|
|
MODULE_LICENSE("GPL v2");
|