blob: 5a45b805e3da66c26a9237b6b4bfa28f03f04d9d [file] [log] [blame]
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -08001/* hfp.c
Banajit Goswami4c0dff22016-03-04 18:31:22 -08002Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -08003
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are
6met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above
10 copyright notice, this list of conditions and the following
11 disclaimer in the documentation and/or other materials provided
12 with the distribution.
13 * Neither the name of The Linux Foundation nor the names of its
14 contributors may be used to endorse or promote products derived
15 from this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
28
29#define LOG_TAG "audio_hw_hfp"
30/*#define LOG_NDEBUG 0*/
31#define LOG_NDDEBUG 0
32
33#include <errno.h>
34#include <math.h>
35#include <cutils/log.h>
36
37#include "audio_hw.h"
38#include "platform.h"
39#include "platform_api.h"
40#include <stdlib.h>
41#include <cutils/str_parms.h>
42
43#ifdef HFP_ENABLED
44#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable"
Vimal Puthanveed47e64852013-12-20 13:23:39 -080045#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
Amit Shekhar967cab32014-02-07 17:03:21 -080046#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume"
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080047
Sudheer Papothi19e43d02014-07-16 02:34:41 +053048#ifdef PLATFORM_MSM8994
49#define HFP_RX_VOLUME "SEC AUXPCM LOOPBACK Volume"
Banajit Goswamida77c452015-10-14 20:45:22 -070050#elif defined PLATFORM_MSM8996
51#define HFP_RX_VOLUME "PRI AUXPCM LOOPBACK Volume"
Dhananjay Kumard3ab9f42016-07-20 17:05:47 +053052#elif defined (PLATFORM_MSMCOBALT) || defined (PLATFORM_MSMFALCON)
Banajit Goswami4c0dff22016-03-04 18:31:22 -080053#define HFP_RX_VOLUME "SLIMBUS_7 LOOPBACK Volume"
Sudheer Papothi19e43d02014-07-16 02:34:41 +053054#else
55#define HFP_RX_VOLUME "Internal HFP RX Volume"
56#endif
57
Vimal Puthanveed584048b2013-12-11 17:00:50 -080058static int32_t start_hfp(struct audio_device *adev,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080059 struct str_parms *parms);
60
Vimal Puthanveed584048b2013-12-11 17:00:50 -080061static int32_t stop_hfp(struct audio_device *adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080062
63struct hfp_module {
64 struct pcm *hfp_sco_rx;
65 struct pcm *hfp_sco_tx;
66 struct pcm *hfp_pcm_rx;
67 struct pcm *hfp_pcm_tx;
68 bool is_hfp_running;
Amit Shekhar967cab32014-02-07 17:03:21 -080069 float hfp_volume;
Vimal Puthanveed47e64852013-12-20 13:23:39 -080070 audio_usecase_t ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080071};
72
73static struct hfp_module hfpmod = {
74 .hfp_sco_rx = NULL,
75 .hfp_sco_tx = NULL,
76 .hfp_pcm_rx = NULL,
77 .hfp_pcm_tx = NULL,
78 .hfp_volume = 0,
79 .is_hfp_running = 0,
Vimal Puthanveed47e64852013-12-20 13:23:39 -080080 .ucid = USECASE_AUDIO_HFP_SCO,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080081};
82static struct pcm_config pcm_config_hfp = {
83 .channels = 1,
84 .rate = 8000,
85 .period_size = 240,
86 .period_count = 2,
87 .format = PCM_FORMAT_S16_LE,
88 .start_threshold = 0,
89 .stop_threshold = INT_MAX,
90 .avail_min = 0,
91};
92
Amit Shekhar967cab32014-02-07 17:03:21 -080093static int32_t hfp_set_volume(struct audio_device *adev, float value)
94{
95 int32_t vol, ret = 0;
96 struct mixer_ctl *ctl;
Sudheer Papothi19e43d02014-07-16 02:34:41 +053097 const char *mixer_ctl_name = HFP_RX_VOLUME;
Amit Shekhar967cab32014-02-07 17:03:21 -080098
99 ALOGV("%s: entry", __func__);
100 ALOGD("%s: (%f)\n", __func__, value);
101
Satya Krishna Pindiproli3ed6d792014-09-09 15:32:25 +0530102 hfpmod.hfp_volume = value;
Amit Shekhar967cab32014-02-07 17:03:21 -0800103 if (value < 0.0) {
104 ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
105 value = 0.0;
106 } else {
107 value = ((value > 15.000000) ? 1.0 : (value / 15));
108 ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
109 }
110 vol = lrint((value * 0x2000) + 0.5);
Amit Shekhar967cab32014-02-07 17:03:21 -0800111
112 if (!hfpmod.is_hfp_running) {
113 ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
114 return -EIO;
115 }
116
117 ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
118 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
119 if (!ctl) {
120 ALOGE("%s: Could not get ctl for mixer cmd - %s",
121 __func__, mixer_ctl_name);
122 return -EINVAL;
123 }
124 if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
125 ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
126 return -EINVAL;
127 }
128
129 ALOGV("%s: exit", __func__);
130 return ret;
131}
132
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800133static int32_t start_hfp(struct audio_device *adev,
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700134 struct str_parms *parms __unused)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800135{
Satya Krishna Pindiprolif1cd92b2016-04-14 19:05:23 +0530136 int32_t ret = 0;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800137 struct audio_usecase *uc_info;
138 int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
139
140 ALOGD("%s: enter", __func__);
141
142 uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
Haynes Mathew Georgeb51ceb12014-06-30 13:56:18 -0700143
144 if (!uc_info)
145 return -ENOMEM;
146
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800147 uc_info->id = hfpmod.ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800148 uc_info->type = PCM_HFP_CALL;
149 uc_info->stream.out = adev->primary_output;
150 uc_info->devices = adev->primary_output->devices;
151 uc_info->in_snd_device = SND_DEVICE_NONE;
152 uc_info->out_snd_device = SND_DEVICE_NONE;
153
154 list_add_tail(&adev->usecase_list, &uc_info->list);
155
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800156 select_devices(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800157
158 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
159 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
160 pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
161 pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
162 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
163 pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
164 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
165 __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
166 ret = -EIO;
167 goto exit;
168 }
169
170 ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
171 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
172
173 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800174 __func__, adev->snd_card, pcm_dev_rx_id);
175 hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800176 pcm_dev_asm_rx_id,
177 PCM_OUT, &pcm_config_hfp);
178 if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
179 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
180 ret = -EIO;
181 goto exit;
182 }
183 ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800184 __func__, adev->snd_card, pcm_dev_tx_id);
185 hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800186 pcm_dev_rx_id,
187 PCM_OUT, &pcm_config_hfp);
188 if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
189 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
190 ret = -EIO;
191 goto exit;
192 }
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800193 hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800194 pcm_dev_asm_tx_id,
195 PCM_IN, &pcm_config_hfp);
196 if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
197 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
198 ret = -EIO;
199 goto exit;
200 }
201 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800202 __func__, adev->snd_card, pcm_dev_tx_id);
203 hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800204 pcm_dev_tx_id,
205 PCM_IN, &pcm_config_hfp);
206 if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
207 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
208 ret = -EIO;
209 goto exit;
210 }
Satya Krishna Pindiprolice903a02014-07-28 12:40:56 +0530211 if (pcm_start(hfpmod.hfp_sco_rx) < 0) {
212 ALOGE("%s: pcm start for hfp sco rx failed", __func__);
213 ret = -EINVAL;
214 goto exit;
215 }
216 if (pcm_start(hfpmod.hfp_sco_tx) < 0) {
217 ALOGE("%s: pcm start for hfp sco tx failed", __func__);
218 ret = -EINVAL;
219 goto exit;
220 }
221 if (pcm_start(hfpmod.hfp_pcm_rx) < 0) {
222 ALOGE("%s: pcm start for hfp pcm rx failed", __func__);
223 ret = -EINVAL;
224 goto exit;
225 }
226 if (pcm_start(hfpmod.hfp_pcm_tx) < 0) {
227 ALOGE("%s: pcm start for hfp pcm tx failed", __func__);
228 ret = -EINVAL;
229 goto exit;
230 }
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800231
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800232 hfpmod.is_hfp_running = true;
Amit Shekhar967cab32014-02-07 17:03:21 -0800233 hfp_set_volume(adev, hfpmod.hfp_volume);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800234
235 ALOGD("%s: exit: status(%d)", __func__, ret);
236 return 0;
237
238exit:
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800239 stop_hfp(adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800240 ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
241 return ret;
242}
243
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800244static int32_t stop_hfp(struct audio_device *adev)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800245{
Satya Krishna Pindiprolif1cd92b2016-04-14 19:05:23 +0530246 int32_t ret = 0;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800247 struct audio_usecase *uc_info;
248
249 ALOGD("%s: enter", __func__);
250 hfpmod.is_hfp_running = false;
251
252 /* 1. Close the PCM devices */
253 if (hfpmod.hfp_sco_rx) {
254 pcm_close(hfpmod.hfp_sco_rx);
255 hfpmod.hfp_sco_rx = NULL;
256 }
257 if (hfpmod.hfp_sco_tx) {
258 pcm_close(hfpmod.hfp_sco_tx);
259 hfpmod.hfp_sco_tx = NULL;
260 }
261 if (hfpmod.hfp_pcm_rx) {
262 pcm_close(hfpmod.hfp_pcm_rx);
263 hfpmod.hfp_pcm_rx = NULL;
264 }
265 if (hfpmod.hfp_pcm_tx) {
266 pcm_close(hfpmod.hfp_pcm_tx);
267 hfpmod.hfp_pcm_tx = NULL;
268 }
269
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800270 uc_info = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800271 if (uc_info == NULL) {
272 ALOGE("%s: Could not find the usecase (%d) in the list",
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800273 __func__, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800274 return -EINVAL;
275 }
276
Venkata Narendra Kumar Gutta1bbbf542014-09-04 19:11:25 +0530277 /* 2. Disable echo reference while stopping hfp */
Apoorv Raghuvanshi924b3022015-07-06 15:07:14 -0700278 platform_set_echo_reference(adev, false, uc_info->devices);
Venkata Narendra Kumar Gutta1bbbf542014-09-04 19:11:25 +0530279
280 /* 3. Get and set stream specific mixer controls */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700281 disable_audio_route(adev, uc_info);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800282
Venkata Narendra Kumar Gutta1bbbf542014-09-04 19:11:25 +0530283 /* 4. Disable the rx and tx devices */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700284 disable_snd_device(adev, uc_info->out_snd_device);
285 disable_snd_device(adev, uc_info->in_snd_device);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800286
287 list_remove(&uc_info->list);
288 free(uc_info);
289
290 ALOGD("%s: exit: status(%d)", __func__, ret);
291 return ret;
292}
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800293
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800294bool audio_extn_hfp_is_active(struct audio_device *adev)
295{
296 struct audio_usecase *hfp_usecase = NULL;
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800297 hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800298
299 if (hfp_usecase != NULL)
300 return true;
301 else
302 return false;
303}
304
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800305audio_usecase_t audio_extn_hfp_get_usecase()
306{
307 return hfpmod.ucid;
308}
309
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800310void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
311{
312 int ret;
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800313 int rate;
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800314 int val;
Amit Shekhar967cab32014-02-07 17:03:21 -0800315 float vol;
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800316 char value[32]={0};
317
318 ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
319 sizeof(value));
320 if (ret >= 0) {
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700321 if (!strncmp(value,"true",sizeof(value)))
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800322 ret = start_hfp(adev,parms);
323 else
324 stop_hfp(adev);
325 }
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800326 memset(value, 0, sizeof(value));
327 ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
328 sizeof(value));
329 if (ret >= 0) {
330 rate = atoi(value);
331 if (rate == 8000){
332 hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
333 pcm_config_hfp.rate = rate;
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700334 } else if (rate == 16000){
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800335 hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
336 pcm_config_hfp.rate = rate;
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700337 } else
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800338 ALOGE("Unsupported rate..");
339 }
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800340
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700341 if (hfpmod.is_hfp_running) {
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800342 memset(value, 0, sizeof(value));
343 ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
344 value, sizeof(value));
345 if (ret >= 0) {
346 val = atoi(value);
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700347 if (val > 0)
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800348 select_devices(adev, hfpmod.ucid);
349 }
350 }
Amit Shekhar967cab32014-02-07 17:03:21 -0800351
352 memset(value, 0, sizeof(value));
353 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
354 value, sizeof(value));
355 if (ret >= 0) {
356 if (sscanf(value, "%f", &vol) != 1){
357 ALOGE("%s: error in retrieving hfp volume", __func__);
358 ret = -EIO;
359 goto exit;
360 }
361 ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
362 hfp_set_volume(adev, vol);
363 }
364exit:
365 ALOGV("%s Exit",__func__);
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800366}
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800367#endif /*HFP_ENABLED*/