Commit 6cd4c645 authored by Lars-Peter Clausen's avatar Lars-Peter Clausen Committed by Mark Brown

ASoC: Add ADAU1372 audio CODEC support

Add support for the Analog Devices ADAU1372 audio CODEC.

[Alexandre Belloni: allow 32kHz for TDM4 in slave mode]
Signed-off-by: default avatarLars-Peter Clausen <lars@metafoo.de>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20201127123030.1610574-2-alexandre.belloni@bootlin.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 32025c7c
......@@ -23,6 +23,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_AD193X_I2C
imply SND_SOC_AD1980
imply SND_SOC_AD73311
imply SND_SOC_ADAU1372_I2C
imply SND_SOC_ADAU1372_SPI
imply SND_SOC_ADAU1373
imply SND_SOC_ADAU1761_I2C
imply SND_SOC_ADAU1761_SPI
......@@ -364,6 +366,20 @@ config SND_SOC_AD73311
config SND_SOC_ADAU_UTILS
tristate
config SND_SOC_ADAU1372
tristate
select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1372_I2C
tristate "Analog Devices ADAU1372 CODEC (I2C)"
select SND_SOC_ADAU1372
select REGMAP_I2C
config SND_SOC_ADAU1372_SPI
tristate "Analog Devices ADAU1372 CODEC (SPI)"
select SND_SOC_ADAU1372
select REGMAP_SPI
config SND_SOC_ADAU1373
tristate
depends on I2C
......
......@@ -9,6 +9,9 @@ snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-adau-utils-objs := adau-utils.o
snd-soc-adau1372-objs := adau1372.o
snd-soc-adau1372-i2c-objs := adau1372-i2c.o
snd-soc-adau1372-spi-objs := adau1372-spi.o
snd-soc-adau1373-objs := adau1373.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau17x1-objs := adau17x1.o
......@@ -319,6 +322,9 @@ obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o
obj-$(CONFIG_SND_SOC_ADAU1372) += snd-soc-adau1372.o
obj-$(CONFIG_SND_SOC_ADAU1372_I2C) += snd-soc-adau1372-i2c.o
obj-$(CONFIG_SND_SOC_ADAU1372_SPI) += snd-soc-adau1372-spi.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1372 codec
*
* Copyright 2016 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adau1372.h"
static int adau1372_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
return adau1372_probe(&client->dev,
devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL);
}
static const struct i2c_device_id adau1372_i2c_ids[] = {
{ "adau1372", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids);
static struct i2c_driver adau1372_i2c_driver = {
.driver = {
.name = "adau1372",
},
.probe = adau1372_i2c_probe,
.id_table = adau1372_i2c_ids,
};
module_i2c_driver(adau1372_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1372 CODEC I2C driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1372 codec
*
* Copyright 2016 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "adau1372.h"
static void adau1372_spi_switch_mode(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/*
* To get the device into SPI mode CLATCH has to be pulled low three
* times. Do this by issuing three dummy reads.
*/
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
}
static int adau1372_spi_probe(struct spi_device *spi)
{
struct regmap_config config;
config = adau1372_regmap_config;
config.read_flag_mask = 0x1;
return adau1372_probe(&spi->dev,
devm_regmap_init_spi(spi, &config), adau1372_spi_switch_mode);
}
static const struct spi_device_id adau1372_spi_id[] = {
{ "adau1372", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, adau1372_spi_id);
static struct spi_driver adau1372_spi_driver = {
.driver = {
.name = "adau1372",
},
.probe = adau1372_spi_probe,
.id_table = adau1372_spi_id,
};
module_spi_driver(adau1372_spi_driver);
MODULE_DESCRIPTION("ASoC ADAU1372 CODEC SPI driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices ADAU1372 Audio Codec driver
*
* Copyright 2016 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include "adau1372.h"
#include "adau-utils.h"
struct adau1372 {
struct clk *clk;
struct regmap *regmap;
void (*switch_mode)(struct device *dev);
bool use_pll;
bool enabled;
bool master;
struct snd_pcm_hw_constraint_list rate_constraints;
unsigned int slot_width;
struct clk *mclk;
struct gpio_desc *pd_gpio;
struct device *dev;
};
#define ADAU1372_REG_CLK_CTRL 0x00
#define ADAU1372_REG_PLL(x) (0x01 + (x))
#define ADAU1372_REG_DAC_SOURCE 0x11
#define ADAU1372_REG_SOUT_SOURCE_0_1 0x13
#define ADAU1372_REG_SOUT_SOURCE_2_3 0x14
#define ADAU1372_REG_SOUT_SOURCE_4_5 0x15
#define ADAU1372_REG_SOUT_SOURCE_6_7 0x16
#define ADAU1372_REG_ADC_SDATA_CH 0x17
#define ADAU1372_REG_ASRCO_SOURCE_0_1 0x18
#define ADAU1372_REG_ASRCO_SOURCE_2_3 0x19
#define ADAU1372_REG_ASRC_MODE 0x1a
#define ADAU1372_REG_ADC_CTRL0 0x1b
#define ADAU1372_REG_ADC_CTRL1 0x1c
#define ADAU1372_REG_ADC_CTRL2 0x1d
#define ADAU1372_REG_ADC_CTRL3 0x1e
#define ADAU1372_REG_ADC_VOL(x) (0x1f + (x))
#define ADAU1372_REG_PGA_CTRL(x) (0x23 + (x))
#define ADAU1372_REG_PGA_BOOST 0x28
#define ADAU1372_REG_MICBIAS 0x2d
#define ADAU1372_REG_DAC_CTRL 0x2e
#define ADAU1372_REG_DAC_VOL(x) (0x2f + (x))
#define ADAU1372_REG_OP_STAGE_MUTE 0x31
#define ADAU1372_REG_SAI0 0x32
#define ADAU1372_REG_SAI1 0x33
#define ADAU1372_REG_SOUT_CTRL 0x34
#define ADAU1372_REG_MODE_MP(x) (0x38 + (x))
#define ADAU1372_REG_OP_STAGE_CTRL 0x43
#define ADAU1372_REG_DECIM_PWR 0x44
#define ADAU1372_REG_INTERP_PWR 0x45
#define ADAU1372_REG_BIAS_CTRL0 0x46
#define ADAU1372_REG_BIAS_CTRL1 0x47
#define ADAU1372_CLK_CTRL_PLL_EN BIT(7)
#define ADAU1372_CLK_CTRL_XTAL_DIS BIT(4)
#define ADAU1372_CLK_CTRL_CLKSRC BIT(3)
#define ADAU1372_CLK_CTRL_CC_MDIV BIT(1)
#define ADAU1372_CLK_CTRL_MCLK_EN BIT(0)
#define ADAU1372_SAI0_DELAY1 (0x0 << 6)
#define ADAU1372_SAI0_DELAY0 (0x1 << 6)
#define ADAU1372_SAI0_DELAY_MASK (0x3 << 6)
#define ADAU1372_SAI0_SAI_I2S (0x0 << 4)
#define ADAU1372_SAI0_SAI_TDM2 (0x1 << 4)
#define ADAU1372_SAI0_SAI_TDM4 (0x2 << 4)
#define ADAU1372_SAI0_SAI_TDM8 (0x3 << 4)
#define ADAU1372_SAI0_SAI_MASK (0x3 << 4)
#define ADAU1372_SAI0_FS_48 0x0
#define ADAU1372_SAI0_FS_8 0x1
#define ADAU1372_SAI0_FS_12 0x2
#define ADAU1372_SAI0_FS_16 0x3
#define ADAU1372_SAI0_FS_24 0x4
#define ADAU1372_SAI0_FS_32 0x5
#define ADAU1372_SAI0_FS_96 0x6
#define ADAU1372_SAI0_FS_192 0x7
#define ADAU1372_SAI0_FS_MASK 0xf
#define ADAU1372_SAI1_TDM_TS BIT(7)
#define ADAU1372_SAI1_BCLK_TDMC BIT(6)
#define ADAU1372_SAI1_LR_MODE BIT(5)
#define ADAU1372_SAI1_LR_POL BIT(4)
#define ADAU1372_SAI1_BCLKRATE BIT(2)
#define ADAU1372_SAI1_BCLKEDGE BIT(1)
#define ADAU1372_SAI1_MS BIT(0)
static const unsigned int adau1372_rates[] = {
[ADAU1372_SAI0_FS_8] = 8000,
[ADAU1372_SAI0_FS_12] = 12000,
[ADAU1372_SAI0_FS_16] = 16000,
[ADAU1372_SAI0_FS_24] = 24000,
[ADAU1372_SAI0_FS_32] = 32000,
[ADAU1372_SAI0_FS_48] = 48000,
[ADAU1372_SAI0_FS_96] = 96000,
[ADAU1372_SAI0_FS_192] = 192000,
};
/* 8k, 12k, 24k, 48k */
#define ADAU1372_RATE_MASK_TDM8 0x17
/* + 16k, 96k */
#define ADAU1372_RATE_MASK_TDM4_MASTER (ADAU1372_RATE_MASK_TDM8 | 0x48 | 0x20)
/* +32k */
#define ADAU1372_RATE_MASK_TDM4 (ADAU1372_RATE_MASK_TDM4_MASTER | 0x20)
/* + 192k */
#define ADAU1372_RATE_MASK_TDM2 (ADAU1372_RATE_MASK_TDM4 | 0x80)
static const DECLARE_TLV_DB_MINMAX(adau1372_digital_tlv, -9563, 0);
static const DECLARE_TLV_DB_SCALE(adau1372_pga_tlv, -1200, 75, 0);
static const DECLARE_TLV_DB_SCALE(adau1372_pga_boost_tlv, 0, 1000, 0);
static const char * const adau1372_bias_text[] = {
"Normal operation", "Extreme power saving", "Enhanced performance",
"Power saving",
};
static const unsigned int adau1372_bias_adc_values[] = {
0, 2, 3,
};
static const char * const adau1372_bias_adc_text[] = {
"Normal operation", "Enhanced performance", "Power saving",
};
static const char * const adau1372_bias_dac_text[] = {
"Normal operation", "Power saving", "Superior performance",
"Enhanced performance",
};
static SOC_ENUM_SINGLE_DECL(adau1372_bias_hp_enum,
ADAU1372_REG_BIAS_CTRL0, 6, adau1372_bias_text);
static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe0_1_enum,
ADAU1372_REG_BIAS_CTRL0, 4, adau1372_bias_text);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc2_3_enum,
ADAU1372_REG_BIAS_CTRL0, 2, 0x3, adau1372_bias_adc_text,
adau1372_bias_adc_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc0_1_enum,
ADAU1372_REG_BIAS_CTRL0, 0, 0x3, adau1372_bias_adc_text,
adau1372_bias_adc_values);
static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe2_3_enum,
ADAU1372_REG_BIAS_CTRL1, 4, adau1372_bias_text);
static SOC_ENUM_SINGLE_DECL(adau1372_bias_mic_enum,
ADAU1372_REG_BIAS_CTRL1, 2, adau1372_bias_text);
static SOC_ENUM_SINGLE_DECL(adau1372_bias_dac_enum,
ADAU1372_REG_BIAS_CTRL1, 0, adau1372_bias_dac_text);
static const char * const adau1372_hpf_text[] = {
"Off",
"1 Hz",
"4 Hz",
"8 Hz",
};
static SOC_ENUM_SINGLE_DECL(adau1372_hpf0_1_enum, ADAU1372_REG_ADC_CTRL2, 5,
adau1372_hpf_text);
static SOC_ENUM_SINGLE_DECL(adau1372_hpf2_3_enum, ADAU1372_REG_ADC_CTRL3, 5,
adau1372_hpf_text);
static const struct snd_kcontrol_new adau1372_controls[] = {
SOC_SINGLE_TLV("ADC 0 Capture Volume", ADAU1372_REG_ADC_VOL(0),
0, 0xff, 1, adau1372_digital_tlv),
SOC_SINGLE_TLV("ADC 1 Capture Volume", ADAU1372_REG_ADC_VOL(1),
0, 0xff, 1, adau1372_digital_tlv),
SOC_SINGLE_TLV("ADC 2 Capture Volume", ADAU1372_REG_ADC_VOL(2),
0, 0xff, 1, adau1372_digital_tlv),
SOC_SINGLE_TLV("ADC 3 Capture Volume", ADAU1372_REG_ADC_VOL(3),
0, 0xff, 1, adau1372_digital_tlv),
SOC_SINGLE("ADC 0 Capture Switch", ADAU1372_REG_ADC_CTRL0, 3, 1, 1),
SOC_SINGLE("ADC 1 Capture Switch", ADAU1372_REG_ADC_CTRL0, 4, 1, 1),
SOC_SINGLE("ADC 2 Capture Switch", ADAU1372_REG_ADC_CTRL1, 3, 1, 1),
SOC_SINGLE("ADC 3 Capture Switch", ADAU1372_REG_ADC_CTRL1, 4, 1, 1),
SOC_ENUM("ADC 0+1 High-Pass-Filter", adau1372_hpf0_1_enum),
SOC_ENUM("ADC 2+3 High-Pass-Filter", adau1372_hpf2_3_enum),
SOC_SINGLE_TLV("PGA 0 Capture Volume", ADAU1372_REG_PGA_CTRL(0),
0, 0x3f, 0, adau1372_pga_tlv),
SOC_SINGLE_TLV("PGA 1 Capture Volume", ADAU1372_REG_PGA_CTRL(1),
0, 0x3f, 0, adau1372_pga_tlv),
SOC_SINGLE_TLV("PGA 2 Capture Volume", ADAU1372_REG_PGA_CTRL(2),
0, 0x3f, 0, adau1372_pga_tlv),
SOC_SINGLE_TLV("PGA 3 Capture Volume", ADAU1372_REG_PGA_CTRL(3),
0, 0x3f, 0, adau1372_pga_tlv),
SOC_SINGLE_TLV("PGA 0 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
0, 1, 0, adau1372_pga_boost_tlv),
SOC_SINGLE_TLV("PGA 1 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
1, 1, 0, adau1372_pga_boost_tlv),
SOC_SINGLE_TLV("PGA 2 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
2, 1, 0, adau1372_pga_boost_tlv),
SOC_SINGLE_TLV("PGA 3 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
3, 1, 0, adau1372_pga_boost_tlv),
SOC_SINGLE("PGA 0 Capture Switch", ADAU1372_REG_PGA_CTRL(0), 7, 1, 1),
SOC_SINGLE("PGA 1 Capture Switch", ADAU1372_REG_PGA_CTRL(1), 7, 1, 1),
SOC_SINGLE("PGA 2 Capture Switch", ADAU1372_REG_PGA_CTRL(2), 7, 1, 1),
SOC_SINGLE("PGA 3 Capture Switch", ADAU1372_REG_PGA_CTRL(3), 7, 1, 1),
SOC_SINGLE_TLV("DAC 0 Playback Volume", ADAU1372_REG_DAC_VOL(0),
0, 0xff, 1, adau1372_digital_tlv),
SOC_SINGLE_TLV("DAC 1 Playback Volume", ADAU1372_REG_DAC_VOL(1),
0, 0xff, 1, adau1372_digital_tlv),
SOC_SINGLE("DAC 0 Playback Switch", ADAU1372_REG_DAC_CTRL, 3, 1, 1),
SOC_SINGLE("DAC 1 Playback Switch", ADAU1372_REG_DAC_CTRL, 4, 1, 1),
SOC_ENUM("Headphone Bias", adau1372_bias_hp_enum),
SOC_ENUM("Microphone Bias", adau1372_bias_mic_enum),
SOC_ENUM("AFE 0+1 Bias", adau1372_bias_afe0_1_enum),
SOC_ENUM("AFE 2+3 Bias", adau1372_bias_afe2_3_enum),
SOC_ENUM("ADC 0+1 Bias", adau1372_bias_adc0_1_enum),
SOC_ENUM("ADC 2+3 Bias", adau1372_bias_adc2_3_enum),
SOC_ENUM("DAC 0+1 Bias", adau1372_bias_dac_enum),
};
static const char * const adau1372_decimator_mux_text[] = {
"ADC",
"DMIC",
};
static SOC_ENUM_SINGLE_DECL(adau1372_decimator0_1_mux_enum, ADAU1372_REG_ADC_CTRL2,
2, adau1372_decimator_mux_text);
static const struct snd_kcontrol_new adau1372_decimator0_1_mux_control =
SOC_DAPM_ENUM("Decimator 0+1 Capture Mux", adau1372_decimator0_1_mux_enum);
static SOC_ENUM_SINGLE_DECL(adau1372_decimator2_3_mux_enum, ADAU1372_REG_ADC_CTRL3,
2, adau1372_decimator_mux_text);
static const struct snd_kcontrol_new adau1372_decimator2_3_mux_control =
SOC_DAPM_ENUM("Decimator 2+3 Capture Mux", adau1372_decimator2_3_mux_enum);
static const unsigned int adau1372_asrco_mux_values[] = {
4, 5, 6, 7,
};
static const char * const adau1372_asrco_mux_text[] = {
"Decimator0",
"Decimator1",
"Decimator2",
"Decimator3",
};
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco0_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1,
0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco1_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1,
4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco2_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3,
0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco3_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3,
4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
static const struct snd_kcontrol_new adau1372_asrco0_mux_control =
SOC_DAPM_ENUM("Output ASRC0 Capture Mux", adau1372_asrco0_mux_enum);
static const struct snd_kcontrol_new adau1372_asrco1_mux_control =
SOC_DAPM_ENUM("Output ASRC1 Capture Mux", adau1372_asrco1_mux_enum);
static const struct snd_kcontrol_new adau1372_asrco2_mux_control =
SOC_DAPM_ENUM("Output ASRC2 Capture Mux", adau1372_asrco2_mux_enum);
static const struct snd_kcontrol_new adau1372_asrco3_mux_control =
SOC_DAPM_ENUM("Output ASRC3 Capture Mux", adau1372_asrco3_mux_enum);
static const unsigned int adau1372_sout_mux_values[] = {
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};
static const char * const adau1372_sout_mux_text[] = {
"Output ASRC0",
"Output ASRC1",
"Output ASRC2",
"Output ASRC3",
"Serial Input 0",
"Serial Input 1",
"Serial Input 2",
"Serial Input 3",
"Serial Input 4",
"Serial Input 5",
"Serial Input 6",
"Serial Input 7",
};
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout0_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1,
0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout1_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1,
4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout2_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3,
0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout3_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3,
4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout4_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5,
0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout5_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5,
4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout6_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7,
0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout7_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7,
4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
static const struct snd_kcontrol_new adau1372_sout0_mux_control =
SOC_DAPM_ENUM("Serial Output 0 Capture Mux", adau1372_sout0_mux_enum);
static const struct snd_kcontrol_new adau1372_sout1_mux_control =
SOC_DAPM_ENUM("Serial Output 1 Capture Mux", adau1372_sout1_mux_enum);
static const struct snd_kcontrol_new adau1372_sout2_mux_control =
SOC_DAPM_ENUM("Serial Output 2 Capture Mux", adau1372_sout2_mux_enum);
static const struct snd_kcontrol_new adau1372_sout3_mux_control =
SOC_DAPM_ENUM("Serial Output 3 Capture Mux", adau1372_sout3_mux_enum);
static const struct snd_kcontrol_new adau1372_sout4_mux_control =
SOC_DAPM_ENUM("Serial Output 4 Capture Mux", adau1372_sout4_mux_enum);
static const struct snd_kcontrol_new adau1372_sout5_mux_control =
SOC_DAPM_ENUM("Serial Output 5 Capture Mux", adau1372_sout5_mux_enum);
static const struct snd_kcontrol_new adau1372_sout6_mux_control =
SOC_DAPM_ENUM("Serial Output 6 Capture Mux", adau1372_sout6_mux_enum);
static const struct snd_kcontrol_new adau1372_sout7_mux_control =
SOC_DAPM_ENUM("Serial Output 7 Capture Mux", adau1372_sout7_mux_enum);
static const char * const adau1372_asrci_mux_text[] = {
"Serial Input 0+1",
"Serial Input 2+3",
"Serial Input 4+5",
"Serial Input 6+7",
};
static SOC_ENUM_SINGLE_DECL(adau1372_asrci_mux_enum,
ADAU1372_REG_ASRC_MODE, 2, adau1372_asrci_mux_text);
static const struct snd_kcontrol_new adau1372_asrci_mux_control =
SOC_DAPM_ENUM("Input ASRC Playback Mux", adau1372_asrci_mux_enum);
static const unsigned int adau1372_dac_mux_values[] = {
12, 13
};
static const char * const adau1372_dac_mux_text[] = {
"Input ASRC0",
"Input ASRC1",
};
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac0_mux_enum, ADAU1372_REG_DAC_SOURCE,
0, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac1_mux_enum, ADAU1372_REG_DAC_SOURCE,
4, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values);
static const struct snd_kcontrol_new adau1372_dac0_mux_control =
SOC_DAPM_ENUM("DAC 0 Playback Mux", adau1372_dac0_mux_enum);
static const struct snd_kcontrol_new adau1372_dac1_mux_control =
SOC_DAPM_ENUM("DAC 1 Playback Mux", adau1372_dac1_mux_enum);
static const struct snd_soc_dapm_widget adau1372_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN0"),
SND_SOC_DAPM_INPUT("AIN1"),
SND_SOC_DAPM_INPUT("AIN2"),
SND_SOC_DAPM_INPUT("AIN3"),
SND_SOC_DAPM_INPUT("DMIC0_1"),
SND_SOC_DAPM_INPUT("DMIC2_3"),
SND_SOC_DAPM_SUPPLY("MICBIAS0", ADAU1372_REG_MICBIAS, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1372_REG_MICBIAS, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("PGA0", ADAU1372_REG_PGA_CTRL(0), 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("PGA1", ADAU1372_REG_PGA_CTRL(1), 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("PGA2", ADAU1372_REG_PGA_CTRL(2), 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("PGA3", ADAU1372_REG_PGA_CTRL(3), 6, 0, NULL, 0),
SND_SOC_DAPM_ADC("ADC0", NULL, ADAU1372_REG_ADC_CTRL2, 0, 0),
SND_SOC_DAPM_ADC("ADC1", NULL, ADAU1372_REG_ADC_CTRL2, 1, 0),
SND_SOC_DAPM_ADC("ADC2", NULL, ADAU1372_REG_ADC_CTRL3, 0, 0),
SND_SOC_DAPM_ADC("ADC3", NULL, ADAU1372_REG_ADC_CTRL3, 1, 0),
SND_SOC_DAPM_SUPPLY("ADC0 Filter", ADAU1372_REG_DECIM_PWR, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC1 Filter", ADAU1372_REG_DECIM_PWR, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC2 Filter", ADAU1372_REG_DECIM_PWR, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC3 Filter", ADAU1372_REG_DECIM_PWR, 3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Output ASRC0 Decimator", ADAU1372_REG_DECIM_PWR, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Output ASRC1 Decimator", ADAU1372_REG_DECIM_PWR, 5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Output ASRC2 Decimator", ADAU1372_REG_DECIM_PWR, 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Output ASRC3 Decimator", ADAU1372_REG_DECIM_PWR, 7, 0, NULL, 0),
SND_SOC_DAPM_MUX("Decimator0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control),
SND_SOC_DAPM_MUX("Decimator1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control),
SND_SOC_DAPM_MUX("Decimator2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control),
SND_SOC_DAPM_MUX("Decimator3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control),
SND_SOC_DAPM_MUX("Output ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco0_mux_control),
SND_SOC_DAPM_MUX("Output ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco1_mux_control),
SND_SOC_DAPM_MUX("Output ASRC2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco2_mux_control),
SND_SOC_DAPM_MUX("Output ASRC3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco3_mux_control),
SND_SOC_DAPM_MUX("Serial Output 0 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout0_mux_control),
SND_SOC_DAPM_MUX("Serial Output 1 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout1_mux_control),
SND_SOC_DAPM_MUX("Serial Output 2 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout2_mux_control),
SND_SOC_DAPM_MUX("Serial Output 3 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout3_mux_control),
SND_SOC_DAPM_MUX("Serial Output 4 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout4_mux_control),
SND_SOC_DAPM_MUX("Serial Output 5 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout5_mux_control),
SND_SOC_DAPM_MUX("Serial Output 6 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout6_mux_control),
SND_SOC_DAPM_MUX("Serial Output 7 Capture Mux", SND_SOC_NOPM, 0, 0,
&adau1372_sout7_mux_control),
SND_SOC_DAPM_AIF_IN("Serial Input 0", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 1", NULL, 1, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 2", NULL, 2, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 3", NULL, 3, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 4", NULL, 4, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 5", NULL, 5, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 6", NULL, 6, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Serial Input 7", NULL, 7, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 0", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 1", NULL, 1, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 2", NULL, 2, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 3", NULL, 3, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 4", NULL, 4, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 5", NULL, 5, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 6", NULL, 6, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Serial Output 7", NULL, 7, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("Output ASRC Supply", ADAU1372_REG_ASRC_MODE, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Input ASRC Supply", ADAU1372_REG_ASRC_MODE, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC1 Modulator", ADAU1372_REG_INTERP_PWR, 3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC0 Modulator", ADAU1372_REG_INTERP_PWR, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Input ASRC1 Interpolator", ADAU1372_REG_INTERP_PWR, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Input ASRC0 Interpolator", ADAU1372_REG_INTERP_PWR, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Input ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control),
SND_SOC_DAPM_MUX("Input ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control),
SND_SOC_DAPM_MUX("DAC 0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac0_mux_control),
SND_SOC_DAPM_MUX("DAC 1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac1_mux_control),
SND_SOC_DAPM_DAC("DAC0", NULL, ADAU1372_REG_DAC_CTRL, 0, 0),
SND_SOC_DAPM_DAC("DAC1", NULL, ADAU1372_REG_DAC_CTRL, 1, 0),
SND_SOC_DAPM_OUT_DRV("OP_STAGE_LP", ADAU1372_REG_OP_STAGE_CTRL, 0, 1, NULL, 0),
SND_SOC_DAPM_OUT_DRV("OP_STAGE_LN", ADAU1372_REG_OP_STAGE_CTRL, 1, 1, NULL, 0),
SND_SOC_DAPM_OUT_DRV("OP_STAGE_RP", ADAU1372_REG_OP_STAGE_CTRL, 2, 1, NULL, 0),
SND_SOC_DAPM_OUT_DRV("OP_STAGE_RN", ADAU1372_REG_OP_STAGE_CTRL, 3, 1, NULL, 0),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
};
#define ADAU1372_SOUT_ROUTES(x) \
{ "Serial Output " #x " Capture Mux", "Output ASRC0", "Output ASRC0 Mux" }, \
{ "Serial Output " #x " Capture Mux", "Output ASRC1", "Output ASRC1 Mux" }, \
{ "Serial Output " #x " Capture Mux", "Output ASRC2", "Output ASRC2 Mux" }, \
{ "Serial Output " #x " Capture Mux", "Output ASRC3", "Output ASRC3 Mux" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 0", "Serial Input 0" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 1", "Serial Input 1" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 2", "Serial Input 2" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 3", "Serial Input 3" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 4", "Serial Input 4" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 5", "Serial Input 5" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 6", "Serial Input 6" }, \
{ "Serial Output " #x " Capture Mux", "Serial Input 7", "Serial Input 7" }, \
{ "Serial Output " #x, NULL, "Serial Output " #x " Capture Mux" }, \
{ "Capture", NULL, "Serial Output " #x }
#define ADAU1372_ASRCO_ROUTES(x) \
{ "Output ASRC" #x " Mux", "Decimator0", "Decimator0 Mux" }, \
{ "Output ASRC" #x " Mux", "Decimator1", "Decimator1 Mux" }, \
{ "Output ASRC" #x " Mux", "Decimator2", "Decimator2 Mux" }, \
{ "Output ASRC" #x " Mux", "Decimator3", "Decimator3 Mux" }
static const struct snd_soc_dapm_route adau1372_dapm_routes[] = {
{ "PGA0", NULL, "AIN0" },
{ "PGA1", NULL, "AIN1" },
{ "PGA2", NULL, "AIN2" },
{ "PGA3", NULL, "AIN3" },
{ "ADC0", NULL, "PGA0" },
{ "ADC1", NULL, "PGA1" },
{ "ADC2", NULL, "PGA2" },
{ "ADC3", NULL, "PGA3" },
{ "Decimator0 Mux", "ADC", "ADC0" },
{ "Decimator1 Mux", "ADC", "ADC1" },
{ "Decimator2 Mux", "ADC", "ADC2" },
{ "Decimator3 Mux", "ADC", "ADC3" },
{ "Decimator0 Mux", "DMIC", "DMIC0_1" },
{ "Decimator1 Mux", "DMIC", "DMIC0_1" },
{ "Decimator2 Mux", "DMIC", "DMIC2_3" },
{ "Decimator3 Mux", "DMIC", "DMIC2_3" },
{ "Decimator0 Mux", NULL, "ADC0 Filter" },
{ "Decimator1 Mux", NULL, "ADC1 Filter" },
{ "Decimator2 Mux", NULL, "ADC2 Filter" },
{ "Decimator3 Mux", NULL, "ADC3 Filter" },
{ "Output ASRC0 Mux", NULL, "Output ASRC Supply" },
{ "Output ASRC1 Mux", NULL, "Output ASRC Supply" },
{ "Output ASRC2 Mux", NULL, "Output ASRC Supply" },
{ "Output ASRC3 Mux", NULL, "Output ASRC Supply" },
{ "Output ASRC0 Mux", NULL, "Output ASRC0 Decimator" },
{ "Output ASRC1 Mux", NULL, "Output ASRC1 Decimator" },
{ "Output ASRC2 Mux", NULL, "Output ASRC2 Decimator" },
{ "Output ASRC3 Mux", NULL, "Output ASRC3 Decimator" },
ADAU1372_ASRCO_ROUTES(0),
ADAU1372_ASRCO_ROUTES(1),
ADAU1372_ASRCO_ROUTES(2),
ADAU1372_ASRCO_ROUTES(3),
ADAU1372_SOUT_ROUTES(0),
ADAU1372_SOUT_ROUTES(1),
ADAU1372_SOUT_ROUTES(2),
ADAU1372_SOUT_ROUTES(3),
ADAU1372_SOUT_ROUTES(4),
ADAU1372_SOUT_ROUTES(5),
ADAU1372_SOUT_ROUTES(6),
ADAU1372_SOUT_ROUTES(7),
{ "Serial Input 0", NULL, "Playback" },
{ "Serial Input 1", NULL, "Playback" },
{ "Serial Input 2", NULL, "Playback" },
{ "Serial Input 3", NULL, "Playback" },
{ "Serial Input 4", NULL, "Playback" },
{ "Serial Input 5", NULL, "Playback" },
{ "Serial Input 6", NULL, "Playback" },
{ "Serial Input 7", NULL, "Playback" },
{ "Input ASRC0 Mux", "Serial Input 0+1", "Serial Input 0" },
{ "Input ASRC1 Mux", "Serial Input 0+1", "Serial Input 1" },
{ "Input ASRC0 Mux", "Serial Input 2+3", "Serial Input 2" },
{ "Input ASRC1 Mux", "Serial Input 2+3", "Serial Input 3" },
{ "Input ASRC0 Mux", "Serial Input 4+5", "Serial Input 4" },
{ "Input ASRC1 Mux", "Serial Input 4+5", "Serial Input 5" },
{ "Input ASRC0 Mux", "Serial Input 6+7", "Serial Input 6" },
{ "Input ASRC1 Mux", "Serial Input 6+7", "Serial Input 7" },
{ "Input ASRC0 Mux", NULL, "Input ASRC Supply" },
{ "Input ASRC1 Mux", NULL, "Input ASRC Supply" },
{ "Input ASRC0 Mux", NULL, "Input ASRC0 Interpolator" },
{ "Input ASRC1 Mux", NULL, "Input ASRC1 Interpolator" },
{ "DAC 0 Mux", "Input ASRC0", "Input ASRC0 Mux" },
{ "DAC 0 Mux", "Input ASRC1", "Input ASRC1 Mux" },
{ "DAC 1 Mux", "Input ASRC0", "Input ASRC0 Mux" },
{ "DAC 1 Mux", "Input ASRC1", "Input ASRC1 Mux" },
{ "DAC0", NULL, "DAC 0 Mux" },
{ "DAC1", NULL, "DAC 1 Mux" },
{ "DAC0", NULL, "DAC0 Modulator" },
{ "DAC1", NULL, "DAC1 Modulator" },
{ "OP_STAGE_LP", NULL, "DAC0" },
{ "OP_STAGE_LN", NULL, "DAC0" },
{ "OP_STAGE_RP", NULL, "DAC1" },
{ "OP_STAGE_RN", NULL, "DAC1" },
{ "HPOUTL", NULL, "OP_STAGE_LP" },
{ "HPOUTL", NULL, "OP_STAGE_LN" },
{ "HPOUTR", NULL, "OP_STAGE_RP" },
{ "HPOUTR", NULL, "OP_STAGE_RN" },
};
static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
unsigned int sai0 = 0, sai1 = 0;
bool invert_lrclk = false;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
adau1372->master = true;
sai1 |= ADAU1372_SAI1_MS;
break;
case SND_SOC_DAIFMT_CBS_CFS:
adau1372->master = false;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
invert_lrclk = false;
break;
case SND_SOC_DAIFMT_NB_IF:
invert_lrclk = true;
break;
case SND_SOC_DAIFMT_IB_NF:
invert_lrclk = false;
sai1 |= ADAU1372_SAI1_BCLKEDGE;
break;
case SND_SOC_DAIFMT_IB_IF:
invert_lrclk = true;
sai1 |= ADAU1372_SAI1_BCLKEDGE;
break;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
sai0 |= ADAU1372_SAI0_DELAY1;
break;
case SND_SOC_DAIFMT_LEFT_J:
sai0 |= ADAU1372_SAI0_DELAY0;
invert_lrclk = !invert_lrclk;
break;
case SND_SOC_DAIFMT_DSP_A:
sai0 |= ADAU1372_SAI0_DELAY1;
sai1 |= ADAU1372_SAI1_LR_MODE;
break;
case SND_SOC_DAIFMT_DSP_B:
sai0 |= ADAU1372_SAI0_DELAY0;
sai1 |= ADAU1372_SAI1_LR_MODE;
break;
}
if (invert_lrclk)
sai1 |= ADAU1372_SAI1_LR_POL;
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_DELAY_MASK, sai0);
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1,
ADAU1372_SAI1_MS | ADAU1372_SAI1_BCLKEDGE |
ADAU1372_SAI1_LR_MODE | ADAU1372_SAI1_LR_POL, sai1);
return 0;
}
static int adau1372_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
unsigned int rate = params_rate(params);
unsigned int slot_width;
unsigned int sai0, sai1;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(adau1372_rates); i++) {
if (rate == adau1372_rates[i])
break;
}
if (i == ARRAY_SIZE(adau1372_rates))
return -EINVAL;
sai0 = i;
slot_width = adau1372->slot_width;
if (slot_width == 0)
slot_width = params_width(params);
switch (slot_width) {
case 16:
sai1 = ADAU1372_SAI1_BCLKRATE;
break;
case 32:
sai1 = 0;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_FS_MASK, sai0);
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLKRATE, sai1);
return 0;
}
static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
unsigned int sai0, sai1;
/* I2S mode */
if (slots == 0) {
/* The other settings dont matter in I2S mode */
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0,
ADAU1372_SAI0_SAI_MASK, ADAU1372_SAI0_SAI_I2S);
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
adau1372->slot_width = 0;
return 0;
}
/* We have 8 channels anything outside that is not supported */
if ((tx_mask & ~0xff) != 0 || (rx_mask & ~0xff) != 0)
return -EINVAL;
switch (width) {
case 16:
sai1 = ADAU1372_SAI1_BCLK_TDMC;
break;
case 32:
sai1 = 0;
break;
default:
return -EINVAL;
}
switch (slots) {
case 2:
sai0 = ADAU1372_SAI0_SAI_TDM2;
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
break;
case 4:
sai0 = ADAU1372_SAI0_SAI_TDM4;
if (adau1372->master)
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER;
else
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4;
break;
case 8:
sai0 = ADAU1372_SAI0_SAI_TDM8;
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM8;
break;
default:
return -EINVAL;
}
adau1372->slot_width = width;
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_SAI_MASK, sai0);
regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLK_TDMC, sai1);
/* Mask is inverted in hardware */
regmap_write(adau1372->regmap, ADAU1372_REG_SOUT_CTRL, ~tx_mask);
return 0;
}
static int adau1372_set_tristate(struct snd_soc_dai *dai, int tristate)
{
struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
unsigned int sai1;
if (tristate)
sai1 = ADAU1372_SAI1_TDM_TS;
else
sai1 = 0;
return regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_TDM_TS, sai1);
}
static int adau1372_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&adau1372->rate_constraints);
return 0;
}
static void adau1372_enable_pll(struct adau1372 *adau1372)
{
unsigned int val, timeout = 0;
int ret;
regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
ADAU1372_CLK_CTRL_PLL_EN, ADAU1372_CLK_CTRL_PLL_EN);
do {
/* Takes about 1ms to lock */
usleep_range(1000, 2000);
ret = regmap_read(adau1372->regmap, ADAU1372_REG_PLL(5), &val);
if (ret)
break;
timeout++;
} while (!(val & 1) && timeout < 3);
if (ret < 0 || !(val & 1))
dev_err(adau1372->dev, "Failed to lock PLL\n");
}
static void adau1372_set_power(struct adau1372 *adau1372, bool enable)
{
if (adau1372->enabled == enable)
return;
if (enable) {
unsigned int clk_ctrl = ADAU1372_CLK_CTRL_MCLK_EN;
clk_prepare_enable(adau1372->mclk);
if (adau1372->pd_gpio)
gpiod_set_value(adau1372->pd_gpio, 0);
if (adau1372->switch_mode)
adau1372->switch_mode(adau1372->dev);
regcache_cache_only(adau1372->regmap, false);
/*
* Clocks needs to be enabled before any other register can be
* accessed.
*/
if (adau1372->use_pll) {
adau1372_enable_pll(adau1372);
clk_ctrl |= ADAU1372_CLK_CTRL_CLKSRC;
}
regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_CLKSRC, clk_ctrl);
regcache_sync(adau1372->regmap);
} else {
if (adau1372->pd_gpio) {
/*
* This will turn everything off and reset the register
* map. No need to do any register writes to manually
* turn things off.
*/
gpiod_set_value(adau1372->pd_gpio, 1);
regcache_mark_dirty(adau1372->regmap);
} else {
regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_PLL_EN, 0);
}
clk_disable_unprepare(adau1372->mclk);
regcache_cache_only(adau1372->regmap, true);
}
adau1372->enabled = enable;
}
static int adau1372_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct adau1372 *adau1372 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
adau1372_set_power(adau1372, true);
break;
case SND_SOC_BIAS_OFF:
adau1372_set_power(adau1372, false);
break;
}
return 0;
}
static const struct snd_soc_component_driver adau1372_driver = {
.set_bias_level = adau1372_set_bias_level,
.controls = adau1372_controls,
.num_controls = ARRAY_SIZE(adau1372_controls),
.dapm_widgets = adau1372_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets),
.dapm_routes = adau1372_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes),
};
static const struct snd_soc_dai_ops adau1372_dai_ops = {
.set_fmt = adau1372_set_dai_fmt,
.set_tdm_slot = adau1372_set_tdm_slot,
.set_tristate = adau1372_set_tristate,
.hw_params = adau1372_hw_params,
.startup = adau1372_startup,
};
#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver adau1372_dai_driver = {
.name = "adau1372",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = ADAU1372_FORMATS,
.sig_bits = 24,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = ADAU1372_FORMATS,
.sig_bits = 24,
},
.ops = &adau1372_dai_ops,
.symmetric_rates = 1,
};
static int adau1372_setup_pll(struct adau1372 *adau1372, unsigned int rate)
{
u8 regs[5];
unsigned int i;
int ret;
ret = adau_calc_pll_cfg(rate, 49152000, regs);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(regs); i++)
regmap_write(adau1372->regmap, ADAU1372_REG_PLL(i), regs[i]);
return 0;
}
int adau1372_probe(struct device *dev, struct regmap *regmap,
void (*switch_mode)(struct device *dev))
{
struct adau1372 *adau1372;
unsigned int clk_ctrl;
unsigned long rate;
int ret;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
adau1372 = devm_kzalloc(dev, sizeof(*adau1372), GFP_KERNEL);
if (!adau1372)
return -ENOMEM;
adau1372->clk = devm_clk_get(dev, "mclk");
if (IS_ERR(adau1372->clk))
return PTR_ERR(adau1372->clk);
adau1372->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH);
if (IS_ERR(adau1372->pd_gpio))
return PTR_ERR(adau1372->pd_gpio);
adau1372->regmap = regmap;
adau1372->switch_mode = switch_mode;
adau1372->dev = dev;
adau1372->rate_constraints.list = adau1372_rates;
adau1372->rate_constraints.count = ARRAY_SIZE(adau1372_rates);
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
dev_set_drvdata(dev, adau1372);
/*
* The datasheet says that the internal MCLK always needs to run at
* 12.288MHz. Automatically choose a valid configuration from the
* external clock.
*/
rate = clk_get_rate(adau1372->clk);
switch (rate) {
case 12288000:
clk_ctrl = ADAU1372_CLK_CTRL_CC_MDIV;
break;
case 24576000:
clk_ctrl = 0;
break;
default:
clk_ctrl = 0;
ret = adau1372_setup_pll(adau1372, rate);
if (ret < 0)
return ret;
adau1372->use_pll = true;
break;
}
/*
* Most of the registers are inaccessible unless the internal clock is
* enabled.
*/
regcache_cache_only(regmap, true);
regmap_update_bits(regmap, ADAU1372_REG_CLK_CTRL, ADAU1372_CLK_CTRL_CC_MDIV, clk_ctrl);
/*
* No pinctrl support yet, put the multi-purpose pins in the most
* sensible mode for general purpose CODEC operation.
*/
regmap_write(regmap, ADAU1372_REG_MODE_MP(1), 0x00); /* SDATA OUT */
regmap_write(regmap, ADAU1372_REG_MODE_MP(6), 0x12); /* CLOCKOUT */
regmap_write(regmap, ADAU1372_REG_OP_STAGE_MUTE, 0x0);
regmap_write(regmap, 0x7, 0x01); /* CLOCK OUT */
return devm_snd_soc_register_component(dev, &adau1372_driver, &adau1372_dai_driver, 1);
}
EXPORT_SYMBOL(adau1372_probe);
static const struct reg_default adau1372_reg_defaults[] = {
{ ADAU1372_REG_CLK_CTRL, 0x00 },
{ ADAU1372_REG_PLL(0), 0x00 },
{ ADAU1372_REG_PLL(1), 0x00 },
{ ADAU1372_REG_PLL(2), 0x00 },
{ ADAU1372_REG_PLL(3), 0x00 },
{ ADAU1372_REG_PLL(4), 0x00 },
{ ADAU1372_REG_PLL(5), 0x00 },
{ ADAU1372_REG_DAC_SOURCE, 0x10 },
{ ADAU1372_REG_SOUT_SOURCE_0_1, 0x54 },
{ ADAU1372_REG_SOUT_SOURCE_2_3, 0x76 },
{ ADAU1372_REG_SOUT_SOURCE_4_5, 0x54 },
{ ADAU1372_REG_SOUT_SOURCE_6_7, 0x76 },
{ ADAU1372_REG_ADC_SDATA_CH, 0x04 },
{ ADAU1372_REG_ASRCO_SOURCE_0_1, 0x10 },
{ ADAU1372_REG_ASRCO_SOURCE_2_3, 0x32 },
{ ADAU1372_REG_ASRC_MODE, 0x00 },
{ ADAU1372_REG_ADC_CTRL0, 0x19 },
{ ADAU1372_REG_ADC_CTRL1, 0x19 },
{ ADAU1372_REG_ADC_CTRL2, 0x00 },
{ ADAU1372_REG_ADC_CTRL3, 0x00 },
{ ADAU1372_REG_ADC_VOL(0), 0x00 },
{ ADAU1372_REG_ADC_VOL(1), 0x00 },
{ ADAU1372_REG_ADC_VOL(2), 0x00 },
{ ADAU1372_REG_ADC_VOL(3), 0x00 },
{ ADAU1372_REG_PGA_CTRL(0), 0x40 },
{ ADAU1372_REG_PGA_CTRL(1), 0x40 },
{ ADAU1372_REG_PGA_CTRL(2), 0x40 },
{ ADAU1372_REG_PGA_CTRL(3), 0x40 },
{ ADAU1372_REG_PGA_BOOST, 0x00 },
{ ADAU1372_REG_MICBIAS, 0x00 },
{ ADAU1372_REG_DAC_CTRL, 0x18 },
{ ADAU1372_REG_DAC_VOL(0), 0x00 },
{ ADAU1372_REG_DAC_VOL(1), 0x00 },
{ ADAU1372_REG_OP_STAGE_MUTE, 0x0f },
{ ADAU1372_REG_SAI0, 0x00 },
{ ADAU1372_REG_SAI1, 0x00 },
{ ADAU1372_REG_SOUT_CTRL, 0x00 },
{ ADAU1372_REG_MODE_MP(0), 0x00 },
{ ADAU1372_REG_MODE_MP(1), 0x10 },
{ ADAU1372_REG_MODE_MP(4), 0x00 },
{ ADAU1372_REG_MODE_MP(5), 0x00 },
{ ADAU1372_REG_MODE_MP(6), 0x11 },
{ ADAU1372_REG_OP_STAGE_CTRL, 0x0f },
{ ADAU1372_REG_DECIM_PWR, 0x00 },
{ ADAU1372_REG_INTERP_PWR, 0x00 },
{ ADAU1372_REG_BIAS_CTRL0, 0x00 },
{ ADAU1372_REG_BIAS_CTRL1, 0x00 },
};
static bool adau1372_volatile_register(struct device *dev, unsigned int reg)
{
if (reg == ADAU1372_REG_PLL(5))
return true;
return false;
}
const struct regmap_config adau1372_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.max_register = 0x4d,
.reg_defaults = adau1372_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults),
.volatile_reg = adau1372_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(adau1372_regmap_config);
MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1372 driver
*
* Copyright 2016 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#ifndef SOUND_SOC_CODECS_ADAU1372_H
#define SOUND_SOC_CODECS_ADAU1372_H
#include <linux/regmap.h>
struct device;
int adau1372_probe(struct device *dev, struct regmap *regmap,
void (*switch_mode)(struct device *dev));
extern const struct regmap_config adau1372_regmap_config;
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment