brintos

brintos / linux-shallow public Read only

0
0
Text · 16.5 KiB · 0f190ab Raw
776 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Helper routines for R-Car sound ADG.4//5//  Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>6#include <linux/clk-provider.h>7#include <linux/clkdev.h>8#include "rsnd.h"9 10#define CLKA	011#define CLKB	112#define CLKC	213#define CLKI	314#define CLKINMAX 415 16#define CLKOUT	017#define CLKOUT1	118#define CLKOUT2	219#define CLKOUT3	320#define CLKOUTMAX 421 22#define BRRx_MASK(x) (0x3FF & x)23 24static struct rsnd_mod_ops adg_ops = {25	.name = "adg",26};27 28#define ADG_HZ_441	029#define ADG_HZ_48	130#define ADG_HZ_SIZE	231 32struct rsnd_adg {33	struct clk *clkin[CLKINMAX];34	struct clk *clkout[CLKOUTMAX];35	struct clk *null_clk;36	struct clk_onecell_data onecell;37	struct rsnd_mod mod;38	int clkin_rate[CLKINMAX];39	int clkin_size;40	int clkout_size;41	u32 ckr;42	u32 brga;43	u32 brgb;44 45	int brg_rate[ADG_HZ_SIZE]; /* BRGA / BRGB */46};47 48#define for_each_rsnd_clkin(pos, adg, i)	\49	for (i = 0;				\50	     (i < adg->clkin_size) &&		\51	     ((pos) = adg->clkin[i]);		\52	     i++)53#define for_each_rsnd_clkout(pos, adg, i)	\54	for (i = 0;				\55	     (i < adg->clkout_size) &&		\56	     ((pos) = adg->clkout[i]);	\57	     i++)58#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)59 60static const char * const clkin_name_gen4[] = {61	[CLKA]	= "clkin",62};63 64static const char * const clkin_name_gen2[] = {65	[CLKA]	= "clk_a",66	[CLKB]	= "clk_b",67	[CLKC]	= "clk_c",68	[CLKI]	= "clk_i",69};70 71static const char * const clkout_name_gen2[] = {72	[CLKOUT]  = "audio_clkout",73	[CLKOUT1] = "audio_clkout1",74	[CLKOUT2] = "audio_clkout2",75	[CLKOUT3] = "audio_clkout3",76};77 78static u32 rsnd_adg_calculate_brgx(unsigned long div)79{80	int i;81 82	if (!div)83		return 0;84 85	for (i = 3; i >= 0; i--) {86		int ratio = 2 << (i * 2);87		if (0 == (div % ratio))88			return (u32)((i << 8) | ((div / ratio) - 1));89	}90 91	return ~0;92}93 94static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)95{96	struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);97	int id = rsnd_mod_id(ssi_mod);98	int ws = id;99 100	if (rsnd_ssi_is_pin_sharing(io)) {101		switch (id) {102		case 1:103		case 2:104		case 9:105			ws = 0;106			break;107		case 4:108			ws = 3;109			break;110		case 8:111			ws = 7;112			break;113		}114	} else {115		/*116		 * SSI8 is not connected to ADG.117		 * Thus SSI9 is using ws = 8118		 */119		if (id == 9)120			ws = 8;121	}122 123	return (0x6 + ws) << 8;124}125 126static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,127				       struct rsnd_dai_stream *io,128				       unsigned int target_rate,129				       unsigned int *target_val,130				       unsigned int *target_en)131{132	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);133	struct device *dev = rsnd_priv_to_dev(priv);134	int sel;135	unsigned int val, en;136	unsigned int min, diff;137	unsigned int sel_rate[] = {138		adg->clkin_rate[CLKA],	/* 0000: CLKA */139		adg->clkin_rate[CLKB],	/* 0001: CLKB */140		adg->clkin_rate[CLKC],	/* 0010: CLKC */141		adg->brg_rate[ADG_HZ_441],	/* 0011: BRGA */142		adg->brg_rate[ADG_HZ_48],	/* 0100: BRGB */143	};144 145	min = ~0;146	val = 0;147	en = 0;148	for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {149		int idx = 0;150		int step = 2;151		int div;152 153		if (!sel_rate[sel])154			continue;155 156		for (div = 2; div <= 98304; div += step) {157			diff = abs(target_rate - sel_rate[sel] / div);158			if (min > diff) {159				val = (sel << 8) | idx;160				min = diff;161				en = 1 << (sel + 1); /* fixme */162			}163 164			/*165			 * step of 0_0000 / 0_0001 / 0_1101166			 * are out of order167			 */168			if ((idx > 2) && (idx % 2))169				step *= 2;170			if (idx == 0x1c) {171				div += step;172				step *= 2;173			}174			idx++;175		}176	}177 178	if (min == ~0) {179		dev_err(dev, "no Input clock\n");180		return;181	}182 183	*target_val = val;184	if (target_en)185		*target_en = en;186}187 188static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,189				       struct rsnd_dai_stream *io,190				       unsigned int in_rate,191				       unsigned int out_rate,192				       u32 *in, u32 *out, u32 *en)193{194	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);195	unsigned int target_rate;196	u32 *target_val;197	u32 _in;198	u32 _out;199	u32 _en;200 201	/* default = SSI WS */202	_in =203	_out = rsnd_adg_ssi_ws_timing_gen2(io);204 205	target_rate = 0;206	target_val = NULL;207	_en = 0;208	if (runtime->rate != in_rate) {209		target_rate = out_rate;210		target_val  = &_out;211	} else if (runtime->rate != out_rate) {212		target_rate = in_rate;213		target_val  = &_in;214	}215 216	if (target_rate)217		__rsnd_adg_get_timesel_ratio(priv, io,218					     target_rate,219					     target_val, &_en);220 221	if (in)222		*in = _in;223	if (out)224		*out = _out;225	if (en)226		*en = _en;227}228 229int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,230				 struct rsnd_dai_stream *io)231{232	struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);233	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);234	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);235	int id = rsnd_mod_id(cmd_mod);236	int shift = (id % 2) ? 16 : 0;237	u32 mask, val;238 239	rsnd_adg_get_timesel_ratio(priv, io,240				   rsnd_src_get_in_rate(priv, io),241				   rsnd_src_get_out_rate(priv, io),242				   NULL, &val, NULL);243 244	val  = val	<< shift;245	mask = 0x0f1f	<< shift;246 247	rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);248 249	return 0;250}251 252int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,253				  struct rsnd_dai_stream *io,254				  unsigned int in_rate,255				  unsigned int out_rate)256{257	struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);258	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);259	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);260	u32 in, out;261	u32 mask, en;262	int id = rsnd_mod_id(src_mod);263	int shift = (id % 2) ? 16 : 0;264 265	rsnd_mod_make_sure(src_mod, RSND_MOD_SRC);266 267	rsnd_adg_get_timesel_ratio(priv, io,268				   in_rate, out_rate,269				   &in, &out, &en);270 271	in   = in	<< shift;272	out  = out	<< shift;273	mask = 0x0f1f	<< shift;274 275	rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2),  mask, in);276	rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out);277 278	if (en)279		rsnd_mod_bset(adg_mod, DIV_EN, en, en);280 281	return 0;282}283 284static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)285{286	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);287	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);288	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);289	struct device *dev = rsnd_priv_to_dev(priv);290	int id = rsnd_mod_id(ssi_mod);291	int shift = (id % 4) * 8;292	u32 mask = 0xFF << shift;293 294	rsnd_mod_make_sure(ssi_mod, RSND_MOD_SSI);295 296	val = val << shift;297 298	/*299	 * SSI 8 is not connected to ADG.300	 * it works with SSI 7301	 */302	if (id == 8)303		return;304 305	rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val);306 307	dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val);308}309 310int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)311{312	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);313	struct clk *clk;314	int i;315	int sel_table[] = {316		[CLKA] = 0x1,317		[CLKB] = 0x2,318		[CLKC] = 0x3,319		[CLKI] = 0x0,320	};321 322	/*323	 * find suitable clock from324	 * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.325	 */326	for_each_rsnd_clkin(clk, adg, i)327		if (rate == adg->clkin_rate[i])328			return sel_table[i];329 330	/*331	 * find divided clock from BRGA/BRGB332	 */333	if (rate == adg->brg_rate[ADG_HZ_441])334		return 0x10;335 336	if (rate == adg->brg_rate[ADG_HZ_48])337		return 0x20;338 339	return -EIO;340}341 342int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)343{344	rsnd_adg_set_ssi_clk(ssi_mod, 0);345 346	return 0;347}348 349int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)350{351	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);352	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);353	struct device *dev = rsnd_priv_to_dev(priv);354	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);355	int data;356	u32 ckr = 0;357 358	data = rsnd_adg_clk_query(priv, rate);359	if (data < 0)360		return data;361 362	rsnd_adg_set_ssi_clk(ssi_mod, data);363 364	if (0 == (rate % 8000))365		ckr = 0x80000000; /* BRGB output = 48kHz */366 367	rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr);368 369	dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n",370		(ckr) ? 'B' : 'A',371		(ckr) ?	adg->brg_rate[ADG_HZ_48] :372			adg->brg_rate[ADG_HZ_441]);373 374	return 0;375}376 377void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)378{379	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);380	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);381	struct clk *clk;382	int i;383 384	if (enable) {385		rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);386		rsnd_mod_write(adg_mod, BRRA,  adg->brga);387		rsnd_mod_write(adg_mod, BRRB,  adg->brgb);388	}389 390	for_each_rsnd_clkin(clk, adg, i) {391		if (enable) {392			clk_prepare_enable(clk);393 394			/*395			 * We shouldn't use clk_get_rate() under396			 * atomic context. Let's keep it when397			 * rsnd_adg_clk_enable() was called398			 */399			adg->clkin_rate[i] = clk_get_rate(clk);400		} else {401			clk_disable_unprepare(clk);402		}403	}404}405 406static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv,407					    const char * const name,408					    const char *parent)409{410	struct device *dev = rsnd_priv_to_dev(priv);411	struct clk *clk;412 413	clk = clk_register_fixed_rate(dev, name, parent, 0, 0);414	if (IS_ERR_OR_NULL(clk)) {415		dev_err(dev, "create null clk error\n");416		return ERR_CAST(clk);417	}418 419	return clk;420}421 422static struct clk *rsnd_adg_null_clk_get(struct rsnd_priv *priv)423{424	struct rsnd_adg *adg = priv->adg;425 426	if (!adg->null_clk) {427		static const char * const name = "rsnd_adg_null";428 429		adg->null_clk = rsnd_adg_create_null_clk(priv, name, NULL);430	}431 432	return adg->null_clk;433}434 435static void rsnd_adg_null_clk_clean(struct rsnd_priv *priv)436{437	struct rsnd_adg *adg = priv->adg;438 439	if (adg->null_clk)440		clk_unregister_fixed_rate(adg->null_clk);441}442 443static int rsnd_adg_get_clkin(struct rsnd_priv *priv)444{445	struct rsnd_adg *adg = priv->adg;446	struct device *dev = rsnd_priv_to_dev(priv);447	struct clk *clk;448	const char * const *clkin_name;449	int clkin_size;450	int i;451 452	clkin_name = clkin_name_gen2;453	clkin_size = ARRAY_SIZE(clkin_name_gen2);454	if (rsnd_is_gen4(priv)) {455		clkin_name = clkin_name_gen4;456		clkin_size = ARRAY_SIZE(clkin_name_gen4);457	}458 459	for (i = 0; i < clkin_size; i++) {460		clk = devm_clk_get(dev, clkin_name[i]);461 462		if (IS_ERR_OR_NULL(clk))463			clk = rsnd_adg_null_clk_get(priv);464		if (IS_ERR_OR_NULL(clk))465			goto err;466 467		adg->clkin[i] = clk;468	}469 470	adg->clkin_size = clkin_size;471 472	return 0;473 474err:475	dev_err(dev, "adg clock IN get failed\n");476 477	rsnd_adg_null_clk_clean(priv);478 479	return -EIO;480}481 482static void rsnd_adg_unregister_clkout(struct rsnd_priv *priv)483{484	struct rsnd_adg *adg = priv->adg;485	struct clk *clk;486	int i;487 488	for_each_rsnd_clkout(clk, adg, i)489		clk_unregister_fixed_rate(clk);490}491 492static int rsnd_adg_get_clkout(struct rsnd_priv *priv)493{494	struct rsnd_adg *adg = priv->adg;495	struct clk *clk;496	struct device *dev = rsnd_priv_to_dev(priv);497	struct device_node *np = dev->of_node;498	struct property *prop;499	u32 ckr, brgx, brga, brgb;500	u32 req_rate[ADG_HZ_SIZE] = {};501	uint32_t count = 0;502	unsigned long req_Hz[ADG_HZ_SIZE];503	int clkout_size;504	int i, req_size;505	int approximate = 0;506	const char *parent_clk_name = NULL;507	const char * const *clkout_name;508	int brg_table[] = {509		[CLKA] = 0x0,510		[CLKB] = 0x1,511		[CLKC] = 0x4,512		[CLKI] = 0x2,513	};514 515	ckr = 0;516	brga = 0xff; /* default */517	brgb = 0xff; /* default */518 519	/*520	 * ADG supports BRRA/BRRB output only521	 * this means all clkout0/1/2/3 will be same rate522	 */523	prop = of_find_property(np, "clock-frequency", NULL);524	if (!prop)525		goto rsnd_adg_get_clkout_end;526 527	req_size = prop->length / sizeof(u32);528	if (req_size > ADG_HZ_SIZE) {529		dev_err(dev, "too many clock-frequency\n");530		return -EINVAL;531	}532 533	of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);534	req_Hz[ADG_HZ_48]  = 0;535	req_Hz[ADG_HZ_441] = 0;536	for (i = 0; i < req_size; i++) {537		if (0 == (req_rate[i] % 44100))538			req_Hz[ADG_HZ_441] = req_rate[i];539		if (0 == (req_rate[i] % 48000))540			req_Hz[ADG_HZ_48] = req_rate[i];541	}542 543	/*544	 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC545	 * have 44.1kHz or 48kHz base clocks for now.546	 *547	 * SSI itself can divide parent clock by 1/1 - 1/16548	 * see549	 *	rsnd_adg_ssi_clk_try_start()550	 *	rsnd_ssi_master_clk_start()551	 */552 553	/*554	 * [APPROXIMATE]555	 *556	 * clk_i (internal clock) can't create accurate rate, it will be approximate rate.557	 *558	 * <Note>559	 *560	 * clk_i needs x2 of required maximum rate.561	 * see562	 *	- Minimum division of BRRA/BRRB563	 *	- rsnd_ssi_clk_query()564	 *565	 * Sample Settings for TDM 8ch, 32bit width566	 *567	 *	8(ch) x 32(bit) x 44100(Hz) x 2<Note> = 22579200568	 *	8(ch) x 32(bit) x 48000(Hz) x 2<Note> = 24576000569	 *570	 *	clock-frequency = <22579200 24576000>;571	 */572	for_each_rsnd_clkin(clk, adg, i) {573		u32 rate, div;574 575		rate = clk_get_rate(clk);576 577		if (0 == rate) /* not used */578			continue;579 580		/* BRGA */581 582		if (i == CLKI)583			/* see [APPROXIMATE] */584			rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441];585		if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) {586			div = rate / req_Hz[ADG_HZ_441];587			brgx = rsnd_adg_calculate_brgx(div);588			if (BRRx_MASK(brgx) == brgx) {589				brga = brgx;590				adg->brg_rate[ADG_HZ_441] = rate / div;591				ckr |= brg_table[i] << 20;592				if (req_Hz[ADG_HZ_441])593					parent_clk_name = __clk_get_name(clk);594				if (i == CLKI)595					approximate = 1;596			}597		}598 599		/* BRGB */600 601		if (i == CLKI)602			/* see [APPROXIMATE] */603			rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48];604		if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) {605			div = rate / req_Hz[ADG_HZ_48];606			brgx = rsnd_adg_calculate_brgx(div);607			if (BRRx_MASK(brgx) == brgx) {608				brgb = brgx;609				adg->brg_rate[ADG_HZ_48] = rate / div;610				ckr |= brg_table[i] << 16;611				if (req_Hz[ADG_HZ_48])612					parent_clk_name = __clk_get_name(clk);613				if (i == CLKI)614					approximate = 1;615			}616		}617	}618 619	if (!(adg->brg_rate[ADG_HZ_48]  && req_Hz[ADG_HZ_48]) &&620	    !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441]))621		goto rsnd_adg_get_clkout_end;622 623	if (approximate)624		dev_info(dev, "It uses CLK_I as approximate rate");625 626	clkout_name = clkout_name_gen2;627	clkout_size = ARRAY_SIZE(clkout_name_gen2);628	if (rsnd_is_gen4(priv))629		clkout_size = 1; /* reuse clkout_name_gen2[] */630 631	/*632	 * ADG supports BRRA/BRRB output only.633	 * this means all clkout0/1/2/3 will be * same rate634	 */635 636	of_property_read_u32(np, "#clock-cells", &count);637	/*638	 * for clkout639	 */640	if (!count) {641		clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],642					      parent_clk_name, 0, req_rate[0]);643		if (IS_ERR_OR_NULL(clk))644			goto err;645 646		adg->clkout[CLKOUT] = clk;647		adg->clkout_size = 1;648		of_clk_add_provider(np, of_clk_src_simple_get, clk);649	}650	/*651	 * for clkout0/1/2/3652	 */653	else {654		for (i = 0; i < clkout_size; i++) {655			clk = clk_register_fixed_rate(dev, clkout_name[i],656						      parent_clk_name, 0,657						      req_rate[0]);658			if (IS_ERR_OR_NULL(clk))659				goto err;660 661			adg->clkout[i] = clk;662		}663		adg->onecell.clks	= adg->clkout;664		adg->onecell.clk_num	= clkout_size;665		adg->clkout_size	= clkout_size;666		of_clk_add_provider(np, of_clk_src_onecell_get,667				    &adg->onecell);668	}669 670rsnd_adg_get_clkout_end:671	adg->ckr = ckr;672	adg->brga = brga;673	adg->brgb = brgb;674 675	return 0;676 677err:678	dev_err(dev, "adg clock OUT get failed\n");679 680	rsnd_adg_unregister_clkout(priv);681 682	return -EIO;683}684 685#if defined(DEBUG) || defined(CONFIG_DEBUG_FS)686__printf(3, 4)687static void dbg_msg(struct device *dev, struct seq_file *m,688				   const char *fmt, ...)689{690	char msg[128];691	va_list args;692 693	va_start(args, fmt);694	vsnprintf(msg, sizeof(msg), fmt, args);695	va_end(args);696 697	if (m)698		seq_puts(m, msg);699	else700		dev_dbg(dev, "%s", msg);701}702 703void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m)704{705	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);706	struct device *dev = rsnd_priv_to_dev(priv);707	struct clk *clk;708	int i;709 710	for_each_rsnd_clkin(clk, adg, i)711		dbg_msg(dev, m, "%-18s : %pa : %ld\n",712			__clk_get_name(clk), clk, clk_get_rate(clk));713 714	dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",715		adg->ckr, adg->brga, adg->brgb);716	dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->brg_rate[ADG_HZ_441]);717	dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->brg_rate[ADG_HZ_48]);718 719	/*720	 * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start()721	 * by BRGCKR::BRGCKR_31722	 */723	for_each_rsnd_clkout(clk, adg, i)724		dbg_msg(dev, m, "%-18s : %pa : %ld\n",725			__clk_get_name(clk), clk, clk_get_rate(clk));726}727#else728#define rsnd_adg_clk_dbg_info(priv, m)729#endif730 731int rsnd_adg_probe(struct rsnd_priv *priv)732{733	struct rsnd_adg *adg;734	struct device *dev = rsnd_priv_to_dev(priv);735	int ret;736 737	adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);738	if (!adg)739		return -ENOMEM;740 741	ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,742		      NULL, 0, 0);743	if (ret)744		return ret;745 746	priv->adg = adg;747 748	ret = rsnd_adg_get_clkin(priv);749	if (ret)750		return ret;751 752	ret = rsnd_adg_get_clkout(priv);753	if (ret)754		return ret;755 756	rsnd_adg_clk_enable(priv);757	rsnd_adg_clk_dbg_info(priv, NULL);758 759	return 0;760}761 762void rsnd_adg_remove(struct rsnd_priv *priv)763{764	struct device *dev = rsnd_priv_to_dev(priv);765	struct device_node *np = dev->of_node;766 767	rsnd_adg_unregister_clkout(priv);768 769	of_clk_del_provider(np);770 771	rsnd_adg_clk_disable(priv);772 773	/* It should be called after rsnd_adg_clk_disable() */774	rsnd_adg_null_clk_clean(priv);775}776