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)
unsigned int ofs = sai->soc_data->reg_offset;
struct device *dev = &sai->pdev->dev;
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
......@@ -76,7 +76,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
flags = xcsr & mask;
if (flags)
irq_none = false;
iret = IRQ_HANDLED;
else
goto irq_rx;
......@@ -110,7 +110,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
flags = xcsr & mask;
if (flags)
irq_none = false;
iret = IRQ_HANDLED;
else
goto out;
......@@ -139,10 +139,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr);
out:
if (irq_none)
return IRQ_NONE;
else
return IRQ_HANDLED;
return iret;
}
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,
}
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);
unsigned int ofs = sai->soc_data->reg_offset;
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
u32 val_cr2 = 0;
switch (clk_id) {
......@@ -205,15 +201,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
if (dir == SND_SOC_CLOCK_IN)
return 0;
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
FSL_FMT_TRANSMITTER);
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
if (ret) {
dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
return ret;
}
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
FSL_FMT_RECEIVER);
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false);
if (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,
}
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);
unsigned int ofs = sai->soc_data->reg_offset;
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
u32 val_cr2 = 0, val_cr4 = 0;
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)
{
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) {
dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", 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)
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)
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);
unsigned int ofs = sai->soc_data->reg_offset;
unsigned int reg, ofs = sai->soc_data->reg_offset;
unsigned long clk_rate;
u32 savediv = 0, ratio, savesub = freq;
u32 savediv = 0, ratio, bestdiff = freq;
int adir = tx ? RX : TX;
int dir = tx ? TX : RX;
u32 id;
int ret = 0;
bool support_1_1_ratio = sai->verid.version >= 0x0301;
/* Don't apply to 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)
id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0;
for (; id < FSL_SAI_MCLK_MAX; id++) {
int diff;
clk_rate = clk_get_rate(sai->mclk_clk[id]);
if (!clk_rate)
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
* divided into the required rate.
*/
if (ret != 0 && clk_rate / ret < 1000)
if (diff != 0 && clk_rate / diff < 1000)
continue;
dev_dbg(dai->dev,
"ratio %d for freq %dHz based on clock %ldHz\n",
ratio, freq, clk_rate);
if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512)
ratio /= 2;
else
continue;
if (ret < savesub) {
if (diff < bestdiff) {
savediv = ratio;
sai->mclk_id[tx] = id;
savesub = ret;
bestdiff = diff;
}
if (ret == 0)
if (diff == 0)
break;
}
......@@ -408,6 +405,9 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
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
* set TCR2 register for playback.
......@@ -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
* ignore it.
*/
if (fsl_sai_dir_is_synced(sai, adir)) {
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);
} 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);
}
if (fsl_sai_dir_is_synced(sai, adir))
reg = FSL_SAI_xCR2(!tx, ofs);
else if (!sai->synchronous[dir])
reg = FSL_SAI_xCR2(tx, ofs);
else
return 0;
dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
sai->mclk_id[tx], savediv, savesub);
regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
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;
}
......@@ -972,10 +974,8 @@ static int fsl_sai_check_version(struct device *dev)
dev_dbg(dev, "VERID: 0x%016X\n", val);
sai->verid.major = (val & FSL_SAI_VERID_MAJOR_MASK) >>
FSL_SAI_VERID_MAJOR_SHIFT;
sai->verid.minor = (val & FSL_SAI_VERID_MINOR_MASK) >>
FSL_SAI_VERID_MINOR_SHIFT;
sai->verid.version = val &
(FSL_SAI_VERID_MAJOR_MASK | FSL_SAI_VERID_MINOR_MASK);
sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK;
ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val);
......@@ -1147,7 +1147,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
/* Select MCLK direction */
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,
FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
}
......
......@@ -201,9 +201,6 @@
#define FSL_SAI_REC_SYN BIT(4)
#define FSL_SAI_USE_I2S_SLAVE BIT(5)
#define FSL_FMT_TRANSMITTER 0
#define FSL_FMT_RECEIVER 1
/* SAI clock sources */
#define FSL_SAI_CLK_BUS 0
#define FSL_SAI_CLK_MAST1 1
......@@ -230,15 +227,13 @@ struct fsl_sai_soc_data {
/**
* struct fsl_sai_verid - version id data
* @major: major version number
* @minor: minor version number
* @version: version number
* @feature: feature specification number
* 0000000000000000b - Standard feature set
* 0000000000000000b - Standard feature set
*/
struct fsl_sai_verid {
u32 major;
u32 minor;
u32 version;
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