blob: b9ce60baa1485e45f5e459719d93f46e2918ce78 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#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/of.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.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 "tlv320adc5140.h"
#define ADC5140_RESET 0x01
#define ADC5140_BCLKINV_BIT 0x04
#define ADC5140_BCLK_FSYNC_MASTER 0x80
#define ADC5140_I2S_MODE_BIT 0x40
#define ADC5140_LEFT_JUST_BIT 0x80
#define ADC5140_ASI_FMT_MSK 0xC0
#define ADC5140_ASI_CFG2_MSK 0x80
#define ADC5140_MCLK_MSK 0x07
#define ADC5140_FS_RATE_SHIFT 4
#define ADC5140_MCLK_INPUT 0xa0
#define ADC5140_SLV_INPUT 0x00
#define ADC5140_20_BIT_WORD 0x10
#define ADC5140_24_BIT_WORD 0x20
#define ADC5140_32_BIT_WORD 0x30
#define ADC5140_WORD_LEN_MSK 0x30
#define ADC5140_TX_FILL 0x01
#define ADC5140_TX_EDGE_MSK 0x02
#define ADC5140_TX_EDGE_ENABLE 0x02
#define ADC5140_TX_CFG1_MSK 0xE0
#define ADC5140_CH_SLOT_MSK 0x1F
#define ADC5140_PWR_DOWN 0x00
#define ADC5140_PWR_MASK_ADC 0x40
#define ADC5140_PWR_MASK_PLL 0x20
#define ADC5140_PWR_UP 0x60
#define ADC5140_PWR_ON 0xf0
#define ADC5140_CH_INPUT_EN 0xf0
#define ADC5140_CH_OUTPUT_EN 0xf0
#define ADC5140_BUS_KEEPER 0x20
#define ADC5140_LSB_HI_Z 0x80
#define ADC5140_WAKE_UP 0x81
#define ADC5140_SLEEP_MODE 0x80
#define SW_RESET_ENABLED 0
#define SHUTDOWN_ENABLED 1
#define ADC5140_MST_SEL 0x98
#define ADC5140_SLV_SEL 0x18
#define ADC5140_CHN_GAIN 0x1E
#define ADC5140_CHN_VOL 0xF6
static bool daisy_chain_flag;
static struct snd_soc_component *g_component;
/* Private struct for internal data */
struct adc5140_priv {
struct regmap *regmap;
struct device *dev;
int adc_num;
int reset_pin;
int daisy_chain;
bool disable_suspend;
int pga_gain;
int dga_gain;
};
static const struct reg_default adc5140_reg_defaults[] = {
{ADC5140_PAGE_SELECT, 0x00},
};
static int adc5140_reset_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int adc5140_reset_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static const struct regmap_range_cfg adc5140_ranges[] = {
{
.range_min = 0,
.range_max = 12 * 128,
.selector_reg = ADC5140_PAGE_SELECT,
.selector_mask = 0xff,
.selector_shift = 0,
.window_start = 0,
.window_len = 128,
},
};
static const struct regmap_config adc5140_i2c_regmap = {
.reg_bits = 8,
.val_bits = 8,
.reg_defaults = adc5140_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adc5140_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.ranges = adc5140_ranges,
.num_ranges = ARRAY_SIZE(adc5140_ranges),
.max_register = 12 * 128,
};
static const char * const mic_select_text[] = {
"2.5 kOhm", "10 kOhm", "20 kOhm"
};
static const char * const input_config_text[] = {
"Diff", "Single", "Digital"
};
static const DECLARE_TLV_DB_MINMAX_MUTE(adc_fgain_tlv, -10000, 2700);
static const DECLARE_TLV_DB_LINEAR(adc_chgain_tlv, 0, 4200);
static const struct soc_enum adc5140_enum[] = {
SOC_ENUM_SINGLE(ADC5140_CH1_CFG0, 2, ARRAY_SIZE(mic_select_text),
mic_select_text),
SOC_ENUM_SINGLE(ADC5140_CH2_CFG0, 2, ARRAY_SIZE(mic_select_text),
mic_select_text),
SOC_ENUM_SINGLE(ADC5140_CH3_CFG0, 2, ARRAY_SIZE(mic_select_text),
mic_select_text),
SOC_ENUM_SINGLE(ADC5140_CH4_CFG0, 2, ARRAY_SIZE(mic_select_text),
mic_select_text),
SOC_ENUM_SINGLE(ADC5140_CH1_CFG0, 4, ARRAY_SIZE(input_config_text),
input_config_text),
SOC_ENUM_SINGLE(ADC5140_CH2_CFG0, 4, ARRAY_SIZE(input_config_text),
input_config_text),
SOC_ENUM_SINGLE(ADC5140_CH3_CFG0, 4, ARRAY_SIZE(input_config_text),
input_config_text),
SOC_ENUM_SINGLE(ADC5140_CH4_CFG0, 4, ARRAY_SIZE(input_config_text),
input_config_text),
};
static const struct snd_soc_dapm_widget adc5140_dapm_widgets_a[] = {
/* Inputs */
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_ADC("ADC", "Capture", 0, 0, 0),
};
static const struct snd_soc_dapm_widget adc5140_dapm_widgets_b[] = {
/* Inputs */
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_ADC("ADC", "Capture", 0, 0, 0),
};
static const struct snd_soc_dapm_route adc5140_audio_map_a[] = {
/* Mic input */
{"MIC1", NULL, "ADC"},
};
static const struct snd_soc_dapm_route adc5140_audio_map_b[] = {
/* Mic input */
{"MIC1", NULL, "ADC"},
};
static const struct snd_kcontrol_new adc5140_snd_controls_a[] = {
SOC_SINGLE_TLV("Ch1 Digital Capture Vol A", ADC5140_CH1_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch2 Digital Capture Vol A", ADC5140_CH2_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch3 Digital Capture Vol A", ADC5140_CH3_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch4 Digital Capture Vol A", ADC5140_CH4_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch5 Digital Capture Vol A", ADC5140_CH5_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch6 Digital Capture Vol A", ADC5140_CH6_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch7 Digital Capture Vol A", ADC5140_CH7_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch8 Digital Capture Vol A", ADC5140_CH8_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
/* Channel Gain - 0 to +42db */
SOC_SINGLE_TLV("Ch1 Gain A", ADC5140_CH1_CFG1, 2, 42, 0,
adc_chgain_tlv),
SOC_SINGLE_TLV("Ch2 Gain A", ADC5140_CH2_CFG1, 2, 42, 0,
adc_chgain_tlv),
SOC_SINGLE_TLV("Ch3 Gain A", ADC5140_CH3_CFG1, 2, 42, 0,
adc_chgain_tlv),
SOC_SINGLE_TLV("Ch4 Gain A", ADC5140_CH4_CFG1, 2, 42, 0,
adc_chgain_tlv),
/* Input Impedance (Applicable for Analog Input)*/
SOC_ENUM("Ch1 Input Impedance A", adc5140_enum[0]),
SOC_ENUM("Ch2 Input Impedance A", adc5140_enum[1]),
SOC_ENUM("Ch3 Input Impedance A", adc5140_enum[2]),
SOC_ENUM("Ch4 Input Impedance A", adc5140_enum[3]),
/* Input Configuration. Differential, Single-Ended, Digital */
SOC_ENUM("Ch1 Input Config A", adc5140_enum[4]),
SOC_ENUM("Ch2 Input Config A", adc5140_enum[5]),
SOC_ENUM("Ch3 Input Config A", adc5140_enum[6]),
SOC_ENUM("Ch4 Input Config A", adc5140_enum[7]),
/* DRE - AGC Enabler */
SOC_SINGLE("Ch1 DRE Enable A", ADC5140_CH1_CFG0, 0, 1, 0),
SOC_SINGLE("Ch2 DRE Enable A", ADC5140_CH2_CFG0, 0, 1, 0),
SOC_SINGLE("Ch3 DRE Enable A", ADC5140_CH3_CFG0, 0, 1, 0),
SOC_SINGLE("Ch4 DRE Enable A", ADC5140_CH4_CFG0, 0, 1, 0),
/* DRE - Params */
SOC_SINGLE("DRE Trigger Level A", ADC5140_DRE_CFG0, 4, 15, 0),
SOC_SINGLE("DRE Max Gain A", ADC5140_DRE_CFG0, 0, 15, 0),
#ifdef DEBUG_CHANNELS
/* Input Channel */
SOC_SINGLE("Ch1 Input Enable A", ADC5140_IN_CH_EN, 7, 1, 0),
SOC_SINGLE("Ch2 Input Enable A", ADC5140_IN_CH_EN, 6, 1, 0),
SOC_SINGLE("Ch3 Input Enable A", ADC5140_IN_CH_EN, 5, 1, 0),
SOC_SINGLE("Ch4 Input Enable A", ADC5140_IN_CH_EN, 4, 1, 0),
/* Output Slot Enable */
SOC_SINGLE("Ch1 Slot Enable A", ADC5140_ASI_OUT_CH_EN, 7, 1, 0),
SOC_SINGLE("Ch2 Slot Enable A", ADC5140_ASI_OUT_CH_EN, 6, 1, 0),
SOC_SINGLE("Ch3 Slot Enable A", ADC5140_ASI_OUT_CH_EN, 5, 1, 0),
SOC_SINGLE("Ch4 Slot Enable A", ADC5140_ASI_OUT_CH_EN, 4, 1, 0),
#endif
/* Slot selection on TDM bus */
SOC_SINGLE("Ch1 Slot Offset A", ADC5140_ASI_CH1, 0, 63, 0),
SOC_SINGLE("Ch2 Slot Offset A", ADC5140_ASI_CH2, 0, 63, 0),
SOC_SINGLE("Ch3 Slot Offset A", ADC5140_ASI_CH3, 0, 63, 0),
SOC_SINGLE("Ch4 Slot Offset A", ADC5140_ASI_CH4, 0, 63, 0),
/* Reset codecs */
SOC_SINGLE_BOOL_EXT("ADC Reset A", 0, adc5140_reset_get,
adc5140_reset_set),
};
static const struct snd_kcontrol_new adc5140_snd_controls_b[] = {
SOC_SINGLE_TLV("Ch1 Digital Capture Vol B", ADC5140_CH1_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch2 Digital Capture Vol B", ADC5140_CH2_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch3 Digital Capture Vol B", ADC5140_CH3_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch4 Digital Capture Vol B", ADC5140_CH4_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch5 Digital Capture Vol B", ADC5140_CH5_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch6 Digital Capture Vol B", ADC5140_CH6_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch7 Digital Capture Vol B", ADC5140_CH7_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
SOC_SINGLE_TLV("Ch8 Digital Capture Vol B", ADC5140_CH8_CFG2,
0, 0xFF, 0, adc_fgain_tlv),
/* Channel Gain - 0 to +42db */
SOC_SINGLE_TLV("Ch1 Gain B", ADC5140_CH1_CFG1, 2, 42, 0,
adc_chgain_tlv),
SOC_SINGLE_TLV("Ch2 Gain B", ADC5140_CH2_CFG1, 2, 42, 0,
adc_chgain_tlv),
SOC_SINGLE_TLV("Ch3 Gain B", ADC5140_CH3_CFG1, 2, 42, 0,
adc_chgain_tlv),
SOC_SINGLE_TLV("Ch4 Gain B", ADC5140_CH4_CFG1, 2, 42, 0,
adc_chgain_tlv),
/* Input Impedance (Applicable for Analog Input)*/
SOC_ENUM("Ch1 Input Impedance B", adc5140_enum[0]),
SOC_ENUM("Ch2 Input Impedance B", adc5140_enum[1]),
SOC_ENUM("Ch3 Input Impedance B", adc5140_enum[2]),
SOC_ENUM("Ch4 Input Impedance B", adc5140_enum[3]),
/* Input Configuration. Differential, Single-Ended, Digital */
SOC_ENUM("Ch1 Input Config B", adc5140_enum[4]),
SOC_ENUM("Ch2 Input Config B", adc5140_enum[5]),
SOC_ENUM("Ch3 Input Config B", adc5140_enum[6]),
SOC_ENUM("Ch4 Input Config B", adc5140_enum[7]),
/* DRE - AGC Enabler */
SOC_SINGLE("Ch1 DRE Enable B", ADC5140_CH1_CFG0, 0, 1, 0),
SOC_SINGLE("Ch2 DRE Enable B", ADC5140_CH2_CFG0, 0, 1, 0),
SOC_SINGLE("Ch3 DRE Enable B", ADC5140_CH3_CFG0, 0, 1, 0),
SOC_SINGLE("Ch4 DRE Enable B", ADC5140_CH4_CFG0, 0, 1, 0),
/* DRE - Params */
SOC_SINGLE("DRE Trigger Level B", ADC5140_DRE_CFG0, 4, 15, 0),
SOC_SINGLE("DRE Max Gain B", ADC5140_DRE_CFG0, 0, 15, 0),
#ifdef DEBUG_CHANNELS
/* Input Channel */
SOC_SINGLE("Ch1 Input Enable B", ADC5140_IN_CH_EN, 7, 1, 0),
SOC_SINGLE("Ch2 Input Enable B", ADC5140_IN_CH_EN, 6, 1, 0),
SOC_SINGLE("Ch3 Input Enable B", ADC5140_IN_CH_EN, 5, 1, 0),
SOC_SINGLE("Ch4 Input Enable B", ADC5140_IN_CH_EN, 4, 1, 0),
/* Output Slot Enable */
SOC_SINGLE("Ch1 Slot Enable B", ADC5140_ASI_OUT_CH_EN, 7, 1, 0),
SOC_SINGLE("Ch2 Slot Enable B", ADC5140_ASI_OUT_CH_EN, 6, 1, 0),
SOC_SINGLE("Ch3 Slot Enable B", ADC5140_ASI_OUT_CH_EN, 5, 1, 0),
SOC_SINGLE("Ch4 Slot Enable B", ADC5140_ASI_OUT_CH_EN, 4, 1, 0),
#endif
/* Slot selection on TDM bus */
SOC_SINGLE("Ch1 Slot Offset B", ADC5140_ASI_CH1, 0, 63, 0),
SOC_SINGLE("Ch2 Slot Offset B", ADC5140_ASI_CH2, 0, 63, 0),
SOC_SINGLE("Ch3 Slot Offset B", ADC5140_ASI_CH3, 0, 63, 0),
SOC_SINGLE("Ch4 Slot Offset B", ADC5140_ASI_CH4, 0, 63, 0),
/* Reset codecs */
SOC_SINGLE_BOOL_EXT("ADC Reset B", 0, adc5140_reset_get,
adc5140_reset_set),
};
static void adc5140_reset(struct snd_soc_component *component)
{
#if SW_RESET_ENABLED
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
#endif
dev_info(component->dev, "%s:+\n", __func__);
snd_soc_component_write(component, ADC5140_SLEEP_CFG,
ADC5140_WAKE_UP);
mdelay(10);
#if SW_RESET_ENABLED
regcache_cache_only(adc5140->regmap, true);
regcache_mark_dirty(adc5140->regmap);
snd_soc_component_write(component, ADC5140_SW_RESET,
ADC5140_RESET);
mdelay(20);
regcache_cache_only(adc5140->regmap, false);
regcache_sync(adc5140->regmap);
snd_soc_component_write(component, ADC5140_SLEEP_CFG,
ADC5140_WAKE_UP);
mdelay(10);
#endif
dev_info(component->dev, "%s:-\n", __func__);
}
static int adc5140_reset_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static int adc5140_reset_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
dev_info(component->dev, "%s: component %d\n",
__func__, adc5140->adc_num);
if (ucontrol->value.integer.value[0])
adc5140_reset(component);
else
dev_info(component->dev, "%s: 0 value won't reset ADCs\n",
__func__);
return 0;
}
static int adc5140_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_component *component = codec_dai->component;
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
u8 wlen = 0, fs_rate = 0, fs_bclk_ratio = 0, tx_edge = 0;
unsigned int width = params_width(params);
unsigned int channels = params_channels(params);
unsigned int bclk_freq = 0;
dev_info(component->dev, "%s+: width %d rate %d ch %d codec %d\n",
__func__, width, params_rate(params),
channels, adc5140->adc_num);
switch (width) {
case 16:
break;
case 20:
wlen = ADC5140_20_BIT_WORD;
break;
case 24:
wlen = ADC5140_24_BIT_WORD;
break;
case 32:
wlen = ADC5140_32_BIT_WORD;
break;
default:
dev_err(component->dev, "%s: Unsupported width %d\n",
__func__, params_width(params));
return -EINVAL;
}
switch (params_rate(params)) {
case 16000:
fs_rate = 1 << ADC5140_FS_RATE_SHIFT;
break;
case 96000:
fs_rate = 5 << ADC5140_FS_RATE_SHIFT;
break;
case 48000:
default:
/* 48k is also default */
fs_rate = 4 << ADC5140_FS_RATE_SHIFT;
}
/* For loopback */
if (channels == 10)
channels = 8;
else if (channels == 6)
channels = 4;
/* For odd number of channels, subtract 1 for reference ch */
if (channels % 2)
channels--;
switch (width * channels) {
case 64:
fs_bclk_ratio = 4;
break;
case 96:
fs_bclk_ratio = 5;
break;
case 128:
fs_bclk_ratio = 6;
break;
case 192:
fs_bclk_ratio = 7;
break;
case 256:
fs_bclk_ratio = 8;
break;
case 384:
fs_bclk_ratio = 9;
break;
case 512:
fs_bclk_ratio = 10;
break;
case 1024:
fs_bclk_ratio = 11;
break;
case 2048:
fs_bclk_ratio = 12;
break;
default:
dev_err(component->dev, "%s: Unsupported fs_bclk_ratio %d\n",
__func__, width * channels);
return -EINVAL;
}
bclk_freq = width * channels * params_rate(params);
if (bclk_freq > ADC5140_FREQ_18500000)
tx_edge = ADC5140_TX_EDGE_ENABLE;
dev_info(component->dev, "%s: wlen 0x%X rate 0x%X fs_bclk_ratio 0x%X tx_edge %d\n",
__func__, wlen, fs_rate, fs_bclk_ratio, tx_edge);
snd_soc_component_update_bits(component, ADC5140_ASI_CFG0,
(ADC5140_WORD_LEN_MSK | ADC5140_TX_FILL | ADC5140_TX_EDGE_MSK),
(wlen | ADC5140_TX_FILL) | tx_edge);
dev_info(adc5140->dev, "%s-\n", __func__);
return 0;
}
static int adc5140_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
u8 iface_reg1 = 0;
u8 iface_reg2 = 0;
dev_info(component->dev, "%s: fmt 0x%X component %d\n",
__func__, fmt, adc5140->adc_num);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
iface_reg2 = ADC5140_BCLK_FSYNC_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
break;
default:
dev_err(component->dev, "%s: Invalid DAI master/slave interface\n",
__func__);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
iface_reg1 |= ADC5140_BCLKINV_BIT;
break;
default:
dev_err(component->dev, "%s: Invalid DAI clock signal polarity\n",
__func__);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_DSP_B:
/* Treat I2S same as TDM */
break;
case SND_SOC_DAIFMT_LEFT_J:
iface_reg1 |= ADC5140_LEFT_JUST_BIT;
break;
default:
dev_info(component->dev, "%s: No Format. Using default TDM\n",
__func__);
}
snd_soc_component_update_bits(component, ADC5140_ASI_CFG0,
(ADC5140_BCLKINV_BIT | ADC5140_ASI_FMT_MSK), iface_reg1);
snd_soc_component_update_bits(component, ADC5140_MST_CFG0,
ADC5140_BCLK_FSYNC_MASTER, iface_reg2);
snd_soc_component_update_bits(component, ADC5140_ASI_CFG1,
ADC5140_TX_CFG1_MSK, ADC5140_LSB_HI_Z);
return 0;
}
static int adc5140_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 adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
int value, mclk_freq_conf = 0;
dev_info(component->dev, "%s: clk_id %d, freq %d, dir %d\n",
__func__, clk_id, freq, dir);
value = snd_soc_component_read32(component, ADC5140_MST_CFG0);
if (value < 0) {
dev_warn(component->dev, "%s: failed to access adc", __func__);
return 0;
} else if (!(value & ADC5140_BCLK_FSYNC_MASTER)) {
dev_info(component->dev, "%s: adc is as slave mode", __func__);
return 0;
}
/* master mode */
switch (freq) {
case ADC5140_FREQ_24576000:
mclk_freq_conf = 7;
break;
case ADC5140_FREQ_24000000:
mclk_freq_conf = 6;
break;
case ADC5140_FREQ_19268000:
mclk_freq_conf = 5;
break;
case ADC5140_FREQ_19200000:
mclk_freq_conf = 4;
break;
case ADC5140_FREQ_16000000:
mclk_freq_conf = 3;
break;
case ADC5140_FREQ_13000000:
mclk_freq_conf = 2;
break;
case ADC5140_FREQ_12288000:
mclk_freq_conf = 1;
break;
case ADC5140_FREQ_12000000:
mclk_freq_conf = 0;
break;
default:
dev_warn(component->dev, "%s: Invalid freq %d\n",
__func__, freq);
return -EINVAL;
}
snd_soc_component_update_bits(component, ADC5140_MST_CFG0,
ADC5140_MCLK_MSK, mclk_freq_conf);
if (adc5140->daisy_chain != 1)
snd_soc_component_write(component, ADC5140_GPIO_CFG0,
ADC5140_SLV_INPUT);
return 0;
}
static int adc5140_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_component *component = codec_dai->component;
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
dev_info(component->dev, "%s: component %d\n", __func__,
adc5140->adc_num);
/* Enable Channels Inputs */
snd_soc_component_write(component, ADC5140_IN_CH_EN,
ADC5140_CH_INPUT_EN);
if (adc5140->adc_num == 1) {
if (!g_component) {
dev_err(component->dev, "%s:found no adc0\n", __func__);
return 0;
}
/* Enable Channels Inputs for ADC5120 */
snd_soc_component_write(component, ADC5140_IN_CH_EN,
ADC5140_CH_INPUT_EN);
/* Power On */
snd_soc_component_update_bits(g_component, ADC5140_PWR_CFG,
(ADC5140_PWR_MASK_ADC | ADC5140_PWR_MASK_PLL),
ADC5140_PWR_UP);
snd_soc_component_update_bits(component, ADC5140_PWR_CFG,
(ADC5140_PWR_MASK_ADC | ADC5140_PWR_MASK_PLL),
ADC5140_PWR_UP);
/* delay 100ms to avoid glitch */
msleep(100);
/* Enable Channels Slot Output */
snd_soc_component_write(g_component, ADC5140_ASI_OUT_CH_EN,
ADC5140_CH_OUTPUT_EN);
snd_soc_component_write(component, ADC5140_ASI_OUT_CH_EN,
ADC5140_CH_OUTPUT_EN);
}
return 0;
}
static int adc5140_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_component *component = codec_dai->component;
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
int value;
dev_info(component->dev, "%s:+ component %d\n", __func__,
adc5140->adc_num);
value = snd_soc_component_read32(component, ADC5140_PWR_CFG);
if (value != -1 && value & ADC5140_PWR_ON)
adc5140_power_down(codec_dai);
dev_info(component->dev, "%s:- component %d value %x\n", __func__,
adc5140->adc_num, value);
return 0;
}
void adc5140_power_down(struct snd_soc_dai *codec_dai)
{
struct snd_soc_component *component = codec_dai->component;
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
dev_info(component->dev, "%s: component %d\n", __func__,
adc5140->adc_num);
#if SHUTDOWN_ENABLED
snd_soc_component_write(component, ADC5140_IN_CH_EN, 0);
/* 20ms recommended by TI */
mdelay(20);
snd_soc_component_write(component, ADC5140_ASI_OUT_CH_EN, 0);
snd_soc_component_update_bits(component, ADC5140_PWR_CFG,
(ADC5140_PWR_MASK_ADC | ADC5140_PWR_MASK_PLL),
ADC5140_PWR_DOWN);
/* Sleep and wake up is needed to fix weakness in shutdown of ADC5140 */
snd_soc_component_write(component, ADC5140_SLEEP_CFG,
ADC5140_SLEEP_MODE);
/* 1ms recommended by TI */
mdelay(1);
snd_soc_component_write(component, ADC5140_SLEEP_CFG,
ADC5140_WAKE_UP);
/* 10ms recommended by TI */
mdelay(10);
#endif
}
static const struct snd_soc_dai_ops adc5140_dai_ops = {
.hw_params = adc5140_hw_params,
.set_sysclk = adc5140_set_dai_sysclk,
.set_fmt = adc5140_set_dai_fmt,
.prepare = adc5140_dai_prepare,
.hw_free = adc5140_hw_free,
};
static void adc5140_set_gain_vol(struct snd_soc_component *component,
int pag_gain, int dga_gain)
{
int adc5140_gain[] = {
ADC5140_CH1_CFG1, ADC5140_CH2_CFG1,
ADC5140_CH3_CFG1, ADC5140_CH4_CFG1,
ADC5140_CH5_CFG1, ADC5140_CH6_CFG1,
ADC5140_CH7_CFG1, ADC5140_CH8_CFG1
};
int adc5140_vol[] = {
ADC5140_CH1_CFG2, ADC5140_CH2_CFG2,
ADC5140_CH3_CFG2, ADC5140_CH4_CFG2,
ADC5140_CH5_CFG2, ADC5140_CH6_CFG2,
ADC5140_CH7_CFG2, ADC5140_CH8_CFG2
};
int i;
for (i = 0; i < 8; i++) {
snd_soc_component_write(component, adc5140_gain[i], pag_gain);
snd_soc_component_write(component, adc5140_vol[i], dga_gain);
}
}
static int adc5140_set_daisy_chain(struct snd_soc_component *component)
{
pr_info("%s\n", __func__);
snd_soc_component_update_bits(component, ADC5140_ASI_CFG2,
ADC5140_ASI_CFG2_MSK, 0x80);
snd_soc_component_write(component, ADC5140_GPIO_CFG0, 0xb0);
daisy_chain_flag = true;
return 0;
}
static int adc5140_codec_probe(struct snd_soc_component *component)
{
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
int ret;
dev_info(adc5140->dev, "%s:+ component %d\n", __func__,
adc5140->adc_num);
/* reset component */
adc5140_reset(component);
mdelay(5);
/* select clk source for hoya: 48k */
snd_soc_component_write(component, ADC5140_CLK_SRC, ADC5140_SLV_SEL);
mdelay(5);
/* set gain & volume */
adc5140_set_gain_vol(component, ADC5140_CHN_GAIN, ADC5140_CHN_VOL);
/* daisy chain TDM connection */
if (adc5140->adc_num == 0 && adc5140->daisy_chain == 1)
adc5140_set_daisy_chain(component);
if (adc5140->adc_num == 1 && !daisy_chain_flag) {
snd_soc_component_write(component, ADC5140_ASI_CH1, 0x4);
snd_soc_component_write(component, ADC5140_ASI_CH2, 0x5);
snd_soc_component_write(component, ADC5140_ASI_CH3, 0x6);
snd_soc_component_write(component, ADC5140_ASI_CH4, 0x7);
}
if (adc5140->adc_num == 0)
ret = snd_soc_add_component_controls(component,
adc5140_snd_controls_a,
ARRAY_SIZE(adc5140_snd_controls_a));
else
ret = snd_soc_add_component_controls(component,
adc5140_snd_controls_b,
ARRAY_SIZE(adc5140_snd_controls_b));
if (ret < 0) {
dev_err(adc5140->dev, "%s: could not add kcontrol for component %d (err=%d)\n",
__func__, adc5140->adc_num, ret);
return -ret;
}
/* Only log error. DAPM is not necessary to operate this driver */
if (adc5140->adc_num == 0)
ret = snd_soc_dapm_new_controls(dapm, adc5140_dapm_widgets_a,
ARRAY_SIZE(adc5140_dapm_widgets_a));
else
ret = snd_soc_dapm_new_controls(dapm, adc5140_dapm_widgets_b,
ARRAY_SIZE(adc5140_dapm_widgets_b));
if (ret < 0)
dev_err(adc5140->dev,
"%s: could not add dapm for component %d (err=%d)\n",
__func__, adc5140->adc_num, ret);
if (adc5140->adc_num == 0)
ret = snd_soc_dapm_add_routes(dapm, adc5140_audio_map_a,
ARRAY_SIZE(adc5140_audio_map_a));
else
ret = snd_soc_dapm_add_routes(dapm, adc5140_audio_map_b,
ARRAY_SIZE(adc5140_audio_map_b));
if (ret < 0)
dev_err(adc5140->dev,
"%s: could not add routes for component %d (err=%d)\n",
__func__, adc5140->adc_num, ret);
if (adc5140->adc_num == 0)
g_component = component;
if (adc5140->pga_gain && adc5140->dga_gain)
adc5140_set_gain_vol(component,
adc5140->pga_gain, adc5140->dga_gain);
dev_info(adc5140->dev, "%s:-\n", __func__);
return 0;
}
static void adc5140_codec_remove(struct snd_soc_component *component)
{
dev_info(component->dev, "%s:\n", __func__);
}
static int adc5140_codec_suspend(struct snd_soc_component *component)
{
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
int ret = 0;
dev_info(component->dev, "%s: codec %d disable suspend %d\n", __func__,
adc5140->adc_num, adc5140->disable_suspend);
if (adc5140->disable_suspend)
return 0;
regcache_cache_only(adc5140->regmap, true);
regcache_mark_dirty(adc5140->regmap);
return ret;
};
static int adc5140_codec_resume(struct snd_soc_component *component)
{
struct adc5140_priv *adc5140 = snd_soc_component_get_drvdata(component);
int ret = 0;
dev_info(component->dev, "%s: codec %d disable suspend %d\n", __func__,
adc5140->adc_num, adc5140->disable_suspend);
if (adc5140->disable_suspend)
return 0;
regcache_cache_only(adc5140->regmap, false);
ret = regcache_sync(adc5140->regmap);
if (ret)
dev_err(component->dev, "%s failed to sync registers(%d)\n",
__func__, ret);
return ret;
};
static const struct snd_soc_component_driver soc_codec_driver_adc5140 = {
.probe = adc5140_codec_probe,
.remove = adc5140_codec_remove,
.suspend = adc5140_codec_suspend,
.resume = adc5140_codec_resume,
};
static struct snd_soc_dai_driver adc5140_dai_driver_a[] = {
{
.name = "tlv320adc5140-codec-a",
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = ADC5140_RATES,
.formats = ADC5140_FORMATS,
},
.ops = &adc5140_dai_ops,
.symmetric_rates = 1,
}
};
static struct snd_soc_dai_driver adc5140_dai_driver_b[] = {
{
.name = "tlv320adc5140-codec-b",
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = ADC5140_RATES,
.formats = ADC5140_FORMATS,
},
.ops = &adc5140_dai_ops,
.symmetric_rates = 1,
}
};
static int adc5140_parse_node(struct adc5140_priv *adc5140)
{
int ret;
adc5140->reset_pin =
of_get_named_gpio(adc5140->dev->of_node, "reset-gpio", 0);
if (adc5140->reset_pin < 0) {
pr_warn("%s fail to get reset pin from dts!\n", __func__);
goto parse_other;
}
ret = devm_gpio_request_one(adc5140->dev, adc5140->reset_pin,
GPIOF_OUT_INIT_LOW,
"adc5140-reset-pin");
if (ret < 0) {
pr_err("%s fail to request reset-pin!\n", __func__);
goto parse_other;
}
gpio_direction_output(adc5140->reset_pin, GPIOF_OUT_INIT_LOW);
usleep_range(1000, 1100);
gpio_direction_output(adc5140->reset_pin, GPIOF_OUT_INIT_HIGH);
usleep_range(10000, 11000);
pr_info("%s set reset-pin OK\n", __func__);
parse_other:
ret = device_property_read_u32(adc5140->dev, "adc-num",
&adc5140->adc_num);
if (ret) {
dev_err(adc5140->dev, "%s: adc_num DT property missing\n",
__func__);
return ret;
}
ret = device_property_read_u32(adc5140->dev, "pga-gain",
&adc5140->pga_gain);
if (ret) {
dev_err(adc5140->dev, "%s: pga-gain DT property missing\n",
__func__);
}
ret = device_property_read_u32(adc5140->dev, "dga-gain",
&adc5140->dga_gain);
if (ret) {
dev_err(adc5140->dev, "%s: dga-gain DT property missing\n",
__func__);
}
if (device_property_read_bool(adc5140->dev, "disable-suspend"))
adc5140->disable_suspend = true;
dev_info(adc5140->dev, "%s: suspend disabled %d\n", __func__,
adc5140->disable_suspend);
ret = device_property_read_u32(adc5140->dev, "daisy-chain",
&adc5140->daisy_chain);
if (ret)
dev_warn(adc5140->dev, "%s: fail to get daisy-chain!\n",
__func__);
dev_info(adc5140->dev, "%s:- codec %d\n", __func__, adc5140->adc_num);
return ret;
}
static int adc5140_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct adc5140_priv *adc5140_data;
dev_info(&i2c->dev, "%s: %s+ codec_type = %d\n", __func__,
id->name, (int)id->driver_data);
adc5140_data = devm_kzalloc(&i2c->dev, sizeof(struct adc5140_priv),
GFP_KERNEL);
if (!adc5140_data)
return -ENOMEM;
adc5140_data->regmap = devm_regmap_init_i2c(i2c, &adc5140_i2c_regmap);
if (IS_ERR(adc5140_data->regmap)) {
ret = PTR_ERR(adc5140_data->regmap);
dev_err(&i2c->dev, "Failed to allocate 1st register map: %d\n",
ret);
return ret;
}
adc5140_data->dev = &i2c->dev;
i2c_set_clientdata(i2c, adc5140_data);
adc5140_parse_node(adc5140_data);
if (adc5140_data->adc_num == 0)
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_codec_driver_adc5140,
adc5140_dai_driver_a, 1);
else
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_codec_driver_adc5140,
adc5140_dai_driver_b, 1);
if (ret)
dev_err(&i2c->dev, "Failed to register codec %d. Error %d\n",
adc5140_data->adc_num, ret);
dev_info(&i2c->dev, "%s:- codec = %d\n", __func__,
adc5140_data->adc_num);
return ret;
}
static const struct of_device_id tlv320adc5140_of_match[] = {
{ .compatible = "ti,tlv320adc5140" },
{},
};
MODULE_DEVICE_TABLE(of, tlv320adc5140_of_match);
static const struct i2c_device_id adc5140_i2c_id[] = {
{ "tlv320adc5140", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, adc5140_i2c_id);
static struct i2c_driver adc5140_i2c_driver = {
.driver = {
.name = "tlv320adc5140",
.of_match_table = tlv320adc5140_of_match,
},
.probe = adc5140_i2c_probe,
.id_table = adc5140_i2c_id,
};
module_i2c_driver(adc5140_i2c_driver);
MODULE_DESCRIPTION("ASoC TLV320ADC5140 Codec Driver");
MODULE_AUTHOR("C. Omer Rafique <rafiquec@amazon.com>");
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_LICENSE("GPL v2");