Пример #1
0
/**
 * This function configures the related dividers according to the bclk rate
 */
static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
	u32 hck_rate = esai_priv->hck_rate[tx];
	u32 sub, ratio = hck_rate / freq;

	/* Don't apply for fully slave mode*/
	if (esai_priv->slave_mode)
		return 0;

	if (ratio * freq > hck_rate)
		sub = ratio * freq - hck_rate;
	else if (ratio * freq < hck_rate)
		sub = hck_rate - ratio * freq;
	else
		sub = 0;

	/* Block if clock source can not be divided into the required rate */
	if (sub != 0 && hck_rate / sub < 1000) {
		dev_err(dai->dev, "failed to derive required SCK%c rate\n",
				tx ? 'T' : 'R');
		return -EINVAL;
	}

	if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
		dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
		return -EINVAL;
	}

	return fsl_esai_divisor_cal(dai, tx, ratio, true,
			esai_priv->sck_div[tx] ? 0 : ratio);
}
Пример #2
0
/**
 * This function configures the related dividers according to the bclk rate
 */
static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
	u32 hck_rate = esai_priv->hck_rate[tx];
	u32 sub, ratio = hck_rate / freq;
	int ret;

	/* Don't apply for fully slave mode or unchanged bclk */
	if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
		return 0;

	if (ratio * freq > hck_rate)
		sub = ratio * freq - hck_rate;
	else if (ratio * freq < hck_rate)
		sub = hck_rate - ratio * freq;
	else
		sub = 0;

	/* Block if clock source can not be divided into the required rate */
	if (sub != 0 && hck_rate / sub < 1000) {
		dev_err(dai->dev, "failed to derive required SCK%c rate\n",
				tx ? 'T' : 'R');
		return -EINVAL;
	}

	/* The ratio should be contented by FP alone if bypassing PM and PSR */
	if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
		dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
		return -EINVAL;
	}

	ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
			esai_priv->sck_div[tx] ? 0 : ratio);
	if (ret)
		return ret;

	/* Save current bclk rate */
	esai_priv->sck_rate[tx] = freq;

	return 0;
}
Пример #3
0
/**
 * This function mainly configures the clock frequency of MCLK (HCKT/HCKR)
 *
 * @Parameters:
 * clk_id: The clock source of HCKT/HCKR
 *	  (Input from outside; output from inside, FSYS or EXTAL)
 * freq: The required clock rate of HCKT/HCKR
 * dir: The clock direction of HCKT/HCKR
 *
 * Note: If the direction is input, we do not care about clk_id.
 */
static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
				   unsigned int freq, int dir)
{
	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
	struct clk *clksrc = esai_priv->extalclk;
	bool tx = clk_id <= ESAI_HCKT_EXTAL;
	bool in = dir == SND_SOC_CLOCK_IN;
	u32 ret, ratio, ecr = 0;
	unsigned long clk_rate;

	/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
	esai_priv->sck_div[tx] = true;

	/* Set the direction of HCKT/HCKR pins */
	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
			   ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);

	if (in)
		goto out;

	switch (clk_id) {
	case ESAI_HCKT_FSYS:
	case ESAI_HCKR_FSYS:
		clksrc = esai_priv->fsysclk;
		break;
	case ESAI_HCKT_EXTAL:
		ecr |= ESAI_ECR_ETI;
	case ESAI_HCKR_EXTAL:
		ecr |= ESAI_ECR_ERI;
		break;
	default:
		return -EINVAL;
	}

	if (IS_ERR(clksrc)) {
		dev_err(dai->dev, "no assigned %s clock\n",
				clk_id % 2 ? "extal" : "fsys");
		return PTR_ERR(clksrc);
	}
	clk_rate = clk_get_rate(clksrc);

	ratio = clk_rate / freq;
	if (ratio * freq > clk_rate)
		ret = ratio * freq - clk_rate;
	else if (ratio * freq < clk_rate)
		ret = clk_rate - ratio * freq;
	else
		ret = 0;

	/* Block if clock source can not be divided into the required rate */
	if (ret != 0 && clk_rate / ret < 1000) {
		dev_err(dai->dev, "failed to derive required HCK%c rate\n",
				tx ? 'T' : 'R');
		return -EINVAL;
	}

	if (ratio == 1) {
		/* Bypass all the dividers if not being needed */
		ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
		goto out;
	}

	ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
	if (ret)
		return ret;

	esai_priv->sck_div[tx] = false;

out:
	esai_priv->hck_rate[tx] = freq;

	regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
			   tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :
			   ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);

	return 0;
}