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