xref: /OK3568_Linux_fs/kernel/sound/soc/img/img-i2s-out.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * IMG I2S output 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_OUT_TX_FIFO			0x0
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL				0x4
29*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_DATA_EN_MASK		BIT(24)
30*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK	0xffe000
31*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT	13
32*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_FRM_SIZE_MASK		BIT(8)
33*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_MASTER_MASK		BIT(6)
34*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_CLK_MASK		BIT(5)
35*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_CLK_EN_MASK		BIT(4)
36*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK	BIT(3)
37*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_BCLK_POL_MASK		BIT(2)
38*4882a593Smuzhiyun #define IMG_I2S_OUT_CTL_ME_MASK			BIT(0)
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define IMG_I2S_OUT_CH_CTL			0x4
41*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_CH_MASK		BIT(11)
42*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_LT_MASK		BIT(10)
43*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_FMT_MASK		0xf0
44*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT		4
45*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_JUST_MASK		BIT(3)
46*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK		BIT(1)
47*4882a593Smuzhiyun #define IMG_I2S_OUT_CHAN_CTL_ME_MASK		BIT(0)
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #define IMG_I2S_OUT_CH_STRIDE			0x20
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun struct img_i2s_out {
52*4882a593Smuzhiyun 	void __iomem *base;
53*4882a593Smuzhiyun 	struct clk *clk_sys;
54*4882a593Smuzhiyun 	struct clk *clk_ref;
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 	bool force_clk_active;
60*4882a593Smuzhiyun 	unsigned int active_channels;
61*4882a593Smuzhiyun 	struct reset_control *rst;
62*4882a593Smuzhiyun 	struct snd_soc_dai_driver dai_driver;
63*4882a593Smuzhiyun 	u32 suspend_ctl;
64*4882a593Smuzhiyun 	u32 *suspend_ch_ctl;
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun 
img_i2s_out_runtime_suspend(struct device * dev)67*4882a593Smuzhiyun static int img_i2s_out_runtime_suspend(struct device *dev)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	struct img_i2s_out *i2s = dev_get_drvdata(dev);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	clk_disable_unprepare(i2s->clk_ref);
72*4882a593Smuzhiyun 	clk_disable_unprepare(i2s->clk_sys);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	return 0;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
img_i2s_out_runtime_resume(struct device * dev)77*4882a593Smuzhiyun static int img_i2s_out_runtime_resume(struct device *dev)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	struct img_i2s_out *i2s = dev_get_drvdata(dev);
80*4882a593Smuzhiyun 	int ret;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	ret = clk_prepare_enable(i2s->clk_sys);
83*4882a593Smuzhiyun 	if (ret) {
84*4882a593Smuzhiyun 		dev_err(dev, "clk_enable failed: %d\n", ret);
85*4882a593Smuzhiyun 		return ret;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	ret = clk_prepare_enable(i2s->clk_ref);
89*4882a593Smuzhiyun 	if (ret) {
90*4882a593Smuzhiyun 		dev_err(dev, "clk_enable failed: %d\n", ret);
91*4882a593Smuzhiyun 		clk_disable_unprepare(i2s->clk_sys);
92*4882a593Smuzhiyun 		return ret;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	return 0;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
img_i2s_out_writel(struct img_i2s_out * i2s,u32 val,u32 reg)98*4882a593Smuzhiyun static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
99*4882a593Smuzhiyun 					u32 reg)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	writel(val, i2s->base + reg);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
img_i2s_out_readl(struct img_i2s_out * i2s,u32 reg)104*4882a593Smuzhiyun static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	return readl(i2s->base + reg);
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
img_i2s_out_ch_writel(struct img_i2s_out * i2s,u32 chan,u32 val,u32 reg)109*4882a593Smuzhiyun static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
110*4882a593Smuzhiyun 					u32 chan, u32 val, u32 reg)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
img_i2s_out_ch_readl(struct img_i2s_out * i2s,u32 chan,u32 reg)115*4882a593Smuzhiyun static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
116*4882a593Smuzhiyun 					u32 reg)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
img_i2s_out_ch_disable(struct img_i2s_out * i2s,u32 chan)121*4882a593Smuzhiyun static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	u32 reg;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
126*4882a593Smuzhiyun 	reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
127*4882a593Smuzhiyun 	img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
img_i2s_out_ch_enable(struct img_i2s_out * i2s,u32 chan)130*4882a593Smuzhiyun static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	u32 reg;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
135*4882a593Smuzhiyun 	reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
136*4882a593Smuzhiyun 	img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
img_i2s_out_disable(struct img_i2s_out * i2s)139*4882a593Smuzhiyun static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	u32 reg;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
144*4882a593Smuzhiyun 	reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
145*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
img_i2s_out_enable(struct img_i2s_out * i2s)148*4882a593Smuzhiyun static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	u32 reg;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
153*4882a593Smuzhiyun 	reg |= IMG_I2S_OUT_CTL_ME_MASK;
154*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun 
img_i2s_out_reset(struct img_i2s_out * i2s)157*4882a593Smuzhiyun static void img_i2s_out_reset(struct img_i2s_out *i2s)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 	int i;
160*4882a593Smuzhiyun 	u32 core_ctl, chan_ctl;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
163*4882a593Smuzhiyun 			~IMG_I2S_OUT_CTL_ME_MASK &
164*4882a593Smuzhiyun 			~IMG_I2S_OUT_CTL_DATA_EN_MASK;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (!i2s->force_clk_active)
167*4882a593Smuzhiyun 		core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
170*4882a593Smuzhiyun 			~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	reset_control_assert(i2s->rst);
173*4882a593Smuzhiyun 	reset_control_deassert(i2s->rst);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++)
176*4882a593Smuzhiyun 		img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
179*4882a593Smuzhiyun 		img_i2s_out_ch_enable(i2s, i);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
182*4882a593Smuzhiyun 	img_i2s_out_enable(i2s);
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
img_i2s_out_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)185*4882a593Smuzhiyun static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
186*4882a593Smuzhiyun 	struct snd_soc_dai *dai)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
189*4882a593Smuzhiyun 	u32 reg;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	switch (cmd) {
192*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
193*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
194*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
195*4882a593Smuzhiyun 		reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
196*4882a593Smuzhiyun 		if (!i2s->force_clk_active)
197*4882a593Smuzhiyun 			reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
198*4882a593Smuzhiyun 		reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
199*4882a593Smuzhiyun 		img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
200*4882a593Smuzhiyun 		break;
201*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
202*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
203*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
204*4882a593Smuzhiyun 		img_i2s_out_reset(i2s);
205*4882a593Smuzhiyun 		break;
206*4882a593Smuzhiyun 	default:
207*4882a593Smuzhiyun 		return -EINVAL;
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	return 0;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun 
img_i2s_out_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)213*4882a593Smuzhiyun static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
214*4882a593Smuzhiyun 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
217*4882a593Smuzhiyun 	unsigned int channels, i2s_channels;
218*4882a593Smuzhiyun 	long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
219*4882a593Smuzhiyun 	int i;
220*4882a593Smuzhiyun 	u32 reg, control_mask, control_set = 0;
221*4882a593Smuzhiyun 	snd_pcm_format_t format;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	rate = params_rate(params);
224*4882a593Smuzhiyun 	format = params_format(params);
225*4882a593Smuzhiyun 	channels = params_channels(params);
226*4882a593Smuzhiyun 	i2s_channels = channels / 2;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if (format != SNDRV_PCM_FORMAT_S32_LE)
229*4882a593Smuzhiyun 		return -EINVAL;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	if ((channels < 2) ||
232*4882a593Smuzhiyun 	    (channels > (i2s->max_i2s_chan * 2)) ||
233*4882a593Smuzhiyun 	    (channels % 2))
234*4882a593Smuzhiyun 		return -EINVAL;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
237*4882a593Smuzhiyun 	if (pre_div_a < 0)
238*4882a593Smuzhiyun 		return pre_div_a;
239*4882a593Smuzhiyun 	pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
240*4882a593Smuzhiyun 	if (pre_div_b < 0)
241*4882a593Smuzhiyun 		return pre_div_b;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	diff_a = abs((pre_div_a / 256) - rate);
244*4882a593Smuzhiyun 	diff_b = abs((pre_div_b / 384) - rate);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	/* If diffs are equal, use lower clock rate */
247*4882a593Smuzhiyun 	if (diff_a > diff_b)
248*4882a593Smuzhiyun 		clk_set_rate(i2s->clk_ref, pre_div_b);
249*4882a593Smuzhiyun 	else
250*4882a593Smuzhiyun 		clk_set_rate(i2s->clk_ref, pre_div_a);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	/*
253*4882a593Smuzhiyun 	 * Another driver (eg alsa machine driver) may have rejected the above
254*4882a593Smuzhiyun 	 * change. Get the current rate and set the register bit according to
255*4882a593Smuzhiyun 	 * the new minimum diff
256*4882a593Smuzhiyun 	 */
257*4882a593Smuzhiyun 	clk_rate = clk_get_rate(i2s->clk_ref);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	diff_a = abs((clk_rate / 256) - rate);
260*4882a593Smuzhiyun 	diff_b = abs((clk_rate / 384) - rate);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	if (diff_a > diff_b)
263*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	control_set |= ((i2s_channels - 1) <<
266*4882a593Smuzhiyun 		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
267*4882a593Smuzhiyun 		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
270*4882a593Smuzhiyun 		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	img_i2s_out_disable(i2s);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
275*4882a593Smuzhiyun 	reg = (reg & ~control_mask) | control_set;
276*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	for (i = 0; i < i2s_channels; i++)
279*4882a593Smuzhiyun 		img_i2s_out_ch_enable(i2s, i);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	for (; i < i2s->max_i2s_chan; i++)
282*4882a593Smuzhiyun 		img_i2s_out_ch_disable(i2s, i);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	img_i2s_out_enable(i2s);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	i2s->active_channels = i2s_channels;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	return 0;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
img_i2s_out_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)291*4882a593Smuzhiyun static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
294*4882a593Smuzhiyun 	int i, ret;
295*4882a593Smuzhiyun 	bool force_clk_active;
296*4882a593Smuzhiyun 	u32 chan_control_mask, control_mask, chan_control_set = 0;
297*4882a593Smuzhiyun 	u32 reg, control_set = 0;
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
300*4882a593Smuzhiyun 			SND_SOC_DAIFMT_CONT);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (force_clk_active)
303*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
306*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBM_CFM:
307*4882a593Smuzhiyun 		break;
308*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBS_CFS:
309*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
310*4882a593Smuzhiyun 		break;
311*4882a593Smuzhiyun 	default:
312*4882a593Smuzhiyun 		return -EINVAL;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
316*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_NB_NF:
317*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
318*4882a593Smuzhiyun 		break;
319*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_NB_IF:
320*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
321*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
322*4882a593Smuzhiyun 		break;
323*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_IB_NF:
324*4882a593Smuzhiyun 		break;
325*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_IB_IF:
326*4882a593Smuzhiyun 		control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
327*4882a593Smuzhiyun 		break;
328*4882a593Smuzhiyun 	default:
329*4882a593Smuzhiyun 		return -EINVAL;
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
333*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_I2S:
334*4882a593Smuzhiyun 		chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
335*4882a593Smuzhiyun 		break;
336*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_LEFT_J:
337*4882a593Smuzhiyun 		break;
338*4882a593Smuzhiyun 	default:
339*4882a593Smuzhiyun 		return -EINVAL;
340*4882a593Smuzhiyun 	}
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
343*4882a593Smuzhiyun 		       IMG_I2S_OUT_CTL_MASTER_MASK |
344*4882a593Smuzhiyun 		       IMG_I2S_OUT_CTL_BCLK_POL_MASK |
345*4882a593Smuzhiyun 		       IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	ret = pm_runtime_get_sync(i2s->dev);
350*4882a593Smuzhiyun 	if (ret < 0) {
351*4882a593Smuzhiyun 		pm_runtime_put_noidle(i2s->dev);
352*4882a593Smuzhiyun 		return ret;
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	img_i2s_out_disable(i2s);
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
358*4882a593Smuzhiyun 	reg = (reg & ~control_mask) | control_set;
359*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
362*4882a593Smuzhiyun 		img_i2s_out_ch_disable(i2s, i);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
365*4882a593Smuzhiyun 		reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
366*4882a593Smuzhiyun 		reg = (reg & ~chan_control_mask) | chan_control_set;
367*4882a593Smuzhiyun 		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	for (i = 0; i < i2s->active_channels; i++)
371*4882a593Smuzhiyun 		img_i2s_out_ch_enable(i2s, i);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	img_i2s_out_enable(i2s);
374*4882a593Smuzhiyun 	pm_runtime_put(i2s->dev);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	i2s->force_clk_active = force_clk_active;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	return 0;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
382*4882a593Smuzhiyun 	.trigger = img_i2s_out_trigger,
383*4882a593Smuzhiyun 	.hw_params = img_i2s_out_hw_params,
384*4882a593Smuzhiyun 	.set_fmt = img_i2s_out_set_fmt
385*4882a593Smuzhiyun };
386*4882a593Smuzhiyun 
img_i2s_out_dai_probe(struct snd_soc_dai * dai)387*4882a593Smuzhiyun static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	return 0;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun static const struct snd_soc_component_driver img_i2s_out_component = {
397*4882a593Smuzhiyun 	.name = "img-i2s-out"
398*4882a593Smuzhiyun };
399*4882a593Smuzhiyun 
img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream * st,struct snd_pcm_hw_params * params,struct dma_slave_config * sc)400*4882a593Smuzhiyun static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
401*4882a593Smuzhiyun 	struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	unsigned int i2s_channels = params_channels(params) / 2;
404*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = st->private_data;
405*4882a593Smuzhiyun 	struct snd_dmaengine_dai_dma_data *dma_data;
406*4882a593Smuzhiyun 	int ret;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	ret = snd_hwparams_to_dma_slave_config(st, params, sc);
411*4882a593Smuzhiyun 	if (ret)
412*4882a593Smuzhiyun 		return ret;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	sc->dst_addr = dma_data->addr;
415*4882a593Smuzhiyun 	sc->dst_addr_width = dma_data->addr_width;
416*4882a593Smuzhiyun 	sc->dst_maxburst = 4 * i2s_channels;
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	return 0;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
422*4882a593Smuzhiyun 	.prepare_slave_config = img_i2s_out_dma_prepare_slave_config
423*4882a593Smuzhiyun };
424*4882a593Smuzhiyun 
img_i2s_out_probe(struct platform_device * pdev)425*4882a593Smuzhiyun static int img_i2s_out_probe(struct platform_device *pdev)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct img_i2s_out *i2s;
428*4882a593Smuzhiyun 	struct resource *res;
429*4882a593Smuzhiyun 	void __iomem *base;
430*4882a593Smuzhiyun 	int i, ret;
431*4882a593Smuzhiyun 	unsigned int max_i2s_chan_pow_2;
432*4882a593Smuzhiyun 	u32 reg;
433*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
436*4882a593Smuzhiyun 	if (!i2s)
437*4882a593Smuzhiyun 		return -ENOMEM;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	platform_set_drvdata(pdev, i2s);
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	i2s->dev = &pdev->dev;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
444*4882a593Smuzhiyun 	base = devm_ioremap_resource(&pdev->dev, res);
445*4882a593Smuzhiyun 	if (IS_ERR(base))
446*4882a593Smuzhiyun 		return PTR_ERR(base);
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	i2s->base = base;
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
451*4882a593Smuzhiyun 			&i2s->max_i2s_chan)) {
452*4882a593Smuzhiyun 		dev_err(&pdev->dev, "No img,i2s-channels property\n");
453*4882a593Smuzhiyun 		return -EINVAL;
454*4882a593Smuzhiyun 	}
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
461*4882a593Smuzhiyun 	if (IS_ERR(i2s->rst)) {
462*4882a593Smuzhiyun 		if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
463*4882a593Smuzhiyun 			dev_err(&pdev->dev, "No top level reset found\n");
464*4882a593Smuzhiyun 		return PTR_ERR(i2s->rst);
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
468*4882a593Smuzhiyun 	if (IS_ERR(i2s->clk_sys)) {
469*4882a593Smuzhiyun 		if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
470*4882a593Smuzhiyun 			dev_err(dev, "Failed to acquire clock 'sys'\n");
471*4882a593Smuzhiyun 		return PTR_ERR(i2s->clk_sys);
472*4882a593Smuzhiyun 	}
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
475*4882a593Smuzhiyun 	if (IS_ERR(i2s->clk_ref)) {
476*4882a593Smuzhiyun 		if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
477*4882a593Smuzhiyun 			dev_err(dev, "Failed to acquire clock 'ref'\n");
478*4882a593Smuzhiyun 		return PTR_ERR(i2s->clk_ref);
479*4882a593Smuzhiyun 	}
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	i2s->suspend_ch_ctl = devm_kcalloc(dev,
482*4882a593Smuzhiyun 		i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
483*4882a593Smuzhiyun 	if (!i2s->suspend_ch_ctl)
484*4882a593Smuzhiyun 		return -ENOMEM;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	pm_runtime_enable(&pdev->dev);
487*4882a593Smuzhiyun 	if (!pm_runtime_enabled(&pdev->dev)) {
488*4882a593Smuzhiyun 		ret = img_i2s_out_runtime_resume(&pdev->dev);
489*4882a593Smuzhiyun 		if (ret)
490*4882a593Smuzhiyun 			goto err_pm_disable;
491*4882a593Smuzhiyun 	}
492*4882a593Smuzhiyun 	ret = pm_runtime_get_sync(&pdev->dev);
493*4882a593Smuzhiyun 	if (ret < 0) {
494*4882a593Smuzhiyun 		pm_runtime_put_noidle(&pdev->dev);
495*4882a593Smuzhiyun 		goto err_suspend;
496*4882a593Smuzhiyun 	}
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
499*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
502*4882a593Smuzhiyun 		IMG_I2S_OUT_CHAN_CTL_LT_MASK |
503*4882a593Smuzhiyun 		IMG_I2S_OUT_CHAN_CTL_CH_MASK |
504*4882a593Smuzhiyun 		(8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++)
507*4882a593Smuzhiyun 		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	img_i2s_out_reset(i2s);
510*4882a593Smuzhiyun 	pm_runtime_put(&pdev->dev);
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	i2s->active_channels = 1;
513*4882a593Smuzhiyun 	i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
514*4882a593Smuzhiyun 	i2s->dma_data.addr_width = 4;
515*4882a593Smuzhiyun 	i2s->dma_data.maxburst = 4;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	i2s->dai_driver.probe = img_i2s_out_dai_probe;
518*4882a593Smuzhiyun 	i2s->dai_driver.playback.channels_min = 2;
519*4882a593Smuzhiyun 	i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
520*4882a593Smuzhiyun 	i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
521*4882a593Smuzhiyun 	i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
522*4882a593Smuzhiyun 	i2s->dai_driver.ops = &img_i2s_out_dai_ops;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	ret = devm_snd_soc_register_component(&pdev->dev,
525*4882a593Smuzhiyun 			&img_i2s_out_component, &i2s->dai_driver, 1);
526*4882a593Smuzhiyun 	if (ret)
527*4882a593Smuzhiyun 		goto err_suspend;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
530*4882a593Smuzhiyun 			&img_i2s_out_dma_config, 0);
531*4882a593Smuzhiyun 	if (ret)
532*4882a593Smuzhiyun 		goto err_suspend;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	return 0;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun err_suspend:
537*4882a593Smuzhiyun 	if (!pm_runtime_status_suspended(&pdev->dev))
538*4882a593Smuzhiyun 		img_i2s_out_runtime_suspend(&pdev->dev);
539*4882a593Smuzhiyun err_pm_disable:
540*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	return ret;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
img_i2s_out_dev_remove(struct platform_device * pdev)545*4882a593Smuzhiyun static int img_i2s_out_dev_remove(struct platform_device *pdev)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
548*4882a593Smuzhiyun 	if (!pm_runtime_status_suspended(&pdev->dev))
549*4882a593Smuzhiyun 		img_i2s_out_runtime_suspend(&pdev->dev);
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	return 0;
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
img_i2s_out_suspend(struct device * dev)555*4882a593Smuzhiyun static int img_i2s_out_suspend(struct device *dev)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun 	struct img_i2s_out *i2s = dev_get_drvdata(dev);
558*4882a593Smuzhiyun 	int i, ret;
559*4882a593Smuzhiyun 	u32 reg;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	if (pm_runtime_status_suspended(dev)) {
562*4882a593Smuzhiyun 		ret = img_i2s_out_runtime_resume(dev);
563*4882a593Smuzhiyun 		if (ret)
564*4882a593Smuzhiyun 			return ret;
565*4882a593Smuzhiyun 	}
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
568*4882a593Smuzhiyun 		reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
569*4882a593Smuzhiyun 		i2s->suspend_ch_ctl[i] = reg;
570*4882a593Smuzhiyun 	}
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	img_i2s_out_runtime_suspend(dev);
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	return 0;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
img_i2s_out_resume(struct device * dev)579*4882a593Smuzhiyun static int img_i2s_out_resume(struct device *dev)
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun 	struct img_i2s_out *i2s = dev_get_drvdata(dev);
582*4882a593Smuzhiyun 	int i, ret;
583*4882a593Smuzhiyun 	u32 reg;
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	ret = img_i2s_out_runtime_resume(dev);
586*4882a593Smuzhiyun 	if (ret)
587*4882a593Smuzhiyun 		return ret;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	for (i = 0; i < i2s->max_i2s_chan; i++) {
590*4882a593Smuzhiyun 		reg = i2s->suspend_ch_ctl[i];
591*4882a593Smuzhiyun 		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
592*4882a593Smuzhiyun 	}
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL);
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 	if (pm_runtime_status_suspended(dev))
597*4882a593Smuzhiyun 		img_i2s_out_runtime_suspend(dev);
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	return 0;
600*4882a593Smuzhiyun }
601*4882a593Smuzhiyun #endif
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun static const struct of_device_id img_i2s_out_of_match[] = {
604*4882a593Smuzhiyun 	{ .compatible = "img,i2s-out" },
605*4882a593Smuzhiyun 	{}
606*4882a593Smuzhiyun };
607*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun static const struct dev_pm_ops img_i2s_out_pm_ops = {
610*4882a593Smuzhiyun 	SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend,
611*4882a593Smuzhiyun 			   img_i2s_out_runtime_resume, NULL)
612*4882a593Smuzhiyun 	SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume)
613*4882a593Smuzhiyun };
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun static struct platform_driver img_i2s_out_driver = {
616*4882a593Smuzhiyun 	.driver = {
617*4882a593Smuzhiyun 		.name = "img-i2s-out",
618*4882a593Smuzhiyun 		.of_match_table = img_i2s_out_of_match,
619*4882a593Smuzhiyun 		.pm = &img_i2s_out_pm_ops
620*4882a593Smuzhiyun 	},
621*4882a593Smuzhiyun 	.probe = img_i2s_out_probe,
622*4882a593Smuzhiyun 	.remove = img_i2s_out_dev_remove
623*4882a593Smuzhiyun };
624*4882a593Smuzhiyun module_platform_driver(img_i2s_out_driver);
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
627*4882a593Smuzhiyun MODULE_DESCRIPTION("IMG I2S Output Driver");
628*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
629