blob: 3e09e55bdaeec07d97ac39586620d86cfc9f3c27 [file] [log] [blame]
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -08001/* hfp.c
Apoorv Raghuvanshi924b3022015-07-06 15:07:14 -07002Copyright (c) 2012-2015, 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"
Sudheer Papothi19e43d02014-07-16 02:34:41 +053052#else
53#define HFP_RX_VOLUME "Internal HFP RX Volume"
54#endif
55
Vimal Puthanveed584048b2013-12-11 17:00:50 -080056static int32_t start_hfp(struct audio_device *adev,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080057 struct str_parms *parms);
58
Vimal Puthanveed584048b2013-12-11 17:00:50 -080059static int32_t stop_hfp(struct audio_device *adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080060
61struct hfp_module {
62 struct pcm *hfp_sco_rx;
63 struct pcm *hfp_sco_tx;
64 struct pcm *hfp_pcm_rx;
65 struct pcm *hfp_pcm_tx;
66 bool is_hfp_running;
Amit Shekhar967cab32014-02-07 17:03:21 -080067 float hfp_volume;
Vimal Puthanveed47e64852013-12-20 13:23:39 -080068 audio_usecase_t ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080069};
70
71static struct hfp_module hfpmod = {
72 .hfp_sco_rx = NULL,
73 .hfp_sco_tx = NULL,
74 .hfp_pcm_rx = NULL,
75 .hfp_pcm_tx = NULL,
76 .hfp_volume = 0,
77 .is_hfp_running = 0,
Vimal Puthanveed47e64852013-12-20 13:23:39 -080078 .ucid = USECASE_AUDIO_HFP_SCO,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080079};
80static struct pcm_config pcm_config_hfp = {
81 .channels = 1,
82 .rate = 8000,
83 .period_size = 240,
84 .period_count = 2,
85 .format = PCM_FORMAT_S16_LE,
86 .start_threshold = 0,
87 .stop_threshold = INT_MAX,
88 .avail_min = 0,
89};
90
Amit Shekhar967cab32014-02-07 17:03:21 -080091static int32_t hfp_set_volume(struct audio_device *adev, float value)
92{
93 int32_t vol, ret = 0;
94 struct mixer_ctl *ctl;
Sudheer Papothi19e43d02014-07-16 02:34:41 +053095 const char *mixer_ctl_name = HFP_RX_VOLUME;
Amit Shekhar967cab32014-02-07 17:03:21 -080096
97 ALOGV("%s: entry", __func__);
98 ALOGD("%s: (%f)\n", __func__, value);
99
Satya Krishna Pindiproli3ed6d792014-09-09 15:32:25 +0530100 hfpmod.hfp_volume = value;
Amit Shekhar967cab32014-02-07 17:03:21 -0800101 if (value < 0.0) {
102 ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
103 value = 0.0;
104 } else {
105 value = ((value > 15.000000) ? 1.0 : (value / 15));
106 ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
107 }
108 vol = lrint((value * 0x2000) + 0.5);
Amit Shekhar967cab32014-02-07 17:03:21 -0800109
110 if (!hfpmod.is_hfp_running) {
111 ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
112 return -EIO;
113 }
114
115 ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
116 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
117 if (!ctl) {
118 ALOGE("%s: Could not get ctl for mixer cmd - %s",
119 __func__, mixer_ctl_name);
120 return -EINVAL;
121 }
122 if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
123 ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
124 return -EINVAL;
125 }
126
127 ALOGV("%s: exit", __func__);
128 return ret;
129}
130
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800131static int32_t start_hfp(struct audio_device *adev,
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700132 struct str_parms *parms __unused)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800133{
134 int32_t i, ret = 0;
135 struct audio_usecase *uc_info;
136 int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
137
138 ALOGD("%s: enter", __func__);
139
140 uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
Haynes Mathew Georgeb51ceb12014-06-30 13:56:18 -0700141
142 if (!uc_info)
143 return -ENOMEM;
144
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800145 uc_info->id = hfpmod.ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800146 uc_info->type = PCM_HFP_CALL;
147 uc_info->stream.out = adev->primary_output;
148 uc_info->devices = adev->primary_output->devices;
149 uc_info->in_snd_device = SND_DEVICE_NONE;
150 uc_info->out_snd_device = SND_DEVICE_NONE;
151
152 list_add_tail(&adev->usecase_list, &uc_info->list);
153
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800154 select_devices(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800155
156 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
157 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
158 pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
159 pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
160 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
161 pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
162 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
163 __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
164 ret = -EIO;
165 goto exit;
166 }
167
168 ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
169 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
170
171 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800172 __func__, adev->snd_card, pcm_dev_rx_id);
173 hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800174 pcm_dev_asm_rx_id,
175 PCM_OUT, &pcm_config_hfp);
176 if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
177 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
178 ret = -EIO;
179 goto exit;
180 }
181 ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800182 __func__, adev->snd_card, pcm_dev_tx_id);
183 hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800184 pcm_dev_rx_id,
185 PCM_OUT, &pcm_config_hfp);
186 if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
187 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
188 ret = -EIO;
189 goto exit;
190 }
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800191 hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800192 pcm_dev_asm_tx_id,
193 PCM_IN, &pcm_config_hfp);
194 if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
195 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
196 ret = -EIO;
197 goto exit;
198 }
199 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800200 __func__, adev->snd_card, pcm_dev_tx_id);
201 hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800202 pcm_dev_tx_id,
203 PCM_IN, &pcm_config_hfp);
204 if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
205 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
206 ret = -EIO;
207 goto exit;
208 }
Satya Krishna Pindiprolice903a02014-07-28 12:40:56 +0530209 if (pcm_start(hfpmod.hfp_sco_rx) < 0) {
210 ALOGE("%s: pcm start for hfp sco rx failed", __func__);
211 ret = -EINVAL;
212 goto exit;
213 }
214 if (pcm_start(hfpmod.hfp_sco_tx) < 0) {
215 ALOGE("%s: pcm start for hfp sco tx failed", __func__);
216 ret = -EINVAL;
217 goto exit;
218 }
219 if (pcm_start(hfpmod.hfp_pcm_rx) < 0) {
220 ALOGE("%s: pcm start for hfp pcm rx failed", __func__);
221 ret = -EINVAL;
222 goto exit;
223 }
224 if (pcm_start(hfpmod.hfp_pcm_tx) < 0) {
225 ALOGE("%s: pcm start for hfp pcm tx failed", __func__);
226 ret = -EINVAL;
227 goto exit;
228 }
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800229
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800230 hfpmod.is_hfp_running = true;
Amit Shekhar967cab32014-02-07 17:03:21 -0800231 hfp_set_volume(adev, hfpmod.hfp_volume);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800232
233 ALOGD("%s: exit: status(%d)", __func__, ret);
234 return 0;
235
236exit:
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800237 stop_hfp(adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800238 ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
239 return ret;
240}
241
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800242static int32_t stop_hfp(struct audio_device *adev)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800243{
244 int32_t i, ret = 0;
245 struct audio_usecase *uc_info;
246
247 ALOGD("%s: enter", __func__);
248 hfpmod.is_hfp_running = false;
249
250 /* 1. Close the PCM devices */
251 if (hfpmod.hfp_sco_rx) {
252 pcm_close(hfpmod.hfp_sco_rx);
253 hfpmod.hfp_sco_rx = NULL;
254 }
255 if (hfpmod.hfp_sco_tx) {
256 pcm_close(hfpmod.hfp_sco_tx);
257 hfpmod.hfp_sco_tx = NULL;
258 }
259 if (hfpmod.hfp_pcm_rx) {
260 pcm_close(hfpmod.hfp_pcm_rx);
261 hfpmod.hfp_pcm_rx = NULL;
262 }
263 if (hfpmod.hfp_pcm_tx) {
264 pcm_close(hfpmod.hfp_pcm_tx);
265 hfpmod.hfp_pcm_tx = NULL;
266 }
267
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800268 uc_info = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800269 if (uc_info == NULL) {
270 ALOGE("%s: Could not find the usecase (%d) in the list",
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800271 __func__, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800272 return -EINVAL;
273 }
274
Venkata Narendra Kumar Gutta1bbbf542014-09-04 19:11:25 +0530275 /* 2. Disable echo reference while stopping hfp */
Apoorv Raghuvanshi924b3022015-07-06 15:07:14 -0700276 platform_set_echo_reference(adev, false, uc_info->devices);
Venkata Narendra Kumar Gutta1bbbf542014-09-04 19:11:25 +0530277
278 /* 3. Get and set stream specific mixer controls */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700279 disable_audio_route(adev, uc_info);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800280
Venkata Narendra Kumar Gutta1bbbf542014-09-04 19:11:25 +0530281 /* 4. Disable the rx and tx devices */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700282 disable_snd_device(adev, uc_info->out_snd_device);
283 disable_snd_device(adev, uc_info->in_snd_device);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800284
285 list_remove(&uc_info->list);
286 free(uc_info);
287
288 ALOGD("%s: exit: status(%d)", __func__, ret);
289 return ret;
290}
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800291
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800292bool audio_extn_hfp_is_active(struct audio_device *adev)
293{
294 struct audio_usecase *hfp_usecase = NULL;
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800295 hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800296
297 if (hfp_usecase != NULL)
298 return true;
299 else
300 return false;
301}
302
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800303audio_usecase_t audio_extn_hfp_get_usecase()
304{
305 return hfpmod.ucid;
306}
307
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800308void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
309{
310 int ret;
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800311 int rate;
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800312 int val;
Amit Shekhar967cab32014-02-07 17:03:21 -0800313 float vol;
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800314 char value[32]={0};
315
316 ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
317 sizeof(value));
318 if (ret >= 0) {
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700319 if (!strncmp(value,"true",sizeof(value)))
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800320 ret = start_hfp(adev,parms);
321 else
322 stop_hfp(adev);
323 }
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800324 memset(value, 0, sizeof(value));
325 ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
326 sizeof(value));
327 if (ret >= 0) {
328 rate = atoi(value);
329 if (rate == 8000){
330 hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
331 pcm_config_hfp.rate = rate;
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700332 } else if (rate == 16000){
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800333 hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
334 pcm_config_hfp.rate = rate;
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700335 } else
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800336 ALOGE("Unsupported rate..");
337 }
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800338
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700339 if (hfpmod.is_hfp_running) {
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800340 memset(value, 0, sizeof(value));
341 ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
342 value, sizeof(value));
343 if (ret >= 0) {
344 val = atoi(value);
Ravi Kumar Alamandabdf14162014-09-05 16:14:17 -0700345 if (val > 0)
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800346 select_devices(adev, hfpmod.ucid);
347 }
348 }
Amit Shekhar967cab32014-02-07 17:03:21 -0800349
350 memset(value, 0, sizeof(value));
351 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
352 value, sizeof(value));
353 if (ret >= 0) {
354 if (sscanf(value, "%f", &vol) != 1){
355 ALOGE("%s: error in retrieving hfp volume", __func__);
356 ret = -EIO;
357 goto exit;
358 }
359 ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
360 hfp_set_volume(adev, vol);
361 }
362exit:
363 ALOGV("%s Exit",__func__);
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800364}
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800365#endif /*HFP_ENABLED*/