mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-10 14:43:54 +08:00
ASoC: rsnd: use mod base common method on CMD
Renesas sound needs many devices (SSI/SSIU/SRC/CTU/MIX/DVC/CMD/AudioDMAC/AudioDMACpp). SSI/SRC/CTU/MIX/DVC are implemented as module. SSI parent, SSIU are implemented as part of SSI CMD is implemented as part of CTU/MIX/DVC AudioDMAC/AudioDMACpp are implemented as part of SSI/SRC It is nice sense that these all devices are implemented as mod. This patch makes CMD mod base common method Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
497debaa80
commit
1b2ca0adf1
@ -1,4 +1,4 @@
|
|||||||
snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o
|
snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o cmd.o
|
||||||
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
|
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
|
||||||
|
|
||||||
snd-soc-rsrc-card-objs := rsrc-card.o
|
snd-soc-rsrc-card-objs := rsrc-card.o
|
||||||
|
153
sound/soc/sh/rcar/cmd.c
Normal file
153
sound/soc/sh/rcar/cmd.c
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Renesas R-Car CMD support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Renesas Solutions Corp.
|
||||||
|
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
#include "rsnd.h"
|
||||||
|
|
||||||
|
struct rsnd_cmd {
|
||||||
|
struct rsnd_mod mod;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CMD_NAME "cmd"
|
||||||
|
|
||||||
|
#define rsnd_cmd_nr(priv) ((priv)->cmd_nr)
|
||||||
|
#define for_each_rsnd_cmd(pos, priv, i) \
|
||||||
|
for ((i) = 0; \
|
||||||
|
((i) < rsnd_cmd_nr(priv)) && \
|
||||||
|
((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \
|
||||||
|
i++)
|
||||||
|
|
||||||
|
static int rsnd_cmd_init(struct rsnd_mod *mod,
|
||||||
|
struct rsnd_dai_stream *io,
|
||||||
|
struct rsnd_priv *priv)
|
||||||
|
{
|
||||||
|
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
|
||||||
|
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
|
||||||
|
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||||
|
struct device *dev = rsnd_priv_to_dev(priv);
|
||||||
|
u32 data;
|
||||||
|
|
||||||
|
if (!mix && !dvc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (mix) {
|
||||||
|
struct rsnd_dai *rdai;
|
||||||
|
int i;
|
||||||
|
u32 path[] = {
|
||||||
|
[0] = 0,
|
||||||
|
[1] = 1 << 0,
|
||||||
|
[2] = 0,
|
||||||
|
[3] = 0,
|
||||||
|
[4] = 0,
|
||||||
|
[5] = 1 << 8
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* it is assuming that integrater is well understanding about
|
||||||
|
* data path. Here doesn't check impossible connection,
|
||||||
|
* like src2 + src5
|
||||||
|
*/
|
||||||
|
data = 0;
|
||||||
|
for_each_rsnd_dai(rdai, priv, i) {
|
||||||
|
io = &rdai->playback;
|
||||||
|
if (mix == rsnd_io_to_mod_mix(io))
|
||||||
|
data |= path[rsnd_mod_id(src)];
|
||||||
|
|
||||||
|
io = &rdai->capture;
|
||||||
|
if (mix == rsnd_io_to_mod_mix(io))
|
||||||
|
data |= path[rsnd_mod_id(src)];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
u32 path[] = {
|
||||||
|
[0] = 0x30000,
|
||||||
|
[1] = 0x30001,
|
||||||
|
[2] = 0x40000,
|
||||||
|
[3] = 0x10000,
|
||||||
|
[4] = 0x20000,
|
||||||
|
[5] = 0x40100
|
||||||
|
};
|
||||||
|
|
||||||
|
data = path[rsnd_mod_id(src)];
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
|
||||||
|
|
||||||
|
rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
|
||||||
|
|
||||||
|
rsnd_mod_write(mod, CMD_CTRL, 0x10);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rsnd_mod_ops rsnd_cmd_ops = {
|
||||||
|
.name = CMD_NAME,
|
||||||
|
.init = rsnd_cmd_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
|
||||||
|
{
|
||||||
|
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||||
|
struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id);
|
||||||
|
|
||||||
|
return rsnd_dai_connect(mod, io, mod->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
|
||||||
|
{
|
||||||
|
if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
|
||||||
|
id = 0;
|
||||||
|
|
||||||
|
return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rsnd_cmd_probe(struct platform_device *pdev,
|
||||||
|
const struct rsnd_of_data *of_data,
|
||||||
|
struct rsnd_priv *priv)
|
||||||
|
{
|
||||||
|
struct device *dev = rsnd_priv_to_dev(priv);
|
||||||
|
struct rsnd_cmd *cmd;
|
||||||
|
int i, nr, ret;
|
||||||
|
|
||||||
|
/* This driver doesn't support Gen1 at this point */
|
||||||
|
if (rsnd_is_gen1(priv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* same number as DVC */
|
||||||
|
nr = priv->dvc_nr;
|
||||||
|
if (!nr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL);
|
||||||
|
if (!cmd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->cmd_nr = nr;
|
||||||
|
priv->cmd = cmd;
|
||||||
|
|
||||||
|
for_each_rsnd_cmd(cmd, priv, i) {
|
||||||
|
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
|
||||||
|
&rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsnd_cmd_remove(struct platform_device *pdev,
|
||||||
|
struct rsnd_priv *priv)
|
||||||
|
{
|
||||||
|
struct rsnd_cmd *cmd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for_each_rsnd_cmd(cmd, priv, i) {
|
||||||
|
rsnd_mod_quit(rsnd_mod_get(cmd));
|
||||||
|
}
|
||||||
|
}
|
@ -589,79 +589,6 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
|
|||||||
ret; \
|
ret; \
|
||||||
})
|
})
|
||||||
|
|
||||||
void rsnd_path_parse(struct rsnd_priv *priv,
|
|
||||||
struct rsnd_dai_stream *io)
|
|
||||||
{
|
|
||||||
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
|
|
||||||
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
|
|
||||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
|
||||||
struct rsnd_mod *cmd;
|
|
||||||
struct device *dev = rsnd_priv_to_dev(priv);
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
/* Gen1 is not supported */
|
|
||||||
if (rsnd_is_gen1(priv))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!mix && !dvc)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (mix) {
|
|
||||||
struct rsnd_dai *rdai;
|
|
||||||
int i;
|
|
||||||
u32 path[] = {
|
|
||||||
[0] = 0,
|
|
||||||
[1] = 1 << 0,
|
|
||||||
[2] = 0,
|
|
||||||
[3] = 0,
|
|
||||||
[4] = 0,
|
|
||||||
[5] = 1 << 8
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* it is assuming that integrater is well understanding about
|
|
||||||
* data path. Here doesn't check impossible connection,
|
|
||||||
* like src2 + src5
|
|
||||||
*/
|
|
||||||
data = 0;
|
|
||||||
for_each_rsnd_dai(rdai, priv, i) {
|
|
||||||
io = &rdai->playback;
|
|
||||||
if (mix == rsnd_io_to_mod_mix(io))
|
|
||||||
data |= path[rsnd_mod_id(src)];
|
|
||||||
|
|
||||||
io = &rdai->capture;
|
|
||||||
if (mix == rsnd_io_to_mod_mix(io))
|
|
||||||
data |= path[rsnd_mod_id(src)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We can't use ctu = rsnd_io_ctu() here.
|
|
||||||
* Since, ID of dvc/mix are 0 or 1 (= same as CMD number)
|
|
||||||
* but ctu IDs are 0 - 7 (= CTU00 - CTU13)
|
|
||||||
*/
|
|
||||||
cmd = mix;
|
|
||||||
} else {
|
|
||||||
u32 path[] = {
|
|
||||||
[0] = 0x30000,
|
|
||||||
[1] = 0x30001,
|
|
||||||
[2] = 0x40000,
|
|
||||||
[3] = 0x10000,
|
|
||||||
[4] = 0x20000,
|
|
||||||
[5] = 0x40100
|
|
||||||
};
|
|
||||||
|
|
||||||
data = path[rsnd_mod_id(src)];
|
|
||||||
|
|
||||||
cmd = dvc;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
|
|
||||||
|
|
||||||
rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
|
|
||||||
|
|
||||||
rsnd_mod_write(cmd, CMD_CTRL, 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rsnd_path_init(struct rsnd_priv *priv,
|
static int rsnd_path_init(struct rsnd_priv *priv,
|
||||||
struct rsnd_dai *rdai,
|
struct rsnd_dai *rdai,
|
||||||
struct rsnd_dai_stream *io)
|
struct rsnd_dai_stream *io)
|
||||||
@ -1208,6 +1135,7 @@ static int rsnd_probe(struct platform_device *pdev)
|
|||||||
rsnd_ctu_probe,
|
rsnd_ctu_probe,
|
||||||
rsnd_mix_probe,
|
rsnd_mix_probe,
|
||||||
rsnd_dvc_probe,
|
rsnd_dvc_probe,
|
||||||
|
rsnd_cmd_probe,
|
||||||
rsnd_adg_probe,
|
rsnd_adg_probe,
|
||||||
rsnd_dai_probe,
|
rsnd_dai_probe,
|
||||||
};
|
};
|
||||||
@ -1296,6 +1224,7 @@ static int rsnd_remove(struct platform_device *pdev)
|
|||||||
rsnd_ctu_remove,
|
rsnd_ctu_remove,
|
||||||
rsnd_mix_remove,
|
rsnd_mix_remove,
|
||||||
rsnd_dvc_remove,
|
rsnd_dvc_remove,
|
||||||
|
rsnd_cmd_remove,
|
||||||
};
|
};
|
||||||
int ret = 0, i;
|
int ret = 0, i;
|
||||||
|
|
||||||
|
@ -31,6 +31,13 @@ static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
|
|||||||
rsnd_mod_write(mod, CTU_CTUIR, enable);
|
rsnd_mod_write(mod, CTU_CTUIR, enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rsnd_ctu_probe_(struct rsnd_mod *mod,
|
||||||
|
struct rsnd_dai_stream *io,
|
||||||
|
struct rsnd_priv *priv)
|
||||||
|
{
|
||||||
|
return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
static int rsnd_ctu_init(struct rsnd_mod *mod,
|
static int rsnd_ctu_init(struct rsnd_mod *mod,
|
||||||
struct rsnd_dai_stream *io,
|
struct rsnd_dai_stream *io,
|
||||||
struct rsnd_priv *priv)
|
struct rsnd_priv *priv)
|
||||||
@ -57,6 +64,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
|
|||||||
|
|
||||||
static struct rsnd_mod_ops rsnd_ctu_ops = {
|
static struct rsnd_mod_ops rsnd_ctu_ops = {
|
||||||
.name = CTU_NAME,
|
.name = CTU_NAME,
|
||||||
|
.probe = rsnd_ctu_probe_,
|
||||||
.init = rsnd_ctu_init,
|
.init = rsnd_ctu_init,
|
||||||
.quit = rsnd_ctu_quit,
|
.quit = rsnd_ctu_quit,
|
||||||
};
|
};
|
||||||
|
@ -134,7 +134,14 @@ static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
|
|||||||
rsnd_mod_write(mod, DVC_DVUER, 1);
|
rsnd_mod_write(mod, DVC_DVUER, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
|
static int rsnd_dvc_probe_(struct rsnd_mod *mod,
|
||||||
|
struct rsnd_dai_stream *io,
|
||||||
|
struct rsnd_priv *priv)
|
||||||
|
{
|
||||||
|
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rsnd_dvc_remove_(struct rsnd_mod *mod,
|
||||||
struct rsnd_dai_stream *io,
|
struct rsnd_dai_stream *io,
|
||||||
struct rsnd_priv *priv)
|
struct rsnd_priv *priv)
|
||||||
{
|
{
|
||||||
@ -159,8 +166,6 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
|
|||||||
|
|
||||||
rsnd_dvc_initialize_lock(mod);
|
rsnd_dvc_initialize_lock(mod);
|
||||||
|
|
||||||
rsnd_path_parse(priv, io);
|
|
||||||
|
|
||||||
rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io));
|
rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io));
|
||||||
|
|
||||||
/* ch0/ch1 Volume */
|
/* ch0/ch1 Volume */
|
||||||
@ -269,7 +274,8 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
|
|||||||
static struct rsnd_mod_ops rsnd_dvc_ops = {
|
static struct rsnd_mod_ops rsnd_dvc_ops = {
|
||||||
.name = DVC_NAME,
|
.name = DVC_NAME,
|
||||||
.dma_req = rsnd_dvc_dma_req,
|
.dma_req = rsnd_dvc_dma_req,
|
||||||
.remove = rsnd_dvc_remove_gen2,
|
.probe = rsnd_dvc_probe_,
|
||||||
|
.remove = rsnd_dvc_remove_,
|
||||||
.init = rsnd_dvc_init,
|
.init = rsnd_dvc_init,
|
||||||
.quit = rsnd_dvc_quit,
|
.quit = rsnd_dvc_quit,
|
||||||
.start = rsnd_dvc_start,
|
.start = rsnd_dvc_start,
|
||||||
|
@ -54,6 +54,13 @@ static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
|
|||||||
rsnd_mod_write(mod, MIX_MDBER, 1);
|
rsnd_mod_write(mod, MIX_MDBER, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rsnd_mix_probe_(struct rsnd_mod *mod,
|
||||||
|
struct rsnd_dai_stream *io,
|
||||||
|
struct rsnd_priv *priv)
|
||||||
|
{
|
||||||
|
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
|
||||||
|
}
|
||||||
|
|
||||||
static int rsnd_mix_init(struct rsnd_mod *mod,
|
static int rsnd_mix_init(struct rsnd_mod *mod,
|
||||||
struct rsnd_dai_stream *io,
|
struct rsnd_dai_stream *io,
|
||||||
struct rsnd_priv *priv)
|
struct rsnd_priv *priv)
|
||||||
@ -66,8 +73,6 @@ static int rsnd_mix_init(struct rsnd_mod *mod,
|
|||||||
|
|
||||||
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
|
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
|
||||||
|
|
||||||
rsnd_path_parse(priv, io);
|
|
||||||
|
|
||||||
/* volume step */
|
/* volume step */
|
||||||
rsnd_mod_write(mod, MIX_MIXMR, 0);
|
rsnd_mod_write(mod, MIX_MIXMR, 0);
|
||||||
rsnd_mod_write(mod, MIX_MVPDR, 0);
|
rsnd_mod_write(mod, MIX_MVPDR, 0);
|
||||||
@ -90,6 +95,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
|
|||||||
|
|
||||||
static struct rsnd_mod_ops rsnd_mix_ops = {
|
static struct rsnd_mod_ops rsnd_mix_ops = {
|
||||||
.name = MIX_NAME,
|
.name = MIX_NAME,
|
||||||
|
.probe = rsnd_mix_probe_,
|
||||||
.init = rsnd_mix_init,
|
.init = rsnd_mix_init,
|
||||||
.quit = rsnd_mix_quit,
|
.quit = rsnd_mix_quit,
|
||||||
};
|
};
|
||||||
|
@ -187,8 +187,6 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
|
|||||||
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
||||||
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
||||||
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
||||||
void rsnd_path_parse(struct rsnd_priv *priv,
|
|
||||||
struct rsnd_dai_stream *io);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* R-Car DMA
|
* R-Car DMA
|
||||||
@ -210,6 +208,7 @@ enum rsnd_mod_type {
|
|||||||
RSND_MOD_DVC,
|
RSND_MOD_DVC,
|
||||||
RSND_MOD_MIX,
|
RSND_MOD_MIX,
|
||||||
RSND_MOD_CTU,
|
RSND_MOD_CTU,
|
||||||
|
RSND_MOD_CMD,
|
||||||
RSND_MOD_SRC,
|
RSND_MOD_SRC,
|
||||||
RSND_MOD_SSI,
|
RSND_MOD_SSI,
|
||||||
RSND_MOD_MAX,
|
RSND_MOD_MAX,
|
||||||
@ -474,6 +473,12 @@ struct rsnd_priv {
|
|||||||
void *dvc;
|
void *dvc;
|
||||||
int dvc_nr;
|
int dvc_nr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* below value will be filled on rsnd_cmd_probe()
|
||||||
|
*/
|
||||||
|
void *cmd;
|
||||||
|
int cmd_nr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* below value will be filled on rsnd_dai_probe()
|
* below value will be filled on rsnd_dai_probe()
|
||||||
*/
|
*/
|
||||||
@ -606,6 +611,17 @@ void rsnd_dvc_remove(struct platform_device *pdev,
|
|||||||
struct rsnd_priv *priv);
|
struct rsnd_priv *priv);
|
||||||
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
|
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* R-Car CMD
|
||||||
|
*/
|
||||||
|
int rsnd_cmd_probe(struct platform_device *pdev,
|
||||||
|
const struct rsnd_of_data *of_data,
|
||||||
|
struct rsnd_priv *priv);
|
||||||
|
void rsnd_cmd_remove(struct platform_device *pdev,
|
||||||
|
struct rsnd_priv *priv);
|
||||||
|
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
|
||||||
|
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
|
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
|
||||||
#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
|
#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
|
||||||
|
Loading…
Reference in New Issue
Block a user