12874c5fdSThomas 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 34*2adfc688SMarcin Wojtas /* These registers are relative to the second register region - 35*2adfc688SMarcin Wojtas * audio pll configuration. 36*2adfc688SMarcin Wojtas */ 37*2adfc688SMarcin Wojtas #define A38X_PLL_CONF_REG0 0x0 38*2adfc688SMarcin Wojtas #define A38X_PLL_FB_CLK_DIV_OFFSET 10 39*2adfc688SMarcin Wojtas #define A38X_PLL_FB_CLK_DIV_MASK 0x7fc00 40*2adfc688SMarcin Wojtas #define A38X_PLL_CONF_REG1 0x4 41*2adfc688SMarcin Wojtas #define A38X_PLL_FREQ_OFFSET_MASK 0xffff 42*2adfc688SMarcin Wojtas #define A38X_PLL_FREQ_OFFSET_VALID BIT(16) 43*2adfc688SMarcin Wojtas #define A38X_PLL_SW_RESET BIT(31) 44*2adfc688SMarcin Wojtas #define A38X_PLL_CONF_REG2 0x8 45*2adfc688SMarcin Wojtas #define A38X_PLL_AUDIO_POSTDIV_MASK 0x7f 46*2adfc688SMarcin Wojtas 47*2adfc688SMarcin Wojtas /* Bit below belongs to SoC control register corresponding to the third 48*2adfc688SMarcin Wojtas * register region. 49*2adfc688SMarcin Wojtas */ 50*2adfc688SMarcin Wojtas #define A38X_SPDIF_MODE_ENABLE BIT(27) 51*2adfc688SMarcin Wojtas 52*2adfc688SMarcin Wojtas static int armada_38x_i2s_init_quirk(struct platform_device *pdev, 53*2adfc688SMarcin Wojtas struct kirkwood_dma_data *priv, 54*2adfc688SMarcin Wojtas struct snd_soc_dai_driver *dai_drv) 55*2adfc688SMarcin Wojtas { 56*2adfc688SMarcin Wojtas struct device_node *np = pdev->dev.of_node; 57*2adfc688SMarcin Wojtas u32 reg_val; 58*2adfc688SMarcin Wojtas int i; 59*2adfc688SMarcin Wojtas 60*2adfc688SMarcin Wojtas priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs"); 61*2adfc688SMarcin Wojtas if (IS_ERR(priv->pll_config)) 62*2adfc688SMarcin Wojtas return -ENOMEM; 63*2adfc688SMarcin Wojtas 64*2adfc688SMarcin Wojtas priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl"); 65*2adfc688SMarcin Wojtas if (IS_ERR(priv->soc_control)) 66*2adfc688SMarcin Wojtas return -ENOMEM; 67*2adfc688SMarcin Wojtas 68*2adfc688SMarcin Wojtas /* Select one of exceptive modes: I2S or S/PDIF */ 69*2adfc688SMarcin Wojtas reg_val = readl(priv->soc_control); 70*2adfc688SMarcin Wojtas if (of_property_read_bool(np, "spdif-mode")) { 71*2adfc688SMarcin Wojtas reg_val |= A38X_SPDIF_MODE_ENABLE; 72*2adfc688SMarcin Wojtas dev_info(&pdev->dev, "using S/PDIF mode\n"); 73*2adfc688SMarcin Wojtas } else { 74*2adfc688SMarcin Wojtas reg_val &= ~A38X_SPDIF_MODE_ENABLE; 75*2adfc688SMarcin Wojtas dev_info(&pdev->dev, "using I2S mode\n"); 76*2adfc688SMarcin Wojtas } 77*2adfc688SMarcin Wojtas writel(reg_val, priv->soc_control); 78*2adfc688SMarcin Wojtas 79*2adfc688SMarcin Wojtas /* Update available rates of mclk's fs */ 80*2adfc688SMarcin Wojtas for (i = 0; i < 2; i++) { 81*2adfc688SMarcin Wojtas dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000; 82*2adfc688SMarcin Wojtas dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000; 83*2adfc688SMarcin Wojtas } 84*2adfc688SMarcin Wojtas 85*2adfc688SMarcin Wojtas return 0; 86*2adfc688SMarcin Wojtas } 87*2adfc688SMarcin Wojtas 88*2adfc688SMarcin Wojtas static inline void armada_38x_set_pll(void __iomem *base, unsigned long rate) 89*2adfc688SMarcin Wojtas { 90*2adfc688SMarcin Wojtas u32 reg_val; 91*2adfc688SMarcin Wojtas u16 freq_offset = 0x22b0; 92*2adfc688SMarcin Wojtas u8 audio_postdiv, fb_clk_div = 0x1d; 93*2adfc688SMarcin Wojtas 94*2adfc688SMarcin Wojtas /* Set frequency offset value to not valid and enable PLL reset */ 95*2adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG1); 96*2adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID; 97*2adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_SW_RESET; 98*2adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1); 99*2adfc688SMarcin Wojtas 100*2adfc688SMarcin Wojtas udelay(1); 101*2adfc688SMarcin Wojtas 102*2adfc688SMarcin Wojtas /* Update PLL parameters */ 103*2adfc688SMarcin Wojtas switch (rate) { 104*2adfc688SMarcin Wojtas default: 105*2adfc688SMarcin Wojtas case 44100: 106*2adfc688SMarcin Wojtas freq_offset = 0x735; 107*2adfc688SMarcin Wojtas fb_clk_div = 0x1b; 108*2adfc688SMarcin Wojtas audio_postdiv = 0xc; 109*2adfc688SMarcin Wojtas break; 110*2adfc688SMarcin Wojtas case 48000: 111*2adfc688SMarcin Wojtas audio_postdiv = 0xc; 112*2adfc688SMarcin Wojtas break; 113*2adfc688SMarcin Wojtas case 96000: 114*2adfc688SMarcin Wojtas audio_postdiv = 0x6; 115*2adfc688SMarcin Wojtas break; 116*2adfc688SMarcin Wojtas case 192000: 117*2adfc688SMarcin Wojtas audio_postdiv = 0x3; 118*2adfc688SMarcin Wojtas break; 119*2adfc688SMarcin Wojtas } 120*2adfc688SMarcin Wojtas 121*2adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG0); 122*2adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK; 123*2adfc688SMarcin Wojtas reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET); 124*2adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG0); 125*2adfc688SMarcin Wojtas 126*2adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG2); 127*2adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK; 128*2adfc688SMarcin Wojtas reg_val |= audio_postdiv; 129*2adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG2); 130*2adfc688SMarcin Wojtas 131*2adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG1); 132*2adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK; 133*2adfc688SMarcin Wojtas reg_val |= freq_offset; 134*2adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1); 135*2adfc688SMarcin Wojtas 136*2adfc688SMarcin Wojtas udelay(1); 137*2adfc688SMarcin Wojtas 138*2adfc688SMarcin Wojtas /* Disable reset */ 139*2adfc688SMarcin Wojtas reg_val |= A38X_PLL_SW_RESET; 140*2adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1); 141*2adfc688SMarcin Wojtas 142*2adfc688SMarcin Wojtas /* Wait 50us for PLL to lock */ 143*2adfc688SMarcin Wojtas udelay(50); 144*2adfc688SMarcin Wojtas 145*2adfc688SMarcin Wojtas /* Restore frequency offset value validity */ 146*2adfc688SMarcin Wojtas reg_val |= A38X_PLL_FREQ_OFFSET_VALID; 147*2adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1); 148*2adfc688SMarcin Wojtas } 149*2adfc688SMarcin Wojtas 150f9b95980Sapatard@mandriva.com static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 151f9b95980Sapatard@mandriva.com unsigned int fmt) 152f9b95980Sapatard@mandriva.com { 153f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai); 154f9b95980Sapatard@mandriva.com unsigned long mask; 155f9b95980Sapatard@mandriva.com unsigned long value; 156f9b95980Sapatard@mandriva.com 157f9b95980Sapatard@mandriva.com switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 158f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_RIGHT_J: 159f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_RJ; 160f9b95980Sapatard@mandriva.com break; 161f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_LEFT_J: 162f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_LJ; 163f9b95980Sapatard@mandriva.com break; 164f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_I2S: 165f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_I2S; 166f9b95980Sapatard@mandriva.com break; 167f9b95980Sapatard@mandriva.com default: 168f9b95980Sapatard@mandriva.com return -EINVAL; 169f9b95980Sapatard@mandriva.com } 170f9b95980Sapatard@mandriva.com 171f9b95980Sapatard@mandriva.com /* 172f9b95980Sapatard@mandriva.com * Set same format for playback and record 173f9b95980Sapatard@mandriva.com * This avoids some troubles. 174f9b95980Sapatard@mandriva.com */ 175f9b95980Sapatard@mandriva.com value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL); 176f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 177f9b95980Sapatard@mandriva.com value |= mask; 178f9b95980Sapatard@mandriva.com writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL); 179f9b95980Sapatard@mandriva.com 180f9b95980Sapatard@mandriva.com value = readl(priv->io+KIRKWOOD_I2S_RECCTL); 181f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 182f9b95980Sapatard@mandriva.com value |= mask; 183f9b95980Sapatard@mandriva.com writel(value, priv->io+KIRKWOOD_I2S_RECCTL); 184f9b95980Sapatard@mandriva.com 185f9b95980Sapatard@mandriva.com return 0; 186f9b95980Sapatard@mandriva.com } 187f9b95980Sapatard@mandriva.com 188f9b95980Sapatard@mandriva.com static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate) 189f9b95980Sapatard@mandriva.com { 190f9b95980Sapatard@mandriva.com unsigned long value; 191f9b95980Sapatard@mandriva.com 192f9b95980Sapatard@mandriva.com value = KIRKWOOD_DCO_CTL_OFFSET_0; 193f9b95980Sapatard@mandriva.com switch (rate) { 194f9b95980Sapatard@mandriva.com default: 195f9b95980Sapatard@mandriva.com case 44100: 196f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_11; 197f9b95980Sapatard@mandriva.com break; 198f9b95980Sapatard@mandriva.com case 48000: 199f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_12; 200f9b95980Sapatard@mandriva.com break; 201f9b95980Sapatard@mandriva.com case 96000: 202f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_24; 203f9b95980Sapatard@mandriva.com break; 204f9b95980Sapatard@mandriva.com } 205f9b95980Sapatard@mandriva.com writel(value, io + KIRKWOOD_DCO_CTL); 206f9b95980Sapatard@mandriva.com 207f9b95980Sapatard@mandriva.com /* wait for dco locked */ 208f9b95980Sapatard@mandriva.com do { 209f9b95980Sapatard@mandriva.com cpu_relax(); 210f9b95980Sapatard@mandriva.com value = readl(io + KIRKWOOD_DCO_SPCR_STATUS); 2112424d458SRussell King value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK; 212f9b95980Sapatard@mandriva.com } while (value == 0); 213f9b95980Sapatard@mandriva.com } 214f9b95980Sapatard@mandriva.com 215363589bfSRussell King static void kirkwood_set_rate(struct snd_soc_dai *dai, 216363589bfSRussell King struct kirkwood_dma_data *priv, unsigned long rate) 217363589bfSRussell King { 218363589bfSRussell King uint32_t clks_ctrl; 219363589bfSRussell King 2201f1b6579SJean-Francois Moine if (IS_ERR(priv->extclk)) { 2218a537f85SJean-Francois Moine /* use internal dco for the supported rates 2228a537f85SJean-Francois Moine * defined in kirkwood_i2s_dai */ 223363589bfSRussell King dev_dbg(dai->dev, "%s: dco set rate = %lu\n", 224363589bfSRussell King __func__, rate); 225*2adfc688SMarcin Wojtas if (priv->pll_config) 226*2adfc688SMarcin Wojtas armada_38x_set_pll(priv->pll_config, rate); 227*2adfc688SMarcin Wojtas else 228363589bfSRussell King kirkwood_set_dco(priv->io, rate); 229363589bfSRussell King 230363589bfSRussell King clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; 2318a537f85SJean-Francois Moine } else { 2328a537f85SJean-Francois Moine /* use the external clock for the other rates 2338a537f85SJean-Francois Moine * defined in kirkwood_i2s_dai_extclk */ 234363589bfSRussell King dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", 235363589bfSRussell King __func__, rate, 256 * rate); 236363589bfSRussell King clk_set_rate(priv->extclk, 256 * rate); 237363589bfSRussell King 238363589bfSRussell King clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK; 239363589bfSRussell King } 240363589bfSRussell King writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL); 241363589bfSRussell King } 242363589bfSRussell King 243f0fba2adSLiam Girdwood static int kirkwood_i2s_startup(struct snd_pcm_substream *substream, 244f0fba2adSLiam Girdwood struct snd_soc_dai *dai) 245f0fba2adSLiam Girdwood { 246f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 247f0fba2adSLiam Girdwood 248f0fba2adSLiam Girdwood snd_soc_dai_set_dma_data(dai, substream, priv); 249f0fba2adSLiam Girdwood return 0; 250f0fba2adSLiam Girdwood } 251f0fba2adSLiam Girdwood 252f9b95980Sapatard@mandriva.com static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, 253f9b95980Sapatard@mandriva.com struct snd_pcm_hw_params *params, 254f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai) 255f9b95980Sapatard@mandriva.com { 256f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 257d8d11ba5SRussell King uint32_t ctl_play, ctl_rec; 258d8d11ba5SRussell King unsigned int i2s_reg; 259d8d11ba5SRussell King unsigned long i2s_value; 260f9b95980Sapatard@mandriva.com 261f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 262f9b95980Sapatard@mandriva.com i2s_reg = KIRKWOOD_I2S_PLAYCTL; 263f9b95980Sapatard@mandriva.com } else { 264f9b95980Sapatard@mandriva.com i2s_reg = KIRKWOOD_I2S_RECCTL; 265f9b95980Sapatard@mandriva.com } 266f9b95980Sapatard@mandriva.com 267363589bfSRussell King kirkwood_set_rate(dai, priv, params_rate(params)); 268f9b95980Sapatard@mandriva.com 269f9b95980Sapatard@mandriva.com i2s_value = readl(priv->io+i2s_reg); 270f9b95980Sapatard@mandriva.com i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; 271f9b95980Sapatard@mandriva.com 272f9b95980Sapatard@mandriva.com /* 273f9b95980Sapatard@mandriva.com * Size settings in play/rec i2s control regs and play/rec control 274f9b95980Sapatard@mandriva.com * regs must be the same. 275f9b95980Sapatard@mandriva.com */ 276f9b95980Sapatard@mandriva.com switch (params_format(params)) { 277f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S16_LE: 278f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; 279d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C | 28075b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_I2S_EN | 28175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_EN; 282d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C | 28375b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_I2S_EN | 28475b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_SPDIF_EN; 285f9b95980Sapatard@mandriva.com break; 286f9b95980Sapatard@mandriva.com /* 287f9b95980Sapatard@mandriva.com * doesn't work... S20_3LE != kirkwood 20bit format ? 288f9b95980Sapatard@mandriva.com * 289f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S20_3LE: 290f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; 291d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 | 292d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 293d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_20 | 294d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 295f9b95980Sapatard@mandriva.com break; 296f9b95980Sapatard@mandriva.com */ 297f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S24_LE: 298f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; 299d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 | 30075b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_I2S_EN | 30175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_EN; 302d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_24 | 30375b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_I2S_EN | 30475b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_SPDIF_EN; 305f9b95980Sapatard@mandriva.com break; 306f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S32_LE: 307f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; 308d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 | 309d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN; 310d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_32 | 311d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN; 312f9b95980Sapatard@mandriva.com break; 313f9b95980Sapatard@mandriva.com default: 314f9b95980Sapatard@mandriva.com return -EINVAL; 315f9b95980Sapatard@mandriva.com } 316dfe4c936Sarnaud.patard@rtp-net.org 317dfe4c936Sarnaud.patard@rtp-net.org if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 318dfe4c936Sarnaud.patard@rtp-net.org if (params_channels(params) == 1) 319d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH; 320dfe4c936Sarnaud.patard@rtp-net.org else 321d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; 322d8d11ba5SRussell King 323d8d11ba5SRussell King priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | 324db43b16fSRussell King KIRKWOOD_PLAYCTL_ENABLE_MASK | 325d8d11ba5SRussell King KIRKWOOD_PLAYCTL_SIZE_MASK); 326d8d11ba5SRussell King priv->ctl_play |= ctl_play; 327d8d11ba5SRussell King } else { 32867721906SRussell King priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK | 32967721906SRussell King KIRKWOOD_RECCTL_SIZE_MASK); 330d8d11ba5SRussell King priv->ctl_rec |= ctl_rec; 331dfe4c936Sarnaud.patard@rtp-net.org } 332dfe4c936Sarnaud.patard@rtp-net.org 333f9b95980Sapatard@mandriva.com writel(i2s_value, priv->io+i2s_reg); 334f9b95980Sapatard@mandriva.com 335f9b95980Sapatard@mandriva.com return 0; 336f9b95980Sapatard@mandriva.com } 337f9b95980Sapatard@mandriva.com 3382fbc3821SRussell King static unsigned kirkwood_i2s_play_mute(unsigned ctl) 3392fbc3821SRussell King { 3402fbc3821SRussell King if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN)) 3412fbc3821SRussell King ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE; 3422fbc3821SRussell King if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN)) 3432fbc3821SRussell King ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE; 3442fbc3821SRussell King return ctl; 3452fbc3821SRussell King } 3462fbc3821SRussell King 347f9b95980Sapatard@mandriva.com static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, 348f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai) 349f9b95980Sapatard@mandriva.com { 350920ec4e5SRussell King struct snd_pcm_runtime *runtime = substream->runtime; 351f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 352982b604bSRussell King uint32_t ctl, value; 353f9b95980Sapatard@mandriva.com 354982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 3554d2097e5SRussell King if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) { 356982b604bSRussell King unsigned timeout = 5000; 357f9b95980Sapatard@mandriva.com /* 358982b604bSRussell King * The Armada510 spec says that if we enter pause mode, the 359982b604bSRussell King * busy bit must be read back as clear _twice_. Make sure 360982b604bSRussell King * we respect that otherwise we get DMA underruns. 361f9b95980Sapatard@mandriva.com */ 362982b604bSRussell King do { 363982b604bSRussell King value = ctl; 364982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 365982b604bSRussell King if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) 366982b604bSRussell King break; 367982b604bSRussell King udelay(1); 368982b604bSRussell King } while (timeout--); 369982b604bSRussell King 370982b604bSRussell King if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) 371982b604bSRussell King dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", 372982b604bSRussell King ctl); 373982b604bSRussell King } 374f9b95980Sapatard@mandriva.com 3754f6f1478SJean-Francois Moine switch (cmd) { 3764f6f1478SJean-Francois Moine case SNDRV_PCM_TRIGGER_START: 3774f6f1478SJean-Francois Moine /* configure */ 3784f6f1478SJean-Francois Moine ctl = priv->ctl_play; 37975b9b65eSJean-Francois Moine if (dai->id == 0) 38075b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */ 38175b9b65eSJean-Francois Moine else 38275b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */ 3832fbc3821SRussell King ctl = kirkwood_i2s_play_mute(ctl); 384db43b16fSRussell King value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 385d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_PLAYCTL); 386d8d11ba5SRussell King 387d8d11ba5SRussell King /* enable interrupts */ 388920ec4e5SRussell King if (!runtime->no_period_wakeup) { 389f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 390f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; 391f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 392920ec4e5SRussell King } 393f9b95980Sapatard@mandriva.com 394d8d11ba5SRussell King /* enable playback */ 395982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 396f9b95980Sapatard@mandriva.com break; 397f9b95980Sapatard@mandriva.com 398f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP: 399f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */ 40075b9b65eSJean-Francois Moine ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 40175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE; 402982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 403f9b95980Sapatard@mandriva.com 404f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 405f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; 406f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 407f9b95980Sapatard@mandriva.com 408f9b95980Sapatard@mandriva.com /* disable all playbacks */ 409db43b16fSRussell King ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 410982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 411f9b95980Sapatard@mandriva.com break; 412f9b95980Sapatard@mandriva.com 413f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 414f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND: 41575b9b65eSJean-Francois Moine ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 41675b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE; 417982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 418f9b95980Sapatard@mandriva.com break; 419f9b95980Sapatard@mandriva.com 420f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME: 421f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 42275b9b65eSJean-Francois Moine ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 42375b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE); 4242fbc3821SRussell King ctl = kirkwood_i2s_play_mute(ctl); 425982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 426f9b95980Sapatard@mandriva.com break; 427f9b95980Sapatard@mandriva.com 428f9b95980Sapatard@mandriva.com default: 429f9b95980Sapatard@mandriva.com return -EINVAL; 430f9b95980Sapatard@mandriva.com } 431f9b95980Sapatard@mandriva.com 432f9b95980Sapatard@mandriva.com return 0; 433f9b95980Sapatard@mandriva.com } 434f9b95980Sapatard@mandriva.com 435f9b95980Sapatard@mandriva.com static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, 436f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai) 437f9b95980Sapatard@mandriva.com { 438f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 439d8d11ba5SRussell King uint32_t ctl, value; 440f9b95980Sapatard@mandriva.com 441f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 442f9b95980Sapatard@mandriva.com 443f9b95980Sapatard@mandriva.com switch (cmd) { 444f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_START: 445d8d11ba5SRussell King /* configure */ 446d8d11ba5SRussell King ctl = priv->ctl_rec; 44775b9b65eSJean-Francois Moine if (dai->id == 0) 44875b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */ 44975b9b65eSJean-Francois Moine else 45075b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */ 45175b9b65eSJean-Francois Moine 45252b896cfSRussell King value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK; 453d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_RECCTL); 454d8d11ba5SRussell King 455d8d11ba5SRussell King /* enable interrupts */ 456f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 457f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_REC_BYTES; 458f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 459f9b95980Sapatard@mandriva.com 460d8d11ba5SRussell King /* enable record */ 461d8d11ba5SRussell King writel(ctl, priv->io + KIRKWOOD_RECCTL); 462f9b95980Sapatard@mandriva.com break; 463f9b95980Sapatard@mandriva.com 464f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP: 465f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */ 466f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 467b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 468f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 469f9b95980Sapatard@mandriva.com 470f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK); 471f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; 472f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK); 473f9b95980Sapatard@mandriva.com 474f9b95980Sapatard@mandriva.com /* disable all records */ 475f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 47652b896cfSRussell King value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 477f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 478f9b95980Sapatard@mandriva.com break; 479f9b95980Sapatard@mandriva.com 480f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 481f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND: 482f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 483b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 484f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 485f9b95980Sapatard@mandriva.com break; 486f9b95980Sapatard@mandriva.com 487f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME: 488f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 489f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 490b424ec95Sarnaud.patard@rtp-net.org value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); 491f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 492f9b95980Sapatard@mandriva.com break; 493f9b95980Sapatard@mandriva.com 494f9b95980Sapatard@mandriva.com default: 495f9b95980Sapatard@mandriva.com return -EINVAL; 496f9b95980Sapatard@mandriva.com } 497f9b95980Sapatard@mandriva.com 498f9b95980Sapatard@mandriva.com return 0; 499f9b95980Sapatard@mandriva.com } 500f9b95980Sapatard@mandriva.com 501f9b95980Sapatard@mandriva.com static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 502f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai) 503f9b95980Sapatard@mandriva.com { 504f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 505f9b95980Sapatard@mandriva.com return kirkwood_i2s_play_trigger(substream, cmd, dai); 506f9b95980Sapatard@mandriva.com else 507f9b95980Sapatard@mandriva.com return kirkwood_i2s_rec_trigger(substream, cmd, dai); 508f9b95980Sapatard@mandriva.com 509f9b95980Sapatard@mandriva.com return 0; 510f9b95980Sapatard@mandriva.com } 511f9b95980Sapatard@mandriva.com 51275b9b65eSJean-Francois Moine static int kirkwood_i2s_init(struct kirkwood_dma_data *priv) 513f9b95980Sapatard@mandriva.com { 514f9b95980Sapatard@mandriva.com unsigned long value; 515f9b95980Sapatard@mandriva.com unsigned int reg_data; 516f9b95980Sapatard@mandriva.com 517f9b95980Sapatard@mandriva.com /* put system in a "safe" state : */ 518f9b95980Sapatard@mandriva.com /* disable audio interrupts */ 519f9b95980Sapatard@mandriva.com writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); 520f9b95980Sapatard@mandriva.com writel(0, priv->io + KIRKWOOD_INT_MASK); 521f9b95980Sapatard@mandriva.com 522f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200); 523f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8)); 524f9b95980Sapatard@mandriva.com reg_data |= 0x111D18; 525f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200); 526f9b95980Sapatard@mandriva.com 527f9b95980Sapatard@mandriva.com msleep(500); 528f9b95980Sapatard@mandriva.com 529f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200); 530f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8)); 531f9b95980Sapatard@mandriva.com reg_data |= 0x111D18; 532f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200); 533f9b95980Sapatard@mandriva.com 534f9b95980Sapatard@mandriva.com /* disable playback/record */ 535f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_PLAYCTL); 536db43b16fSRussell King value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 537f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_PLAYCTL); 538f9b95980Sapatard@mandriva.com 539f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL); 54052b896cfSRussell King value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 541f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL); 542f9b95980Sapatard@mandriva.com 543f9b95980Sapatard@mandriva.com return 0; 544f9b95980Sapatard@mandriva.com 545f9b95980Sapatard@mandriva.com } 546f9b95980Sapatard@mandriva.com 54785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { 548f0fba2adSLiam Girdwood .startup = kirkwood_i2s_startup, 549f9b95980Sapatard@mandriva.com .trigger = kirkwood_i2s_trigger, 550f9b95980Sapatard@mandriva.com .hw_params = kirkwood_i2s_hw_params, 551f9b95980Sapatard@mandriva.com .set_fmt = kirkwood_i2s_set_fmt, 552f9b95980Sapatard@mandriva.com }; 553f9b95980Sapatard@mandriva.com 55475b9b65eSJean-Francois Moine static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = { 55575b9b65eSJean-Francois Moine { 55675b9b65eSJean-Francois Moine .name = "i2s", 55775b9b65eSJean-Francois Moine .id = 0, 558f9b95980Sapatard@mandriva.com .playback = { 559f9b95980Sapatard@mandriva.com .channels_min = 1, 560f9b95980Sapatard@mandriva.com .channels_max = 2, 5619e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 5629e12cbd9SMark Brown SNDRV_PCM_RATE_96000, 563363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 564363589bfSRussell King }, 565f9b95980Sapatard@mandriva.com .capture = { 566f9b95980Sapatard@mandriva.com .channels_min = 1, 567f9b95980Sapatard@mandriva.com .channels_max = 2, 5689e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 5699e12cbd9SMark Brown SNDRV_PCM_RATE_96000, 570363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 571363589bfSRussell King }, 572363589bfSRussell King .ops = &kirkwood_i2s_dai_ops, 57375b9b65eSJean-Francois Moine }, 57475b9b65eSJean-Francois Moine { 57575b9b65eSJean-Francois Moine .name = "spdif", 57675b9b65eSJean-Francois Moine .id = 1, 57775b9b65eSJean-Francois Moine .playback = { 57875b9b65eSJean-Francois Moine .channels_min = 1, 57975b9b65eSJean-Francois Moine .channels_max = 2, 58075b9b65eSJean-Francois Moine .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 58175b9b65eSJean-Francois Moine SNDRV_PCM_RATE_96000, 5821c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 58375b9b65eSJean-Francois Moine }, 58475b9b65eSJean-Francois Moine .capture = { 58575b9b65eSJean-Francois Moine .channels_min = 1, 58675b9b65eSJean-Francois Moine .channels_max = 2, 58775b9b65eSJean-Francois Moine .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 58875b9b65eSJean-Francois Moine SNDRV_PCM_RATE_96000, 5891c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 59075b9b65eSJean-Francois Moine }, 59175b9b65eSJean-Francois Moine .ops = &kirkwood_i2s_dai_ops, 59275b9b65eSJean-Francois Moine }, 593363589bfSRussell King }; 594363589bfSRussell King 59575b9b65eSJean-Francois Moine static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = { 59675b9b65eSJean-Francois Moine { 59775b9b65eSJean-Francois Moine .name = "i2s", 59875b9b65eSJean-Francois Moine .id = 0, 599363589bfSRussell King .playback = { 600363589bfSRussell King .channels_min = 1, 601363589bfSRussell King .channels_max = 2, 60202fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 60302fc17c1SJean-Francois Moine .rate_min = 5512, 60402fc17c1SJean-Francois Moine .rate_max = 192000, 605363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 606363589bfSRussell King }, 607363589bfSRussell King .capture = { 608363589bfSRussell King .channels_min = 1, 609363589bfSRussell King .channels_max = 2, 61002fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 61102fc17c1SJean-Francois Moine .rate_min = 5512, 61202fc17c1SJean-Francois Moine .rate_max = 192000, 613363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS, 614363589bfSRussell King }, 615f9b95980Sapatard@mandriva.com .ops = &kirkwood_i2s_dai_ops, 61675b9b65eSJean-Francois Moine }, 61775b9b65eSJean-Francois Moine { 61875b9b65eSJean-Francois Moine .name = "spdif", 61975b9b65eSJean-Francois Moine .id = 1, 62075b9b65eSJean-Francois Moine .playback = { 62175b9b65eSJean-Francois Moine .channels_min = 1, 62275b9b65eSJean-Francois Moine .channels_max = 2, 62302fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 62402fc17c1SJean-Francois Moine .rate_min = 5512, 62502fc17c1SJean-Francois Moine .rate_max = 192000, 6261c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 62775b9b65eSJean-Francois Moine }, 62875b9b65eSJean-Francois Moine .capture = { 62975b9b65eSJean-Francois Moine .channels_min = 1, 63075b9b65eSJean-Francois Moine .channels_max = 2, 63102fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS, 63202fc17c1SJean-Francois Moine .rate_min = 5512, 63302fc17c1SJean-Francois Moine .rate_max = 192000, 6341c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS, 63575b9b65eSJean-Francois Moine }, 63675b9b65eSJean-Francois Moine .ops = &kirkwood_i2s_dai_ops, 63775b9b65eSJean-Francois Moine }, 638f9b95980Sapatard@mandriva.com }; 639f9b95980Sapatard@mandriva.com 64034e15fbdSBill Pemberton static int kirkwood_i2s_dev_probe(struct platform_device *pdev) 641f9b95980Sapatard@mandriva.com { 642363589bfSRussell King struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data; 64375b9b65eSJean-Francois Moine struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai; 644f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv; 645eb632318SJean-Francois Moine struct device_node *np = pdev->dev.of_node; 646f9b95980Sapatard@mandriva.com int err; 647f9b95980Sapatard@mandriva.com 648dbc517bfSRussell King priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 649c7591edcSMarkus Elfring if (!priv) 650dbc517bfSRussell King return -ENOMEM; 651c7591edcSMarkus Elfring 652f0fba2adSLiam Girdwood dev_set_drvdata(&pdev->dev, priv); 653f9b95980Sapatard@mandriva.com 654*2adfc688SMarcin Wojtas if (of_device_is_compatible(np, "marvell,armada-380-audio")) 655*2adfc688SMarcin Wojtas priv->io = devm_platform_ioremap_resource_byname(pdev, "i2s_regs"); 656*2adfc688SMarcin Wojtas else 65789dd38bfSYueHaibing priv->io = devm_platform_ioremap_resource(pdev, 0); 658b25b5aa0SThierry Reding if (IS_ERR(priv->io)) 659b25b5aa0SThierry Reding return PTR_ERR(priv->io); 660f9b95980Sapatard@mandriva.com 661f9b95980Sapatard@mandriva.com priv->irq = platform_get_irq(pdev, 0); 662cf9441adSStephen Boyd if (priv->irq < 0) 663e7b2e30aSGustavo A. R. Silva return priv->irq; 664f9b95980Sapatard@mandriva.com 665*2adfc688SMarcin Wojtas if (of_device_is_compatible(np, "marvell,armada-380-audio")) { 666*2adfc688SMarcin Wojtas err = armada_38x_i2s_init_quirk(pdev, priv, soc_dai); 667*2adfc688SMarcin Wojtas if (err < 0) 668*2adfc688SMarcin Wojtas return err; 669*2adfc688SMarcin Wojtas /* Set initial pll frequency */ 670*2adfc688SMarcin Wojtas armada_38x_set_pll(priv->pll_config, 44100); 671*2adfc688SMarcin Wojtas } 672*2adfc688SMarcin Wojtas 673eb632318SJean-Francois Moine if (np) { 674eb632318SJean-Francois Moine priv->burst = 128; /* might be 32 or 128 */ 675eb632318SJean-Francois Moine } else if (data) { 676eb632318SJean-Francois Moine priv->burst = data->burst; 677eb632318SJean-Francois Moine } else { 678eb632318SJean-Francois Moine dev_err(&pdev->dev, "no DT nor platform data ?!\n"); 679dbc517bfSRussell King return -EINVAL; 680f9b95980Sapatard@mandriva.com } 681f9b95980Sapatard@mandriva.com 682eb632318SJean-Francois Moine priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL); 683e919c716SAndrew Lunn if (IS_ERR(priv->clk)) { 684e919c716SAndrew Lunn dev_err(&pdev->dev, "no clock\n"); 685dbc517bfSRussell King return PTR_ERR(priv->clk); 686e919c716SAndrew Lunn } 687dbc517bfSRussell King 6884734dc96SMark Brown priv->extclk = devm_clk_get(&pdev->dev, "extclk"); 68984aac6c7SJean-Francois Moine if (IS_ERR(priv->extclk)) { 69084aac6c7SJean-Francois Moine if (PTR_ERR(priv->extclk) == -EPROBE_DEFER) 69184aac6c7SJean-Francois Moine return -EPROBE_DEFER; 69284aac6c7SJean-Francois Moine } else { 693aaa6d062SShawn Guo if (clk_is_match(priv->extclk, priv->clk)) { 694af64d734SRussell King devm_clk_put(&pdev->dev, priv->extclk); 695d8d11ba5SRussell King priv->extclk = ERR_PTR(-EINVAL); 696d8d11ba5SRussell King } else { 697d8d11ba5SRussell King dev_info(&pdev->dev, "found external clock\n"); 698d8d11ba5SRussell King clk_prepare_enable(priv->extclk); 69999d8d3baSJean-Francois Moine soc_dai = kirkwood_i2s_dai_extclk; 700d8d11ba5SRussell King } 701d8d11ba5SRussell King } 702d8d11ba5SRussell King 7034523817dSRussell King err = clk_prepare_enable(priv->clk); 7044523817dSRussell King if (err < 0) 7054523817dSRussell King return err; 7064523817dSRussell King 707d8d11ba5SRussell King /* Some sensible defaults - this reflects the powerup values */ 708d8d11ba5SRussell King priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24; 709d8d11ba5SRussell King priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; 710d8d11ba5SRussell King 711d8d11ba5SRussell King /* Select the burst size */ 712eb632318SJean-Francois Moine if (priv->burst == 32) { 713d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; 714d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; 715d8d11ba5SRussell King } else { 716d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128; 717d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128; 718d8d11ba5SRussell King } 719d8d11ba5SRussell King 720dc39596aSRussell King err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component, 72175b9b65eSJean-Francois Moine soc_dai, 2); 72264ddf1f8SRussell King if (err) { 72383d85f53SKuninori Morimoto dev_err(&pdev->dev, "snd_soc_register_component failed\n"); 72464ddf1f8SRussell King goto err_component; 72564ddf1f8SRussell King } 726baffab28SSimon Baatz 72775b9b65eSJean-Francois Moine kirkwood_i2s_init(priv); 72875b9b65eSJean-Francois Moine 72964ddf1f8SRussell King return 0; 730f98fc0f8SKuninori Morimoto 73164ddf1f8SRussell King err_component: 7324734dc96SMark Brown if (!IS_ERR(priv->extclk)) 733363589bfSRussell King clk_disable_unprepare(priv->extclk); 734baffab28SSimon Baatz clk_disable_unprepare(priv->clk); 735f9b95980Sapatard@mandriva.com 736f9b95980Sapatard@mandriva.com return err; 737f9b95980Sapatard@mandriva.com } 738f9b95980Sapatard@mandriva.com 73934e15fbdSBill Pemberton static int kirkwood_i2s_dev_remove(struct platform_device *pdev) 740f9b95980Sapatard@mandriva.com { 741f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); 742f0fba2adSLiam Girdwood 743dc39596aSRussell King snd_soc_unregister_component(&pdev->dev); 7444734dc96SMark Brown if (!IS_ERR(priv->extclk)) 745363589bfSRussell King clk_disable_unprepare(priv->extclk); 746e919c716SAndrew Lunn clk_disable_unprepare(priv->clk); 747f0fba2adSLiam Girdwood 748f9b95980Sapatard@mandriva.com return 0; 749f9b95980Sapatard@mandriva.com } 750f9b95980Sapatard@mandriva.com 751eb632318SJean-Francois Moine #ifdef CONFIG_OF 7527f2c52afSFabian Frederick static const struct of_device_id mvebu_audio_of_match[] = { 753d098b2f0SThomas Petazzoni { .compatible = "marvell,kirkwood-audio" }, 754d098b2f0SThomas Petazzoni { .compatible = "marvell,dove-audio" }, 7559a0d5113SThomas Petazzoni { .compatible = "marvell,armada370-audio" }, 756*2adfc688SMarcin Wojtas { .compatible = "marvell,armada-380-audio" }, 757eb632318SJean-Francois Moine { } 758eb632318SJean-Francois Moine }; 759eb632318SJean-Francois Moine MODULE_DEVICE_TABLE(of, mvebu_audio_of_match); 760eb632318SJean-Francois Moine #endif 761eb632318SJean-Francois Moine 762f9b95980Sapatard@mandriva.com static struct platform_driver kirkwood_i2s_driver = { 763f9b95980Sapatard@mandriva.com .probe = kirkwood_i2s_dev_probe, 76434e15fbdSBill Pemberton .remove = kirkwood_i2s_dev_remove, 765f9b95980Sapatard@mandriva.com .driver = { 766f9b95980Sapatard@mandriva.com .name = DRV_NAME, 767eb632318SJean-Francois Moine .of_match_table = of_match_ptr(mvebu_audio_of_match), 768f9b95980Sapatard@mandriva.com }, 769f9b95980Sapatard@mandriva.com }; 770f9b95980Sapatard@mandriva.com 77141b10225SAxel Lin module_platform_driver(kirkwood_i2s_driver); 772f9b95980Sapatard@mandriva.com 773f9b95980Sapatard@mandriva.com /* Module information */ 77469737897SArnaud Patard (Rtp) MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); 775f9b95980Sapatard@mandriva.com MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); 776f9b95980Sapatard@mandriva.com MODULE_LICENSE("GPL"); 77764ddf1f8SRussell King MODULE_ALIAS("platform:mvebu-audio"); 778