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/dmaengine_pcm.h>
22 #include <sound/pcm.h>
23 #include <sound/pcm_params.h>
24 #include <sound/soc.h>
25 #include <sound/spear_dma.h>
26 #include <sound/spear_spdif.h>
27 #include "spdif_in_regs.h"
28 #include "spear_pcm.h"
30 struct spdif_in_params
{
36 struct spear_dma_data dma_params
;
37 struct spdif_in_params saved_params
;
40 void (*reset_perip
)(void);
42 struct snd_dmaengine_dai_dma_data dma_params_rx
;
43 struct snd_dmaengine_pcm_config config
;
46 static void spdif_in_configure(struct spdif_in_dev
*host
)
48 u32 ctrl
= SPDIF_IN_PRTYEN
| SPDIF_IN_STATEN
| SPDIF_IN_USREN
|
49 SPDIF_IN_VALEN
| SPDIF_IN_BLKEN
;
50 ctrl
|= SPDIF_MODE_16BIT
| SPDIF_FIFO_THRES_16
;
52 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
53 writel(0xF, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
56 static int spdif_in_dai_probe(struct snd_soc_dai
*dai
)
58 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
60 host
->dma_params_rx
.filter_data
= &host
->dma_params
;
61 dai
->capture_dma_data
= &host
->dma_params_rx
;
66 static void spdif_in_shutdown(struct snd_pcm_substream
*substream
,
67 struct snd_soc_dai
*dai
)
69 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
71 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
74 writel(0x0, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
77 static void spdif_in_format(struct spdif_in_dev
*host
, u32 format
)
79 u32 ctrl
= readl(host
->io_base
+ SPDIF_IN_CTRL
);
82 case SNDRV_PCM_FORMAT_S16_LE
:
83 ctrl
|= SPDIF_XTRACT_16BIT
;
86 case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
:
87 ctrl
&= ~SPDIF_XTRACT_16BIT
;
91 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
94 static int spdif_in_hw_params(struct snd_pcm_substream
*substream
,
95 struct snd_pcm_hw_params
*params
,
96 struct snd_soc_dai
*dai
)
98 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
101 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
104 format
= params_format(params
);
105 host
->saved_params
.format
= format
;
110 static int spdif_in_trigger(struct snd_pcm_substream
*substream
, int cmd
,
111 struct snd_soc_dai
*dai
)
113 struct spdif_in_dev
*host
= snd_soc_dai_get_drvdata(dai
);
117 if (substream
->stream
!= SNDRV_PCM_STREAM_CAPTURE
)
121 case SNDRV_PCM_TRIGGER_START
:
122 case SNDRV_PCM_TRIGGER_RESUME
:
123 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
124 clk_enable(host
->clk
);
125 spdif_in_configure(host
);
126 spdif_in_format(host
, host
->saved_params
.format
);
128 ctrl
= readl(host
->io_base
+ SPDIF_IN_CTRL
);
129 ctrl
|= SPDIF_IN_SAMPLE
| SPDIF_IN_ENB
;
130 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
131 writel(0xF, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
134 case SNDRV_PCM_TRIGGER_STOP
:
135 case SNDRV_PCM_TRIGGER_SUSPEND
:
136 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
137 ctrl
= readl(host
->io_base
+ SPDIF_IN_CTRL
);
138 ctrl
&= ~(SPDIF_IN_SAMPLE
| SPDIF_IN_ENB
);
139 writel(ctrl
, host
->io_base
+ SPDIF_IN_CTRL
);
140 writel(0x0, host
->io_base
+ SPDIF_IN_IRQ_MASK
);
142 if (host
->reset_perip
)
144 clk_disable(host
->clk
);
154 static const struct snd_soc_dai_ops spdif_in_dai_ops
= {
155 .shutdown
= spdif_in_shutdown
,
156 .trigger
= spdif_in_trigger
,
157 .hw_params
= spdif_in_hw_params
,
160 static struct snd_soc_dai_driver spdif_in_dai
= {
161 .probe
= spdif_in_dai_probe
,
165 .rates
= (SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
| \
166 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_96000
| \
167 SNDRV_PCM_RATE_192000
),
168 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| \
169 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
,
171 .ops
= &spdif_in_dai_ops
,
174 static const struct snd_soc_component_driver spdif_in_component
= {
176 .legacy_dai_naming
= 1,
179 static irqreturn_t
spdif_in_irq(int irq
, void *arg
)
181 struct spdif_in_dev
*host
= (struct spdif_in_dev
*)arg
;
183 u32 irq_status
= readl(host
->io_base
+ SPDIF_IN_IRQ
);
188 if (irq_status
& SPDIF_IRQ_FIFOWRITE
)
189 dev_err(host
->dev
, "spdif in: fifo write error");
190 if (irq_status
& SPDIF_IRQ_EMPTYFIFOREAD
)
191 dev_err(host
->dev
, "spdif in: empty fifo read error");
192 if (irq_status
& SPDIF_IRQ_FIFOFULL
)
193 dev_err(host
->dev
, "spdif in: fifo full error");
194 if (irq_status
& SPDIF_IRQ_OUTOFRANGE
)
195 dev_err(host
->dev
, "spdif in: out of range error");
197 writel(0, host
->io_base
+ SPDIF_IN_IRQ
);
202 static int spdif_in_probe(struct platform_device
*pdev
)
204 struct spdif_in_dev
*host
;
205 struct spear_spdif_platform_data
*pdata
;
206 struct resource
*res_fifo
;
207 void __iomem
*io_base
;
210 io_base
= devm_platform_ioremap_resource(pdev
, 0);
212 return PTR_ERR(io_base
);
214 res_fifo
= platform_get_resource(pdev
, IORESOURCE_IO
, 0);
218 host
= devm_kzalloc(&pdev
->dev
, sizeof(*host
), GFP_KERNEL
);
222 host
->io_base
= io_base
;
223 host
->irq
= platform_get_irq(pdev
, 0);
225 dev_warn(&pdev
->dev
, "failed to get IRQ: %d\n", host
->irq
);
229 host
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
230 if (IS_ERR(host
->clk
))
231 return PTR_ERR(host
->clk
);
233 pdata
= dev_get_platdata(&pdev
->dev
);
238 host
->dma_params
.data
= pdata
->dma_params
;
239 host
->dma_params
.addr
= res_fifo
->start
;
240 host
->dma_params
.max_burst
= 16;
241 host
->dma_params
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
242 host
->reset_perip
= pdata
->reset_perip
;
244 host
->dev
= &pdev
->dev
;
245 dev_set_drvdata(&pdev
->dev
, host
);
247 ret
= devm_request_irq(&pdev
->dev
, host
->irq
, spdif_in_irq
, 0,
250 dev_warn(&pdev
->dev
, "request_irq failed\n");
254 ret
= devm_snd_soc_register_component(&pdev
->dev
, &spdif_in_component
,
259 return devm_spear_pcm_platform_register(&pdev
->dev
, &host
->config
,
263 static struct platform_driver spdif_in_driver
= {
264 .probe
= spdif_in_probe
,
270 module_platform_driver(spdif_in_driver
);
272 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
273 MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface");
274 MODULE_LICENSE("GPL");
275 MODULE_ALIAS("platform:spdif_in");