static inline int kirkwood_set_rate(struct kirkwood_dma_data* priv, unsigned long rate) { int ret = -EINVAL; /* First check if ext clk is available */ if (!IS_ERR(priv->extclk)) { /* use optional external clk for other rates */ printk (">>> %s :: extclk set rate = %lu -> %lu\n", __FUNCTION__, rate, 256*rate); ret = clk_set_rate(priv->extclk, 256*rate); if (!ret) writel(KIRKWOOD_MCLK_SOURCE_EXTCLK, priv->io+KIRKWOOD_CLOCKS_CTRL); } if (ret && (rate == 44100 || rate == 48000 || rate == 96000)) { /* use internal dco for supported rates */ printk (">>> %s :: dco set rate = %lu\n", __FUNCTION__, rate); ret = kirkwood_set_dco(priv->io, rate); if (!ret) writel(KIRKWOOD_MCLK_SOURCE_DCO, priv->io+KIRKWOOD_CLOCKS_CTRL); } return ret; }
static void kirkwood_set_rate(struct snd_soc_dai *dai, struct kirkwood_dma_data *priv, unsigned long rate) { uint32_t clks_ctrl; if (IS_ERR(priv->extclk)) { /* use internal dco for the supported rates * defined in kirkwood_i2s_dai */ dev_dbg(dai->dev, "%s: dco set rate = %lu\n", __func__, rate); kirkwood_set_dco(priv->io, rate); clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; } else { /* use the external clock for the other rates * defined in kirkwood_i2s_dai_extclk */ dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", __func__, rate, 256 * rate); clk_set_rate(priv->extclk, 256 * rate); clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK; } writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL); }
static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); unsigned int i2s_reg, reg; unsigned long i2s_value, value; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { i2s_reg = KIRKWOOD_I2S_PLAYCTL; reg = KIRKWOOD_PLAYCTL; } else { i2s_reg = KIRKWOOD_I2S_RECCTL; reg = KIRKWOOD_RECCTL; } /* set dco conf */ kirkwood_set_dco(priv->io, params_rate(params)); i2s_value = readl(priv->io+i2s_reg); i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; value = readl(priv->io+reg); value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK; /* * Size settings in play/rec i2s control regs and play/rec control * regs must be the same. */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; value |= KIRKWOOD_PLAYCTL_SIZE_16_C; break; /* * doesn't work... S20_3LE != kirkwood 20bit format ? * case SNDRV_PCM_FORMAT_S20_3LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; value |= KIRKWOOD_PLAYCTL_SIZE_20; break; */ case SNDRV_PCM_FORMAT_S24_LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; value |= KIRKWOOD_PLAYCTL_SIZE_24; break; case SNDRV_PCM_FORMAT_S32_LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; value |= KIRKWOOD_PLAYCTL_SIZE_32; break; default: return -EINVAL; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { value &= ~KIRKWOOD_PLAYCTL_MONO_MASK; if (params_channels(params) == 1) value |= KIRKWOOD_PLAYCTL_MONO_BOTH; else value |= KIRKWOOD_PLAYCTL_MONO_OFF; } writel(i2s_value, priv->io+i2s_reg); writel(value, priv->io+reg); return 0; }