18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Renesas R-Car DVC support
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Renesas Solutions Corp.
68c2ecf20Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * Playback Volume
108c2ecf20Sopenharmony_ci *	amixer set "DVC Out" 100%
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Capture Volume
138c2ecf20Sopenharmony_ci *	amixer set "DVC In" 100%
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Playback Mute
168c2ecf20Sopenharmony_ci *	amixer set "DVC Out Mute" on
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Capture Mute
198c2ecf20Sopenharmony_ci *	amixer set "DVC In Mute" on
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Volume Ramp
228c2ecf20Sopenharmony_ci *	amixer set "DVC Out Ramp Up Rate"   "0.125 dB/64 steps"
238c2ecf20Sopenharmony_ci *	amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
248c2ecf20Sopenharmony_ci *	amixer set "DVC Out Ramp" on
258c2ecf20Sopenharmony_ci *	aplay xxx.wav &
268c2ecf20Sopenharmony_ci *	amixer set "DVC Out"  80%  // Volume Down
278c2ecf20Sopenharmony_ci *	amixer set "DVC Out" 100%  // Volume Up
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "rsnd.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define RSND_DVC_NAME_SIZE	16
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define DVC_NAME "dvc"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct rsnd_dvc {
378c2ecf20Sopenharmony_ci	struct rsnd_mod mod;
388c2ecf20Sopenharmony_ci	struct rsnd_kctrl_cfg_m volume;
398c2ecf20Sopenharmony_ci	struct rsnd_kctrl_cfg_m mute;
408c2ecf20Sopenharmony_ci	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */
418c2ecf20Sopenharmony_ci	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */
428c2ecf20Sopenharmony_ci	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
468c2ecf20Sopenharmony_ci#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define rsnd_mod_to_dvc(_mod)	\
498c2ecf20Sopenharmony_ci	container_of((_mod), struct rsnd_dvc, mod)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define for_each_rsnd_dvc(pos, priv, i)				\
528c2ecf20Sopenharmony_ci	for ((i) = 0;						\
538c2ecf20Sopenharmony_ci	     ((i) < rsnd_dvc_nr(priv)) &&			\
548c2ecf20Sopenharmony_ci	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
558c2ecf20Sopenharmony_ci	     i++)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void rsnd_dvc_activation(struct rsnd_mod *mod)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_SWRSR, 0);
608c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_SWRSR, 1);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void rsnd_dvc_halt(struct rsnd_mod *mod)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUIR, 1);
668c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_SWRSR, 0);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \
708c2ecf20Sopenharmony_ci				 rsnd_kctrl_vals(dvc->rdown))
718c2ecf20Sopenharmony_ci#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13))
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
748c2ecf20Sopenharmony_ci					      struct rsnd_mod *mod)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
778c2ecf20Sopenharmony_ci	u32 val[RSND_MAX_CHANNELS];
788c2ecf20Sopenharmony_ci	int i;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Enable Ramp */
818c2ecf20Sopenharmony_ci	if (rsnd_kctrl_vals(dvc->ren))
828c2ecf20Sopenharmony_ci		for (i = 0; i < RSND_MAX_CHANNELS; i++)
838c2ecf20Sopenharmony_ci			val[i] = rsnd_kctrl_max(dvc->volume);
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		for (i = 0; i < RSND_MAX_CHANNELS; i++)
868c2ecf20Sopenharmony_ci			val[i] = rsnd_kctrl_valm(dvc->volume, i);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Enable Digital Volume */
898c2ecf20Sopenharmony_ci	for (i = 0; i < RSND_MAX_CHANNELS; i++)
908c2ecf20Sopenharmony_ci		rsnd_mod_write(mod, DVC_VOLxR(i), val[i]);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
948c2ecf20Sopenharmony_ci				 struct rsnd_mod *mod)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
978c2ecf20Sopenharmony_ci	u32 adinr = 0;
988c2ecf20Sopenharmony_ci	u32 dvucr = 0;
998c2ecf20Sopenharmony_ci	u32 vrctr = 0;
1008c2ecf20Sopenharmony_ci	u32 vrpdr = 0;
1018c2ecf20Sopenharmony_ci	u32 vrdbr = 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	adinr = rsnd_get_adinr_bit(mod, io) |
1048c2ecf20Sopenharmony_ci		rsnd_runtime_channel_after_ctu(io);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Enable Digital Volume, Zero Cross Mute Mode */
1078c2ecf20Sopenharmony_ci	dvucr |= 0x101;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Enable Ramp */
1108c2ecf20Sopenharmony_ci	if (rsnd_kctrl_vals(dvc->ren)) {
1118c2ecf20Sopenharmony_ci		dvucr |= 0x10;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		/*
1148c2ecf20Sopenharmony_ci		 * FIXME !!
1158c2ecf20Sopenharmony_ci		 * use scale-downed Digital Volume
1168c2ecf20Sopenharmony_ci		 * as Volume Ramp
1178c2ecf20Sopenharmony_ci		 * 7F FFFF -> 3FF
1188c2ecf20Sopenharmony_ci		 */
1198c2ecf20Sopenharmony_ci		vrctr = 0xff;
1208c2ecf20Sopenharmony_ci		vrpdr = rsnd_dvc_get_vrpdr(dvc);
1218c2ecf20Sopenharmony_ci		vrdbr = rsnd_dvc_get_vrdbr(dvc);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Initialize operation */
1258c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUIR, 1);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* General Information */
1288c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_ADINR, adinr);
1298c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUCR, dvucr);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Volume Ramp Parameter */
1328c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRCTR, vrctr);
1338c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
1348c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Digital Volume Function Parameter */
1378c2ecf20Sopenharmony_ci	rsnd_dvc_volume_parameter(io, mod);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* cancel operation */
1408c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUIR, 0);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
1448c2ecf20Sopenharmony_ci				   struct rsnd_mod *mod)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
1478c2ecf20Sopenharmony_ci	u32 zcmcr = 0;
1488c2ecf20Sopenharmony_ci	u32 vrpdr = 0;
1498c2ecf20Sopenharmony_ci	u32 vrdbr = 0;
1508c2ecf20Sopenharmony_ci	int i;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++)
1538c2ecf20Sopenharmony_ci		zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (rsnd_kctrl_vals(dvc->ren)) {
1568c2ecf20Sopenharmony_ci		vrpdr = rsnd_dvc_get_vrpdr(dvc);
1578c2ecf20Sopenharmony_ci		vrdbr = rsnd_dvc_get_vrdbr(dvc);
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* Disable DVC Register access */
1618c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUER, 0);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Zero Cross Mute Function */
1648c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Volume Ramp Function */
1678c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
1688c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
1698c2ecf20Sopenharmony_ci	/* add DVC_VRWTR here */
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Digital Volume Function Parameter */
1728c2ecf20Sopenharmony_ci	rsnd_dvc_volume_parameter(io, mod);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Enable DVC Register access */
1758c2ecf20Sopenharmony_ci	rsnd_mod_write(mod, DVC_DVUER, 1);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int rsnd_dvc_probe_(struct rsnd_mod *mod,
1798c2ecf20Sopenharmony_ci			   struct rsnd_dai_stream *io,
1808c2ecf20Sopenharmony_ci			   struct rsnd_priv *priv)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	return rsnd_cmd_attach(io, rsnd_mod_id(mod));
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int rsnd_dvc_init(struct rsnd_mod *mod,
1868c2ecf20Sopenharmony_ci			 struct rsnd_dai_stream *io,
1878c2ecf20Sopenharmony_ci			 struct rsnd_priv *priv)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ret = rsnd_mod_power_on(mod);
1928c2ecf20Sopenharmony_ci	if (ret < 0)
1938c2ecf20Sopenharmony_ci		return ret;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	rsnd_dvc_activation(mod);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	rsnd_dvc_volume_init(io, mod);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	rsnd_dvc_volume_update(io, mod);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int rsnd_dvc_quit(struct rsnd_mod *mod,
2058c2ecf20Sopenharmony_ci			 struct rsnd_dai_stream *io,
2068c2ecf20Sopenharmony_ci			 struct rsnd_priv *priv)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	rsnd_dvc_halt(mod);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	rsnd_mod_power_off(mod);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
2168c2ecf20Sopenharmony_ci			    struct rsnd_dai_stream *io,
2178c2ecf20Sopenharmony_ci			    struct snd_soc_pcm_runtime *rtd)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
2208c2ecf20Sopenharmony_ci	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
2218c2ecf20Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
2228c2ecf20Sopenharmony_ci	int channels = rsnd_rdai_channels_get(rdai);
2238c2ecf20Sopenharmony_ci	int ret;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Volume */
2268c2ecf20Sopenharmony_ci	ret = rsnd_kctrl_new_m(mod, io, rtd,
2278c2ecf20Sopenharmony_ci			is_play ?
2288c2ecf20Sopenharmony_ci			"DVC Out Playback Volume" : "DVC In Capture Volume",
2298c2ecf20Sopenharmony_ci			rsnd_kctrl_accept_anytime,
2308c2ecf20Sopenharmony_ci			rsnd_dvc_volume_update,
2318c2ecf20Sopenharmony_ci			&dvc->volume, channels,
2328c2ecf20Sopenharmony_ci			0x00800000 - 1);
2338c2ecf20Sopenharmony_ci	if (ret < 0)
2348c2ecf20Sopenharmony_ci		return ret;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Mute */
2378c2ecf20Sopenharmony_ci	ret = rsnd_kctrl_new_m(mod, io, rtd,
2388c2ecf20Sopenharmony_ci			is_play ?
2398c2ecf20Sopenharmony_ci			"DVC Out Mute Switch" : "DVC In Mute Switch",
2408c2ecf20Sopenharmony_ci			rsnd_kctrl_accept_anytime,
2418c2ecf20Sopenharmony_ci			rsnd_dvc_volume_update,
2428c2ecf20Sopenharmony_ci			&dvc->mute, channels,
2438c2ecf20Sopenharmony_ci			1);
2448c2ecf20Sopenharmony_ci	if (ret < 0)
2458c2ecf20Sopenharmony_ci		return ret;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Ramp */
2488c2ecf20Sopenharmony_ci	ret = rsnd_kctrl_new_s(mod, io, rtd,
2498c2ecf20Sopenharmony_ci			is_play ?
2508c2ecf20Sopenharmony_ci			"DVC Out Ramp Switch" : "DVC In Ramp Switch",
2518c2ecf20Sopenharmony_ci			rsnd_kctrl_accept_anytime,
2528c2ecf20Sopenharmony_ci			rsnd_dvc_volume_update,
2538c2ecf20Sopenharmony_ci			&dvc->ren, 1);
2548c2ecf20Sopenharmony_ci	if (ret < 0)
2558c2ecf20Sopenharmony_ci		return ret;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	ret = rsnd_kctrl_new_e(mod, io, rtd,
2588c2ecf20Sopenharmony_ci			is_play ?
2598c2ecf20Sopenharmony_ci			"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
2608c2ecf20Sopenharmony_ci			rsnd_kctrl_accept_anytime,
2618c2ecf20Sopenharmony_ci			rsnd_dvc_volume_update,
2628c2ecf20Sopenharmony_ci			&dvc->rup,
2638c2ecf20Sopenharmony_ci			volume_ramp_rate,
2648c2ecf20Sopenharmony_ci			VOLUME_RAMP_MAX_DVC);
2658c2ecf20Sopenharmony_ci	if (ret < 0)
2668c2ecf20Sopenharmony_ci		return ret;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	ret = rsnd_kctrl_new_e(mod, io, rtd,
2698c2ecf20Sopenharmony_ci			is_play ?
2708c2ecf20Sopenharmony_ci			"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
2718c2ecf20Sopenharmony_ci			rsnd_kctrl_accept_anytime,
2728c2ecf20Sopenharmony_ci			rsnd_dvc_volume_update,
2738c2ecf20Sopenharmony_ci			&dvc->rdown,
2748c2ecf20Sopenharmony_ci			volume_ramp_rate,
2758c2ecf20Sopenharmony_ci			VOLUME_RAMP_MAX_DVC);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (ret < 0)
2788c2ecf20Sopenharmony_ci		return ret;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
2848c2ecf20Sopenharmony_ci					 struct rsnd_mod *mod)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
2898c2ecf20Sopenharmony_ci					mod, "tx");
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic struct rsnd_mod_ops rsnd_dvc_ops = {
2938c2ecf20Sopenharmony_ci	.name		= DVC_NAME,
2948c2ecf20Sopenharmony_ci	.dma_req	= rsnd_dvc_dma_req,
2958c2ecf20Sopenharmony_ci	.probe		= rsnd_dvc_probe_,
2968c2ecf20Sopenharmony_ci	.init		= rsnd_dvc_init,
2978c2ecf20Sopenharmony_ci	.quit		= rsnd_dvc_quit,
2988c2ecf20Sopenharmony_ci	.pcm_new	= rsnd_dvc_pcm_new,
2998c2ecf20Sopenharmony_ci	.get_status	= rsnd_mod_get_status,
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistruct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
3058c2ecf20Sopenharmony_ci		id = 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return rsnd_mod_get(rsnd_dvc_get(priv, id));
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ciint rsnd_dvc_probe(struct rsnd_priv *priv)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct device_node *node;
3138c2ecf20Sopenharmony_ci	struct device_node *np;
3148c2ecf20Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
3158c2ecf20Sopenharmony_ci	struct rsnd_dvc *dvc;
3168c2ecf20Sopenharmony_ci	struct clk *clk;
3178c2ecf20Sopenharmony_ci	char name[RSND_DVC_NAME_SIZE];
3188c2ecf20Sopenharmony_ci	int i, nr, ret;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* This driver doesn't support Gen1 at this point */
3218c2ecf20Sopenharmony_ci	if (rsnd_is_gen1(priv))
3228c2ecf20Sopenharmony_ci		return 0;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	node = rsnd_dvc_of_node(priv);
3258c2ecf20Sopenharmony_ci	if (!node)
3268c2ecf20Sopenharmony_ci		return 0; /* not used is not error */
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	nr = of_get_child_count(node);
3298c2ecf20Sopenharmony_ci	if (!nr) {
3308c2ecf20Sopenharmony_ci		ret = -EINVAL;
3318c2ecf20Sopenharmony_ci		goto rsnd_dvc_probe_done;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	dvc	= devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL);
3358c2ecf20Sopenharmony_ci	if (!dvc) {
3368c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3378c2ecf20Sopenharmony_ci		goto rsnd_dvc_probe_done;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	priv->dvc_nr	= nr;
3418c2ecf20Sopenharmony_ci	priv->dvc	= dvc;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	i = 0;
3448c2ecf20Sopenharmony_ci	ret = 0;
3458c2ecf20Sopenharmony_ci	for_each_child_of_node(node, np) {
3468c2ecf20Sopenharmony_ci		dvc = rsnd_dvc_get(priv, i);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
3498c2ecf20Sopenharmony_ci			 DVC_NAME, i);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		clk = devm_clk_get(dev, name);
3528c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
3538c2ecf20Sopenharmony_ci			ret = PTR_ERR(clk);
3548c2ecf20Sopenharmony_ci			of_node_put(np);
3558c2ecf20Sopenharmony_ci			goto rsnd_dvc_probe_done;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
3598c2ecf20Sopenharmony_ci				    clk, RSND_MOD_DVC, i);
3608c2ecf20Sopenharmony_ci		if (ret) {
3618c2ecf20Sopenharmony_ci			of_node_put(np);
3628c2ecf20Sopenharmony_ci			goto rsnd_dvc_probe_done;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		i++;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cirsnd_dvc_probe_done:
3698c2ecf20Sopenharmony_ci	of_node_put(node);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return ret;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_civoid rsnd_dvc_remove(struct rsnd_priv *priv)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct rsnd_dvc *dvc;
3778c2ecf20Sopenharmony_ci	int i;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	for_each_rsnd_dvc(dvc, priv, i) {
3808c2ecf20Sopenharmony_ci		rsnd_mod_quit(rsnd_mod_get(dvc));
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci}
383