brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · 024d91c Raw
357 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// mix.c4//5// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>6 7/*8 *		    CTUn	MIXn9 *		    +------+	+------+10 * [SRC3 / SRC6] -> |CTU n0| ->	[MIX n0| ->11 * [SRC4 / SRC9] -> |CTU n1| ->	[MIX n1| ->12 * [SRC0 / SRC1] -> |CTU n2| ->	[MIX n2| ->13 * [SRC2 / SRC5] -> |CTU n3| ->	[MIX n3| ->14 *		    +------+	+------+15 *16 * ex)17 *	DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>;18 *	DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;19 *20 * MIX Volume21 *	amixer set "MIX",0  100%  // DAI0 Volume22 *	amixer set "MIX",1  100%  // DAI1 Volume23 *24 * Volume Ramp25 *	amixer set "MIX Ramp Up Rate"   "0.125 dB/1 step"26 *	amixer set "MIX Ramp Down Rate" "4 dB/1 step"27 *	amixer set "MIX Ramp" on28 *	aplay xxx.wav &29 *	amixer set "MIX",0  80%  // DAI0 Volume Down30 *	amixer set "MIX",1 100%  // DAI1 Volume Up31 */32 33#include "rsnd.h"34 35#define MIX_NAME_SIZE	1636#define MIX_NAME "mix"37 38struct rsnd_mix {39	struct rsnd_mod mod;40	struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */41	struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */42	struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */43	struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */44	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */45	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */46	struct rsnd_kctrl_cfg_s rdw;	/* Ramp Rate Down */47	u32 flags;48};49 50#define ONCE_KCTRL_INITIALIZED		(1 << 0)51#define HAS_VOLA			(1 << 1)52#define HAS_VOLB			(1 << 2)53#define HAS_VOLC			(1 << 3)54#define HAS_VOLD			(1 << 4)55 56#define VOL_MAX				0x3ff57 58#define rsnd_mod_to_mix(_mod)	\59	container_of((_mod), struct rsnd_mix, mod)60 61#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)62#define rsnd_mix_nr(priv) ((priv)->mix_nr)63#define for_each_rsnd_mix(pos, priv, i)					\64	for ((i) = 0;							\65	     ((i) < rsnd_mix_nr(priv)) &&				\66		     ((pos) = (struct rsnd_mix *)(priv)->mix + i);	\67	     i++)68 69static void rsnd_mix_activation(struct rsnd_mod *mod)70{71	rsnd_mod_write(mod, MIX_SWRSR, 0);72	rsnd_mod_write(mod, MIX_SWRSR, 1);73}74 75static void rsnd_mix_halt(struct rsnd_mod *mod)76{77	rsnd_mod_write(mod, MIX_MIXIR, 1);78	rsnd_mod_write(mod, MIX_SWRSR, 0);79}80 81#define rsnd_mix_get_vol(mix, X) \82	rsnd_flags_has(mix, HAS_VOL##X) ? \83		(VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 084static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,85				      struct rsnd_mod *mod)86{87	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);88	struct device *dev = rsnd_priv_to_dev(priv);89	struct rsnd_mix *mix = rsnd_mod_to_mix(mod);90	u32 volA = rsnd_mix_get_vol(mix, A);91	u32 volB = rsnd_mix_get_vol(mix, B);92	u32 volC = rsnd_mix_get_vol(mix, C);93	u32 volD = rsnd_mix_get_vol(mix, D);94 95	dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n",96		volA, volB, volC, volD);97 98	rsnd_mod_write(mod, MIX_MDBAR, volA);99	rsnd_mod_write(mod, MIX_MDBBR, volB);100	rsnd_mod_write(mod, MIX_MDBCR, volC);101	rsnd_mod_write(mod, MIX_MDBDR, volD);102}103 104static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,105				 struct rsnd_mod *mod)106{107	struct rsnd_mix *mix = rsnd_mod_to_mix(mod);108 109	rsnd_mod_write(mod, MIX_MIXIR, 1);110 111	/* General Information */112	rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));113 114	/* volume step */115	rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren));116	rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 |117				       rsnd_kctrl_vals(mix->rdw));118 119	/* common volume parameter */120	rsnd_mix_volume_parameter(io, mod);121 122	rsnd_mod_write(mod, MIX_MIXIR, 0);123}124 125static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,126				  struct rsnd_mod *mod)127{128	/* Disable MIX dB setting */129	rsnd_mod_write(mod, MIX_MDBER, 0);130 131	/* common volume parameter */132	rsnd_mix_volume_parameter(io, mod);133 134	/* Enable MIX dB setting */135	rsnd_mod_write(mod, MIX_MDBER, 1);136}137 138static int rsnd_mix_probe_(struct rsnd_mod *mod,139			   struct rsnd_dai_stream *io,140			   struct rsnd_priv *priv)141{142	return rsnd_cmd_attach(io, rsnd_mod_id(mod));143}144 145static int rsnd_mix_init(struct rsnd_mod *mod,146			 struct rsnd_dai_stream *io,147			 struct rsnd_priv *priv)148{149	int ret;150 151	ret = rsnd_mod_power_on(mod);152	if (ret < 0)153		return ret;154 155	rsnd_mix_activation(mod);156 157	rsnd_mix_volume_init(io, mod);158 159	rsnd_mix_volume_update(io, mod);160 161	return 0;162}163 164static int rsnd_mix_quit(struct rsnd_mod *mod,165			 struct rsnd_dai_stream *io,166			 struct rsnd_priv *priv)167{168	rsnd_mix_halt(mod);169 170	rsnd_mod_power_off(mod);171 172	return 0;173}174 175static int rsnd_mix_pcm_new(struct rsnd_mod *mod,176			    struct rsnd_dai_stream *io,177			    struct snd_soc_pcm_runtime *rtd)178{179	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);180	struct device *dev = rsnd_priv_to_dev(priv);181	struct rsnd_mix *mix = rsnd_mod_to_mix(mod);182	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);183	struct rsnd_kctrl_cfg_s *volume;184	int ret;185 186	switch (rsnd_mod_id(src_mod)) {187	case 3:188	case 6:	/* MDBAR */189		volume = &mix->volumeA;190		rsnd_flags_set(mix, HAS_VOLA);191		break;192	case 4:193	case 9:	/* MDBBR */194		volume = &mix->volumeB;195		rsnd_flags_set(mix, HAS_VOLB);196		break;197	case 0:198	case 1:	/* MDBCR */199		volume = &mix->volumeC;200		rsnd_flags_set(mix, HAS_VOLC);201		break;202	case 2:203	case 5:	/* MDBDR */204		volume = &mix->volumeD;205		rsnd_flags_set(mix, HAS_VOLD);206		break;207	default:208		dev_err(dev, "unknown SRC is connected\n");209		return -EINVAL;210	}211 212	/* Volume */213	ret = rsnd_kctrl_new_s(mod, io, rtd,214			       "MIX Playback Volume",215			       rsnd_kctrl_accept_anytime,216			       rsnd_mix_volume_update,217			       volume, VOL_MAX);218	if (ret < 0)219		return ret;220	rsnd_kctrl_vals(*volume) = VOL_MAX;221 222	if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED))223		return ret;224 225	/* Ramp */226	ret = rsnd_kctrl_new_s(mod, io, rtd,227			       "MIX Ramp Switch",228			       rsnd_kctrl_accept_anytime,229			       rsnd_mix_volume_update,230			       &mix->ren, 1);231	if (ret < 0)232		return ret;233 234	ret = rsnd_kctrl_new_e(mod, io, rtd,235			       "MIX Ramp Up Rate",236			       rsnd_kctrl_accept_anytime,237			       rsnd_mix_volume_update,238			       &mix->rup,239			       volume_ramp_rate,240			       VOLUME_RAMP_MAX_MIX);241	if (ret < 0)242		return ret;243 244	ret = rsnd_kctrl_new_e(mod, io, rtd,245			       "MIX Ramp Down Rate",246			       rsnd_kctrl_accept_anytime,247			       rsnd_mix_volume_update,248			       &mix->rdw,249			       volume_ramp_rate,250			       VOLUME_RAMP_MAX_MIX);251 252	rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED);253 254	return ret;255}256 257#ifdef CONFIG_DEBUG_FS258static void rsnd_mix_debug_info(struct seq_file *m,259				struct rsnd_dai_stream *io,260				struct rsnd_mod *mod)261{262	rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,263				  0xd00 + rsnd_mod_id(mod) * 0x40, 0x30);264}265#define DEBUG_INFO .debug_info = rsnd_mix_debug_info266#else267#define DEBUG_INFO268#endif269 270static struct rsnd_mod_ops rsnd_mix_ops = {271	.name		= MIX_NAME,272	.probe		= rsnd_mix_probe_,273	.init		= rsnd_mix_init,274	.quit		= rsnd_mix_quit,275	.pcm_new	= rsnd_mix_pcm_new,276	.get_status	= rsnd_mod_get_status,277	DEBUG_INFO278};279 280struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)281{282	if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))283		id = 0;284 285	return rsnd_mod_get(rsnd_mix_get(priv, id));286}287 288int rsnd_mix_probe(struct rsnd_priv *priv)289{290	struct device_node *node;291	struct device_node *np;292	struct device *dev = rsnd_priv_to_dev(priv);293	struct rsnd_mix *mix;294	struct clk *clk;295	char name[MIX_NAME_SIZE];296	int i, nr, ret;297 298	node = rsnd_mix_of_node(priv);299	if (!node)300		return 0; /* not used is not error */301 302	nr = of_get_child_count(node);303	if (!nr) {304		ret = -EINVAL;305		goto rsnd_mix_probe_done;306	}307 308	mix	= devm_kcalloc(dev, nr, sizeof(*mix), GFP_KERNEL);309	if (!mix) {310		ret = -ENOMEM;311		goto rsnd_mix_probe_done;312	}313 314	priv->mix_nr	= nr;315	priv->mix	= mix;316 317	i = 0;318	ret = 0;319	for_each_child_of_node(node, np) {320		mix = rsnd_mix_get(priv, i);321 322		snprintf(name, MIX_NAME_SIZE, "%s.%d",323			 MIX_NAME, i);324 325		clk = devm_clk_get(dev, name);326		if (IS_ERR(clk)) {327			ret = PTR_ERR(clk);328			of_node_put(np);329			goto rsnd_mix_probe_done;330		}331 332		ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,333				    clk, RSND_MOD_MIX, i);334		if (ret) {335			of_node_put(np);336			goto rsnd_mix_probe_done;337		}338 339		i++;340	}341 342rsnd_mix_probe_done:343	of_node_put(node);344 345	return ret;346}347 348void rsnd_mix_remove(struct rsnd_priv *priv)349{350	struct rsnd_mix *mix;351	int i;352 353	for_each_rsnd_mix(mix, priv, i) {354		rsnd_mod_quit(rsnd_mod_get(mix));355	}356}357