1f9b95980Sapatard@mandriva.com /* 2f9b95980Sapatard@mandriva.com * kirkwood-i2s.c 3f9b95980Sapatard@mandriva.com * 4f9b95980Sapatard@mandriva.com * (c) 2010 Arnaud Patard <apatard@mandriva.com> 569737897SArnaud Patard (Rtp) * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 6f9b95980Sapatard@mandriva.com * 7f9b95980Sapatard@mandriva.com * This program is free software; you can redistribute it and/or modify it 8f9b95980Sapatard@mandriva.com * under the terms of the GNU General Public License as published by the 9f9b95980Sapatard@mandriva.com * Free Software Foundation; either version 2 of the License, or (at your 10f9b95980Sapatard@mandriva.com * option) any later version. 11f9b95980Sapatard@mandriva.com */ 12f9b95980Sapatard@mandriva.com 13f9b95980Sapatard@mandriva.com #include <linux/init.h> 14f9b95980Sapatard@mandriva.com #include <linux/module.h> 15f9b95980Sapatard@mandriva.com #include <linux/platform_device.h> 16f9b95980Sapatard@mandriva.com #include <linux/io.h> 17f9b95980Sapatard@mandriva.com #include <linux/slab.h> 18f9b95980Sapatard@mandriva.com #include <linux/mbus.h> 19f9b95980Sapatard@mandriva.com #include <linux/delay.h> 20e919c716SAndrew Lunn #include <linux/clk.h> 21f9b95980Sapatard@mandriva.com #include <sound/pcm.h> 22f9b95980Sapatard@mandriva.com #include <sound/pcm_params.h> 23f9b95980Sapatard@mandriva.com #include <sound/soc.h> 24c02cecb9SArnd Bergmann #include <linux/platform_data/asoc-kirkwood.h> 25f9b95980Sapatard@mandriva.com #include "kirkwood.h" 26f9b95980Sapatard@mandriva.com 27f9b95980Sapatard@mandriva.com #define DRV_NAME "kirkwood-i2s" 28f9b95980Sapatard@mandriva.com 29f9b95980Sapatard@mandriva.com #define KIRKWOOD_I2S_FORMATS \ 30f9b95980Sapatard@mandriva.com (SNDRV_PCM_FMTBIT_S16_LE | \ 31f9b95980Sapatard@mandriva.com SNDRV_PCM_FMTBIT_S24_LE | \ 32f9b95980Sapatard@mandriva.com SNDRV_PCM_FMTBIT_S32_LE) 33f9b95980Sapatard@mandriva.com 34f9b95980Sapatard@mandriva.com static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 35f9b95980Sapatard@mandriva.com unsigned int fmt) 36f9b95980Sapatard@mandriva.com { 37f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai); 38f9b95980Sapatard@mandriva.com unsigned long mask; 39f9b95980Sapatard@mandriva.com unsigned long value; 40f9b95980Sapatard@mandriva.com 41f9b95980Sapatard@mandriva.com switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 42f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_RIGHT_J: 43f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_RJ; 44f9b95980Sapatard@mandriva.com break; 45f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_LEFT_J: 46f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_LJ; 47f9b95980Sapatard@mandriva.com break; 48f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_I2S: 49f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_I2S; 50f9b95980Sapatard@mandriva.com break; 51f9b95980Sapatard@mandriva.com default: 52f9b95980Sapatard@mandriva.com return -EINVAL; 53f9b95980Sapatard@mandriva.com } 54f9b95980Sapatard@mandriva.com 55f9b95980Sapatard@mandriva.com /* 56f9b95980Sapatard@mandriva.com * Set same format for playback and record 57f9b95980Sapatard@mandriva.com * This avoids some troubles. 58f9b95980Sapatard@mandriva.com */ 59f9b95980Sapatard@mandriva.com value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL); 60f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 61f9b95980Sapatard@mandriva.com value |= mask; 62f9b95980Sapatard@mandriva.com writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL); 63f9b95980Sapatard@mandriva.com 64f9b95980Sapatard@mandriva.com value = readl(priv->io+KIRKWOOD_I2S_RECCTL); 65f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 66f9b95980Sapatard@mandriva.com value |= mask; 67f9b95980Sapatard@mandriva.com writel(value, priv->io+KIRKWOOD_I2S_RECCTL); 68f9b95980Sapatard@mandriva.com 69f9b95980Sapatard@mandriva.com return 0; 70f9b95980Sapatard@mandriva.com } 71f9b95980Sapatard@mandriva.com 72f9b95980Sapatard@mandriva.com static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate) 73f9b95980Sapatard@mandriva.com { 74f9b95980Sapatard@mandriva.com unsigned long value; 75f9b95980Sapatard@mandriva.com 76f9b95980Sapatard@mandriva.com value = KIRKWOOD_DCO_CTL_OFFSET_0; 77f9b95980Sapatard@mandriva.com switch (rate) { 78f9b95980Sapatard@mandriva.com default: 79f9b95980Sapatard@mandriva.com case 44100: 80f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_11; 81f9b95980Sapatard@mandriva.com break; 82f9b95980Sapatard@mandriva.com case 48000: 83f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_12; 84f9b95980Sapatard@mandriva.com break; 85f9b95980Sapatard@mandriva.com case 96000: 86f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_24; 87f9b95980Sapatard@mandriva.com break; 88f9b95980Sapatard@mandriva.com } 89f9b95980Sapatard@mandriva.com writel(value, io + KIRKWOOD_DCO_CTL); 90f9b95980Sapatard@mandriva.com 91f9b95980Sapatard@mandriva.com /* wait for dco locked */ 92f9b95980Sapatard@mandriva.com do { 93f9b95980Sapatard@mandriva.com cpu_relax(); 94f9b95980Sapatard@mandriva.com value = readl(io + KIRKWOOD_DCO_SPCR_STATUS); 952424d458SRussell King value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK; 96f9b95980Sapatard@mandriva.com } while (value == 0); 97f9b95980Sapatard@mandriva.com } 98f9b95980Sapatard@mandriva.com 99363589bfSRussell King static void kirkwood_set_rate(struct snd_soc_dai *dai, 100363589bfSRussell King struct kirkwood_dma_data *priv, unsigned long rate) 101363589bfSRussell King { 102363589bfSRussell King uint32_t clks_ctrl; 103363589bfSRussell King 104363589bfSRussell King if (rate == 44100 || rate == 48000 || rate == 96000) { 1058a537f85SJean-Francois Moine /* use internal dco for the supported rates 1068a537f85SJean-Francois Moine * defined in kirkwood_i2s_dai */ 107363589bfSRussell King dev_dbg(dai->dev, "%s: dco set rate = %lu\n", 108363589bfSRussell King __func__, rate); 109363589bfSRussell King kirkwood_set_dco(priv->io, rate); 110363589bfSRussell King 111363589bfSRussell King clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; 1128a537f85SJean-Francois Moine } else { 1138a537f85SJean-Francois Moine /* use the external clock for the other rates 1148a537f85SJean-Francois Moine * defined in kirkwood_i2s_dai_extclk */ 115363589bfSRussell King dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", 116363589bfSRussell King __func__, rate, 256 * rate); 117363589bfSRussell King clk_set_rate(priv->extclk, 256 * rate); 118363589bfSRussell King 119363589bfSRussell King clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK; 120363589bfSRussell King } 121363589bfSRussell King writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL); 122363589bfSRussell King } 123363589bfSRussell King 124f0fba2adSLiam Girdwood static int kirkwood_i2s_startup(struct snd_pcm_substream *substream, 125f0fba2adSLiam Girdwood struct snd_soc_dai *dai) 126f0fba2adSLiam Girdwood { 127f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 128f0fba2adSLiam Girdwood 129f0fba2adSLiam Girdwood snd_soc_dai_set_dma_data(dai, substream, priv); 130f0fba2adSLiam Girdwood return 0; 131f0fba2adSLiam Girdwood } 132f0fba2adSLiam Girdwood 133f9b95980Sapatard@mandriva.com static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, 134f9b95980Sapatard@mandriva.com struct snd_pcm_hw_params *params, 135f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai) 136f9b95980Sapatard@mandriva.com { 137f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 138d8d11ba5SRussell King uint32_t ctl_play, ctl_rec; 139d8d11ba5SRussell King unsigned int i2s_reg; 140d8d11ba5SRussell King unsigned long i2s_value; 141f9b95980Sapatard@mandriva.com 142f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 143f9b95980Sapatard@mandriva.com i2s_reg = KIRKWOOD_I2S_PLAYCTL; 144f9b95980Sapatard@mandriva.com } else { 145f9b95980Sapatard@mandriva.com i2s_reg = KIRKWOOD_I2S_RECCTL; 146f9b95980Sapatard@mandriva.com } 147f9b95980Sapatard@mandriva.com 148363589bfSRussell King kirkwood_set_rate(dai, priv, params_rate(params)); 149f9b95980Sapatard@mandriva.com 150f9b95980Sapatard@mandriva.com i2s_value = readl(priv->io+i2s_reg); 151f9b95980Sapatard@mandriva.com i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; 152f9b95980Sapatard@mandriva.com 153f9b95980Sapatard@mandriva.com /* 154f9b95980Sapatard@mandriva.com * Size settings in play/rec i2s control regs and play/rec control 155f9b95980Sapatard@mandriva.com * regs must be the same. 156f9b95980Sapatard@mandriva.com */ 157f9b95980Sapatard@mandriva.com switch (params_format(params)) { 158f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S16_LE: 159f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; 160d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C | 161d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 162d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C | 163d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 164f9b95980Sapatard@mandriva.com break; 165f9b95980Sapatard@mandriva.com /* 166f9b95980Sapatard@mandriva.com * doesn't work... S20_3LE != kirkwood 20bit format ? 167f9b95980Sapatard@mandriva.com * 168f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S20_3LE: 169f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; 170d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 | 171d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 172d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_20 | 173d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 174f9b95980Sapatard@mandriva.com break; 175f9b95980Sapatard@mandriva.com */ 176f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S24_LE: 177f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; 178d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 | 179d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 180d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_24 | 181d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 182f9b95980Sapatard@mandriva.com break; 183f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S32_LE: 184f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; 185d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 | 186d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 187d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_32 | 188d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 189f9b95980Sapatard@mandriva.com break; 190f9b95980Sapatard@mandriva.com default: 191f9b95980Sapatard@mandriva.com return -EINVAL; 192f9b95980Sapatard@mandriva.com } 193dfe4c936Sarnaud.patard@rtp-net.org 194dfe4c936Sarnaud.patard@rtp-net.org if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 195dfe4c936Sarnaud.patard@rtp-net.org if (params_channels(params) == 1) 196d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH; 197dfe4c936Sarnaud.patard@rtp-net.org else 198d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; 199d8d11ba5SRussell King 200d8d11ba5SRussell King priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | 201d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN | 202d8d11ba5SRussell King KIRKWOOD_PLAYCTL_SPDIF_EN | 203d8d11ba5SRussell King KIRKWOOD_PLAYCTL_SIZE_MASK); 204d8d11ba5SRussell King priv->ctl_play |= ctl_play; 205d8d11ba5SRussell King } else { 206d8d11ba5SRussell King priv->ctl_rec &= ~KIRKWOOD_RECCTL_SIZE_MASK; 207d8d11ba5SRussell King priv->ctl_rec |= ctl_rec; 208dfe4c936Sarnaud.patard@rtp-net.org } 209dfe4c936Sarnaud.patard@rtp-net.org 210f9b95980Sapatard@mandriva.com writel(i2s_value, priv->io+i2s_reg); 211f9b95980Sapatard@mandriva.com 212f9b95980Sapatard@mandriva.com return 0; 213f9b95980Sapatard@mandriva.com } 214f9b95980Sapatard@mandriva.com 215f9b95980Sapatard@mandriva.com static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, 216f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai) 217f9b95980Sapatard@mandriva.com { 218f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 219982b604bSRussell King uint32_t ctl, value; 220f9b95980Sapatard@mandriva.com 221982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 222982b604bSRussell King if (ctl & KIRKWOOD_PLAYCTL_PAUSE) { 223982b604bSRussell King unsigned timeout = 5000; 224f9b95980Sapatard@mandriva.com /* 225982b604bSRussell King * The Armada510 spec says that if we enter pause mode, the 226982b604bSRussell King * busy bit must be read back as clear _twice_. Make sure 227982b604bSRussell King * we respect that otherwise we get DMA underruns. 228f9b95980Sapatard@mandriva.com */ 229982b604bSRussell King do { 230982b604bSRussell King value = ctl; 231982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 232982b604bSRussell King if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) 233982b604bSRussell King break; 234982b604bSRussell King udelay(1); 235982b604bSRussell King } while (timeout--); 236982b604bSRussell King 237982b604bSRussell King if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) 238982b604bSRussell King dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", 239982b604bSRussell King ctl); 240982b604bSRussell King } 241f9b95980Sapatard@mandriva.com 242f9b95980Sapatard@mandriva.com switch (cmd) { 243f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_START: 244d8d11ba5SRussell King /* configure */ 245d8d11ba5SRussell King ctl = priv->ctl_play; 246d8d11ba5SRussell King value = ctl & ~(KIRKWOOD_PLAYCTL_I2S_EN | 247d8d11ba5SRussell King KIRKWOOD_PLAYCTL_SPDIF_EN); 248d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_PLAYCTL); 249d8d11ba5SRussell King 250d8d11ba5SRussell King /* enable interrupts */ 251f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 252f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; 253f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 254f9b95980Sapatard@mandriva.com 255d8d11ba5SRussell King /* enable playback */ 256982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 257f9b95980Sapatard@mandriva.com break; 258f9b95980Sapatard@mandriva.com 259f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP: 260f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */ 261982b604bSRussell King ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; 262982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 263f9b95980Sapatard@mandriva.com 264f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 265f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; 266f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 267f9b95980Sapatard@mandriva.com 268f9b95980Sapatard@mandriva.com /* disable all playbacks */ 269982b604bSRussell King ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); 270982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 271f9b95980Sapatard@mandriva.com break; 272f9b95980Sapatard@mandriva.com 273f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 274f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND: 275982b604bSRussell King ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; 276982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 277f9b95980Sapatard@mandriva.com break; 278f9b95980Sapatard@mandriva.com 279f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME: 280f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 281982b604bSRussell King ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE); 282982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 283f9b95980Sapatard@mandriva.com break; 284f9b95980Sapatard@mandriva.com 285f9b95980Sapatard@mandriva.com default: 286f9b95980Sapatard@mandriva.com return -EINVAL; 287f9b95980Sapatard@mandriva.com } 288f9b95980Sapatard@mandriva.com 289f9b95980Sapatard@mandriva.com return 0; 290f9b95980Sapatard@mandriva.com } 291f9b95980Sapatard@mandriva.com 292f9b95980Sapatard@mandriva.com static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, 293f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai) 294f9b95980Sapatard@mandriva.com { 295f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 296d8d11ba5SRussell King uint32_t ctl, value; 297f9b95980Sapatard@mandriva.com 298f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 299f9b95980Sapatard@mandriva.com 300f9b95980Sapatard@mandriva.com switch (cmd) { 301f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_START: 302d8d11ba5SRussell King /* configure */ 303d8d11ba5SRussell King ctl = priv->ctl_rec; 304d8d11ba5SRussell King value = ctl & ~KIRKWOOD_RECCTL_I2S_EN; 305d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_RECCTL); 306d8d11ba5SRussell King 307d8d11ba5SRussell King /* enable interrupts */ 308f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 309f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_REC_BYTES; 310f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 311f9b95980Sapatard@mandriva.com 312d8d11ba5SRussell King /* enable record */ 313d8d11ba5SRussell King writel(ctl, priv->io + KIRKWOOD_RECCTL); 314f9b95980Sapatard@mandriva.com break; 315f9b95980Sapatard@mandriva.com 316f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP: 317f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */ 318f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 319b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 320f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 321f9b95980Sapatard@mandriva.com 322f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 323f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; 324f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 325f9b95980Sapatard@mandriva.com 326f9b95980Sapatard@mandriva.com /* disable all records */ 327f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 328f9b95980Sapatard@mandriva.com value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN); 329f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 330f9b95980Sapatard@mandriva.com break; 331f9b95980Sapatard@mandriva.com 332f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 333f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND: 334f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 335b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 336f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 337f9b95980Sapatard@mandriva.com break; 338f9b95980Sapatard@mandriva.com 339f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME: 340f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 341f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 342b424ec95Sarnaud.patard@rtp-net.org value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); 343f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 344f9b95980Sapatard@mandriva.com break; 345f9b95980Sapatard@mandriva.com 346f9b95980Sapatard@mandriva.com default: 347f9b95980Sapatard@mandriva.com return -EINVAL; 348f9b95980Sapatard@mandriva.com } 349f9b95980Sapatard@mandriva.com 350f9b95980Sapatard@mandriva.com return 0; 351f9b95980Sapatard@mandriva.com } 352f9b95980Sapatard@mandriva.com 353f9b95980Sapatard@mandriva.com static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 354f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai) 355f9b95980Sapatard@mandriva.com { 356f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 357f9b95980Sapatard@mandriva.com return kirkwood_i2s_play_trigger(substream, cmd, dai); 358f9b95980Sapatard@mandriva.com else 359f9b95980Sapatard@mandriva.com return kirkwood_i2s_rec_trigger(substream, cmd, dai); 360f9b95980Sapatard@mandriva.com 361f9b95980Sapatard@mandriva.com return 0; 362f9b95980Sapatard@mandriva.com } 363f9b95980Sapatard@mandriva.com 364f0fba2adSLiam Girdwood static int kirkwood_i2s_probe(struct snd_soc_dai *dai) 365f9b95980Sapatard@mandriva.com { 366f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 367f9b95980Sapatard@mandriva.com unsigned long value; 368f9b95980Sapatard@mandriva.com unsigned int reg_data; 369f9b95980Sapatard@mandriva.com 370f9b95980Sapatard@mandriva.com /* put system in a "safe" state : */ 371f9b95980Sapatard@mandriva.com /* disable audio interrupts */ 372f9b95980Sapatard@mandriva.com writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); 373f9b95980Sapatard@mandriva.com writel(0, priv->io + KIRKWOOD_INT_MASK); 374f9b95980Sapatard@mandriva.com 375f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200); 376f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8)); 377f9b95980Sapatard@mandriva.com reg_data |= 0x111D18; 378f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200); 379f9b95980Sapatard@mandriva.com 380f9b95980Sapatard@mandriva.com msleep(500); 381f9b95980Sapatard@mandriva.com 382f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200); 383f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8)); 384f9b95980Sapatard@mandriva.com reg_data |= 0x111D18; 385f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200); 386f9b95980Sapatard@mandriva.com 387f9b95980Sapatard@mandriva.com /* disable playback/record */ 388f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_PLAYCTL); 389f9b95980Sapatard@mandriva.com value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN); 390f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_PLAYCTL); 391f9b95980Sapatard@mandriva.com 392f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 393f9b95980Sapatard@mandriva.com value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN); 394f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 395f9b95980Sapatard@mandriva.com 396f9b95980Sapatard@mandriva.com return 0; 397f9b95980Sapatard@mandriva.com 398f9b95980Sapatard@mandriva.com } 399f9b95980Sapatard@mandriva.com 40085e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { 401f0fba2adSLiam Girdwood .startup = kirkwood_i2s_startup, 402f9b95980Sapatard@mandriva.com .trigger = kirkwood_i2s_trigger, 403f9b95980Sapatard@mandriva.com .hw_params = kirkwood_i2s_hw_params, 404f9b95980Sapatard@mandriva.com .set_fmt = kirkwood_i2s_set_fmt, 405f9b95980Sapatard@mandriva.com }; 406f9b95980Sapatard@mandriva.com 407f9b95980Sapatard@mandriva.com 408f0fba2adSLiam Girdwood static struct snd_soc_dai_driver kirkwood_i2s_dai = { 409f9b95980Sapatard@mandriva.com .probe = kirkwood_i2s_probe, 410f9b95980Sapatard@mandriva.com .playback = { 411f9b95980Sapatard@mandriva.com .channels_min = 1, 412f9b95980Sapatard@mandriva.com .channels_max = 2, 4139e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4149e12cbd9SMark Brown SNDRV_PCM_RATE_96000, 415363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 416363589bfSRussell King }, 417f9b95980Sapatard@mandriva.com .capture = { 418f9b95980Sapatard@mandriva.com .channels_min = 1, 419f9b95980Sapatard@mandriva.com .channels_max = 2, 4209e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4219e12cbd9SMark Brown SNDRV_PCM_RATE_96000, 422363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 423363589bfSRussell King }, 424363589bfSRussell King .ops = &kirkwood_i2s_dai_ops, 425363589bfSRussell King }; 426363589bfSRussell King 427363589bfSRussell King static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = { 428363589bfSRussell King .probe = kirkwood_i2s_probe, 429363589bfSRussell King .playback = { 430363589bfSRussell King .channels_min = 1, 431363589bfSRussell King .channels_max = 2, 432363589bfSRussell King .rates = SNDRV_PCM_RATE_8000_192000 | 433363589bfSRussell King SNDRV_PCM_RATE_CONTINUOUS | 434363589bfSRussell King SNDRV_PCM_RATE_KNOT, 435363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 436363589bfSRussell King }, 437363589bfSRussell King .capture = { 438363589bfSRussell King .channels_min = 1, 439363589bfSRussell King .channels_max = 2, 440363589bfSRussell King .rates = SNDRV_PCM_RATE_8000_192000 | 441363589bfSRussell King SNDRV_PCM_RATE_CONTINUOUS | 442363589bfSRussell King SNDRV_PCM_RATE_KNOT, 443363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 444363589bfSRussell King }, 445f9b95980Sapatard@mandriva.com .ops = &kirkwood_i2s_dai_ops, 446f9b95980Sapatard@mandriva.com }; 447f9b95980Sapatard@mandriva.com 44883d85f53SKuninori Morimoto static const struct snd_soc_component_driver kirkwood_i2s_component = { 44983d85f53SKuninori Morimoto .name = DRV_NAME, 45083d85f53SKuninori Morimoto }; 45183d85f53SKuninori Morimoto 45234e15fbdSBill Pemberton static int kirkwood_i2s_dev_probe(struct platform_device *pdev) 453f9b95980Sapatard@mandriva.com { 454363589bfSRussell King struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data; 455363589bfSRussell King struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai; 456f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv; 457363589bfSRussell King struct resource *mem; 458f9b95980Sapatard@mandriva.com int err; 459f9b95980Sapatard@mandriva.com 460dbc517bfSRussell King priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 461f9b95980Sapatard@mandriva.com if (!priv) { 462f9b95980Sapatard@mandriva.com dev_err(&pdev->dev, "allocation failed\n"); 463dbc517bfSRussell King return -ENOMEM; 464f9b95980Sapatard@mandriva.com } 465f0fba2adSLiam Girdwood dev_set_drvdata(&pdev->dev, priv); 466f9b95980Sapatard@mandriva.com 467f9b95980Sapatard@mandriva.com mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 468b25b5aa0SThierry Reding priv->io = devm_ioremap_resource(&pdev->dev, mem); 469b25b5aa0SThierry Reding if (IS_ERR(priv->io)) 470b25b5aa0SThierry Reding return PTR_ERR(priv->io); 471f9b95980Sapatard@mandriva.com 472f9b95980Sapatard@mandriva.com priv->irq = platform_get_irq(pdev, 0); 473f9b95980Sapatard@mandriva.com if (priv->irq <= 0) { 474f9b95980Sapatard@mandriva.com dev_err(&pdev->dev, "platform_get_irq failed\n"); 475dbc517bfSRussell King return -ENXIO; 476f9b95980Sapatard@mandriva.com } 477f9b95980Sapatard@mandriva.com 478d4d9820bSAxel Lin if (!data) { 479f9b95980Sapatard@mandriva.com dev_err(&pdev->dev, "no platform data ?!\n"); 480dbc517bfSRussell King return -EINVAL; 481f9b95980Sapatard@mandriva.com } 482f9b95980Sapatard@mandriva.com 483f9b95980Sapatard@mandriva.com priv->burst = data->burst; 484f9b95980Sapatard@mandriva.com 485dbc517bfSRussell King priv->clk = devm_clk_get(&pdev->dev, NULL); 486e919c716SAndrew Lunn if (IS_ERR(priv->clk)) { 487e919c716SAndrew Lunn dev_err(&pdev->dev, "no clock\n"); 488dbc517bfSRussell King return PTR_ERR(priv->clk); 489e919c716SAndrew Lunn } 490dbc517bfSRussell King 491dbc517bfSRussell King err = clk_prepare_enable(priv->clk); 492dbc517bfSRussell King if (err < 0) 493dbc517bfSRussell King return err; 494e919c716SAndrew Lunn 4954734dc96SMark Brown priv->extclk = devm_clk_get(&pdev->dev, "extclk"); 496d8d11ba5SRussell King if (!IS_ERR(priv->extclk)) { 497d8d11ba5SRussell King if (priv->extclk == priv->clk) { 498*af64d734SRussell King devm_clk_put(&pdev->dev, priv->extclk); 499d8d11ba5SRussell King priv->extclk = ERR_PTR(-EINVAL); 500d8d11ba5SRussell King } else { 501d8d11ba5SRussell King dev_info(&pdev->dev, "found external clock\n"); 502d8d11ba5SRussell King clk_prepare_enable(priv->extclk); 503d8d11ba5SRussell King soc_dai = &kirkwood_i2s_dai_extclk; 504d8d11ba5SRussell King } 505d8d11ba5SRussell King } 506d8d11ba5SRussell King 507d8d11ba5SRussell King /* Some sensible defaults - this reflects the powerup values */ 508d8d11ba5SRussell King priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24; 509d8d11ba5SRussell King priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; 510d8d11ba5SRussell King 511d8d11ba5SRussell King /* Select the burst size */ 512d8d11ba5SRussell King if (data->burst == 32) { 513d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; 514d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; 515d8d11ba5SRussell King } else { 516d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128; 517d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128; 518d8d11ba5SRussell King } 519d8d11ba5SRussell King 52083d85f53SKuninori Morimoto err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component, 52183d85f53SKuninori Morimoto soc_dai, 1); 522baffab28SSimon Baatz if (!err) 523baffab28SSimon Baatz return 0; 52483d85f53SKuninori Morimoto dev_err(&pdev->dev, "snd_soc_register_component failed\n"); 525baffab28SSimon Baatz 5264734dc96SMark Brown if (!IS_ERR(priv->extclk)) 527363589bfSRussell King clk_disable_unprepare(priv->extclk); 528baffab28SSimon Baatz clk_disable_unprepare(priv->clk); 529f9b95980Sapatard@mandriva.com 530f9b95980Sapatard@mandriva.com return err; 531f9b95980Sapatard@mandriva.com } 532f9b95980Sapatard@mandriva.com 53334e15fbdSBill Pemberton static int kirkwood_i2s_dev_remove(struct platform_device *pdev) 534f9b95980Sapatard@mandriva.com { 535f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); 536f0fba2adSLiam Girdwood 53783d85f53SKuninori Morimoto snd_soc_unregister_component(&pdev->dev); 538e919c716SAndrew Lunn 5394734dc96SMark Brown if (!IS_ERR(priv->extclk)) 540363589bfSRussell King clk_disable_unprepare(priv->extclk); 541e919c716SAndrew Lunn clk_disable_unprepare(priv->clk); 542f0fba2adSLiam Girdwood 543f9b95980Sapatard@mandriva.com return 0; 544f9b95980Sapatard@mandriva.com } 545f9b95980Sapatard@mandriva.com 546f9b95980Sapatard@mandriva.com static struct platform_driver kirkwood_i2s_driver = { 547f9b95980Sapatard@mandriva.com .probe = kirkwood_i2s_dev_probe, 54834e15fbdSBill Pemberton .remove = kirkwood_i2s_dev_remove, 549f9b95980Sapatard@mandriva.com .driver = { 550f9b95980Sapatard@mandriva.com .name = DRV_NAME, 551f9b95980Sapatard@mandriva.com .owner = THIS_MODULE, 552f9b95980Sapatard@mandriva.com }, 553f9b95980Sapatard@mandriva.com }; 554f9b95980Sapatard@mandriva.com 55541b10225SAxel Lin module_platform_driver(kirkwood_i2s_driver); 556f9b95980Sapatard@mandriva.com 557f9b95980Sapatard@mandriva.com /* Module information */ 55869737897SArnaud Patard (Rtp) MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); 559f9b95980Sapatard@mandriva.com MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); 560f9b95980Sapatard@mandriva.com MODULE_LICENSE("GPL"); 561f9b95980Sapatard@mandriva.com MODULE_ALIAS("platform:kirkwood-i2s"); 562