blob: ce981c5a5a04761bf71b26a6564b58a7148a5262 [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 -030065/* SCO WB and NB uses 8kHz for now, 16kHz it's on TO DO*/
66struct pcm_config pcm_config_voice_sco_wb = {
67 .channels = 1,
68 .rate = SCO_DEFAULT_SAMPLING_RATE,
69 .period_size = SCO_PERIOD_SIZE,
70 .period_count = SCO_PERIOD_COUNT,
71 .format = PCM_FORMAT_S16_LE,
72};
73
Christopher N. Hesse696959d2017-02-02 20:49:55 +010074/* Prototypes */
75int start_voice_call(struct audio_device *adev);
76int stop_voice_call(struct audio_device *adev);
77
78void set_voice_session_audio_path(struct voice_session *session)
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010079{
Christopher N. Hesse696959d2017-02-02 20:49:55 +010080 enum _AudioPath device_type;
Andreas Schneiderd44edaa2017-02-13 16:21:35 +010081 int rc;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010082
83 switch(session->out_device) {
84 case AUDIO_DEVICE_OUT_SPEAKER:
85 device_type = SOUND_AUDIO_PATH_SPEAKER;
86 break;
87 case AUDIO_DEVICE_OUT_EARPIECE:
Andreas Schneidere6286b92017-02-14 17:24:23 +010088 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010089 break;
90 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
91 device_type = SOUND_AUDIO_PATH_HEADSET;
92 break;
93 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
94 device_type = SOUND_AUDIO_PATH_HEADPHONE;
95 break;
96 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
97 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
98 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
99 device_type = SOUND_AUDIO_PATH_BLUETOOTH;
100 break;
101 default:
Andreas Schneidere6286b92017-02-14 17:24:23 +0100102 /* if output device isn't supported, use earpiece by default */
103 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100104 break;
105 }
106
107 ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
108
Andreas Schneiderd44edaa2017-02-13 16:21:35 +0100109 rc = ril_set_call_audio_path(&session->ril, device_type);
110 ALOGE_IF(rc != 0, "Failed to set audio path: (%d)", rc);
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100111}
112
113/*
114 * This decides based on the output device, if we enable
115 * two mic control
116 */
117void prepare_voice_session(struct voice_session *session,
118 audio_devices_t active_out_devices)
119{
120 ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
121
122 session->out_device = active_out_devices;
123
124 switch (session->out_device) {
125 case AUDIO_DEVICE_OUT_EARPIECE:
126 case AUDIO_DEVICE_OUT_SPEAKER:
127 session->two_mic_control = true;
128 break;
129 default:
130 session->two_mic_control = false;
131 break;
132 }
133
134 if (session->two_mic_disabled) {
135 session->two_mic_control = false;
136 }
137}
138
139/*
Andreas Schneider05bc1882017-02-09 14:03:11 +0100140 * This must be called with the hw device mutex locked, OK to hold other
141 * mutexes.
142 */
143static void stop_voice_session_bt_sco(struct voice_session *session) {
144 ALOGV("%s: Closing SCO PCMs", __func__);
145
146 if (session->pcm_sco_rx != NULL) {
147 pcm_stop(session->pcm_sco_rx);
148 pcm_close(session->pcm_sco_rx);
149 session->pcm_sco_rx = NULL;
150 }
151
152 if (session->pcm_sco_tx != NULL) {
153 pcm_stop(session->pcm_sco_tx);
154 pcm_close(session->pcm_sco_tx);
155 session->pcm_sco_tx = NULL;
156 }
157}
158
159/* must be called with the hw device mutex locked, OK to hold other mutexes */
160void start_voice_session_bt_sco(struct voice_session *session)
161{
Fevax51bd12c2017-03-15 10:56:39 -0300162 struct pcm_config *voice_sco_config;
163
Andreas Schneider05bc1882017-02-09 14:03:11 +0100164 if (session->pcm_sco_rx != NULL || session->pcm_sco_tx != NULL) {
165 ALOGW("%s: SCO PCMs already open!\n", __func__);
166 return;
167 }
168
169 ALOGV("%s: Opening SCO PCMs", __func__);
170
Fevax51bd12c2017-03-15 10:56:39 -0300171 if (session->wb_amr_type >= 1) {
172 ALOGV("%s: pcm_config wideband", __func__);
173 voice_sco_config = &pcm_config_voice_sco_wb;
174 } else {
175 ALOGV("%s: pcm_config narrowband", __func__);
176 voice_sco_config = &pcm_config_voice_sco;
177 }
Andreas Schneider05bc1882017-02-09 14:03:11 +0100178
179 session->pcm_sco_rx = pcm_open(SOUND_CARD,
180 SOUND_PLAYBACK_SCO_DEVICE,
181 PCM_OUT|PCM_MONOTONIC,
Fevax51bd12c2017-03-15 10:56:39 -0300182 voice_sco_config);
Andreas Schneider05bc1882017-02-09 14:03:11 +0100183 if (session->pcm_sco_rx != NULL && !pcm_is_ready(session->pcm_sco_rx)) {
184 ALOGE("%s: cannot open PCM SCO RX stream: %s",
185 __func__, pcm_get_error(session->pcm_sco_rx));
186 goto err_sco_rx;
187 }
188
189 session->pcm_sco_tx = pcm_open(SOUND_CARD,
190 SOUND_CAPTURE_SCO_DEVICE,
191 PCM_IN|PCM_MONOTONIC,
Fevax51bd12c2017-03-15 10:56:39 -0300192 voice_sco_config);
Andreas Schneider05bc1882017-02-09 14:03:11 +0100193 if (session->pcm_sco_tx && !pcm_is_ready(session->pcm_sco_tx)) {
194 ALOGE("%s: cannot open PCM SCO TX stream: %s",
195 __func__, pcm_get_error(session->pcm_sco_tx));
196 goto err_sco_tx;
197 }
198
199 pcm_start(session->pcm_sco_rx);
200 pcm_start(session->pcm_sco_tx);
201
202 return;
203
204err_sco_tx:
205 pcm_close(session->pcm_sco_tx);
206 session->pcm_sco_tx = NULL;
207err_sco_rx:
208 pcm_close(session->pcm_sco_rx);
209 session->pcm_sco_rx = NULL;
210}
211/*
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100212 * This function must be called with hw device mutex locked, OK to hold other
213 * mutexes
214 */
215int start_voice_session(struct voice_session *session)
216{
217 struct pcm_config *voice_config;
218
219 if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
220 ALOGW("%s: Voice PCMs already open!\n", __func__);
221 return 0;
222 }
223
224 ALOGV("%s: Opening voice PCMs", __func__);
225
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100226 /* TODO: Handle wb_amr=2 */
227 if (session->wb_amr_type >= 1) {
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100228 ALOGV("%s: pcm_config wideband", __func__);
229 voice_config = &pcm_config_voicecall_wideband;
230 } else {
231 ALOGV("%s: pcm_config narrowband", __func__);
232 voice_config = &pcm_config_voicecall;
233 }
234
235 /* Open modem PCM channels */
236 session->pcm_voice_rx = pcm_open(SOUND_CARD,
237 SOUND_PLAYBACK_VOICE_DEVICE,
238 PCM_OUT|PCM_MONOTONIC,
239 voice_config);
240 if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
241 ALOGE("%s: cannot open PCM voice RX stream: %s",
242 __func__,
243 pcm_get_error(session->pcm_voice_rx));
244
245 pcm_close(session->pcm_voice_tx);
246 session->pcm_voice_tx = NULL;
247
248 return -ENOMEM;
249 }
250
251 session->pcm_voice_tx = pcm_open(SOUND_CARD,
252 SOUND_CAPTURE_VOICE_DEVICE,
253 PCM_IN|PCM_MONOTONIC,
254 voice_config);
255 if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
256 ALOGE("%s: cannot open PCM voice TX stream: %s",
257 __func__,
258 pcm_get_error(session->pcm_voice_tx));
259
260 pcm_close(session->pcm_voice_rx);
261 session->pcm_voice_rx = NULL;
262
263 return -ENOMEM;
264 }
265
266 pcm_start(session->pcm_voice_rx);
267 pcm_start(session->pcm_voice_tx);
268
Andreas Schneider05bc1882017-02-09 14:03:11 +0100269 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
270 start_voice_session_bt_sco(session);
271 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100272
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100273#ifdef AUDIENCE_EARSMART_IC
274 ALOGV("%s: Enabling Audience IC", __func__);
275 es_start_voice_session(session);
276#endif
277
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100278 if (session->two_mic_control) {
279 ALOGV("%s: enabling two mic control", __func__);
280 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
281 } else {
282 ALOGV("%s: disabling two mic control", __func__);
283 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
284 }
285
286 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
287
288 return 0;
289}
290
291/*
292 * This function must be called with hw device mutex locked, OK to hold other
293 * mutexes
294 */
295void stop_voice_session(struct voice_session *session)
296{
297 int status = 0;
298
299 ALOGV("%s: Closing active PCMs", __func__);
300
301 if (session->pcm_voice_rx != NULL) {
302 pcm_stop(session->pcm_voice_rx);
303 pcm_close(session->pcm_voice_rx);
304 session->pcm_voice_rx = NULL;
305 status++;
306 }
307
308 if (session->pcm_voice_tx != NULL) {
309 pcm_stop(session->pcm_voice_tx);
310 pcm_close(session->pcm_voice_tx);
311 session->pcm_voice_tx = NULL;
312 status++;
313 }
314
Andreas Schneider05bc1882017-02-09 14:03:11 +0100315 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
316 stop_voice_session_bt_sco(session);
317 }
318
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100319#ifdef AUDIENCE_EARSMART_IC
320 ALOGV("%s: Disabling Audience IC", __func__);
321 es_stop_voice_session();
322#endif
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100323
324 session->out_device = AUDIO_DEVICE_NONE;
325
326 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
327}
328
329void set_voice_session_volume(struct voice_session *session, float volume)
330{
331 enum _SoundType sound_type;
332
333 switch (session->out_device) {
334 case AUDIO_DEVICE_OUT_EARPIECE:
335 sound_type = SOUND_TYPE_VOICE;
336 break;
337 case AUDIO_DEVICE_OUT_SPEAKER:
338 sound_type = SOUND_TYPE_SPEAKER;
339 break;
340 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
341 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
342 sound_type = SOUND_TYPE_HEADSET;
343 break;
344 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
345 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
346 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
347 case AUDIO_DEVICE_OUT_ALL_SCO:
348 sound_type = SOUND_TYPE_BTVOICE;
349 break;
350 default:
351 sound_type = SOUND_TYPE_VOICE;
352 }
353
354 ril_set_call_volume(&session->ril, sound_type, volume);
355}
356
Andreas Schneider107a8482017-02-06 12:36:31 +0100357void set_voice_session_mic_mute(struct voice_session *session, bool state)
358{
359 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
360
361 ril_set_mute(&session->ril, mute_condition);
362}
363
Andreas Schneider82f32482017-02-06 09:00:48 +0100364bool voice_session_uses_twomic(struct voice_session *session)
365{
366 if (session->two_mic_disabled) {
367 return false;
368 }
369
370 return session->two_mic_control;
371}
372
Andreas Schneider59486fa2017-02-06 09:16:39 +0100373bool voice_session_uses_wideband(struct voice_session *session)
374{
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100375 return session->wb_amr_type >= 1;
Andreas Schneider59486fa2017-02-06 09:16:39 +0100376}
377
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100378static void voice_session_wb_amr_callback(void *data, int wb_amr_type)
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100379{
380 struct audio_device *adev = (struct audio_device *)data;
381 struct voice_session *session =
382 (struct voice_session *)adev->voice.session;
383
384 pthread_mutex_lock(&adev->lock);
385
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100386 if (session->wb_amr_type != wb_amr_type) {
387 session->wb_amr_type = wb_amr_type;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100388
389 /* reopen the modem PCMs at the new rate */
390 if (adev->voice.in_call) {
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100391 ALOGV("%s: %s wide band voice call (WB_AMR=%d)",
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100392 __func__,
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100393 wb_amr_type > 0 ? "Enable" : "Disable",
394 wb_amr_type);
395
396 /* TODO Handle wb_amr_type=2 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100397
Andreas Schneidere9a44a22017-02-14 13:00:48 +0100398 /*
399 * We need stop the PCM and start with the
400 * wide band pcm_config.
401 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100402 stop_voice_call(adev);
403 start_voice_call(adev);
404 }
405 }
406
407 pthread_mutex_unlock(&adev->lock);
408}
409
410struct voice_session *voice_session_init(struct audio_device *adev)
411{
412 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100413 struct voice_session *session;
414 int ret;
415
416 session = calloc(1, sizeof(struct voice_session));
417 if (session == NULL) {
418 return NULL;
419 }
420
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100421 /* Two mic control */
422 ret = property_get_bool("audio_hal.disable_two_mic", false);
423 if (ret > 0) {
424 session->two_mic_disabled = true;
425 }
426
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100427 /* Do this as the last step so we do not have to close it on error */
428 ret = ril_open(&session->ril);
429 if (ret != 0) {
430 free(session);
431 return NULL;
432 }
433
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100434 ret = property_get("audio_hal.force_voice_config", voice_config, "");
435 if (ret > 0) {
436 if ((strncmp(voice_config, "narrow", 6)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100437 session->wb_amr_type = 0;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100438 else if ((strncmp(voice_config, "wide", 4)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100439 session->wb_amr_type = 1;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100440 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
441 } else {
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100442 if (RIL_UNSOL_SNDMGR_WB_AMR_REPORT > 0) {
443 /* register callback for wideband AMR setting */
444 ret = ril_set_wb_amr_callback(&session->ril,
445 voice_session_wb_amr_callback,
446 (void *)adev);
447 if (ret != 0) {
448 ALOGE("%s: Failed to register WB_AMR callback", __func__);
449 free(session);
450 return NULL;
451 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100452
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100453 ALOGV("%s: Registered WB_AMR callback", __func__);
454 } else {
455 ALOGV("%s: WB_AMR callback not supported", __func__);
456 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100457 }
458
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100459 return session;
460}
461
462void voice_session_deinit(struct voice_session *session)
463{
464 ril_close(&session->ril);
465 free(session);
466}