xref: /OK3568_Linux_fs/kernel/sound/soc/img/img-i2s-in.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * IMG I2S input controller driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2015 Imagination Technologies Ltd.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/clk.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/of.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/pm_runtime.h>
17*4882a593Smuzhiyun #include <linux/reset.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <sound/core.h>
20*4882a593Smuzhiyun #include <sound/dmaengine_pcm.h>
21*4882a593Smuzhiyun #include <sound/initval.h>
22*4882a593Smuzhiyun #include <sound/pcm.h>
23*4882a593Smuzhiyun #include <sound/pcm_params.h>
24*4882a593Smuzhiyun #include <sound/soc.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #define IMG_I2S_IN_RX_FIFO			0x0
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #define IMG_I2S_IN_CTL				0x4
29*4882a593Smuzhiyun #define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK		0xfffffffc
30*4882a593Smuzhiyun #define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT		2
31*4882a593Smuzhiyun #define IMG_I2S_IN_CTL_16PACK_MASK		BIT(1)
32*4882a593Smuzhiyun #define IMG_I2S_IN_CTL_ME_MASK			BIT(0)
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL			0x4
35*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_CCDEL_MASK		0x38000
36*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT		15
37*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_FEN_MASK		BIT(14)
38*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_FMODE_MASK		BIT(13)
39*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_16PACK_MASK		BIT(12)
40*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_JUST_MASK		BIT(10)
41*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_PACKH_MASK		BIT(9)
42*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK	BIT(8)
43*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_BLKP_MASK		BIT(7)
44*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK	BIT(6)
45*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_LRD_MASK		BIT(3)
46*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_FW_MASK		BIT(2)
47*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_SW_MASK		BIT(1)
48*4882a593Smuzhiyun #define IMG_I2S_IN_CH_CTL_ME_MASK		BIT(0)
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #define IMG_I2S_IN_CH_STRIDE			0x20
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun struct img_i2s_in {
53*4882a593Smuzhiyun 	void __iomem *base;
54*4882a593Smuzhiyun 	struct clk *clk_sys;
55*4882a593Smuzhiyun 	struct snd_dmaengine_dai_dma_data dma_data;
56*4882a593Smuzhiyun 	struct device *dev;
57*4882a593Smuzhiyun 	unsigned int max_i2s_chan;
58*4882a593Smuzhiyun 	void __iomem *channel_base;
59*4882a593Smuzhiyun 	unsigned int active_channels;
60*4882a593Smuzhiyun 	struct snd_soc_dai_driver dai_driver;
61*4882a593Smuzhiyun 	u32 suspend_ctl;
62*4882a593Smuzhiyun 	u32 *suspend_ch_ctl;
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun 
img_i2s_in_runtime_suspend(struct device * dev)65*4882a593Smuzhiyun static int img_i2s_in_runtime_suspend(struct device *dev)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	struct img_i2s_in *i2s = dev_get_drvdata(dev);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	clk_disable_unprepare(i2s->clk_sys);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	return 0;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun 
img_i2s_in_runtime_resume(struct device * dev)74*4882a593Smuzhiyun static int img_i2s_in_runtime_resume(struct device *dev)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	struct img_i2s_in *i2s = dev_get_drvdata(dev);
77*4882a593Smuzhiyun 	int ret;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	ret = clk_prepare_enable(i2s->clk_sys);
80*4882a593Smuzhiyun 	if (ret) {
81*4882a593Smuzhiyun 		dev_err(dev, "Unable to enable sys clock\n");
82*4882a593Smuzhiyun 		return ret;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	return 0;
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun 
img_i2s_in_writel(struct img_i2s_in * i2s,u32 val,u32 reg)88*4882a593Smuzhiyun static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	writel(val, i2s->base + reg);
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
img_i2s_in_readl(struct img_i2s_in * i2s,u32 reg)93*4882a593Smuzhiyun static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	return readl(i2s->base + reg);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
img_i2s_in_ch_writel(struct img_i2s_in * i2s,u32 chan,u32 val,u32 reg)98*4882a593Smuzhiyun static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan,
99*4882a593Smuzhiyun 					u32 val, u32 reg)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
img_i2s_in_ch_readl(struct img_i2s_in * i2s,u32 chan,u32 reg)104*4882a593Smuzhiyun static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan,
105*4882a593Smuzhiyun 					u32 reg)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
img_i2s_in_ch_disable(struct img_i2s_in * i2s,u32 chan)110*4882a593Smuzhiyun static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	u32 reg;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
115*4882a593Smuzhiyun 	reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK;
116*4882a593Smuzhiyun 	img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun 
img_i2s_in_ch_enable(struct img_i2s_in * i2s,u32 chan)119*4882a593Smuzhiyun static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun 	u32 reg;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
124*4882a593Smuzhiyun 	reg |= IMG_I2S_IN_CH_CTL_ME_MASK;
125*4882a593Smuzhiyun 	img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun 
img_i2s_in_disable(struct img_i2s_in * i2s)128*4882a593Smuzhiyun static inline void img_i2s_in_disable(struct img_i2s_in *i2s)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	u32 reg;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
133*4882a593Smuzhiyun 	reg &= ~IMG_I2S_IN_CTL_ME_MASK;
134*4882a593Smuzhiyun 	img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
img_i2s_in_enable(struct img_i2s_in * i2s)137*4882a593Smuzhiyun static inline void img_i2s_in_enable(struct img_i2s_in *i2s)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	u32 reg;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
142*4882a593Smuzhiyun 	reg |= IMG_I2S_IN_CTL_ME_MASK;
143*4882a593Smuzhiyun 	img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
img_i2s_in_flush(struct img_i2s_in * i2s)146*4882a593Smuzhiyun static inline void img_i2s_in_flush(struct img_i2s_in *i2s)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	int i;
149*4882a593Smuzhiyun 	u32 reg;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++) {
152*4882a593Smuzhiyun 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
153*4882a593Smuzhiyun 		reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
154*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
155*4882a593Smuzhiyun 		reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
156*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
img_i2s_in_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)160*4882a593Smuzhiyun static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd,
161*4882a593Smuzhiyun 	struct snd_soc_dai *dai)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	switch (cmd) {
166*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
167*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
168*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
169*4882a593Smuzhiyun 		img_i2s_in_enable(i2s);
170*4882a593Smuzhiyun 		break;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
173*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
174*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
175*4882a593Smuzhiyun 		img_i2s_in_disable(i2s);
176*4882a593Smuzhiyun 		break;
177*4882a593Smuzhiyun 	default:
178*4882a593Smuzhiyun 		return -EINVAL;
179*4882a593Smuzhiyun 	}
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	return 0;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
img_i2s_in_check_rate(struct img_i2s_in * i2s,unsigned int sample_rate,unsigned int frame_size,unsigned int * bclk_filter_enable,unsigned int * bclk_filter_value)184*4882a593Smuzhiyun static int img_i2s_in_check_rate(struct img_i2s_in *i2s,
185*4882a593Smuzhiyun 		unsigned int sample_rate, unsigned int frame_size,
186*4882a593Smuzhiyun 		unsigned int *bclk_filter_enable,
187*4882a593Smuzhiyun 		unsigned int *bclk_filter_value)
188*4882a593Smuzhiyun {
189*4882a593Smuzhiyun 	unsigned int bclk_freq, cur_freq;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	bclk_freq = sample_rate * frame_size;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	cur_freq = clk_get_rate(i2s->clk_sys);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if (cur_freq >= bclk_freq * 8) {
196*4882a593Smuzhiyun 		*bclk_filter_enable = 1;
197*4882a593Smuzhiyun 		*bclk_filter_value = 0;
198*4882a593Smuzhiyun 	} else if (cur_freq >= bclk_freq * 7) {
199*4882a593Smuzhiyun 		*bclk_filter_enable = 1;
200*4882a593Smuzhiyun 		*bclk_filter_value = 1;
201*4882a593Smuzhiyun 	} else if (cur_freq >= bclk_freq * 6) {
202*4882a593Smuzhiyun 		*bclk_filter_enable = 0;
203*4882a593Smuzhiyun 		*bclk_filter_value = 0;
204*4882a593Smuzhiyun 	} else {
205*4882a593Smuzhiyun 		dev_err(i2s->dev,
206*4882a593Smuzhiyun 			"Sys clock rate %u insufficient for sample rate %u\n",
207*4882a593Smuzhiyun 			cur_freq, sample_rate);
208*4882a593Smuzhiyun 		return -EINVAL;
209*4882a593Smuzhiyun 	}
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	return 0;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun 
img_i2s_in_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)214*4882a593Smuzhiyun static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
215*4882a593Smuzhiyun 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
218*4882a593Smuzhiyun 	unsigned int rate, channels, i2s_channels, frame_size;
219*4882a593Smuzhiyun 	unsigned int bclk_filter_enable, bclk_filter_value;
220*4882a593Smuzhiyun 	int i, ret = 0;
221*4882a593Smuzhiyun 	u32 reg, control_mask, chan_control_mask;
222*4882a593Smuzhiyun 	u32 control_set = 0, chan_control_set = 0;
223*4882a593Smuzhiyun 	snd_pcm_format_t format;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	rate = params_rate(params);
226*4882a593Smuzhiyun 	format = params_format(params);
227*4882a593Smuzhiyun 	channels = params_channels(params);
228*4882a593Smuzhiyun 	i2s_channels = channels / 2;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	switch (format) {
231*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S32_LE:
232*4882a593Smuzhiyun 		frame_size = 64;
233*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
234*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
235*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK;
236*4882a593Smuzhiyun 		break;
237*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S24_LE:
238*4882a593Smuzhiyun 		frame_size = 64;
239*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
240*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
241*4882a593Smuzhiyun 		break;
242*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S16_LE:
243*4882a593Smuzhiyun 		frame_size = 32;
244*4882a593Smuzhiyun 		control_set |= IMG_I2S_IN_CTL_16PACK_MASK;
245*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK;
246*4882a593Smuzhiyun 		break;
247*4882a593Smuzhiyun 	default:
248*4882a593Smuzhiyun 		return -EINVAL;
249*4882a593Smuzhiyun 	}
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	if ((channels < 2) ||
252*4882a593Smuzhiyun 	    (channels > (i2s->max_i2s_chan * 2)) ||
253*4882a593Smuzhiyun 	    (channels % 2))
254*4882a593Smuzhiyun 		return -EINVAL;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	ret = img_i2s_in_check_rate(i2s, rate, frame_size,
259*4882a593Smuzhiyun 			&bclk_filter_enable, &bclk_filter_value);
260*4882a593Smuzhiyun 	if (ret < 0)
261*4882a593Smuzhiyun 		return ret;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	if (bclk_filter_enable)
264*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (bclk_filter_value)
267*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	control_mask = IMG_I2S_IN_CTL_16PACK_MASK |
270*4882a593Smuzhiyun 		       IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK |
273*4882a593Smuzhiyun 			    IMG_I2S_IN_CH_CTL_FEN_MASK |
274*4882a593Smuzhiyun 			    IMG_I2S_IN_CH_CTL_FMODE_MASK |
275*4882a593Smuzhiyun 			    IMG_I2S_IN_CH_CTL_SW_MASK |
276*4882a593Smuzhiyun 			    IMG_I2S_IN_CH_CTL_FW_MASK |
277*4882a593Smuzhiyun 			    IMG_I2S_IN_CH_CTL_PACKH_MASK;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
280*4882a593Smuzhiyun 	reg = (reg & ~control_mask) | control_set;
281*4882a593Smuzhiyun 	img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
284*4882a593Smuzhiyun 		img_i2s_in_ch_disable(i2s, i);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
287*4882a593Smuzhiyun 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
288*4882a593Smuzhiyun 		reg = (reg & ~chan_control_mask) | chan_control_set;
289*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	i2s->active_channels = i2s_channels;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	img_i2s_in_flush(i2s);
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
297*4882a593Smuzhiyun 		img_i2s_in_ch_enable(i2s, i);
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	return 0;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun 
img_i2s_in_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)302*4882a593Smuzhiyun static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
305*4882a593Smuzhiyun 	int i, ret;
306*4882a593Smuzhiyun 	u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
307*4882a593Smuzhiyun 	u32 reg;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
310*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_NB_NF:
311*4882a593Smuzhiyun 		lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
312*4882a593Smuzhiyun 		break;
313*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_NB_IF:
314*4882a593Smuzhiyun 		break;
315*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_IB_NF:
316*4882a593Smuzhiyun 		lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
317*4882a593Smuzhiyun 		blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
318*4882a593Smuzhiyun 		break;
319*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_IB_IF:
320*4882a593Smuzhiyun 		blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
321*4882a593Smuzhiyun 		break;
322*4882a593Smuzhiyun 	default:
323*4882a593Smuzhiyun 		return -EINVAL;
324*4882a593Smuzhiyun 	}
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
327*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_I2S:
328*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
329*4882a593Smuzhiyun 		break;
330*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_LEFT_J:
331*4882a593Smuzhiyun 		break;
332*4882a593Smuzhiyun 	default:
333*4882a593Smuzhiyun 		return -EINVAL;
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
337*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBM_CFM:
338*4882a593Smuzhiyun 		break;
339*4882a593Smuzhiyun 	default:
340*4882a593Smuzhiyun 		return -EINVAL;
341*4882a593Smuzhiyun 	}
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	ret = pm_runtime_get_sync(i2s->dev);
346*4882a593Smuzhiyun 	if (ret < 0) {
347*4882a593Smuzhiyun 		pm_runtime_put_noidle(i2s->dev);
348*4882a593Smuzhiyun 		return ret;
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
352*4882a593Smuzhiyun 		img_i2s_in_ch_disable(i2s, i);
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	/*
355*4882a593Smuzhiyun 	 * BLKP and LRD must be set during separate register writes
356*4882a593Smuzhiyun 	 */
357*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
358*4882a593Smuzhiyun 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
359*4882a593Smuzhiyun 		reg = (reg & ~chan_control_mask) | chan_control_set;
360*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
361*4882a593Smuzhiyun 		reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set;
362*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
363*4882a593Smuzhiyun 		reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set;
364*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
365*4882a593Smuzhiyun 	}
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
368*4882a593Smuzhiyun 		img_i2s_in_ch_enable(i2s, i);
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	pm_runtime_put(i2s->dev);
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	return 0;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
376*4882a593Smuzhiyun 	.trigger = img_i2s_in_trigger,
377*4882a593Smuzhiyun 	.hw_params = img_i2s_in_hw_params,
378*4882a593Smuzhiyun 	.set_fmt = img_i2s_in_set_fmt
379*4882a593Smuzhiyun };
380*4882a593Smuzhiyun 
img_i2s_in_dai_probe(struct snd_soc_dai * dai)381*4882a593Smuzhiyun static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
382*4882a593Smuzhiyun {
383*4882a593Smuzhiyun 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	return 0;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun static const struct snd_soc_component_driver img_i2s_in_component = {
391*4882a593Smuzhiyun 	.name = "img-i2s-in"
392*4882a593Smuzhiyun };
393*4882a593Smuzhiyun 
img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream * st,struct snd_pcm_hw_params * params,struct dma_slave_config * sc)394*4882a593Smuzhiyun static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
395*4882a593Smuzhiyun 	struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	unsigned int i2s_channels = params_channels(params) / 2;
398*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = st->private_data;
399*4882a593Smuzhiyun 	struct snd_dmaengine_dai_dma_data *dma_data;
400*4882a593Smuzhiyun 	int ret;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st);
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	ret = snd_hwparams_to_dma_slave_config(st, params, sc);
405*4882a593Smuzhiyun 	if (ret)
406*4882a593Smuzhiyun 		return ret;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	sc->src_addr = dma_data->addr;
409*4882a593Smuzhiyun 	sc->src_addr_width = dma_data->addr_width;
410*4882a593Smuzhiyun 	sc->src_maxburst = 4 * i2s_channels;
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	return 0;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
416*4882a593Smuzhiyun 	.prepare_slave_config = img_i2s_in_dma_prepare_slave_config
417*4882a593Smuzhiyun };
418*4882a593Smuzhiyun 
img_i2s_in_probe(struct platform_device * pdev)419*4882a593Smuzhiyun static int img_i2s_in_probe(struct platform_device *pdev)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct img_i2s_in *i2s;
422*4882a593Smuzhiyun 	struct resource *res;
423*4882a593Smuzhiyun 	void __iomem *base;
424*4882a593Smuzhiyun 	int ret, i;
425*4882a593Smuzhiyun 	struct reset_control *rst;
426*4882a593Smuzhiyun 	unsigned int max_i2s_chan_pow_2;
427*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
430*4882a593Smuzhiyun 	if (!i2s)
431*4882a593Smuzhiyun 		return -ENOMEM;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	platform_set_drvdata(pdev, i2s);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	i2s->dev = dev;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
438*4882a593Smuzhiyun 	base = devm_ioremap_resource(dev, res);
439*4882a593Smuzhiyun 	if (IS_ERR(base))
440*4882a593Smuzhiyun 		return PTR_ERR(base);
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	i2s->base = base;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
445*4882a593Smuzhiyun 			&i2s->max_i2s_chan)) {
446*4882a593Smuzhiyun 		dev_err(dev, "No img,i2s-channels property\n");
447*4882a593Smuzhiyun 		return -EINVAL;
448*4882a593Smuzhiyun 	}
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	i2s->clk_sys = devm_clk_get(dev, "sys");
455*4882a593Smuzhiyun 	if (IS_ERR(i2s->clk_sys)) {
456*4882a593Smuzhiyun 		if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
457*4882a593Smuzhiyun 			dev_err(dev, "Failed to acquire clock 'sys'\n");
458*4882a593Smuzhiyun 		return PTR_ERR(i2s->clk_sys);
459*4882a593Smuzhiyun 	}
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	pm_runtime_enable(&pdev->dev);
462*4882a593Smuzhiyun 	if (!pm_runtime_enabled(&pdev->dev)) {
463*4882a593Smuzhiyun 		ret = img_i2s_in_runtime_resume(&pdev->dev);
464*4882a593Smuzhiyun 		if (ret)
465*4882a593Smuzhiyun 			goto err_pm_disable;
466*4882a593Smuzhiyun 	}
467*4882a593Smuzhiyun 	ret = pm_runtime_resume_and_get(&pdev->dev);
468*4882a593Smuzhiyun 	if (ret < 0)
469*4882a593Smuzhiyun 		goto err_suspend;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	i2s->active_channels = 1;
472*4882a593Smuzhiyun 	i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
473*4882a593Smuzhiyun 	i2s->dma_data.addr_width = 4;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	i2s->dai_driver.probe = img_i2s_in_dai_probe;
476*4882a593Smuzhiyun 	i2s->dai_driver.capture.channels_min = 2;
477*4882a593Smuzhiyun 	i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
478*4882a593Smuzhiyun 	i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
479*4882a593Smuzhiyun 	i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE |
480*4882a593Smuzhiyun 		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
481*4882a593Smuzhiyun 	i2s->dai_driver.ops = &img_i2s_in_dai_ops;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	rst = devm_reset_control_get_exclusive(dev, "rst");
484*4882a593Smuzhiyun 	if (IS_ERR(rst)) {
485*4882a593Smuzhiyun 		if (PTR_ERR(rst) == -EPROBE_DEFER) {
486*4882a593Smuzhiyun 			ret = -EPROBE_DEFER;
487*4882a593Smuzhiyun 			pm_runtime_put(&pdev->dev);
488*4882a593Smuzhiyun 			goto err_suspend;
489*4882a593Smuzhiyun 		}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 		dev_dbg(dev, "No top level reset found\n");
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 		img_i2s_in_disable(i2s);
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 		for (i = 0; i < i2s->max_i2s_chan; i++)
496*4882a593Smuzhiyun 			img_i2s_in_ch_disable(i2s, i);
497*4882a593Smuzhiyun 	} else {
498*4882a593Smuzhiyun 		reset_control_assert(rst);
499*4882a593Smuzhiyun 		reset_control_deassert(rst);
500*4882a593Smuzhiyun 	}
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL);
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++)
505*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i,
506*4882a593Smuzhiyun 			(4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) |
507*4882a593Smuzhiyun 			IMG_I2S_IN_CH_CTL_JUST_MASK |
508*4882a593Smuzhiyun 			IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	pm_runtime_put(&pdev->dev);
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	i2s->suspend_ch_ctl = devm_kcalloc(dev,
513*4882a593Smuzhiyun 		i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
514*4882a593Smuzhiyun 	if (!i2s->suspend_ch_ctl) {
515*4882a593Smuzhiyun 		ret = -ENOMEM;
516*4882a593Smuzhiyun 		goto err_suspend;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
520*4882a593Smuzhiyun 						&i2s->dai_driver, 1);
521*4882a593Smuzhiyun 	if (ret)
522*4882a593Smuzhiyun 		goto err_suspend;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
525*4882a593Smuzhiyun 	if (ret)
526*4882a593Smuzhiyun 		goto err_suspend;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	return 0;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun err_suspend:
531*4882a593Smuzhiyun 	if (!pm_runtime_enabled(&pdev->dev))
532*4882a593Smuzhiyun 		img_i2s_in_runtime_suspend(&pdev->dev);
533*4882a593Smuzhiyun err_pm_disable:
534*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	return ret;
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun 
img_i2s_in_dev_remove(struct platform_device * pdev)539*4882a593Smuzhiyun static int img_i2s_in_dev_remove(struct platform_device *pdev)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
542*4882a593Smuzhiyun 	if (!pm_runtime_status_suspended(&pdev->dev))
543*4882a593Smuzhiyun 		img_i2s_in_runtime_suspend(&pdev->dev);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	return 0;
546*4882a593Smuzhiyun }
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
img_i2s_in_suspend(struct device * dev)549*4882a593Smuzhiyun static int img_i2s_in_suspend(struct device *dev)
550*4882a593Smuzhiyun {
551*4882a593Smuzhiyun 	struct img_i2s_in *i2s = dev_get_drvdata(dev);
552*4882a593Smuzhiyun 	int i, ret;
553*4882a593Smuzhiyun 	u32 reg;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	if (pm_runtime_status_suspended(dev)) {
556*4882a593Smuzhiyun 		ret = img_i2s_in_runtime_resume(dev);
557*4882a593Smuzhiyun 		if (ret)
558*4882a593Smuzhiyun 			return ret;
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
562*4882a593Smuzhiyun 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
563*4882a593Smuzhiyun 		i2s->suspend_ch_ctl[i] = reg;
564*4882a593Smuzhiyun 	}
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	i2s->suspend_ctl = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	img_i2s_in_runtime_suspend(dev);
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	return 0;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun 
img_i2s_in_resume(struct device * dev)573*4882a593Smuzhiyun static int img_i2s_in_resume(struct device *dev)
574*4882a593Smuzhiyun {
575*4882a593Smuzhiyun 	struct img_i2s_in *i2s = dev_get_drvdata(dev);
576*4882a593Smuzhiyun 	int i, ret;
577*4882a593Smuzhiyun 	u32 reg;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	ret = img_i2s_in_runtime_resume(dev);
580*4882a593Smuzhiyun 	if (ret)
581*4882a593Smuzhiyun 		return ret;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
584*4882a593Smuzhiyun 		reg = i2s->suspend_ch_ctl[i];
585*4882a593Smuzhiyun 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
586*4882a593Smuzhiyun 	}
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	img_i2s_in_writel(i2s, i2s->suspend_ctl, IMG_I2S_IN_CTL);
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	if (pm_runtime_status_suspended(dev))
591*4882a593Smuzhiyun 		img_i2s_in_runtime_suspend(dev);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	return 0;
594*4882a593Smuzhiyun }
595*4882a593Smuzhiyun #endif
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun static const struct of_device_id img_i2s_in_of_match[] = {
598*4882a593Smuzhiyun 	{ .compatible = "img,i2s-in" },
599*4882a593Smuzhiyun 	{}
600*4882a593Smuzhiyun };
601*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun static const struct dev_pm_ops img_i2s_in_pm_ops = {
604*4882a593Smuzhiyun 	SET_RUNTIME_PM_OPS(img_i2s_in_runtime_suspend,
605*4882a593Smuzhiyun 			   img_i2s_in_runtime_resume, NULL)
606*4882a593Smuzhiyun 	SET_SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume)
607*4882a593Smuzhiyun };
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun static struct platform_driver img_i2s_in_driver = {
610*4882a593Smuzhiyun 	.driver = {
611*4882a593Smuzhiyun 		.name = "img-i2s-in",
612*4882a593Smuzhiyun 		.of_match_table = img_i2s_in_of_match,
613*4882a593Smuzhiyun 		.pm = &img_i2s_in_pm_ops
614*4882a593Smuzhiyun 	},
615*4882a593Smuzhiyun 	.probe = img_i2s_in_probe,
616*4882a593Smuzhiyun 	.remove = img_i2s_in_dev_remove
617*4882a593Smuzhiyun };
618*4882a593Smuzhiyun module_platform_driver(img_i2s_in_driver);
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
621*4882a593Smuzhiyun MODULE_DESCRIPTION("IMG I2S Input Driver");
622*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
623