175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c4458b74SDamien.Horsley /*
3c4458b74SDamien.Horsley * IMG SPDIF input controller driver
4c4458b74SDamien.Horsley *
5c4458b74SDamien.Horsley * Copyright (C) 2015 Imagination Technologies Ltd.
6c4458b74SDamien.Horsley *
7c4458b74SDamien.Horsley * Author: Damien Horsley <Damien.Horsley@imgtec.com>
8c4458b74SDamien.Horsley */
9c4458b74SDamien.Horsley
10c4458b74SDamien.Horsley #include <linux/clk.h>
11c4458b74SDamien.Horsley #include <linux/init.h>
12c4458b74SDamien.Horsley #include <linux/kernel.h>
13c4458b74SDamien.Horsley #include <linux/module.h>
14c4458b74SDamien.Horsley #include <linux/of.h>
15c4458b74SDamien.Horsley #include <linux/platform_device.h>
161fac824fSEd Blake #include <linux/pm_runtime.h>
17c4458b74SDamien.Horsley #include <linux/reset.h>
18c4458b74SDamien.Horsley
19c4458b74SDamien.Horsley #include <sound/core.h>
20c4458b74SDamien.Horsley #include <sound/dmaengine_pcm.h>
21c4458b74SDamien.Horsley #include <sound/initval.h>
22c4458b74SDamien.Horsley #include <sound/pcm.h>
23c4458b74SDamien.Horsley #include <sound/pcm_params.h>
24c4458b74SDamien.Horsley #include <sound/soc.h>
25c4458b74SDamien.Horsley
26c4458b74SDamien.Horsley #define IMG_SPDIF_IN_RX_FIFO_OFFSET 0
27c4458b74SDamien.Horsley
28c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL 0x4
29c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff
30c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0
31c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00
32c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8
33c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000
34c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_TRK_SHIFT 16
35c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000
36c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_SRD_SHIFT 28
37c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31)
38c4458b74SDamien.Horsley
39c4458b74SDamien.Horsley #define IMG_SPDIF_IN_STATUS 0x8
40c4458b74SDamien.Horsley #define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000
41c4458b74SDamien.Horsley #define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12
42c4458b74SDamien.Horsley #define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15)
43c4458b74SDamien.Horsley #define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15
44c4458b74SDamien.Horsley
45c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CLKGEN 0x1c
46c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff
47c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0
48c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000
49c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16
50c4458b74SDamien.Horsley
51c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CSL 0x20
52c4458b74SDamien.Horsley
53c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CSH 0x24
54c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CSH_MASK 0xff
55c4458b74SDamien.Horsley #define IMG_SPDIF_IN_CSH_SHIFT 0
56c4458b74SDamien.Horsley
57c4458b74SDamien.Horsley #define IMG_SPDIF_IN_SOFT_RESET 0x28
58c4458b74SDamien.Horsley #define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0)
59c4458b74SDamien.Horsley
60c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_START 0x2c
61c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff
62c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0
63c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00
64c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10
65c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000
66c4458b74SDamien.Horsley #define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20
67c4458b74SDamien.Horsley
68c4458b74SDamien.Horsley #define IMG_SPDIF_IN_NUM_ACLKGEN 4
69c4458b74SDamien.Horsley
70c4458b74SDamien.Horsley struct img_spdif_in {
71c4458b74SDamien.Horsley spinlock_t lock;
72c4458b74SDamien.Horsley void __iomem *base;
73c4458b74SDamien.Horsley struct clk *clk_sys;
74c4458b74SDamien.Horsley struct snd_dmaengine_dai_dma_data dma_data;
75c4458b74SDamien.Horsley struct device *dev;
76c4458b74SDamien.Horsley unsigned int trk;
77c4458b74SDamien.Horsley bool multi_freq;
78c4458b74SDamien.Horsley int lock_acquire;
79c4458b74SDamien.Horsley int lock_release;
80c4458b74SDamien.Horsley unsigned int single_freq;
81c4458b74SDamien.Horsley unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
82c4458b74SDamien.Horsley bool active;
8352eb0ed3SEd Blake u32 suspend_clkgen;
8452eb0ed3SEd Blake u32 suspend_ctl;
85c4458b74SDamien.Horsley
86c4458b74SDamien.Horsley /* Write-only registers */
87c4458b74SDamien.Horsley unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
88c4458b74SDamien.Horsley };
89c4458b74SDamien.Horsley
img_spdif_in_runtime_suspend(struct device * dev)901fac824fSEd Blake static int img_spdif_in_runtime_suspend(struct device *dev)
911fac824fSEd Blake {
921fac824fSEd Blake struct img_spdif_in *spdif = dev_get_drvdata(dev);
931fac824fSEd Blake
941fac824fSEd Blake clk_disable_unprepare(spdif->clk_sys);
951fac824fSEd Blake
961fac824fSEd Blake return 0;
971fac824fSEd Blake }
981fac824fSEd Blake
img_spdif_in_runtime_resume(struct device * dev)991fac824fSEd Blake static int img_spdif_in_runtime_resume(struct device *dev)
1001fac824fSEd Blake {
1011fac824fSEd Blake struct img_spdif_in *spdif = dev_get_drvdata(dev);
1021fac824fSEd Blake int ret;
1031fac824fSEd Blake
1041fac824fSEd Blake ret = clk_prepare_enable(spdif->clk_sys);
1051fac824fSEd Blake if (ret) {
1061fac824fSEd Blake dev_err(dev, "Unable to enable sys clock\n");
1071fac824fSEd Blake return ret;
1081fac824fSEd Blake }
1091fac824fSEd Blake
1101fac824fSEd Blake return 0;
1111fac824fSEd Blake }
1121fac824fSEd Blake
img_spdif_in_writel(struct img_spdif_in * spdif,u32 val,u32 reg)113c4458b74SDamien.Horsley static inline void img_spdif_in_writel(struct img_spdif_in *spdif,
114c4458b74SDamien.Horsley u32 val, u32 reg)
115c4458b74SDamien.Horsley {
116c4458b74SDamien.Horsley writel(val, spdif->base + reg);
117c4458b74SDamien.Horsley }
118c4458b74SDamien.Horsley
img_spdif_in_readl(struct img_spdif_in * spdif,u32 reg)119c4458b74SDamien.Horsley static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg)
120c4458b74SDamien.Horsley {
121c4458b74SDamien.Horsley return readl(spdif->base + reg);
122c4458b74SDamien.Horsley }
123c4458b74SDamien.Horsley
img_spdif_in_aclkgen_writel(struct img_spdif_in * spdif,u32 index)124c4458b74SDamien.Horsley static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif,
125c4458b74SDamien.Horsley u32 index)
126c4458b74SDamien.Horsley {
127c4458b74SDamien.Horsley img_spdif_in_writel(spdif, spdif->aclkgen_regs[index],
128c4458b74SDamien.Horsley IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4));
129c4458b74SDamien.Horsley }
130c4458b74SDamien.Horsley
img_spdif_in_check_max_rate(struct img_spdif_in * spdif,unsigned int sample_rate,unsigned long * actual_freq)131c4458b74SDamien.Horsley static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif,
132c4458b74SDamien.Horsley unsigned int sample_rate, unsigned long *actual_freq)
133c4458b74SDamien.Horsley {
134c4458b74SDamien.Horsley unsigned long min_freq, freq_t;
135c4458b74SDamien.Horsley
136c4458b74SDamien.Horsley /* Clock rate must be at least 24x the bit rate */
137c4458b74SDamien.Horsley min_freq = sample_rate * 2 * 32 * 24;
138c4458b74SDamien.Horsley
139c4458b74SDamien.Horsley freq_t = clk_get_rate(spdif->clk_sys);
140c4458b74SDamien.Horsley
141c4458b74SDamien.Horsley if (freq_t < min_freq)
142c4458b74SDamien.Horsley return -EINVAL;
143c4458b74SDamien.Horsley
144c4458b74SDamien.Horsley *actual_freq = freq_t;
145c4458b74SDamien.Horsley
146c4458b74SDamien.Horsley return 0;
147c4458b74SDamien.Horsley }
148c4458b74SDamien.Horsley
img_spdif_in_do_clkgen_calc(unsigned int rate,unsigned int * pnom,unsigned int * phld,unsigned long clk_rate)149c4458b74SDamien.Horsley static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom,
150c4458b74SDamien.Horsley unsigned int *phld, unsigned long clk_rate)
151c4458b74SDamien.Horsley {
152c4458b74SDamien.Horsley unsigned int ori, nom, hld;
153c4458b74SDamien.Horsley
154c4458b74SDamien.Horsley /*
155c4458b74SDamien.Horsley * Calculate oversampling ratio, nominal phase increment and hold
156c4458b74SDamien.Horsley * increment for the given rate / frequency
157c4458b74SDamien.Horsley */
158c4458b74SDamien.Horsley
159c4458b74SDamien.Horsley if (!rate)
160c4458b74SDamien.Horsley return -EINVAL;
161c4458b74SDamien.Horsley
162c4458b74SDamien.Horsley ori = clk_rate / (rate * 64);
163c4458b74SDamien.Horsley
164c4458b74SDamien.Horsley if (!ori)
165c4458b74SDamien.Horsley return -EINVAL;
166c4458b74SDamien.Horsley
167c4458b74SDamien.Horsley nom = (4096 / ori) + 1;
168c4458b74SDamien.Horsley do
169c4458b74SDamien.Horsley hld = 4096 - (--nom * (ori - 1));
170c4458b74SDamien.Horsley while (hld < 120);
171c4458b74SDamien.Horsley
172c4458b74SDamien.Horsley *pnom = nom;
173c4458b74SDamien.Horsley *phld = hld;
174c4458b74SDamien.Horsley
175c4458b74SDamien.Horsley return 0;
176c4458b74SDamien.Horsley }
177c4458b74SDamien.Horsley
img_spdif_in_do_clkgen_single(struct img_spdif_in * spdif,unsigned int rate)178c4458b74SDamien.Horsley static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif,
179c4458b74SDamien.Horsley unsigned int rate)
180c4458b74SDamien.Horsley {
181c4458b74SDamien.Horsley unsigned int nom, hld;
182c4458b74SDamien.Horsley unsigned long flags, clk_rate;
183c4458b74SDamien.Horsley int ret = 0;
184c4458b74SDamien.Horsley u32 reg;
185c4458b74SDamien.Horsley
186c4458b74SDamien.Horsley ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate);
187c4458b74SDamien.Horsley if (ret)
188c4458b74SDamien.Horsley return ret;
189c4458b74SDamien.Horsley
190c4458b74SDamien.Horsley ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
191c4458b74SDamien.Horsley if (ret)
192c4458b74SDamien.Horsley return ret;
193c4458b74SDamien.Horsley
194c4458b74SDamien.Horsley reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) &
195c4458b74SDamien.Horsley IMG_SPDIF_IN_CLKGEN_NOM_MASK;
196c4458b74SDamien.Horsley reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) &
197c4458b74SDamien.Horsley IMG_SPDIF_IN_CLKGEN_HLD_MASK;
198c4458b74SDamien.Horsley
199c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
200c4458b74SDamien.Horsley
201c4458b74SDamien.Horsley if (spdif->active) {
202c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
203c4458b74SDamien.Horsley return -EBUSY;
204c4458b74SDamien.Horsley }
205c4458b74SDamien.Horsley
206c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN);
207c4458b74SDamien.Horsley
208c4458b74SDamien.Horsley spdif->single_freq = rate;
209c4458b74SDamien.Horsley
210c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
211c4458b74SDamien.Horsley
212c4458b74SDamien.Horsley return 0;
213c4458b74SDamien.Horsley }
214c4458b74SDamien.Horsley
img_spdif_in_do_clkgen_multi(struct img_spdif_in * spdif,unsigned int multi_freqs[])215c4458b74SDamien.Horsley static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif,
216c4458b74SDamien.Horsley unsigned int multi_freqs[])
217c4458b74SDamien.Horsley {
218c4458b74SDamien.Horsley unsigned int nom, hld, rate, max_rate = 0;
219c4458b74SDamien.Horsley unsigned long flags, clk_rate;
220c4458b74SDamien.Horsley int i, ret = 0;
221c4458b74SDamien.Horsley u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
222c4458b74SDamien.Horsley
223c4458b74SDamien.Horsley for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++)
224c4458b74SDamien.Horsley if (multi_freqs[i] > max_rate)
225c4458b74SDamien.Horsley max_rate = multi_freqs[i];
226c4458b74SDamien.Horsley
227c4458b74SDamien.Horsley ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate);
228c4458b74SDamien.Horsley if (ret)
229c4458b74SDamien.Horsley return ret;
230c4458b74SDamien.Horsley
231c4458b74SDamien.Horsley for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
232c4458b74SDamien.Horsley rate = multi_freqs[i];
233c4458b74SDamien.Horsley
234c4458b74SDamien.Horsley ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
235c4458b74SDamien.Horsley if (ret)
236c4458b74SDamien.Horsley return ret;
237c4458b74SDamien.Horsley
238c4458b74SDamien.Horsley reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) &
239c4458b74SDamien.Horsley IMG_SPDIF_IN_ACLKGEN_NOM_MASK;
240c4458b74SDamien.Horsley reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) &
241c4458b74SDamien.Horsley IMG_SPDIF_IN_ACLKGEN_HLD_MASK;
242c4458b74SDamien.Horsley temp_regs[i] = reg;
243c4458b74SDamien.Horsley }
244c4458b74SDamien.Horsley
245c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
246c4458b74SDamien.Horsley
247c4458b74SDamien.Horsley if (spdif->active) {
248c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
249c4458b74SDamien.Horsley return -EBUSY;
250c4458b74SDamien.Horsley }
251c4458b74SDamien.Horsley
252c4458b74SDamien.Horsley trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT;
253c4458b74SDamien.Horsley
254c4458b74SDamien.Horsley for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
255c4458b74SDamien.Horsley spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg;
256c4458b74SDamien.Horsley img_spdif_in_aclkgen_writel(spdif, i);
257c4458b74SDamien.Horsley }
258c4458b74SDamien.Horsley
259c4458b74SDamien.Horsley spdif->multi_freq = true;
260c4458b74SDamien.Horsley spdif->multi_freqs[0] = multi_freqs[0];
261c4458b74SDamien.Horsley spdif->multi_freqs[1] = multi_freqs[1];
262c4458b74SDamien.Horsley spdif->multi_freqs[2] = multi_freqs[2];
263c4458b74SDamien.Horsley spdif->multi_freqs[3] = multi_freqs[3];
264c4458b74SDamien.Horsley
265c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
266c4458b74SDamien.Horsley
267c4458b74SDamien.Horsley return 0;
268c4458b74SDamien.Horsley }
269c4458b74SDamien.Horsley
img_spdif_in_iec958_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)270c4458b74SDamien.Horsley static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol,
271c4458b74SDamien.Horsley struct snd_ctl_elem_info *uinfo)
272c4458b74SDamien.Horsley {
273c4458b74SDamien.Horsley uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
274c4458b74SDamien.Horsley uinfo->count = 1;
275c4458b74SDamien.Horsley
276c4458b74SDamien.Horsley return 0;
277c4458b74SDamien.Horsley }
278c4458b74SDamien.Horsley
img_spdif_in_get_status_mask(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)279c4458b74SDamien.Horsley static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol,
280c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
281c4458b74SDamien.Horsley {
282c4458b74SDamien.Horsley ucontrol->value.iec958.status[0] = 0xff;
283c4458b74SDamien.Horsley ucontrol->value.iec958.status[1] = 0xff;
284c4458b74SDamien.Horsley ucontrol->value.iec958.status[2] = 0xff;
285c4458b74SDamien.Horsley ucontrol->value.iec958.status[3] = 0xff;
286c4458b74SDamien.Horsley ucontrol->value.iec958.status[4] = 0xff;
287c4458b74SDamien.Horsley
288c4458b74SDamien.Horsley return 0;
289c4458b74SDamien.Horsley }
290c4458b74SDamien.Horsley
img_spdif_in_get_status(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)291c4458b74SDamien.Horsley static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol,
292c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
293c4458b74SDamien.Horsley {
294c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
295c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
296c4458b74SDamien.Horsley u32 reg;
297c4458b74SDamien.Horsley
298c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL);
299c4458b74SDamien.Horsley ucontrol->value.iec958.status[0] = reg & 0xff;
300c4458b74SDamien.Horsley ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
301c4458b74SDamien.Horsley ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
302c4458b74SDamien.Horsley ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
303c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH);
304c4458b74SDamien.Horsley ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK)
305c4458b74SDamien.Horsley >> IMG_SPDIF_IN_CSH_SHIFT;
306c4458b74SDamien.Horsley
307c4458b74SDamien.Horsley return 0;
308c4458b74SDamien.Horsley }
309c4458b74SDamien.Horsley
img_spdif_in_info_multi_freq(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)310c4458b74SDamien.Horsley static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol,
311c4458b74SDamien.Horsley struct snd_ctl_elem_info *uinfo)
312c4458b74SDamien.Horsley {
313c4458b74SDamien.Horsley uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
314c4458b74SDamien.Horsley uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN;
315c4458b74SDamien.Horsley uinfo->value.integer.min = 0;
316c4458b74SDamien.Horsley uinfo->value.integer.max = LONG_MAX;
317c4458b74SDamien.Horsley
318c4458b74SDamien.Horsley return 0;
319c4458b74SDamien.Horsley }
320c4458b74SDamien.Horsley
img_spdif_in_get_multi_freq(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)321c4458b74SDamien.Horsley static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol,
322c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
323c4458b74SDamien.Horsley {
324c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
325c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
326c4458b74SDamien.Horsley unsigned long flags;
327c4458b74SDamien.Horsley
328c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
329c4458b74SDamien.Horsley if (spdif->multi_freq) {
330c4458b74SDamien.Horsley ucontrol->value.integer.value[0] = spdif->multi_freqs[0];
331c4458b74SDamien.Horsley ucontrol->value.integer.value[1] = spdif->multi_freqs[1];
332c4458b74SDamien.Horsley ucontrol->value.integer.value[2] = spdif->multi_freqs[2];
333c4458b74SDamien.Horsley ucontrol->value.integer.value[3] = spdif->multi_freqs[3];
334c4458b74SDamien.Horsley } else {
335c4458b74SDamien.Horsley ucontrol->value.integer.value[0] = 0;
336c4458b74SDamien.Horsley ucontrol->value.integer.value[1] = 0;
337c4458b74SDamien.Horsley ucontrol->value.integer.value[2] = 0;
338c4458b74SDamien.Horsley ucontrol->value.integer.value[3] = 0;
339c4458b74SDamien.Horsley }
340c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
341c4458b74SDamien.Horsley
342c4458b74SDamien.Horsley return 0;
343c4458b74SDamien.Horsley }
344c4458b74SDamien.Horsley
img_spdif_in_set_multi_freq(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)345c4458b74SDamien.Horsley static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol,
346c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
347c4458b74SDamien.Horsley {
348c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
349c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
350c4458b74SDamien.Horsley unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
351c4458b74SDamien.Horsley bool multi_freq;
352c4458b74SDamien.Horsley unsigned long flags;
353c4458b74SDamien.Horsley
354c4458b74SDamien.Horsley if ((ucontrol->value.integer.value[0] == 0) &&
355c4458b74SDamien.Horsley (ucontrol->value.integer.value[1] == 0) &&
356c4458b74SDamien.Horsley (ucontrol->value.integer.value[2] == 0) &&
357c4458b74SDamien.Horsley (ucontrol->value.integer.value[3] == 0)) {
358c4458b74SDamien.Horsley multi_freq = false;
359c4458b74SDamien.Horsley } else {
360c4458b74SDamien.Horsley multi_freqs[0] = ucontrol->value.integer.value[0];
361c4458b74SDamien.Horsley multi_freqs[1] = ucontrol->value.integer.value[1];
362c4458b74SDamien.Horsley multi_freqs[2] = ucontrol->value.integer.value[2];
363c4458b74SDamien.Horsley multi_freqs[3] = ucontrol->value.integer.value[3];
364c4458b74SDamien.Horsley multi_freq = true;
365c4458b74SDamien.Horsley }
366c4458b74SDamien.Horsley
367c4458b74SDamien.Horsley if (multi_freq)
368c4458b74SDamien.Horsley return img_spdif_in_do_clkgen_multi(spdif, multi_freqs);
369c4458b74SDamien.Horsley
370c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
371c4458b74SDamien.Horsley
372c4458b74SDamien.Horsley if (spdif->active) {
373c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
374c4458b74SDamien.Horsley return -EBUSY;
375c4458b74SDamien.Horsley }
376c4458b74SDamien.Horsley
377c4458b74SDamien.Horsley spdif->multi_freq = false;
378c4458b74SDamien.Horsley
379c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
380c4458b74SDamien.Horsley
381c4458b74SDamien.Horsley return 0;
382c4458b74SDamien.Horsley }
383c4458b74SDamien.Horsley
img_spdif_in_info_lock_freq(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)384c4458b74SDamien.Horsley static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol,
385c4458b74SDamien.Horsley struct snd_ctl_elem_info *uinfo)
386c4458b74SDamien.Horsley {
387c4458b74SDamien.Horsley uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
388c4458b74SDamien.Horsley uinfo->count = 1;
389c4458b74SDamien.Horsley uinfo->value.integer.min = 0;
390c4458b74SDamien.Horsley uinfo->value.integer.max = LONG_MAX;
391c4458b74SDamien.Horsley
392c4458b74SDamien.Horsley return 0;
393c4458b74SDamien.Horsley }
394c4458b74SDamien.Horsley
img_spdif_in_get_lock_freq(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * uc)395c4458b74SDamien.Horsley static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol,
396c4458b74SDamien.Horsley struct snd_ctl_elem_value *uc)
397c4458b74SDamien.Horsley {
398c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
399c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
400c4458b74SDamien.Horsley u32 reg;
401c4458b74SDamien.Horsley int i;
402c4458b74SDamien.Horsley unsigned long flags;
403c4458b74SDamien.Horsley
404c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
405c4458b74SDamien.Horsley
406c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS);
407c4458b74SDamien.Horsley if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) {
408c4458b74SDamien.Horsley if (spdif->multi_freq) {
409c4458b74SDamien.Horsley i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >>
410c4458b74SDamien.Horsley IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1;
411c4458b74SDamien.Horsley uc->value.integer.value[0] = spdif->multi_freqs[i];
412c4458b74SDamien.Horsley } else {
413c4458b74SDamien.Horsley uc->value.integer.value[0] = spdif->single_freq;
414c4458b74SDamien.Horsley }
415c4458b74SDamien.Horsley } else {
416c4458b74SDamien.Horsley uc->value.integer.value[0] = 0;
417c4458b74SDamien.Horsley }
418c4458b74SDamien.Horsley
419c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
420c4458b74SDamien.Horsley
421c4458b74SDamien.Horsley return 0;
422c4458b74SDamien.Horsley }
423c4458b74SDamien.Horsley
img_spdif_in_info_trk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)424c4458b74SDamien.Horsley static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol,
425c4458b74SDamien.Horsley struct snd_ctl_elem_info *uinfo)
426c4458b74SDamien.Horsley {
427c4458b74SDamien.Horsley uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
428c4458b74SDamien.Horsley uinfo->count = 1;
429c4458b74SDamien.Horsley uinfo->value.integer.min = 0;
430c4458b74SDamien.Horsley uinfo->value.integer.max = 255;
431c4458b74SDamien.Horsley
432c4458b74SDamien.Horsley return 0;
433c4458b74SDamien.Horsley }
434c4458b74SDamien.Horsley
img_spdif_in_get_trk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)435c4458b74SDamien.Horsley static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol,
436c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
437c4458b74SDamien.Horsley {
438c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
439c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
440c4458b74SDamien.Horsley
441c4458b74SDamien.Horsley ucontrol->value.integer.value[0] = spdif->trk;
442c4458b74SDamien.Horsley
443c4458b74SDamien.Horsley return 0;
444c4458b74SDamien.Horsley }
445c4458b74SDamien.Horsley
img_spdif_in_set_trk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)446c4458b74SDamien.Horsley static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol,
447c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
448c4458b74SDamien.Horsley {
449c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
450c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
451c4458b74SDamien.Horsley unsigned long flags;
452c4458b74SDamien.Horsley int i;
453c4458b74SDamien.Horsley u32 reg;
454c4458b74SDamien.Horsley
455c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
456c4458b74SDamien.Horsley
457c4458b74SDamien.Horsley if (spdif->active) {
458c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
459c4458b74SDamien.Horsley return -EBUSY;
460c4458b74SDamien.Horsley }
461c4458b74SDamien.Horsley
462c4458b74SDamien.Horsley spdif->trk = ucontrol->value.integer.value[0];
463c4458b74SDamien.Horsley
464c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
465c4458b74SDamien.Horsley reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK;
466c4458b74SDamien.Horsley reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT;
467c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
468c4458b74SDamien.Horsley
469c4458b74SDamien.Horsley for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
470c4458b74SDamien.Horsley spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] &
471c4458b74SDamien.Horsley ~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) |
472c4458b74SDamien.Horsley (spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT);
473c4458b74SDamien.Horsley
474c4458b74SDamien.Horsley img_spdif_in_aclkgen_writel(spdif, i);
475c4458b74SDamien.Horsley }
476c4458b74SDamien.Horsley
477c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
478c4458b74SDamien.Horsley
479c4458b74SDamien.Horsley return 0;
480c4458b74SDamien.Horsley }
481c4458b74SDamien.Horsley
img_spdif_in_info_lock(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)482c4458b74SDamien.Horsley static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol,
483c4458b74SDamien.Horsley struct snd_ctl_elem_info *uinfo)
484c4458b74SDamien.Horsley {
485c4458b74SDamien.Horsley uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
486c4458b74SDamien.Horsley uinfo->count = 1;
487c4458b74SDamien.Horsley uinfo->value.integer.min = -128;
488c4458b74SDamien.Horsley uinfo->value.integer.max = 127;
489c4458b74SDamien.Horsley
490c4458b74SDamien.Horsley return 0;
491c4458b74SDamien.Horsley }
492c4458b74SDamien.Horsley
img_spdif_in_get_lock_acquire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)493c4458b74SDamien.Horsley static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol,
494c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
495c4458b74SDamien.Horsley {
496c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
497c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
498c4458b74SDamien.Horsley
499c4458b74SDamien.Horsley ucontrol->value.integer.value[0] = spdif->lock_acquire;
500c4458b74SDamien.Horsley
501c4458b74SDamien.Horsley return 0;
502c4458b74SDamien.Horsley }
503c4458b74SDamien.Horsley
img_spdif_in_set_lock_acquire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)504c4458b74SDamien.Horsley static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol,
505c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
506c4458b74SDamien.Horsley {
507c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
508c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
509c4458b74SDamien.Horsley unsigned long flags;
510c4458b74SDamien.Horsley u32 reg;
511c4458b74SDamien.Horsley
512c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
513c4458b74SDamien.Horsley
514c4458b74SDamien.Horsley if (spdif->active) {
515c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
516c4458b74SDamien.Horsley return -EBUSY;
517c4458b74SDamien.Horsley }
518c4458b74SDamien.Horsley
519c4458b74SDamien.Horsley spdif->lock_acquire = ucontrol->value.integer.value[0];
520c4458b74SDamien.Horsley
521c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
522c4458b74SDamien.Horsley reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK;
523c4458b74SDamien.Horsley reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
524c4458b74SDamien.Horsley IMG_SPDIF_IN_CTL_LOCKHI_MASK;
525c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
526c4458b74SDamien.Horsley
527c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
528c4458b74SDamien.Horsley
529c4458b74SDamien.Horsley return 0;
530c4458b74SDamien.Horsley }
531c4458b74SDamien.Horsley
img_spdif_in_get_lock_release(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)532c4458b74SDamien.Horsley static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol,
533c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
534c4458b74SDamien.Horsley {
535c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
536c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
537c4458b74SDamien.Horsley
538c4458b74SDamien.Horsley ucontrol->value.integer.value[0] = spdif->lock_release;
539c4458b74SDamien.Horsley
540c4458b74SDamien.Horsley return 0;
541c4458b74SDamien.Horsley }
542c4458b74SDamien.Horsley
img_spdif_in_set_lock_release(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)543c4458b74SDamien.Horsley static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol,
544c4458b74SDamien.Horsley struct snd_ctl_elem_value *ucontrol)
545c4458b74SDamien.Horsley {
546c4458b74SDamien.Horsley struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
547c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
548c4458b74SDamien.Horsley unsigned long flags;
549c4458b74SDamien.Horsley u32 reg;
550c4458b74SDamien.Horsley
551c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
552c4458b74SDamien.Horsley
553c4458b74SDamien.Horsley if (spdif->active) {
554c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
555c4458b74SDamien.Horsley return -EBUSY;
556c4458b74SDamien.Horsley }
557c4458b74SDamien.Horsley
558c4458b74SDamien.Horsley spdif->lock_release = ucontrol->value.integer.value[0];
559c4458b74SDamien.Horsley
560c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
561c4458b74SDamien.Horsley reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK;
562c4458b74SDamien.Horsley reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
563c4458b74SDamien.Horsley IMG_SPDIF_IN_CTL_LOCKLO_MASK;
564c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
565c4458b74SDamien.Horsley
566c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
567c4458b74SDamien.Horsley
568c4458b74SDamien.Horsley return 0;
569c4458b74SDamien.Horsley }
570c4458b74SDamien.Horsley
571c4458b74SDamien.Horsley static struct snd_kcontrol_new img_spdif_in_controls[] = {
572c4458b74SDamien.Horsley {
573c4458b74SDamien.Horsley .access = SNDRV_CTL_ELEM_ACCESS_READ,
574c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
575c4458b74SDamien.Horsley .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
576c4458b74SDamien.Horsley .info = img_spdif_in_iec958_info,
577c4458b74SDamien.Horsley .get = img_spdif_in_get_status_mask
578c4458b74SDamien.Horsley },
579c4458b74SDamien.Horsley {
580c4458b74SDamien.Horsley .access = SNDRV_CTL_ELEM_ACCESS_READ |
581c4458b74SDamien.Horsley SNDRV_CTL_ELEM_ACCESS_VOLATILE,
582c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
583c4458b74SDamien.Horsley .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
584c4458b74SDamien.Horsley .info = img_spdif_in_iec958_info,
585c4458b74SDamien.Horsley .get = img_spdif_in_get_status
586c4458b74SDamien.Horsley },
587c4458b74SDamien.Horsley {
588c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
589c4458b74SDamien.Horsley .name = "SPDIF In Multi Frequency Acquire",
590c4458b74SDamien.Horsley .info = img_spdif_in_info_multi_freq,
591c4458b74SDamien.Horsley .get = img_spdif_in_get_multi_freq,
592c4458b74SDamien.Horsley .put = img_spdif_in_set_multi_freq
593c4458b74SDamien.Horsley },
594c4458b74SDamien.Horsley {
595c4458b74SDamien.Horsley .access = SNDRV_CTL_ELEM_ACCESS_READ |
596c4458b74SDamien.Horsley SNDRV_CTL_ELEM_ACCESS_VOLATILE,
597c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
598c4458b74SDamien.Horsley .name = "SPDIF In Lock Frequency",
599c4458b74SDamien.Horsley .info = img_spdif_in_info_lock_freq,
600c4458b74SDamien.Horsley .get = img_spdif_in_get_lock_freq
601c4458b74SDamien.Horsley },
602c4458b74SDamien.Horsley {
603c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
604c4458b74SDamien.Horsley .name = "SPDIF In Lock TRK",
605c4458b74SDamien.Horsley .info = img_spdif_in_info_trk,
606c4458b74SDamien.Horsley .get = img_spdif_in_get_trk,
607c4458b74SDamien.Horsley .put = img_spdif_in_set_trk
608c4458b74SDamien.Horsley },
609c4458b74SDamien.Horsley {
610c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
611c4458b74SDamien.Horsley .name = "SPDIF In Lock Acquire Threshold",
612c4458b74SDamien.Horsley .info = img_spdif_in_info_lock,
613c4458b74SDamien.Horsley .get = img_spdif_in_get_lock_acquire,
614c4458b74SDamien.Horsley .put = img_spdif_in_set_lock_acquire
615c4458b74SDamien.Horsley },
616c4458b74SDamien.Horsley {
617c4458b74SDamien.Horsley .iface = SNDRV_CTL_ELEM_IFACE_PCM,
618c4458b74SDamien.Horsley .name = "SPDIF In Lock Release Threshold",
619c4458b74SDamien.Horsley .info = img_spdif_in_info_lock,
620c4458b74SDamien.Horsley .get = img_spdif_in_get_lock_release,
621c4458b74SDamien.Horsley .put = img_spdif_in_set_lock_release
622c4458b74SDamien.Horsley }
623c4458b74SDamien.Horsley };
624c4458b74SDamien.Horsley
img_spdif_in_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)625c4458b74SDamien.Horsley static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
626c4458b74SDamien.Horsley struct snd_soc_dai *dai)
627c4458b74SDamien.Horsley {
628c4458b74SDamien.Horsley unsigned long flags;
629c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
630c4458b74SDamien.Horsley int ret = 0;
631c4458b74SDamien.Horsley u32 reg;
632c4458b74SDamien.Horsley
633c4458b74SDamien.Horsley spin_lock_irqsave(&spdif->lock, flags);
634c4458b74SDamien.Horsley
635c4458b74SDamien.Horsley switch (cmd) {
636c4458b74SDamien.Horsley case SNDRV_PCM_TRIGGER_START:
637c4458b74SDamien.Horsley case SNDRV_PCM_TRIGGER_RESUME:
638c4458b74SDamien.Horsley case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
639c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
640c4458b74SDamien.Horsley if (spdif->multi_freq)
641c4458b74SDamien.Horsley reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK;
642c4458b74SDamien.Horsley else
643c4458b74SDamien.Horsley reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT);
644c4458b74SDamien.Horsley reg |= IMG_SPDIF_IN_CTL_SRT_MASK;
645c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
646c4458b74SDamien.Horsley spdif->active = true;
647c4458b74SDamien.Horsley break;
648c4458b74SDamien.Horsley case SNDRV_PCM_TRIGGER_STOP:
649c4458b74SDamien.Horsley case SNDRV_PCM_TRIGGER_SUSPEND:
650c4458b74SDamien.Horsley case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
651c4458b74SDamien.Horsley reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
652c4458b74SDamien.Horsley reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK;
653c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
654c4458b74SDamien.Horsley spdif->active = false;
655c4458b74SDamien.Horsley break;
656c4458b74SDamien.Horsley default:
657c4458b74SDamien.Horsley ret = -EINVAL;
658c4458b74SDamien.Horsley }
659c4458b74SDamien.Horsley
660c4458b74SDamien.Horsley spin_unlock_irqrestore(&spdif->lock, flags);
661c4458b74SDamien.Horsley
662c4458b74SDamien.Horsley return ret;
663c4458b74SDamien.Horsley }
664c4458b74SDamien.Horsley
img_spdif_in_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)665c4458b74SDamien.Horsley static int img_spdif_in_hw_params(struct snd_pcm_substream *substream,
666c4458b74SDamien.Horsley struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
667c4458b74SDamien.Horsley {
668c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
669c4458b74SDamien.Horsley unsigned int rate, channels;
670c4458b74SDamien.Horsley snd_pcm_format_t format;
671c4458b74SDamien.Horsley
672c4458b74SDamien.Horsley rate = params_rate(params);
673c4458b74SDamien.Horsley channels = params_channels(params);
674c4458b74SDamien.Horsley format = params_format(params);
675c4458b74SDamien.Horsley
676c4458b74SDamien.Horsley if (format != SNDRV_PCM_FORMAT_S32_LE)
677c4458b74SDamien.Horsley return -EINVAL;
678c4458b74SDamien.Horsley
679c4458b74SDamien.Horsley if (channels != 2)
680c4458b74SDamien.Horsley return -EINVAL;
681c4458b74SDamien.Horsley
682c4458b74SDamien.Horsley return img_spdif_in_do_clkgen_single(spdif, rate);
683c4458b74SDamien.Horsley }
684c4458b74SDamien.Horsley
img_spdif_in_dai_probe(struct snd_soc_dai * dai)685c4458b74SDamien.Horsley static int img_spdif_in_dai_probe(struct snd_soc_dai *dai)
686c4458b74SDamien.Horsley {
687c4458b74SDamien.Horsley struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
688c4458b74SDamien.Horsley
689c4458b74SDamien.Horsley snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data);
690c4458b74SDamien.Horsley
691c4458b74SDamien.Horsley snd_soc_add_dai_controls(dai, img_spdif_in_controls,
692c4458b74SDamien.Horsley ARRAY_SIZE(img_spdif_in_controls));
693c4458b74SDamien.Horsley
694c4458b74SDamien.Horsley return 0;
695c4458b74SDamien.Horsley }
696c4458b74SDamien.Horsley
697*ca6b2aacSKuninori Morimoto static const struct snd_soc_dai_ops img_spdif_in_dai_ops = {
698c4458b74SDamien.Horsley .probe = img_spdif_in_dai_probe,
699*ca6b2aacSKuninori Morimoto .trigger = img_spdif_in_trigger,
700*ca6b2aacSKuninori Morimoto .hw_params = img_spdif_in_hw_params
701*ca6b2aacSKuninori Morimoto };
702*ca6b2aacSKuninori Morimoto
703*ca6b2aacSKuninori Morimoto static struct snd_soc_dai_driver img_spdif_in_dai = {
704c4458b74SDamien.Horsley .capture = {
705c4458b74SDamien.Horsley .channels_min = 2,
706c4458b74SDamien.Horsley .channels_max = 2,
707c4458b74SDamien.Horsley .rates = SNDRV_PCM_RATE_8000_192000,
708c4458b74SDamien.Horsley .formats = SNDRV_PCM_FMTBIT_S32_LE
709c4458b74SDamien.Horsley },
710c4458b74SDamien.Horsley .ops = &img_spdif_in_dai_ops
711c4458b74SDamien.Horsley };
712c4458b74SDamien.Horsley
713c4458b74SDamien.Horsley static const struct snd_soc_component_driver img_spdif_in_component = {
7145f9d6998SCharles Keepax .name = "img-spdif-in",
7155f9d6998SCharles Keepax .legacy_dai_naming = 1,
716c4458b74SDamien.Horsley };
717c4458b74SDamien.Horsley
img_spdif_in_probe(struct platform_device * pdev)718c4458b74SDamien.Horsley static int img_spdif_in_probe(struct platform_device *pdev)
719c4458b74SDamien.Horsley {
720c4458b74SDamien.Horsley struct img_spdif_in *spdif;
721c4458b74SDamien.Horsley struct resource *res;
722c4458b74SDamien.Horsley void __iomem *base;
723c4458b74SDamien.Horsley int ret;
724c4458b74SDamien.Horsley struct reset_control *rst;
725c4458b74SDamien.Horsley u32 reg;
726c4458b74SDamien.Horsley struct device *dev = &pdev->dev;
727c4458b74SDamien.Horsley
728c4458b74SDamien.Horsley spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
729c4458b74SDamien.Horsley if (!spdif)
730c4458b74SDamien.Horsley return -ENOMEM;
731c4458b74SDamien.Horsley
732c4458b74SDamien.Horsley platform_set_drvdata(pdev, spdif);
733c4458b74SDamien.Horsley
734c4458b74SDamien.Horsley spdif->dev = &pdev->dev;
735c4458b74SDamien.Horsley
736c481f383SYang Yingliang base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
737c4458b74SDamien.Horsley if (IS_ERR(base))
738c4458b74SDamien.Horsley return PTR_ERR(base);
739c4458b74SDamien.Horsley
740c4458b74SDamien.Horsley spdif->base = base;
741c4458b74SDamien.Horsley
742c4458b74SDamien.Horsley spdif->clk_sys = devm_clk_get(dev, "sys");
743ef12f373SKuninori Morimoto if (IS_ERR(spdif->clk_sys))
744ef12f373SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(spdif->clk_sys),
745ef12f373SKuninori Morimoto "Failed to acquire clock 'sys'\n");
746c4458b74SDamien.Horsley
7471fac824fSEd Blake pm_runtime_enable(&pdev->dev);
7481fac824fSEd Blake if (!pm_runtime_enabled(&pdev->dev)) {
7491fac824fSEd Blake ret = img_spdif_in_runtime_resume(&pdev->dev);
750c4458b74SDamien.Horsley if (ret)
7511fac824fSEd Blake goto err_pm_disable;
7521fac824fSEd Blake }
7536eaaf9bdSMinghao Chi ret = pm_runtime_resume_and_get(&pdev->dev);
7546eaaf9bdSMinghao Chi if (ret < 0)
7551fac824fSEd Blake goto err_suspend;
756c4458b74SDamien.Horsley
7573a4e1b93SPhilipp Zabel rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
758c4458b74SDamien.Horsley if (IS_ERR(rst)) {
759c4458b74SDamien.Horsley if (PTR_ERR(rst) == -EPROBE_DEFER) {
760c4458b74SDamien.Horsley ret = -EPROBE_DEFER;
7611fac824fSEd Blake goto err_pm_put;
762c4458b74SDamien.Horsley }
763c4458b74SDamien.Horsley dev_dbg(dev, "No top level reset found\n");
764c4458b74SDamien.Horsley img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK,
765c4458b74SDamien.Horsley IMG_SPDIF_IN_SOFT_RESET);
766c4458b74SDamien.Horsley img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET);
767c4458b74SDamien.Horsley } else {
768c4458b74SDamien.Horsley reset_control_assert(rst);
769c4458b74SDamien.Horsley reset_control_deassert(rst);
770c4458b74SDamien.Horsley }
771c4458b74SDamien.Horsley
772c4458b74SDamien.Horsley spin_lock_init(&spdif->lock);
773c4458b74SDamien.Horsley
774c4458b74SDamien.Horsley spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET;
775c4458b74SDamien.Horsley spdif->dma_data.addr_width = 4;
776c4458b74SDamien.Horsley spdif->dma_data.maxburst = 4;
777c4458b74SDamien.Horsley spdif->trk = 0x80;
778c4458b74SDamien.Horsley spdif->lock_acquire = 4;
779c4458b74SDamien.Horsley spdif->lock_release = -128;
780c4458b74SDamien.Horsley
781c4458b74SDamien.Horsley reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
782c4458b74SDamien.Horsley IMG_SPDIF_IN_CTL_LOCKHI_MASK;
783c4458b74SDamien.Horsley reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
784c4458b74SDamien.Horsley IMG_SPDIF_IN_CTL_LOCKLO_MASK;
785c4458b74SDamien.Horsley reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) &
786c4458b74SDamien.Horsley IMG_SPDIF_IN_CTL_TRK_MASK;
787c4458b74SDamien.Horsley img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
788c4458b74SDamien.Horsley
7891fac824fSEd Blake pm_runtime_put(&pdev->dev);
7901fac824fSEd Blake
791c4458b74SDamien.Horsley ret = devm_snd_soc_register_component(&pdev->dev,
792c4458b74SDamien.Horsley &img_spdif_in_component, &img_spdif_in_dai, 1);
793c4458b74SDamien.Horsley if (ret)
7941fac824fSEd Blake goto err_suspend;
795c4458b74SDamien.Horsley
796c4458b74SDamien.Horsley ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
797c4458b74SDamien.Horsley if (ret)
7981fac824fSEd Blake goto err_suspend;
799c4458b74SDamien.Horsley
800c4458b74SDamien.Horsley return 0;
801c4458b74SDamien.Horsley
8021fac824fSEd Blake err_pm_put:
8031fac824fSEd Blake pm_runtime_put(&pdev->dev);
8041fac824fSEd Blake err_suspend:
8051fac824fSEd Blake if (!pm_runtime_enabled(&pdev->dev))
8061fac824fSEd Blake img_spdif_in_runtime_suspend(&pdev->dev);
8071fac824fSEd Blake err_pm_disable:
8081fac824fSEd Blake pm_runtime_disable(&pdev->dev);
809c4458b74SDamien.Horsley
810c4458b74SDamien.Horsley return ret;
811c4458b74SDamien.Horsley }
812c4458b74SDamien.Horsley
img_spdif_in_dev_remove(struct platform_device * pdev)813029a00aeSUwe Kleine-König static void img_spdif_in_dev_remove(struct platform_device *pdev)
814c4458b74SDamien.Horsley {
8151fac824fSEd Blake pm_runtime_disable(&pdev->dev);
8161fac824fSEd Blake if (!pm_runtime_status_suspended(&pdev->dev))
8171fac824fSEd Blake img_spdif_in_runtime_suspend(&pdev->dev);
818c4458b74SDamien.Horsley }
819c4458b74SDamien.Horsley
82052eb0ed3SEd Blake #ifdef CONFIG_PM_SLEEP
img_spdif_in_suspend(struct device * dev)82152eb0ed3SEd Blake static int img_spdif_in_suspend(struct device *dev)
82252eb0ed3SEd Blake {
82352eb0ed3SEd Blake struct img_spdif_in *spdif = dev_get_drvdata(dev);
8241fac824fSEd Blake int ret;
8251fac824fSEd Blake
8261fac824fSEd Blake if (pm_runtime_status_suspended(dev)) {
8271fac824fSEd Blake ret = img_spdif_in_runtime_resume(dev);
8281fac824fSEd Blake if (ret)
8291fac824fSEd Blake return ret;
8301fac824fSEd Blake }
83152eb0ed3SEd Blake
83252eb0ed3SEd Blake spdif->suspend_clkgen = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CLKGEN);
83352eb0ed3SEd Blake spdif->suspend_ctl = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
83452eb0ed3SEd Blake
8351fac824fSEd Blake img_spdif_in_runtime_suspend(dev);
83652eb0ed3SEd Blake
83752eb0ed3SEd Blake return 0;
83852eb0ed3SEd Blake }
83952eb0ed3SEd Blake
img_spdif_in_resume(struct device * dev)84052eb0ed3SEd Blake static int img_spdif_in_resume(struct device *dev)
84152eb0ed3SEd Blake {
84252eb0ed3SEd Blake struct img_spdif_in *spdif = dev_get_drvdata(dev);
8431fac824fSEd Blake int i, ret;
84452eb0ed3SEd Blake
8451fac824fSEd Blake ret = img_spdif_in_runtime_resume(dev);
8461fac824fSEd Blake if (ret)
8471fac824fSEd Blake return ret;
84852eb0ed3SEd Blake
84952eb0ed3SEd Blake for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++)
85052eb0ed3SEd Blake img_spdif_in_aclkgen_writel(spdif, i);
85152eb0ed3SEd Blake
85252eb0ed3SEd Blake img_spdif_in_writel(spdif, spdif->suspend_clkgen, IMG_SPDIF_IN_CLKGEN);
85352eb0ed3SEd Blake img_spdif_in_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_IN_CTL);
85452eb0ed3SEd Blake
8551fac824fSEd Blake if (pm_runtime_status_suspended(dev))
8561fac824fSEd Blake img_spdif_in_runtime_suspend(dev);
8571fac824fSEd Blake
85852eb0ed3SEd Blake return 0;
85952eb0ed3SEd Blake }
86052eb0ed3SEd Blake #endif
86152eb0ed3SEd Blake
862c4458b74SDamien.Horsley static const struct of_device_id img_spdif_in_of_match[] = {
863c4458b74SDamien.Horsley { .compatible = "img,spdif-in" },
864c4458b74SDamien.Horsley {}
865c4458b74SDamien.Horsley };
866c4458b74SDamien.Horsley MODULE_DEVICE_TABLE(of, img_spdif_in_of_match);
867c4458b74SDamien.Horsley
86852eb0ed3SEd Blake static const struct dev_pm_ops img_spdif_in_pm_ops = {
8691fac824fSEd Blake SET_RUNTIME_PM_OPS(img_spdif_in_runtime_suspend,
8701fac824fSEd Blake img_spdif_in_runtime_resume, NULL)
87152eb0ed3SEd Blake SET_SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume)
87252eb0ed3SEd Blake };
87352eb0ed3SEd Blake
874c4458b74SDamien.Horsley static struct platform_driver img_spdif_in_driver = {
875c4458b74SDamien.Horsley .driver = {
876c4458b74SDamien.Horsley .name = "img-spdif-in",
87752eb0ed3SEd Blake .of_match_table = img_spdif_in_of_match,
87852eb0ed3SEd Blake .pm = &img_spdif_in_pm_ops
879c4458b74SDamien.Horsley },
880c4458b74SDamien.Horsley .probe = img_spdif_in_probe,
881029a00aeSUwe Kleine-König .remove_new = img_spdif_in_dev_remove
882c4458b74SDamien.Horsley };
883c4458b74SDamien.Horsley module_platform_driver(img_spdif_in_driver);
884c4458b74SDamien.Horsley
885c4458b74SDamien.Horsley MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
886c4458b74SDamien.Horsley MODULE_DESCRIPTION("IMG SPDIF Input driver");
887c4458b74SDamien.Horsley MODULE_LICENSE("GPL v2");
888