1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // Copyright (c) 2010 Samsung Electronics Co. Ltd
6*4882a593Smuzhiyun // http://www.samsung.com/
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/clk.h>
9*4882a593Smuzhiyun #include <linux/io.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <sound/soc.h>
13*4882a593Smuzhiyun #include <sound/pcm_params.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/platform_data/asoc-s3c.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include "dma.h"
18*4882a593Smuzhiyun #include "spdif.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun /* Registers */
21*4882a593Smuzhiyun #define CLKCON 0x00
22*4882a593Smuzhiyun #define CON 0x04
23*4882a593Smuzhiyun #define BSTAS 0x08
24*4882a593Smuzhiyun #define CSTAS 0x0C
25*4882a593Smuzhiyun #define DATA_OUTBUF 0x10
26*4882a593Smuzhiyun #define DCNT 0x14
27*4882a593Smuzhiyun #define BSTAS_S 0x18
28*4882a593Smuzhiyun #define DCNT_S 0x1C
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define CLKCTL_MASK 0x7
31*4882a593Smuzhiyun #define CLKCTL_MCLK_EXT (0x1 << 2)
32*4882a593Smuzhiyun #define CLKCTL_PWR_ON (0x1 << 0)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define CON_MASK 0x3ffffff
35*4882a593Smuzhiyun #define CON_FIFO_TH_SHIFT 19
36*4882a593Smuzhiyun #define CON_FIFO_TH_MASK (0x7 << 19)
37*4882a593Smuzhiyun #define CON_USERDATA_23RDBIT (0x1 << 12)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define CON_SW_RESET (0x1 << 5)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #define CON_MCLKDIV_MASK (0x3 << 3)
42*4882a593Smuzhiyun #define CON_MCLKDIV_256FS (0x0 << 3)
43*4882a593Smuzhiyun #define CON_MCLKDIV_384FS (0x1 << 3)
44*4882a593Smuzhiyun #define CON_MCLKDIV_512FS (0x2 << 3)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun #define CON_PCM_MASK (0x3 << 1)
47*4882a593Smuzhiyun #define CON_PCM_16BIT (0x0 << 1)
48*4882a593Smuzhiyun #define CON_PCM_20BIT (0x1 << 1)
49*4882a593Smuzhiyun #define CON_PCM_24BIT (0x2 << 1)
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun #define CON_PCM_DATA (0x1 << 0)
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun #define CSTAS_MASK 0x3fffffff
54*4882a593Smuzhiyun #define CSTAS_SAMP_FREQ_MASK (0xF << 24)
55*4882a593Smuzhiyun #define CSTAS_SAMP_FREQ_44 (0x0 << 24)
56*4882a593Smuzhiyun #define CSTAS_SAMP_FREQ_48 (0x2 << 24)
57*4882a593Smuzhiyun #define CSTAS_SAMP_FREQ_32 (0x3 << 24)
58*4882a593Smuzhiyun #define CSTAS_SAMP_FREQ_96 (0xA << 24)
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #define CSTAS_CATEGORY_MASK (0xFF << 8)
61*4882a593Smuzhiyun #define CSTAS_CATEGORY_CODE_CDP (0x01 << 8)
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun #define CSTAS_NO_COPYRIGHT (0x1 << 2)
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun /**
66*4882a593Smuzhiyun * struct samsung_spdif_info - Samsung S/PDIF Controller information
67*4882a593Smuzhiyun * @lock: Spin lock for S/PDIF.
68*4882a593Smuzhiyun * @dev: The parent device passed to use from the probe.
69*4882a593Smuzhiyun * @regs: The pointer to the device register block.
70*4882a593Smuzhiyun * @clk_rate: Current clock rate for calcurate ratio.
71*4882a593Smuzhiyun * @pclk: The peri-clock pointer for spdif master operation.
72*4882a593Smuzhiyun * @sclk: The source clock pointer for making sync signals.
73*4882a593Smuzhiyun * @saved_clkcon: Backup clkcon reg. in suspend.
74*4882a593Smuzhiyun * @saved_con: Backup con reg. in suspend.
75*4882a593Smuzhiyun * @saved_cstas: Backup cstas reg. in suspend.
76*4882a593Smuzhiyun * @dma_playback: DMA information for playback channel.
77*4882a593Smuzhiyun */
78*4882a593Smuzhiyun struct samsung_spdif_info {
79*4882a593Smuzhiyun spinlock_t lock;
80*4882a593Smuzhiyun struct device *dev;
81*4882a593Smuzhiyun void __iomem *regs;
82*4882a593Smuzhiyun unsigned long clk_rate;
83*4882a593Smuzhiyun struct clk *pclk;
84*4882a593Smuzhiyun struct clk *sclk;
85*4882a593Smuzhiyun u32 saved_clkcon;
86*4882a593Smuzhiyun u32 saved_con;
87*4882a593Smuzhiyun u32 saved_cstas;
88*4882a593Smuzhiyun struct snd_dmaengine_dai_dma_data *dma_playback;
89*4882a593Smuzhiyun };
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun static struct snd_dmaengine_dai_dma_data spdif_stereo_out;
92*4882a593Smuzhiyun static struct samsung_spdif_info spdif_info;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun static inline struct samsung_spdif_info
component_to_info(struct snd_soc_component * component)95*4882a593Smuzhiyun *component_to_info(struct snd_soc_component *component)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun return snd_soc_component_get_drvdata(component);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
to_info(struct snd_soc_dai * cpu_dai)100*4882a593Smuzhiyun static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun return snd_soc_dai_get_drvdata(cpu_dai);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
spdif_snd_txctrl(struct samsung_spdif_info * spdif,int on)105*4882a593Smuzhiyun static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun void __iomem *regs = spdif->regs;
108*4882a593Smuzhiyun u32 clkcon;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
113*4882a593Smuzhiyun if (on)
114*4882a593Smuzhiyun writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
115*4882a593Smuzhiyun else
116*4882a593Smuzhiyun writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
spdif_set_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)119*4882a593Smuzhiyun static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
120*4882a593Smuzhiyun int clk_id, unsigned int freq, int dir)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun struct samsung_spdif_info *spdif = to_info(cpu_dai);
123*4882a593Smuzhiyun u32 clkcon;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun clkcon = readl(spdif->regs + CLKCON);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (clk_id == SND_SOC_SPDIF_INT_MCLK)
130*4882a593Smuzhiyun clkcon &= ~CLKCTL_MCLK_EXT;
131*4882a593Smuzhiyun else
132*4882a593Smuzhiyun clkcon |= CLKCTL_MCLK_EXT;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun writel(clkcon, spdif->regs + CLKCON);
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun spdif->clk_rate = freq;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
spdif_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)141*4882a593Smuzhiyun static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
142*4882a593Smuzhiyun struct snd_soc_dai *dai)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
145*4882a593Smuzhiyun struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0));
146*4882a593Smuzhiyun unsigned long flags;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun switch (cmd) {
151*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_START:
152*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_RESUME:
153*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
154*4882a593Smuzhiyun spin_lock_irqsave(&spdif->lock, flags);
155*4882a593Smuzhiyun spdif_snd_txctrl(spdif, 1);
156*4882a593Smuzhiyun spin_unlock_irqrestore(&spdif->lock, flags);
157*4882a593Smuzhiyun break;
158*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_STOP:
159*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_SUSPEND:
160*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
161*4882a593Smuzhiyun spin_lock_irqsave(&spdif->lock, flags);
162*4882a593Smuzhiyun spdif_snd_txctrl(spdif, 0);
163*4882a593Smuzhiyun spin_unlock_irqrestore(&spdif->lock, flags);
164*4882a593Smuzhiyun break;
165*4882a593Smuzhiyun default:
166*4882a593Smuzhiyun return -EINVAL;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun static int spdif_sysclk_ratios[] = {
173*4882a593Smuzhiyun 512, 384, 256,
174*4882a593Smuzhiyun };
175*4882a593Smuzhiyun
spdif_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * socdai)176*4882a593Smuzhiyun static int spdif_hw_params(struct snd_pcm_substream *substream,
177*4882a593Smuzhiyun struct snd_pcm_hw_params *params,
178*4882a593Smuzhiyun struct snd_soc_dai *socdai)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
181*4882a593Smuzhiyun struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0));
182*4882a593Smuzhiyun void __iomem *regs = spdif->regs;
183*4882a593Smuzhiyun struct snd_dmaengine_dai_dma_data *dma_data;
184*4882a593Smuzhiyun u32 con, clkcon, cstas;
185*4882a593Smuzhiyun unsigned long flags;
186*4882a593Smuzhiyun int i, ratio;
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
191*4882a593Smuzhiyun dma_data = spdif->dma_playback;
192*4882a593Smuzhiyun else {
193*4882a593Smuzhiyun dev_err(spdif->dev, "Capture is not supported\n");
194*4882a593Smuzhiyun return -EINVAL;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_data);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun spin_lock_irqsave(&spdif->lock, flags);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun con = readl(regs + CON) & CON_MASK;
202*4882a593Smuzhiyun cstas = readl(regs + CSTAS) & CSTAS_MASK;
203*4882a593Smuzhiyun clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun con &= ~CON_FIFO_TH_MASK;
206*4882a593Smuzhiyun con |= (0x7 << CON_FIFO_TH_SHIFT);
207*4882a593Smuzhiyun con |= CON_USERDATA_23RDBIT;
208*4882a593Smuzhiyun con |= CON_PCM_DATA;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun con &= ~CON_PCM_MASK;
211*4882a593Smuzhiyun switch (params_width(params)) {
212*4882a593Smuzhiyun case 16:
213*4882a593Smuzhiyun con |= CON_PCM_16BIT;
214*4882a593Smuzhiyun break;
215*4882a593Smuzhiyun default:
216*4882a593Smuzhiyun dev_err(spdif->dev, "Unsupported data size.\n");
217*4882a593Smuzhiyun goto err;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun ratio = spdif->clk_rate / params_rate(params);
221*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++)
222*4882a593Smuzhiyun if (ratio == spdif_sysclk_ratios[i])
223*4882a593Smuzhiyun break;
224*4882a593Smuzhiyun if (i == ARRAY_SIZE(spdif_sysclk_ratios)) {
225*4882a593Smuzhiyun dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n",
226*4882a593Smuzhiyun spdif->clk_rate, params_rate(params));
227*4882a593Smuzhiyun goto err;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun con &= ~CON_MCLKDIV_MASK;
231*4882a593Smuzhiyun switch (ratio) {
232*4882a593Smuzhiyun case 256:
233*4882a593Smuzhiyun con |= CON_MCLKDIV_256FS;
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun case 384:
236*4882a593Smuzhiyun con |= CON_MCLKDIV_384FS;
237*4882a593Smuzhiyun break;
238*4882a593Smuzhiyun case 512:
239*4882a593Smuzhiyun con |= CON_MCLKDIV_512FS;
240*4882a593Smuzhiyun break;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun cstas &= ~CSTAS_SAMP_FREQ_MASK;
244*4882a593Smuzhiyun switch (params_rate(params)) {
245*4882a593Smuzhiyun case 44100:
246*4882a593Smuzhiyun cstas |= CSTAS_SAMP_FREQ_44;
247*4882a593Smuzhiyun break;
248*4882a593Smuzhiyun case 48000:
249*4882a593Smuzhiyun cstas |= CSTAS_SAMP_FREQ_48;
250*4882a593Smuzhiyun break;
251*4882a593Smuzhiyun case 32000:
252*4882a593Smuzhiyun cstas |= CSTAS_SAMP_FREQ_32;
253*4882a593Smuzhiyun break;
254*4882a593Smuzhiyun case 96000:
255*4882a593Smuzhiyun cstas |= CSTAS_SAMP_FREQ_96;
256*4882a593Smuzhiyun break;
257*4882a593Smuzhiyun default:
258*4882a593Smuzhiyun dev_err(spdif->dev, "Invalid sampling rate %d\n",
259*4882a593Smuzhiyun params_rate(params));
260*4882a593Smuzhiyun goto err;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun cstas &= ~CSTAS_CATEGORY_MASK;
264*4882a593Smuzhiyun cstas |= CSTAS_CATEGORY_CODE_CDP;
265*4882a593Smuzhiyun cstas |= CSTAS_NO_COPYRIGHT;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun writel(con, regs + CON);
268*4882a593Smuzhiyun writel(cstas, regs + CSTAS);
269*4882a593Smuzhiyun writel(clkcon, regs + CLKCON);
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun spin_unlock_irqrestore(&spdif->lock, flags);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun return 0;
274*4882a593Smuzhiyun err:
275*4882a593Smuzhiyun spin_unlock_irqrestore(&spdif->lock, flags);
276*4882a593Smuzhiyun return -EINVAL;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
spdif_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)279*4882a593Smuzhiyun static void spdif_shutdown(struct snd_pcm_substream *substream,
280*4882a593Smuzhiyun struct snd_soc_dai *dai)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
283*4882a593Smuzhiyun struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0));
284*4882a593Smuzhiyun void __iomem *regs = spdif->regs;
285*4882a593Smuzhiyun u32 con, clkcon;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun con = readl(regs + CON) & CON_MASK;
290*4882a593Smuzhiyun clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun writel(con | CON_SW_RESET, regs + CON);
293*4882a593Smuzhiyun cpu_relax();
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun #ifdef CONFIG_PM
spdif_suspend(struct snd_soc_component * component)299*4882a593Smuzhiyun static int spdif_suspend(struct snd_soc_component *component)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun struct samsung_spdif_info *spdif = component_to_info(component);
302*4882a593Smuzhiyun u32 con = spdif->saved_con;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK;
307*4882a593Smuzhiyun spdif->saved_con = readl(spdif->regs + CON) & CON_MASK;
308*4882a593Smuzhiyun spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun writel(con | CON_SW_RESET, spdif->regs + CON);
311*4882a593Smuzhiyun cpu_relax();
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun return 0;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun
spdif_resume(struct snd_soc_component * component)316*4882a593Smuzhiyun static int spdif_resume(struct snd_soc_component *component)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun struct samsung_spdif_info *spdif = component_to_info(component);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun dev_dbg(spdif->dev, "Entered %s\n", __func__);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun writel(spdif->saved_clkcon, spdif->regs + CLKCON);
323*4882a593Smuzhiyun writel(spdif->saved_con, spdif->regs + CON);
324*4882a593Smuzhiyun writel(spdif->saved_cstas, spdif->regs + CSTAS);
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun return 0;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun #else
329*4882a593Smuzhiyun #define spdif_suspend NULL
330*4882a593Smuzhiyun #define spdif_resume NULL
331*4882a593Smuzhiyun #endif
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun static const struct snd_soc_dai_ops spdif_dai_ops = {
334*4882a593Smuzhiyun .set_sysclk = spdif_set_sysclk,
335*4882a593Smuzhiyun .trigger = spdif_trigger,
336*4882a593Smuzhiyun .hw_params = spdif_hw_params,
337*4882a593Smuzhiyun .shutdown = spdif_shutdown,
338*4882a593Smuzhiyun };
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun static struct snd_soc_dai_driver samsung_spdif_dai = {
341*4882a593Smuzhiyun .name = "samsung-spdif",
342*4882a593Smuzhiyun .playback = {
343*4882a593Smuzhiyun .stream_name = "S/PDIF Playback",
344*4882a593Smuzhiyun .channels_min = 2,
345*4882a593Smuzhiyun .channels_max = 2,
346*4882a593Smuzhiyun .rates = (SNDRV_PCM_RATE_32000 |
347*4882a593Smuzhiyun SNDRV_PCM_RATE_44100 |
348*4882a593Smuzhiyun SNDRV_PCM_RATE_48000 |
349*4882a593Smuzhiyun SNDRV_PCM_RATE_96000),
350*4882a593Smuzhiyun .formats = SNDRV_PCM_FMTBIT_S16_LE, },
351*4882a593Smuzhiyun .ops = &spdif_dai_ops,
352*4882a593Smuzhiyun };
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun static const struct snd_soc_component_driver samsung_spdif_component = {
355*4882a593Smuzhiyun .name = "samsung-spdif",
356*4882a593Smuzhiyun .suspend = spdif_suspend,
357*4882a593Smuzhiyun .resume = spdif_resume,
358*4882a593Smuzhiyun };
359*4882a593Smuzhiyun
spdif_probe(struct platform_device * pdev)360*4882a593Smuzhiyun static int spdif_probe(struct platform_device *pdev)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun struct s3c_audio_pdata *spdif_pdata;
363*4882a593Smuzhiyun struct resource *mem_res;
364*4882a593Smuzhiyun struct samsung_spdif_info *spdif;
365*4882a593Smuzhiyun dma_filter_fn filter;
366*4882a593Smuzhiyun int ret;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun spdif_pdata = pdev->dev.platform_data;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun dev_dbg(&pdev->dev, "Entered %s\n", __func__);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
373*4882a593Smuzhiyun if (!mem_res) {
374*4882a593Smuzhiyun dev_err(&pdev->dev, "Unable to get register resource.\n");
375*4882a593Smuzhiyun return -ENXIO;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun if (spdif_pdata && spdif_pdata->cfg_gpio
379*4882a593Smuzhiyun && spdif_pdata->cfg_gpio(pdev)) {
380*4882a593Smuzhiyun dev_err(&pdev->dev, "Unable to configure GPIO pins\n");
381*4882a593Smuzhiyun return -EINVAL;
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun spdif = &spdif_info;
385*4882a593Smuzhiyun spdif->dev = &pdev->dev;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun spin_lock_init(&spdif->lock);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun spdif->pclk = devm_clk_get(&pdev->dev, "spdif");
390*4882a593Smuzhiyun if (IS_ERR(spdif->pclk)) {
391*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to get peri-clock\n");
392*4882a593Smuzhiyun ret = -ENOENT;
393*4882a593Smuzhiyun goto err0;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun ret = clk_prepare_enable(spdif->pclk);
396*4882a593Smuzhiyun if (ret)
397*4882a593Smuzhiyun goto err0;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif");
400*4882a593Smuzhiyun if (IS_ERR(spdif->sclk)) {
401*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to get internal source clock\n");
402*4882a593Smuzhiyun ret = -ENOENT;
403*4882a593Smuzhiyun goto err1;
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun ret = clk_prepare_enable(spdif->sclk);
406*4882a593Smuzhiyun if (ret)
407*4882a593Smuzhiyun goto err1;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun /* Request S/PDIF Register's memory region */
410*4882a593Smuzhiyun if (!request_mem_region(mem_res->start,
411*4882a593Smuzhiyun resource_size(mem_res), "samsung-spdif")) {
412*4882a593Smuzhiyun dev_err(&pdev->dev, "Unable to request register region\n");
413*4882a593Smuzhiyun ret = -EBUSY;
414*4882a593Smuzhiyun goto err2;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun spdif->regs = ioremap(mem_res->start, 0x100);
418*4882a593Smuzhiyun if (spdif->regs == NULL) {
419*4882a593Smuzhiyun dev_err(&pdev->dev, "Cannot ioremap registers\n");
420*4882a593Smuzhiyun ret = -ENXIO;
421*4882a593Smuzhiyun goto err3;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun spdif_stereo_out.addr_width = 2;
425*4882a593Smuzhiyun spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF;
426*4882a593Smuzhiyun filter = NULL;
427*4882a593Smuzhiyun if (spdif_pdata) {
428*4882a593Smuzhiyun spdif_stereo_out.filter_data = spdif_pdata->dma_playback;
429*4882a593Smuzhiyun filter = spdif_pdata->dma_filter;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun spdif->dma_playback = &spdif_stereo_out;
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
434*4882a593Smuzhiyun NULL, NULL, NULL);
435*4882a593Smuzhiyun if (ret) {
436*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
437*4882a593Smuzhiyun goto err4;
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun dev_set_drvdata(&pdev->dev, spdif);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun ret = devm_snd_soc_register_component(&pdev->dev,
443*4882a593Smuzhiyun &samsung_spdif_component, &samsung_spdif_dai, 1);
444*4882a593Smuzhiyun if (ret != 0) {
445*4882a593Smuzhiyun dev_err(&pdev->dev, "fail to register dai\n");
446*4882a593Smuzhiyun goto err4;
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun return 0;
450*4882a593Smuzhiyun err4:
451*4882a593Smuzhiyun iounmap(spdif->regs);
452*4882a593Smuzhiyun err3:
453*4882a593Smuzhiyun release_mem_region(mem_res->start, resource_size(mem_res));
454*4882a593Smuzhiyun err2:
455*4882a593Smuzhiyun clk_disable_unprepare(spdif->sclk);
456*4882a593Smuzhiyun err1:
457*4882a593Smuzhiyun clk_disable_unprepare(spdif->pclk);
458*4882a593Smuzhiyun err0:
459*4882a593Smuzhiyun return ret;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun
spdif_remove(struct platform_device * pdev)462*4882a593Smuzhiyun static int spdif_remove(struct platform_device *pdev)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun struct samsung_spdif_info *spdif = &spdif_info;
465*4882a593Smuzhiyun struct resource *mem_res;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun iounmap(spdif->regs);
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
470*4882a593Smuzhiyun if (mem_res)
471*4882a593Smuzhiyun release_mem_region(mem_res->start, resource_size(mem_res));
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun clk_disable_unprepare(spdif->sclk);
474*4882a593Smuzhiyun clk_disable_unprepare(spdif->pclk);
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun return 0;
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun static struct platform_driver samsung_spdif_driver = {
480*4882a593Smuzhiyun .probe = spdif_probe,
481*4882a593Smuzhiyun .remove = spdif_remove,
482*4882a593Smuzhiyun .driver = {
483*4882a593Smuzhiyun .name = "samsung-spdif",
484*4882a593Smuzhiyun },
485*4882a593Smuzhiyun };
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun module_platform_driver(samsung_spdif_driver);
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
490*4882a593Smuzhiyun MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
491*4882a593Smuzhiyun MODULE_LICENSE("GPL");
492*4882a593Smuzhiyun MODULE_ALIAS("platform:samsung-spdif");
493