1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2f9b95980Sapatard@mandriva.com /* 3f9b95980Sapatard@mandriva.com * kirkwood-i2s.c 4f9b95980Sapatard@mandriva.com * 5f9b95980Sapatard@mandriva.com * (c) 2010 Arnaud Patard <apatard@mandriva.com> 669737897SArnaud Patard (Rtp) * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 7f9b95980Sapatard@mandriva.com */ 8f9b95980Sapatard@mandriva.com 9f9b95980Sapatard@mandriva.com #include <linux/init.h> 10f9b95980Sapatard@mandriva.com #include <linux/module.h> 11f9b95980Sapatard@mandriva.com #include <linux/platform_device.h> 12f9b95980Sapatard@mandriva.com #include <linux/io.h> 13f9b95980Sapatard@mandriva.com #include <linux/slab.h> 14f9b95980Sapatard@mandriva.com #include <linux/mbus.h> 15f9b95980Sapatard@mandriva.com #include <linux/delay.h> 16e919c716SAndrew Lunn #include <linux/clk.h> 17f9b95980Sapatard@mandriva.com #include <sound/pcm.h> 18f9b95980Sapatard@mandriva.com #include <sound/pcm_params.h> 19f9b95980Sapatard@mandriva.com #include <sound/soc.h> 20c02cecb9SArnd Bergmann #include <linux/platform_data/asoc-kirkwood.h> 21eb632318SJean-Francois Moine #include <linux/of.h> 22eb632318SJean-Francois Moine 23f9b95980Sapatard@mandriva.com #include "kirkwood.h" 24f9b95980Sapatard@mandriva.com 25f9b95980Sapatard@mandriva.com #define KIRKWOOD_I2S_FORMATS \ 26f9b95980Sapatard@mandriva.com (SNDRV_PCM_FMTBIT_S16_LE | \ 27f9b95980Sapatard@mandriva.com SNDRV_PCM_FMTBIT_S24_LE | \ 28f9b95980Sapatard@mandriva.com SNDRV_PCM_FMTBIT_S32_LE) 29f9b95980Sapatard@mandriva.com 301c195ddbSJean-Francois Moine #define KIRKWOOD_SPDIF_FORMATS \ 311c195ddbSJean-Francois Moine (SNDRV_PCM_FMTBIT_S16_LE | \ 321c195ddbSJean-Francois Moine SNDRV_PCM_FMTBIT_S24_LE) 331c195ddbSJean-Francois Moine 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 1041f1b6579SJean-Francois Moine if (IS_ERR(priv->extclk)) { 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 | 16175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_I2S_EN | 16275b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_EN; 163d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C | 16475b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_I2S_EN | 16575b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_SPDIF_EN; 166f9b95980Sapatard@mandriva.com break; 167f9b95980Sapatard@mandriva.com /* 168f9b95980Sapatard@mandriva.com * doesn't work... S20_3LE != kirkwood 20bit format ? 169f9b95980Sapatard@mandriva.com * 170f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S20_3LE: 171f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; 172d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 | 173d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 174d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_20 | 175d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 176f9b95980Sapatard@mandriva.com break; 177f9b95980Sapatard@mandriva.com */ 178f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S24_LE: 179f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; 180d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 | 18175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_I2S_EN | 18275b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_EN; 183d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_24 | 18475b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_I2S_EN | 18575b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_SPDIF_EN; 186f9b95980Sapatard@mandriva.com break; 187f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S32_LE: 188f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; 189d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 | 190d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 191d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_32 | 192d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 193f9b95980Sapatard@mandriva.com break; 194f9b95980Sapatard@mandriva.com default: 195f9b95980Sapatard@mandriva.com return -EINVAL; 196f9b95980Sapatard@mandriva.com } 197dfe4c936Sarnaud.patard@rtp-net.org 198dfe4c936Sarnaud.patard@rtp-net.org if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 199dfe4c936Sarnaud.patard@rtp-net.org if (params_channels(params) == 1) 200d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH; 201dfe4c936Sarnaud.patard@rtp-net.org else 202d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; 203d8d11ba5SRussell King 204d8d11ba5SRussell King priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | 205db43b16fSRussell King KIRKWOOD_PLAYCTL_ENABLE_MASK | 206d8d11ba5SRussell King KIRKWOOD_PLAYCTL_SIZE_MASK); 207d8d11ba5SRussell King priv->ctl_play |= ctl_play; 208d8d11ba5SRussell King } else { 20967721906SRussell King priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK | 21067721906SRussell King KIRKWOOD_RECCTL_SIZE_MASK); 211d8d11ba5SRussell King priv->ctl_rec |= ctl_rec; 212dfe4c936Sarnaud.patard@rtp-net.org } 213dfe4c936Sarnaud.patard@rtp-net.org 214f9b95980Sapatard@mandriva.com writel(i2s_value, priv->io+i2s_reg); 215f9b95980Sapatard@mandriva.com 216f9b95980Sapatard@mandriva.com return 0; 217f9b95980Sapatard@mandriva.com } 218f9b95980Sapatard@mandriva.com 2192fbc3821SRussell King static unsigned kirkwood_i2s_play_mute(unsigned ctl) 2202fbc3821SRussell King { 2212fbc3821SRussell King if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN)) 2222fbc3821SRussell King ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE; 2232fbc3821SRussell King if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN)) 2242fbc3821SRussell King ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE; 2252fbc3821SRussell King return ctl; 2262fbc3821SRussell King } 2272fbc3821SRussell King 228f9b95980Sapatard@mandriva.com static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, 229f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai) 230f9b95980Sapatard@mandriva.com { 231920ec4e5SRussell King struct snd_pcm_runtime *runtime = substream->runtime; 232f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 233982b604bSRussell King uint32_t ctl, value; 234f9b95980Sapatard@mandriva.com 235982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 2364d2097e5SRussell King if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) { 237982b604bSRussell King unsigned timeout = 5000; 238f9b95980Sapatard@mandriva.com /* 239982b604bSRussell King * The Armada510 spec says that if we enter pause mode, the 240982b604bSRussell King * busy bit must be read back as clear _twice_. Make sure 241982b604bSRussell King * we respect that otherwise we get DMA underruns. 242f9b95980Sapatard@mandriva.com */ 243982b604bSRussell King do { 244982b604bSRussell King value = ctl; 245982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 246982b604bSRussell King if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) 247982b604bSRussell King break; 248982b604bSRussell King udelay(1); 249982b604bSRussell King } while (timeout--); 250982b604bSRussell King 251982b604bSRussell King if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) 252982b604bSRussell King dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", 253982b604bSRussell King ctl); 254982b604bSRussell King } 255f9b95980Sapatard@mandriva.com 2564f6f1478SJean-Francois Moine switch (cmd) { 2574f6f1478SJean-Francois Moine case SNDRV_PCM_TRIGGER_START: 2584f6f1478SJean-Francois Moine /* configure */ 2594f6f1478SJean-Francois Moine ctl = priv->ctl_play; 26075b9b65eSJean-Francois Moine if (dai->id == 0) 26175b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */ 26275b9b65eSJean-Francois Moine else 26375b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */ 2642fbc3821SRussell King ctl = kirkwood_i2s_play_mute(ctl); 265db43b16fSRussell King value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 266d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_PLAYCTL); 267d8d11ba5SRussell King 268d8d11ba5SRussell King /* enable interrupts */ 269920ec4e5SRussell King if (!runtime->no_period_wakeup) { 270f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 271f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; 272f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 273920ec4e5SRussell King } 274f9b95980Sapatard@mandriva.com 275d8d11ba5SRussell King /* enable playback */ 276982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 277f9b95980Sapatard@mandriva.com break; 278f9b95980Sapatard@mandriva.com 279f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP: 280f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */ 28175b9b65eSJean-Francois Moine ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 28275b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE; 283982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 284f9b95980Sapatard@mandriva.com 285f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 286f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; 287f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 288f9b95980Sapatard@mandriva.com 289f9b95980Sapatard@mandriva.com /* disable all playbacks */ 290db43b16fSRussell King ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 291982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 292f9b95980Sapatard@mandriva.com break; 293f9b95980Sapatard@mandriva.com 294f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 295f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND: 29675b9b65eSJean-Francois Moine ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 29775b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE; 298982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 299f9b95980Sapatard@mandriva.com break; 300f9b95980Sapatard@mandriva.com 301f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME: 302f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 30375b9b65eSJean-Francois Moine ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 30475b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE); 3052fbc3821SRussell King ctl = kirkwood_i2s_play_mute(ctl); 306982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 307f9b95980Sapatard@mandriva.com break; 308f9b95980Sapatard@mandriva.com 309f9b95980Sapatard@mandriva.com default: 310f9b95980Sapatard@mandriva.com return -EINVAL; 311f9b95980Sapatard@mandriva.com } 312f9b95980Sapatard@mandriva.com 313f9b95980Sapatard@mandriva.com return 0; 314f9b95980Sapatard@mandriva.com } 315f9b95980Sapatard@mandriva.com 316f9b95980Sapatard@mandriva.com static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, 317f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai) 318f9b95980Sapatard@mandriva.com { 319f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 320d8d11ba5SRussell King uint32_t ctl, value; 321f9b95980Sapatard@mandriva.com 322f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 323f9b95980Sapatard@mandriva.com 324f9b95980Sapatard@mandriva.com switch (cmd) { 325f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_START: 326d8d11ba5SRussell King /* configure */ 327d8d11ba5SRussell King ctl = priv->ctl_rec; 32875b9b65eSJean-Francois Moine if (dai->id == 0) 32975b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */ 33075b9b65eSJean-Francois Moine else 33175b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */ 33275b9b65eSJean-Francois Moine 33352b896cfSRussell King value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK; 334d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_RECCTL); 335d8d11ba5SRussell King 336d8d11ba5SRussell King /* enable interrupts */ 337f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 338f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_REC_BYTES; 339f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 340f9b95980Sapatard@mandriva.com 341d8d11ba5SRussell King /* enable record */ 342d8d11ba5SRussell King writel(ctl, priv->io + KIRKWOOD_RECCTL); 343f9b95980Sapatard@mandriva.com break; 344f9b95980Sapatard@mandriva.com 345f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP: 346f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */ 347f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 348b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 349f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 350f9b95980Sapatard@mandriva.com 351f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 352f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; 353f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 354f9b95980Sapatard@mandriva.com 355f9b95980Sapatard@mandriva.com /* disable all records */ 356f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 35752b896cfSRussell King value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 358f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 359f9b95980Sapatard@mandriva.com break; 360f9b95980Sapatard@mandriva.com 361f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 362f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND: 363f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 364b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 365f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 366f9b95980Sapatard@mandriva.com break; 367f9b95980Sapatard@mandriva.com 368f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME: 369f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 370f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 371b424ec95Sarnaud.patard@rtp-net.org value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); 372f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 373f9b95980Sapatard@mandriva.com break; 374f9b95980Sapatard@mandriva.com 375f9b95980Sapatard@mandriva.com default: 376f9b95980Sapatard@mandriva.com return -EINVAL; 377f9b95980Sapatard@mandriva.com } 378f9b95980Sapatard@mandriva.com 379f9b95980Sapatard@mandriva.com return 0; 380f9b95980Sapatard@mandriva.com } 381f9b95980Sapatard@mandriva.com 382f9b95980Sapatard@mandriva.com static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 383f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai) 384f9b95980Sapatard@mandriva.com { 385f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 386f9b95980Sapatard@mandriva.com return kirkwood_i2s_play_trigger(substream, cmd, dai); 387f9b95980Sapatard@mandriva.com else 388f9b95980Sapatard@mandriva.com return kirkwood_i2s_rec_trigger(substream, cmd, dai); 389f9b95980Sapatard@mandriva.com 390f9b95980Sapatard@mandriva.com return 0; 391f9b95980Sapatard@mandriva.com } 392f9b95980Sapatard@mandriva.com 39375b9b65eSJean-Francois Moine static int kirkwood_i2s_init(struct kirkwood_dma_data *priv) 394f9b95980Sapatard@mandriva.com { 395f9b95980Sapatard@mandriva.com unsigned long value; 396f9b95980Sapatard@mandriva.com unsigned int reg_data; 397f9b95980Sapatard@mandriva.com 398f9b95980Sapatard@mandriva.com /* put system in a "safe" state : */ 399f9b95980Sapatard@mandriva.com /* disable audio interrupts */ 400f9b95980Sapatard@mandriva.com writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); 401f9b95980Sapatard@mandriva.com writel(0, priv->io + KIRKWOOD_INT_MASK); 402f9b95980Sapatard@mandriva.com 403f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200); 404f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8)); 405f9b95980Sapatard@mandriva.com reg_data |= 0x111D18; 406f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200); 407f9b95980Sapatard@mandriva.com 408f9b95980Sapatard@mandriva.com msleep(500); 409f9b95980Sapatard@mandriva.com 410f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200); 411f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8)); 412f9b95980Sapatard@mandriva.com reg_data |= 0x111D18; 413f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200); 414f9b95980Sapatard@mandriva.com 415f9b95980Sapatard@mandriva.com /* disable playback/record */ 416f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_PLAYCTL); 417db43b16fSRussell King value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 418f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_PLAYCTL); 419f9b95980Sapatard@mandriva.com 420f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 42152b896cfSRussell King value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 422f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 423f9b95980Sapatard@mandriva.com 424f9b95980Sapatard@mandriva.com return 0; 425f9b95980Sapatard@mandriva.com 426f9b95980Sapatard@mandriva.com } 427f9b95980Sapatard@mandriva.com 42885e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { 429f0fba2adSLiam Girdwood .startup = kirkwood_i2s_startup, 430f9b95980Sapatard@mandriva.com .trigger = kirkwood_i2s_trigger, 431f9b95980Sapatard@mandriva.com .hw_params = kirkwood_i2s_hw_params, 432f9b95980Sapatard@mandriva.com .set_fmt = kirkwood_i2s_set_fmt, 433f9b95980Sapatard@mandriva.com }; 434f9b95980Sapatard@mandriva.com 43575b9b65eSJean-Francois Moine static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = { 43675b9b65eSJean-Francois Moine { 43775b9b65eSJean-Francois Moine .name = "i2s", 43875b9b65eSJean-Francois Moine .id = 0, 439f9b95980Sapatard@mandriva.com .playback = { 440f9b95980Sapatard@mandriva.com .channels_min = 1, 441f9b95980Sapatard@mandriva.com .channels_max = 2, 4429e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4439e12cbd9SMark Brown SNDRV_PCM_RATE_96000, 444363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 445363589bfSRussell King }, 446f9b95980Sapatard@mandriva.com .capture = { 447f9b95980Sapatard@mandriva.com .channels_min = 1, 448f9b95980Sapatard@mandriva.com .channels_max = 2, 4499e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4509e12cbd9SMark Brown SNDRV_PCM_RATE_96000, 451363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 452363589bfSRussell King }, 453363589bfSRussell King .ops = &kirkwood_i2s_dai_ops, 45475b9b65eSJean-Francois Moine }, 45575b9b65eSJean-Francois Moine { 45675b9b65eSJean-Francois Moine .name = "spdif", 45775b9b65eSJean-Francois Moine .id = 1, 45875b9b65eSJean-Francois Moine .playback = { 45975b9b65eSJean-Francois Moine .channels_min = 1, 46075b9b65eSJean-Francois Moine .channels_max = 2, 46175b9b65eSJean-Francois Moine .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 46275b9b65eSJean-Francois Moine SNDRV_PCM_RATE_96000, 4631c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 46475b9b65eSJean-Francois Moine }, 46575b9b65eSJean-Francois Moine .capture = { 46675b9b65eSJean-Francois Moine .channels_min = 1, 46775b9b65eSJean-Francois Moine .channels_max = 2, 46875b9b65eSJean-Francois Moine .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 46975b9b65eSJean-Francois Moine SNDRV_PCM_RATE_96000, 4701c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 47175b9b65eSJean-Francois Moine }, 47275b9b65eSJean-Francois Moine .ops = &kirkwood_i2s_dai_ops, 47375b9b65eSJean-Francois Moine }, 474363589bfSRussell King }; 475363589bfSRussell King 47675b9b65eSJean-Francois Moine static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = { 47775b9b65eSJean-Francois Moine { 47875b9b65eSJean-Francois Moine .name = "i2s", 47975b9b65eSJean-Francois Moine .id = 0, 480363589bfSRussell King .playback = { 481363589bfSRussell King .channels_min = 1, 482363589bfSRussell King .channels_max = 2, 48302fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 48402fc17c1SJean-Francois Moine .rate_min = 5512, 48502fc17c1SJean-Francois Moine .rate_max = 192000, 486363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 487363589bfSRussell King }, 488363589bfSRussell King .capture = { 489363589bfSRussell King .channels_min = 1, 490363589bfSRussell King .channels_max = 2, 49102fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 49202fc17c1SJean-Francois Moine .rate_min = 5512, 49302fc17c1SJean-Francois Moine .rate_max = 192000, 494363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 495363589bfSRussell King }, 496f9b95980Sapatard@mandriva.com .ops = &kirkwood_i2s_dai_ops, 49775b9b65eSJean-Francois Moine }, 49875b9b65eSJean-Francois Moine { 49975b9b65eSJean-Francois Moine .name = "spdif", 50075b9b65eSJean-Francois Moine .id = 1, 50175b9b65eSJean-Francois Moine .playback = { 50275b9b65eSJean-Francois Moine .channels_min = 1, 50375b9b65eSJean-Francois Moine .channels_max = 2, 50402fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 50502fc17c1SJean-Francois Moine .rate_min = 5512, 50602fc17c1SJean-Francois Moine .rate_max = 192000, 5071c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 50875b9b65eSJean-Francois Moine }, 50975b9b65eSJean-Francois Moine .capture = { 51075b9b65eSJean-Francois Moine .channels_min = 1, 51175b9b65eSJean-Francois Moine .channels_max = 2, 51202fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 51302fc17c1SJean-Francois Moine .rate_min = 5512, 51402fc17c1SJean-Francois Moine .rate_max = 192000, 5151c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 51675b9b65eSJean-Francois Moine }, 51775b9b65eSJean-Francois Moine .ops = &kirkwood_i2s_dai_ops, 51875b9b65eSJean-Francois Moine }, 519f9b95980Sapatard@mandriva.com }; 520f9b95980Sapatard@mandriva.com 52134e15fbdSBill Pemberton static int kirkwood_i2s_dev_probe(struct platform_device *pdev) 522f9b95980Sapatard@mandriva.com { 523363589bfSRussell King struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data; 52475b9b65eSJean-Francois Moine struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai; 525f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv; 526363589bfSRussell King struct resource *mem; 527eb632318SJean-Francois Moine struct device_node *np = pdev->dev.of_node; 528f9b95980Sapatard@mandriva.com int err; 529f9b95980Sapatard@mandriva.com 530dbc517bfSRussell King priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 531c7591edcSMarkus Elfring if (!priv) 532dbc517bfSRussell King return -ENOMEM; 533c7591edcSMarkus Elfring 534f0fba2adSLiam Girdwood dev_set_drvdata(&pdev->dev, priv); 535f9b95980Sapatard@mandriva.com 536f9b95980Sapatard@mandriva.com mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 537b25b5aa0SThierry Reding priv->io = devm_ioremap_resource(&pdev->dev, mem); 538b25b5aa0SThierry Reding if (IS_ERR(priv->io)) 539b25b5aa0SThierry Reding return PTR_ERR(priv->io); 540f9b95980Sapatard@mandriva.com 541f9b95980Sapatard@mandriva.com priv->irq = platform_get_irq(pdev, 0); 542e7b2e30aSGustavo A. R. Silva if (priv->irq < 0) { 543e7b2e30aSGustavo A. R. Silva dev_err(&pdev->dev, "platform_get_irq failed: %d\n", priv->irq); 544e7b2e30aSGustavo A. R. Silva return priv->irq; 545f9b95980Sapatard@mandriva.com } 546f9b95980Sapatard@mandriva.com 547eb632318SJean-Francois Moine if (np) { 548eb632318SJean-Francois Moine priv->burst = 128; /* might be 32 or 128 */ 549eb632318SJean-Francois Moine } else if (data) { 550eb632318SJean-Francois Moine priv->burst = data->burst; 551eb632318SJean-Francois Moine } else { 552eb632318SJean-Francois Moine dev_err(&pdev->dev, "no DT nor platform data ?!\n"); 553dbc517bfSRussell King return -EINVAL; 554f9b95980Sapatard@mandriva.com } 555f9b95980Sapatard@mandriva.com 556eb632318SJean-Francois Moine priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL); 557e919c716SAndrew Lunn if (IS_ERR(priv->clk)) { 558e919c716SAndrew Lunn dev_err(&pdev->dev, "no clock\n"); 559dbc517bfSRussell King return PTR_ERR(priv->clk); 560e919c716SAndrew Lunn } 561dbc517bfSRussell King 562dbc517bfSRussell King err = clk_prepare_enable(priv->clk); 563dbc517bfSRussell King if (err < 0) 564dbc517bfSRussell King return err; 565e919c716SAndrew Lunn 5664734dc96SMark Brown priv->extclk = devm_clk_get(&pdev->dev, "extclk"); 56784aac6c7SJean-Francois Moine if (IS_ERR(priv->extclk)) { 56884aac6c7SJean-Francois Moine if (PTR_ERR(priv->extclk) == -EPROBE_DEFER) 56984aac6c7SJean-Francois Moine return -EPROBE_DEFER; 57084aac6c7SJean-Francois Moine } else { 571aaa6d062SShawn Guo if (clk_is_match(priv->extclk, priv->clk)) { 572af64d734SRussell King devm_clk_put(&pdev->dev, priv->extclk); 573d8d11ba5SRussell King priv->extclk = ERR_PTR(-EINVAL); 574d8d11ba5SRussell King } else { 575d8d11ba5SRussell King dev_info(&pdev->dev, "found external clock\n"); 576d8d11ba5SRussell King clk_prepare_enable(priv->extclk); 57799d8d3baSJean-Francois Moine soc_dai = kirkwood_i2s_dai_extclk; 578d8d11ba5SRussell King } 579d8d11ba5SRussell King } 580d8d11ba5SRussell King 581d8d11ba5SRussell King /* Some sensible defaults - this reflects the powerup values */ 582d8d11ba5SRussell King priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24; 583d8d11ba5SRussell King priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; 584d8d11ba5SRussell King 585d8d11ba5SRussell King /* Select the burst size */ 586eb632318SJean-Francois Moine if (priv->burst == 32) { 587d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; 588d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; 589d8d11ba5SRussell King } else { 590d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128; 591d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128; 592d8d11ba5SRussell King } 593d8d11ba5SRussell King 594f98fc0f8SKuninori Morimoto err = devm_snd_soc_register_component(&pdev->dev, &kirkwood_soc_component, 59575b9b65eSJean-Francois Moine soc_dai, 2); 59664ddf1f8SRussell King if (err) { 59783d85f53SKuninori Morimoto dev_err(&pdev->dev, "snd_soc_register_component failed\n"); 59864ddf1f8SRussell King goto err_component; 59964ddf1f8SRussell King } 600baffab28SSimon Baatz 60175b9b65eSJean-Francois Moine kirkwood_i2s_init(priv); 60275b9b65eSJean-Francois Moine 60364ddf1f8SRussell King return 0; 604f98fc0f8SKuninori Morimoto 60564ddf1f8SRussell King err_component: 6064734dc96SMark Brown if (!IS_ERR(priv->extclk)) 607363589bfSRussell King clk_disable_unprepare(priv->extclk); 608baffab28SSimon Baatz clk_disable_unprepare(priv->clk); 609f9b95980Sapatard@mandriva.com 610f9b95980Sapatard@mandriva.com return err; 611f9b95980Sapatard@mandriva.com } 612f9b95980Sapatard@mandriva.com 61334e15fbdSBill Pemberton static int kirkwood_i2s_dev_remove(struct platform_device *pdev) 614f9b95980Sapatard@mandriva.com { 615f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); 616f0fba2adSLiam Girdwood 6174734dc96SMark Brown if (!IS_ERR(priv->extclk)) 618363589bfSRussell King clk_disable_unprepare(priv->extclk); 619e919c716SAndrew Lunn clk_disable_unprepare(priv->clk); 620f0fba2adSLiam Girdwood 621f9b95980Sapatard@mandriva.com return 0; 622f9b95980Sapatard@mandriva.com } 623f9b95980Sapatard@mandriva.com 624eb632318SJean-Francois Moine #ifdef CONFIG_OF 6257f2c52afSFabian Frederick static const struct of_device_id mvebu_audio_of_match[] = { 626d098b2f0SThomas Petazzoni { .compatible = "marvell,kirkwood-audio" }, 627d098b2f0SThomas Petazzoni { .compatible = "marvell,dove-audio" }, 6289a0d5113SThomas Petazzoni { .compatible = "marvell,armada370-audio" }, 629eb632318SJean-Francois Moine { } 630eb632318SJean-Francois Moine }; 631eb632318SJean-Francois Moine MODULE_DEVICE_TABLE(of, mvebu_audio_of_match); 632eb632318SJean-Francois Moine #endif 633eb632318SJean-Francois Moine 634f9b95980Sapatard@mandriva.com static struct platform_driver kirkwood_i2s_driver = { 635f9b95980Sapatard@mandriva.com .probe = kirkwood_i2s_dev_probe, 63634e15fbdSBill Pemberton .remove = kirkwood_i2s_dev_remove, 637f9b95980Sapatard@mandriva.com .driver = { 638f9b95980Sapatard@mandriva.com .name = DRV_NAME, 639eb632318SJean-Francois Moine .of_match_table = of_match_ptr(mvebu_audio_of_match), 640f9b95980Sapatard@mandriva.com }, 641f9b95980Sapatard@mandriva.com }; 642f9b95980Sapatard@mandriva.com 64341b10225SAxel Lin module_platform_driver(kirkwood_i2s_driver); 644f9b95980Sapatard@mandriva.com 645f9b95980Sapatard@mandriva.com /* Module information */ 64669737897SArnaud Patard (Rtp) MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); 647f9b95980Sapatard@mandriva.com MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); 648f9b95980Sapatard@mandriva.com MODULE_LICENSE("GPL"); 64964ddf1f8SRussell King MODULE_ALIAS("platform:mvebu-audio"); 650