1dbf0344aSSylwester Nawrocki // SPDX-License-Identifier: GPL-2.0
2dbf0344aSSylwester Nawrocki //
3dbf0344aSSylwester Nawrocki // ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
4dbf0344aSSylwester Nawrocki //
5dbf0344aSSylwester Nawrocki // Copyright (c) 2010 Samsung Electronics Co. Ltd
6dbf0344aSSylwester Nawrocki // http://www.samsung.com/
75033f43cSJassi Brar
85033f43cSJassi Brar #include <linux/clk.h>
95033f43cSJassi Brar #include <linux/io.h>
10da155d5bSPaul Gortmaker #include <linux/module.h>
115033f43cSJassi Brar
125033f43cSJassi Brar #include <sound/soc.h>
130378b6acSSeungwhan Youn #include <sound/pcm_params.h>
145033f43cSJassi Brar
15436d42c6SArnd Bergmann #include <linux/platform_data/asoc-s3c.h>
165033f43cSJassi Brar
175033f43cSJassi Brar #include "dma.h"
185033f43cSJassi Brar #include "spdif.h"
195033f43cSJassi Brar
205033f43cSJassi Brar /* Registers */
215033f43cSJassi Brar #define CLKCON 0x00
225033f43cSJassi Brar #define CON 0x04
235033f43cSJassi Brar #define BSTAS 0x08
245033f43cSJassi Brar #define CSTAS 0x0C
255033f43cSJassi Brar #define DATA_OUTBUF 0x10
265033f43cSJassi Brar #define DCNT 0x14
275033f43cSJassi Brar #define BSTAS_S 0x18
285033f43cSJassi Brar #define DCNT_S 0x1C
295033f43cSJassi Brar
305033f43cSJassi Brar #define CLKCTL_MASK 0x7
315033f43cSJassi Brar #define CLKCTL_MCLK_EXT (0x1 << 2)
325033f43cSJassi Brar #define CLKCTL_PWR_ON (0x1 << 0)
335033f43cSJassi Brar
345033f43cSJassi Brar #define CON_MASK 0x3ffffff
355033f43cSJassi Brar #define CON_FIFO_TH_SHIFT 19
365033f43cSJassi Brar #define CON_FIFO_TH_MASK (0x7 << 19)
375033f43cSJassi Brar #define CON_USERDATA_23RDBIT (0x1 << 12)
385033f43cSJassi Brar
395033f43cSJassi Brar #define CON_SW_RESET (0x1 << 5)
405033f43cSJassi Brar
415033f43cSJassi Brar #define CON_MCLKDIV_MASK (0x3 << 3)
425033f43cSJassi Brar #define CON_MCLKDIV_256FS (0x0 << 3)
435033f43cSJassi Brar #define CON_MCLKDIV_384FS (0x1 << 3)
445033f43cSJassi Brar #define CON_MCLKDIV_512FS (0x2 << 3)
455033f43cSJassi Brar
465033f43cSJassi Brar #define CON_PCM_MASK (0x3 << 1)
475033f43cSJassi Brar #define CON_PCM_16BIT (0x0 << 1)
485033f43cSJassi Brar #define CON_PCM_20BIT (0x1 << 1)
495033f43cSJassi Brar #define CON_PCM_24BIT (0x2 << 1)
505033f43cSJassi Brar
515033f43cSJassi Brar #define CON_PCM_DATA (0x1 << 0)
525033f43cSJassi Brar
535033f43cSJassi Brar #define CSTAS_MASK 0x3fffffff
545033f43cSJassi Brar #define CSTAS_SAMP_FREQ_MASK (0xF << 24)
555033f43cSJassi Brar #define CSTAS_SAMP_FREQ_44 (0x0 << 24)
565033f43cSJassi Brar #define CSTAS_SAMP_FREQ_48 (0x2 << 24)
575033f43cSJassi Brar #define CSTAS_SAMP_FREQ_32 (0x3 << 24)
585033f43cSJassi Brar #define CSTAS_SAMP_FREQ_96 (0xA << 24)
595033f43cSJassi Brar
605033f43cSJassi Brar #define CSTAS_CATEGORY_MASK (0xFF << 8)
615033f43cSJassi Brar #define CSTAS_CATEGORY_CODE_CDP (0x01 << 8)
625033f43cSJassi Brar
635033f43cSJassi Brar #define CSTAS_NO_COPYRIGHT (0x1 << 2)
645033f43cSJassi Brar
655033f43cSJassi Brar /**
665033f43cSJassi Brar * struct samsung_spdif_info - Samsung S/PDIF Controller information
675033f43cSJassi Brar * @lock: Spin lock for S/PDIF.
685033f43cSJassi Brar * @dev: The parent device passed to use from the probe.
695033f43cSJassi Brar * @regs: The pointer to the device register block.
705033f43cSJassi Brar * @clk_rate: Current clock rate for calcurate ratio.
715033f43cSJassi Brar * @pclk: The peri-clock pointer for spdif master operation.
725033f43cSJassi Brar * @sclk: The source clock pointer for making sync signals.
7353c512d8SPierre-Louis Bossart * @saved_clkcon: Backup clkcon reg. in suspend.
7453c512d8SPierre-Louis Bossart * @saved_con: Backup con reg. in suspend.
7553c512d8SPierre-Louis Bossart * @saved_cstas: Backup cstas reg. in suspend.
765033f43cSJassi Brar * @dma_playback: DMA information for playback channel.
775033f43cSJassi Brar */
785033f43cSJassi Brar struct samsung_spdif_info {
795033f43cSJassi Brar spinlock_t lock;
805033f43cSJassi Brar struct device *dev;
815033f43cSJassi Brar void __iomem *regs;
825033f43cSJassi Brar unsigned long clk_rate;
835033f43cSJassi Brar struct clk *pclk;
845033f43cSJassi Brar struct clk *sclk;
855033f43cSJassi Brar u32 saved_clkcon;
865033f43cSJassi Brar u32 saved_con;
875033f43cSJassi Brar u32 saved_cstas;
882b658345SSylwester Nawrocki struct snd_dmaengine_dai_dma_data *dma_playback;
895033f43cSJassi Brar };
905033f43cSJassi Brar
912b658345SSylwester Nawrocki static struct snd_dmaengine_dai_dma_data spdif_stereo_out;
925033f43cSJassi Brar static struct samsung_spdif_info spdif_info;
935033f43cSJassi Brar
9479a5cf90SKuninori Morimoto static inline struct samsung_spdif_info
component_to_info(struct snd_soc_component * component)9579a5cf90SKuninori Morimoto *component_to_info(struct snd_soc_component *component)
9679a5cf90SKuninori Morimoto {
9779a5cf90SKuninori Morimoto return snd_soc_component_get_drvdata(component);
9879a5cf90SKuninori Morimoto }
9979a5cf90SKuninori Morimoto
to_info(struct snd_soc_dai * cpu_dai)1005033f43cSJassi Brar static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
1015033f43cSJassi Brar {
1025033f43cSJassi Brar return snd_soc_dai_get_drvdata(cpu_dai);
1035033f43cSJassi Brar }
1045033f43cSJassi Brar
spdif_snd_txctrl(struct samsung_spdif_info * spdif,int on)1055033f43cSJassi Brar static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
1065033f43cSJassi Brar {
1075033f43cSJassi Brar void __iomem *regs = spdif->regs;
1085033f43cSJassi Brar u32 clkcon;
1095033f43cSJassi Brar
1105033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
1115033f43cSJassi Brar
1125033f43cSJassi Brar clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
1135033f43cSJassi Brar if (on)
1145033f43cSJassi Brar writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
1155033f43cSJassi Brar else
1165033f43cSJassi Brar writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
1175033f43cSJassi Brar }
1185033f43cSJassi Brar
spdif_set_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)1195033f43cSJassi Brar static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
1205033f43cSJassi Brar int clk_id, unsigned int freq, int dir)
1215033f43cSJassi Brar {
1225033f43cSJassi Brar struct samsung_spdif_info *spdif = to_info(cpu_dai);
1235033f43cSJassi Brar u32 clkcon;
1245033f43cSJassi Brar
1255033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
1265033f43cSJassi Brar
1275033f43cSJassi Brar clkcon = readl(spdif->regs + CLKCON);
1285033f43cSJassi Brar
1295033f43cSJassi Brar if (clk_id == SND_SOC_SPDIF_INT_MCLK)
1305033f43cSJassi Brar clkcon &= ~CLKCTL_MCLK_EXT;
1315033f43cSJassi Brar else
1325033f43cSJassi Brar clkcon |= CLKCTL_MCLK_EXT;
1335033f43cSJassi Brar
1345033f43cSJassi Brar writel(clkcon, spdif->regs + CLKCON);
1355033f43cSJassi Brar
1365033f43cSJassi Brar spdif->clk_rate = freq;
1375033f43cSJassi Brar
1385033f43cSJassi Brar return 0;
1395033f43cSJassi Brar }
1405033f43cSJassi Brar
spdif_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)1415033f43cSJassi Brar static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
1425033f43cSJassi Brar struct snd_soc_dai *dai)
1435033f43cSJassi Brar {
144c101ce88SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1457de6b6bcSKuninori Morimoto struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0));
1465033f43cSJassi Brar unsigned long flags;
1475033f43cSJassi Brar
1485033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
1495033f43cSJassi Brar
1505033f43cSJassi Brar switch (cmd) {
1515033f43cSJassi Brar case SNDRV_PCM_TRIGGER_START:
1525033f43cSJassi Brar case SNDRV_PCM_TRIGGER_RESUME:
1535033f43cSJassi Brar case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1545033f43cSJassi Brar spin_lock_irqsave(&spdif->lock, flags);
1555033f43cSJassi Brar spdif_snd_txctrl(spdif, 1);
1565033f43cSJassi Brar spin_unlock_irqrestore(&spdif->lock, flags);
1575033f43cSJassi Brar break;
1585033f43cSJassi Brar case SNDRV_PCM_TRIGGER_STOP:
1595033f43cSJassi Brar case SNDRV_PCM_TRIGGER_SUSPEND:
1605033f43cSJassi Brar case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1615033f43cSJassi Brar spin_lock_irqsave(&spdif->lock, flags);
1625033f43cSJassi Brar spdif_snd_txctrl(spdif, 0);
1635033f43cSJassi Brar spin_unlock_irqrestore(&spdif->lock, flags);
1645033f43cSJassi Brar break;
1655033f43cSJassi Brar default:
1665033f43cSJassi Brar return -EINVAL;
1675033f43cSJassi Brar }
1685033f43cSJassi Brar
1695033f43cSJassi Brar return 0;
1705033f43cSJassi Brar }
1715033f43cSJassi Brar
1725033f43cSJassi Brar static int spdif_sysclk_ratios[] = {
1735033f43cSJassi Brar 512, 384, 256,
1745033f43cSJassi Brar };
1755033f43cSJassi Brar
spdif_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * socdai)1765033f43cSJassi Brar static int spdif_hw_params(struct snd_pcm_substream *substream,
1775033f43cSJassi Brar struct snd_pcm_hw_params *params,
1785033f43cSJassi Brar struct snd_soc_dai *socdai)
1795033f43cSJassi Brar {
180c101ce88SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1817de6b6bcSKuninori Morimoto struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0));
1825033f43cSJassi Brar void __iomem *regs = spdif->regs;
1832b658345SSylwester Nawrocki struct snd_dmaengine_dai_dma_data *dma_data;
1845033f43cSJassi Brar u32 con, clkcon, cstas;
1855033f43cSJassi Brar unsigned long flags;
1865033f43cSJassi Brar int i, ratio;
1875033f43cSJassi Brar
1885033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
1895033f43cSJassi Brar
1905033f43cSJassi Brar if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1915033f43cSJassi Brar dma_data = spdif->dma_playback;
1925033f43cSJassi Brar else {
1935033f43cSJassi Brar dev_err(spdif->dev, "Capture is not supported\n");
1945033f43cSJassi Brar return -EINVAL;
1955033f43cSJassi Brar }
1965033f43cSJassi Brar
1977de6b6bcSKuninori Morimoto snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_data);
1985033f43cSJassi Brar
1995033f43cSJassi Brar spin_lock_irqsave(&spdif->lock, flags);
2005033f43cSJassi Brar
2015033f43cSJassi Brar con = readl(regs + CON) & CON_MASK;
2025033f43cSJassi Brar cstas = readl(regs + CSTAS) & CSTAS_MASK;
2035033f43cSJassi Brar clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
2045033f43cSJassi Brar
2055033f43cSJassi Brar con &= ~CON_FIFO_TH_MASK;
2065033f43cSJassi Brar con |= (0x7 << CON_FIFO_TH_SHIFT);
2075033f43cSJassi Brar con |= CON_USERDATA_23RDBIT;
2085033f43cSJassi Brar con |= CON_PCM_DATA;
2095033f43cSJassi Brar
2105033f43cSJassi Brar con &= ~CON_PCM_MASK;
21188ce1465STushar Behera switch (params_width(params)) {
21288ce1465STushar Behera case 16:
2135033f43cSJassi Brar con |= CON_PCM_16BIT;
2145033f43cSJassi Brar break;
2155033f43cSJassi Brar default:
2165033f43cSJassi Brar dev_err(spdif->dev, "Unsupported data size.\n");
2175033f43cSJassi Brar goto err;
2185033f43cSJassi Brar }
2195033f43cSJassi Brar
2205033f43cSJassi Brar ratio = spdif->clk_rate / params_rate(params);
2215033f43cSJassi Brar for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++)
2225033f43cSJassi Brar if (ratio == spdif_sysclk_ratios[i])
2235033f43cSJassi Brar break;
2245033f43cSJassi Brar if (i == ARRAY_SIZE(spdif_sysclk_ratios)) {
2255033f43cSJassi Brar dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n",
2265033f43cSJassi Brar spdif->clk_rate, params_rate(params));
2275033f43cSJassi Brar goto err;
2285033f43cSJassi Brar }
2295033f43cSJassi Brar
2305033f43cSJassi Brar con &= ~CON_MCLKDIV_MASK;
2315033f43cSJassi Brar switch (ratio) {
2325033f43cSJassi Brar case 256:
2335033f43cSJassi Brar con |= CON_MCLKDIV_256FS;
2345033f43cSJassi Brar break;
2355033f43cSJassi Brar case 384:
2365033f43cSJassi Brar con |= CON_MCLKDIV_384FS;
2375033f43cSJassi Brar break;
2385033f43cSJassi Brar case 512:
2395033f43cSJassi Brar con |= CON_MCLKDIV_512FS;
2405033f43cSJassi Brar break;
2415033f43cSJassi Brar }
2425033f43cSJassi Brar
2435033f43cSJassi Brar cstas &= ~CSTAS_SAMP_FREQ_MASK;
2445033f43cSJassi Brar switch (params_rate(params)) {
2455033f43cSJassi Brar case 44100:
2465033f43cSJassi Brar cstas |= CSTAS_SAMP_FREQ_44;
2475033f43cSJassi Brar break;
2485033f43cSJassi Brar case 48000:
2495033f43cSJassi Brar cstas |= CSTAS_SAMP_FREQ_48;
2505033f43cSJassi Brar break;
2515033f43cSJassi Brar case 32000:
2525033f43cSJassi Brar cstas |= CSTAS_SAMP_FREQ_32;
2535033f43cSJassi Brar break;
2545033f43cSJassi Brar case 96000:
2555033f43cSJassi Brar cstas |= CSTAS_SAMP_FREQ_96;
2565033f43cSJassi Brar break;
2575033f43cSJassi Brar default:
2585033f43cSJassi Brar dev_err(spdif->dev, "Invalid sampling rate %d\n",
2595033f43cSJassi Brar params_rate(params));
2605033f43cSJassi Brar goto err;
2615033f43cSJassi Brar }
2625033f43cSJassi Brar
2635033f43cSJassi Brar cstas &= ~CSTAS_CATEGORY_MASK;
2645033f43cSJassi Brar cstas |= CSTAS_CATEGORY_CODE_CDP;
2655033f43cSJassi Brar cstas |= CSTAS_NO_COPYRIGHT;
2665033f43cSJassi Brar
2675033f43cSJassi Brar writel(con, regs + CON);
2685033f43cSJassi Brar writel(cstas, regs + CSTAS);
2695033f43cSJassi Brar writel(clkcon, regs + CLKCON);
2705033f43cSJassi Brar
2715033f43cSJassi Brar spin_unlock_irqrestore(&spdif->lock, flags);
2725033f43cSJassi Brar
2735033f43cSJassi Brar return 0;
2745033f43cSJassi Brar err:
2755033f43cSJassi Brar spin_unlock_irqrestore(&spdif->lock, flags);
2765033f43cSJassi Brar return -EINVAL;
2775033f43cSJassi Brar }
2785033f43cSJassi Brar
spdif_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)2795033f43cSJassi Brar static void spdif_shutdown(struct snd_pcm_substream *substream,
2805033f43cSJassi Brar struct snd_soc_dai *dai)
2815033f43cSJassi Brar {
282c101ce88SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2837de6b6bcSKuninori Morimoto struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0));
2845033f43cSJassi Brar void __iomem *regs = spdif->regs;
2855033f43cSJassi Brar u32 con, clkcon;
2865033f43cSJassi Brar
2875033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
2885033f43cSJassi Brar
2895033f43cSJassi Brar con = readl(regs + CON) & CON_MASK;
2905033f43cSJassi Brar clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
2915033f43cSJassi Brar
2925033f43cSJassi Brar writel(con | CON_SW_RESET, regs + CON);
2935033f43cSJassi Brar cpu_relax();
2945033f43cSJassi Brar
2955033f43cSJassi Brar writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
2965033f43cSJassi Brar }
2975033f43cSJassi Brar
2985033f43cSJassi Brar #ifdef CONFIG_PM
spdif_suspend(struct snd_soc_component * component)29979a5cf90SKuninori Morimoto static int spdif_suspend(struct snd_soc_component *component)
3005033f43cSJassi Brar {
30179a5cf90SKuninori Morimoto struct samsung_spdif_info *spdif = component_to_info(component);
3025033f43cSJassi Brar u32 con = spdif->saved_con;
3035033f43cSJassi Brar
3045033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
3055033f43cSJassi Brar
3065033f43cSJassi Brar spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK;
3075033f43cSJassi Brar spdif->saved_con = readl(spdif->regs + CON) & CON_MASK;
3085033f43cSJassi Brar spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK;
3095033f43cSJassi Brar
3105033f43cSJassi Brar writel(con | CON_SW_RESET, spdif->regs + CON);
3115033f43cSJassi Brar cpu_relax();
3125033f43cSJassi Brar
3135033f43cSJassi Brar return 0;
3145033f43cSJassi Brar }
3155033f43cSJassi Brar
spdif_resume(struct snd_soc_component * component)31679a5cf90SKuninori Morimoto static int spdif_resume(struct snd_soc_component *component)
3175033f43cSJassi Brar {
31879a5cf90SKuninori Morimoto struct samsung_spdif_info *spdif = component_to_info(component);
3195033f43cSJassi Brar
3205033f43cSJassi Brar dev_dbg(spdif->dev, "Entered %s\n", __func__);
3215033f43cSJassi Brar
3225033f43cSJassi Brar writel(spdif->saved_clkcon, spdif->regs + CLKCON);
3235033f43cSJassi Brar writel(spdif->saved_con, spdif->regs + CON);
3245033f43cSJassi Brar writel(spdif->saved_cstas, spdif->regs + CSTAS);
3255033f43cSJassi Brar
3265033f43cSJassi Brar return 0;
3275033f43cSJassi Brar }
3285033f43cSJassi Brar #else
3295033f43cSJassi Brar #define spdif_suspend NULL
3305033f43cSJassi Brar #define spdif_resume NULL
3315033f43cSJassi Brar #endif
3325033f43cSJassi Brar
33385e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops spdif_dai_ops = {
3345033f43cSJassi Brar .set_sysclk = spdif_set_sysclk,
3355033f43cSJassi Brar .trigger = spdif_trigger,
3365033f43cSJassi Brar .hw_params = spdif_hw_params,
3375033f43cSJassi Brar .shutdown = spdif_shutdown,
3385033f43cSJassi Brar };
3395033f43cSJassi Brar
340bc6bd90eSAxel Lin static struct snd_soc_dai_driver samsung_spdif_dai = {
3415033f43cSJassi Brar .name = "samsung-spdif",
3425033f43cSJassi Brar .playback = {
3435033f43cSJassi Brar .stream_name = "S/PDIF Playback",
3445033f43cSJassi Brar .channels_min = 2,
3455033f43cSJassi Brar .channels_max = 2,
3465033f43cSJassi Brar .rates = (SNDRV_PCM_RATE_32000 |
3475033f43cSJassi Brar SNDRV_PCM_RATE_44100 |
3485033f43cSJassi Brar SNDRV_PCM_RATE_48000 |
3495033f43cSJassi Brar SNDRV_PCM_RATE_96000),
3505033f43cSJassi Brar .formats = SNDRV_PCM_FMTBIT_S16_LE, },
3515033f43cSJassi Brar .ops = &spdif_dai_ops,
3525033f43cSJassi Brar };
3535033f43cSJassi Brar
354c3764d8bSKuninori Morimoto static const struct snd_soc_component_driver samsung_spdif_component = {
355c3764d8bSKuninori Morimoto .name = "samsung-spdif",
35679a5cf90SKuninori Morimoto .suspend = spdif_suspend,
35779a5cf90SKuninori Morimoto .resume = spdif_resume,
358f7bfa516SCharles Keepax .legacy_dai_naming = 1,
359c3764d8bSKuninori Morimoto };
360c3764d8bSKuninori Morimoto
spdif_probe(struct platform_device * pdev)361fdca21adSBill Pemberton static int spdif_probe(struct platform_device *pdev)
3625033f43cSJassi Brar {
3635033f43cSJassi Brar struct s3c_audio_pdata *spdif_pdata;
364b9a1a743SArnd Bergmann struct resource *mem_res;
3655033f43cSJassi Brar struct samsung_spdif_info *spdif;
3669bdca822SArnd Bergmann dma_filter_fn filter;
3675033f43cSJassi Brar int ret;
3685033f43cSJassi Brar
3695033f43cSJassi Brar spdif_pdata = pdev->dev.platform_data;
3705033f43cSJassi Brar
3715033f43cSJassi Brar dev_dbg(&pdev->dev, "Entered %s\n", __func__);
3725033f43cSJassi Brar
3735033f43cSJassi Brar mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3745033f43cSJassi Brar if (!mem_res) {
3755033f43cSJassi Brar dev_err(&pdev->dev, "Unable to get register resource.\n");
3765033f43cSJassi Brar return -ENXIO;
3775033f43cSJassi Brar }
3785033f43cSJassi Brar
3795033f43cSJassi Brar if (spdif_pdata && spdif_pdata->cfg_gpio
3805033f43cSJassi Brar && spdif_pdata->cfg_gpio(pdev)) {
3815033f43cSJassi Brar dev_err(&pdev->dev, "Unable to configure GPIO pins\n");
3825033f43cSJassi Brar return -EINVAL;
3835033f43cSJassi Brar }
3845033f43cSJassi Brar
3855033f43cSJassi Brar spdif = &spdif_info;
3865033f43cSJassi Brar spdif->dev = &pdev->dev;
3875033f43cSJassi Brar
3885033f43cSJassi Brar spin_lock_init(&spdif->lock);
3895033f43cSJassi Brar
3907497185fSMark Brown spdif->pclk = devm_clk_get(&pdev->dev, "spdif");
3915033f43cSJassi Brar if (IS_ERR(spdif->pclk)) {
3925033f43cSJassi Brar dev_err(&pdev->dev, "failed to get peri-clock\n");
3935033f43cSJassi Brar ret = -ENOENT;
3945033f43cSJassi Brar goto err0;
3955033f43cSJassi Brar }
3960f11daf1SArvind Yadav ret = clk_prepare_enable(spdif->pclk);
3970f11daf1SArvind Yadav if (ret)
3980f11daf1SArvind Yadav goto err0;
3995033f43cSJassi Brar
4007497185fSMark Brown spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif");
4015033f43cSJassi Brar if (IS_ERR(spdif->sclk)) {
4025033f43cSJassi Brar dev_err(&pdev->dev, "failed to get internal source clock\n");
4035033f43cSJassi Brar ret = -ENOENT;
4045033f43cSJassi Brar goto err1;
4055033f43cSJassi Brar }
4060f11daf1SArvind Yadav ret = clk_prepare_enable(spdif->sclk);
4070f11daf1SArvind Yadav if (ret)
4080f11daf1SArvind Yadav goto err1;
4095033f43cSJassi Brar
4105033f43cSJassi Brar /* Request S/PDIF Register's memory region */
4115033f43cSJassi Brar if (!request_mem_region(mem_res->start,
4125033f43cSJassi Brar resource_size(mem_res), "samsung-spdif")) {
4135033f43cSJassi Brar dev_err(&pdev->dev, "Unable to request register region\n");
4145033f43cSJassi Brar ret = -EBUSY;
4155033f43cSJassi Brar goto err2;
4165033f43cSJassi Brar }
4175033f43cSJassi Brar
4185033f43cSJassi Brar spdif->regs = ioremap(mem_res->start, 0x100);
4195033f43cSJassi Brar if (spdif->regs == NULL) {
4205033f43cSJassi Brar dev_err(&pdev->dev, "Cannot ioremap registers\n");
4215033f43cSJassi Brar ret = -ENXIO;
4225033f43cSJassi Brar goto err3;
4235033f43cSJassi Brar }
4245033f43cSJassi Brar
425a4513320SSylwester Nawrocki spdif_stereo_out.addr_width = 2;
426a4513320SSylwester Nawrocki spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF;
427a4513320SSylwester Nawrocki filter = NULL;
428a4513320SSylwester Nawrocki if (spdif_pdata) {
429a4513320SSylwester Nawrocki spdif_stereo_out.filter_data = spdif_pdata->dma_playback;
430a4513320SSylwester Nawrocki filter = spdif_pdata->dma_filter;
431a4513320SSylwester Nawrocki }
432a4513320SSylwester Nawrocki spdif->dma_playback = &spdif_stereo_out;
433a4513320SSylwester Nawrocki
43473f5dfc6SMarek Szyprowski ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
43596f06cdeSSylwester Nawrocki NULL, NULL, NULL);
43673f5dfc6SMarek Szyprowski if (ret) {
43773f5dfc6SMarek Szyprowski dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
43873f5dfc6SMarek Szyprowski goto err4;
43973f5dfc6SMarek Szyprowski }
44073f5dfc6SMarek Szyprowski
4415033f43cSJassi Brar dev_set_drvdata(&pdev->dev, spdif);
4425033f43cSJassi Brar
4437253e354STushar Behera ret = devm_snd_soc_register_component(&pdev->dev,
4447253e354STushar Behera &samsung_spdif_component, &samsung_spdif_dai, 1);
4455033f43cSJassi Brar if (ret != 0) {
4465033f43cSJassi Brar dev_err(&pdev->dev, "fail to register dai\n");
4475033f43cSJassi Brar goto err4;
4485033f43cSJassi Brar }
4495033f43cSJassi Brar
450a08485d8SPadmavathi Venna return 0;
4515033f43cSJassi Brar err4:
4525033f43cSJassi Brar iounmap(spdif->regs);
4535033f43cSJassi Brar err3:
4545033f43cSJassi Brar release_mem_region(mem_res->start, resource_size(mem_res));
4555033f43cSJassi Brar err2:
4569d01e9b2SThomas Abraham clk_disable_unprepare(spdif->sclk);
4575033f43cSJassi Brar err1:
4589d01e9b2SThomas Abraham clk_disable_unprepare(spdif->pclk);
4595033f43cSJassi Brar err0:
4605033f43cSJassi Brar return ret;
4615033f43cSJassi Brar }
4625033f43cSJassi Brar
spdif_remove(struct platform_device * pdev)463*7695582fSUwe Kleine-König static void spdif_remove(struct platform_device *pdev)
4645033f43cSJassi Brar {
4655033f43cSJassi Brar struct samsung_spdif_info *spdif = &spdif_info;
4665033f43cSJassi Brar struct resource *mem_res;
4675033f43cSJassi Brar
4685033f43cSJassi Brar iounmap(spdif->regs);
4695033f43cSJassi Brar
4705033f43cSJassi Brar mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4715033f43cSJassi Brar release_mem_region(mem_res->start, resource_size(mem_res));
4725033f43cSJassi Brar
4739d01e9b2SThomas Abraham clk_disable_unprepare(spdif->sclk);
4749d01e9b2SThomas Abraham clk_disable_unprepare(spdif->pclk);
4755033f43cSJassi Brar }
4765033f43cSJassi Brar
4775033f43cSJassi Brar static struct platform_driver samsung_spdif_driver = {
4785033f43cSJassi Brar .probe = spdif_probe,
479*7695582fSUwe Kleine-König .remove_new = spdif_remove,
4805033f43cSJassi Brar .driver = {
4815033f43cSJassi Brar .name = "samsung-spdif",
4825033f43cSJassi Brar },
4835033f43cSJassi Brar };
4845033f43cSJassi Brar
485e00c3f55SMark Brown module_platform_driver(samsung_spdif_driver);
4865033f43cSJassi Brar
4875033f43cSJassi Brar MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
4885033f43cSJassi Brar MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
4895033f43cSJassi Brar MODULE_LICENSE("GPL");
4905033f43cSJassi Brar MODULE_ALIAS("platform:samsung-spdif");
491