xref: /openbmc/linux/sound/soc/samsung/spdif.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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