xref: /openbmc/linux/sound/soc/img/img-spdif-in.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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