/** * 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); }
/** * 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; }
/** * 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; }