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.
This commit is contained in:
Коммит
9fce18ab0b
|
@ -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 @@ irq_rx:
|
|||
flags = xcsr & mask;
|
||||
|
||||
if (flags)
|
||||
irq_none = false;
|
||||
iret = IRQ_HANDLED;
|
||||
else
|
||||
goto out;
|
||||
|
||||
|
@ -139,10 +139,7 @@ irq_rx:
|
|||
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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче