linuxOS_D21X/source/linux-5.10/sound/soc/codecs/ac102.c
2024-11-29 16:13:46 +08:00

465 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/of.h>
#include <sound/tlv.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include "ac102.h"
static const struct reg_default ac102_reg_default[] = {
{ 0x00, 0x12 },
{ 0x01, 0x73 },
{ 0x02, 0x1b },
{ 0x03, 0x34 },
{ 0x04, 0x01 },
{ 0x05, 0x01 },
{ 0x06, 0x3f },
{ 0x07, 0x17 },
{ 0x08, 0x05 },
{ 0x09, 0x00 },
{ 0x0a, 0x0f },
{ 0x0b, 0x15 },
{ 0x0c, 0x33 },
{ 0x0d, 0x00 },
{ 0x0e, 0x05 },
{ 0x0f, 0x03 },
{ 0x11, 0x0a },
{ 0x13, 0x05 },
{ 0x16, 0xe4 },
{ 0x18, 0x0f },
{ 0x19, 0x0a },
{ 0x1a, 0x81 },
{ 0x1b, 0x60 },
{ 0x1c, 0x81 },
{ 0x1d, 0x01 },
{ 0x1f, 0x15 },
{ 0x20, 0x60 },
{ 0x25, 0x00 },
{ 0x26, 0x00 },
{ 0x27, 0xb6 },
{ 0x28, 0x33 },
{ 0x30, 0x00 },
{ 0x31, 0x01 },
{ 0x32, 0xc5 },
{ 0x33, 0x32 },
{ 0x34, 0x28 },
{ 0x35, 0x00 },
{ 0x36, 0x05 },
{ 0x37, 0x1e },
{ 0x38, 0xb8 },
{ 0x39, 0x00 },
{ 0x3a, 0x5f },
{ 0x3b, 0x00 },
{ 0x3c, 0x1f },
{ 0x3d, 0x14 },
{ 0x3e, 0x00 },
{ 0x3f, 0x05 },
{ 0x40, 0x1e },
{ 0x41, 0xb8 },
{ 0x42, 0x00 },
{ 0x43, 0xff },
{ 0x44, 0xfa },
{ 0x45, 0xc1 },
{ 0x46, 0x09 },
{ 0x4f, 0x00 },
{ 0x50, 0x01 },
{ 0x51, 0x00 },
{ 0x52, 0x00 },
{ 0x53, 0x00 },
{ 0x54, 0x00 },
{ 0x55, 0x00 },
{ 0x56, 0x00 },
{ 0x57, 0x00 },
{ 0x58, 0x00 },
{ 0x59, 0x00 },
{ 0x5a, 0x00 },
{ 0x5b, 0x00 },
{ 0x5c, 0x00 },
{ 0x5d, 0x00 },
{ 0x5e, 0x00 },
{ 0x60, 0x01 },
{ 0x61, 0x00 },
{ 0x62, 0x00 },
{ 0x63, 0x00 },
{ 0x64, 0x00 },
{ 0x65, 0x00 },
{ 0x66, 0x00 },
{ 0x67, 0x00 },
{ 0x68, 0x00 },
{ 0x69, 0x00 },
{ 0x6a, 0x00 },
{ 0x6b, 0x00 },
{ 0x6c, 0x00 },
{ 0x6d, 0x00 },
{ 0x6e, 0x00 },
{ 0x70, 0x01 },
{ 0x71, 0x00 },
{ 0x72, 0x00 },
{ 0x73, 0x00 },
{ 0x74, 0x00 },
{ 0x75, 0x00 },
{ 0x76, 0x00 },
{ 0x77, 0x00 },
{ 0x78, 0x00 },
{ 0x79, 0x00 },
{ 0x7a, 0x00 },
{ 0x7b, 0x00 },
{ 0x7c, 0x00 },
{ 0x7d, 0x00 },
{ 0x7e, 0x00 },
};
struct ac102_adc_dac_clk_div {
u8 div;
u8 val;
};
static const struct ac102_adc_dac_clk_div ac102_adcdac_clkdiv[] = {
{.val = 0, .div = 1},
{.val = 1, .div = 2},
{.val = 2, .div = 3},
{.val = 3, .div = 4},
{.val = 4, .div = 6},
{.val = 5, .div = 8},
{.val = 6, .div = 12},
{.val = 7, .div = 16},
{.val = 8, .div = 24},
};
struct ac102_priv {
struct regmap *regmap;
unsigned int mclk_freq;
unsigned int f_128fs;
unsigned int format;
unsigned int slots;
unsigned int slot_width;
};
static DECLARE_TLV_DB_SCALE(adc_dvc_scale, -6400, 50, 1);
static DECLARE_TLV_DB_SCALE(dac_dvc_scale, -6400, 50, 1);
static DECLARE_TLV_DB_SCALE(pga_gain_scale, 0, 100, 0);
static DECLARE_TLV_DB_SCALE(line_out_amp_gain, 0, 300, 0);
static const struct snd_kcontrol_new ac102_codec_controls[] = {
SOC_SINGLE_TLV("ADC Digital Volume", ADC_DVC, 0, 0xff, 0,
adc_dvc_scale),
SOC_SINGLE_TLV("DAC Digital Volume", DAC_DVC, 0, 0xff, 0,
dac_dvc_scale),
SOC_SINGLE_TLV("PGA gain", ADC_ANA_CTRL1, 3, 0x1f, 0,
pga_gain_scale),
SOC_SINGLE_TLV("Line out amplifier gain", DAC_ANA_CTRL2,
0, 0xf, 0, line_out_amp_gain),
};
static const struct snd_soc_dapm_widget ac102_dapm_widgets[] = {
/* Digital parts of the DAC */
SND_SOC_DAPM_SUPPLY("DAC", DAC_DIG_CTRL, 0, 0, NULL, 0),
/* Analog parts of the DAC */
SND_SOC_DAPM_DAC("DAC_OUT", "Playback", SYS_FUNC_CTRL, 0, 0),
/* Analog parts of the ADC */
SND_SOC_DAPM_ADC("ADC_PGA", "Capture", ADC_ANA_CTRL1, 0, 0),
/* Input/Output widget */
SND_SOC_DAPM_INPUT("MICP"),
SND_SOC_DAPM_INPUT("MICN"),
SND_SOC_DAPM_OUTPUT("LOUTP"),
SND_SOC_DAPM_OUTPUT("LOUTN"),
};
static const struct snd_soc_dapm_route ac102_dapm_routes[] = {
{"ADC_PGA", NULL, "MICP"},
{"ADC_PGA", NULL, "MICN"},
{"DAC_OUT", NULL, "DAC"},
{"LOUTP", NULL, "DAC_OUT"},
{"LOUTN", NULL, "DAC_OUT"},
};
static int ac102_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
struct ac102_priv *ac102 = snd_soc_component_get_drvdata(component);
u8 mode = snd_soc_component_read(component, I2S_FMT_CTRL1) & 0x3;
/* Set Transfer Mode selection */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAI_FORMAT_DSP_A:
mode |= 0x4;
break;
case SND_SOC_DAI_FORMAT_DSP_B:
break;
case SND_SOC_DAI_FORMAT_I2S:
mode |= 0x14;
break;
case SND_SOC_DAI_FORMAT_LEFT_J:
mode |= 0x10;
break;
case SND_SOC_DAI_FORMAT_RIGHT_J:
mode |= 0x20;
break;
default:
dev_err(component->dev, "Unsupported transfer mode of AC102\n");
return -EINVAL;
}
snd_soc_component_write(component, I2S_FMT_CTRL1, mode);
ac102->format = fmt;
return 0;
}
static int ac102_get_sr_sw_div(unsigned int width)
{
return (width - 8) / 4 + 1;
}
static int ac102_get_adcdac_clkval(unsigned char clkdiv)
{
int i;
const struct ac102_adc_dac_clk_div *dividers = &ac102_adcdac_clkdiv[0];
for (i = 0; i < ARRAY_SIZE(ac102_adcdac_clkdiv); i++) {
const struct ac102_adc_dac_clk_div *clkdivp = &dividers[i];
if (clkdivp->div == clkdiv)
return clkdivp->val;
}
return -EINVAL;
}
static int ac102_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 ac102_priv *ac102 = snd_soc_component_get_drvdata(component);
unsigned int word_size = params_width(params);
unsigned int slot_width = params_physical_width(params);
unsigned int slots = params_channels(params);
unsigned int sample_rate = params_rate(params);
u8 adc_dac_clkdiv = 12288000 / 128 / sample_rate;
u8 adc_dac_clkval = 0;
unsigned int lrck_period = 0;
u8 sr, sw;
u8 test_data;
u8 i;
adc_dac_clkval = ac102_get_adcdac_clkval(adc_dac_clkdiv);
/* Set ADC or DAC clock divider */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_component_write(component, DAC_CLK_SET, adc_dac_clkval);
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
snd_soc_component_write(component, ADC_CLK_SET, adc_dac_clkval);
/* Set LRCK Period */
switch (ac102->format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAI_FORMAT_I2S:
case SND_SOC_DAI_FORMAT_LEFT_J:
case SND_SOC_DAI_FORMAT_RIGHT_J:
lrck_period = slots * slot_width / 2 - 1;
break;
case SND_SOC_DAI_FORMAT_DSP_A:
case SND_SOC_DAI_FORMAT_DSP_B:
default: /* I2S default Mode is PCM mode */
lrck_period = slots * slot_width - 1;
break;
}
snd_soc_component_write(component, I2S_LRCK_CTRL2, lrck_period);
/* Set sample resolution and slot width */
sr = ac102_get_sr_sw_div(word_size);
sw = ac102_get_sr_sw_div(slot_width);
sr |= (sw << 4);
snd_soc_component_write(component, I2S_FMT_CTRL2, sr);
/****************Test MicBias enable or not************************/
test_data = snd_soc_component_read(component, PWR_CTRL2);
for (i = 0; i <= 0x31; i++) {
test_data = snd_soc_component_read(component, i);
dev_dbg(component->dev, "0x%02x: 0x%02x\n", i, test_data);
}
return 0;
}
static const struct snd_soc_dai_ops ac102_dai_ops = {
.set_fmt = ac102_set_fmt,
.hw_params = ac102_hw_params,
};
static struct snd_soc_dai_driver ac102_dai = {
.name = "ac102-dai",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &ac102_dai_ops,
};
static const struct snd_soc_component_driver soc_component_dev_ac102 = {
.controls = ac102_codec_controls,
.num_controls = ARRAY_SIZE(ac102_codec_controls),
.dapm_widgets = ac102_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ac102_dapm_widgets),
.dapm_routes = ac102_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ac102_dapm_routes),
.non_legacy_dai_naming = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
};
static const struct regmap_config ac102_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = EQ3_A2_L,
.cache_type = REGCACHE_FLAT,
.reg_defaults = ac102_reg_default,
.num_reg_defaults = ARRAY_SIZE(ac102_reg_default),
};
static int ac102_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ac102_priv *ac102;
int ret;
unsigned int test_data = 0;
ac102 = devm_kzalloc(&i2c->dev, sizeof(struct ac102_priv), GFP_KERNEL);
if (!ac102)
return -ENOMEM;
ac102->regmap = devm_regmap_init_i2c(i2c, &ac102_regmap_config);
if (IS_ERR(ac102->regmap)) {
ret = PTR_ERR(ac102->regmap);
dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
i2c_set_clientdata(i2c, ac102);
/* Disable DAC analog part */
ret = regmap_write(ac102->regmap, SYS_FUNC_CTRL, 0x34);
if (ret != 0) {
dev_err(&i2c->dev,
"Failed to disable DAC analog part: %d\n", ret);
return ret;
}
/* Reset the codec */
ret = regmap_write(ac102->regmap, CHIP_SOFT_RST, 0x34);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to reset codec ac102: %d\n", ret);
return ret;
}
/* turn down volume */
ret = regmap_write(ac102->regmap, DAC_ANA_CTRL2, 5);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to reset codec ac102: %d\n", ret);
return ret;
}
/* Enable MICBIAS */
ret = regmap_write(ac102->regmap, PWR_CTRL2, 0x1F);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable ac102 MICBIAS: %d\n", ret);
return ret;
}
regmap_read(ac102->regmap, PWR_CTRL2, &test_data);
/* Enable HPF */
ret = regmap_write(ac102->regmap, AGC_CTRL, 0x09);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable ac102 HPF: %d\n", ret);
return ret;
}
regmap_read(ac102->regmap, AGC_CTRL, &test_data);
/* Enable ADC */
ret = regmap_write(ac102->regmap, ADC_DIG_CTRL, 0x01);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable ac102 ADC: %d\n", ret);
return ret;
}
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_ac102,
&ac102_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
return ret;
}
return 0;
}
static int ac102_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
strlcpy(info->type, "ac102", I2C_NAME_SIZE);
return 0;
}
static const unsigned short ac102_i2c_addr = 0x33;
static const struct i2c_board_info ac102_i2c_board_info[] = {
{I2C_BOARD_INFO("ac102", 0x33),},
};
static const struct i2c_device_id ac102_i2c_id[] = {
{"ac102", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ac102_i2c_id);
static const struct of_device_id ac102_of_match[] = {
{.compatible = "allwinner,ac102"},
{}
};
MODULE_DEVICE_TABLE(of, ac102_of_match);
static struct i2c_driver ac102_i2c_driver = {
.driver = {
.name = "ac102",
.of_match_table = ac102_of_match,
},
.probe = ac102_i2c_probe,
.id_table = ac102_i2c_id,
.address_list = &ac102_i2c_addr,
.detect = ac102_i2c_detect,
};
module_i2c_driver(ac102_i2c_driver);
MODULE_DESCRIPTION("Asoc AC102 codec driver");
MODULE_AUTHOR("dwj weijie.ding@artinchip.com");
MODULE_LICENSE("GPL");