blob: 2219d9a4ef892bb554687563067aef51782e4204 [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 */
stenkinevgeniy53929f72018-07-09 11:20:36 +0000142void stop_voice_session_bt_sco(struct audio_device *adev) {
Andreas Schneider05bc1882017-02-09 14:03:11 +0100143 ALOGV("%s: Closing SCO PCMs", __func__);
144
stenkinevgeniy53929f72018-07-09 11:20:36 +0000145 if (adev->pcm_sco_rx != NULL) {
146 pcm_stop(adev->pcm_sco_rx);
147 pcm_close(adev->pcm_sco_rx);
148 adev->pcm_sco_rx = NULL;
Andreas Schneider05bc1882017-02-09 14:03:11 +0100149 }
150
stenkinevgeniy53929f72018-07-09 11:20:36 +0000151 if (adev->pcm_sco_tx != NULL) {
152 pcm_stop(adev->pcm_sco_tx);
153 pcm_close(adev->pcm_sco_tx);
154 adev->pcm_sco_tx = NULL;
Andreas Schneider05bc1882017-02-09 14:03:11 +0100155 }
156}
157
158/* must be called with the hw device mutex locked, OK to hold other mutexes */
stenkinevgeniy53929f72018-07-09 11:20:36 +0000159void start_voice_session_bt_sco(struct audio_device *adev)
Andreas Schneider05bc1882017-02-09 14:03:11 +0100160{
Fevax51bd12c2017-03-15 10:56:39 -0300161 struct pcm_config *voice_sco_config;
162
stenkinevgeniy53929f72018-07-09 11:20:36 +0000163 if (adev->pcm_sco_rx != NULL || adev->pcm_sco_tx != NULL) {
Andreas Schneider05bc1882017-02-09 14:03:11 +0100164 ALOGW("%s: SCO PCMs already open!\n", __func__);
165 return;
166 }
167
168 ALOGV("%s: Opening SCO PCMs", __func__);
169
stenkinevgeniy53929f72018-07-09 11:20:36 +0000170 if (adev->voice.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
stenkinevgeniy53929f72018-07-09 11:20:36 +0000178 adev->pcm_sco_rx = pcm_open(SOUND_CARD,
Andreas Schneider05bc1882017-02-09 14:03:11 +0100179 SOUND_PLAYBACK_SCO_DEVICE,
180 PCM_OUT|PCM_MONOTONIC,
Fevax51bd12c2017-03-15 10:56:39 -0300181 voice_sco_config);
stenkinevgeniy53929f72018-07-09 11:20:36 +0000182 if (adev->pcm_sco_rx != NULL && !pcm_is_ready(adev->pcm_sco_rx)) {
Andreas Schneider05bc1882017-02-09 14:03:11 +0100183 ALOGE("%s: cannot open PCM SCO RX stream: %s",
stenkinevgeniy53929f72018-07-09 11:20:36 +0000184 __func__, pcm_get_error(adev->pcm_sco_rx));
Andreas Schneider05bc1882017-02-09 14:03:11 +0100185 goto err_sco_rx;
186 }
187
stenkinevgeniy53929f72018-07-09 11:20:36 +0000188 adev->pcm_sco_tx = pcm_open(SOUND_CARD,
Andreas Schneider05bc1882017-02-09 14:03:11 +0100189 SOUND_CAPTURE_SCO_DEVICE,
190 PCM_IN|PCM_MONOTONIC,
Fevax51bd12c2017-03-15 10:56:39 -0300191 voice_sco_config);
stenkinevgeniy53929f72018-07-09 11:20:36 +0000192 if (adev->pcm_sco_tx && !pcm_is_ready(adev->pcm_sco_tx)) {
Andreas Schneider05bc1882017-02-09 14:03:11 +0100193 ALOGE("%s: cannot open PCM SCO TX stream: %s",
stenkinevgeniy53929f72018-07-09 11:20:36 +0000194 __func__, pcm_get_error(adev->pcm_sco_tx));
Andreas Schneider05bc1882017-02-09 14:03:11 +0100195 goto err_sco_tx;
196 }
197
stenkinevgeniy53929f72018-07-09 11:20:36 +0000198 pcm_start(adev->pcm_sco_rx);
199 pcm_start(adev->pcm_sco_tx);
Andreas Schneider05bc1882017-02-09 14:03:11 +0100200
201 return;
202
203err_sco_tx:
stenkinevgeniy53929f72018-07-09 11:20:36 +0000204 pcm_close(adev->pcm_sco_tx);
205 adev->pcm_sco_tx = NULL;
Andreas Schneider05bc1882017-02-09 14:03:11 +0100206err_sco_rx:
stenkinevgeniy53929f72018-07-09 11:20:36 +0000207 pcm_close(adev->pcm_sco_rx);
208 adev->pcm_sco_rx = NULL;
Andreas Schneider05bc1882017-02-09 14:03:11 +0100209}
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
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100268#ifdef AUDIENCE_EARSMART_IC
269 ALOGV("%s: Enabling Audience IC", __func__);
270 es_start_voice_session(session);
271#endif
272
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100273 if (session->two_mic_control) {
274 ALOGV("%s: enabling two mic control", __func__);
275 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
276 } else {
277 ALOGV("%s: disabling two mic control", __func__);
278 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
279 }
280
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100281 return 0;
282}
283
284/*
285 * This function must be called with hw device mutex locked, OK to hold other
286 * mutexes
287 */
288void stop_voice_session(struct voice_session *session)
289{
290 int status = 0;
291
Usaamah Patelb8250d02017-12-19 15:42:38 +0000292 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_STOP);
293
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100294 ALOGV("%s: Closing active PCMs", __func__);
295
296 if (session->pcm_voice_rx != NULL) {
297 pcm_stop(session->pcm_voice_rx);
298 pcm_close(session->pcm_voice_rx);
299 session->pcm_voice_rx = NULL;
300 status++;
301 }
302
303 if (session->pcm_voice_tx != NULL) {
304 pcm_stop(session->pcm_voice_tx);
305 pcm_close(session->pcm_voice_tx);
306 session->pcm_voice_tx = NULL;
307 status++;
308 }
309
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100310#ifdef AUDIENCE_EARSMART_IC
311 ALOGV("%s: Disabling Audience IC", __func__);
312 es_stop_voice_session();
313#endif
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100314
315 session->out_device = AUDIO_DEVICE_NONE;
316
317 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
318}
319
320void set_voice_session_volume(struct voice_session *session, float volume)
321{
322 enum _SoundType sound_type;
323
324 switch (session->out_device) {
325 case AUDIO_DEVICE_OUT_EARPIECE:
326 sound_type = SOUND_TYPE_VOICE;
327 break;
328 case AUDIO_DEVICE_OUT_SPEAKER:
329 sound_type = SOUND_TYPE_SPEAKER;
330 break;
331 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
332 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
333 sound_type = SOUND_TYPE_HEADSET;
334 break;
335 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
336 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
337 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
338 case AUDIO_DEVICE_OUT_ALL_SCO:
339 sound_type = SOUND_TYPE_BTVOICE;
340 break;
341 default:
342 sound_type = SOUND_TYPE_VOICE;
343 }
344
345 ril_set_call_volume(&session->ril, sound_type, volume);
346}
347
Andreas Schneider107a8482017-02-06 12:36:31 +0100348void set_voice_session_mic_mute(struct voice_session *session, bool state)
349{
350 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
351
352 ril_set_mute(&session->ril, mute_condition);
353}
354
Andreas Schneider82f32482017-02-06 09:00:48 +0100355bool voice_session_uses_twomic(struct voice_session *session)
356{
357 if (session->two_mic_disabled) {
358 return false;
359 }
360
361 return session->two_mic_control;
362}
363
Andreas Schneider59486fa2017-02-06 09:16:39 +0100364bool voice_session_uses_wideband(struct voice_session *session)
365{
Christopher N. Hessee4a1c592018-01-16 18:33:38 +0100366 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
Christopher N. Hesse6a960022018-01-29 03:28:10 +0100367 return session->vdata->bluetooth_wb;
Christopher N. Hessee4a1c592018-01-16 18:33:38 +0100368 }
369
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100370 return session->wb_amr_type >= 1;
Andreas Schneider59486fa2017-02-06 09:16:39 +0100371}
372
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100373static void voice_session_wb_amr_callback(void *data, int wb_amr_type)
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100374{
375 struct audio_device *adev = (struct audio_device *)data;
376 struct voice_session *session =
377 (struct voice_session *)adev->voice.session;
378
379 pthread_mutex_lock(&adev->lock);
380
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100381 if (session->wb_amr_type != wb_amr_type) {
382 session->wb_amr_type = wb_amr_type;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100383
384 /* reopen the modem PCMs at the new rate */
385 if (adev->voice.in_call) {
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100386 ALOGV("%s: %s wide band voice call (WB_AMR=%d)",
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100387 __func__,
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100388 wb_amr_type > 0 ? "Enable" : "Disable",
389 wb_amr_type);
390
391 /* TODO Handle wb_amr_type=2 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100392
Andreas Schneidere9a44a22017-02-14 13:00:48 +0100393 /*
394 * We need stop the PCM and start with the
395 * wide band pcm_config.
396 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100397 stop_voice_call(adev);
398 start_voice_call(adev);
399 }
400 }
401
402 pthread_mutex_unlock(&adev->lock);
403}
404
405struct voice_session *voice_session_init(struct audio_device *adev)
406{
407 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100408 struct voice_session *session;
409 int ret;
410
411 session = calloc(1, sizeof(struct voice_session));
412 if (session == NULL) {
413 return NULL;
414 }
415
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100416 /* Two mic control */
417 ret = property_get_bool("audio_hal.disable_two_mic", false);
418 if (ret > 0) {
419 session->two_mic_disabled = true;
420 }
421
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100422 /* Do this as the last step so we do not have to close it on error */
423 ret = ril_open(&session->ril);
424 if (ret != 0) {
425 free(session);
426 return NULL;
427 }
428
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100429 ret = property_get("audio_hal.force_voice_config", voice_config, "");
430 if (ret > 0) {
431 if ((strncmp(voice_config, "narrow", 6)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100432 session->wb_amr_type = 0;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100433 else if ((strncmp(voice_config, "wide", 4)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100434 session->wb_amr_type = 1;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100435 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
436 } else {
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100437 if (RIL_UNSOL_SNDMGR_WB_AMR_REPORT > 0) {
438 /* register callback for wideband AMR setting */
439 ret = ril_set_wb_amr_callback(&session->ril,
440 voice_session_wb_amr_callback,
441 (void *)adev);
442 if (ret != 0) {
443 ALOGE("%s: Failed to register WB_AMR callback", __func__);
444 free(session);
445 return NULL;
446 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100447
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100448 ALOGV("%s: Registered WB_AMR callback", __func__);
449 } else {
450 ALOGV("%s: WB_AMR callback not supported", __func__);
451 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100452 }
453
Christopher N. Hesse6a960022018-01-29 03:28:10 +0100454 session->vdata = &adev->voice;
455
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100456 return session;
457}
458
459void voice_session_deinit(struct voice_session *session)
460{
461 ril_close(&session->ril);
462 free(session);
463}