xref: /linux/sound/soc/renesas/rcar/dvc.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
1*c087a94bSLad Prabhakar // SPDX-License-Identifier: GPL-2.0
2*c087a94bSLad Prabhakar //
3*c087a94bSLad Prabhakar // Renesas R-Car DVC support
4*c087a94bSLad Prabhakar //
5*c087a94bSLad Prabhakar // Copyright (C) 2014 Renesas Solutions Corp.
6*c087a94bSLad Prabhakar // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
7*c087a94bSLad Prabhakar 
8*c087a94bSLad Prabhakar /*
9*c087a94bSLad Prabhakar  * Playback Volume
10*c087a94bSLad Prabhakar  *	amixer set "DVC Out" 100%
11*c087a94bSLad Prabhakar  *
12*c087a94bSLad Prabhakar  * Capture Volume
13*c087a94bSLad Prabhakar  *	amixer set "DVC In" 100%
14*c087a94bSLad Prabhakar  *
15*c087a94bSLad Prabhakar  * Playback Mute
16*c087a94bSLad Prabhakar  *	amixer set "DVC Out Mute" on
17*c087a94bSLad Prabhakar  *
18*c087a94bSLad Prabhakar  * Capture Mute
19*c087a94bSLad Prabhakar  *	amixer set "DVC In Mute" on
20*c087a94bSLad Prabhakar  *
21*c087a94bSLad Prabhakar  * Volume Ramp
22*c087a94bSLad Prabhakar  *	amixer set "DVC Out Ramp Up Rate"   "0.125 dB/64 steps"
23*c087a94bSLad Prabhakar  *	amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
24*c087a94bSLad Prabhakar  *	amixer set "DVC Out Ramp" on
25*c087a94bSLad Prabhakar  *	aplay xxx.wav &
26*c087a94bSLad Prabhakar  *	amixer set "DVC Out"  80%  // Volume Down
27*c087a94bSLad Prabhakar  *	amixer set "DVC Out" 100%  // Volume Up
28*c087a94bSLad Prabhakar  */
29*c087a94bSLad Prabhakar 
30*c087a94bSLad Prabhakar #include "rsnd.h"
31*c087a94bSLad Prabhakar 
32*c087a94bSLad Prabhakar #define RSND_DVC_NAME_SIZE	16
33*c087a94bSLad Prabhakar 
34*c087a94bSLad Prabhakar #define DVC_NAME "dvc"
35*c087a94bSLad Prabhakar 
36*c087a94bSLad Prabhakar struct rsnd_dvc {
37*c087a94bSLad Prabhakar 	struct rsnd_mod mod;
38*c087a94bSLad Prabhakar 	struct rsnd_kctrl_cfg_m volume;
39*c087a94bSLad Prabhakar 	struct rsnd_kctrl_cfg_m mute;
40*c087a94bSLad Prabhakar 	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */
41*c087a94bSLad Prabhakar 	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */
42*c087a94bSLad Prabhakar 	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
43*c087a94bSLad Prabhakar };
44*c087a94bSLad Prabhakar 
45*c087a94bSLad Prabhakar #define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
46*c087a94bSLad Prabhakar #define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
47*c087a94bSLad Prabhakar 
48*c087a94bSLad Prabhakar #define rsnd_mod_to_dvc(_mod)	\
49*c087a94bSLad Prabhakar 	container_of((_mod), struct rsnd_dvc, mod)
50*c087a94bSLad Prabhakar 
51*c087a94bSLad Prabhakar #define for_each_rsnd_dvc(pos, priv, i)				\
52*c087a94bSLad Prabhakar 	for ((i) = 0;						\
53*c087a94bSLad Prabhakar 	     ((i) < rsnd_dvc_nr(priv)) &&			\
54*c087a94bSLad Prabhakar 	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
55*c087a94bSLad Prabhakar 	     i++)
56*c087a94bSLad Prabhakar 
57*c087a94bSLad Prabhakar static void rsnd_dvc_activation(struct rsnd_mod *mod)
58*c087a94bSLad Prabhakar {
59*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_SWRSR, 0);
60*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_SWRSR, 1);
61*c087a94bSLad Prabhakar }
62*c087a94bSLad Prabhakar 
63*c087a94bSLad Prabhakar static void rsnd_dvc_halt(struct rsnd_mod *mod)
64*c087a94bSLad Prabhakar {
65*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_DVUIR, 1);
66*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_SWRSR, 0);
67*c087a94bSLad Prabhakar }
68*c087a94bSLad Prabhakar 
69*c087a94bSLad Prabhakar #define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \
70*c087a94bSLad Prabhakar 				 rsnd_kctrl_vals(dvc->rdown))
71*c087a94bSLad Prabhakar #define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13))
72*c087a94bSLad Prabhakar 
73*c087a94bSLad Prabhakar static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
74*c087a94bSLad Prabhakar 					      struct rsnd_mod *mod)
75*c087a94bSLad Prabhakar {
76*c087a94bSLad Prabhakar 	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
77*c087a94bSLad Prabhakar 	u32 val[RSND_MAX_CHANNELS];
78*c087a94bSLad Prabhakar 	int i;
79*c087a94bSLad Prabhakar 
80*c087a94bSLad Prabhakar 	/* Enable Ramp */
81*c087a94bSLad Prabhakar 	if (rsnd_kctrl_vals(dvc->ren))
82*c087a94bSLad Prabhakar 		for (i = 0; i < RSND_MAX_CHANNELS; i++)
83*c087a94bSLad Prabhakar 			val[i] = rsnd_kctrl_max(dvc->volume);
84*c087a94bSLad Prabhakar 	else
85*c087a94bSLad Prabhakar 		for (i = 0; i < RSND_MAX_CHANNELS; i++)
86*c087a94bSLad Prabhakar 			val[i] = rsnd_kctrl_valm(dvc->volume, i);
87*c087a94bSLad Prabhakar 
88*c087a94bSLad Prabhakar 	/* Enable Digital Volume */
89*c087a94bSLad Prabhakar 	for (i = 0; i < RSND_MAX_CHANNELS; i++)
90*c087a94bSLad Prabhakar 		rsnd_mod_write(mod, DVC_VOLxR(i), val[i]);
91*c087a94bSLad Prabhakar }
92*c087a94bSLad Prabhakar 
93*c087a94bSLad Prabhakar static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
94*c087a94bSLad Prabhakar 				 struct rsnd_mod *mod)
95*c087a94bSLad Prabhakar {
96*c087a94bSLad Prabhakar 	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
97*c087a94bSLad Prabhakar 	u32 adinr = 0;
98*c087a94bSLad Prabhakar 	u32 dvucr = 0;
99*c087a94bSLad Prabhakar 	u32 vrctr = 0;
100*c087a94bSLad Prabhakar 	u32 vrpdr = 0;
101*c087a94bSLad Prabhakar 	u32 vrdbr = 0;
102*c087a94bSLad Prabhakar 
103*c087a94bSLad Prabhakar 	adinr = rsnd_get_adinr_bit(mod, io) |
104*c087a94bSLad Prabhakar 		rsnd_runtime_channel_after_ctu(io);
105*c087a94bSLad Prabhakar 
106*c087a94bSLad Prabhakar 	/* Enable Digital Volume, Zero Cross Mute Mode */
107*c087a94bSLad Prabhakar 	dvucr |= 0x101;
108*c087a94bSLad Prabhakar 
109*c087a94bSLad Prabhakar 	/* Enable Ramp */
110*c087a94bSLad Prabhakar 	if (rsnd_kctrl_vals(dvc->ren)) {
111*c087a94bSLad Prabhakar 		dvucr |= 0x10;
112*c087a94bSLad Prabhakar 
113*c087a94bSLad Prabhakar 		/*
114*c087a94bSLad Prabhakar 		 * FIXME !!
115*c087a94bSLad Prabhakar 		 * use scale-downed Digital Volume
116*c087a94bSLad Prabhakar 		 * as Volume Ramp
117*c087a94bSLad Prabhakar 		 * 7F FFFF -> 3FF
118*c087a94bSLad Prabhakar 		 */
119*c087a94bSLad Prabhakar 		vrctr = 0xff;
120*c087a94bSLad Prabhakar 		vrpdr = rsnd_dvc_get_vrpdr(dvc);
121*c087a94bSLad Prabhakar 		vrdbr = rsnd_dvc_get_vrdbr(dvc);
122*c087a94bSLad Prabhakar 	}
123*c087a94bSLad Prabhakar 
124*c087a94bSLad Prabhakar 	/* Initialize operation */
125*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_DVUIR, 1);
126*c087a94bSLad Prabhakar 
127*c087a94bSLad Prabhakar 	/* General Information */
128*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_ADINR, adinr);
129*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_DVUCR, dvucr);
130*c087a94bSLad Prabhakar 
131*c087a94bSLad Prabhakar 	/* Volume Ramp Parameter */
132*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_VRCTR, vrctr);
133*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
134*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
135*c087a94bSLad Prabhakar 
136*c087a94bSLad Prabhakar 	/* Digital Volume Function Parameter */
137*c087a94bSLad Prabhakar 	rsnd_dvc_volume_parameter(io, mod);
138*c087a94bSLad Prabhakar 
139*c087a94bSLad Prabhakar 	/* cancel operation */
140*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_DVUIR, 0);
141*c087a94bSLad Prabhakar }
142*c087a94bSLad Prabhakar 
143*c087a94bSLad Prabhakar static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
144*c087a94bSLad Prabhakar 				   struct rsnd_mod *mod)
145*c087a94bSLad Prabhakar {
146*c087a94bSLad Prabhakar 	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
147*c087a94bSLad Prabhakar 	u32 zcmcr = 0;
148*c087a94bSLad Prabhakar 	u32 vrpdr = 0;
149*c087a94bSLad Prabhakar 	u32 vrdbr = 0;
150*c087a94bSLad Prabhakar 	int i;
151*c087a94bSLad Prabhakar 
152*c087a94bSLad Prabhakar 	for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++)
153*c087a94bSLad Prabhakar 		zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i;
154*c087a94bSLad Prabhakar 
155*c087a94bSLad Prabhakar 	if (rsnd_kctrl_vals(dvc->ren)) {
156*c087a94bSLad Prabhakar 		vrpdr = rsnd_dvc_get_vrpdr(dvc);
157*c087a94bSLad Prabhakar 		vrdbr = rsnd_dvc_get_vrdbr(dvc);
158*c087a94bSLad Prabhakar 	}
159*c087a94bSLad Prabhakar 
160*c087a94bSLad Prabhakar 	/* Disable DVC Register access */
161*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_DVUER, 0);
162*c087a94bSLad Prabhakar 
163*c087a94bSLad Prabhakar 	/* Zero Cross Mute Function */
164*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
165*c087a94bSLad Prabhakar 
166*c087a94bSLad Prabhakar 	/* Volume Ramp Function */
167*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
168*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
169*c087a94bSLad Prabhakar 	/* add DVC_VRWTR here */
170*c087a94bSLad Prabhakar 
171*c087a94bSLad Prabhakar 	/* Digital Volume Function Parameter */
172*c087a94bSLad Prabhakar 	rsnd_dvc_volume_parameter(io, mod);
173*c087a94bSLad Prabhakar 
174*c087a94bSLad Prabhakar 	/* Enable DVC Register access */
175*c087a94bSLad Prabhakar 	rsnd_mod_write(mod, DVC_DVUER, 1);
176*c087a94bSLad Prabhakar }
177*c087a94bSLad Prabhakar 
178*c087a94bSLad Prabhakar static int rsnd_dvc_probe_(struct rsnd_mod *mod,
179*c087a94bSLad Prabhakar 			   struct rsnd_dai_stream *io,
180*c087a94bSLad Prabhakar 			   struct rsnd_priv *priv)
181*c087a94bSLad Prabhakar {
182*c087a94bSLad Prabhakar 	return rsnd_cmd_attach(io, rsnd_mod_id(mod));
183*c087a94bSLad Prabhakar }
184*c087a94bSLad Prabhakar 
185*c087a94bSLad Prabhakar static int rsnd_dvc_init(struct rsnd_mod *mod,
186*c087a94bSLad Prabhakar 			 struct rsnd_dai_stream *io,
187*c087a94bSLad Prabhakar 			 struct rsnd_priv *priv)
188*c087a94bSLad Prabhakar {
189*c087a94bSLad Prabhakar 	int ret;
190*c087a94bSLad Prabhakar 
191*c087a94bSLad Prabhakar 	ret = rsnd_mod_power_on(mod);
192*c087a94bSLad Prabhakar 	if (ret < 0)
193*c087a94bSLad Prabhakar 		return ret;
194*c087a94bSLad Prabhakar 
195*c087a94bSLad Prabhakar 	rsnd_dvc_activation(mod);
196*c087a94bSLad Prabhakar 
197*c087a94bSLad Prabhakar 	rsnd_dvc_volume_init(io, mod);
198*c087a94bSLad Prabhakar 
199*c087a94bSLad Prabhakar 	rsnd_dvc_volume_update(io, mod);
200*c087a94bSLad Prabhakar 
201*c087a94bSLad Prabhakar 	return 0;
202*c087a94bSLad Prabhakar }
203*c087a94bSLad Prabhakar 
204*c087a94bSLad Prabhakar static int rsnd_dvc_quit(struct rsnd_mod *mod,
205*c087a94bSLad Prabhakar 			 struct rsnd_dai_stream *io,
206*c087a94bSLad Prabhakar 			 struct rsnd_priv *priv)
207*c087a94bSLad Prabhakar {
208*c087a94bSLad Prabhakar 	rsnd_dvc_halt(mod);
209*c087a94bSLad Prabhakar 
210*c087a94bSLad Prabhakar 	rsnd_mod_power_off(mod);
211*c087a94bSLad Prabhakar 
212*c087a94bSLad Prabhakar 	return 0;
213*c087a94bSLad Prabhakar }
214*c087a94bSLad Prabhakar 
215*c087a94bSLad Prabhakar static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
216*c087a94bSLad Prabhakar 			    struct rsnd_dai_stream *io,
217*c087a94bSLad Prabhakar 			    struct snd_soc_pcm_runtime *rtd)
218*c087a94bSLad Prabhakar {
219*c087a94bSLad Prabhakar 	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
220*c087a94bSLad Prabhakar 	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
221*c087a94bSLad Prabhakar 	int is_play = rsnd_io_is_play(io);
222*c087a94bSLad Prabhakar 	int channels = rsnd_rdai_channels_get(rdai);
223*c087a94bSLad Prabhakar 	int ret;
224*c087a94bSLad Prabhakar 
225*c087a94bSLad Prabhakar 	/* Volume */
226*c087a94bSLad Prabhakar 	ret = rsnd_kctrl_new_m(mod, io, rtd,
227*c087a94bSLad Prabhakar 			is_play ?
228*c087a94bSLad Prabhakar 			"DVC Out Playback Volume" : "DVC In Capture Volume",
229*c087a94bSLad Prabhakar 			rsnd_kctrl_accept_anytime,
230*c087a94bSLad Prabhakar 			rsnd_dvc_volume_update,
231*c087a94bSLad Prabhakar 			&dvc->volume, channels,
232*c087a94bSLad Prabhakar 			0x00800000 - 1);
233*c087a94bSLad Prabhakar 	if (ret < 0)
234*c087a94bSLad Prabhakar 		return ret;
235*c087a94bSLad Prabhakar 
236*c087a94bSLad Prabhakar 	/* Mute */
237*c087a94bSLad Prabhakar 	ret = rsnd_kctrl_new_m(mod, io, rtd,
238*c087a94bSLad Prabhakar 			is_play ?
239*c087a94bSLad Prabhakar 			"DVC Out Mute Switch" : "DVC In Mute Switch",
240*c087a94bSLad Prabhakar 			rsnd_kctrl_accept_anytime,
241*c087a94bSLad Prabhakar 			rsnd_dvc_volume_update,
242*c087a94bSLad Prabhakar 			&dvc->mute, channels,
243*c087a94bSLad Prabhakar 			1);
244*c087a94bSLad Prabhakar 	if (ret < 0)
245*c087a94bSLad Prabhakar 		return ret;
246*c087a94bSLad Prabhakar 
247*c087a94bSLad Prabhakar 	/* Ramp */
248*c087a94bSLad Prabhakar 	ret = rsnd_kctrl_new_s(mod, io, rtd,
249*c087a94bSLad Prabhakar 			is_play ?
250*c087a94bSLad Prabhakar 			"DVC Out Ramp Switch" : "DVC In Ramp Switch",
251*c087a94bSLad Prabhakar 			rsnd_kctrl_accept_anytime,
252*c087a94bSLad Prabhakar 			rsnd_dvc_volume_update,
253*c087a94bSLad Prabhakar 			&dvc->ren, 1);
254*c087a94bSLad Prabhakar 	if (ret < 0)
255*c087a94bSLad Prabhakar 		return ret;
256*c087a94bSLad Prabhakar 
257*c087a94bSLad Prabhakar 	ret = rsnd_kctrl_new_e(mod, io, rtd,
258*c087a94bSLad Prabhakar 			is_play ?
259*c087a94bSLad Prabhakar 			"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
260*c087a94bSLad Prabhakar 			rsnd_kctrl_accept_anytime,
261*c087a94bSLad Prabhakar 			rsnd_dvc_volume_update,
262*c087a94bSLad Prabhakar 			&dvc->rup,
263*c087a94bSLad Prabhakar 			volume_ramp_rate,
264*c087a94bSLad Prabhakar 			VOLUME_RAMP_MAX_DVC);
265*c087a94bSLad Prabhakar 	if (ret < 0)
266*c087a94bSLad Prabhakar 		return ret;
267*c087a94bSLad Prabhakar 
268*c087a94bSLad Prabhakar 	ret = rsnd_kctrl_new_e(mod, io, rtd,
269*c087a94bSLad Prabhakar 			is_play ?
270*c087a94bSLad Prabhakar 			"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
271*c087a94bSLad Prabhakar 			rsnd_kctrl_accept_anytime,
272*c087a94bSLad Prabhakar 			rsnd_dvc_volume_update,
273*c087a94bSLad Prabhakar 			&dvc->rdown,
274*c087a94bSLad Prabhakar 			volume_ramp_rate,
275*c087a94bSLad Prabhakar 			VOLUME_RAMP_MAX_DVC);
276*c087a94bSLad Prabhakar 
277*c087a94bSLad Prabhakar 	if (ret < 0)
278*c087a94bSLad Prabhakar 		return ret;
279*c087a94bSLad Prabhakar 
280*c087a94bSLad Prabhakar 	return 0;
281*c087a94bSLad Prabhakar }
282*c087a94bSLad Prabhakar 
283*c087a94bSLad Prabhakar static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
284*c087a94bSLad Prabhakar 					 struct rsnd_mod *mod)
285*c087a94bSLad Prabhakar {
286*c087a94bSLad Prabhakar 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
287*c087a94bSLad Prabhakar 
288*c087a94bSLad Prabhakar 	return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
289*c087a94bSLad Prabhakar 					DVC_NAME, mod, "tx");
290*c087a94bSLad Prabhakar }
291*c087a94bSLad Prabhakar 
292*c087a94bSLad Prabhakar #ifdef CONFIG_DEBUG_FS
293*c087a94bSLad Prabhakar static void rsnd_dvc_debug_info(struct seq_file *m,
294*c087a94bSLad Prabhakar 				struct rsnd_dai_stream *io,
295*c087a94bSLad Prabhakar 				struct rsnd_mod *mod)
296*c087a94bSLad Prabhakar {
297*c087a94bSLad Prabhakar 	rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
298*c087a94bSLad Prabhakar 				  0xe00 + rsnd_mod_id(mod) * 0x100, 0x60);
299*c087a94bSLad Prabhakar }
300*c087a94bSLad Prabhakar #define DEBUG_INFO .debug_info = rsnd_dvc_debug_info
301*c087a94bSLad Prabhakar #else
302*c087a94bSLad Prabhakar #define DEBUG_INFO
303*c087a94bSLad Prabhakar #endif
304*c087a94bSLad Prabhakar 
305*c087a94bSLad Prabhakar static struct rsnd_mod_ops rsnd_dvc_ops = {
306*c087a94bSLad Prabhakar 	.name		= DVC_NAME,
307*c087a94bSLad Prabhakar 	.dma_req	= rsnd_dvc_dma_req,
308*c087a94bSLad Prabhakar 	.probe		= rsnd_dvc_probe_,
309*c087a94bSLad Prabhakar 	.init		= rsnd_dvc_init,
310*c087a94bSLad Prabhakar 	.quit		= rsnd_dvc_quit,
311*c087a94bSLad Prabhakar 	.pcm_new	= rsnd_dvc_pcm_new,
312*c087a94bSLad Prabhakar 	.get_status	= rsnd_mod_get_status,
313*c087a94bSLad Prabhakar 	DEBUG_INFO
314*c087a94bSLad Prabhakar };
315*c087a94bSLad Prabhakar 
316*c087a94bSLad Prabhakar struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
317*c087a94bSLad Prabhakar {
318*c087a94bSLad Prabhakar 	if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
319*c087a94bSLad Prabhakar 		id = 0;
320*c087a94bSLad Prabhakar 
321*c087a94bSLad Prabhakar 	return rsnd_mod_get(rsnd_dvc_get(priv, id));
322*c087a94bSLad Prabhakar }
323*c087a94bSLad Prabhakar 
324*c087a94bSLad Prabhakar int rsnd_dvc_probe(struct rsnd_priv *priv)
325*c087a94bSLad Prabhakar {
326*c087a94bSLad Prabhakar 	struct device_node *node;
327*c087a94bSLad Prabhakar 	struct device_node *np;
328*c087a94bSLad Prabhakar 	struct device *dev = rsnd_priv_to_dev(priv);
329*c087a94bSLad Prabhakar 	struct rsnd_dvc *dvc;
330*c087a94bSLad Prabhakar 	struct clk *clk;
331*c087a94bSLad Prabhakar 	char name[RSND_DVC_NAME_SIZE];
332*c087a94bSLad Prabhakar 	int i, nr, ret;
333*c087a94bSLad Prabhakar 
334*c087a94bSLad Prabhakar 	node = rsnd_dvc_of_node(priv);
335*c087a94bSLad Prabhakar 	if (!node)
336*c087a94bSLad Prabhakar 		return 0; /* not used is not error */
337*c087a94bSLad Prabhakar 
338*c087a94bSLad Prabhakar 	nr = of_get_child_count(node);
339*c087a94bSLad Prabhakar 	if (!nr) {
340*c087a94bSLad Prabhakar 		ret = -EINVAL;
341*c087a94bSLad Prabhakar 		goto rsnd_dvc_probe_done;
342*c087a94bSLad Prabhakar 	}
343*c087a94bSLad Prabhakar 
344*c087a94bSLad Prabhakar 	dvc	= devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL);
345*c087a94bSLad Prabhakar 	if (!dvc) {
346*c087a94bSLad Prabhakar 		ret = -ENOMEM;
347*c087a94bSLad Prabhakar 		goto rsnd_dvc_probe_done;
348*c087a94bSLad Prabhakar 	}
349*c087a94bSLad Prabhakar 
350*c087a94bSLad Prabhakar 	priv->dvc_nr	= nr;
351*c087a94bSLad Prabhakar 	priv->dvc	= dvc;
352*c087a94bSLad Prabhakar 
353*c087a94bSLad Prabhakar 	i = 0;
354*c087a94bSLad Prabhakar 	ret = 0;
355*c087a94bSLad Prabhakar 	for_each_child_of_node(node, np) {
356*c087a94bSLad Prabhakar 		dvc = rsnd_dvc_get(priv, i);
357*c087a94bSLad Prabhakar 
358*c087a94bSLad Prabhakar 		snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
359*c087a94bSLad Prabhakar 			 DVC_NAME, i);
360*c087a94bSLad Prabhakar 
361*c087a94bSLad Prabhakar 		clk = devm_clk_get(dev, name);
362*c087a94bSLad Prabhakar 		if (IS_ERR(clk)) {
363*c087a94bSLad Prabhakar 			ret = PTR_ERR(clk);
364*c087a94bSLad Prabhakar 			of_node_put(np);
365*c087a94bSLad Prabhakar 			goto rsnd_dvc_probe_done;
366*c087a94bSLad Prabhakar 		}
367*c087a94bSLad Prabhakar 
368*c087a94bSLad Prabhakar 		ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
369*c087a94bSLad Prabhakar 				    clk, RSND_MOD_DVC, i);
370*c087a94bSLad Prabhakar 		if (ret) {
371*c087a94bSLad Prabhakar 			of_node_put(np);
372*c087a94bSLad Prabhakar 			goto rsnd_dvc_probe_done;
373*c087a94bSLad Prabhakar 		}
374*c087a94bSLad Prabhakar 
375*c087a94bSLad Prabhakar 		i++;
376*c087a94bSLad Prabhakar 	}
377*c087a94bSLad Prabhakar 
378*c087a94bSLad Prabhakar rsnd_dvc_probe_done:
379*c087a94bSLad Prabhakar 	of_node_put(node);
380*c087a94bSLad Prabhakar 
381*c087a94bSLad Prabhakar 	return ret;
382*c087a94bSLad Prabhakar }
383*c087a94bSLad Prabhakar 
384*c087a94bSLad Prabhakar void rsnd_dvc_remove(struct rsnd_priv *priv)
385*c087a94bSLad Prabhakar {
386*c087a94bSLad Prabhakar 	struct rsnd_dvc *dvc;
387*c087a94bSLad Prabhakar 	int i;
388*c087a94bSLad Prabhakar 
389*c087a94bSLad Prabhakar 	for_each_rsnd_dvc(dvc, priv, i) {
390*c087a94bSLad Prabhakar 		rsnd_mod_quit(rsnd_mod_get(dvc));
391*c087a94bSLad Prabhakar 	}
392*c087a94bSLad Prabhakar }
393