Unverified Commit 9fce18ab authored by Mark Brown's avatar Mark Brown
Browse files

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
Loading
Loading
Loading
Loading
+50 −50
Original line number Diff line number Diff line
@@ -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,
	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;

	regmap_update_bits(sai->regmap, reg, 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",
			sai->mclk_id[tx], savediv, savesub);
	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);
	}
+2 −7
Original line number Diff line number Diff line
@@ -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;
};