18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * kirkwood-i2s.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2010 Arnaud Patard <apatard@mandriva.com> 68c2ecf20Sopenharmony_ci * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/mbus.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 198c2ecf20Sopenharmony_ci#include <sound/soc.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-kirkwood.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "kirkwood.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define KIRKWOOD_I2S_FORMATS \ 268c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | \ 278c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 288c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define KIRKWOOD_SPDIF_FORMATS \ 318c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | \ 328c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 358c2ecf20Sopenharmony_ci unsigned int fmt) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai); 388c2ecf20Sopenharmony_ci unsigned long mask; 398c2ecf20Sopenharmony_ci unsigned long value; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 428c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 438c2ecf20Sopenharmony_ci mask = KIRKWOOD_I2S_CTL_RJ; 448c2ecf20Sopenharmony_ci break; 458c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 468c2ecf20Sopenharmony_ci mask = KIRKWOOD_I2S_CTL_LJ; 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 498c2ecf20Sopenharmony_ci mask = KIRKWOOD_I2S_CTL_I2S; 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci default: 528c2ecf20Sopenharmony_ci return -EINVAL; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* 568c2ecf20Sopenharmony_ci * Set same format for playback and record 578c2ecf20Sopenharmony_ci * This avoids some troubles. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL); 608c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 618c2ecf20Sopenharmony_ci value |= mask; 628c2ecf20Sopenharmony_ci writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci value = readl(priv->io+KIRKWOOD_I2S_RECCTL); 658c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 668c2ecf20Sopenharmony_ci value |= mask; 678c2ecf20Sopenharmony_ci writel(value, priv->io+KIRKWOOD_I2S_RECCTL); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline void kirkwood_set_dco(void __iomem *io, unsigned long rate) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci unsigned long value; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci value = KIRKWOOD_DCO_CTL_OFFSET_0; 778c2ecf20Sopenharmony_ci switch (rate) { 788c2ecf20Sopenharmony_ci default: 798c2ecf20Sopenharmony_ci case 44100: 808c2ecf20Sopenharmony_ci value |= KIRKWOOD_DCO_CTL_FREQ_11; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci case 48000: 838c2ecf20Sopenharmony_ci value |= KIRKWOOD_DCO_CTL_FREQ_12; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci case 96000: 868c2ecf20Sopenharmony_ci value |= KIRKWOOD_DCO_CTL_FREQ_24; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci writel(value, io + KIRKWOOD_DCO_CTL); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* wait for dco locked */ 928c2ecf20Sopenharmony_ci do { 938c2ecf20Sopenharmony_ci cpu_relax(); 948c2ecf20Sopenharmony_ci value = readl(io + KIRKWOOD_DCO_SPCR_STATUS); 958c2ecf20Sopenharmony_ci value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK; 968c2ecf20Sopenharmony_ci } while (value == 0); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void kirkwood_set_rate(struct snd_soc_dai *dai, 1008c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv, unsigned long rate) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci uint32_t clks_ctrl; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (IS_ERR(priv->extclk)) { 1058c2ecf20Sopenharmony_ci /* use internal dco for the supported rates 1068c2ecf20Sopenharmony_ci * defined in kirkwood_i2s_dai */ 1078c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: dco set rate = %lu\n", 1088c2ecf20Sopenharmony_ci __func__, rate); 1098c2ecf20Sopenharmony_ci kirkwood_set_dco(priv->io, rate); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci /* use the external clock for the other rates 1148c2ecf20Sopenharmony_ci * defined in kirkwood_i2s_dai_extclk */ 1158c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", 1168c2ecf20Sopenharmony_ci __func__, rate, 256 * rate); 1178c2ecf20Sopenharmony_ci clk_set_rate(priv->extclk, 256 * rate); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int kirkwood_i2s_startup(struct snd_pcm_substream *substream, 1258c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, priv); 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, 1348c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1358c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 1388c2ecf20Sopenharmony_ci uint32_t ctl_play, ctl_rec; 1398c2ecf20Sopenharmony_ci unsigned int i2s_reg; 1408c2ecf20Sopenharmony_ci unsigned long i2s_value; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 1438c2ecf20Sopenharmony_ci i2s_reg = KIRKWOOD_I2S_PLAYCTL; 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci i2s_reg = KIRKWOOD_I2S_RECCTL; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci kirkwood_set_rate(dai, priv, params_rate(params)); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci i2s_value = readl(priv->io+i2s_reg); 1518c2ecf20Sopenharmony_ci i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* 1548c2ecf20Sopenharmony_ci * Size settings in play/rec i2s control regs and play/rec control 1558c2ecf20Sopenharmony_ci * regs must be the same. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci switch (params_format(params)) { 1588c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 1598c2ecf20Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; 1608c2ecf20Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C | 1618c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN | 1628c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_EN; 1638c2ecf20Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C | 1648c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN | 1658c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_SPDIF_EN; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * doesn't work... S20_3LE != kirkwood 20bit format ? 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 1718c2ecf20Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; 1728c2ecf20Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 | 1738c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN; 1748c2ecf20Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_20 | 1758c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 1798c2ecf20Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; 1808c2ecf20Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 | 1818c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN | 1828c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_EN; 1838c2ecf20Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_24 | 1848c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN | 1858c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_SPDIF_EN; 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 1888c2ecf20Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; 1898c2ecf20Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 | 1908c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN; 1918c2ecf20Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_32 | 1928c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN; 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 1998c2ecf20Sopenharmony_ci if (params_channels(params) == 1) 2008c2ecf20Sopenharmony_ci ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH; 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | 2058c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_ENABLE_MASK | 2068c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_SIZE_MASK); 2078c2ecf20Sopenharmony_ci priv->ctl_play |= ctl_play; 2088c2ecf20Sopenharmony_ci } else { 2098c2ecf20Sopenharmony_ci priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK | 2108c2ecf20Sopenharmony_ci KIRKWOOD_RECCTL_SIZE_MASK); 2118c2ecf20Sopenharmony_ci priv->ctl_rec |= ctl_rec; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci writel(i2s_value, priv->io+i2s_reg); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic unsigned kirkwood_i2s_play_mute(unsigned ctl) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN)) 2228c2ecf20Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE; 2238c2ecf20Sopenharmony_ci if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN)) 2248c2ecf20Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE; 2258c2ecf20Sopenharmony_ci return ctl; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, 2298c2ecf20Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2328c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 2338c2ecf20Sopenharmony_ci uint32_t ctl, value; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 2368c2ecf20Sopenharmony_ci if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) { 2378c2ecf20Sopenharmony_ci unsigned timeout = 5000; 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * The Armada510 spec says that if we enter pause mode, the 2408c2ecf20Sopenharmony_ci * busy bit must be read back as clear _twice_. Make sure 2418c2ecf20Sopenharmony_ci * we respect that otherwise we get DMA underruns. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci do { 2448c2ecf20Sopenharmony_ci value = ctl; 2458c2ecf20Sopenharmony_ci ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 2468c2ecf20Sopenharmony_ci if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci udelay(1); 2498c2ecf20Sopenharmony_ci } while (timeout--); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) 2528c2ecf20Sopenharmony_ci dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", 2538c2ecf20Sopenharmony_ci ctl); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci switch (cmd) { 2578c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2588c2ecf20Sopenharmony_ci /* configure */ 2598c2ecf20Sopenharmony_ci ctl = priv->ctl_play; 2608c2ecf20Sopenharmony_ci if (dai->id == 0) 2618c2ecf20Sopenharmony_ci ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */ 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */ 2648c2ecf20Sopenharmony_ci ctl = kirkwood_i2s_play_mute(ctl); 2658c2ecf20Sopenharmony_ci value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 2668c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_PLAYCTL); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* enable interrupts */ 2698c2ecf20Sopenharmony_ci if (!runtime->no_period_wakeup) { 2708c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 2718c2ecf20Sopenharmony_ci value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; 2728c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* enable playback */ 2768c2ecf20Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2808c2ecf20Sopenharmony_ci /* stop audio, disable interrupts */ 2818c2ecf20Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 2828c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_MUTE; 2838c2ecf20Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 2868c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; 2878c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* disable all playbacks */ 2908c2ecf20Sopenharmony_ci ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 2918c2ecf20Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2958c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2968c2ecf20Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 2978c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_MUTE; 2988c2ecf20Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 3028c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3038c2ecf20Sopenharmony_ci ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 3048c2ecf20Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_MUTE); 3058c2ecf20Sopenharmony_ci ctl = kirkwood_i2s_play_mute(ctl); 3068c2ecf20Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, 3178c2ecf20Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 3208c2ecf20Sopenharmony_ci uint32_t ctl, value; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci switch (cmd) { 3258c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3268c2ecf20Sopenharmony_ci /* configure */ 3278c2ecf20Sopenharmony_ci ctl = priv->ctl_rec; 3288c2ecf20Sopenharmony_ci if (dai->id == 0) 3298c2ecf20Sopenharmony_ci ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */ 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */ 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK; 3348c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* enable interrupts */ 3378c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 3388c2ecf20Sopenharmony_ci value |= KIRKWOOD_INT_CAUSE_REC_BYTES; 3398c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* enable record */ 3428c2ecf20Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_RECCTL); 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3468c2ecf20Sopenharmony_ci /* stop audio, disable interrupts */ 3478c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 3488c2ecf20Sopenharmony_ci value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 3498c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 3528c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; 3538c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* disable all records */ 3568c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 3578c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 3588c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3628c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3638c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 3648c2ecf20Sopenharmony_ci value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 3658c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 3698c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3708c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 3718c2ecf20Sopenharmony_ci value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); 3728c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 3838c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3868c2ecf20Sopenharmony_ci return kirkwood_i2s_play_trigger(substream, cmd, dai); 3878c2ecf20Sopenharmony_ci else 3888c2ecf20Sopenharmony_ci return kirkwood_i2s_rec_trigger(substream, cmd, dai); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int kirkwood_i2s_init(struct kirkwood_dma_data *priv) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci unsigned long value; 3968c2ecf20Sopenharmony_ci unsigned int reg_data; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* put system in a "safe" state : */ 3998c2ecf20Sopenharmony_ci /* disable audio interrupts */ 4008c2ecf20Sopenharmony_ci writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); 4018c2ecf20Sopenharmony_ci writel(0, priv->io + KIRKWOOD_INT_MASK); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci reg_data = readl(priv->io + 0x1200); 4048c2ecf20Sopenharmony_ci reg_data &= (~(0x333FF8)); 4058c2ecf20Sopenharmony_ci reg_data |= 0x111D18; 4068c2ecf20Sopenharmony_ci writel(reg_data, priv->io + 0x1200); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci msleep(500); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci reg_data = readl(priv->io + 0x1200); 4118c2ecf20Sopenharmony_ci reg_data &= (~(0x333FF8)); 4128c2ecf20Sopenharmony_ci reg_data |= 0x111D18; 4138c2ecf20Sopenharmony_ci writel(reg_data, priv->io + 0x1200); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* disable playback/record */ 4168c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_PLAYCTL); 4178c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 4188c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_PLAYCTL); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 4218c2ecf20Sopenharmony_ci value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 4228c2ecf20Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { 4298c2ecf20Sopenharmony_ci .startup = kirkwood_i2s_startup, 4308c2ecf20Sopenharmony_ci .trigger = kirkwood_i2s_trigger, 4318c2ecf20Sopenharmony_ci .hw_params = kirkwood_i2s_hw_params, 4328c2ecf20Sopenharmony_ci .set_fmt = kirkwood_i2s_set_fmt, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai[2] = { 4368c2ecf20Sopenharmony_ci { 4378c2ecf20Sopenharmony_ci .name = "i2s", 4388c2ecf20Sopenharmony_ci .id = 0, 4398c2ecf20Sopenharmony_ci .playback = { 4408c2ecf20Sopenharmony_ci .channels_min = 1, 4418c2ecf20Sopenharmony_ci .channels_max = 2, 4428c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4438c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000, 4448c2ecf20Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 4458c2ecf20Sopenharmony_ci }, 4468c2ecf20Sopenharmony_ci .capture = { 4478c2ecf20Sopenharmony_ci .channels_min = 1, 4488c2ecf20Sopenharmony_ci .channels_max = 2, 4498c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4508c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000, 4518c2ecf20Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 4528c2ecf20Sopenharmony_ci }, 4538c2ecf20Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 4548c2ecf20Sopenharmony_ci }, 4558c2ecf20Sopenharmony_ci { 4568c2ecf20Sopenharmony_ci .name = "spdif", 4578c2ecf20Sopenharmony_ci .id = 1, 4588c2ecf20Sopenharmony_ci .playback = { 4598c2ecf20Sopenharmony_ci .channels_min = 1, 4608c2ecf20Sopenharmony_ci .channels_max = 2, 4618c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4628c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000, 4638c2ecf20Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 4648c2ecf20Sopenharmony_ci }, 4658c2ecf20Sopenharmony_ci .capture = { 4668c2ecf20Sopenharmony_ci .channels_min = 1, 4678c2ecf20Sopenharmony_ci .channels_max = 2, 4688c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 4698c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000, 4708c2ecf20Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 4718c2ecf20Sopenharmony_ci }, 4728c2ecf20Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 4738c2ecf20Sopenharmony_ci }, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = { 4778c2ecf20Sopenharmony_ci { 4788c2ecf20Sopenharmony_ci .name = "i2s", 4798c2ecf20Sopenharmony_ci .id = 0, 4808c2ecf20Sopenharmony_ci .playback = { 4818c2ecf20Sopenharmony_ci .channels_min = 1, 4828c2ecf20Sopenharmony_ci .channels_max = 2, 4838c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 4848c2ecf20Sopenharmony_ci .rate_min = 5512, 4858c2ecf20Sopenharmony_ci .rate_max = 192000, 4868c2ecf20Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 4878c2ecf20Sopenharmony_ci }, 4888c2ecf20Sopenharmony_ci .capture = { 4898c2ecf20Sopenharmony_ci .channels_min = 1, 4908c2ecf20Sopenharmony_ci .channels_max = 2, 4918c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 4928c2ecf20Sopenharmony_ci .rate_min = 5512, 4938c2ecf20Sopenharmony_ci .rate_max = 192000, 4948c2ecf20Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 4958c2ecf20Sopenharmony_ci }, 4968c2ecf20Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 4978c2ecf20Sopenharmony_ci }, 4988c2ecf20Sopenharmony_ci { 4998c2ecf20Sopenharmony_ci .name = "spdif", 5008c2ecf20Sopenharmony_ci .id = 1, 5018c2ecf20Sopenharmony_ci .playback = { 5028c2ecf20Sopenharmony_ci .channels_min = 1, 5038c2ecf20Sopenharmony_ci .channels_max = 2, 5048c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 5058c2ecf20Sopenharmony_ci .rate_min = 5512, 5068c2ecf20Sopenharmony_ci .rate_max = 192000, 5078c2ecf20Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 5088c2ecf20Sopenharmony_ci }, 5098c2ecf20Sopenharmony_ci .capture = { 5108c2ecf20Sopenharmony_ci .channels_min = 1, 5118c2ecf20Sopenharmony_ci .channels_max = 2, 5128c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 5138c2ecf20Sopenharmony_ci .rate_min = 5512, 5148c2ecf20Sopenharmony_ci .rate_max = 192000, 5158c2ecf20Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 5168c2ecf20Sopenharmony_ci }, 5178c2ecf20Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 5188c2ecf20Sopenharmony_ci }, 5198c2ecf20Sopenharmony_ci}; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int kirkwood_i2s_dev_probe(struct platform_device *pdev) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data; 5248c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai; 5258c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv; 5268c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 5278c2ecf20Sopenharmony_ci int err; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 5308c2ecf20Sopenharmony_ci if (!priv) 5318c2ecf20Sopenharmony_ci return -ENOMEM; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, priv); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci priv->io = devm_platform_ioremap_resource(pdev, 0); 5368c2ecf20Sopenharmony_ci if (IS_ERR(priv->io)) 5378c2ecf20Sopenharmony_ci return PTR_ERR(priv->io); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci priv->irq = platform_get_irq(pdev, 0); 5408c2ecf20Sopenharmony_ci if (priv->irq < 0) 5418c2ecf20Sopenharmony_ci return priv->irq; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (np) { 5448c2ecf20Sopenharmony_ci priv->burst = 128; /* might be 32 or 128 */ 5458c2ecf20Sopenharmony_ci } else if (data) { 5468c2ecf20Sopenharmony_ci priv->burst = data->burst; 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no DT nor platform data ?!\n"); 5498c2ecf20Sopenharmony_ci return -EINVAL; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL); 5538c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 5548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no clock\n"); 5558c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci priv->extclk = devm_clk_get(&pdev->dev, "extclk"); 5598c2ecf20Sopenharmony_ci if (IS_ERR(priv->extclk)) { 5608c2ecf20Sopenharmony_ci if (PTR_ERR(priv->extclk) == -EPROBE_DEFER) 5618c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 5628c2ecf20Sopenharmony_ci } else { 5638c2ecf20Sopenharmony_ci if (clk_is_match(priv->extclk, priv->clk)) { 5648c2ecf20Sopenharmony_ci devm_clk_put(&pdev->dev, priv->extclk); 5658c2ecf20Sopenharmony_ci priv->extclk = ERR_PTR(-EINVAL); 5668c2ecf20Sopenharmony_ci } else { 5678c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "found external clock\n"); 5688c2ecf20Sopenharmony_ci clk_prepare_enable(priv->extclk); 5698c2ecf20Sopenharmony_ci soc_dai = kirkwood_i2s_dai_extclk; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci err = clk_prepare_enable(priv->clk); 5748c2ecf20Sopenharmony_ci if (err < 0) 5758c2ecf20Sopenharmony_ci return err; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Some sensible defaults - this reflects the powerup values */ 5788c2ecf20Sopenharmony_ci priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24; 5798c2ecf20Sopenharmony_ci priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Select the burst size */ 5828c2ecf20Sopenharmony_ci if (priv->burst == 32) { 5838c2ecf20Sopenharmony_ci priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; 5848c2ecf20Sopenharmony_ci priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; 5858c2ecf20Sopenharmony_ci } else { 5868c2ecf20Sopenharmony_ci priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128; 5878c2ecf20Sopenharmony_ci priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component, 5918c2ecf20Sopenharmony_ci soc_dai, 2); 5928c2ecf20Sopenharmony_ci if (err) { 5938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "snd_soc_register_component failed\n"); 5948c2ecf20Sopenharmony_ci goto err_component; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci kirkwood_i2s_init(priv); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci err_component: 6028c2ecf20Sopenharmony_ci if (!IS_ERR(priv->extclk)) 6038c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->extclk); 6048c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return err; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int kirkwood_i2s_dev_remove(struct platform_device *pdev) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 6148c2ecf20Sopenharmony_ci if (!IS_ERR(priv->extclk)) 6158c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->extclk); 6168c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6228c2ecf20Sopenharmony_cistatic const struct of_device_id mvebu_audio_of_match[] = { 6238c2ecf20Sopenharmony_ci { .compatible = "marvell,kirkwood-audio" }, 6248c2ecf20Sopenharmony_ci { .compatible = "marvell,dove-audio" }, 6258c2ecf20Sopenharmony_ci { .compatible = "marvell,armada370-audio" }, 6268c2ecf20Sopenharmony_ci { } 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvebu_audio_of_match); 6298c2ecf20Sopenharmony_ci#endif 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic struct platform_driver kirkwood_i2s_driver = { 6328c2ecf20Sopenharmony_ci .probe = kirkwood_i2s_dev_probe, 6338c2ecf20Sopenharmony_ci .remove = kirkwood_i2s_dev_remove, 6348c2ecf20Sopenharmony_ci .driver = { 6358c2ecf20Sopenharmony_ci .name = DRV_NAME, 6368c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mvebu_audio_of_match), 6378c2ecf20Sopenharmony_ci }, 6388c2ecf20Sopenharmony_ci}; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cimodule_platform_driver(kirkwood_i2s_driver); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* Module information */ 6438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); 6448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); 6458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6468c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mvebu-audio"); 647