Commit e39f6bc7 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/tas2552' into asoc-next

parents 6afff9e0 8604bc28
......@@ -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;
}
......
......@@ -39,7 +39,7 @@
#define TAS2552_PDM_CFG 0x11
#define TAS2552_PGA_GAIN 0x12
#define TAS2552_EDGE_RATE_CTRL 0x13
#define TAS2552_BOOST_PT_CTRL 0x14
#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_D_UPPER(x) (((x) >> 8) & 0x3f)
#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff)
#define TAS2552_PLL_BYPASS (1 << 7)
#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