blob: a4cc03a2e77ac5cfed165c49cc5b513fcefee9d3 [file] [log] [blame]
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +01001/*
2 * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "audio_hw_voice"
Christopher N. Hesse86f7ea82017-02-15 09:41:05 +010018#define LOG_NDEBUG 0
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010019/*#define VERY_VERY_VERBOSE_LOGGING*/
20#ifdef VERY_VERY_VERBOSE_LOGGING
21#define ALOGVV ALOGV
22#else
23#define ALOGVV(a...) do { } while(0)
24#endif
25
26#include <stdlib.h>
27#include <pthread.h>
28
Christopher N. Hesse696959d2017-02-02 20:49:55 +010029#include <cutils/log.h>
30#include <cutils/properties.h>
31
32#include <samsung_audio.h>
33
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010034#include "audio_hw.h"
35#include "voice.h"
36
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +010037#ifdef AUDIENCE_EARSMART_IC
38#include "audience.h"
39#endif
40
Christopher N. Hesse696959d2017-02-02 20:49:55 +010041static struct pcm_config pcm_config_voicecall = {
42 .channels = 2,
43 .rate = 8000,
44 .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
45 .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
46 .format = PCM_FORMAT_S16_LE,
47};
48
49static struct pcm_config pcm_config_voicecall_wideband = {
50 .channels = 2,
51 .rate = 16000,
52 .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
53 .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
54 .format = PCM_FORMAT_S16_LE,
55};
56
Andreas Schneider05bc1882017-02-09 14:03:11 +010057struct pcm_config pcm_config_voice_sco = {
58 .channels = 1,
59 .rate = SCO_DEFAULT_SAMPLING_RATE,
60 .period_size = SCO_PERIOD_SIZE,
61 .period_count = SCO_PERIOD_COUNT,
62 .format = PCM_FORMAT_S16_LE,
63};
64
Fevax51bd12c2017-03-15 10:56:39 -030065struct pcm_config pcm_config_voice_sco_wb = {
66 .channels = 1,
Martin Hoffmannf1a35392017-07-26 12:10:37 +020067 .rate = SCO_WB_SAMPLING_RATE,
Fevax51bd12c2017-03-15 10:56:39 -030068 .period_size = SCO_PERIOD_SIZE,
69 .period_count = SCO_PERIOD_COUNT,
70 .format = PCM_FORMAT_S16_LE,
71};
72
Christopher N. Hesse696959d2017-02-02 20:49:55 +010073/* Prototypes */
74int start_voice_call(struct audio_device *adev);
75int stop_voice_call(struct audio_device *adev);
76
77void set_voice_session_audio_path(struct voice_session *session)
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010078{
Christopher N. Hesse696959d2017-02-02 20:49:55 +010079 enum _AudioPath device_type;
Andreas Schneiderd44edaa2017-02-13 16:21:35 +010080 int rc;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010081
82 switch(session->out_device) {
83 case AUDIO_DEVICE_OUT_SPEAKER:
84 device_type = SOUND_AUDIO_PATH_SPEAKER;
85 break;
86 case AUDIO_DEVICE_OUT_EARPIECE:
Andreas Schneidere6286b92017-02-14 17:24:23 +010087 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010088 break;
89 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
90 device_type = SOUND_AUDIO_PATH_HEADSET;
91 break;
92 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
93 device_type = SOUND_AUDIO_PATH_HEADPHONE;
94 break;
95 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
96 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
97 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
98 device_type = SOUND_AUDIO_PATH_BLUETOOTH;
99 break;
100 default:
Andreas Schneidere6286b92017-02-14 17:24:23 +0100101 /* if output device isn't supported, use earpiece by default */
102 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100103 break;
104 }
105
106 ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
107
Andreas Schneiderd44edaa2017-02-13 16:21:35 +0100108 rc = ril_set_call_audio_path(&session->ril, device_type);
109 ALOGE_IF(rc != 0, "Failed to set audio path: (%d)", rc);
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100110}
111
112/*
113 * This decides based on the output device, if we enable
114 * two mic control
115 */
116void prepare_voice_session(struct voice_session *session,
117 audio_devices_t active_out_devices)
118{
119 ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
120
121 session->out_device = active_out_devices;
122
123 switch (session->out_device) {
124 case AUDIO_DEVICE_OUT_EARPIECE:
125 case AUDIO_DEVICE_OUT_SPEAKER:
126 session->two_mic_control = true;
127 break;
128 default:
129 session->two_mic_control = false;
130 break;
131 }
132
133 if (session->two_mic_disabled) {
134 session->two_mic_control = false;
135 }
136}
137
138/*
Andreas Schneider05bc1882017-02-09 14:03:11 +0100139 * This must be called with the hw device mutex locked, OK to hold other
140 * mutexes.
141 */
142static void stop_voice_session_bt_sco(struct voice_session *session) {
143 ALOGV("%s: Closing SCO PCMs", __func__);
144
145 if (session->pcm_sco_rx != NULL) {
146 pcm_stop(session->pcm_sco_rx);
147 pcm_close(session->pcm_sco_rx);
148 session->pcm_sco_rx = NULL;
149 }
150
151 if (session->pcm_sco_tx != NULL) {
152 pcm_stop(session->pcm_sco_tx);
153 pcm_close(session->pcm_sco_tx);
154 session->pcm_sco_tx = NULL;
155 }
156}
157
158/* must be called with the hw device mutex locked, OK to hold other mutexes */
159void start_voice_session_bt_sco(struct voice_session *session)
160{
Fevax51bd12c2017-03-15 10:56:39 -0300161 struct pcm_config *voice_sco_config;
162
Andreas Schneider05bc1882017-02-09 14:03:11 +0100163 if (session->pcm_sco_rx != NULL || session->pcm_sco_tx != NULL) {
164 ALOGW("%s: SCO PCMs already open!\n", __func__);
165 return;
166 }
167
168 ALOGV("%s: Opening SCO PCMs", __func__);
169
Christopher N. Hesse6a960022018-01-29 03:28:10 +0100170 if (session->vdata->bluetooth_wb) {
Fevax51bd12c2017-03-15 10:56:39 -0300171 ALOGV("%s: pcm_config wideband", __func__);
172 voice_sco_config = &pcm_config_voice_sco_wb;
173 } else {
174 ALOGV("%s: pcm_config narrowband", __func__);
175 voice_sco_config = &pcm_config_voice_sco;
176 }
Andreas Schneider05bc1882017-02-09 14:03:11 +0100177
178 session->pcm_sco_rx = pcm_open(SOUND_CARD,
179 SOUND_PLAYBACK_SCO_DEVICE,
180 PCM_OUT|PCM_MONOTONIC,
Fevax51bd12c2017-03-15 10:56:39 -0300181 voice_sco_config);
Andreas Schneider05bc1882017-02-09 14:03:11 +0100182 if (session->pcm_sco_rx != NULL && !pcm_is_ready(session->pcm_sco_rx)) {
183 ALOGE("%s: cannot open PCM SCO RX stream: %s",
184 __func__, pcm_get_error(session->pcm_sco_rx));
185 goto err_sco_rx;
186 }
187
188 session->pcm_sco_tx = pcm_open(SOUND_CARD,
189 SOUND_CAPTURE_SCO_DEVICE,
190 PCM_IN|PCM_MONOTONIC,
Fevax51bd12c2017-03-15 10:56:39 -0300191 voice_sco_config);
Andreas Schneider05bc1882017-02-09 14:03:11 +0100192 if (session->pcm_sco_tx && !pcm_is_ready(session->pcm_sco_tx)) {
193 ALOGE("%s: cannot open PCM SCO TX stream: %s",
194 __func__, pcm_get_error(session->pcm_sco_tx));
195 goto err_sco_tx;
196 }
197
198 pcm_start(session->pcm_sco_rx);
199 pcm_start(session->pcm_sco_tx);
200
201 return;
202
203err_sco_tx:
204 pcm_close(session->pcm_sco_tx);
205 session->pcm_sco_tx = NULL;
206err_sco_rx:
207 pcm_close(session->pcm_sco_rx);
208 session->pcm_sco_rx = NULL;
209}
210/*
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100211 * This function must be called with hw device mutex locked, OK to hold other
212 * mutexes
213 */
214int start_voice_session(struct voice_session *session)
215{
216 struct pcm_config *voice_config;
217
218 if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
219 ALOGW("%s: Voice PCMs already open!\n", __func__);
220 return 0;
221 }
222
223 ALOGV("%s: Opening voice PCMs", __func__);
224
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100225 /* TODO: Handle wb_amr=2 */
226 if (session->wb_amr_type >= 1) {
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100227 ALOGV("%s: pcm_config wideband", __func__);
228 voice_config = &pcm_config_voicecall_wideband;
229 } else {
230 ALOGV("%s: pcm_config narrowband", __func__);
231 voice_config = &pcm_config_voicecall;
232 }
233
234 /* Open modem PCM channels */
235 session->pcm_voice_rx = pcm_open(SOUND_CARD,
236 SOUND_PLAYBACK_VOICE_DEVICE,
237 PCM_OUT|PCM_MONOTONIC,
238 voice_config);
239 if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
240 ALOGE("%s: cannot open PCM voice RX stream: %s",
241 __func__,
242 pcm_get_error(session->pcm_voice_rx));
243
244 pcm_close(session->pcm_voice_tx);
245 session->pcm_voice_tx = NULL;
246
247 return -ENOMEM;
248 }
249
250 session->pcm_voice_tx = pcm_open(SOUND_CARD,
251 SOUND_CAPTURE_VOICE_DEVICE,
252 PCM_IN|PCM_MONOTONIC,
253 voice_config);
254 if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
255 ALOGE("%s: cannot open PCM voice TX stream: %s",
256 __func__,
257 pcm_get_error(session->pcm_voice_tx));
258
259 pcm_close(session->pcm_voice_rx);
260 session->pcm_voice_rx = NULL;
261
262 return -ENOMEM;
263 }
264
265 pcm_start(session->pcm_voice_rx);
266 pcm_start(session->pcm_voice_tx);
267
Andreas Schneider05bc1882017-02-09 14:03:11 +0100268 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
269 start_voice_session_bt_sco(session);
270 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100271
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100272#ifdef AUDIENCE_EARSMART_IC
273 ALOGV("%s: Enabling Audience IC", __func__);
274 es_start_voice_session(session);
275#endif
276
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100277 if (session->two_mic_control) {
278 ALOGV("%s: enabling two mic control", __func__);
279 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
280 } else {
281 ALOGV("%s: disabling two mic control", __func__);
282 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
283 }
284
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100285 return 0;
286}
287
288/*
289 * This function must be called with hw device mutex locked, OK to hold other
290 * mutexes
291 */
292void stop_voice_session(struct voice_session *session)
293{
294 int status = 0;
295
Usaamah Patelb8250d02017-12-19 15:42:38 +0000296 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_STOP);
297
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100298 ALOGV("%s: Closing active PCMs", __func__);
299
300 if (session->pcm_voice_rx != NULL) {
301 pcm_stop(session->pcm_voice_rx);
302 pcm_close(session->pcm_voice_rx);
303 session->pcm_voice_rx = NULL;
304 status++;
305 }
306
307 if (session->pcm_voice_tx != NULL) {
308 pcm_stop(session->pcm_voice_tx);
309 pcm_close(session->pcm_voice_tx);
310 session->pcm_voice_tx = NULL;
311 status++;
312 }
313
Andreas Schneider05bc1882017-02-09 14:03:11 +0100314 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
315 stop_voice_session_bt_sco(session);
316 }
317
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100318#ifdef AUDIENCE_EARSMART_IC
319 ALOGV("%s: Disabling Audience IC", __func__);
320 es_stop_voice_session();
321#endif
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100322
323 session->out_device = AUDIO_DEVICE_NONE;
324
325 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
326}
327
328void set_voice_session_volume(struct voice_session *session, float volume)
329{
330 enum _SoundType sound_type;
331
332 switch (session->out_device) {
333 case AUDIO_DEVICE_OUT_EARPIECE:
334 sound_type = SOUND_TYPE_VOICE;
335 break;
336 case AUDIO_DEVICE_OUT_SPEAKER:
337 sound_type = SOUND_TYPE_SPEAKER;
338 break;
339 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
340 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
341 sound_type = SOUND_TYPE_HEADSET;
342 break;
343 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
344 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
345 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
346 case AUDIO_DEVICE_OUT_ALL_SCO:
347 sound_type = SOUND_TYPE_BTVOICE;
348 break;
349 default:
350 sound_type = SOUND_TYPE_VOICE;
351 }
352
353 ril_set_call_volume(&session->ril, sound_type, volume);
354}
355
Andreas Schneider107a8482017-02-06 12:36:31 +0100356void set_voice_session_mic_mute(struct voice_session *session, bool state)
357{
358 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
359
360 ril_set_mute(&session->ril, mute_condition);
361}
362
Andreas Schneider82f32482017-02-06 09:00:48 +0100363bool voice_session_uses_twomic(struct voice_session *session)
364{
365 if (session->two_mic_disabled) {
366 return false;
367 }
368
369 return session->two_mic_control;
370}
371
Andreas Schneider59486fa2017-02-06 09:16:39 +0100372bool voice_session_uses_wideband(struct voice_session *session)
373{
Christopher N. Hessee4a1c592018-01-16 18:33:38 +0100374 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
Christopher N. Hesse6a960022018-01-29 03:28:10 +0100375 return session->vdata->bluetooth_wb;
Christopher N. Hessee4a1c592018-01-16 18:33:38 +0100376 }
377
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100378 return session->wb_amr_type >= 1;
Andreas Schneider59486fa2017-02-06 09:16:39 +0100379}
380
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100381static void voice_session_wb_amr_callback(void *data, int wb_amr_type)
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100382{
383 struct audio_device *adev = (struct audio_device *)data;
384 struct voice_session *session =
385 (struct voice_session *)adev->voice.session;
386
387 pthread_mutex_lock(&adev->lock);
388
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100389 if (session->wb_amr_type != wb_amr_type) {
390 session->wb_amr_type = wb_amr_type;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100391
392 /* reopen the modem PCMs at the new rate */
393 if (adev->voice.in_call) {
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100394 ALOGV("%s: %s wide band voice call (WB_AMR=%d)",
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100395 __func__,
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100396 wb_amr_type > 0 ? "Enable" : "Disable",
397 wb_amr_type);
398
399 /* TODO Handle wb_amr_type=2 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100400
Andreas Schneidere9a44a22017-02-14 13:00:48 +0100401 /*
402 * We need stop the PCM and start with the
403 * wide band pcm_config.
404 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100405 stop_voice_call(adev);
406 start_voice_call(adev);
407 }
408 }
409
410 pthread_mutex_unlock(&adev->lock);
411}
412
413struct voice_session *voice_session_init(struct audio_device *adev)
414{
415 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100416 struct voice_session *session;
417 int ret;
418
419 session = calloc(1, sizeof(struct voice_session));
420 if (session == NULL) {
421 return NULL;
422 }
423
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100424 /* Two mic control */
425 ret = property_get_bool("audio_hal.disable_two_mic", false);
426 if (ret > 0) {
427 session->two_mic_disabled = true;
428 }
429
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100430 /* Do this as the last step so we do not have to close it on error */
431 ret = ril_open(&session->ril);
432 if (ret != 0) {
433 free(session);
434 return NULL;
435 }
436
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100437 ret = property_get("audio_hal.force_voice_config", voice_config, "");
438 if (ret > 0) {
439 if ((strncmp(voice_config, "narrow", 6)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100440 session->wb_amr_type = 0;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100441 else if ((strncmp(voice_config, "wide", 4)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100442 session->wb_amr_type = 1;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100443 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
444 } else {
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100445 if (RIL_UNSOL_SNDMGR_WB_AMR_REPORT > 0) {
446 /* register callback for wideband AMR setting */
447 ret = ril_set_wb_amr_callback(&session->ril,
448 voice_session_wb_amr_callback,
449 (void *)adev);
450 if (ret != 0) {
451 ALOGE("%s: Failed to register WB_AMR callback", __func__);
452 free(session);
453 return NULL;
454 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100455
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100456 ALOGV("%s: Registered WB_AMR callback", __func__);
457 } else {
458 ALOGV("%s: WB_AMR callback not supported", __func__);
459 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100460 }
461
Christopher N. Hesse6a960022018-01-29 03:28:10 +0100462 session->vdata = &adev->voice;
463
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100464 return session;
465}
466
467void voice_session_deinit(struct voice_session *session)
468{
469 ril_close(&session->ril);
470 free(session);
471}