Commit 715ee191 authored by Ladislav Michl's avatar Ladislav Michl Committed by Mark Brown

ASoC: max9867: Calculate LRCLK divider

Drop "Common NI Values Table" and calculate LRCLK divider, then
add allowed rate constraints based on master clock frequency.
Signed-off-by: default avatarLadislav Michl <ladis@linux-mips.org>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 29f58ff0
...@@ -126,124 +126,106 @@ static const struct snd_soc_dapm_route max9867_audio_map[] = { ...@@ -126,124 +126,106 @@ static const struct snd_soc_dapm_route max9867_audio_map[] = {
{"LINE_IN", NULL, "Right Line"}, {"LINE_IN", NULL, "Right Line"},
}; };
enum rates { static const unsigned int max9867_rates_44k1[] = {
pcm_rate_8, pcm_rate_16, pcm_rate_24, 11025, 22050, 44100,
pcm_rate_32, pcm_rate_44,
pcm_rate_48, max_pcm_rate,
}; };
static const struct ni_div_rates { static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = {
u32 mclk; .list = max9867_rates_44k1,
u16 ni[max_pcm_rate]; .count = ARRAY_SIZE(max9867_rates_44k1),
} ni_div[] = {
{11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} },
{12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} },
{12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} },
{13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} },
{19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} },
{24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} },
{26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} },
{27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} },
}; };
static inline int get_ni_value(int mclk, int rate) static const unsigned int max9867_rates_48k[] = {
8000, 16000, 32000, 48000,
};
static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = {
.list = max9867_rates_48k,
.count = ARRAY_SIZE(max9867_rates_48k),
};
struct max9867_priv {
struct regmap *regmap;
const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk, pclk;
bool master, dsp_a;
};
static int max9867_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{ {
int i, ret = 0; struct max9867_priv *max9867 =
snd_soc_component_get_drvdata(dai->component);
/* find the closest rate index*/ if (max9867->constraints)
for (i = 0; i < ARRAY_SIZE(ni_div); i++) { snd_pcm_hw_constraint_list(substream->runtime, 0,
if (ni_div[i].mclk >= mclk) SNDRV_PCM_HW_PARAM_RATE, max9867->constraints);
break;
}
if (i == ARRAY_SIZE(ni_div))
return -EINVAL;
switch (rate) { return 0;
case 8000:
return ni_div[i].ni[pcm_rate_8];
case 16000:
return ni_div[i].ni[pcm_rate_16];
case 32000:
return ni_div[i].ni[pcm_rate_32];
case 44100:
return ni_div[i].ni[pcm_rate_44];
case 48000:
return ni_div[i].ni[pcm_rate_48];
default:
pr_err("%s wrong rate %d\n", __func__, rate);
ret = -EINVAL;
}
return ret;
} }
static int max9867_dai_hw_params(struct snd_pcm_substream *substream, static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ {
int value;
unsigned long int rate, ratio;
struct snd_soc_component *component = dai->component; struct snd_soc_component *component = dai->component;
struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
unsigned int ni_h, ni_l; unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params),
int value; max9867->pclk);
value = get_ni_value(max9867->sysclk, params_rate(params));
if (value < 0)
return value;
ni_h = (0xFF00 & value) >> 8;
ni_l = 0x00FF & value;
/* set up the ni value */ /* set up the ni value */
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
MAX9867_NI_HIGH_MASK, ni_h); MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8);
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
MAX9867_NI_LOW_MASK, ni_l); MAX9867_NI_LOW_MASK, 0x00FF & ni);
if (!max9867->master) { if (max9867->master) {
/* if (max9867->dsp_a) {
* digital pll locks on to any externally supplied LRCLK signal value = MAX9867_IFC1B_48X;
* and also enable rapid lock mode. } else {
*/ rate = params_rate(params) * 2 * params_width(params);
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, ratio = max9867->pclk / rate;
MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); switch (params_width(params)) {
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
MAX9867_PLL, MAX9867_PLL);
} else {
unsigned long int bclk_rate, pclk_bclk_ratio;
int bclk_value;
bclk_rate = params_rate(params) * 2 * params_width(params);
pclk_bclk_ratio = max9867->pclk/bclk_rate;
switch (params_width(params)) {
case 8:
case 16:
switch (pclk_bclk_ratio) {
case 2:
bclk_value = MAX9867_IFC1B_PCLK_2;
break;
case 4:
bclk_value = MAX9867_IFC1B_PCLK_4;
break;
case 8: case 8:
bclk_value = MAX9867_IFC1B_PCLK_8;
break;
case 16: case 16:
bclk_value = MAX9867_IFC1B_PCLK_16; switch (ratio) {
case 2:
value = MAX9867_IFC1B_PCLK_2;
break;
case 4:
value = MAX9867_IFC1B_PCLK_4;
break;
case 8:
value = MAX9867_IFC1B_PCLK_8;
break;
case 16:
value = MAX9867_IFC1B_PCLK_16;
break;
default:
return -EINVAL;
}
break;
case 24:
value = MAX9867_IFC1B_48X;
break;
case 32:
value = MAX9867_IFC1B_64X;
break; break;
default: default:
dev_err(component->dev,
"unsupported sampling rate\n");
return -EINVAL; return -EINVAL;
} }
break;
case 24:
bclk_value = MAX9867_IFC1B_24BIT;
break;
case 32:
bclk_value = MAX9867_IFC1B_32BIT;
break;
default:
dev_err(component->dev, "unsupported sampling rate\n");
return -EINVAL;
} }
regmap_update_bits(max9867->regmap, MAX9867_IFC1B, regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
MAX9867_IFC1B_BCLK_MASK, bclk_value); MAX9867_IFC1B_BCLK_MASK, value);
} else {
/*
* digital pll locks on to any externally supplied LRCLK signal
* and also enable rapid lock mode.
*/
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
MAX9867_PLL, MAX9867_PLL);
} }
return 0; return 0;
} }
...@@ -285,8 +267,16 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, ...@@ -285,8 +267,16 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
freq); freq);
return -EINVAL; return -EINVAL;
} }
value = value << MAX9867_PSCLK_SHIFT; if (freq % 48000 == 0)
max9867->constraints = &max9867_constraints_48k;
else if (freq % 44100 == 0)
max9867->constraints = &max9867_constraints_44k1;
else
dev_warn(component->dev,
"Unable to set exact rate with %uHz clock frequency\n",
freq);
max9867->sysclk = freq; max9867->sysclk = freq;
value = value << MAX9867_PSCLK_SHIFT;
/* exact integer mode is not supported */ /* exact integer mode is not supported */
value &= ~MAX9867_FREQ_MASK; value &= ~MAX9867_FREQ_MASK;
regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
...@@ -299,16 +289,17 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, ...@@ -299,16 +289,17 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
{ {
struct snd_soc_component *component = codec_dai->component; struct snd_soc_component *component = codec_dai->component;
struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
u8 iface1A = 0, iface1B = 0; u8 iface1A, iface1B;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
max9867->master = 1; max9867->master = true;
iface1A |= MAX9867_MASTER; iface1A = MAX9867_MASTER;
iface1B = MAX9867_IFC1B_48X;
break; break;
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
max9867->master = 0; max9867->master = false;
iface1A &= ~MAX9867_MASTER; iface1A = iface1B = 0;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -316,9 +307,11 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, ...@@ -316,9 +307,11 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
max9867->dsp_a = false;
iface1A |= MAX9867_I2S_DLY; iface1A |= MAX9867_I2S_DLY;
break; break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A:
max9867->dsp_a = true;
iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ;
break; break;
default: default:
...@@ -344,20 +337,18 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, ...@@ -344,20 +337,18 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
return 0; return 0;
} }
static const struct snd_soc_dai_ops max9867_dai_ops = { static const struct snd_soc_dai_ops max9867_dai_ops = {
.set_fmt = max9867_dai_set_fmt,
.set_sysclk = max9867_set_dai_sysclk, .set_sysclk = max9867_set_dai_sysclk,
.set_fmt = max9867_dai_set_fmt,
.digital_mute = max9867_mute, .digital_mute = max9867_mute,
.hw_params = max9867_dai_hw_params, .startup = max9867_startup,
.hw_params = max9867_dai_hw_params,
}; };
#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
static struct snd_soc_dai_driver max9867_dai[] = { static struct snd_soc_dai_driver max9867_dai[] = {
{ {
.name = "max9867-aif1", .name = "max9867-aif1",
...@@ -365,15 +356,15 @@ static struct snd_soc_dai_driver max9867_dai[] = { ...@@ -365,15 +356,15 @@ static struct snd_soc_dai_driver max9867_dai[] = {
.stream_name = "HiFi Playback", .stream_name = "HiFi Playback",
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.rates = MAX9867_RATES, .rates = SNDRV_PCM_RATE_8000_48000,
.formats = MAX9867_FORMATS, .formats = SNDRV_PCM_FMTBIT_S16_LE,
}, },
.capture = { .capture = {
.stream_name = "HiFi Capture", .stream_name = "HiFi Capture",
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.rates = MAX9867_RATES, .rates = SNDRV_PCM_RATE_8000_48000,
.formats = MAX9867_FORMATS, .formats = SNDRV_PCM_FMTBIT_S16_LE,
}, },
.ops = &max9867_dai_ops, .ops = &max9867_dai_ops,
.symmetric_rates = 1, .symmetric_rates = 1,
......
...@@ -26,13 +26,11 @@ ...@@ -26,13 +26,11 @@
#define MAX9867_PSCLK_10_20 0x1 #define MAX9867_PSCLK_10_20 0x1
#define MAX9867_PSCLK_20_40 0x2 #define MAX9867_PSCLK_20_40 0x2
#define MAX9867_PSCLK_40_60 0x3 #define MAX9867_PSCLK_40_60 0x3
#define MAX9867_AUDIOCLKHIGH 0x06 #define MAX9867_AUDIOCLKHIGH 0x06
#define MAX9867_NI_HIGH_WIDTH 0x7 #define MAX9867_NI_HIGH_MASK 0x7F
#define MAX9867_NI_HIGH_MASK 0x7F #define MAX9867_NI_LOW_MASK 0xFE
#define MAX9867_NI_LOW_MASK 0x7F #define MAX9867_PLL (1<<7)
#define MAX9867_NI_LOW_SHIFT 0x1 #define MAX9867_AUDIOCLKLOW 0x07
#define MAX9867_PLL (1<<7)
#define MAX9867_AUDIOCLKLOW 0x07
#define MAX9867_RAPID_LOCK 0x01 #define MAX9867_RAPID_LOCK 0x01
#define MAX9867_IFC1A 0x08 #define MAX9867_IFC1A 0x08
#define MAX9867_MASTER (1<<7) #define MAX9867_MASTER (1<<7)
...@@ -43,12 +41,12 @@ ...@@ -43,12 +41,12 @@
#define MAX9867_BCI_MODE (1<<5) #define MAX9867_BCI_MODE (1<<5)
#define MAX9867_IFC1B 0x09 #define MAX9867_IFC1B 0x09
#define MAX9867_IFC1B_BCLK_MASK 7 #define MAX9867_IFC1B_BCLK_MASK 7
#define MAX9867_IFC1B_32BIT 0x01 #define MAX9867_IFC1B_64X 0x01
#define MAX9867_IFC1B_24BIT 0x02 #define MAX9867_IFC1B_48X 0x02
#define MAX9867_IFC1B_PCLK_2 4 #define MAX9867_IFC1B_PCLK_2 0x04
#define MAX9867_IFC1B_PCLK_4 5 #define MAX9867_IFC1B_PCLK_4 0x05
#define MAX9867_IFC1B_PCLK_8 6 #define MAX9867_IFC1B_PCLK_8 0x06
#define MAX9867_IFC1B_PCLK_16 7 #define MAX9867_IFC1B_PCLK_16 0x07
#define MAX9867_CODECFLTR 0x0a #define MAX9867_CODECFLTR 0x0a
#define MAX9867_DACGAIN 0x0b #define MAX9867_DACGAIN 0x0b
#define MAX9867_DACLEVEL 0x0c #define MAX9867_DACLEVEL 0x0c
...@@ -72,11 +70,4 @@ ...@@ -72,11 +70,4 @@
#define MAX9867_CACHEREGNUM 10 #define MAX9867_CACHEREGNUM 10
/* codec private data */
struct max9867_priv {
struct regmap *regmap;
unsigned int sysclk;
unsigned int pclk;
unsigned int master;
};
#endif #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