Merge remote-tracking branch 'asoc/topic/tas2552' into asoc-next
This commit is contained in:
Коммит
e39f6bc7de
|
@ -14,6 +14,12 @@ Required properties:
|
|||
Optional properties:
|
||||
- enable-gpio - gpio pin to enable/disable the device
|
||||
|
||||
tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the
|
||||
internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
|
||||
reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
|
||||
For system integration the dt-bindings/sound/tas2552.h header file provides
|
||||
defined values to selct and configure the PLL and PDM reference clocks.
|
||||
|
||||
Example:
|
||||
|
||||
tas2552: tas2552@41 {
|
||||
|
|
|
@ -45,7 +45,7 @@ static struct reg_default tas2552_reg_defs[] = {
|
|||
{TAS2552_OUTPUT_DATA, 0xc0},
|
||||
{TAS2552_PDM_CFG, 0x01},
|
||||
{TAS2552_PGA_GAIN, 0x00},
|
||||
{TAS2552_BOOST_PT_CTRL, 0x0f},
|
||||
{TAS2552_BOOST_APT_CTRL, 0x0f},
|
||||
{TAS2552_RESERVED_0D, 0xbe},
|
||||
{TAS2552_LIMIT_RATE_HYS, 0x08},
|
||||
{TAS2552_CFG_2, 0xef},
|
||||
|
@ -77,7 +77,9 @@ struct tas2552_data {
|
|||
struct gpio_desc *enable_gpio;
|
||||
unsigned char regs[TAS2552_VBAT_DATA];
|
||||
unsigned int pll_clkin;
|
||||
int pll_clk_id;
|
||||
unsigned int pdm_clk;
|
||||
int pdm_clk_id;
|
||||
|
||||
unsigned int dai_fmt;
|
||||
unsigned int tdm_delay;
|
||||
|
@ -143,31 +145,105 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
|
|||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
|
||||
static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
|
||||
{
|
||||
u8 cfg1_reg = 0;
|
||||
|
||||
if (!tas_data->codec)
|
||||
if (!tas2552->codec)
|
||||
return;
|
||||
|
||||
if (sw_shutdown)
|
||||
cfg1_reg = TAS2552_SWS;
|
||||
|
||||
snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS,
|
||||
snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS,
|
||||
cfg1_reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tas2552_setup_pll(struct snd_soc_codec *codec,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
|
||||
bool bypass_pll = false;
|
||||
unsigned int pll_clk = params_rate(params) * 512;
|
||||
unsigned int pll_clkin = tas2552->pll_clkin;
|
||||
u8 pll_enable;
|
||||
|
||||
if (!pll_clkin) {
|
||||
if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
|
||||
return -EINVAL;
|
||||
|
||||
pll_clkin = snd_soc_params_to_bclk(params);
|
||||
pll_clkin += tas2552->tdm_delay;
|
||||
}
|
||||
|
||||
pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
|
||||
|
||||
if (pll_clkin == pll_clk)
|
||||
bypass_pll = true;
|
||||
|
||||
if (bypass_pll) {
|
||||
/* By pass the PLL configuration */
|
||||
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
|
||||
TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
|
||||
} else {
|
||||
/* Fill in the PLL control registers for J & D
|
||||
* pll_clk = (.5 * pll_clkin * J.D) / 2^p
|
||||
* Need to fill in J and D here based on incoming freq
|
||||
*/
|
||||
unsigned int d;
|
||||
u8 j;
|
||||
u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
|
||||
u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
|
||||
|
||||
p = (p >> 7);
|
||||
|
||||
recalc:
|
||||
j = (pll_clk * 2 * (1 << p)) / pll_clkin;
|
||||
d = (pll_clk * 2 * (1 << p)) % pll_clkin;
|
||||
d /= (pll_clkin / 10000);
|
||||
|
||||
if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
|
||||
if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
|
||||
pll_clkin = 1800000;
|
||||
pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
|
||||
TAS2552_PLL_SRC_MASK;
|
||||
} else {
|
||||
pll_clkin = snd_soc_params_to_bclk(params);
|
||||
pll_clkin += tas2552->tdm_delay;
|
||||
pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
|
||||
TAS2552_PLL_SRC_MASK;
|
||||
}
|
||||
goto recalc;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
|
||||
pll_sel);
|
||||
|
||||
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
|
||||
TAS2552_PLL_J_MASK, j);
|
||||
/* Will clear the PLL_BYPASS bit */
|
||||
snd_soc_write(codec, TAS2552_PLL_CTRL_2,
|
||||
TAS2552_PLL_D_UPPER(d));
|
||||
snd_soc_write(codec, TAS2552_PLL_CTRL_3,
|
||||
TAS2552_PLL_D_LOWER(d));
|
||||
}
|
||||
|
||||
/* Restore PLL status */
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
|
||||
pll_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2552_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
|
||||
int sample_rate, pll_clk;
|
||||
int d;
|
||||
int cpf;
|
||||
u8 p, j;
|
||||
u8 ser_ctrl1_reg, wclk_rate;
|
||||
|
||||
switch (params_width(params)) {
|
||||
|
@ -245,49 +321,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
|
|||
snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
|
||||
wclk_rate);
|
||||
|
||||
if (!tas2552->pll_clkin)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
|
||||
|
||||
if (tas2552->pll_clkin == TAS2552_245MHZ_CLK ||
|
||||
tas2552->pll_clkin == TAS2552_225MHZ_CLK) {
|
||||
/* By pass the PLL configuration */
|
||||
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
|
||||
TAS2552_PLL_BYPASS_MASK,
|
||||
TAS2552_PLL_BYPASS);
|
||||
} else {
|
||||
/* Fill in the PLL control registers for J & D
|
||||
* PLL_CLK = (.5 * freq * J.D) / 2^p
|
||||
* Need to fill in J and D here based on incoming freq
|
||||
*/
|
||||
p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
|
||||
p = (p >> 7);
|
||||
sample_rate = params_rate(params);
|
||||
|
||||
if (sample_rate == 48000)
|
||||
pll_clk = TAS2552_245MHZ_CLK;
|
||||
else if (sample_rate == 44100)
|
||||
pll_clk = TAS2552_225MHZ_CLK;
|
||||
else {
|
||||
dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
|
||||
params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin;
|
||||
d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin;
|
||||
|
||||
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
|
||||
TAS2552_PLL_J_MASK, j);
|
||||
snd_soc_write(codec, TAS2552_PLL_CTRL_2,
|
||||
(d >> 7) & TAS2552_PLL_D_UPPER_MASK);
|
||||
snd_soc_write(codec, TAS2552_PLL_CTRL_3,
|
||||
d & TAS2552_PLL_D_LOWER_MASK);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
return tas2552_setup_pll(codec, params);
|
||||
}
|
||||
|
||||
#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
|
||||
|
@ -370,12 +404,21 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
|||
|
||||
switch (clk_id) {
|
||||
case TAS2552_PLL_CLKIN_MCLK:
|
||||
case TAS2552_PLL_CLKIN_BCLK:
|
||||
case TAS2552_PLL_CLKIN_IVCLKIN:
|
||||
if (freq < 512000 || freq > 24576000) {
|
||||
/* out of range PLL_CLKIN, fall back to use BCLK */
|
||||
dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n",
|
||||
freq);
|
||||
clk_id = TAS2552_PLL_CLKIN_BCLK;
|
||||
freq = 0;
|
||||
}
|
||||
/* fall through */
|
||||
case TAS2552_PLL_CLKIN_BCLK:
|
||||
case TAS2552_PLL_CLKIN_1_8_FIXED:
|
||||
mask = TAS2552_PLL_SRC_MASK;
|
||||
val = (clk_id << 3) & mask; /* bit 4:5 in the register */
|
||||
reg = TAS2552_CFG_1;
|
||||
tas2552->pll_clk_id = clk_id;
|
||||
tas2552->pll_clkin = freq;
|
||||
break;
|
||||
case TAS2552_PDM_CLK_PLL:
|
||||
|
@ -385,6 +428,7 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
|||
mask = TAS2552_PDM_CLK_SEL_MASK;
|
||||
val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
|
||||
reg = TAS2552_PDM_CFG;
|
||||
tas2552->pdm_clk_id = clk_id;
|
||||
tas2552->pdm_clk = freq;
|
||||
break;
|
||||
default:
|
||||
|
@ -509,9 +553,20 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
|
|||
*/
|
||||
static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
|
||||
|
||||
static const char * const tas2552_din_source_select[] = {
|
||||
"Muted",
|
||||
"Left",
|
||||
"Right",
|
||||
"Left + Right average",
|
||||
};
|
||||
static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
|
||||
TAS2552_CFG_3, 3,
|
||||
tas2552_din_source_select);
|
||||
|
||||
static const struct snd_kcontrol_new tas2552_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Speaker Driver Playback Volume",
|
||||
TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
|
||||
SOC_ENUM("DIN source", tas2552_din_source_enum),
|
||||
};
|
||||
|
||||
static int tas2552_codec_probe(struct snd_soc_codec *codec)
|
||||
|
@ -543,13 +598,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
|
|||
snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
|
||||
snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
|
||||
TAS2552_DIN_SRC_SEL_AVG_L_R);
|
||||
snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
|
||||
snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
|
||||
snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
|
||||
TAS2552_APT_THRESH_2_1_7);
|
||||
snd_soc_write(codec, TAS2552_OUTPUT_DATA,
|
||||
TAS2552_PDM_DATA_SEL_V_I |
|
||||
TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
|
||||
snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
|
||||
TAS2552_APT_THRESH_20_17);
|
||||
|
||||
snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
|
||||
TAS2552_APT_EN | TAS2552_LIM_EN);
|
||||
snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
|
||||
TAS2552_LIM_EN);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -647,13 +703,10 @@ static int tas2552_probe(struct i2c_client *client,
|
|||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->enable_gpio)) {
|
||||
if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
data->enable_gpio = NULL;;
|
||||
}
|
||||
data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->enable_gpio))
|
||||
return PTR_ERR(data->enable_gpio);
|
||||
|
||||
data->tas2552_client = client;
|
||||
data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
|
||||
|
@ -695,6 +748,7 @@ static int tas2552_probe(struct i2c_client *client,
|
|||
static int tas2552_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
pm_runtime_disable(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#define __TAS2552_H__
|
||||
|
||||
/* Register Address Map */
|
||||
#define TAS2552_DEVICE_STATUS 0x00
|
||||
#define TAS2552_DEVICE_STATUS 0x00
|
||||
#define TAS2552_CFG_1 0x01
|
||||
#define TAS2552_CFG_2 0x02
|
||||
#define TAS2552_CFG_3 0x03
|
||||
|
@ -33,13 +33,13 @@
|
|||
#define TAS2552_BTIP 0x0b
|
||||
#define TAS2552_BTS_CTRL 0x0c
|
||||
#define TAS2552_RESERVED_0D 0x0d
|
||||
#define TAS2552_LIMIT_RATE_HYS 0x0e
|
||||
#define TAS2552_LIMIT_RELEASE 0x0f
|
||||
#define TAS2552_LIMIT_INT_COUNT 0x10
|
||||
#define TAS2552_LIMIT_RATE_HYS 0x0e
|
||||
#define TAS2552_LIMIT_RELEASE 0x0f
|
||||
#define TAS2552_LIMIT_INT_COUNT 0x10
|
||||
#define TAS2552_PDM_CFG 0x11
|
||||
#define TAS2552_PGA_GAIN 0x12
|
||||
#define TAS2552_EDGE_RATE_CTRL 0x13
|
||||
#define TAS2552_BOOST_PT_CTRL 0x14
|
||||
#define TAS2552_EDGE_RATE_CTRL 0x13
|
||||
#define TAS2552_BOOST_APT_CTRL 0x14
|
||||
#define TAS2552_VER_NUM 0x16
|
||||
#define TAS2552_VBAT_DATA 0x19
|
||||
#define TAS2552_MAX_REG 0x20
|
||||
|
@ -103,10 +103,21 @@
|
|||
#define TAS2552_WCLKDIR (1 << 7)
|
||||
|
||||
/* OUTPUT_DATA register */
|
||||
#define TAS2552_PDM_DATA_I 0x00
|
||||
#define TAS2552_PDM_DATA_V (1 << 6)
|
||||
#define TAS2552_PDM_DATA_I_V (1 << 7)
|
||||
#define TAS2552_PDM_DATA_V_I (0x11 << 6)
|
||||
#define TAS2552_DATA_OUT_I_DATA (0x0)
|
||||
#define TAS2552_DATA_OUT_V_DATA (0x1)
|
||||
#define TAS2552_DATA_OUT_VBAT_DATA (0x2)
|
||||
#define TAS2552_DATA_OUT_VBOOST_DATA (0x3)
|
||||
#define TAS2552_DATA_OUT_PGA_GAIN (0x4)
|
||||
#define TAS2552_DATA_OUT_IV_DATA (0x5)
|
||||
#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN (0x6)
|
||||
#define TAS2552_DATA_OUT_DISABLED (0x7)
|
||||
#define TAS2552_L_DATA_OUT(x) ((x) << 0)
|
||||
#define TAS2552_R_DATA_OUT(x) ((x) << 3)
|
||||
#define TAS2552_PDM_DATA_SEL_I (0x0 << 6)
|
||||
#define TAS2552_PDM_DATA_SEL_V (0x1 << 6)
|
||||
#define TAS2552_PDM_DATA_SEL_I_V (0x2 << 6)
|
||||
#define TAS2552_PDM_DATA_SEL_V_I (0x3 << 6)
|
||||
#define TAS2552_PDM_DATA_SEL_MASK TAS2552_PDM_DATA_SEL_V_I
|
||||
|
||||
/* PDM CFG Register */
|
||||
#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0)
|
||||
|
@ -116,24 +127,20 @@
|
|||
#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK
|
||||
#define TAS2552_PDM_DATA_ES (1 << 2)
|
||||
|
||||
/* Boost pass-through register */
|
||||
#define TAS2552_APT_DELAY_50 0x00
|
||||
#define TAS2552_APT_DELAY_75 (1 << 1)
|
||||
#define TAS2552_APT_DELAY_125 (1 << 2)
|
||||
#define TAS2552_APT_DELAY_200 (1 << 3)
|
||||
|
||||
#define TAS2552_APT_THRESH_2_5 0x00
|
||||
#define TAS2552_APT_THRESH_1_7 (1 << 3)
|
||||
#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4)
|
||||
#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2)
|
||||
/* Boost Auto-pass through register */
|
||||
#define TAS2552_APT_DELAY_50 (0x0 << 0)
|
||||
#define TAS2552_APT_DELAY_75 (0x1 << 0)
|
||||
#define TAS2552_APT_DELAY_125 (0x2 << 0)
|
||||
#define TAS2552_APT_DELAY_200 (0x3 << 0)
|
||||
#define TAS2552_APT_THRESH_05_02 (0x0 << 2)
|
||||
#define TAS2552_APT_THRESH_10_07 (0x1 << 2)
|
||||
#define TAS2552_APT_THRESH_14_11 (0x2 << 2)
|
||||
#define TAS2552_APT_THRESH_20_17 (0x3 << 2)
|
||||
|
||||
/* PLL Control Register */
|
||||
#define TAS2552_245MHZ_CLK 24576000
|
||||
#define TAS2552_225MHZ_CLK 22579200
|
||||
#define TAS2552_PLL_J_MASK 0x7f
|
||||
#define TAS2552_PLL_D_UPPER_MASK 0x3f
|
||||
#define TAS2552_PLL_D_LOWER_MASK 0xff
|
||||
#define TAS2552_PLL_BYPASS_MASK 0x80
|
||||
#define TAS2552_PLL_BYPASS 0x80
|
||||
#define TAS2552_PLL_J_MASK 0x7f
|
||||
#define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f)
|
||||
#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff)
|
||||
#define TAS2552_PLL_BYPASS (1 << 7)
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче