[alsa-devel] handling voice calls in ALSA soc (on Droid 4)
Hi!
During voice call, I need audio components to be active "as if" aplay or arecord was running, because modem needs to comunicate with speaker and microphone.
I hacked something up, but... I believe I'll need help here. Look at "enable_call" for "interesting" stuff I had to do.
...and also. What is right interface for this? Mixer component that says if voice call is active or not?
Any ideas?
Thanks, Pavel
(edited). +++ b/sound/soc/codecs/cpcap.c @@ -330,6 +330,10 @@ static const char * const cpcap_in_left_mux_texts[] = { "Off", "Mic 2", "Ext Left" };
+static const char * const cpcap_mode_texts[] = { + "Normal", "Handsfree", "Call", +}; + /* * input muxes use unusual register layout, so that we need to use custom * getter/setter methods @@ -354,6 +358,8 @@ static SOC_ENUM_SINGLE_DECL(cpcap_hs_l_mux_enum, 0, 6, cpcap_out_mux_texts); static SOC_ENUM_SINGLE_DECL(cpcap_emu_l_mux_enum, 0, 7, cpcap_out_mux_texts); static SOC_ENUM_SINGLE_DECL(cpcap_emu_r_mux_enum, 0, 8, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_mode_enum, 0, 9, cpcap_mode_texts); + static int cpcap_output_mux_get_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -442,6 +448,211 @@ static int cpcap_output_mux_put_enum(struct snd_kcontrol *kcontrol, return 0; }
+static int mode; + +static int cpcap_mode_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = mode; + + return 0; +} + +static struct snd_soc_dai *voice_codec_dai_hack; + +static int enable_call(struct snd_soc_component *component, int on) +{ + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rt; + + rt = snd_soc_get_pcm_runtime(component->card, "40126000.mcbsp-cpcap-voice"); + printk("num_dai: %d, got runtime %lx\n", component->num_dai, rt); + + if (rt) { + snd_soc_dapm_stream_event(rt, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_START); + snd_soc_dapm_stream_event(rt, SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_START); + } + + cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, 1, 19200000); + cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, 8000); + + cpcap_voice_set_dai_fmt(voice_codec_dai_hack, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); + + return 0; +} + +static int cpcap_mode_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + unsigned int muxval = ucontrol->value.enumerated.item[0]; + + printk("Requested mode %d\n", muxval); + + mode = muxval; + + switch (muxval) { + case 1: + enable_call(component, 1); + break; + case 2: + enable_call(component, 1); + + regmap_assert(cpcap, CPCAP_REG_TXI, 0xffff, 0x0cc6); ... + break; + + default: + break; + } + + return 0; +} + static int cpcap_input_right_mux_get_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -630,6 +841,10 @@ static const struct snd_kcontrol_new cpcap_earpiece_mux = SOC_DAPM_ENUM_EXT("Earpiece", cpcap_earpiece_mux_enum, cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_mode = + SOC_DAPM_ENUM_EXT("Mode", cpcap_mode_enum, + cpcap_mode_get_enum, cpcap_mode_put_enum); + static const struct snd_kcontrol_new cpcap_hifi_mono_mixer_controls[] = { SOC_DAPM_SINGLE("HiFi Mono Playback Switch", CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC1, 1, 0), @@ -771,6 +986,9 @@ static const struct snd_soc_dapm_widget cpcap_dapm_widgets[] = { SND_SOC_DAPM_MUX("EMU Left Playback Route", SND_SOC_NOPM, 0, 0, &cpcap_emu_left_mux),
+ SND_SOC_DAPM_MUX("Mode", SND_SOC_NOPM, 0, 0, + &cpcap_mode), + /* Output Amplifier */ SND_SOC_DAPM_PGA("Earpiece PGA", CPCAP_REG_RXOA, CPCAP_BIT_A1_EAR_EN, 0, NULL, 0), @@ -791,7 +1009,7 @@ static const struct snd_soc_dapm_widget cpcap_dapm_widgets[] = { SND_SOC_DAPM_PGA("EMU Left PGA", CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_L_EN, 0, NULL, 0),
- /* Headet Charge Pump */ + /* Headset Charge Pump */ SND_SOC_DAPM_SUPPLY("Headset Charge Pump", CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, 0, NULL, 0),
@@ -1304,6 +1525,7 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, u16 val = 0x0000; int err;
+ voice_codec_dai_hack = codec_dai; dev_dbg(component->dev, "Voice setup dai format (%08x)", fmt);
/* @@ -1343,10 +1565,7 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, break; }
- if (val & BIT(CPCAP_BIT_CLK_INV)) - val &= ~BIT(CPCAP_BIT_CLK_INV); - else - val |= BIT(CPCAP_BIT_CLK_INV); + val ^= BIT(CPCAP_BIT_CLK_INV);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S:
On Mon, Jun 11, 2018 at 12:25:30PM +0200, Pavel Machek wrote:
...and also. What is right interface for this? Mixer component that says if voice call is active or not?
Your modem should be represented as a component in the system and have an input and an output representing the input and output on the cell network. See speyside for an example of how this can look in software though there's a bunch of different ways modems can appear so you might not have a fully digital link like that.
On Mon 2018-06-11 12:10:11, Mark Brown wrote:
On Mon, Jun 11, 2018 at 12:25:30PM +0200, Pavel Machek wrote:
...and also. What is right interface for this? Mixer component that says if voice call is active or not?
Your modem should be represented as a component in the system and have an input and an output representing the input and output on the cell network. See speyside for an example of how this can look in software though there's a bunch of different ways modems can appear so you might not have a fully digital link like that.
Thanks for the pointer.
With setup like that, how does userland tell kernel that the baseband <-> microphone/speaker connection should be activated?
Best regards, Pavel
On Mon, Jun 11, 2018 at 02:01:58PM +0200, Pavel Machek wrote:
With setup like that, how does userland tell kernel that the baseband <-> microphone/speaker connection should be activated?
Audio routing should be done as normal, and ideally the driver for the modem will be able to figure out if there's an active call or not. If userspace has to enable the input and output manually then you can set up SOC_DAPM_PIN_SWITCH()es as normal.
Hi!
Sebastian, would you have pointer to original Motorola sources you used for inspiration?
With setup like that, how does userland tell kernel that the baseband <-> microphone/speaker connection should be activated?
Audio routing should be done as normal, and ideally the driver for the modem will be able to figure out if there's an active call or not. If userspace has to enable the input and output manually then you can set up SOC_DAPM_PIN_SWITCH()es as normal.
Modem talks AT commands, so the driver is in userspace for now.
I tried SOC_DAPM_PIN_SWITCH(), but it results in alsamixer oopsing, I guess I'm doing something wrong.
Message from syslogd@devuan at Jun 12 13:51:31 ... kernel:[ 743.678588] BUG: spinlock bad magic on CPU#1, alsamixer/2217
Message from syslogd@devuan at Jun 12 13:51:31 ... kernel:[ 743.684417] lock: 0xede423a0, .magic: eee2a6a4, .owner: <none>/-1, .owner_cpu: -287136604
I'm trying to understand how it is supposed to work, but https://www.alsa-project.org/main/index.php/DAPM has TODO's at critical places. If there's better source of information, let me know.
Best regards, Pavel
participants (2)
-
Mark Brown
-
Pavel Machek