blob: 9b15b95ac412cfa07f5c206711d482999b54d3a6 [file] [log] [blame]
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/******************************************************************************
Copyright 2010 Broadcom Corporation. All rights reserved.
Unless you and Broadcom execute a separate written software license agreement
governing use of this software, this software is licensed to you under the
terms of the GNU General Public License version 2, available at
http://www.gnu.org/copyleft/gpl.html (the "GPL").
Notwithstanding the above, under no circumstances may you combine this software
in any way with any other Broadcom software provided under a license other than
the GPL, without Broadcom's express prior written consent.
******************************************************************************/
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
#include <sound/asound.h>
#include <sound/rawmidi.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "mobcom_types.h"
#include "resultcode.h"
#include "audio_consts.h"
#include "bcm_fuse_sysparm_CIB.h"
#include "csl_caph.h"
#include "audio_vdriver.h"
#include "audio_ddriver.h"
#include "audio_controller.h"
#include "audio_caph.h"
#include "caph_common.h"
#if defined(CONFIG_MAP_VOIP)
#include "audio_pipe.h"
#endif
#include "audio_trace.h"
static int MiscCtrlPut(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static Boolean isSTIHF = FALSE;
/**
* VolumeCtrlInfo - volume/gain mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about volume/gain mixer.
*
* Returns 0 for success.
*/
static int VolumeCtrlInfo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.step = 1;
switch (stream) {
case CTL_STREAM_PANEL_PCMOUT1:
case CTL_STREAM_PANEL_PCMOUT2:
case CTL_STREAM_PANEL_VOIPOUT:
case CTL_STREAM_PANEL_FM:
uinfo->count = 2;
uinfo->value.integer.min = MIN_VOLUME_mB;
uinfo->value.integer.max = MAX_VOLUME_mB;
break;
case CTL_STREAM_PANEL_VOICECALL:
uinfo->count = 1;
uinfo->value.integer.min = MIN_VOICE_VOLUME_mB;
uinfo->value.integer.max = MAX_VOICE_VOLUME_mB;
break;
case CTL_STREAM_PANEL_PCMIN:
case CTL_STREAM_PANEL_SPEECHIN:
case CTL_STREAM_PANEL_VOIPIN:
uinfo->count = 1;
uinfo->value.integer.min = MIN_GAIN_mB;
uinfo->value.integer.max = MAX_GAIN_mB;
break;
default:
break;
}
return 0;
}
/**
* VolumeCtrlGet - volume/gain mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of volume/gain mixer.
*
* Returns 0 for success.
*/
static int VolumeCtrlGet(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
int dev = DEV_OF_CTL(priv);
s32 *pVolume;
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
stream--;
pVolume = pChip->streamCtl[stream].ctlLine[dev].iVolume;
/*
* May need to get the value from driver
*/
ucontrol->value.integer.value[0] = pVolume[0];
ucontrol->value.integer.value[1] = pVolume[1];
aTrace(LOG_ALSA_INTERFACE, "\nALSA-CAPH %s stream=%d, volume %d:%d\n",
__func__, stream, pVolume[0], pVolume[1]);
return 0;
}
/**
* VolumeCtrlPut - volume/gain mixer put callback
* @kcontrol: mixer control
* @ucontrol: the value that needs to be set.
*
* Callback to set the value of volume/gain mixer.
*
* Set volume/gain of pcm playback, pcm capture, FM, and voice call etc
* @ucontrol setting.
* Update the mixer control only if the stream is not running.Call audio driver
* to apply when stream is running
*
* Returns 0 for success.
*/
static int VolumeCtrlPut(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
int dev = DEV_OF_CTL(priv);
s32 *pVolume;
struct snd_pcm_substream *pStream = NULL;
s32 *pCurSel;
BRCM_AUDIO_Param_Volume_t parm_vol;
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
pVolume = pChip->streamCtl[stream - 1].ctlLine[dev].iVolume;
pVolume[0] = ucontrol->value.integer.value[0];
pVolume[1] = ucontrol->value.integer.value[1];
pCurSel = pChip->streamCtl[stream - 1].iLineSelect;
/*aTrace(LOG_ALSA_INTERFACE, "ALSA-CAPH %s"
" stream=%d, volume %d:%d, dev %d, pCurSel %d:%d\n",
__func__, stream - 1, pVolume[0], pVolume[1], dev,
pCurSel[0], pCurSel[1]); */
/*
* Apply Volume if the stream is running
*/
switch (stream) {
#if defined(CONFIG_MAP_VOIP)
case CTL_STREAM_PANEL_VOIPOUT:
{
parm_vol.source = pChip->streamCtl[stream - 1].
dev_prop.p[0].source;
parm_vol.sink = pChip->streamCtl[stream - 1].
dev_prop.p[0].sink;
parm_vol.volume1 = pVolume[0];
parm_vol.volume2 = pVolume[1];
parm_vol.stream = (stream - 1);
parm_vol.gain_format = AUDIO_GAIN_FORMAT_mB;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetPlaybackVolume,
&parm_vol, NULL, 0);
}
break;
case CTL_STREAM_PANEL_VOIPIN:
{
parm_vol.source = pChip->streamCtl[stream - 1].
dev_prop.c.source;
parm_vol.volume1 = pVolume[0];
parm_vol.volume2 = pVolume[1];
parm_vol.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_SetRecordGain,
&parm_vol, NULL, 0);
}
break;
#endif
case CTL_STREAM_PANEL_PCMOUT1:
case CTL_STREAM_PANEL_PCMOUT2:
#if !defined(CONFIG_MAP_VOIP)
case CTL_STREAM_PANEL_VOIPOUT:
#endif
/*
* if current sink is diffent, dont call the driver to
* change the volume
*/
if (pCurSel[0] == dev) {
if (pChip->streamCtl[stream - 1].pSubStream != NULL)
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
else {
/* playback not started, only update
user volume setting */
parm_vol.sink = pCurSel[0];
parm_vol.volume1 = pVolume[0];
parm_vol.volume2 = pVolume[1];
parm_vol.app = AUDIO_APP_MUSIC;
AUDIO_Ctrl_Trigger(
ACTION_AUD_UpdateUserVolSetting,
&parm_vol, NULL, 0);
break;
}
aTrace(LOG_ALSA_INTERFACE,
"VolumeCtrlPut stream=%d, tream state = %d\n",
stream - 1, pStream->runtime->status->state);
if (pStream->runtime->status->state ==
SNDRV_PCM_STATE_RUNNING ||
pStream->runtime->status->state ==
SNDRV_PCM_STATE_PAUSED) {
/*
* call audio driver to set volume
*/
parm_vol.source =
pChip->streamCtl[stream -
1].dev_prop.p[0].source;
parm_vol.sink =
pChip->streamCtl[stream -
1].dev_prop.p[0].sink;
parm_vol.volume1 = pVolume[0];
parm_vol.volume2 = pVolume[1];
parm_vol.stream = (stream - 1);
parm_vol.gain_format = AUDIO_GAIN_FORMAT_mB;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetPlaybackVolume,
&parm_vol, NULL, 0);
}
}
break;
case CTL_STREAM_PANEL_FM:
/*
* if current sink is diffent, dont call the driver to
* change the volume
*/
if (pCurSel[0] == dev) {
/*
* call audio driver to set volume
*/
parm_vol.source =
pChip->streamCtl[stream - 1].dev_prop.p[0].source;
parm_vol.sink =
pChip->streamCtl[stream - 1].dev_prop.p[0].sink;
parm_vol.volume1 = pVolume[0];
parm_vol.volume2 = pVolume[1];
parm_vol.stream = (stream - 1);
parm_vol.gain_format = AUDIO_GAIN_FORMAT_mB;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetPlaybackVolume,
&parm_vol, NULL, 0);
}
break;
case CTL_STREAM_PANEL_VOICECALL:
/*
* call audio driver to set gain/volume
*/
if (!pChip->iEnablePhoneCall)
break; /* Do not set hw if not in call */
if (pCurSel[1] == dev) {
parm_vol.sink = pCurSel[1];
parm_vol.volume1 = pVolume[0];
parm_vol.gain_format = AUDIO_GAIN_FORMAT_mB;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetTelephonySpkrVolume,
&parm_vol, NULL, 0);
}
break;
case CTL_STREAM_PANEL_PCMIN:
case CTL_STREAM_PANEL_SPEECHIN:
#if !defined(CONFIG_MAP_VOIP)
case CTL_STREAM_PANEL_VOIPIN:
#endif
/*
* if current sink is diffent, dont call the driver to
* change the volume
*/
if (pCurSel[0] == dev) {
if (pChip->streamCtl[stream - 1].pSubStream != NULL)
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
else
break;
aTrace(LOG_ALSA_INTERFACE,
"VolumeCtrlPut stream=%d, stream state = %d\n",
stream - 1, pStream->runtime->status->state);
if (pStream->runtime->status->state ==
SNDRV_PCM_STATE_RUNNING ||
pStream->runtime->status->state ==
SNDRV_PCM_STATE_PAUSED) {
/*
* call audio driver to set volume
*/
parm_vol.source =
pChip->streamCtl[stream -
1].dev_prop.c.source;
parm_vol.volume1 = pVolume[0];
parm_vol.volume2 = pVolume[1];
parm_vol.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_SetRecordGain,
&parm_vol, NULL, 0);
}
}
break;
default:
break;
}
return 0;
}
/**
* SelCtrlInfo - playback sink or capture source mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about playback sink or capture source mixer.
* Update the mixer control only if the stream is not running.Call audio
* driver to apply when stream is running.
*
* Returns 0 for success.
*/
static int SelCtrlInfo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv); /* kcontrol->id.device */
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
#if 1
if (stream == CTL_STREAM_PANEL_VOICECALL)
uinfo->count = 2;
else
uinfo->count = 1;
#else
uinfo->count = 2;
#endif
stream--;
/*
* coverity[OVERRUN_STATIC] - false alarm. stream is getting
* decremented by 1 and used
*/
if (pChip->streamCtl[stream].iFlags & MIXER_STREAM_FLAGS_CAPTURE) {
uinfo->value.integer.min = AUDIO_SOURCE_ANALOG_MAIN;
uinfo->value.integer.max = MIC_TOTAL_COUNT_FOR_USER;
} else {
uinfo->value.integer.min = AUDIO_SINK_HANDSET;
uinfo->value.integer.max = AUDIO_SINK_TOTAL_COUNT;
}
uinfo->value.integer.step = 1;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
return 0;
}
/**
* SelCtrlGet - playback sink or capture source mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of playback sink or capture source mixer.
*
* Returns 0 for success.
*/
static int SelCtrlGet(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
s32 *pSel;
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
stream--;
/*
* coverity[OVERRUN_STATIC] - false alarm. stream is getting
* decremented by 1 and used
*/
pSel = pChip->streamCtl[stream].iLineSelect;
/*
* May need to get the value from driver
*/
ucontrol->value.integer.value[0] = pSel[0];
/* ucontrol->value.integer.value[1] = pSel[1]; */
/*aTrace(LOG_ALSA_INTERFACE, "\nALSA-CAPH %s,"
" stream=%d, pSel %d, numid=%d index=%d\n",
__func__, stream, pSel[0], pSel[1],
ucontrol->id.numid, ucontrol->id.index); */
return 0;
}
/**
* SelCtrlPut - playback sink or capture source mixer put callback
* @kcontrol: mixer control
* @ucontrol: the value that needs to be set.
*
* Callback to set the value of playback sink or capture source mixer.
*
* Returns 0 for success.
*/
static int SelCtrlPut(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
#if 1
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
#endif
s32 *pSel, pCurSel[MAX_PLAYBACK_DEV];
#if defined(CONFIG_MAP_VOIP)
long uctrl_action = 0, uctrl_param = 0;
#endif
struct snd_pcm_substream *pStream = NULL;
BRCM_AUDIO_Param_Spkr_t parm_spkr;
BRCM_AUDIO_Param_Call_t parm_call;
int i = 0, count = 0;
AUDIO_SINK_Enum_t curSpk;
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
/*
* coverity[OVERRUN_STATIC] - false alarm. stream is getting
* decremented by 1 and used
*/
pSel = pChip->streamCtl[stream - 1].iLineSelect;
/*
* save current setting
*/
for (i = 0; i < MAX_PLAYBACK_DEV; i++)
pCurSel[i] = pSel[i];
pSel[0] = ucontrol->value.integer.value[0];
pSel[1] = ucontrol->value.integer.value[1];
if (stream != CTL_STREAM_PANEL_VOICECALL)
pSel[1] = pSel[0];
#if defined(CONFIG_MAP_VOIP)
/* for voip out/in stream, value[0] used for control,
such as sel/add/desel/remove actions */
uctrl_action = ucontrol->value.integer.value[0];
/* for voip out/in stream, value[1] used
for audctrl control devices */
uctrl_param = ucontrol->value.integer.value[1];
#endif
if ((stream != CTL_STREAM_PANEL_VOICECALL) && (pSel[0] == pSel[1]))
pSel[1] = AUDIO_SINK_UNDEFINED;
pSel[2] = AUDIO_SINK_UNDEFINED;
#ifndef CONFIG_CAPH_STEREO_IHF
/* if stereo IHF is not supported on a
platform, then configure EP */
if (isSTIHF == TRUE) {
if (pSel[0] == AUDIO_SINK_LOUDSPK) {
if (pSel[1] != AUDIO_SINK_UNDEFINED)
pSel[2] = pSel[1];
pSel[1] = AUDIO_SINK_HANDSET;
} else if (pSel[1] == AUDIO_SINK_LOUDSPK)
pSel[2] = AUDIO_SINK_HANDSET;
}
#endif
/*aTrace(LOG_ALSA_INTERFACE, "ALSA-CAPH %s"
" stream=%d, pSel %d:%d:%d\n",
__func__, stream - 1, pSel[0], pSel[1], pSel[2]); */
switch (stream) {
case CTL_STREAM_PANEL_PCMOUT1: /* pcmout 1 */
case CTL_STREAM_PANEL_PCMOUT2: /* pcmout 2 */
curSpk = pCurSel[0];
if (pChip->streamCtl[stream - 1].pSubStream != NULL)
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
else {
/*aError("m:breaking out\n");*/
break; /* stream is not running, return */
}
if (pStream->runtime == NULL) {
aError("Stream's runtime is NULL and hence returning");
return -EINVAL;
}
/*aTrace(LOG_ALSA_INTERFACE, "SetCtrlput stream=%d,"
"stream state = %d\n",
stream - 1, pStream->runtime->status->state); */
if (pStream->runtime->status->state == SNDRV_PCM_STATE_RUNNING
|| pStream->runtime->status->state ==
SNDRV_PCM_STATE_PAUSED) {
/*
* During playback, Px-SEL can not handle multicast to
* singlecast. Reroute to Px-CHG.
* This is a workaround. It is suggested to use Px-CHG
* to do multicast.
*/
if (pCurSel[0] < AUDIO_SINK_VALID_TOTAL
&& pCurSel[1] < AUDIO_SINK_VALID_TOTAL
&& pSel[1] >= AUDIO_SINK_VALID_TOTAL) {
struct snd_kcontrol tmp_kcontrol;
struct snd_ctl_elem_value tmp_ucontrol;
s32 new_sel[MAX_PLAYBACK_DEV];
aTrace(LOG_ALSA_INTERFACE,
"ALSA-CAPH apply multicast to singlecast "
"workaround\n");
for (i = 0; i < MAX_PLAYBACK_DEV; i++) {
new_sel[i] = pSel[i];
/*restore values for Px-CHG */
pSel[i] = pCurSel[i];
}
memcpy(&tmp_kcontrol, kcontrol,
sizeof(tmp_kcontrol));
tmp_kcontrol.private_value =
CAPH_CTL_PRIVATE(stream, 0,
CTL_FUNCTION_SINK_CHG_REM);
memcpy(&tmp_ucontrol, ucontrol,
sizeof(tmp_ucontrol));
/*1 to remove a sink */
/* tmp_ucontrol.value.integer.value[0] = 1; */
if (pCurSel[0] != new_sel[0]) {
tmp_ucontrol.value.integer.value[0] =
pCurSel[0];
MiscCtrlPut(&tmp_kcontrol,
&tmp_ucontrol);
}
if (pCurSel[1] != new_sel[0]) {
tmp_ucontrol.value.integer.value[0] =
pCurSel[1];
MiscCtrlPut(&tmp_kcontrol,
&tmp_ucontrol);
}
break;
}
/*
* call audio driver to set sink, or do switching if
* the current and new device are not same
*/
for (i = 0; i < MAX_PLAYBACK_DEV; i++) {
pChip->streamCtl[stream -
1].dev_prop.p[i].source =
AUDIO_SOURCE_MEM;
if (pSel[i] != pCurSel[i]) {
if (pSel[i] >= AUDIO_SINK_HANDSET
&& pSel[i] <
AUDIO_SINK_VALID_TOTAL) {
pChip->streamCtl[stream -
1].dev_prop.
p[i].sink = pSel[i];
} else {
/*
* No valid device in the list
* to do a playback,return
* error
*/
if (++count ==
MAX_PLAYBACK_DEV) {
return -EINVAL;
} else
pChip->
streamCtl[stream -
1].
dev_prop.p[i].sink =
AUDIO_SINK_UNDEFINED
;
}
/*
* If stIHF remove EP path first.
*/
#ifndef CONFIG_CAPH_STEREO_IHF
if (i < MAX_PLAYBACK_DEV - 1)
if ((isSTIHF) &&
(pCurSel[i] == AUDIO_SINK_LOUDSPK)
&& (pChip->streamCtl[stream - 1].
dev_prop.p[i + 1].sink ==
AUDIO_SINK_HANDSET)) {
aTrace(LOG_ALSA_INTERFACE,
"Stereo IHF ,"
"remove EP path first.\n");
parm_spkr.src =
pChip->streamCtl[stream -
1].
dev_prop.p[0].source;
parm_spkr.sink = curSpk;
parm_spkr.stream = (stream - 1);
AUDIO_Ctrl_Trigger
(ACTION_AUD_RemoveChannel,
&parm_spkr, NULL, 0);
pChip->streamCtl[stream -
1].dev_prop.
p[i + 1].sink =
AUDIO_SINK_UNDEFINED;
}
#endif
if (i == 0) {
/*
* do the real switching now.
*/
parm_spkr.src =
pChip->streamCtl[stream -
1].
dev_prop.p[0].source;
parm_spkr.sink =
pChip->streamCtl[stream -
1].
dev_prop.p[0].sink;
parm_spkr.stream = (stream - 1);
AUDIO_Ctrl_Trigger
(ACTION_AUD_SwitchSpkr,
&parm_spkr, NULL, 0);
} else {
if (pChip->
streamCtl[stream -
1].dev_prop.p[i].
sink !=
AUDIO_SINK_UNDEFINED) {
parm_spkr.src =
pChip->
streamCtl[stream -
1].
dev_prop.p[0].
source;
parm_spkr.sink =
pChip->
streamCtl[stream -
1].
dev_prop.p[i].sink;
parm_spkr.stream =
(stream - 1);
AUDIO_Ctrl_Trigger
(
ACTION_AUD_AddChannel,
&parm_spkr, NULL,
0);
}
}
}
}
}
break;
case CTL_STREAM_PANEL_VOICECALL: /* voice call */
parm_call.cur_mic = pCurSel[0];
parm_call.cur_spkr = pCurSel[1];
parm_call.new_mic = pSel[0];
parm_call.new_spkr = pSel[1];
/* CHECK IF REQUIRED TINYALSATOCHECK */
if (parm_call.new_spkr != parm_call.cur_spkr) {
/*aTrace(LOG_ALSA_INTERFACE,
"parm_call.new_mic = %d"
"parm_call.new_spkr = %d\n",
parm_call.new_mic, parm_call.new_spkr);*/
AUDIO_Ctrl_Trigger(ACTION_AUD_SetTelephonyMicSpkr,
&parm_call, NULL, 0);
}
break;
case CTL_STREAM_PANEL_FM: /* FM */
curSpk = pCurSel[0];
pChip->streamCtl[stream - 1].dev_prop.p[0].source =
AUDIO_SOURCE_I2S;
if ((pChip->iEnableFM) && (!(pChip->iEnablePhoneCall))
&& (curSpk != pSel[0])) {
/*
* change the sink/spk
*/
if (pSel[0] >= AUDIO_SINK_HANDSET
&& pSel[0] < AUDIO_SINK_VALID_TOTAL) {
pChip->streamCtl[stream -
1].dev_prop.p[0].sink =
pSel[0];
} else {
aError("No device selected by the user ?\n");
return -EINVAL;
}
parm_spkr.src =
pChip->streamCtl[stream - 1].dev_prop.p[0].source;
parm_spkr.sink =
pChip->streamCtl[stream - 1].dev_prop.p[0].sink;
parm_spkr.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_SwitchSpkr, &parm_spkr,
NULL, 0);
}
break;
#if defined(CONFIG_MAP_VOIP)
/* this is the upper layer control code for map voip
there is no stream running here, need customize the code here
call the audp api here and detailed control/sync
will be done in audp */
/* TODO: we take simple approach: use add/remove to handle
3 cases in the design doc
1. start from beginning: add src/snk, remove src/snk
2. switch in the middle: remove the src/snk,
and then add new src/snk
3. add new src/snk
4. to stop, just remove src/snk
*/
case CTL_STREAM_PANEL_VOIPOUT: /* map voip dl */
case CTL_STREAM_PANEL_VOIPIN: /* map voip ul */
{
PIPE_DIRECTION_t direction = AUDIOP_PIPE_DIR_NONE;
if (stream == CTL_STREAM_PANEL_VOIPIN)
direction = AUDIOP_PIPE_DIR_UL;
else if (stream == CTL_STREAM_PANEL_VOIPOUT)
direction = AUDIOP_PIPE_DIR_DL;
audp_ctrl_handler(uctrl_action, uctrl_param, direction);
}
break;
#endif
default:
break;
}
return 0;
}
/**
* SwitchCtrlGet - mute mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of mute mixer.
*
* Returns 0 for success.
*/
static int SwitchCtrlGet(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
int dev = DEV_OF_CTL(priv);
s32 *pMute;
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
stream--;
/*
* coverity[OVERRUN_STATIC] - false alarm. stream is getting
* decremented by 1 and used
*/
pMute = pChip->streamCtl[stream].ctlLine[dev].iMute;
ucontrol->value.integer.value[0] = pMute[0];
/* ucontrol->value.integer.value[1] = pMute[1]; */
/*aTrace(LOG_ALSA_INTERFACE, "\nALSA-CAPH %s"
" stream=%d, pMute %d\n", :%d\n",
__func__, stream, pMute[0]); , pMute[1]); */
return 0;
}
/**
* SwitchCtrlPut - mute mixer put callback
* @kcontrol: mixer control
* @ucontrol: the value that needs to be set.
*
* Callback to set the value of mute mixer.
* Update the mixer control only if the stream is not running.
* Call audio driver to apply when stream is running.
*
* Returns 0 for success.
*/
static int SwitchCtrlPut(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int stream = STREAM_OF_CTL(priv);
int dev = DEV_OF_CTL(priv);
s32 *pMute;
struct snd_pcm_substream *pStream = NULL;
BRCM_AUDIO_Param_Mute_t parm_mute;
CAPH_ASSERT(stream >= CTL_STREAM_PANEL_FIRST
&& stream < CTL_STREAM_PANEL_LAST);
/*
* coverity[OVERRUN_STATIC] - false alarm. stream is getting
* decremented by 1 and used
*/
pMute = pChip->streamCtl[stream - 1].ctlLine[dev].iMute;
pMute[0] = ucontrol->value.integer.value[0];
/* pMute[1] = ucontrol->value.integer.value[1]; */
/*aTrace(LOG_ALSA_INTERFACE, "ALSA-CAPH %s"
" stream=%d, pMute %d\n",
__func__, stream - 1, pMute[0]);, pMute[1]); */
/*
* Apply mute is the stream is running
*/
switch (stream) {
case CTL_STREAM_PANEL_PCMOUT1:
case CTL_STREAM_PANEL_PCMOUT2:
case CTL_STREAM_PANEL_VOIPOUT:
if (pChip->streamCtl[stream - 1].pSubStream != NULL)
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
else
break;
aTrace(LOG_ALSA_INTERFACE,
"SwitchCtrlPut stream=%d, stream state = %d\n",
stream - 1, pStream->runtime->status->state);
if (pStream->runtime->status->state == SNDRV_PCM_STATE_RUNNING
|| pStream->runtime->status->state ==
SNDRV_PCM_STATE_PAUSED) {
/*
* call audio driver to set mute
*/
parm_mute.source =
pChip->streamCtl[stream - 1].dev_prop.p[0].source;
parm_mute.sink =
pChip->streamCtl[stream - 1].dev_prop.p[0].sink;
parm_mute.mute1 = pMute[0];
parm_mute.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_MutePlayback, &parm_mute,
NULL, 0);
}
break;
case CTL_STREAM_PANEL_FM:
/*
* call audio driver to set mute
*/
parm_mute.source =
pChip->streamCtl[stream - 1].dev_prop.p[0].source;
parm_mute.sink =
pChip->streamCtl[stream - 1].dev_prop.p[0].sink;
parm_mute.mute1 = pMute[0];
parm_mute.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_MutePlayback, &parm_mute, NULL,
0);
break;
case CTL_STREAM_PANEL_PCMIN:
if (pChip->streamCtl[stream - 1].pSubStream != NULL)
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
else
break;
/*aTrace(LOG_ALSA_INTERFACE, "SwitchCtrlPut stream=%d,"
" stream state = %d\n",
stream - 1, pStream->runtime->status->state); */
if (pStream->runtime->status->state == SNDRV_PCM_STATE_RUNNING
|| pStream->runtime->status->state ==
SNDRV_PCM_STATE_PAUSED) {
/*
* call audio driver to set mute
*/
parm_mute.source =
pChip->streamCtl[stream - 1].dev_prop.c.source;
parm_mute.mute1 = pMute[0];
parm_mute.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_MuteRecord, &parm_mute,
NULL, 0);
}
break;
case CTL_STREAM_PANEL_VOIPIN:
break;
default:
break;
}
return 0;
}
/**
* MiscCtrlInfo - MISC mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about MISC mixer.
*
* Returns 0 for success.
*/
static int MiscCtrlInfo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int priv = kcontrol->private_value;
int function = FUNC_OF_CTL(priv);
int stream = STREAM_OF_CTL(priv);
uinfo->value.integer.step = 1;
switch (function) {
case CTL_FUNCTION_LOOPBACK_TEST:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 4;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = CAPH_MAX_CTRL_LINES;
break;
case CTL_FUNCTION_PHONE_ENABLE:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 3;
break;
case CTL_FUNCTION_PHONE_CALL_MIC_MUTE:
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
break;
case CTL_FUNCTION_PHONE_ECNS_ENABLE:
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
break;
case CTL_FUNCTION_SPEECH_MIXING_OPTION:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 3;
break;
case CTL_FUNCTION_FM_ENABLE:
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
break;
case CTL_FUNCTION_FM_FORMAT:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2; /* sample rate, stereo/mono */
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 48000;
break;
case CTL_FUNCTION_AT_AUDIO:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
/*#ifndef CAPH_TINYALSA
uinfo->count = 7;
#else*/
uinfo->count = 8;
/*#endif*/
uinfo->value.integer.min = 0x80000000;
uinfo->value.integer.max = 0x7FFFFFFF;
/*
* val[0] is at command handler,
* val[1] is 1st parameter of the AT command parameters
*/
/*#ifndef CAPH_TINYALSA
if (kcontrol->id.index == 1) {
uinfo->count = 1;
uinfo->value.integer.min = 0x0;
uinfo->value.integer.max = 0x7FFFFFFF;
}
#endif*/
break;
case CTL_FUNCTION_BYPASS_VIBRA:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 4;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 6000;
break;
case CTL_FUNCTION_BT_TEST:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 100;
break;
case CTL_FUNCTION_CFG_IHF:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
/*
* integer[0] -- 1 for mono, 2 for stereo;
* integer[1] -- data mixing option if channel is mono,
* 1 for left, 2 for right, 3 for (L+R)/2
*/
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 3;
break;
case CTL_FUNCTION_CFG_SSP:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 3;
break;
case CTL_FUNCTION_VOL:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
if (CTL_STREAM_PANEL_VOICECALL == stream) {
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 5; /* volume level */
} else if (stream == CTL_STREAM_PANEL_FM) {
/* uinfo->count = 2; */
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 14; /* volume level */
}
break;
case CTL_FUNCTION_SINK_CHG_ADD:
case CTL_FUNCTION_SINK_CHG_REM:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = AUDIO_SINK_TOTAL_COUNT;
break;
case CTL_FUNCTION_HW_CTL:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 5;
uinfo->value.integer.min = 0x80000000;
/*
* get the correct range, keep it to MAX for now as it
* supports register address
*/
uinfo->value.integer.max = 0x7FFFFFFF;
break;
case CTL_FUNCTION_MODE_SEL:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 8;
break;
case CTL_FUNCTION_APP_SEL:
case CTL_FUNCTION_APP_RMV:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 17;
break;
case CTL_FUNCTION_AMP_CTL:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
break;
case CTL_FUNCTION_CALL_MODE:
/* 0:CALL_MODE_NONE;1:MODEM_CALL;2:PTT_CALL */
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 2;
break;
case CTL_FUNCTION_COMMIT_AUD_PROFILE:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
break;
default:
aWarn("%s, Unexpected function code %d\n",
__func__, function);
break;
}
return 0;
}
/**
* MiscCtrlGet - MISC mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of MISC mixer.
*
* Returns 0 for success.
*/
static int MiscCtrlGet(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int function = FUNC_OF_CTL(priv);
int stream = STREAM_OF_CTL(priv);
int rtn = 0, chn = 0;
struct snd_ctl_elem_info info;
BRCM_AUDIO_Param_AtCtl_t parm_atctl;
int ssp_index;
int hwctl_index;
switch (function) {
case CTL_FUNCTION_LOOPBACK_TEST:
ucontrol->value.integer.value[0] =
pChip->pi32LoopBackTestParam[0];
ucontrol->value.integer.value[1] =
pChip->pi32LoopBackTestParam[1];
ucontrol->value.integer.value[2] =
pChip->pi32LoopBackTestParam[2];
ucontrol->value.integer.value[3] =
pChip->pi32LoopBackTestParam[3];
break;
case CTL_FUNCTION_PHONE_ENABLE:
ucontrol->value.integer.value[0] = pChip->iEnablePhoneCall;
break;
case CTL_FUNCTION_PHONE_CALL_MIC_MUTE:
ucontrol->value.integer.value[0] = pChip->iMutePhoneCall[0];
/*ucontrol->value.integer.value[1]
= pChip->iMutePhoneCall[1]; */
break;
case CTL_FUNCTION_PHONE_ECNS_ENABLE:
ucontrol->value.integer.value[0] = pChip->iEnableECNSPhoneCall;
break;
case CTL_FUNCTION_SPEECH_MIXING_OPTION:
ucontrol->value.integer.value[0] =
pChip->pi32SpeechMixOption[stream - 1];
break;
case CTL_FUNCTION_FM_ENABLE:
ucontrol->value.integer.value[0] = pChip->iEnableFM;
break;
case CTL_FUNCTION_FM_FORMAT:
break;
case CTL_FUNCTION_AT_AUDIO:
kcontrol->info(kcontrol, &info);
/*rtn =
AtAudCtlHandler_get(kcontrol->id.index, pChip, info.count,
ucontrol->value.integer.value);*/
memset(&parm_atctl, 0, sizeof(parm_atctl));
int index = ucontrol->value.integer.value[info.count-1];
parm_atctl.cmdIndex = index;
/*parm_atctl.cmdIndex = kcontrol->id.index;*/
parm_atctl.pChip = pChip;
parm_atctl.ParamCount = info.count;
parm_atctl.isGet = 1;
memcpy(&parm_atctl.Params, ucontrol->value.integer.value,
sizeof(parm_atctl.Params));
AUDIO_Ctrl_Trigger(ACTION_AUD_AtCtl, &parm_atctl, NULL, 1);
/*copy values back to ucontrol value[] */
memcpy((void *)&(ucontrol->value.integer.value),
(void *)&parm_atctl.Params,
sizeof(parm_atctl.Params));
break;
case CTL_FUNCTION_BYPASS_VIBRA:
ucontrol->value.integer.value[0] =
pChip->pi32BypassVibraParam[0];
ucontrol->value.integer.value[1] =
pChip->pi32BypassVibraParam[1];
ucontrol->value.integer.value[2] =
pChip->pi32BypassVibraParam[2];
ucontrol->value.integer.value[3] =
pChip->pi32BypassVibraParam[3];
break;
case CTL_FUNCTION_BT_TEST:
ucontrol->value.integer.value[0] = pChip->iEnableBTTest;
break;
case CTL_FUNCTION_CFG_IHF:
ucontrol->value.integer.value[0] = pChip->pi32CfgIHF[0];
ucontrol->value.integer.value[1] = pChip->pi32CfgIHF[1];
break;
case CTL_FUNCTION_CFG_SSP:
/* TINYALSATOCHECK */
ssp_index = ucontrol->value.integer.value[1];
ucontrol->value.integer.value[0] =
pChip->i32CfgSSP[ssp_index];
break;
case CTL_FUNCTION_VOL:
/*
* Need to copy the volume for the particular stream only
*/
/*if (stream == CTL_STREAM_PANEL_VOICECALL)
// chn = 1;
//else
// chn = 2;
*/
memcpy(ucontrol->value.integer.value,
pChip->pi32LevelVolume[stream - 1], sizeof(s32));
break;
case CTL_FUNCTION_SINK_CHG_ADD:
case CTL_FUNCTION_SINK_CHG_REM:
ucontrol->value.integer.value[0] = 0;
/* ucontrol->value.integer.value[1] = 0; */
break;
case CTL_FUNCTION_HW_CTL:
/* TINYALSATOCHECK */
hwctl_index = (int)ucontrol->value.integer.
value[4];
AUDCTRL_GetHardwareControl(hwctl_index,
(void *)ucontrol->value.integer.
value);
break;
case CTL_FUNCTION_APP_SEL:
ucontrol->value.integer.value[0] = pChip->i32CurApp;
break;
case CTL_FUNCTION_MODE_SEL:
ucontrol->value.integer.value[0] = pChip->i32CurMode;
break;
case CTL_FUNCTION_APP_RMV:
/* don't need to do anything */
break;
case CTL_FUNCTION_COMMIT_AUD_PROFILE:
/* don't need to do anything */
break;
case CTL_FUNCTION_AMP_CTL:
ucontrol->value.integer.value[0] = pChip->i32CurAmpState;
break;
case CTL_FUNCTION_CALL_MODE:
ucontrol->value.integer.value[0] = pChip->iCallMode;
break;
default:
aWarn("%s, Unexpected function code %d\n",
__func__, function);
break;
}
/*aTrace(LOG_ALSA_INTERFACE, "\nALSA-CAPH %s"
" stream=%d, function %d, value %ld %ld %ld %ld %ld %ld %ld\n",
__func__, stream - 1, function,
ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1],
ucontrol->value.integer.value[2],
ucontrol->value.integer.value[3],
ucontrol->value.integer.value[4],
ucontrol->value.integer.value[5],
ucontrol->value.integer.value[6]);*/
return rtn;
}
/**
* SwitchCtrlPut - MISC mixer put callback
* @kcontrol: mixer control
* @ucontrol: the value that needs to be set.
*
* Callback to set the value of MISC mixer.
*
* Returns 0 for success.
*/
static int MiscCtrlPut(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
brcm_alsa_chip_t *pChip =
(brcm_alsa_chip_t *) snd_kcontrol_chip(kcontrol);
int priv = kcontrol->private_value;
int function = priv & 0xFF;
int *pSel, callMode;
int stream = STREAM_OF_CTL(priv);
static int vibra_count;
BRCM_AUDIO_Control_Params_un_t ctl_parm;
BRCM_AUDIO_Param_Vibra_t vibra;
int rtn = 0, cmd, i, indexVal = -1, cnt = 0;
BRCM_AUDIO_Param_AtCtl_t parm_atctl;
struct snd_pcm_substream *pStream = NULL;
int sink = 0;
struct snd_ctl_elem_info info;
pSel = pChip->streamCtl[stream - 1].iLineSelect;
#if 0
/*aTrace(LOG_ALSA_INTERFACE, "ALSA-CAPH %s"
" stream=%d, function %d, index %d, value %ld %ld %ld %ld\n",
__func__, stream - 1, function,
kcontrol->id.index,
ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1],
ucontrol->value.integer.value[2],
ucontrol->value.integer.value[3]);*/
#endif
/*aError("ALSA-CAPH %s"
" stream=%d, function %d, index %d, value %ld %ld %ld %ld
and ucontrol->id.index = %d\n",
__func__, stream - 1, function,
kcontrol->id.index,
ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1],
ucontrol->value.integer.value[2],
ucontrol->value.integer.value[3],
ucontrol->id.index);*/
switch (function) {
case CTL_FUNCTION_LOOPBACK_TEST:
ctl_parm.parm_loop.parm = pChip->pi32LoopBackTestParam[0] =
ucontrol->value.integer.value[0];
ctl_parm.parm_loop.mic = pChip->pi32LoopBackTestParam[1] =
ucontrol->value.integer.value[1];
ctl_parm.parm_loop.spkr = pChip->pi32LoopBackTestParam[2] =
ucontrol->value.integer.value[2];
ctl_parm.parm_loop.mode = pChip->pi32LoopBackTestParam[3] =
ucontrol->value.integer.value[3];
/*
* Do loopback test
*/
AUDIO_Ctrl_Trigger(ACTION_AUD_SetHWLoopback,
&ctl_parm.parm_loop, NULL, 0);
break;
case CTL_FUNCTION_PHONE_ENABLE:
/* 2 bits mask
00 - Voice call OFF
01 - Voice call ON
10 - VoIP OFF
11 - VoIP ON */
pChip->iEnablePhoneCall =
ucontrol->value.integer.value[0];
/* Right now, we don't need to differentiate
VoIP OFF/Voice call OFF.To keep the logic
simple, set the same value as Voice call
OFF for VoIP OFF */
if (pChip->iEnablePhoneCall == 2)
pChip->iEnablePhoneCall = 0;
pSel =
pChip->streamCtl[CTL_STREAM_PANEL_VOICECALL -
1].iLineSelect;
ctl_parm.parm_call.new_mic =
ctl_parm.parm_call.cur_mic = pSel[0];
ctl_parm.parm_call.new_spkr =
ctl_parm.parm_call.cur_spkr = pSel[1];
aTrace(LOG_ALSA_INTERFACE,
"MiscCtrlPut CTL_FUNCTION_PHONE_ENABLE, %d\n",
pChip->iEnablePhoneCall);
if (!pChip->iEnablePhoneCall) { /* disable voice call */
AUDIO_Ctrl_Trigger(ACTION_AUD_DisableTelephony,
&ctl_parm.parm_call, NULL, 0);
pChip->iCallMode = CALL_MODE_NONE;
} else { /* enable voice call with sink and source */
if (pChip->iCallMode == PTT_CALL) {
AUDIO_Ctrl_Trigger(ACTION_AUD_ConnectDL,
NULL, NULL, 0);
} else {
if (pChip->iEnablePhoneCall == 3) { /* VoIP ON*/
/* set to 16K for VoIP*/
ctl_parm.parm_ratechange.codecID = 0x0A;
AUDIO_Ctrl_Trigger(
ACTION_AUD_RateChange,
&ctl_parm.parm_ratechange,
NULL, 0);
}
AUDIO_Ctrl_Trigger(ACTION_AUD_EnableTelephony,
&ctl_parm.parm_call, NULL, 0);
if ((pChip->iEnablePhoneCall == 1)
|| (pChip->iEnablePhoneCall == 3))
pChip->iCallMode = MODEM_CALL;
/* Set Volume */
ctl_parm.parm_vol.stream = (stream - 1);
ctl_parm.parm_vol.source = pSel[0];
ctl_parm.parm_vol.sink = pSel[1];
ctl_parm.parm_vol.volume1 =
pChip->pi32LevelVolume[
CTL_STREAM_PANEL_VOICECALL - 1][0];
ctl_parm.parm_vol.gain_format =
AUDIO_GAIN_FORMAT_DSP_VOICE_VOL_GAIN;
AUDIO_Ctrl_Trigger(
ACTION_AUD_SetTelephonySpkrVolume,
&ctl_parm.parm_vol, NULL, 0);
}
}
break;
case CTL_FUNCTION_PHONE_CALL_MIC_MUTE:
/*if (pChip->iMutePhoneCall[0] !=
ucontrol->value.integer.value[0]) */
{
pChip->iMutePhoneCall[0] =
ucontrol->value.integer.value[0];
if (pChip->iEnablePhoneCall) { /*only in call */
pSel =
pChip->
streamCtl[CTL_STREAM_PANEL_VOICECALL -
1].iLineSelect;
/*
* call audio driver to mute
*/
ctl_parm.parm_mute.source = pSel[0];
ctl_parm.parm_mute.mute1 =
pChip->iMutePhoneCall[0];
AUDIO_Ctrl_Trigger(ACTION_AUD_MuteTelephony,
&ctl_parm.parm_mute, NULL, 0);
}
}
break;
case CTL_FUNCTION_PHONE_ECNS_ENABLE:
pChip->iEnableECNSPhoneCall = ucontrol->value.integer.value[0];
aTrace(LOG_ALSA_INTERFACE,
"MiscCtrlPut CTL_FUNCTION_PHONE_ECNS_ENABLE "
"pChip->iEnableECNSPhoneCall = %d\n",
pChip->iEnableECNSPhoneCall);
ctl_parm.parm_ecns.ec_ns = pChip->iEnableECNSPhoneCall;
if (!pChip->iEnableECNSPhoneCall) /* disable EC NS */
AUDIO_Ctrl_Trigger(ACTION_AUD_DisableECNSTelephony,
&ctl_parm.parm_ecns, NULL, 0);
else /* enable EC NS */
AUDIO_Ctrl_Trigger(ACTION_AUD_EnableECNSTelephony,
&ctl_parm.parm_ecns, NULL, 0);
break;
case CTL_FUNCTION_SPEECH_MIXING_OPTION:
pChip->pi32SpeechMixOption[stream - 1] =
ucontrol->value.integer.value[0];
break;
case CTL_FUNCTION_FM_ENABLE:
callMode = pChip->iCallMode;
/* if FM listening path is already enabled or disabled,
do nothing, return. */
if (pChip->iEnableFM == ucontrol->value.integer.value[0])
return 0;
pChip->iEnableFM = ucontrol->value.integer.value[0];
pChip->streamCtl[stream - 1].dev_prop.p[0].source =
AUDIO_SOURCE_I2S;
pSel = pChip->streamCtl[stream - 1].iLineSelect;
/*aTrace(LOG_ALSA_INTERFACE, "MiscCtrlPut
CTL_FUNCTION_FM_ENABLE"
"stream=%d, status = %d, pSel[0] = %d-%d\n",
stream, pChip->iEnableFM, pSel[0], pSel[1]); */
if (!pChip->iEnableFM) { /* disable FM */
/*
* disable the playback path
*/
ctl_parm.parm_FM.source =
pChip->streamCtl[stream - 1].dev_prop.p[0].source;
ctl_parm.parm_FM.sink =
pChip->streamCtl[stream - 1].dev_prop.p[0].sink;
ctl_parm.parm_FM.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_DisableFMPlay,
&ctl_parm.parm_FM,
NULL, 0);
} else { /* enable FM */
/*
* route the playback to CAPH
*/
pChip->streamCtl[stream - 1].dev_prop.p[0].drv_type =
AUDIO_DRIVER_PLAY_AUDIO;
if (callMode) {
pChip->streamCtl[stream -
1].dev_prop.p[0].sink =
AUDIO_SINK_DSP;
} else {
if (pSel[0] >= AUDIO_SINK_HANDSET
&& pSel[0] < AUDIO_SINK_VALID_TOTAL) {
pChip->streamCtl[stream -
1].dev_prop.p[0].sink =
pSel[0];
} else {
return -EINVAL;
}
}
if (callMode) {
ctl_parm.parm_FM.fm_mix =
(UInt32) pChip->pi32SpeechMixOption[stream -
1];
AUDIO_Ctrl_Trigger(ACTION_AUD_SetARM2SPInst,
&ctl_parm.parm_FM, NULL, 0);
}
/*
* Enable the playback the path
*/
ctl_parm.parm_FM.source =
pChip->streamCtl[stream - 1].dev_prop.p[0].source;
ctl_parm.parm_FM.sink =
pChip->streamCtl[stream - 1].dev_prop.p[0].sink;
ctl_parm.parm_FM.volume1 =
pChip->streamCtl[stream -
1].ctlLine[pSel[0]].iVolume[0];
ctl_parm.parm_FM.volume2 =
pChip->streamCtl[stream -
1].ctlLine[pSel[0]].iVolume[1];
ctl_parm.parm_FM.stream = (stream - 1);
AUDIO_Ctrl_Trigger(ACTION_AUD_EnableFMPlay,
&ctl_parm.parm_FM,
NULL, 0);
}
break;
case CTL_FUNCTION_FM_FORMAT:
break;
case CTL_FUNCTION_AT_AUDIO:
kcontrol->info(kcontrol, &info);
/*rtn =
AtAudCtlHandler_put(kcontrol->id.index, pChip, info.count,
ucontrol->value.integer.value);*/
memset(&parm_atctl, 0, sizeof(parm_atctl));
parm_atctl.cmdIndex =
ucontrol->value.integer.value[info.count-1];
/*aError("MiscCtrlPut CTL_FUNCTION_AT_AUDIO"
"parm_atctl.cmdIndex = %d\n",
(int)parm_atctl.cmdIndex);*/
/*parm_atctl.cmdIndex = kcontrol->id.index;*/
/*this pointer would not released, so no need to pass the
whole structure*/
parm_atctl.pChip = pChip;
parm_atctl.ParamCount = info.count;
parm_atctl.isGet = 0;
memcpy(&parm_atctl.Params, ucontrol->value.integer.value,
sizeof(parm_atctl.Params));
AUDIO_Ctrl_Trigger(ACTION_AUD_AtCtl, &parm_atctl, NULL, 1);
break;
case CTL_FUNCTION_BYPASS_VIBRA:
{
int index = ucontrol->id.index;
/*aTrace(LOG_ALSA_INTERFACE, "MiscCtrlPut
BypassVibra enable %d, "
"strength %d, direction %d, duration %d.\n",
ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1],
ucontrol->value.integer.value[2],
ucontrol->value.integer.value[3]); */
if (index == 0) {
pChip->pi32BypassVibraParam[0] =
ucontrol->value.integer.value[0];
} else if (index == 1) {
ctl_parm.parm_vibra.strength =
pChip->pi32BypassVibraParam[1] =
ucontrol->value.integer.value[1];
} else if (index == 2) {
ctl_parm.parm_vibra.direction =
pChip->pi32BypassVibraParam[2] =
ucontrol->value.integer.value[2];
} else if (index == 3) {
ctl_parm.parm_vibra.duration =
pChip->pi32BypassVibraParam[3] =
ucontrol->value.integer.value[3];
if (pChip->pi32BypassVibraParam[0] == 1) { /* Enable */
AUDIO_Ctrl_Trigger(ACTION_AUD_EnableByPassVibra,
&ctl_parm.parm_vibra, NULL, 0);
} else
AUDIO_Ctrl_Trigger(ACTION_AUD_DisableByPassVibra, NULL,
NULL, 0);
}
/*aTrace(LOG_ALSA_INTERFACE, "MiscCtrlPut
BypassVibra enable %d, "
"strength %d, direction %d, duration %d.\n",
pChip->pi32BypassVibraParam[0],
pChip->pi32BypassVibraParam[1],
pChip->pi32BypassVibraParam[2],
pChip->pi32BypassVibraParam[3]); */
}
break;
case CTL_FUNCTION_BT_TEST:
ctl_parm.parm_bt_test.mode =
pChip->iEnableBTTest =
ucontrol->value.integer.value[0];
AUDIO_Ctrl_Trigger(ACTION_AUD_BTTest, &ctl_parm.parm_bt_test,
NULL, 0);
break;
case CTL_FUNCTION_CFG_IHF:
pChip->pi32CfgIHF[0] = ucontrol->value.integer.value[0];
pChip->pi32CfgIHF[1] = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[0] == 1) { /* Mono IHF */
ctl_parm.parm_cfg_ihf.stIHF = FALSE;
isSTIHF = FALSE;
AUDIO_Ctrl_Trigger(ACTION_AUD_CfgIHF,
&ctl_parm.parm_cfg_ihf,
NULL, 0);
} else if (ucontrol->value.integer.value[0] == 2) {
#ifdef CONFIG_CAPH_STEREO_IHF
ctl_parm.parm_cfg_ihf.stIHF = TRUE; /* stereo IHF */
isSTIHF = TRUE; /* stereo IHF */
AUDIO_Ctrl_Trigger(ACTION_AUD_CfgIHF,
&ctl_parm.parm_cfg_ihf,
NULL, 0);
#endif
} else {
/*aWarn("%s, Invalid value for"
"setting IHF mode: %ld, 1-mono, 2-stereo.",
__func__, ucontrol->value.integer.value[0]); */
}
break;
case CTL_FUNCTION_CFG_SSP:
/* bus is tdm: 0-pcm 1-i2s 2-mux 3-tdm */
if (ucontrol->value.integer.value[0] == 3) {
ctl_parm.parm_cfg_ssp.mode = 2;
ctl_parm.parm_cfg_ssp.bus = 2;
ctl_parm.parm_cfg_ssp.en_lpbk = 0;
AUDIO_Ctrl_Trigger(ACTION_AUD_CfgSSP,
&ctl_parm.parm_cfg_ssp,
NULL, 0);
} else {
int index = (int)ucontrol->value.integer.value[1];
aTrace(LOG_ALSA_INTERFACE,
"MiscCtrlPut CTL_FUNCTION_CFG_SSP "
"index = %d\n",
index);
ctl_parm.parm_cfg_ssp.mode = index + 1;
ctl_parm.parm_cfg_ssp.bus =
pChip->i32CfgSSP[index] =
ucontrol->value.integer.value[0];
ctl_parm.parm_cfg_ssp.en_lpbk = 0;
/*
* Port is 1 base
*/
AUDIO_Ctrl_Trigger(ACTION_AUD_CfgSSP,
&ctl_parm.parm_cfg_ssp,
NULL, 0);
}
break;
case CTL_FUNCTION_VOL:
ctl_parm.parm_vol.stream = (stream - 1);
if (stream == CTL_STREAM_PANEL_VOICECALL) {
memcpy(pChip->pi32LevelVolume[stream - 1],
ucontrol->value.integer.value, sizeof(s32));
/*
* source and sink are set in function SelCtrlPut()
*/
if (!pChip->iEnablePhoneCall)
break; /* Do not set hw if not in call */
ctl_parm.parm_vol.source = pSel[0];
ctl_parm.parm_vol.sink = pSel[1];
ctl_parm.parm_vol.sink = pSel[0];
ctl_parm.parm_vol.volume1 =
pChip->pi32LevelVolume[CTL_STREAM_PANEL_VOICECALL -
1][0];
ctl_parm.parm_vol.gain_format =
AUDIO_GAIN_FORMAT_DSP_VOICE_VOL_GAIN;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetTelephonySpkrVolume,
&ctl_parm.parm_vol, NULL, 0);
} else if (stream == CTL_STREAM_PANEL_FM) {
memcpy(pChip->pi32LevelVolume[stream - 1],
/* ucontrol->value.integer.value, 2 * sizeof(s32)); */
ucontrol->value.integer.value, sizeof(s32));
/*
* source and sink are set in function SelCtrlPut()
*/
ctl_parm.parm_vol.source = pChip->streamCtl[stream - 1]
.dev_prop.p[0].source; /* AUDIO_SOURCE_I2S */
ctl_parm.parm_vol.sink = pSel[0];
/* left and right vol */
ctl_parm.parm_vol.volume2 = ctl_parm.parm_vol.volume1 =
pChip->pi32LevelVolume[CTL_STREAM_PANEL_FM - 1][0];
/* right vol */
/*ctl_parm.parm_vol.volume2 =
pChip->pi32LevelVolume[CTL_STREAM_PANEL_FM - 1][1]; */
ctl_parm.parm_vol.gain_format =
AUDIO_GAIN_FORMAT_FM_RADIO_DIGITAL_VOLUME_TABLE;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetPlaybackVolume,
&ctl_parm.parm_vol, NULL, 0);
}
break;
case CTL_FUNCTION_SINK_CHG_ADD:
aTrace
(LOG_ALSA_INTERFACE,
"Change sink stream=%d"
"sink=%ld\n", stream - 1,
ucontrol->value.integer.value[0]);
/* ucontrol->value.integer.value[1]); */
/* cmd = ucontrol->value.integer.value[0]; */
pSel = pChip->streamCtl[stream - 1].iLineSelect;
/* if (cmd == 0) { */ /*add device */
for (i = 0; i < MAX_PLAYBACK_DEV; i++) {
if (pSel[i] == AUDIO_SINK_UNDEFINED
&& indexVal == -1) {
indexVal = i;
continue;
} else if (pSel[i] ==
ucontrol->value.integer.value[0]) {
indexVal = -1;
aTrace(LOG_ALSA_INTERFACE,
"Device already added "
"in the list\n");
break;
} else if (++cnt == MAX_PLAYBACK_DEV) {
aTrace(LOG_ALSA_INTERFACE,
"Max devices count "
"reached. Cannot add"
"more device\n");
return -1;
}
}
if (indexVal != -1) {
pSel[indexVal] =
ucontrol->value.integer.value[0];
if (pChip->streamCtl[stream - 1].pSubStream !=
NULL) {
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
/*
* if the stream is running, then call
* the audio driver API to add
* the device
*/
if (pStream->runtime->status->state ==
SNDRV_PCM_STATE_RUNNING
|| pStream->runtime->status->
state == SNDRV_PCM_STATE_PAUSED) {
if (pSel[indexVal] >=
AUDIO_SINK_HANDSET
&& pSel[indexVal] <
AUDIO_SINK_VALID_TOTAL) {
ctl_parm.parm_spkr.src =
AUDIO_SOURCE_MEM;
ctl_parm.
parm_spkr.sink =
pSel[indexVal];
ctl_parm.
parm_spkr.
stream =
(stream - 1);
AUDIO_Ctrl_Trigger
(ACTION_AUD_AddChannel,
&ctl_parm.parm_spkr,
NULL, 0);
}
}
}
}
break;
case CTL_FUNCTION_SINK_CHG_REM:
/*aTrace
(LOG_ALSA_INTERFACE,
"Change sink stream=%d"
"sink=%ld\n", stream - 1,
ucontrol->value.integer.value[0]);*/
/* ucontrol->value.integer.value[1]); */
/* cmd = ucontrol->value.integer.value[0]; */
pSel = pChip->streamCtl[stream - 1].iLineSelect;
/* else if (cmd == 1) { */ /* remove device */
for (i = 0; i < MAX_PLAYBACK_DEV; i++) {
if (pSel[i] == ucontrol->value.integer.value[0]
&& indexVal == -1) {
/* sink to remove */
indexVal = i;
sink = pSel[indexVal];
if (i != 0)
break;
} else if (indexVal != -1) {
if (pSel[i] != AUDIO_SINK_UNDEFINED) {
pSel[indexVal] = pSel[i];
indexVal = i;
break;
}
}
}
if (indexVal != -1) {
if (pChip->streamCtl[stream - 1].pSubStream !=
NULL) {
pStream =
(struct snd_pcm_substream *)pChip->
streamCtl[stream - 1].pSubStream;
/*
* if the stream is running, then call
* the audio driver API to remove
* the device
*/
if (pStream->runtime->status->state ==
SNDRV_PCM_STATE_RUNNING
|| pStream->runtime->status->
state == SNDRV_PCM_STATE_PAUSED) {
ctl_parm.parm_spkr.src =
AUDIO_SOURCE_MEM;
ctl_parm.parm_spkr.sink = sink;
ctl_parm.parm_spkr.stream =
(stream - 1);
AUDIO_Ctrl_Trigger(
ACTION_AUD_RemoveChannel,
&ctl_parm.parm_spkr,
NULL, 0);
}
}
pSel[indexVal] = AUDIO_SINK_UNDEFINED;
}
break;
case CTL_FUNCTION_HW_CTL:
/* ADDED FOR TINYALSA */
ctl_parm.parm_hwCtl.access_type =
(int)ucontrol->value.integer.value[4];
aTrace(LOG_ALSA_INTERFACE,
"ucontrol->value.integer.value[4] = %d ,"
"kcontrol->id.index =%d",
(int)ucontrol->value.integer.value[4],
(int)kcontrol->id.index);
ctl_parm.parm_hwCtl.arg1 =
(int)ucontrol->value.integer.value[0];
ctl_parm.parm_hwCtl.arg2 =
(int)ucontrol->value.integer.value[1];
ctl_parm.parm_hwCtl.arg3 =
(int)ucontrol->value.integer.value[2];
ctl_parm.parm_hwCtl.arg4 =
(int)ucontrol->value.integer.value[3];
AUDIO_Ctrl_Trigger
(ACTION_AUD_HwCtl,
&ctl_parm.parm_hwCtl, NULL, 0);
break;
case CTL_FUNCTION_APP_SEL:
aTrace(LOG_ALSA_INTERFACE,
"CTL_FUNCTION_APP_SEL newApp=%d",
(int)ucontrol->value.integer.value[0]);
pChip->i32CurApp =
ucontrol->value.integer.value[0];
/* Make the call to Audio Controller here */
ctl_parm.parm_setapp.aud_app =
(int)ucontrol->value.integer.value[0];
/*enable app after user space is ready*/
AUDIO_Ctrl_Trigger(ACTION_AUD_SetAudioApp,
&ctl_parm.parm_setapp,
NULL, 0);
break;
case CTL_FUNCTION_MODE_SEL:
aTrace(LOG_ALSA_INTERFACE,
"CTL_FUNCTION_MODE_SEL newMode=%d",
(int)ucontrol->value.integer.value[0]);
pChip->i32CurMode =
ucontrol->value.integer.value[0];
ctl_parm.parm_setmode.aud_mode =
(int)ucontrol->value.integer.value[0];
/*set audio mode from values from user space*/
AUDIO_Ctrl_Trigger(ACTION_AUD_SetAudioMode,
&ctl_parm.parm_setmode,
NULL, 0);
break;
case CTL_FUNCTION_APP_RMV:
aTrace(LOG_ALSA_INTERFACE,
"CTL_FUNCTION_APP_RMV rmApp=%d",
(int)ucontrol->value.integer.value[0]);
/* Make the call to Audio Controller here */
ctl_parm.parm_rmapp.aud_app =
(int)ucontrol->value.integer.value[0];
/*remove app after user application is gone*/
AUDIO_Ctrl_Trigger(ACTION_AUD_RemoveAudioApp,
&ctl_parm.parm_rmapp,
NULL, 0);
break;
case CTL_FUNCTION_COMMIT_AUD_PROFILE:
aTrace(LOG_ALSA_INTERFACE,
"CTL_FUNCTION_COMMIT_AUD_PROFILE ");
/*Set the audio profile based on the existing
app profile and the audio mode*/
AUDIO_Ctrl_Trigger(ACTION_AUD_CommitAudioProfile,
NULL,
NULL, 0);
break;
case CTL_FUNCTION_AMP_CTL:
aTrace(LOG_ALSA_INTERFACE,
"CTL_FUNCTION_AMP_CTL curAmpStatus =%d, newAmpStatus=%d",
(int)pChip->i32CurAmpState,
(int)ucontrol->value.integer.value[0]);
pChip->i32CurAmpState = ucontrol->value.integer.value[0];
/* Make the call to Audio Controller here */
ctl_parm.parm_ampctl.amp_status =
(int)ucontrol->value.integer.value[0];
AUDIO_Ctrl_Trigger(ACTION_AUD_AMPEnable,
&ctl_parm.parm_ampctl, NULL, 0);
break;
case CTL_FUNCTION_CALL_MODE:
if (pChip->iCallMode == PTT_CALL &&
ucontrol->value.integer.value[0] == CALL_MODE_NONE) {
/* In case telephony path not disabled */
ctl_parm.parm_call.new_mic =
ctl_parm.parm_call.cur_mic = pSel[0];
ctl_parm.parm_call.new_spkr =
ctl_parm.parm_call.cur_spkr = pSel[1];
AUDIO_Ctrl_Trigger(ACTION_AUD_DisableTelephony,
&ctl_parm.parm_call, NULL, 0);
}
pChip->iCallMode = ucontrol->value.integer.value[0];
aTrace(LOG_ALSA_INTERFACE,
"CTL_FUNCTION_CALL_MODE callMode =%d\n",
pChip->iCallMode);
ctl_parm.parm_callmode.callMode = pChip->iCallMode;
AUDIO_Ctrl_Trigger(ACTION_AUD_SetCallMode,
&ctl_parm.parm_callmode, NULL, 0);
pSel =
pChip->streamCtl[CTL_STREAM_PANEL_VOICECALL -
1].iLineSelect;
ctl_parm.parm_call.new_mic =
ctl_parm.parm_call.cur_mic = pSel[0];
ctl_parm.parm_call.new_spkr =
ctl_parm.parm_call.cur_spkr = pSel[1];
/* In PTT call, setup telephony path with DL
disconnected */
if (pChip->iCallMode == PTT_CALL)
AUDIO_Ctrl_Trigger(ACTION_AUD_EnableTelephony,
&ctl_parm.parm_call, NULL, 0);
break;
default:
aWarn("%s, Unexpected function code %d\n",
__func__, function);
break;
}
return rtn;
}
/*
* The DECLARE_TLV_DB_SCALE macro defines information about a mixer control
* where each step in the control's value changes the dB value by a constant dB
* amount.
* The first parameter is the name of the variable to be defined. The second
* parameter is the minimum value, in units of 0.01 dB. The third parameter is
* the step size, in units of 0.01 dB. Set the fourth parameter to 1 if the
* minimum value actually mutes the control.
* Control value is in mB, minimium -50db, step 0.01db, minimium
* does not mean MUTE
*/
static const DECLARE_TLV_DB_SCALE(caph_db_scale_volume, -5000, 1, 0);
#define BRCM_MIXER_CTRL_GENERAL(nIface, iDevice, iSubdev, sName, iIndex, \
iAccess, iCount, fInfo, fGet, fPut, pTlv, lPriv_val) \
{ \
.iface = nIface, \
.device = iDevice, \
.subdevice = iSubdev, \
.name = sName, \
.index = iIndex, \
.access = iAccess,\
.count = iCount, \
.info = fInfo, \
.get = fGet, \
.put = fPut, \
.tlv = { .p = pTlv }, \
.private_value = lPriv_val, \
}
#define BRCM_MIXER_CTRL_VOLUME(dev, subdev, xname, xindex, private_val) \
BRCM_MIXER_CTRL_GENERAL(SNDRV_CTL_ELEM_IFACE_MIXER, dev, subdev, \
xname, xindex, SNDRV_CTL_ELEM_ACCESS_READWRITE, 0, VolumeCtrlInfo, \
VolumeCtrlGet, VolumeCtrlPut, caph_db_scale_volume, private_val)
#define BRCM_MIXER_CTRL_SWITCH(dev, subdev, xname, xindex, private_val) \
BRCM_MIXER_CTRL_GENERAL(SNDRV_CTL_ELEM_IFACE_MIXER, dev, subdev, \
xname, xindex, SNDRV_CTL_ELEM_ACCESS_READWRITE, 0, \
snd_ctl_boolean_mono_info, SwitchCtrlGet, SwitchCtrlPut, 0, \
private_val)
#define BRCM_MIXER_CTRL_SELECTION(dev, subdev, xname, xindex, private_val) \
BRCM_MIXER_CTRL_GENERAL(SNDRV_CTL_ELEM_IFACE_MIXER, dev, subdev, \
xname, xindex, SNDRV_CTL_ELEM_ACCESS_READWRITE, 0, SelCtrlInfo, \
SelCtrlGet, SelCtrlPut, 0, private_val)
#define BRCM_MIXER_CTRL_MISC(dev, subdev, xname, xindex, private_val) \
BRCM_MIXER_CTRL_GENERAL(SNDRV_CTL_ELEM_IFACE_MIXER, dev, subdev, \
xname, xindex, SNDRV_CTL_ELEM_ACCESS_READWRITE, 0, MiscCtrlInfo, \
MiscCtrlGet, MiscCtrlPut, 0, private_val)
#define BRCM_MIXER_CTRL_MISC_W(dev, subdev, xname, xindex, private_val) \
BRCM_MIXER_CTRL_GENERAL(SNDRV_CTL_ELEM_IFACE_MIXER, dev, subdev, \
xname, xindex, SNDRV_CTL_ELEM_ACCESS_WRITE, 0, MiscCtrlInfo, \
MiscCtrlGet, MiscCtrlPut, 0, private_val)
/*
* Sink device and source devices
* {.strName = "Handset", }, //AUDIO_SINK_HANDSET
* {.strName = "Headset", }, //AUDIO_SINK_HEADSET
* x{.strName = "Handsfree",}, //AUDIO_SINK_HANDSFREE
* {.strName = "BT SCO", }, //AUDIO_SINK_BTM
* {.strName = "Loud Speaker", }, //AUDIO_SINK_LOUDSPK
* {.strName = "", }, //AUDIO_SINK_TTY
* {.strName = "", }, //AUDIO_SINK_HAC
* x{.strName = "", }, //AUDIO_SINK_USB
* x{.strName = "", }, //AUDIO_SINK_BTS
* {.strName = "I2S", }, //AUDIO_SINK_I2S
* {.strName = "Speaker Vibra", }, //AUDIO_SINK_VIBRA
*
* {.strName = "", }, //AUDIO_SOURCE_UNDEFINED
* {.strName = "Main Mic",}, //AUDIO_SOURCE_ANALOG_MAIN
* {.strName = "AUX Mic",}, //AUDIO_SOURCE_AUX
* {.strName = "Digital MIC 1",}, //AUDIO_SOURCE_DIGI1
* {.strName = "Digital MIC 2",}, //AUDIO_SOURCE_DIGI2
* x{.strName = "Digital Mic 12",}, //AUDCTRL_DUAL_MIC_DIGI12
* x{.strName = "Digital Mic 21",}, //AUDCTRL_DUAL_MIC_DIGI21
* x{.strName = "MIC_ANALOG_DIGI1",}, //AUDCTRL_DUAL_MIC_ANALOG_DIGI1
* x{.strName = "MIC_DIGI1_ANALOG",}, //AUDCTRL_DUAL_MIC_DIGI1_ANALOG
* {.strName = "BT SCO Mic",}, //AUDIO_SOURCE_BTM
* x{.strName = "", }, //AUDIO_SOURCE_USB
* {.strName = "I2S",}, //AUDIO_SOURCE_I2S
* x{.strName = "MIC_DIGI3",},}, //AUDIO_SOURCE_DIGI3
* x{.strName = "MIC_DIGI4",}, //AUDIO_SOURCE_DIGI4
* x{.strName = "MIC_SPEECH_DIGI",}, //AUDIO_SOURCE_SPEECH_DIGI
* x{.strName = "MIC_EANC_DIGI",}, //AUDIO_SOURCE_EANC_DIGI
*/
/*
* must match AUDIO_SINK_Enum_t
*/
#define BCM_CTL_SINK_LINES {\
/*AUDIO_SINK_HANDSET*/ {.strName = "HNT", .iVolume = {0, 0},}, \
/*AUDIO_SINK_HEADSET*/ {.strName = "HST", .iVolume = {-400, -400},},\
/*AUDIO_SINK_HANDSFREE*/{.strName = "HNF", .iVolume = {0, 0},}, \
/*AUDIO_SINK_BTM*/ {.strName = "BTM", .iVolume = {0, 0},}, \
/*AUDIO_SINK_LOUDSPK*/ {.strName = "SPK", .iVolume = {400, 400},},\
/*AUDIO_SINK_TTY*/ {.strName = "TTY", .iVolume = {-400, -400},},\
/*AUDIO_SINK_HAC*/ {.strName = "HAC", .iVolume = {0, 0},}, \
/*AUDIO_SINK_USB*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SINK_BTS*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SINK_I2S*/ {.strName = "I2S", .iVolume = {0, 0},}, \
/*AUDIO_SINK_VIBRA*/ {.strName = "VIB", .iVolume = {0, 0},}, \
/*AUDIO_SINK_HEADPHONE*/{.strName = "", .iVolume = {0, 0},}, \
}
/*
* must match AUDIO_SOURCE_Enum_t
*/
#define BCM_CTL_SRC_LINES { \
/*AUDIO_SOURCE_UNDEFINED*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SOURCE_ANALOG_MAIN*/ {.strName = "MIC", .iVolume = {3000, 3000},},\
/*AUDIO_SOURCE_ANALOG_AUX*/ {.strName = "AUX", .iVolume = {3000, 3000},},\
/*AUDIO_SOURCE_DIGI1*/ {.strName = "DG1", .iVolume = {700, 700},},\
/*AUDIO_SOURCE_DIGI2*/ {.strName = "DG2", .iVolume = {700, 700},},\
/*AUDIO_SOURCE_DIGI3*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SOURCE_DIGI4*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SOURCE_MIC_ARRAY1*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SOURCE_MIC_ARRAY2*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SOURCE_BTM*/ {.strName = "BTM", .iVolume = {700, 700},},\
/*AUDIO_SOURCE_USB*/ {.strName = "", .iVolume = {0, 0},}, \
/*AUDIO_SOURCE_I2S*/ {.strName = "I2S", .iVolume = {300, 300},},\
/*AUDIO_SOURCE_RESERVED1*/ {.strName = "", .iVolume = {0, 0},},\
/*AUDIO_SOURCE_RESERVED2*/ {.strName = "", .iVolume = {0, 0},},\
}
/*
* Initial data of controls, runtime data is in 'chip' data structure
*/
static TPcm_Stream_Ctrls sgCaphStreamCtls[CAPH_MAX_PCM_STREAMS]
= {
/*
* PCMOut1
*/
{
.iTotalCtlLines = AUDIO_SINK_TOTAL_COUNT,
.iLineSelect = {AUDIO_SINK_HANDSET, AUDIO_SINK_UNDEFINED,
AUDIO_SINK_UNDEFINED},
.strStreamName = "P1",
.ctlLine = BCM_CTL_SINK_LINES,
},
/*
* PCMOut2
*/
{
.iTotalCtlLines = AUDIO_SINK_TOTAL_COUNT,
.iLineSelect = {AUDIO_SINK_LOUDSPK, AUDIO_SINK_UNDEFINED,
AUDIO_SINK_UNDEFINED},
.strStreamName = "P2",
.ctlLine = BCM_CTL_SINK_LINES,
},
/*
* VOIP Out
*/
{
.iTotalCtlLines = AUDIO_SINK_TOTAL_COUNT,
.iLineSelect = {AUDIO_SINK_LOUDSPK, AUDIO_SINK_LOUDSPK},
.strStreamName = "VD",
.ctlLine = BCM_CTL_SINK_LINES,
},
/*
* PCM In
*/
{
.iFlags = MIXER_STREAM_FLAGS_CAPTURE,
.iTotalCtlLines = MIC_TOTAL_COUNT_FOR_USER,
.iLineSelect = {AUDIO_SOURCE_ANALOG_MAIN, AUDIO_SOURCE_ANALOG_MAIN},
.strStreamName = "C1",
.ctlLine = BCM_CTL_SRC_LINES,
},
/*
* Speech In
*/
{
.iFlags = MIXER_STREAM_FLAGS_CAPTURE,
.iTotalCtlLines = MIC_TOTAL_COUNT_FOR_USER,
.iLineSelect = {AUDIO_SOURCE_ANALOG_MAIN, AUDIO_SOURCE_ANALOG_MAIN},
.strStreamName = "C2",
.ctlLine = BCM_CTL_SRC_LINES,
},
/*
* Capture Mix O/P In
*/
{
.iFlags = MIXER_STREAM_FLAGS_CAPTURE,
.iTotalCtlLines = MIC_TOTAL_COUNT_FOR_USER,
.iLineSelect = {AUDIO_SOURCE_IHF, AUDIO_SOURCE_IHF},
.strStreamName = "C3",
.ctlLine = BCM_CTL_SRC_LINES,
},
/*
* VOIP In
*/
{
.iFlags = MIXER_STREAM_FLAGS_CAPTURE,
.iTotalCtlLines = MIC_TOTAL_COUNT_FOR_USER,
.iLineSelect = {AUDIO_SOURCE_ANALOG_MAIN, AUDIO_SOURCE_ANALOG_MAIN},
.strStreamName = "VU",
.ctlLine = BCM_CTL_SRC_LINES,
},
/*
* Voice call
*/
{
.iFlags = MIXER_STREAM_FLAGS_CALL,
.iTotalCtlLines = AUDIO_SINK_TOTAL_COUNT,
.iLineSelect = {AUDIO_SOURCE_ANALOG_MAIN, AUDIO_SINK_HANDSET},
.strStreamName = "VC",
.ctlLine = BCM_CTL_SINK_LINES,
},
/*
* FM Radio
*/
{
.iFlags = MIXER_STREAM_FLAGS_FM,
.iTotalCtlLines = AUDIO_SINK_TOTAL_COUNT,
.iLineSelect = {AUDIO_SINK_HEADSET, AUDIO_SINK_HEADSET},
.strStreamName = "FM",
.ctlLine = BCM_CTL_SINK_LINES,
},
};
/*
* Misc controls
*/
static struct snd_kcontrol_new sgSndCtrls[] = {
BRCM_MIXER_CTRL_MISC(0, 0, "LPT", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_LOOPBACK_TEST)),
BRCM_MIXER_CTRL_MISC(0, 0, "AT-AUD", AT_AUD_CTL_INDEX,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_AT_AUDIO)),
BRCM_MIXER_CTRL_MISC(0, 0, "AT-AUD", AT_AUD_CTL_DBG_LEVEL,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_AT_AUDIO)),
BRCM_MIXER_CTRL_MISC(0, 0, "AT-AUD", AT_AUD_CTL_HANDLER,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_AT_AUDIO)),
BRCM_MIXER_CTRL_MISC(0, 0, "VC-SWT", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_VOICECALL, 0,
CTL_FUNCTION_PHONE_ENABLE)),
BRCM_MIXER_CTRL_MISC(0, 0, "VC-MUT", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_VOICECALL, 0,
CTL_FUNCTION_PHONE_CALL_MIC_MUTE)),
BRCM_MIXER_CTRL_MISC(0, 0, "VC-ENC", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_VOICECALL, 0,
CTL_FUNCTION_PHONE_ECNS_ENABLE)),
BRCM_MIXER_CTRL_MISC(0, 0, "CALL-MODE", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_VOICECALL, 0,
CTL_FUNCTION_CALL_MODE)),
BRCM_MIXER_CTRL_MISC(0, 0, "P1-MIX", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_PCMOUT1, 0,
CTL_FUNCTION_SPEECH_MIXING_OPTION)),
BRCM_MIXER_CTRL_MISC(0, 0, "P2-MIX", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_PCMOUT2, 0,
CTL_FUNCTION_SPEECH_MIXING_OPTION)),
BRCM_MIXER_CTRL_MISC(0, 0, "C2-MIX", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_SPEECHIN, 0,
CTL_FUNCTION_SPEECH_MIXING_OPTION)),
BRCM_MIXER_CTRL_MISC(0, 0, "FM-MIX", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_FM, 0,
CTL_FUNCTION_SPEECH_MIXING_OPTION)),
BRCM_MIXER_CTRL_MISC(0, 0, "FM-SWT", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_FM, 0,
CTL_FUNCTION_FM_ENABLE)),
BRCM_MIXER_CTRL_MISC(0, 0, "FM-FMT", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_FM, 0,
CTL_FUNCTION_FM_FORMAT)),
BRCM_MIXER_CTRL_MISC(0, 0, "BYP-VIB", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_BYPASS_VIBRA)),
BRCM_MIXER_CTRL_MISC(0, 0, "BT-TST", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_BT_TEST)),
BRCM_MIXER_CTRL_MISC(0, 0, "CFG-IHF", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_CFG_IHF)),
BRCM_MIXER_CTRL_MISC(0, 0, "CFG-SSP", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_CFG_SSP)), /* SSPI1 */
BRCM_MIXER_CTRL_MISC(0, 0, "CFG-SSP", 1,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_CFG_SSP)), /* SSPI2 */
BRCM_MIXER_CTRL_MISC(0, 0, "CFG-SSP", 2,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_CFG_SSP)), /* SSPI3 */
BRCM_MIXER_CTRL_MISC(0, 0, "VC-VOL-LEVEL", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_VOICECALL, 0,
CTL_FUNCTION_VOL)),
BRCM_MIXER_CTRL_MISC(0, 0, "FM-VOL-LEVEL", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_FM, 0,
CTL_FUNCTION_VOL)),
BRCM_MIXER_CTRL_MISC(0, 0, "P1-ADD", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_PCMOUT1, 0,
CTL_FUNCTION_SINK_CHG_ADD)),
BRCM_MIXER_CTRL_MISC(0, 0, "P1-REM", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_PCMOUT1, 0,
CTL_FUNCTION_SINK_CHG_REM)),
BRCM_MIXER_CTRL_MISC(0, 0, "P2-ADD", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_PCMOUT2, 0,
CTL_FUNCTION_SINK_CHG_ADD)),
BRCM_MIXER_CTRL_MISC(0, 0, "P2-REM", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_PCMOUT2, 0,
CTL_FUNCTION_SINK_CHG_REM)),
BRCM_MIXER_CTRL_MISC(0, 0, "APP-SEL", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_APP_SEL)),
BRCM_MIXER_CTRL_MISC(0, 0, "MODE-SEL", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_MODE_SEL)),
BRCM_MIXER_CTRL_MISC(0, 0, "APP-RMV", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_APP_RMV)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_HEADSET,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_IHF,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_SSP,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_MFD,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_CLK,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_WAIT,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_DMA,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_DUALMIC_REFMIC,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_DAC_LPBK,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_DOCKING,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_EXTRA_VOLUME,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_ARM2SP,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_HUB,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_IHFDL,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_PRIMARY_MIC,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_CFG_ECHO_REF_MIC,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_READ_GAIN,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_WRITE_GAIN,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_READ_REG,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_WRITE_REG,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_PRINT_PATH,
CAPH_CTL_PRIVATE(1, 1, CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "HW-CTL", AUDCTRL_HW_PRINT_MICS,
CAPH_CTL_PRIVATE(1, 1, CTL_FUNCTION_HW_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "AMP-CTL", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_AMP_CTL)),
BRCM_MIXER_CTRL_MISC(0, 0, "COMMIT-AUD-PROFILE", 0,
CAPH_CTL_PRIVATE(CTL_STREAM_PANEL_MISC, 1,
CTL_FUNCTION_COMMIT_AUD_PROFILE)),
};
#define MAX_CTL_NUMS 161
#define MAX_CTL_NAME_LENGTH 44
static char gStrCtlNames[MAX_CTL_NUMS][MAX_CTL_NAME_LENGTH] ;
static Int32 sgCaphSpeechMixCtrls[CAPH_MAX_PCM_STREAMS] = { 1, 1,
0, 3, 3, 0, 0, 1
};
/**
* ControlDeviceNew
* @card: the sound card
*
* Create control device.
* Return 0 for success, non-zero for error code
*/
int ControlDeviceNew(struct snd_card *card)
{
unsigned int idx, j;
int err = 0;
brcm_alsa_chip_t *pChip = (brcm_alsa_chip_t *) card->private_data;
int nIndex = 0;
strncpy(card->mixername, "Broadcom CAPH Mixer", 20);
memcpy(pChip->streamCtl, &sgCaphStreamCtls, sizeof(sgCaphStreamCtls));
/*
* setting the default mixer selection for speech mixing
*/
memcpy(pChip->pi32SpeechMixOption, &sgCaphSpeechMixCtrls,
sizeof(sgCaphSpeechMixCtrls));
for (idx = 0; idx < ARRAY_SIZE(sgCaphStreamCtls); idx++) {
/*
* Selection
*/
struct snd_kcontrol_new devSelect =
BRCM_MIXER_CTRL_SELECTION(0, 0, 0, 0, 0);
snprintf(gStrCtlNames[nIndex], 7, "%s-SEL",
sgCaphStreamCtls[idx].strStreamName);
devSelect.name = gStrCtlNames[nIndex++];
devSelect.private_value = CAPH_CTL_PRIVATE(idx + 1, 0, 0);
CAPH_ASSERT(strlen(devSelect.name) < MAX_CTL_NAME_LENGTH);
err = snd_ctl_add(card, snd_ctl_new1(&devSelect, pChip));
if (err < 0) {
aError("Error to add devselect idx=%d\n", idx);
return err;
}
/*
* volume mute
*/
for (j = 0; j < sgCaphStreamCtls[idx].iTotalCtlLines; j++) {
struct snd_kcontrol_new kctlVolume =
BRCM_MIXER_CTRL_VOLUME(0, 0, 0, 0, 0);
struct snd_kcontrol_new kctlMute =
BRCM_MIXER_CTRL_SWITCH(0, 0, "Mute", 0, 0);
if (sgCaphStreamCtls[idx].ctlLine[j].strName[0] == 0)
continue; /* dummy line */
if (sgCaphStreamCtls[idx].
iFlags & MIXER_STREAM_FLAGS_CAPTURE) {
snprintf(gStrCtlNames[nIndex], 11, "%s-%s-GAN",
sgCaphStreamCtls[idx].strStreamName,
sgCaphStreamCtls[idx].ctlLine[j].
strName);
kctlVolume.name = gStrCtlNames[nIndex++];
} else {
snprintf(gStrCtlNames[nIndex], 11, "%s-%s-VOL",
sgCaphStreamCtls[idx].strStreamName,
sgCaphStreamCtls[idx].ctlLine[j].
strName);
kctlVolume.name = gStrCtlNames[nIndex++];
}
kctlVolume.private_value =
CAPH_CTL_PRIVATE(idx + 1, j, CTL_FUNCTION_VOL);
kctlMute.private_value =
CAPH_CTL_PRIVATE(idx + 1, j, CTL_FUNCTION_MUTE);
CAPH_ASSERT(strlen(kctlVolume.name) <
MAX_CTL_NAME_LENGTH);
err =
snd_ctl_add(card, snd_ctl_new1(&kctlVolume, pChip));
if (err < 0) {
/*aError("error to add volume for "
"idx=%d j=%d err=%d\n", idx, j, err); */
return err;
}
if (0 == (sgCaphStreamCtls[idx].iFlags &
MIXER_STREAM_FLAGS_CALL)) {
/*
* Not for voice call, voice call use only
* one MIC mute
*/
snprintf(gStrCtlNames[nIndex], 11, "%s-%s-MUT",
sgCaphStreamCtls[idx].strStreamName,
sgCaphStreamCtls[idx].ctlLine[j].
strName);
kctlMute.name = gStrCtlNames[nIndex++];
CAPH_ASSERT(strlen(kctlMute.name) <
MAX_CTL_NAME_LENGTH);
err =
snd_ctl_add(card,
snd_ctl_new1(&kctlMute, pChip));
if (err < 0) {
/*aError("error to add mute for"
" idx=%d j=%d err=%d\n", idx, j,
err); */
return err;
}
}
}
}
CAPH_ASSERT(nIndex < MAX_CTL_NUMS);
/* MISC */
for (j = 0; j < (sizeof((sgSndCtrls)) / sizeof(sgSndCtrls[0])); j++) {
err = snd_ctl_add(card, snd_ctl_new1(&sgSndCtrls[j], pChip));
if (err < 0) {
/*aError("error (err=%d) when adding control "
"name=%s index=%d\n", err,
sgSndCtrls[j].name, sgSndCtrls[j].index); */
return err;
}
}
/*
* default value
* must be consistent with driver. It is better to get hardware setting
*/
pChip->i32CfgSSP[1] = 1;
return err;
}
/**
* caphassert: debugging assert util
*
*/
void caphassert(const char *fcn, int line, const char *expr)
{
int x;
aError("ASSERTION FAILED, %s:%s:%d %s\n", __FILE__, fcn, line, expr);
x = *(int *)0; /* force proc to exit */
}