162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * kirkwood-i2s.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 2010 Arnaud Patard <apatard@mandriva.com> 662306a36Sopenharmony_ci * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/mbus.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <sound/pcm.h> 1862306a36Sopenharmony_ci#include <sound/pcm_params.h> 1962306a36Sopenharmony_ci#include <sound/soc.h> 2062306a36Sopenharmony_ci#include <linux/platform_data/asoc-kirkwood.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "kirkwood.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define KIRKWOOD_I2S_FORMATS \ 2662306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | \ 2762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 2862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define KIRKWOOD_SPDIF_FORMATS \ 3162306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | \ 3262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* These registers are relative to the second register region - 3562306a36Sopenharmony_ci * audio pll configuration. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#define A38X_PLL_CONF_REG0 0x0 3862306a36Sopenharmony_ci#define A38X_PLL_FB_CLK_DIV_OFFSET 10 3962306a36Sopenharmony_ci#define A38X_PLL_FB_CLK_DIV_MASK 0x7fc00 4062306a36Sopenharmony_ci#define A38X_PLL_CONF_REG1 0x4 4162306a36Sopenharmony_ci#define A38X_PLL_FREQ_OFFSET_MASK 0xffff 4262306a36Sopenharmony_ci#define A38X_PLL_FREQ_OFFSET_VALID BIT(16) 4362306a36Sopenharmony_ci#define A38X_PLL_SW_RESET BIT(31) 4462306a36Sopenharmony_ci#define A38X_PLL_CONF_REG2 0x8 4562306a36Sopenharmony_ci#define A38X_PLL_AUDIO_POSTDIV_MASK 0x7f 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Bit below belongs to SoC control register corresponding to the third 4862306a36Sopenharmony_ci * register region. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#define A38X_SPDIF_MODE_ENABLE BIT(27) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int armada_38x_i2s_init_quirk(struct platform_device *pdev, 5362306a36Sopenharmony_ci struct kirkwood_dma_data *priv, 5462306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 5762306a36Sopenharmony_ci u32 reg_val; 5862306a36Sopenharmony_ci int i; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs"); 6162306a36Sopenharmony_ci if (IS_ERR(priv->pll_config)) 6262306a36Sopenharmony_ci return -ENOMEM; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl"); 6562306a36Sopenharmony_ci if (IS_ERR(priv->soc_control)) 6662306a36Sopenharmony_ci return -ENOMEM; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Select one of exceptive modes: I2S or S/PDIF */ 6962306a36Sopenharmony_ci reg_val = readl(priv->soc_control); 7062306a36Sopenharmony_ci if (of_property_read_bool(np, "spdif-mode")) { 7162306a36Sopenharmony_ci reg_val |= A38X_SPDIF_MODE_ENABLE; 7262306a36Sopenharmony_ci dev_info(&pdev->dev, "using S/PDIF mode\n"); 7362306a36Sopenharmony_ci } else { 7462306a36Sopenharmony_ci reg_val &= ~A38X_SPDIF_MODE_ENABLE; 7562306a36Sopenharmony_ci dev_info(&pdev->dev, "using I2S mode\n"); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci writel(reg_val, priv->soc_control); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Update available rates of mclk's fs */ 8062306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 8162306a36Sopenharmony_ci dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000; 8262306a36Sopenharmony_ci dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic inline void armada_38x_set_pll(void __iomem *base, unsigned long rate) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u32 reg_val; 9162306a36Sopenharmony_ci u16 freq_offset = 0x22b0; 9262306a36Sopenharmony_ci u8 audio_postdiv, fb_clk_div = 0x1d; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Set frequency offset value to not valid and enable PLL reset */ 9562306a36Sopenharmony_ci reg_val = readl(base + A38X_PLL_CONF_REG1); 9662306a36Sopenharmony_ci reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID; 9762306a36Sopenharmony_ci reg_val &= ~A38X_PLL_SW_RESET; 9862306a36Sopenharmony_ci writel(reg_val, base + A38X_PLL_CONF_REG1); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci udelay(1); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Update PLL parameters */ 10362306a36Sopenharmony_ci switch (rate) { 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci case 44100: 10662306a36Sopenharmony_ci freq_offset = 0x735; 10762306a36Sopenharmony_ci fb_clk_div = 0x1b; 10862306a36Sopenharmony_ci audio_postdiv = 0xc; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case 48000: 11162306a36Sopenharmony_ci audio_postdiv = 0xc; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case 96000: 11462306a36Sopenharmony_ci audio_postdiv = 0x6; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case 192000: 11762306a36Sopenharmony_ci audio_postdiv = 0x3; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci reg_val = readl(base + A38X_PLL_CONF_REG0); 12262306a36Sopenharmony_ci reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK; 12362306a36Sopenharmony_ci reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET); 12462306a36Sopenharmony_ci writel(reg_val, base + A38X_PLL_CONF_REG0); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci reg_val = readl(base + A38X_PLL_CONF_REG2); 12762306a36Sopenharmony_ci reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK; 12862306a36Sopenharmony_ci reg_val |= audio_postdiv; 12962306a36Sopenharmony_ci writel(reg_val, base + A38X_PLL_CONF_REG2); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci reg_val = readl(base + A38X_PLL_CONF_REG1); 13262306a36Sopenharmony_ci reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK; 13362306a36Sopenharmony_ci reg_val |= freq_offset; 13462306a36Sopenharmony_ci writel(reg_val, base + A38X_PLL_CONF_REG1); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci udelay(1); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Disable reset */ 13962306a36Sopenharmony_ci reg_val |= A38X_PLL_SW_RESET; 14062306a36Sopenharmony_ci writel(reg_val, base + A38X_PLL_CONF_REG1); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Wait 50us for PLL to lock */ 14362306a36Sopenharmony_ci udelay(50); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Restore frequency offset value validity */ 14662306a36Sopenharmony_ci reg_val |= A38X_PLL_FREQ_OFFSET_VALID; 14762306a36Sopenharmony_ci writel(reg_val, base + A38X_PLL_CONF_REG1); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 15162306a36Sopenharmony_ci unsigned int fmt) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai); 15462306a36Sopenharmony_ci unsigned long mask; 15562306a36Sopenharmony_ci unsigned long value; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 15862306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 15962306a36Sopenharmony_ci mask = KIRKWOOD_I2S_CTL_RJ; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 16262306a36Sopenharmony_ci mask = KIRKWOOD_I2S_CTL_LJ; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 16562306a36Sopenharmony_ci mask = KIRKWOOD_I2S_CTL_I2S; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci default: 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Set same format for playback and record 17362306a36Sopenharmony_ci * This avoids some troubles. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL); 17662306a36Sopenharmony_ci value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 17762306a36Sopenharmony_ci value |= mask; 17862306a36Sopenharmony_ci writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci value = readl(priv->io+KIRKWOOD_I2S_RECCTL); 18162306a36Sopenharmony_ci value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 18262306a36Sopenharmony_ci value |= mask; 18362306a36Sopenharmony_ci writel(value, priv->io+KIRKWOOD_I2S_RECCTL); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic inline void kirkwood_set_dco(void __iomem *io, unsigned long rate) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci unsigned long value; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci value = KIRKWOOD_DCO_CTL_OFFSET_0; 19362306a36Sopenharmony_ci switch (rate) { 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci case 44100: 19662306a36Sopenharmony_ci value |= KIRKWOOD_DCO_CTL_FREQ_11; 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci case 48000: 19962306a36Sopenharmony_ci value |= KIRKWOOD_DCO_CTL_FREQ_12; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case 96000: 20262306a36Sopenharmony_ci value |= KIRKWOOD_DCO_CTL_FREQ_24; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci writel(value, io + KIRKWOOD_DCO_CTL); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* wait for dco locked */ 20862306a36Sopenharmony_ci do { 20962306a36Sopenharmony_ci cpu_relax(); 21062306a36Sopenharmony_ci value = readl(io + KIRKWOOD_DCO_SPCR_STATUS); 21162306a36Sopenharmony_ci value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK; 21262306a36Sopenharmony_ci } while (value == 0); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void kirkwood_set_rate(struct snd_soc_dai *dai, 21662306a36Sopenharmony_ci struct kirkwood_dma_data *priv, unsigned long rate) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci uint32_t clks_ctrl; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (IS_ERR(priv->extclk)) { 22162306a36Sopenharmony_ci /* use internal dco for the supported rates 22262306a36Sopenharmony_ci * defined in kirkwood_i2s_dai */ 22362306a36Sopenharmony_ci dev_dbg(dai->dev, "%s: dco set rate = %lu\n", 22462306a36Sopenharmony_ci __func__, rate); 22562306a36Sopenharmony_ci if (priv->pll_config) 22662306a36Sopenharmony_ci armada_38x_set_pll(priv->pll_config, rate); 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci kirkwood_set_dco(priv->io, rate); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci /* use the external clock for the other rates 23362306a36Sopenharmony_ci * defined in kirkwood_i2s_dai_extclk */ 23462306a36Sopenharmony_ci dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", 23562306a36Sopenharmony_ci __func__, rate, 256 * rate); 23662306a36Sopenharmony_ci clk_set_rate(priv->extclk, 256 * rate); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int kirkwood_i2s_startup(struct snd_pcm_substream *substream, 24462306a36Sopenharmony_ci struct snd_soc_dai *dai) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, priv); 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, 25362306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 25462306a36Sopenharmony_ci struct snd_soc_dai *dai) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 25762306a36Sopenharmony_ci uint32_t ctl_play, ctl_rec; 25862306a36Sopenharmony_ci unsigned int i2s_reg; 25962306a36Sopenharmony_ci unsigned long i2s_value; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 26262306a36Sopenharmony_ci i2s_reg = KIRKWOOD_I2S_PLAYCTL; 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci i2s_reg = KIRKWOOD_I2S_RECCTL; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci kirkwood_set_rate(dai, priv, params_rate(params)); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci i2s_value = readl(priv->io+i2s_reg); 27062306a36Sopenharmony_ci i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * Size settings in play/rec i2s control regs and play/rec control 27462306a36Sopenharmony_ci * regs must be the same. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci switch (params_format(params)) { 27762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 27862306a36Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; 27962306a36Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C | 28062306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN | 28162306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_EN; 28262306a36Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C | 28362306a36Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN | 28462306a36Sopenharmony_ci KIRKWOOD_RECCTL_SPDIF_EN; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * doesn't work... S20_3LE != kirkwood 20bit format ? 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 29062306a36Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; 29162306a36Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 | 29262306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN; 29362306a36Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_20 | 29462306a36Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 29862306a36Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; 29962306a36Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 | 30062306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN | 30162306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_EN; 30262306a36Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_24 | 30362306a36Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN | 30462306a36Sopenharmony_ci KIRKWOOD_RECCTL_SPDIF_EN; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 30762306a36Sopenharmony_ci i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; 30862306a36Sopenharmony_ci ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 | 30962306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_I2S_EN; 31062306a36Sopenharmony_ci ctl_rec = KIRKWOOD_RECCTL_SIZE_32 | 31162306a36Sopenharmony_ci KIRKWOOD_RECCTL_I2S_EN; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 31862306a36Sopenharmony_ci if (params_channels(params) == 1) 31962306a36Sopenharmony_ci ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH; 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | 32462306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_ENABLE_MASK | 32562306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_SIZE_MASK); 32662306a36Sopenharmony_ci priv->ctl_play |= ctl_play; 32762306a36Sopenharmony_ci } else { 32862306a36Sopenharmony_ci priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK | 32962306a36Sopenharmony_ci KIRKWOOD_RECCTL_SIZE_MASK); 33062306a36Sopenharmony_ci priv->ctl_rec |= ctl_rec; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci writel(i2s_value, priv->io+i2s_reg); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic unsigned kirkwood_i2s_play_mute(unsigned ctl) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN)) 34162306a36Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE; 34262306a36Sopenharmony_ci if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN)) 34362306a36Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE; 34462306a36Sopenharmony_ci return ctl; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, 34862306a36Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 35162306a36Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 35262306a36Sopenharmony_ci uint32_t ctl, value; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 35562306a36Sopenharmony_ci if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) { 35662306a36Sopenharmony_ci unsigned timeout = 5000; 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * The Armada510 spec says that if we enter pause mode, the 35962306a36Sopenharmony_ci * busy bit must be read back as clear _twice_. Make sure 36062306a36Sopenharmony_ci * we respect that otherwise we get DMA underruns. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci do { 36362306a36Sopenharmony_ci value = ctl; 36462306a36Sopenharmony_ci ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 36562306a36Sopenharmony_ci if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci udelay(1); 36862306a36Sopenharmony_ci } while (timeout--); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) 37162306a36Sopenharmony_ci dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", 37262306a36Sopenharmony_ci ctl); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci switch (cmd) { 37662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 37762306a36Sopenharmony_ci /* configure */ 37862306a36Sopenharmony_ci ctl = priv->ctl_play; 37962306a36Sopenharmony_ci if (dai->id == 0) 38062306a36Sopenharmony_ci ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */ 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */ 38362306a36Sopenharmony_ci ctl = kirkwood_i2s_play_mute(ctl); 38462306a36Sopenharmony_ci value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 38562306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_PLAYCTL); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* enable interrupts */ 38862306a36Sopenharmony_ci if (!runtime->no_period_wakeup) { 38962306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 39062306a36Sopenharmony_ci value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; 39162306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* enable playback */ 39562306a36Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 39962306a36Sopenharmony_ci /* stop audio, disable interrupts */ 40062306a36Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 40162306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_MUTE; 40262306a36Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 40562306a36Sopenharmony_ci value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; 40662306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* disable all playbacks */ 40962306a36Sopenharmony_ci ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 41062306a36Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 41462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 41562306a36Sopenharmony_ci ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 41662306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_MUTE; 41762306a36Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 42162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 42262306a36Sopenharmony_ci ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | 42362306a36Sopenharmony_ci KIRKWOOD_PLAYCTL_SPDIF_MUTE); 42462306a36Sopenharmony_ci ctl = kirkwood_i2s_play_mute(ctl); 42562306a36Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci return -EINVAL; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, 43662306a36Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 43962306a36Sopenharmony_ci uint32_t ctl, value; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci switch (cmd) { 44462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 44562306a36Sopenharmony_ci /* configure */ 44662306a36Sopenharmony_ci ctl = priv->ctl_rec; 44762306a36Sopenharmony_ci if (dai->id == 0) 44862306a36Sopenharmony_ci ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */ 44962306a36Sopenharmony_ci else 45062306a36Sopenharmony_ci ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */ 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK; 45362306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* enable interrupts */ 45662306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 45762306a36Sopenharmony_ci value |= KIRKWOOD_INT_CAUSE_REC_BYTES; 45862306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* enable record */ 46162306a36Sopenharmony_ci writel(ctl, priv->io + KIRKWOOD_RECCTL); 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 46562306a36Sopenharmony_ci /* stop audio, disable interrupts */ 46662306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 46762306a36Sopenharmony_ci value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 46862306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_INT_MASK); 47162306a36Sopenharmony_ci value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; 47262306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_INT_MASK); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* disable all records */ 47562306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 47662306a36Sopenharmony_ci value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 47762306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 48162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 48262306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 48362306a36Sopenharmony_ci value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 48462306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 48862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 48962306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 49062306a36Sopenharmony_ci value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); 49162306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci default: 49562306a36Sopenharmony_ci return -EINVAL; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 50262306a36Sopenharmony_ci struct snd_soc_dai *dai) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 50562306a36Sopenharmony_ci return kirkwood_i2s_play_trigger(substream, cmd, dai); 50662306a36Sopenharmony_ci else 50762306a36Sopenharmony_ci return kirkwood_i2s_rec_trigger(substream, cmd, dai); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic int kirkwood_i2s_init(struct kirkwood_dma_data *priv) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci unsigned long value; 51562306a36Sopenharmony_ci unsigned int reg_data; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* put system in a "safe" state : */ 51862306a36Sopenharmony_ci /* disable audio interrupts */ 51962306a36Sopenharmony_ci writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); 52062306a36Sopenharmony_ci writel(0, priv->io + KIRKWOOD_INT_MASK); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci reg_data = readl(priv->io + 0x1200); 52362306a36Sopenharmony_ci reg_data &= (~(0x333FF8)); 52462306a36Sopenharmony_ci reg_data |= 0x111D18; 52562306a36Sopenharmony_ci writel(reg_data, priv->io + 0x1200); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci msleep(500); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci reg_data = readl(priv->io + 0x1200); 53062306a36Sopenharmony_ci reg_data &= (~(0x333FF8)); 53162306a36Sopenharmony_ci reg_data |= 0x111D18; 53262306a36Sopenharmony_ci writel(reg_data, priv->io + 0x1200); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* disable playback/record */ 53562306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_PLAYCTL); 53662306a36Sopenharmony_ci value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; 53762306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_PLAYCTL); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci value = readl(priv->io + KIRKWOOD_RECCTL); 54062306a36Sopenharmony_ci value &= ~KIRKWOOD_RECCTL_ENABLE_MASK; 54162306a36Sopenharmony_ci writel(value, priv->io + KIRKWOOD_RECCTL); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { 54862306a36Sopenharmony_ci .startup = kirkwood_i2s_startup, 54962306a36Sopenharmony_ci .trigger = kirkwood_i2s_trigger, 55062306a36Sopenharmony_ci .hw_params = kirkwood_i2s_hw_params, 55162306a36Sopenharmony_ci .set_fmt = kirkwood_i2s_set_fmt, 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai[2] = { 55562306a36Sopenharmony_ci { 55662306a36Sopenharmony_ci .name = "i2s", 55762306a36Sopenharmony_ci .id = 0, 55862306a36Sopenharmony_ci .playback = { 55962306a36Sopenharmony_ci .channels_min = 1, 56062306a36Sopenharmony_ci .channels_max = 2, 56162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 56262306a36Sopenharmony_ci SNDRV_PCM_RATE_96000, 56362306a36Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 56462306a36Sopenharmony_ci }, 56562306a36Sopenharmony_ci .capture = { 56662306a36Sopenharmony_ci .channels_min = 1, 56762306a36Sopenharmony_ci .channels_max = 2, 56862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 56962306a36Sopenharmony_ci SNDRV_PCM_RATE_96000, 57062306a36Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 57162306a36Sopenharmony_ci }, 57262306a36Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 57362306a36Sopenharmony_ci }, 57462306a36Sopenharmony_ci { 57562306a36Sopenharmony_ci .name = "spdif", 57662306a36Sopenharmony_ci .id = 1, 57762306a36Sopenharmony_ci .playback = { 57862306a36Sopenharmony_ci .channels_min = 1, 57962306a36Sopenharmony_ci .channels_max = 2, 58062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 58162306a36Sopenharmony_ci SNDRV_PCM_RATE_96000, 58262306a36Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 58362306a36Sopenharmony_ci }, 58462306a36Sopenharmony_ci .capture = { 58562306a36Sopenharmony_ci .channels_min = 1, 58662306a36Sopenharmony_ci .channels_max = 2, 58762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 58862306a36Sopenharmony_ci SNDRV_PCM_RATE_96000, 58962306a36Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 59062306a36Sopenharmony_ci }, 59162306a36Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 59262306a36Sopenharmony_ci }, 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = { 59662306a36Sopenharmony_ci { 59762306a36Sopenharmony_ci .name = "i2s", 59862306a36Sopenharmony_ci .id = 0, 59962306a36Sopenharmony_ci .playback = { 60062306a36Sopenharmony_ci .channels_min = 1, 60162306a36Sopenharmony_ci .channels_max = 2, 60262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 60362306a36Sopenharmony_ci .rate_min = 5512, 60462306a36Sopenharmony_ci .rate_max = 192000, 60562306a36Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 60662306a36Sopenharmony_ci }, 60762306a36Sopenharmony_ci .capture = { 60862306a36Sopenharmony_ci .channels_min = 1, 60962306a36Sopenharmony_ci .channels_max = 2, 61062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 61162306a36Sopenharmony_ci .rate_min = 5512, 61262306a36Sopenharmony_ci .rate_max = 192000, 61362306a36Sopenharmony_ci .formats = KIRKWOOD_I2S_FORMATS, 61462306a36Sopenharmony_ci }, 61562306a36Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 61662306a36Sopenharmony_ci }, 61762306a36Sopenharmony_ci { 61862306a36Sopenharmony_ci .name = "spdif", 61962306a36Sopenharmony_ci .id = 1, 62062306a36Sopenharmony_ci .playback = { 62162306a36Sopenharmony_ci .channels_min = 1, 62262306a36Sopenharmony_ci .channels_max = 2, 62362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 62462306a36Sopenharmony_ci .rate_min = 5512, 62562306a36Sopenharmony_ci .rate_max = 192000, 62662306a36Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 62762306a36Sopenharmony_ci }, 62862306a36Sopenharmony_ci .capture = { 62962306a36Sopenharmony_ci .channels_min = 1, 63062306a36Sopenharmony_ci .channels_max = 2, 63162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 63262306a36Sopenharmony_ci .rate_min = 5512, 63362306a36Sopenharmony_ci .rate_max = 192000, 63462306a36Sopenharmony_ci .formats = KIRKWOOD_SPDIF_FORMATS, 63562306a36Sopenharmony_ci }, 63662306a36Sopenharmony_ci .ops = &kirkwood_i2s_dai_ops, 63762306a36Sopenharmony_ci }, 63862306a36Sopenharmony_ci}; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int kirkwood_i2s_dev_probe(struct platform_device *pdev) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data; 64362306a36Sopenharmony_ci struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai; 64462306a36Sopenharmony_ci struct kirkwood_dma_data *priv; 64562306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 64662306a36Sopenharmony_ci int err; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 64962306a36Sopenharmony_ci if (!priv) 65062306a36Sopenharmony_ci return -ENOMEM; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, priv); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (of_device_is_compatible(np, "marvell,armada-380-audio")) 65562306a36Sopenharmony_ci priv->io = devm_platform_ioremap_resource_byname(pdev, "i2s_regs"); 65662306a36Sopenharmony_ci else 65762306a36Sopenharmony_ci priv->io = devm_platform_ioremap_resource(pdev, 0); 65862306a36Sopenharmony_ci if (IS_ERR(priv->io)) 65962306a36Sopenharmony_ci return PTR_ERR(priv->io); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci priv->irq = platform_get_irq(pdev, 0); 66262306a36Sopenharmony_ci if (priv->irq < 0) 66362306a36Sopenharmony_ci return priv->irq; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (of_device_is_compatible(np, "marvell,armada-380-audio")) { 66662306a36Sopenharmony_ci err = armada_38x_i2s_init_quirk(pdev, priv, soc_dai); 66762306a36Sopenharmony_ci if (err < 0) 66862306a36Sopenharmony_ci return err; 66962306a36Sopenharmony_ci /* Set initial pll frequency */ 67062306a36Sopenharmony_ci armada_38x_set_pll(priv->pll_config, 44100); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (np) { 67462306a36Sopenharmony_ci priv->burst = 128; /* might be 32 or 128 */ 67562306a36Sopenharmony_ci } else if (data) { 67662306a36Sopenharmony_ci priv->burst = data->burst; 67762306a36Sopenharmony_ci } else { 67862306a36Sopenharmony_ci dev_err(&pdev->dev, "no DT nor platform data ?!\n"); 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL); 68362306a36Sopenharmony_ci if (IS_ERR(priv->clk)) { 68462306a36Sopenharmony_ci dev_err(&pdev->dev, "no clock\n"); 68562306a36Sopenharmony_ci return PTR_ERR(priv->clk); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci priv->extclk = devm_clk_get(&pdev->dev, "extclk"); 68962306a36Sopenharmony_ci if (IS_ERR(priv->extclk)) { 69062306a36Sopenharmony_ci if (PTR_ERR(priv->extclk) == -EPROBE_DEFER) 69162306a36Sopenharmony_ci return -EPROBE_DEFER; 69262306a36Sopenharmony_ci } else { 69362306a36Sopenharmony_ci if (clk_is_match(priv->extclk, priv->clk)) { 69462306a36Sopenharmony_ci devm_clk_put(&pdev->dev, priv->extclk); 69562306a36Sopenharmony_ci priv->extclk = ERR_PTR(-EINVAL); 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci dev_info(&pdev->dev, "found external clock\n"); 69862306a36Sopenharmony_ci clk_prepare_enable(priv->extclk); 69962306a36Sopenharmony_ci soc_dai = kirkwood_i2s_dai_extclk; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci err = clk_prepare_enable(priv->clk); 70462306a36Sopenharmony_ci if (err < 0) 70562306a36Sopenharmony_ci return err; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Some sensible defaults - this reflects the powerup values */ 70862306a36Sopenharmony_ci priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24; 70962306a36Sopenharmony_ci priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Select the burst size */ 71262306a36Sopenharmony_ci if (priv->burst == 32) { 71362306a36Sopenharmony_ci priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; 71462306a36Sopenharmony_ci priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; 71562306a36Sopenharmony_ci } else { 71662306a36Sopenharmony_ci priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128; 71762306a36Sopenharmony_ci priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component, 72162306a36Sopenharmony_ci soc_dai, 2); 72262306a36Sopenharmony_ci if (err) { 72362306a36Sopenharmony_ci dev_err(&pdev->dev, "snd_soc_register_component failed\n"); 72462306a36Sopenharmony_ci goto err_component; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci kirkwood_i2s_init(priv); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci err_component: 73262306a36Sopenharmony_ci if (!IS_ERR(priv->extclk)) 73362306a36Sopenharmony_ci clk_disable_unprepare(priv->extclk); 73462306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return err; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic void kirkwood_i2s_dev_remove(struct platform_device *pdev) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 74462306a36Sopenharmony_ci if (!IS_ERR(priv->extclk)) 74562306a36Sopenharmony_ci clk_disable_unprepare(priv->extclk); 74662306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci#ifdef CONFIG_OF 75062306a36Sopenharmony_cistatic const struct of_device_id mvebu_audio_of_match[] = { 75162306a36Sopenharmony_ci { .compatible = "marvell,kirkwood-audio" }, 75262306a36Sopenharmony_ci { .compatible = "marvell,dove-audio" }, 75362306a36Sopenharmony_ci { .compatible = "marvell,armada370-audio" }, 75462306a36Sopenharmony_ci { .compatible = "marvell,armada-380-audio" }, 75562306a36Sopenharmony_ci { } 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvebu_audio_of_match); 75862306a36Sopenharmony_ci#endif 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic struct platform_driver kirkwood_i2s_driver = { 76162306a36Sopenharmony_ci .probe = kirkwood_i2s_dev_probe, 76262306a36Sopenharmony_ci .remove_new = kirkwood_i2s_dev_remove, 76362306a36Sopenharmony_ci .driver = { 76462306a36Sopenharmony_ci .name = DRV_NAME, 76562306a36Sopenharmony_ci .of_match_table = of_match_ptr(mvebu_audio_of_match), 76662306a36Sopenharmony_ci }, 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cimodule_platform_driver(kirkwood_i2s_driver); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* Module information */ 77262306a36Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); 77362306a36Sopenharmony_ciMODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); 77462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 77562306a36Sopenharmony_ciMODULE_ALIAS("platform:mvebu-audio"); 776