162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Renesas R-Car DVC support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2014 Renesas Solutions Corp.
662306a36Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Playback Volume
1062306a36Sopenharmony_ci *	amixer set "DVC Out" 100%
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Capture Volume
1362306a36Sopenharmony_ci *	amixer set "DVC In" 100%
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Playback Mute
1662306a36Sopenharmony_ci *	amixer set "DVC Out Mute" on
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Capture Mute
1962306a36Sopenharmony_ci *	amixer set "DVC In Mute" on
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Volume Ramp
2262306a36Sopenharmony_ci *	amixer set "DVC Out Ramp Up Rate"   "0.125 dB/64 steps"
2362306a36Sopenharmony_ci *	amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
2462306a36Sopenharmony_ci *	amixer set "DVC Out Ramp" on
2562306a36Sopenharmony_ci *	aplay xxx.wav &
2662306a36Sopenharmony_ci *	amixer set "DVC Out"  80%  // Volume Down
2762306a36Sopenharmony_ci *	amixer set "DVC Out" 100%  // Volume Up
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "rsnd.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define RSND_DVC_NAME_SIZE	16
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define DVC_NAME "dvc"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct rsnd_dvc {
3762306a36Sopenharmony_ci	struct rsnd_mod mod;
3862306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_m volume;
3962306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_m mute;
4062306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */
4162306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */
4262306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
4662306a36Sopenharmony_ci#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define rsnd_mod_to_dvc(_mod)	\
4962306a36Sopenharmony_ci	container_of((_mod), struct rsnd_dvc, mod)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define for_each_rsnd_dvc(pos, priv, i)				\
5262306a36Sopenharmony_ci	for ((i) = 0;						\
5362306a36Sopenharmony_ci	     ((i) < rsnd_dvc_nr(priv)) &&			\
5462306a36Sopenharmony_ci	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
5562306a36Sopenharmony_ci	     i++)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void rsnd_dvc_activation(struct rsnd_mod *mod)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_SWRSR, 0);
6062306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_SWRSR, 1);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void rsnd_dvc_halt(struct rsnd_mod *mod)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUIR, 1);
6662306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_SWRSR, 0);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \
7062306a36Sopenharmony_ci				 rsnd_kctrl_vals(dvc->rdown))
7162306a36Sopenharmony_ci#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13))
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
7462306a36Sopenharmony_ci					      struct rsnd_mod *mod)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
7762306a36Sopenharmony_ci	u32 val[RSND_MAX_CHANNELS];
7862306a36Sopenharmony_ci	int i;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Enable Ramp */
8162306a36Sopenharmony_ci	if (rsnd_kctrl_vals(dvc->ren))
8262306a36Sopenharmony_ci		for (i = 0; i < RSND_MAX_CHANNELS; i++)
8362306a36Sopenharmony_ci			val[i] = rsnd_kctrl_max(dvc->volume);
8462306a36Sopenharmony_ci	else
8562306a36Sopenharmony_ci		for (i = 0; i < RSND_MAX_CHANNELS; i++)
8662306a36Sopenharmony_ci			val[i] = rsnd_kctrl_valm(dvc->volume, i);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Enable Digital Volume */
8962306a36Sopenharmony_ci	for (i = 0; i < RSND_MAX_CHANNELS; i++)
9062306a36Sopenharmony_ci		rsnd_mod_write(mod, DVC_VOLxR(i), val[i]);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
9462306a36Sopenharmony_ci				 struct rsnd_mod *mod)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
9762306a36Sopenharmony_ci	u32 adinr = 0;
9862306a36Sopenharmony_ci	u32 dvucr = 0;
9962306a36Sopenharmony_ci	u32 vrctr = 0;
10062306a36Sopenharmony_ci	u32 vrpdr = 0;
10162306a36Sopenharmony_ci	u32 vrdbr = 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	adinr = rsnd_get_adinr_bit(mod, io) |
10462306a36Sopenharmony_ci		rsnd_runtime_channel_after_ctu(io);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Enable Digital Volume, Zero Cross Mute Mode */
10762306a36Sopenharmony_ci	dvucr |= 0x101;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* Enable Ramp */
11062306a36Sopenharmony_ci	if (rsnd_kctrl_vals(dvc->ren)) {
11162306a36Sopenharmony_ci		dvucr |= 0x10;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		/*
11462306a36Sopenharmony_ci		 * FIXME !!
11562306a36Sopenharmony_ci		 * use scale-downed Digital Volume
11662306a36Sopenharmony_ci		 * as Volume Ramp
11762306a36Sopenharmony_ci		 * 7F FFFF -> 3FF
11862306a36Sopenharmony_ci		 */
11962306a36Sopenharmony_ci		vrctr = 0xff;
12062306a36Sopenharmony_ci		vrpdr = rsnd_dvc_get_vrpdr(dvc);
12162306a36Sopenharmony_ci		vrdbr = rsnd_dvc_get_vrdbr(dvc);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Initialize operation */
12562306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUIR, 1);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* General Information */
12862306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_ADINR, adinr);
12962306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUCR, dvucr);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Volume Ramp Parameter */
13262306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRCTR, vrctr);
13362306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
13462306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Digital Volume Function Parameter */
13762306a36Sopenharmony_ci	rsnd_dvc_volume_parameter(io, mod);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* cancel operation */
14062306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUIR, 0);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
14462306a36Sopenharmony_ci				   struct rsnd_mod *mod)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
14762306a36Sopenharmony_ci	u32 zcmcr = 0;
14862306a36Sopenharmony_ci	u32 vrpdr = 0;
14962306a36Sopenharmony_ci	u32 vrdbr = 0;
15062306a36Sopenharmony_ci	int i;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++)
15362306a36Sopenharmony_ci		zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (rsnd_kctrl_vals(dvc->ren)) {
15662306a36Sopenharmony_ci		vrpdr = rsnd_dvc_get_vrpdr(dvc);
15762306a36Sopenharmony_ci		vrdbr = rsnd_dvc_get_vrdbr(dvc);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Disable DVC Register access */
16162306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUER, 0);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Zero Cross Mute Function */
16462306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Volume Ramp Function */
16762306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
16862306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
16962306a36Sopenharmony_ci	/* add DVC_VRWTR here */
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* Digital Volume Function Parameter */
17262306a36Sopenharmony_ci	rsnd_dvc_volume_parameter(io, mod);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Enable DVC Register access */
17562306a36Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUER, 1);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int rsnd_dvc_probe_(struct rsnd_mod *mod,
17962306a36Sopenharmony_ci			   struct rsnd_dai_stream *io,
18062306a36Sopenharmony_ci			   struct rsnd_priv *priv)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return rsnd_cmd_attach(io, rsnd_mod_id(mod));
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int rsnd_dvc_init(struct rsnd_mod *mod,
18662306a36Sopenharmony_ci			 struct rsnd_dai_stream *io,
18762306a36Sopenharmony_ci			 struct rsnd_priv *priv)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	int ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = rsnd_mod_power_on(mod);
19262306a36Sopenharmony_ci	if (ret < 0)
19362306a36Sopenharmony_ci		return ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	rsnd_dvc_activation(mod);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	rsnd_dvc_volume_init(io, mod);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	rsnd_dvc_volume_update(io, mod);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int rsnd_dvc_quit(struct rsnd_mod *mod,
20562306a36Sopenharmony_ci			 struct rsnd_dai_stream *io,
20662306a36Sopenharmony_ci			 struct rsnd_priv *priv)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	rsnd_dvc_halt(mod);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	rsnd_mod_power_off(mod);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return 0;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
21662306a36Sopenharmony_ci			    struct rsnd_dai_stream *io,
21762306a36Sopenharmony_ci			    struct snd_soc_pcm_runtime *rtd)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
22062306a36Sopenharmony_ci	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
22162306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
22262306a36Sopenharmony_ci	int channels = rsnd_rdai_channels_get(rdai);
22362306a36Sopenharmony_ci	int ret;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Volume */
22662306a36Sopenharmony_ci	ret = rsnd_kctrl_new_m(mod, io, rtd,
22762306a36Sopenharmony_ci			is_play ?
22862306a36Sopenharmony_ci			"DVC Out Playback Volume" : "DVC In Capture Volume",
22962306a36Sopenharmony_ci			rsnd_kctrl_accept_anytime,
23062306a36Sopenharmony_ci			rsnd_dvc_volume_update,
23162306a36Sopenharmony_ci			&dvc->volume, channels,
23262306a36Sopenharmony_ci			0x00800000 - 1);
23362306a36Sopenharmony_ci	if (ret < 0)
23462306a36Sopenharmony_ci		return ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Mute */
23762306a36Sopenharmony_ci	ret = rsnd_kctrl_new_m(mod, io, rtd,
23862306a36Sopenharmony_ci			is_play ?
23962306a36Sopenharmony_ci			"DVC Out Mute Switch" : "DVC In Mute Switch",
24062306a36Sopenharmony_ci			rsnd_kctrl_accept_anytime,
24162306a36Sopenharmony_ci			rsnd_dvc_volume_update,
24262306a36Sopenharmony_ci			&dvc->mute, channels,
24362306a36Sopenharmony_ci			1);
24462306a36Sopenharmony_ci	if (ret < 0)
24562306a36Sopenharmony_ci		return ret;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Ramp */
24862306a36Sopenharmony_ci	ret = rsnd_kctrl_new_s(mod, io, rtd,
24962306a36Sopenharmony_ci			is_play ?
25062306a36Sopenharmony_ci			"DVC Out Ramp Switch" : "DVC In Ramp Switch",
25162306a36Sopenharmony_ci			rsnd_kctrl_accept_anytime,
25262306a36Sopenharmony_ci			rsnd_dvc_volume_update,
25362306a36Sopenharmony_ci			&dvc->ren, 1);
25462306a36Sopenharmony_ci	if (ret < 0)
25562306a36Sopenharmony_ci		return ret;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ret = rsnd_kctrl_new_e(mod, io, rtd,
25862306a36Sopenharmony_ci			is_play ?
25962306a36Sopenharmony_ci			"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
26062306a36Sopenharmony_ci			rsnd_kctrl_accept_anytime,
26162306a36Sopenharmony_ci			rsnd_dvc_volume_update,
26262306a36Sopenharmony_ci			&dvc->rup,
26362306a36Sopenharmony_ci			volume_ramp_rate,
26462306a36Sopenharmony_ci			VOLUME_RAMP_MAX_DVC);
26562306a36Sopenharmony_ci	if (ret < 0)
26662306a36Sopenharmony_ci		return ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ret = rsnd_kctrl_new_e(mod, io, rtd,
26962306a36Sopenharmony_ci			is_play ?
27062306a36Sopenharmony_ci			"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
27162306a36Sopenharmony_ci			rsnd_kctrl_accept_anytime,
27262306a36Sopenharmony_ci			rsnd_dvc_volume_update,
27362306a36Sopenharmony_ci			&dvc->rdown,
27462306a36Sopenharmony_ci			volume_ramp_rate,
27562306a36Sopenharmony_ci			VOLUME_RAMP_MAX_DVC);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (ret < 0)
27862306a36Sopenharmony_ci		return ret;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
28462306a36Sopenharmony_ci					 struct rsnd_mod *mod)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
28962306a36Sopenharmony_ci					DVC_NAME, mod, "tx");
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
29362306a36Sopenharmony_cistatic void rsnd_dvc_debug_info(struct seq_file *m,
29462306a36Sopenharmony_ci				struct rsnd_dai_stream *io,
29562306a36Sopenharmony_ci				struct rsnd_mod *mod)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
29862306a36Sopenharmony_ci				  0xe00 + rsnd_mod_id(mod) * 0x100, 0x60);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci#define DEBUG_INFO .debug_info = rsnd_dvc_debug_info
30162306a36Sopenharmony_ci#else
30262306a36Sopenharmony_ci#define DEBUG_INFO
30362306a36Sopenharmony_ci#endif
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_dvc_ops = {
30662306a36Sopenharmony_ci	.name		= DVC_NAME,
30762306a36Sopenharmony_ci	.dma_req	= rsnd_dvc_dma_req,
30862306a36Sopenharmony_ci	.probe		= rsnd_dvc_probe_,
30962306a36Sopenharmony_ci	.init		= rsnd_dvc_init,
31062306a36Sopenharmony_ci	.quit		= rsnd_dvc_quit,
31162306a36Sopenharmony_ci	.pcm_new	= rsnd_dvc_pcm_new,
31262306a36Sopenharmony_ci	.get_status	= rsnd_mod_get_status,
31362306a36Sopenharmony_ci	DEBUG_INFO
31462306a36Sopenharmony_ci};
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistruct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
31962306a36Sopenharmony_ci		id = 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return rsnd_mod_get(rsnd_dvc_get(priv, id));
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ciint rsnd_dvc_probe(struct rsnd_priv *priv)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct device_node *node;
32762306a36Sopenharmony_ci	struct device_node *np;
32862306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
32962306a36Sopenharmony_ci	struct rsnd_dvc *dvc;
33062306a36Sopenharmony_ci	struct clk *clk;
33162306a36Sopenharmony_ci	char name[RSND_DVC_NAME_SIZE];
33262306a36Sopenharmony_ci	int i, nr, ret;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* This driver doesn't support Gen1 at this point */
33562306a36Sopenharmony_ci	if (rsnd_is_gen1(priv))
33662306a36Sopenharmony_ci		return 0;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	node = rsnd_dvc_of_node(priv);
33962306a36Sopenharmony_ci	if (!node)
34062306a36Sopenharmony_ci		return 0; /* not used is not error */
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	nr = of_get_child_count(node);
34362306a36Sopenharmony_ci	if (!nr) {
34462306a36Sopenharmony_ci		ret = -EINVAL;
34562306a36Sopenharmony_ci		goto rsnd_dvc_probe_done;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	dvc	= devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL);
34962306a36Sopenharmony_ci	if (!dvc) {
35062306a36Sopenharmony_ci		ret = -ENOMEM;
35162306a36Sopenharmony_ci		goto rsnd_dvc_probe_done;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	priv->dvc_nr	= nr;
35562306a36Sopenharmony_ci	priv->dvc	= dvc;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	i = 0;
35862306a36Sopenharmony_ci	ret = 0;
35962306a36Sopenharmony_ci	for_each_child_of_node(node, np) {
36062306a36Sopenharmony_ci		dvc = rsnd_dvc_get(priv, i);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
36362306a36Sopenharmony_ci			 DVC_NAME, i);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		clk = devm_clk_get(dev, name);
36662306a36Sopenharmony_ci		if (IS_ERR(clk)) {
36762306a36Sopenharmony_ci			ret = PTR_ERR(clk);
36862306a36Sopenharmony_ci			of_node_put(np);
36962306a36Sopenharmony_ci			goto rsnd_dvc_probe_done;
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
37362306a36Sopenharmony_ci				    clk, RSND_MOD_DVC, i);
37462306a36Sopenharmony_ci		if (ret) {
37562306a36Sopenharmony_ci			of_node_put(np);
37662306a36Sopenharmony_ci			goto rsnd_dvc_probe_done;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		i++;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cirsnd_dvc_probe_done:
38362306a36Sopenharmony_ci	of_node_put(node);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return ret;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_civoid rsnd_dvc_remove(struct rsnd_priv *priv)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct rsnd_dvc *dvc;
39162306a36Sopenharmony_ci	int i;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	for_each_rsnd_dvc(dvc, priv, i) {
39462306a36Sopenharmony_ci		rsnd_mod_quit(rsnd_mod_get(dvc));
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci}
397