Merge "asoc: add support to set custom channel mixer coefficients"
This commit is contained in:
commit
753eedb972
@ -101,6 +101,7 @@ struct msm_compr_pdata {
|
||||
struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
|
||||
struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
|
||||
bool is_in_use[MSM_FRONTEND_DAI_MAX];
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE];
|
||||
};
|
||||
|
||||
struct msm_compr_audio {
|
||||
@ -3711,6 +3712,7 @@ static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol,
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
int rc = 0, i;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
|
||||
|
||||
pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
|
||||
|
||||
@ -3726,6 +3728,17 @@ static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol,
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
pdata->ch_map[fe_id]->channel_map[i] =
|
||||
(char)(ucontrol->value.integer.value[i]);
|
||||
|
||||
/* update chmixer_pspd chmap cached with routing driver as well */
|
||||
chmixer_pspd = pdata->chmixer_pspd[fe_id];
|
||||
if (chmixer_pspd && chmixer_pspd->enable) {
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] =
|
||||
pdata->ch_map[fe_id]->channel_map[i];
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
msm_pcm_routing_set_channel_mixer_cfg(fe_id,
|
||||
SESSION_TYPE_RX, chmixer_pspd);
|
||||
}
|
||||
} else {
|
||||
pr_debug("%s: no memory for ch_map, default will be set\n",
|
||||
__func__);
|
||||
@ -3971,10 +3984,11 @@ static int msm_compr_probe(struct snd_soc_component *component)
|
||||
const char *qdsp_version;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
pdata = (struct msm_compr_pdata *)
|
||||
kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
pdata = (struct msm_compr_pdata *) dev_get_drvdata(component->dev);
|
||||
if (!pdata) {
|
||||
pr_err("%s platform data not set\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_set_drvdata(component, pdata);
|
||||
|
||||
@ -4640,6 +4654,580 @@ static int msm_compr_add_event_ack_cmd_control(struct snd_soc_pcm_runtime *rtd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct msm_pcm_channel_mixer *msm_compr_get_chmixer(
|
||||
struct msm_compr_pdata *pdata, u64 fe_id)
|
||||
{
|
||||
if (!pdata) {
|
||||
pr_err("%s: missing pdata\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
|
||||
pr_err("%s: invalid FE %llu\n", __func__, fe_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pdata->chmixer_pspd[fe_id];
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int ret = 0, i = 0, stream_id = 0, be_id = 0;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct snd_compr_stream *cstream = NULL;
|
||||
struct msm_compr_audio *prtd = NULL;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
|
||||
u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
|
||||
bool reset_override_out_ch_map = false;
|
||||
bool reset_override_in_ch_map = false;
|
||||
|
||||
if ((session_type != SESSION_TYPE_TX) &&
|
||||
(session_type != SESSION_TYPE_RX)) {
|
||||
pr_err("%s: invalid session type %d\n", __func__, session_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->enable = ucontrol->value.integer.value[0];
|
||||
chmixer_pspd->rule = ucontrol->value.integer.value[1];
|
||||
chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
|
||||
chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
|
||||
chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
|
||||
|
||||
if (chmixer_pspd->enable) {
|
||||
if (session_type == SESSION_TYPE_RX &&
|
||||
!chmixer_pspd->override_in_ch_map) {
|
||||
if (pdata->ch_map[fe_id]->set_ch_map) {
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] =
|
||||
pdata->ch_map[fe_id]->channel_map[i];
|
||||
} else {
|
||||
q6asm_map_channels(asm_ch_map,
|
||||
chmixer_pspd->input_channel, false);
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
|
||||
}
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
reset_override_in_ch_map = true;
|
||||
} else if (session_type == SESSION_TYPE_TX &&
|
||||
!chmixer_pspd->override_out_ch_map) {
|
||||
if (pdata->ch_map[fe_id]->set_ch_map) {
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] =
|
||||
pdata->ch_map[fe_id]->channel_map[i];
|
||||
} else {
|
||||
q6asm_map_channels(asm_ch_map,
|
||||
chmixer_pspd->output_channel, false);
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
|
||||
}
|
||||
chmixer_pspd->override_out_ch_map = true;
|
||||
reset_override_out_ch_map = true;
|
||||
}
|
||||
} else {
|
||||
chmixer_pspd->override_out_ch_map = false;
|
||||
chmixer_pspd->override_in_ch_map = false;
|
||||
}
|
||||
|
||||
/* cache value and take effect during adm_open stage */
|
||||
msm_pcm_routing_set_channel_mixer_cfg(fe_id,
|
||||
session_type,
|
||||
chmixer_pspd);
|
||||
|
||||
cstream = pdata->cstream[fe_id];
|
||||
if (chmixer_pspd->enable && cstream && cstream->runtime) {
|
||||
prtd = cstream->runtime->private_data;
|
||||
if (!prtd) {
|
||||
pr_err("%s invalid prtd\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (prtd->audio_client) {
|
||||
stream_id = prtd->audio_client->session;
|
||||
be_id = chmixer_pspd->port_idx;
|
||||
ret = msm_pcm_routing_set_channel_mixer_runtime(be_id,
|
||||
stream_id, session_type, chmixer_pspd);
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_override_out_ch_map)
|
||||
chmixer_pspd->override_out_ch_map = false;
|
||||
if (reset_override_in_ch_map)
|
||||
chmixer_pspd->override_in_ch_map = false;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = chmixer_pspd->enable;
|
||||
ucontrol->value.integer.value[1] = chmixer_pspd->rule;
|
||||
ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
|
||||
ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
|
||||
ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_output_map_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->override_out_ch_map = true;
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] =
|
||||
ucontrol->value.integer.value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_output_map_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->out_ch_map[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_input_map_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_input_map_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->in_ch_map[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_weight_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int channel = (kcontrol->private_value >> 16) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
|
||||
pr_err("%s: invalid channel number %d\n", __func__, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
channel--;
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->channel_weight[channel][i] =
|
||||
ucontrol->value.integer.value[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_weight_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int channel = (kcontrol->private_value >> 16) & 0xFF;
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(comp);
|
||||
int i = 0;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
|
||||
pr_err("%s: invalid channel number %d\n", __func__, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
channel--;
|
||||
|
||||
chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->channel_weight[channel][i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_add_platform_controls(struct snd_kcontrol_new *kctl,
|
||||
struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
|
||||
const char *name_suffix, int session_type, int channels)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
char *mixer_name = NULL;
|
||||
const char *deviceNo = "NN";
|
||||
const char *channelNo = "NN";
|
||||
int ctl_len = 0;
|
||||
struct snd_soc_component *component = NULL;
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
|
||||
strlen(channelNo) + 1 + strlen(name_suffix) + 1;
|
||||
|
||||
mixer_name = kzalloc(ctl_len, GFP_KERNEL);
|
||||
if (mixer_name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (channels >= 0) {
|
||||
snprintf(mixer_name, ctl_len, "%s %d %s %d",
|
||||
name_prefix, rtd->pcm->device, name_suffix, channels);
|
||||
kctl->private_value = (rtd->dai_link->id) | (channels << 16);
|
||||
} else {
|
||||
snprintf(mixer_name, ctl_len, "%s %d %s",
|
||||
name_prefix, rtd->pcm->device, name_suffix);
|
||||
kctl->private_value = (rtd->dai_link->id);
|
||||
}
|
||||
if (session_type != INVALID_SESSION)
|
||||
kctl->private_value |= (session_type << 8);
|
||||
|
||||
kctl->name = mixer_name;
|
||||
ret = snd_soc_add_component_controls(component, kctl, 1);
|
||||
kfree(mixer_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid channel map value ranges from 1 to 64 */
|
||||
uinfo->value.integer.min = 1;
|
||||
uinfo->value.integer.max = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_add_channel_mixer_output_map_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Output Map";
|
||||
const char *mixer_ctl_name = NULL;
|
||||
int ret = 0, session_type = INVALID_SESSION, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_output_map_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_compr_channel_mixer_output_map_info,
|
||||
.put = msm_compr_channel_mixer_output_map_ctl_put,
|
||||
.get = msm_compr_channel_mixer_output_map_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
|
||||
playback_mixer_ctl_name : capture_mixer_ctl_name ;
|
||||
ret = msm_compr_add_platform_controls(&channel_mixer_output_map_control,
|
||||
rtd, mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid channel map value ranges from 1 to 64 */
|
||||
uinfo->value.integer.min = 1;
|
||||
uinfo->value.integer.max = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_add_channel_mixer_input_map_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Input Map";
|
||||
const char *mixer_ctl_name = NULL;
|
||||
int ret = 0, session_type = INVALID_SESSION, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_input_map_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_compr_channel_mixer_input_map_info,
|
||||
.put = msm_compr_channel_mixer_input_map_ctl_put,
|
||||
.get = msm_compr_channel_mixer_input_map_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
|
||||
playback_mixer_ctl_name : capture_mixer_ctl_name ;
|
||||
ret = msm_compr_add_platform_controls(&channel_mixer_input_map_control,
|
||||
rtd, mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
/* five int values: enable, rule, in_channels, out_channels and port_id */
|
||||
uinfo->count = 5;
|
||||
/* Valid range is all positive values to support above controls */
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = INT_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_add_channel_mixer_cfg_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Cfg";
|
||||
const char *mixer_ctl_name = NULL;
|
||||
int ret = 0, session_type = INVALID_SESSION, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_cfg_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_compr_channel_mixer_cfg_info,
|
||||
.put = msm_compr_channel_mixer_cfg_ctl_put,
|
||||
.get = msm_compr_channel_mixer_cfg_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
mixer_ctl_name = playback_mixer_ctl_name;
|
||||
} else {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
mixer_ctl_name = capture_mixer_ctl_name;
|
||||
}
|
||||
|
||||
ret = msm_compr_add_platform_controls(&channel_mixer_cfg_control,
|
||||
rtd, mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid range: 0 to 0x4000(Unity) gain weightage */
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 0x4000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_add_channel_mixer_weight_controls(
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
int channel)
|
||||
{
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Weight Ch";
|
||||
const char *mixer_ctl_name = NULL;
|
||||
int ret = 0, session_type = INVALID_SESSION;
|
||||
struct snd_kcontrol_new channel_mixer_weight_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_compr_channel_mixer_weight_info,
|
||||
.put = msm_compr_channel_mixer_weight_ctl_put,
|
||||
.get = msm_compr_channel_mixer_weight_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
|
||||
playback_mixer_ctl_name : capture_mixer_ctl_name ;
|
||||
ret = msm_compr_add_platform_controls(&channel_mixer_weight_control,
|
||||
rtd, mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct msm_compr_pdata *pdata = NULL;
|
||||
struct snd_soc_component *component = NULL;
|
||||
|
||||
if (!rtd) {
|
||||
pr_err("%s NULL rtd\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = (struct msm_compr_pdata *)
|
||||
snd_soc_component_get_drvdata(component);
|
||||
if (!pdata) {
|
||||
pr_err("%s: platform data not populated\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->chmixer_pspd[rtd->dai_link->id]) {
|
||||
pdata->chmixer_pspd[rtd->dai_link->id] =
|
||||
kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
|
||||
if (!pdata->chmixer_pspd[rtd->dai_link->id])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = msm_compr_add_channel_mixer_cfg_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = msm_compr_add_channel_mixer_input_map_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = msm_compr_add_channel_mixer_output_map_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
|
||||
ret = msm_compr_add_channel_mixer_weight_controls(rtd, i);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(pdata->chmixer_pspd[rtd->dai_link->id]);
|
||||
pdata->chmixer_pspd[rtd->dai_link->id] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int rc;
|
||||
@ -4690,6 +5278,10 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
|
||||
if (rc)
|
||||
pr_err("%s: Could not add Compr Channel Map Control\n",
|
||||
__func__);
|
||||
rc = msm_compr_add_channel_mixer_controls(rtd);
|
||||
if (rc)
|
||||
pr_err("%s: Could not add Compr Channel Mixer Controls\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4717,14 +5309,31 @@ static struct snd_soc_component_driver msm_soc_component = {
|
||||
|
||||
static int msm_compr_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_compr_pdata *pdata = NULL;
|
||||
|
||||
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
pdata = (struct msm_compr_pdata *)
|
||||
kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&pdev->dev, pdata);
|
||||
|
||||
return snd_soc_register_component(&pdev->dev,
|
||||
&msm_soc_component, NULL, 0);
|
||||
}
|
||||
|
||||
static int msm_compr_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i = 0;
|
||||
struct msm_compr_pdata *pdata = NULL;
|
||||
|
||||
pdata = dev_get_drvdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++)
|
||||
kfree(pdata->chmixer_pspd[i]);
|
||||
}
|
||||
kfree(pdata);
|
||||
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
@ -64,6 +64,8 @@ static u32 hfp_tx_mute;
|
||||
|
||||
struct msm_pcm_pdata {
|
||||
int perf_mode;
|
||||
struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
|
||||
};
|
||||
|
||||
static void stop_pcm(struct msm_pcm_loopback *pcm);
|
||||
@ -734,6 +736,671 @@ static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct msm_pcm_channel_mixer *msm_pcm_get_chmixer(
|
||||
struct msm_pcm_pdata *pdata,
|
||||
u64 fe_id, int session_type)
|
||||
{
|
||||
if (!pdata) {
|
||||
pr_err("%s: missing pdata\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
|
||||
pr_err("%s: invalid FE %llu\n", __func__, fe_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((session_type != SESSION_TYPE_TX) &&
|
||||
(session_type != SESSION_TYPE_RX)) {
|
||||
pr_err("%s: invalid session type %d\n", __func__, session_type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pdata->chmixer_pspd[fe_id][session_type];
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int ret = 0;
|
||||
int stream_id = 0;
|
||||
int be_id = 0, i = 0;
|
||||
struct msm_pcm_loopback *prtd = NULL;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct snd_pcm *pcm = NULL;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
|
||||
u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
|
||||
bool reset_override_out_ch_map = false;
|
||||
bool reset_override_in_ch_map = false;
|
||||
|
||||
pcm = pdata->pcm_device[fe_id];
|
||||
if (!pcm) {
|
||||
pr_err("%s invalid pcm handle for fe_id %llu\n",
|
||||
__func__, fe_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (session_type == SESSION_TYPE_RX)
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
else
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||
if (!substream) {
|
||||
pr_err("%s substream not found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->enable = ucontrol->value.integer.value[0];
|
||||
chmixer_pspd->rule = ucontrol->value.integer.value[1];
|
||||
chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
|
||||
chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
|
||||
chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
|
||||
|
||||
if (chmixer_pspd->enable) {
|
||||
if (session_type == SESSION_TYPE_RX &&
|
||||
!chmixer_pspd->override_in_ch_map) {
|
||||
q6asm_map_channels(asm_ch_map,
|
||||
chmixer_pspd->input_channel, false);
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
reset_override_in_ch_map = true;
|
||||
} else if (session_type == SESSION_TYPE_TX &&
|
||||
!chmixer_pspd->override_out_ch_map) {
|
||||
q6asm_map_channels(asm_ch_map,
|
||||
chmixer_pspd->output_channel, false);
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
|
||||
chmixer_pspd->override_out_ch_map = true;
|
||||
reset_override_out_ch_map = true;
|
||||
}
|
||||
} else {
|
||||
chmixer_pspd->override_out_ch_map = false;
|
||||
chmixer_pspd->override_in_ch_map = false;
|
||||
}
|
||||
|
||||
/* cache value and take effect during adm_open stage */
|
||||
msm_pcm_routing_set_channel_mixer_cfg(fe_id,
|
||||
session_type,
|
||||
chmixer_pspd);
|
||||
|
||||
if (chmixer_pspd->enable && substream->runtime) {
|
||||
prtd = substream->runtime->private_data;
|
||||
if (!prtd) {
|
||||
pr_err("%s find invalid prtd fail\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (prtd->audio_client) {
|
||||
stream_id = prtd->audio_client->session;
|
||||
be_id = chmixer_pspd->port_idx;
|
||||
msm_pcm_routing_set_channel_mixer_runtime(be_id,
|
||||
stream_id,
|
||||
session_type,
|
||||
chmixer_pspd);
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_override_out_ch_map)
|
||||
chmixer_pspd->override_out_ch_map = false;
|
||||
if (reset_override_in_ch_map)
|
||||
chmixer_pspd->override_in_ch_map = false;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = chmixer_pspd->enable;
|
||||
ucontrol->value.integer.value[1] = chmixer_pspd->rule;
|
||||
ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
|
||||
ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
|
||||
ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_output_map_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->override_out_ch_map = true;
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] =
|
||||
ucontrol->value.integer.value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_output_map_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->out_ch_map[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_input_map_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_input_map_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->in_ch_map[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_weight_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int channel = (kcontrol->private_value >> 16) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
|
||||
pr_err("%s: invalid channel number %d\n", __func__, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
channel--;
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->channel_weight[channel][i] =
|
||||
ucontrol->value.integer.value[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_weight_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int channel = (kcontrol->private_value >> 16) & 0xFF;
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_kcontrol_component(kcontrol);
|
||||
struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
|
||||
int i = 0;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
|
||||
pr_err("%s: invalid channel number %d\n", __func__, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
channel--;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->channel_weight[channel][i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_platform_controls(struct snd_kcontrol_new *kctl,
|
||||
struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
|
||||
const char *name_suffix, int session_type, int channels)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
char *mixer_name = NULL;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *deviceNo = "NN";
|
||||
const char *channelNo = "NN";
|
||||
int ctl_len = 0;
|
||||
struct snd_soc_component *component = NULL;
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
|
||||
strlen(channelNo) + 1 + strlen(name_suffix) + 1;
|
||||
|
||||
mixer_name = kzalloc(ctl_len, GFP_KERNEL);
|
||||
if (mixer_name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (channels >= 0) {
|
||||
snprintf(mixer_name, ctl_len, "%s %d %s %d",
|
||||
name_prefix, pcm->device, name_suffix, channels);
|
||||
kctl->private_value = (rtd->dai_link->id) | (session_type << 8) |
|
||||
(channels << 16);
|
||||
} else {
|
||||
snprintf(mixer_name, ctl_len, "%s %d %s",
|
||||
name_prefix, pcm->device, name_suffix);
|
||||
kctl->private_value = (rtd->dai_link->id) | (session_type << 8);
|
||||
}
|
||||
|
||||
kctl->name = mixer_name;
|
||||
ret = snd_soc_add_component_controls(component, kctl, 1);
|
||||
kfree(mixer_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid channel map value ranges from 1 to 64 */
|
||||
uinfo->value.integer.min = 1;
|
||||
uinfo->value.integer.max = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_output_map_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Output Map";
|
||||
int session_type = 0, ret = 0, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_output_map_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_output_map_info,
|
||||
.put = msm_pcm_channel_mixer_output_map_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_output_map_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid channel map value ranges from 1 to 64 */
|
||||
uinfo->value.integer.min = 1;
|
||||
uinfo->value.integer.max = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_input_map_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Input Map";
|
||||
int session_type = 0, ret = 0, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_input_map_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_input_map_info,
|
||||
.put = msm_pcm_channel_mixer_input_map_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_input_map_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
/* five int values: enable, rule, in_channels, out_channels and port_id */
|
||||
uinfo->count = 5;
|
||||
/* Valid range is all positive values to support above controls */
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = INT_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_cfg_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Cfg";
|
||||
int session_type = 0, ret = 0, channel = -1;
|
||||
struct msm_pcm_pdata *pdata = NULL;
|
||||
struct snd_soc_component *component = NULL;
|
||||
struct snd_kcontrol_new channel_mixer_cfg_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_cfg_info,
|
||||
.put = msm_pcm_channel_mixer_cfg_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_cfg_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
pdata = (struct msm_pcm_pdata *)
|
||||
dev_get_drvdata(component->dev);
|
||||
if (pdata == NULL) {
|
||||
pr_err("%s: platform data not populated\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata->pcm_device[rtd->dai_link->id] = rtd->pcm;
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid range: 0 to 0x4000(Unity) gain weightage */
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 0x4000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_weight_controls(
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
int channel)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Weight Ch";
|
||||
int session_type = 0, ret = 0;
|
||||
struct snd_kcontrol_new channel_mixer_weight_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_weight_info,
|
||||
.put = msm_pcm_channel_mixer_weight_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_weight_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct snd_pcm *pcm = NULL;
|
||||
struct msm_pcm_pdata *pdata = NULL;
|
||||
struct snd_soc_component *component = NULL;
|
||||
|
||||
if (!rtd || !rtd->pcm) {
|
||||
pr_err("%s invalid rtd or pcm\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
pcm = rtd->pcm;
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = (struct msm_pcm_pdata *)
|
||||
dev_get_drvdata(component->dev);
|
||||
if (!pdata) {
|
||||
pr_err("%s: platform data not populated\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
|
||||
!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
|
||||
kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
|
||||
if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
|
||||
!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
|
||||
kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
|
||||
if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
|
||||
ret = msm_pcm_add_channel_mixer_weight_controls(rtd, i);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
|
||||
kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -747,6 +1414,11 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
if (ret)
|
||||
pr_err("%s: pcm add app type controls failed:%d\n",
|
||||
__func__, ret);
|
||||
|
||||
ret = msm_pcm_add_channel_mixer_controls(rtd);
|
||||
if (ret)
|
||||
pr_err("%s: pcm add channel mixer controls failed:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -797,6 +1469,17 @@ static int msm_pcm_probe(struct platform_device *pdev)
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_pcm_pdata *pdata;
|
||||
int i = 0;
|
||||
|
||||
pdata = dev_get_drvdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++) {
|
||||
kfree(pdata->chmixer_pspd[i][SESSION_TYPE_RX]);
|
||||
kfree(pdata->chmixer_pspd[i][SESSION_TYPE_TX]);
|
||||
}
|
||||
}
|
||||
kfree(pdata);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1601,6 +1601,11 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
struct snd_pcm_substream *substream;
|
||||
struct msm_audio *prtd;
|
||||
struct snd_soc_pcm_runtime *rtd = NULL;
|
||||
struct msm_plat_data *pdata = NULL;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
u64 fe_id = 0;
|
||||
|
||||
pr_debug("%s", __func__);
|
||||
substream = snd_pcm_chmap_substream(info, idx);
|
||||
@ -1615,6 +1620,24 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
prtd->channel_map[i] =
|
||||
(char)(ucontrol->value.integer.value[i]);
|
||||
|
||||
/* update chmixer_pspd chmap cached with routing driver as well */
|
||||
rtd = substream->private_data;
|
||||
if (rtd) {
|
||||
fe_id = rtd->dai_link->id;
|
||||
pdata = (struct msm_plat_data *)
|
||||
dev_get_drvdata(component->dev);
|
||||
chmixer_pspd = pdata ?
|
||||
pdata->chmixer_pspd[fe_id][SESSION_TYPE_RX] : NULL;
|
||||
|
||||
if (chmixer_pspd && chmixer_pspd->enable) {
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = prtd->channel_map[i];
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
msm_pcm_routing_set_channel_mixer_cfg(fe_id,
|
||||
SESSION_TYPE_RX, chmixer_pspd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1838,6 +1861,674 @@ static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct msm_pcm_channel_mixer *msm_pcm_get_chmixer(
|
||||
struct msm_plat_data *pdata,
|
||||
u64 fe_id, int session_type)
|
||||
{
|
||||
if (!pdata) {
|
||||
pr_err("%s: missing pdata\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
|
||||
pr_err("%s: invalid FE %llu\n", __func__, fe_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((session_type != SESSION_TYPE_TX) &&
|
||||
(session_type != SESSION_TYPE_RX)) {
|
||||
pr_err("%s: invalid session type %d\n", __func__, session_type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pdata->chmixer_pspd[fe_id][session_type];
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int ret = 0;
|
||||
int stream_id = 0;
|
||||
int be_id = 0, i = 0;
|
||||
struct msm_audio *prtd = NULL;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct snd_pcm *pcm = NULL;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
|
||||
u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
|
||||
bool reset_override_out_ch_map = false;
|
||||
bool reset_override_in_ch_map = false;
|
||||
|
||||
pcm = pdata->pcm_device[fe_id];
|
||||
if (!pcm) {
|
||||
pr_err("%s invalid pcm handle for fe_id %llu\n",
|
||||
__func__, fe_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (session_type == SESSION_TYPE_RX)
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
else
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||
if (!substream) {
|
||||
pr_err("%s substream not found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->enable = ucontrol->value.integer.value[0];
|
||||
chmixer_pspd->rule = ucontrol->value.integer.value[1];
|
||||
chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
|
||||
chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
|
||||
chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
|
||||
|
||||
if (chmixer_pspd->enable) {
|
||||
if (session_type == SESSION_TYPE_RX &&
|
||||
!chmixer_pspd->override_in_ch_map) {
|
||||
if (pdata->ch_map[fe_id] &&
|
||||
pdata->ch_map[fe_id]->set_ch_map) {
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] =
|
||||
pdata->ch_map[fe_id]->channel_map[i];
|
||||
} else {
|
||||
q6asm_map_channels(asm_ch_map,
|
||||
chmixer_pspd->input_channel, false);
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
|
||||
}
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
reset_override_in_ch_map = true;
|
||||
} else if (session_type == SESSION_TYPE_TX &&
|
||||
!chmixer_pspd->override_out_ch_map) {
|
||||
if (pdata->ch_map[fe_id] &&
|
||||
pdata->ch_map[fe_id]->set_ch_map) {
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] =
|
||||
pdata->ch_map[fe_id]->channel_map[i];
|
||||
} else {
|
||||
q6asm_map_channels(asm_ch_map,
|
||||
chmixer_pspd->output_channel, false);
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
|
||||
}
|
||||
chmixer_pspd->override_out_ch_map = true;
|
||||
reset_override_out_ch_map = true;
|
||||
}
|
||||
} else {
|
||||
chmixer_pspd->override_out_ch_map = false;
|
||||
chmixer_pspd->override_in_ch_map = false;
|
||||
}
|
||||
|
||||
/* cache value and take effect during adm_open stage */
|
||||
msm_pcm_routing_set_channel_mixer_cfg(fe_id,
|
||||
session_type,
|
||||
chmixer_pspd);
|
||||
|
||||
if (chmixer_pspd->enable && substream->runtime) {
|
||||
prtd = substream->runtime->private_data;
|
||||
if (!prtd) {
|
||||
pr_err("%s find invalid prtd fail\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (prtd->audio_client) {
|
||||
stream_id = prtd->audio_client->session;
|
||||
be_id = chmixer_pspd->port_idx;
|
||||
msm_pcm_routing_set_channel_mixer_runtime(be_id,
|
||||
stream_id,
|
||||
session_type,
|
||||
chmixer_pspd);
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_override_out_ch_map)
|
||||
chmixer_pspd->override_out_ch_map = false;
|
||||
if (reset_override_in_ch_map)
|
||||
chmixer_pspd->override_in_ch_map = false;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = chmixer_pspd->enable;
|
||||
ucontrol->value.integer.value[1] = chmixer_pspd->rule;
|
||||
ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
|
||||
ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
|
||||
ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_output_map_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->override_out_ch_map = true;
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->out_ch_map[i] =
|
||||
ucontrol->value.integer.value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_output_map_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->out_ch_map[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_input_map_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chmixer_pspd->override_in_ch_map = true;
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_input_map_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->in_ch_map[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_weight_ctl_put(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int channel = (kcontrol->private_value >> 16) & 0xFF;
|
||||
int i = 0;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
|
||||
pr_err("%s: invalid channel number %d\n", __func__, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
channel--;
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
chmixer_pspd->channel_weight[channel][i] =
|
||||
ucontrol->value.integer.value[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_weight_ctl_get(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u64 fe_id = kcontrol->private_value & 0xFF;
|
||||
int session_type = (kcontrol->private_value >> 8) & 0xFF;
|
||||
int channel = (kcontrol->private_value >> 16) & 0xFF;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
|
||||
int i = 0;
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd;
|
||||
|
||||
if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
|
||||
pr_err("%s: invalid channel number %d\n", __func__, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
channel--;
|
||||
|
||||
chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
|
||||
if (!chmixer_pspd) {
|
||||
pr_err("%s: invalid chmixer_pspd in pdata", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
|
||||
ucontrol->value.integer.value[i] =
|
||||
chmixer_pspd->channel_weight[channel][i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_platform_controls(struct snd_kcontrol_new *kctl,
|
||||
struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
|
||||
const char *name_suffix, int session_type, int channels)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
char *mixer_name = NULL;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *deviceNo = "NN";
|
||||
const char *channelNo = "NN";
|
||||
int ctl_len = 0;
|
||||
struct snd_soc_component *component = NULL;
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
|
||||
strlen(channelNo) + 1 + strlen(name_suffix) + 1;
|
||||
|
||||
mixer_name = kzalloc(ctl_len, GFP_KERNEL);
|
||||
if (mixer_name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (channels >= 0) {
|
||||
snprintf(mixer_name, ctl_len, "%s %d %s %d",
|
||||
name_prefix, pcm->device, name_suffix, channels);
|
||||
kctl->private_value = (rtd->dai_link->id) | (session_type << 8) |
|
||||
(channels << 16);
|
||||
} else {
|
||||
snprintf(mixer_name, ctl_len, "%s %d %s",
|
||||
name_prefix, pcm->device, name_suffix);
|
||||
kctl->private_value = (rtd->dai_link->id) | (session_type << 8);
|
||||
}
|
||||
|
||||
kctl->name = mixer_name;
|
||||
ret = snd_soc_add_component_controls(component, kctl, 1);
|
||||
kfree(mixer_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid channel map value ranges from 1 to 64 */
|
||||
uinfo->value.integer.min = 1;
|
||||
uinfo->value.integer.max = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_output_map_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Output Map";
|
||||
int session_type = 0, ret = 0, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_output_map_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_output_map_info,
|
||||
.put = msm_pcm_channel_mixer_output_map_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_output_map_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid channel map value ranges from 1 to 64 */
|
||||
uinfo->value.integer.min = 1;
|
||||
uinfo->value.integer.max = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_input_map_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Input Map";
|
||||
int session_type = 0, ret = 0, channel = -1;
|
||||
struct snd_kcontrol_new channel_mixer_input_map_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_input_map_info,
|
||||
.put = msm_pcm_channel_mixer_input_map_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_input_map_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
/* five int values: enable, rule, in_channels, out_channels and port_id */
|
||||
uinfo->count = 5;
|
||||
/* Valid range is all positive values to support above controls */
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = INT_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_cfg_controls(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Cfg";
|
||||
int session_type = 0, ret = 0, channel = -1;
|
||||
struct msm_plat_data *pdata = NULL;
|
||||
struct snd_soc_component *component = NULL;
|
||||
struct snd_kcontrol_new channel_mixer_cfg_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_cfg_info,
|
||||
.put = msm_pcm_channel_mixer_cfg_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_cfg_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = (struct msm_plat_data *)
|
||||
dev_get_drvdata(component->dev);
|
||||
|
||||
pdata->pcm_device[rtd->dai_link->id] = rtd->pcm;
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
|
||||
/* Valid range: 0 to 0x4000(Unity) gain weightage */
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 0x4000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_weight_controls(
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
int channel)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
const char *playback_mixer_ctl_name = "AudStr";
|
||||
const char *capture_mixer_ctl_name = "AudStr Capture";
|
||||
const char *suffix = "ChMixer Weight Ch";
|
||||
int session_type = 0, ret = 0;
|
||||
struct snd_kcontrol_new channel_mixer_weight_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "?",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = msm_pcm_channel_mixer_weight_info,
|
||||
.put = msm_pcm_channel_mixer_weight_ctl_put,
|
||||
.get = msm_pcm_channel_mixer_weight_ctl_get,
|
||||
.private_value = 0,
|
||||
};
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
|
||||
session_type = SESSION_TYPE_RX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
|
||||
rtd, playback_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
|
||||
session_type = SESSION_TYPE_TX;
|
||||
ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
|
||||
rtd, capture_mixer_ctl_name, suffix, session_type, channel);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pr_err("%s: failed add platform ctl, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct snd_pcm *pcm = NULL;
|
||||
struct msm_plat_data *pdata = NULL;
|
||||
struct snd_soc_component *component = NULL;
|
||||
|
||||
if (!rtd || !rtd->pcm) {
|
||||
pr_err("%s invalid rtd or pcm\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
pcm = rtd->pcm;
|
||||
|
||||
component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
if (!component) {
|
||||
pr_err("%s: component is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = (struct msm_plat_data *)
|
||||
dev_get_drvdata(component->dev);
|
||||
if (!pdata) {
|
||||
pr_err("%s: platform data not populated\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
|
||||
!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
|
||||
kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
|
||||
if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
|
||||
!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
|
||||
kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
|
||||
if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
|
||||
ret = msm_pcm_add_channel_mixer_weight_controls(rtd, i);
|
||||
if (ret) {
|
||||
pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
|
||||
kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
|
||||
pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -1850,6 +2541,10 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
|
||||
if (ret)
|
||||
pr_err("%s: pcm add app type controls failed:%d\n",
|
||||
__func__, ret);
|
||||
ret = msm_pcm_add_channel_mixer_controls(rtd);
|
||||
if (ret)
|
||||
pr_err("%s: pcm add channel mixer controls failed:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1973,8 +2668,15 @@ static int msm_pcm_probe(struct platform_device *pdev)
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_plat_data *pdata;
|
||||
int i = 0;
|
||||
|
||||
pdata = dev_get_drvdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++) {
|
||||
kfree(pdata->chmixer_pspd[i][SESSION_TYPE_RX]);
|
||||
kfree(pdata->chmixer_pspd[i][SESSION_TYPE_TX]);
|
||||
}
|
||||
}
|
||||
kfree(pdata);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
return 0;
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2017, 2019 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _MSM_PCM_H
|
||||
@ -115,6 +115,8 @@ struct msm_plat_data {
|
||||
int perf_mode;
|
||||
struct snd_pcm *pcm;
|
||||
struct msm_pcm_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
|
||||
struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
|
||||
struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
|
||||
};
|
||||
|
||||
struct msm_pcm_ch_map {
|
||||
|
@ -84,6 +84,11 @@ static int msm_ec_ref_port_id;
|
||||
#define WEIGHT_0_DB 0x4000
|
||||
/* all the FEs which can support channel mixer */
|
||||
static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
|
||||
|
||||
/* all the FES which can support channel mixer for bidirection */
|
||||
static struct msm_pcm_channel_mixer
|
||||
channel_mixer_v2[MSM_FRONTEND_DAI_MM_SIZE][2];
|
||||
|
||||
/* input BE for each FE */
|
||||
static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS];
|
||||
|
||||
@ -928,6 +933,51 @@ int msm_pcm_routing_send_chmix_cfg(int fe_id, int ip_channel_cnt,
|
||||
}
|
||||
EXPORT_SYMBOL(msm_pcm_routing_send_chmix_cfg);
|
||||
|
||||
/**
|
||||
* msm_pcm_routing_set_channel_mixer_cfg - cache channel mixer
|
||||
* setting before use case start.
|
||||
*
|
||||
* @fe_id: frontend idx
|
||||
* @type: stream direction type
|
||||
* @params: parameters of channel mixer setting
|
||||
*
|
||||
* Return 0 for success
|
||||
*/
|
||||
int msm_pcm_routing_set_channel_mixer_cfg(
|
||||
int fe_id, int type,
|
||||
struct msm_pcm_channel_mixer *params)
|
||||
{
|
||||
int i, j = 0;
|
||||
|
||||
channel_mixer_v2[fe_id][type].enable = params->enable;
|
||||
channel_mixer_v2[fe_id][type].rule = params->rule;
|
||||
channel_mixer_v2[fe_id][type].input_channel =
|
||||
params->input_channel;
|
||||
channel_mixer_v2[fe_id][type].output_channel =
|
||||
params->output_channel;
|
||||
channel_mixer_v2[fe_id][type].port_idx = params->port_idx;
|
||||
|
||||
for (i = 0; i < ADM_MAX_CHANNELS; i++)
|
||||
channel_mixer_v2[fe_id][type].in_ch_map[i] =
|
||||
params->in_ch_map[i];
|
||||
for (i = 0; i < ADM_MAX_CHANNELS; i++)
|
||||
channel_mixer_v2[fe_id][type].out_ch_map[i] =
|
||||
params->out_ch_map[i];
|
||||
|
||||
for (i = 0; i < ADM_MAX_CHANNELS; i++)
|
||||
for (j = 0; j < ADM_MAX_CHANNELS; j++)
|
||||
channel_mixer_v2[fe_id][type].channel_weight[i][j] =
|
||||
params->channel_weight[i][j];
|
||||
|
||||
channel_mixer_v2[fe_id][type].override_in_ch_map =
|
||||
params->override_in_ch_map;
|
||||
channel_mixer_v2[fe_id][type].override_out_ch_map =
|
||||
params->override_out_ch_map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_cfg);
|
||||
|
||||
int msm_pcm_routing_reg_stream_app_type_cfg(
|
||||
int fedai_id, int session_type, int be_id,
|
||||
struct msm_pcm_stream_app_type_cfg *cfg_data)
|
||||
@ -1490,6 +1540,60 @@ static u32 msm_pcm_routing_get_voc_sessionid(u16 val)
|
||||
return session_id;
|
||||
}
|
||||
|
||||
static int msm_pcm_routing_channel_mixer_v2(int fe_id, bool perf_mode,
|
||||
int dspst_id, int stream_type)
|
||||
{
|
||||
int copp_idx = 0;
|
||||
int sess_type = 0;
|
||||
int j = 0, be_id = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
|
||||
pr_err("%s: invalid FE %d\n", __func__, fe_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
sess_type = SESSION_TYPE_RX;
|
||||
else
|
||||
sess_type = SESSION_TYPE_TX;
|
||||
|
||||
if (!(channel_mixer_v2[fe_id][sess_type].enable)) {
|
||||
pr_debug("%s: channel mixer not enabled for FE %d direction %d\n",
|
||||
__func__, fe_id, sess_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
be_id = channel_mixer_v2[fe_id][sess_type].port_idx - 1;
|
||||
channel_mixer_v2[fe_id][sess_type].input_channels[0] =
|
||||
channel_mixer_v2[fe_id][sess_type].input_channel;
|
||||
|
||||
pr_debug("%s sess type %d,fe_id %d,override in:%d out:%d,be active %d\n",
|
||||
__func__, sess_type, fe_id,
|
||||
channel_mixer_v2[fe_id][sess_type].override_in_ch_map,
|
||||
channel_mixer_v2[fe_id][sess_type].override_out_ch_map,
|
||||
msm_bedais[be_id].active);
|
||||
|
||||
if ((msm_bedais[be_id].active) &&
|
||||
test_bit(fe_id, &msm_bedais[be_id].fe_sessions[0])) {
|
||||
unsigned long copp =
|
||||
session_copp_map[fe_id][sess_type][be_id];
|
||||
for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
|
||||
if (test_bit(j, &copp)) {
|
||||
copp_idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = adm_programable_channel_mixer(
|
||||
msm_bedais[be_id].port_id,
|
||||
copp_idx, dspst_id, sess_type,
|
||||
&channel_mixer_v2[fe_id][sess_type], 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
|
||||
int dspst_id, int stream_type)
|
||||
{
|
||||
@ -1498,6 +1602,14 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
|
||||
int i = 0, j = 0, be_id;
|
||||
int ret = 0;
|
||||
|
||||
ret = msm_pcm_routing_channel_mixer_v2(fe_id, perf_mode,
|
||||
dspst_id, stream_type);
|
||||
if (ret) {
|
||||
pr_err("%s channel mixer v2 cmd set failure%d\n", __func__,
|
||||
fe_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
|
||||
pr_err("%s: invalid FE %d\n", __func__, fe_id);
|
||||
return 0;
|
||||
@ -1546,6 +1658,72 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* msm_pcm_routing_set_channel_mixer_runtime - apply channel mixer
|
||||
* setting during runtime.
|
||||
*
|
||||
* @be_id: backend index
|
||||
* @session_id: session index
|
||||
* @session_type: session type
|
||||
* @params: parameters for channel mixer
|
||||
*
|
||||
* Retuen: 0 for success, else error
|
||||
*/
|
||||
int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
|
||||
int session_type,
|
||||
struct msm_pcm_channel_mixer *params)
|
||||
{
|
||||
int rc = 0;
|
||||
int port_id, copp_idx = 0;
|
||||
|
||||
be_id--;
|
||||
if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
|
||||
pr_err("%s: invalid backend id %d\n", __func__,
|
||||
be_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
port_id = msm_bedais[be_id].port_id;
|
||||
copp_idx = adm_get_default_copp_idx(port_id);
|
||||
pr_debug("%s: port_id - %d, copp_idx %d session id - %d\n",
|
||||
__func__, port_id, copp_idx, session_id);
|
||||
|
||||
if ((params->input_channel < 0) ||
|
||||
(params->input_channel > ADM_MAX_CHANNELS)) {
|
||||
pr_err("%s: invalid input channel %d\n", __func__,
|
||||
params->input_channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((params->output_channel < 0) ||
|
||||
(params->output_channel > ADM_MAX_CHANNELS)) {
|
||||
pr_err("%s: invalid output channel %d\n", __func__,
|
||||
params->output_channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
params->input_channels[0] = params->input_channel;
|
||||
|
||||
pr_debug("%s sess type %d,override in:%d out:%d,be active %d\n",
|
||||
__func__, session_type,
|
||||
params->override_in_ch_map,
|
||||
params->override_out_ch_map,
|
||||
msm_bedais[be_id].active);
|
||||
|
||||
rc = adm_programable_channel_mixer(port_id,
|
||||
copp_idx,
|
||||
session_id,
|
||||
session_type,
|
||||
params,
|
||||
0);
|
||||
if (rc) {
|
||||
pr_err("%s: send params failed rc=%d\n", __func__, rc);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime);
|
||||
|
||||
int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
|
||||
int dspst_id, int stream_type)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
#ifndef _MSM_PCM_ROUTING_H
|
||||
#define _MSM_PCM_ROUTING_H
|
||||
#include <dsp/apr_audio-v2.h>
|
||||
#include <dsp/q6adm-v2.h>
|
||||
|
||||
/*
|
||||
* These names are used by HAL to specify the BE. If any changes are
|
||||
@ -594,4 +595,13 @@ int msm_pcm_routing_send_chmix_cfg(int fe_id, int ip_channel_cnt,
|
||||
int op_channel_cnt, int *ch_wght_coeff,
|
||||
int session_type, int stream_type);
|
||||
int msm_pcm_routing_get_pp_ch_cnt(int fe_id, int session_type);
|
||||
|
||||
int msm_pcm_routing_set_channel_mixer_cfg(
|
||||
int fe_id, int session_type,
|
||||
struct msm_pcm_channel_mixer *params);
|
||||
|
||||
int msm_pcm_routing_set_channel_mixer_runtime(
|
||||
int be_id, int session_id,
|
||||
int session_type,
|
||||
struct msm_pcm_channel_mixer *params);
|
||||
#endif /*_MSM_PCM_H*/
|
||||
|
Loading…
Reference in New Issue
Block a user