/* * Get MCLK and set clock rate, then enable it * * This interface is used for codecs who are using MCLK provided * by saif. */ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, unsigned int rate) { struct mxs_saif *saif = mxs_saif[saif_id]; u32 stat; int ret; struct mxs_saif *master_saif; if (!saif) return -EINVAL; /* Clear Reset */ __raw_writel(BM_SAIF_CTRL_SFTRST, saif->base + SAIF_CTRL + MXS_CLR_ADDR); /* FIXME: need clear clk gate for register r/w */ __raw_writel(BM_SAIF_CTRL_CLKGATE, saif->base + SAIF_CTRL + MXS_CLR_ADDR); master_saif = mxs_saif_get_master(saif); if (saif != master_saif) { dev_err(saif->dev, "can not get mclk from a non-master saif\n"); return -EINVAL; } stat = __raw_readl(saif->base + SAIF_STAT); if (stat & BM_SAIF_STAT_BUSY) { dev_err(saif->dev, "error: busy\n"); return -EBUSY; } saif->mclk_in_use = 1; ret = mxs_saif_set_clk(saif, mclk, rate); if (ret) return ret; ret = clk_prepare_enable(saif->clk); if (ret) return ret; /* enable MCLK output */ __raw_writel(BM_SAIF_CTRL_RUN, saif->base + SAIF_CTRL + MXS_SET_ADDR); return 0; }
/* * Should only be called when port is inactive. * although can be called multiple times by upper layers. */ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); struct mxs_saif *master_saif; u32 scr, stat; int ret; master_saif = mxs_saif_get_master(saif); if (!master_saif) return -EINVAL; /* mclk should already be set */ if (!saif->mclk && saif->mclk_in_use) { dev_err(cpu_dai->dev, "set mclk first\n"); return -EINVAL; } stat = __raw_readl(saif->base + SAIF_STAT); if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) { dev_err(cpu_dai->dev, "error: busy\n"); return -EBUSY; } /* * Set saif clk based on sample rate. * If mclk is used, we also set mclk, if not, saif->mclk is * default 0, means not used. */ ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params)); if (ret) { dev_err(cpu_dai->dev, "unable to get proper clk\n"); return ret; } if (saif != master_saif) { /* * Set an initial clock rate for the saif internal logic to work * properly. This is important when working in EXTMASTER mode * that uses the other saif's BITCLK&LRCLK but it still needs a * basic clock which should be fast enough for the internal * logic. */ clk_enable(saif->clk); ret = clk_set_rate(saif->clk, 24000000); clk_disable(saif->clk); if (ret) return ret; ret = clk_prepare(master_saif->clk); if (ret) return ret; } scr = __raw_readl(saif->base + SAIF_CTRL); scr &= ~BM_SAIF_CTRL_WORD_LENGTH; scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: scr |= BF_SAIF_CTRL_WORD_LENGTH(0); break; case SNDRV_PCM_FORMAT_S20_3LE: scr |= BF_SAIF_CTRL_WORD_LENGTH(4); scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; break; case SNDRV_PCM_FORMAT_S24_LE: scr |= BF_SAIF_CTRL_WORD_LENGTH(8); scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; break; default: return -EINVAL; } /* Tx/Rx config */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* enable TX mode */ scr &= ~BM_SAIF_CTRL_READ_MODE; } else { /* enable RX mode */ scr |= BM_SAIF_CTRL_READ_MODE; } __raw_writel(scr, saif->base + SAIF_CTRL); return 0; }
/* * Should only be called when port is inactive. * although can be called multiple times by upper layers. */ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); u32 scr, stat; int ret; /* mclk should already be set */ if (!saif->mclk && saif->mclk_in_use) { dev_err(cpu_dai->dev, "set mclk first\n"); return -EINVAL; } stat = __raw_readl(saif->base + SAIF_STAT); if (stat & BM_SAIF_STAT_BUSY) { dev_err(cpu_dai->dev, "error: busy\n"); return -EBUSY; } /* * Set saif clk based on sample rate. * If mclk is used, we also set mclk, if not, saif->mclk is * default 0, means not used. */ ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params)); if (ret) { dev_err(cpu_dai->dev, "unable to get proper clk\n"); return ret; } scr = __raw_readl(saif->base + SAIF_CTRL); scr &= ~BM_SAIF_CTRL_WORD_LENGTH; scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: scr |= BF_SAIF_CTRL_WORD_LENGTH(0); break; case SNDRV_PCM_FORMAT_S20_3LE: scr |= BF_SAIF_CTRL_WORD_LENGTH(4); scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; break; case SNDRV_PCM_FORMAT_S24_LE: scr |= BF_SAIF_CTRL_WORD_LENGTH(8); scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; break; default: return -EINVAL; } /* Tx/Rx config */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* enable TX mode */ scr &= ~BM_SAIF_CTRL_READ_MODE; } else { /* enable RX mode */ scr |= BM_SAIF_CTRL_READ_MODE; } __raw_writel(scr, saif->base + SAIF_CTRL); return 0; }