Commit 9fce18ab authored by Mark Brown's avatar Mark Brown

ASoC: fsl_sai: Cleanups and 1:1 bclk:mclk ratio support

Merge series from Sascha Hauer <s.hauer@pengutronix.de>:

This series has some updates for the fsl_sai driver: Some general
cleanup patches, a bugfix in the ip revision checking and finally
the mclk setting is made more accurate and support for 1:1 bclk:mclk
setting is added.
parents 5e36946a a50b7926
...@@ -62,7 +62,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) ...@@ -62,7 +62,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
unsigned int ofs = sai->soc_data->reg_offset; unsigned int ofs = sai->soc_data->reg_offset;
struct device *dev = &sai->pdev->dev; struct device *dev = &sai->pdev->dev;
u32 flags, xcsr, mask; u32 flags, xcsr, mask;
bool irq_none = true; irqreturn_t iret = IRQ_NONE;
/* /*
* Both IRQ status bits and IRQ mask bits are in the xCSR but * Both IRQ status bits and IRQ mask bits are in the xCSR but
...@@ -76,7 +76,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) ...@@ -76,7 +76,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
flags = xcsr & mask; flags = xcsr & mask;
if (flags) if (flags)
irq_none = false; iret = IRQ_HANDLED;
else else
goto irq_rx; goto irq_rx;
...@@ -110,7 +110,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) ...@@ -110,7 +110,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
flags = xcsr & mask; flags = xcsr & mask;
if (flags) if (flags)
irq_none = false; iret = IRQ_HANDLED;
else else
goto out; goto out;
...@@ -139,10 +139,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) ...@@ -139,10 +139,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr); regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr);
out: out:
if (irq_none) return iret;
return IRQ_NONE;
else
return IRQ_HANDLED;
} }
static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
...@@ -167,11 +164,10 @@ static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai, ...@@ -167,11 +164,10 @@ static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai,
} }
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int fsl_dir) int clk_id, unsigned int freq, bool tx)
{ {
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int ofs = sai->soc_data->reg_offset; unsigned int ofs = sai->soc_data->reg_offset;
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
u32 val_cr2 = 0; u32 val_cr2 = 0;
switch (clk_id) { switch (clk_id) {
...@@ -205,15 +201,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, ...@@ -205,15 +201,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
if (dir == SND_SOC_CLOCK_IN) if (dir == SND_SOC_CLOCK_IN)
return 0; return 0;
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
FSL_FMT_TRANSMITTER);
if (ret) { if (ret) {
dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
return ret; return ret;
} }
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false);
FSL_FMT_RECEIVER);
if (ret) if (ret)
dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
...@@ -221,11 +215,10 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, ...@@ -221,11 +215,10 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
} }
static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
unsigned int fmt, int fsl_dir) unsigned int fmt, bool tx)
{ {
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int ofs = sai->soc_data->reg_offset; unsigned int ofs = sai->soc_data->reg_offset;
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
u32 val_cr2 = 0, val_cr4 = 0; u32 val_cr2 = 0, val_cr4 = 0;
if (!sai->is_lsb_first) if (!sai->is_lsb_first)
...@@ -332,13 +325,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) ...@@ -332,13 +325,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{ {
int ret; int ret;
ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true);
if (ret) { if (ret) {
dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
return ret; return ret;
} }
ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false);
if (ret) if (ret)
dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
...@@ -348,13 +341,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) ...@@ -348,13 +341,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{ {
struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
unsigned int ofs = sai->soc_data->reg_offset; unsigned int reg, ofs = sai->soc_data->reg_offset;
unsigned long clk_rate; unsigned long clk_rate;
u32 savediv = 0, ratio, savesub = freq; u32 savediv = 0, ratio, bestdiff = freq;
int adir = tx ? RX : TX; int adir = tx ? RX : TX;
int dir = tx ? TX : RX; int dir = tx ? TX : RX;
u32 id; u32 id;
int ret = 0; bool support_1_1_ratio = sai->verid.version >= 0x0301;
/* Don't apply to consumer mode */ /* Don't apply to consumer mode */
if (sai->is_consumer_mode) if (sai->is_consumer_mode)
...@@ -368,37 +361,41 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) ...@@ -368,37 +361,41 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0; id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0;
for (; id < FSL_SAI_MCLK_MAX; id++) { for (; id < FSL_SAI_MCLK_MAX; id++) {
int diff;
clk_rate = clk_get_rate(sai->mclk_clk[id]); clk_rate = clk_get_rate(sai->mclk_clk[id]);
if (!clk_rate) if (!clk_rate)
continue; continue;
ratio = clk_rate / freq; ratio = DIV_ROUND_CLOSEST(clk_rate, freq);
if (!ratio || ratio > 512)
continue;
if (ratio == 1 && !support_1_1_ratio)
continue;
else if (ratio & 1)
continue;
ret = clk_rate - ratio * freq; diff = abs((long)clk_rate - ratio * freq);
/* /*
* Drop the source that can not be * Drop the source that can not be
* divided into the required rate. * divided into the required rate.
*/ */
if (ret != 0 && clk_rate / ret < 1000) if (diff != 0 && clk_rate / diff < 1000)
continue; continue;
dev_dbg(dai->dev, dev_dbg(dai->dev,
"ratio %d for freq %dHz based on clock %ldHz\n", "ratio %d for freq %dHz based on clock %ldHz\n",
ratio, freq, clk_rate); ratio, freq, clk_rate);
if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512)
ratio /= 2;
else
continue;
if (ret < savesub) { if (diff < bestdiff) {
savediv = ratio; savediv = ratio;
sai->mclk_id[tx] = id; sai->mclk_id[tx] = id;
savesub = ret; bestdiff = diff;
} }
if (ret == 0) if (diff == 0)
break; break;
} }
...@@ -408,6 +405,9 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) ...@@ -408,6 +405,9 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
return -EINVAL; return -EINVAL;
} }
dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
sai->mclk_id[tx], savediv, bestdiff);
/* /*
* 1) For Asynchronous mode, we must set RCR2 register for capture, and * 1) For Asynchronous mode, we must set RCR2 register for capture, and
* set TCR2 register for playback. * set TCR2 register for playback.
...@@ -418,22 +418,24 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) ...@@ -418,22 +418,24 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
* 4) For Tx and Rx are both Synchronous with another SAI, we just * 4) For Tx and Rx are both Synchronous with another SAI, we just
* ignore it. * ignore it.
*/ */
if (fsl_sai_dir_is_synced(sai, adir)) { if (fsl_sai_dir_is_synced(sai, adir))
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), reg = FSL_SAI_xCR2(!tx, ofs);
FSL_SAI_CR2_MSEL_MASK, else if (!sai->synchronous[dir])
FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); reg = FSL_SAI_xCR2(tx, ofs);
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), else
FSL_SAI_CR2_DIV_MASK, savediv - 1); return 0;
} else if (!sai->synchronous[dir]) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_DIV_MASK, savediv - 1);
}
dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
sai->mclk_id[tx], savediv, savesub); FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
if (savediv == 1)
regmap_update_bits(sai->regmap, reg,
FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
FSL_SAI_CR2_BYP);
else
regmap_update_bits(sai->regmap, reg,
FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
savediv / 2 - 1);
return 0; return 0;
} }
...@@ -972,10 +974,8 @@ static int fsl_sai_check_version(struct device *dev) ...@@ -972,10 +974,8 @@ static int fsl_sai_check_version(struct device *dev)
dev_dbg(dev, "VERID: 0x%016X\n", val); dev_dbg(dev, "VERID: 0x%016X\n", val);
sai->verid.major = (val & FSL_SAI_VERID_MAJOR_MASK) >> sai->verid.version = val &
FSL_SAI_VERID_MAJOR_SHIFT; (FSL_SAI_VERID_MAJOR_MASK | FSL_SAI_VERID_MINOR_MASK);
sai->verid.minor = (val & FSL_SAI_VERID_MINOR_MASK) >>
FSL_SAI_VERID_MINOR_SHIFT;
sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK; sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK;
ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val); ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val);
...@@ -1147,7 +1147,7 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -1147,7 +1147,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
/* Select MCLK direction */ /* Select MCLK direction */
if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
sai->verid.major >= 3 && sai->verid.minor >= 1) { sai->verid.version >= 0x0301) {
regmap_update_bits(sai->regmap, FSL_SAI_MCTL, regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
} }
......
...@@ -201,9 +201,6 @@ ...@@ -201,9 +201,6 @@
#define FSL_SAI_REC_SYN BIT(4) #define FSL_SAI_REC_SYN BIT(4)
#define FSL_SAI_USE_I2S_SLAVE BIT(5) #define FSL_SAI_USE_I2S_SLAVE BIT(5)
#define FSL_FMT_TRANSMITTER 0
#define FSL_FMT_RECEIVER 1
/* SAI clock sources */ /* SAI clock sources */
#define FSL_SAI_CLK_BUS 0 #define FSL_SAI_CLK_BUS 0
#define FSL_SAI_CLK_MAST1 1 #define FSL_SAI_CLK_MAST1 1
...@@ -230,15 +227,13 @@ struct fsl_sai_soc_data { ...@@ -230,15 +227,13 @@ struct fsl_sai_soc_data {
/** /**
* struct fsl_sai_verid - version id data * struct fsl_sai_verid - version id data
* @major: major version number * @version: version number
* @minor: minor version number
* @feature: feature specification number * @feature: feature specification number
* 0000000000000000b - Standard feature set * 0000000000000000b - Standard feature set
* 0000000000000000b - Standard feature set * 0000000000000000b - Standard feature set
*/ */
struct fsl_sai_verid { struct fsl_sai_verid {
u32 major; u32 version;
u32 minor;
u32 feature; u32 feature;
}; };
......
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