2 * ALSA SoC SPDIF In Audio Layer for spear processors
4 * Copyright (C) 2012 ST Microelectronics
5 * Vipin Kumar <vipin.kumar@st.com>
7 * This file is licensed under the terms of the GNU General Public
8 * License version 2. This program is licensed "as is" without any
9 * warranty of any kind, whether express or implied.
12 #include <linux/clk.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
18 #include <linux/ioport.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <sound/pcm.h>
22 #include <sound/pcm_params.h>
23 #include <sound/soc.h>
24 #include <sound/spear_dma.h>
25 #include <sound/spear_spdif.h>
26 #include "spdif_in_regs.h"
28 struct spdif_in_params
{
34 struct spear_dma_data dma_params
;
35 struct spdif_in_params saved_params
;
38 void (*reset_perip
)(void);
42 static void spdif_in_configure(struct spdif_in_dev
*host
)
44 u32 ctrl
= SPDIF_IN_PRTYEN
| SPDIF_IN_STATEN
| SPDIF_IN_USREN
|
45 SPDIF_IN_VALEN
| SPDIF_IN_BLKEN
;
46 ctrl
|= SPDIF_MODE_16BIT
| SPDIF_FIFO_THRES_16
;
48 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
49 writel(0xF, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
52 static int spdif_in_startup(struct snd_pcm_substream
*substream
,
53 struct snd_soc_dai
*cpu_dai
)
55 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
57 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
60 snd_soc_dai_set_dma_data(cpu_dai
, substream
, (void *)&host
->dma_params
);
64 static void spdif_in_shutdown(struct snd_pcm_substream
*substream
,
65 struct snd_soc_dai
*dai
)
67 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
69 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
72 writel(0x0, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
73 snd_soc_dai_set_dma_data(dai
, substream
, NULL
);
76 static void spdif_in_format(struct spdif_in_dev
*host
, u32 format
)
78 u32 ctrl
= readl(host
->io_base
+ SPDIF_IN_CTRL
);
81 case SNDRV_PCM_FORMAT_S16_LE
:
82 ctrl
|= SPDIF_XTRACT_16BIT
;
85 case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
:
86 ctrl
&= ~SPDIF_XTRACT_16BIT
;
90 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
93 static int spdif_in_hw_params(struct snd_pcm_substream
*substream
,
94 struct snd_pcm_hw_params
*params
,
95 struct snd_soc_dai
*dai
)
97 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
100 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
103 format
= params_format(params
);
104 host
->saved_params
.format
= format
;
109 static int spdif_in_trigger(struct snd_pcm_substream
*substream
, int cmd
,
110 struct snd_soc_dai
*dai
)
112 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
116 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
120 case SNDRV_PCM_TRIGGER_START
:
121 case SNDRV_PCM_TRIGGER_RESUME
:
122 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
123 clk_enable(host
->clk
);
124 spdif_in_configure(host
);
125 spdif_in_format(host
, host
->saved_params
.format
);
127 ctrl
= readl(host
->io_base
+ SPDIF_IN_CTRL
);
128 ctrl
|= SPDIF_IN_SAMPLE
| SPDIF_IN_ENB
;
129 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
130 writel(0xF, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
133 case SNDRV_PCM_TRIGGER_STOP
:
134 case SNDRV_PCM_TRIGGER_SUSPEND
:
135 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
136 ctrl
= readl(host
->io_base
+ SPDIF_IN_CTRL
);
137 ctrl
&= ~(SPDIF_IN_SAMPLE
| SPDIF_IN_ENB
);
138 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
139 writel(0x0, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
141 if (host
->reset_perip
)
143 clk_disable(host
->clk
);
153 static struct snd_soc_dai_ops spdif_in_dai_ops
= {
154 .startup
= spdif_in_startup
,
155 .shutdown
= spdif_in_shutdown
,
156 .trigger
= spdif_in_trigger
,
157 .hw_params
= spdif_in_hw_params
,
160 struct snd_soc_dai_driver spdif_in_dai
= {
164 .rates
= (SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
| \
165 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_96000
| \
166 SNDRV_PCM_RATE_192000
),
167 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| \
168 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
,
170 .ops
= &spdif_in_dai_ops
,
173 static irqreturn_t
spdif_in_irq(int irq
, void *arg
)
175 struct spdif_in_dev
*host
= (struct spdif_in_dev
*)arg
;
177 u32 irq_status
= readl(host
->io_base
+ SPDIF_IN_IRQ
);
182 if (irq_status
& SPDIF_IRQ_FIFOWRITE
)
183 dev_err(host
->dev
, "spdif in: fifo write error");
184 if (irq_status
& SPDIF_IRQ_EMPTYFIFOREAD
)
185 dev_err(host
->dev
, "spdif in: empty fifo read error");
186 if (irq_status
& SPDIF_IRQ_FIFOFULL
)
187 dev_err(host
->dev
, "spdif in: fifo full error");
188 if (irq_status
& SPDIF_IRQ_OUTOFRANGE
)
189 dev_err(host
->dev
, "spdif in: out of range error");
191 writel(0, host
->io_base
+ SPDIF_IN_IRQ
);
196 static int spdif_in_probe(struct platform_device
*pdev
)
198 struct spdif_in_dev
*host
;
199 struct spear_spdif_platform_data
*pdata
;
200 struct resource
*res
, *res_fifo
;
203 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
207 res_fifo
= platform_get_resource(pdev
, IORESOURCE_IO
, 0);
211 if (!devm_request_mem_region(&pdev
->dev
, res
->start
,
212 resource_size(res
), pdev
->name
)) {
213 dev_warn(&pdev
->dev
, "Failed to get memory resourse\n");
217 host
= devm_kzalloc(&pdev
->dev
, sizeof(*host
), GFP_KERNEL
);
219 dev_warn(&pdev
->dev
, "kzalloc fail\n");
223 host
->io_base
= devm_ioremap(&pdev
->dev
, res
->start
,
225 if (!host
->io_base
) {
226 dev_warn(&pdev
->dev
, "ioremap failed\n");
230 host
->irq
= platform_get_irq(pdev
, 0);
234 host
->clk
= clk_get(&pdev
->dev
, NULL
);
235 if (IS_ERR(host
->clk
))
236 return PTR_ERR(host
->clk
);
238 pdata
= dev_get_platdata(&pdev
->dev
);
243 host
->dma_params
.data
= pdata
->dma_params
;
244 host
->dma_params
.addr
= res_fifo
->start
;
245 host
->dma_params
.max_burst
= 16;
246 host
->dma_params
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
247 host
->dma_params
.filter
= pdata
->filter
;
248 host
->reset_perip
= pdata
->reset_perip
;
250 host
->dev
= &pdev
->dev
;
251 dev_set_drvdata(&pdev
->dev
, host
);
253 ret
= devm_request_irq(&pdev
->dev
, host
->irq
, spdif_in_irq
, 0,
257 dev_warn(&pdev
->dev
, "request_irq failed\n");
261 ret
= snd_soc_register_dai(&pdev
->dev
, &spdif_in_dai
);
270 static int spdif_in_remove(struct platform_device
*pdev
)
272 struct spdif_in_dev
*host
= dev_get_drvdata(&pdev
->dev
);
274 snd_soc_unregister_dai(&pdev
->dev
);
275 dev_set_drvdata(&pdev
->dev
, NULL
);
283 static struct platform_driver spdif_in_driver
= {
284 .probe
= spdif_in_probe
,
285 .remove
= spdif_in_remove
,
288 .owner
= THIS_MODULE
,
292 module_platform_driver(spdif_in_driver
);
294 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
295 MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface");
296 MODULE_LICENSE("GPL");
297 MODULE_ALIAS("platform:spdif_in");