blob: 3c25c1466039c0959d29a8b5fef5a845c438b141 [file] [log] [blame]
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -08001/* hfp.c
Haynes Mathew George1376ca62014-04-24 11:55:48 -07002Copyright (c) 2012-2014, 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"
50#else
51#define HFP_RX_VOLUME "Internal HFP RX Volume"
52#endif
53
Vimal Puthanveed584048b2013-12-11 17:00:50 -080054static int32_t start_hfp(struct audio_device *adev,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080055 struct str_parms *parms);
56
Vimal Puthanveed584048b2013-12-11 17:00:50 -080057static int32_t stop_hfp(struct audio_device *adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080058
59struct hfp_module {
60 struct pcm *hfp_sco_rx;
61 struct pcm *hfp_sco_tx;
62 struct pcm *hfp_pcm_rx;
63 struct pcm *hfp_pcm_tx;
64 bool is_hfp_running;
Amit Shekhar967cab32014-02-07 17:03:21 -080065 float hfp_volume;
Vimal Puthanveed47e64852013-12-20 13:23:39 -080066 audio_usecase_t ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080067};
68
69static struct hfp_module hfpmod = {
70 .hfp_sco_rx = NULL,
71 .hfp_sco_tx = NULL,
72 .hfp_pcm_rx = NULL,
73 .hfp_pcm_tx = NULL,
74 .hfp_volume = 0,
75 .is_hfp_running = 0,
Vimal Puthanveed47e64852013-12-20 13:23:39 -080076 .ucid = USECASE_AUDIO_HFP_SCO,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080077};
78static struct pcm_config pcm_config_hfp = {
79 .channels = 1,
80 .rate = 8000,
81 .period_size = 240,
82 .period_count = 2,
83 .format = PCM_FORMAT_S16_LE,
84 .start_threshold = 0,
85 .stop_threshold = INT_MAX,
86 .avail_min = 0,
87};
88
Amit Shekhar967cab32014-02-07 17:03:21 -080089static int32_t hfp_set_volume(struct audio_device *adev, float value)
90{
91 int32_t vol, ret = 0;
92 struct mixer_ctl *ctl;
Sudheer Papothi19e43d02014-07-16 02:34:41 +053093 const char *mixer_ctl_name = HFP_RX_VOLUME;
Amit Shekhar967cab32014-02-07 17:03:21 -080094
95 ALOGV("%s: entry", __func__);
96 ALOGD("%s: (%f)\n", __func__, value);
97
Satya Krishna Pindiproli3ed6d792014-09-09 15:32:25 +053098 hfpmod.hfp_volume = value;
Amit Shekhar967cab32014-02-07 17:03:21 -080099 if (value < 0.0) {
100 ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
101 value = 0.0;
102 } else {
103 value = ((value > 15.000000) ? 1.0 : (value / 15));
104 ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
105 }
106 vol = lrint((value * 0x2000) + 0.5);
Amit Shekhar967cab32014-02-07 17:03:21 -0800107
108 if (!hfpmod.is_hfp_running) {
109 ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
110 return -EIO;
111 }
112
113 ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
114 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
115 if (!ctl) {
116 ALOGE("%s: Could not get ctl for mixer cmd - %s",
117 __func__, mixer_ctl_name);
118 return -EINVAL;
119 }
120 if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
121 ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
122 return -EINVAL;
123 }
124
125 ALOGV("%s: exit", __func__);
126 return ret;
127}
128
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800129static int32_t start_hfp(struct audio_device *adev,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800130 struct str_parms *parms)
131{
132 int32_t i, ret = 0;
133 struct audio_usecase *uc_info;
134 int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
135
136 ALOGD("%s: enter", __func__);
137
138 uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
Haynes Mathew Georgeb51ceb12014-06-30 13:56:18 -0700139
140 if (!uc_info)
141 return -ENOMEM;
142
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800143 uc_info->id = hfpmod.ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800144 uc_info->type = PCM_HFP_CALL;
145 uc_info->stream.out = adev->primary_output;
146 uc_info->devices = adev->primary_output->devices;
147 uc_info->in_snd_device = SND_DEVICE_NONE;
148 uc_info->out_snd_device = SND_DEVICE_NONE;
149
150 list_add_tail(&adev->usecase_list, &uc_info->list);
151
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800152 select_devices(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800153
154 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
155 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
156 pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
157 pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
158 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
159 pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
160 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
161 __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
162 ret = -EIO;
163 goto exit;
164 }
165
166 ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
167 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
168
169 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800170 __func__, adev->snd_card, pcm_dev_rx_id);
171 hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800172 pcm_dev_asm_rx_id,
173 PCM_OUT, &pcm_config_hfp);
174 if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
175 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
176 ret = -EIO;
177 goto exit;
178 }
179 ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800180 __func__, adev->snd_card, pcm_dev_tx_id);
181 hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800182 pcm_dev_rx_id,
183 PCM_OUT, &pcm_config_hfp);
184 if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
185 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
186 ret = -EIO;
187 goto exit;
188 }
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800189 hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800190 pcm_dev_asm_tx_id,
191 PCM_IN, &pcm_config_hfp);
192 if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
193 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
194 ret = -EIO;
195 goto exit;
196 }
197 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800198 __func__, adev->snd_card, pcm_dev_tx_id);
199 hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800200 pcm_dev_tx_id,
201 PCM_IN, &pcm_config_hfp);
202 if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
203 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
204 ret = -EIO;
205 goto exit;
206 }
Satya Krishna Pindiprolice903a02014-07-28 12:40:56 +0530207 if (pcm_start(hfpmod.hfp_sco_rx) < 0) {
208 ALOGE("%s: pcm start for hfp sco rx failed", __func__);
209 ret = -EINVAL;
210 goto exit;
211 }
212 if (pcm_start(hfpmod.hfp_sco_tx) < 0) {
213 ALOGE("%s: pcm start for hfp sco tx failed", __func__);
214 ret = -EINVAL;
215 goto exit;
216 }
217 if (pcm_start(hfpmod.hfp_pcm_rx) < 0) {
218 ALOGE("%s: pcm start for hfp pcm rx failed", __func__);
219 ret = -EINVAL;
220 goto exit;
221 }
222 if (pcm_start(hfpmod.hfp_pcm_tx) < 0) {
223 ALOGE("%s: pcm start for hfp pcm tx failed", __func__);
224 ret = -EINVAL;
225 goto exit;
226 }
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800227
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800228 hfpmod.is_hfp_running = true;
Amit Shekhar967cab32014-02-07 17:03:21 -0800229 hfp_set_volume(adev, hfpmod.hfp_volume);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800230
231 ALOGD("%s: exit: status(%d)", __func__, ret);
232 return 0;
233
234exit:
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800235 stop_hfp(adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800236 ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
237 return ret;
238}
239
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800240static int32_t stop_hfp(struct audio_device *adev)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800241{
242 int32_t i, ret = 0;
243 struct audio_usecase *uc_info;
244
245 ALOGD("%s: enter", __func__);
246 hfpmod.is_hfp_running = false;
247
248 /* 1. Close the PCM devices */
249 if (hfpmod.hfp_sco_rx) {
250 pcm_close(hfpmod.hfp_sco_rx);
251 hfpmod.hfp_sco_rx = NULL;
252 }
253 if (hfpmod.hfp_sco_tx) {
254 pcm_close(hfpmod.hfp_sco_tx);
255 hfpmod.hfp_sco_tx = NULL;
256 }
257 if (hfpmod.hfp_pcm_rx) {
258 pcm_close(hfpmod.hfp_pcm_rx);
259 hfpmod.hfp_pcm_rx = NULL;
260 }
261 if (hfpmod.hfp_pcm_tx) {
262 pcm_close(hfpmod.hfp_pcm_tx);
263 hfpmod.hfp_pcm_tx = NULL;
264 }
265
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800266 uc_info = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800267 if (uc_info == NULL) {
268 ALOGE("%s: Could not find the usecase (%d) in the list",
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800269 __func__, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800270 return -EINVAL;
271 }
272
273 /* 2. Get and set stream specific mixer controls */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700274 disable_audio_route(adev, uc_info);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800275
276 /* 3. Disable the rx and tx devices */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700277 disable_snd_device(adev, uc_info->out_snd_device);
278 disable_snd_device(adev, uc_info->in_snd_device);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800279
280 list_remove(&uc_info->list);
281 free(uc_info);
282
283 ALOGD("%s: exit: status(%d)", __func__, ret);
284 return ret;
285}
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800286
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800287bool audio_extn_hfp_is_active(struct audio_device *adev)
288{
289 struct audio_usecase *hfp_usecase = NULL;
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800290 hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800291
292 if (hfp_usecase != NULL)
293 return true;
294 else
295 return false;
296}
297
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800298audio_usecase_t audio_extn_hfp_get_usecase()
299{
300 return hfpmod.ucid;
301}
302
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800303void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
304{
305 int ret;
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800306 int rate;
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800307 int val;
Amit Shekhar967cab32014-02-07 17:03:21 -0800308 float vol;
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800309 char value[32]={0};
310
311 ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
312 sizeof(value));
313 if (ret >= 0) {
314 if(!strncmp(value,"true",sizeof(value)))
315 ret = start_hfp(adev,parms);
316 else
317 stop_hfp(adev);
318 }
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800319 memset(value, 0, sizeof(value));
320 ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
321 sizeof(value));
322 if (ret >= 0) {
323 rate = atoi(value);
324 if (rate == 8000){
325 hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
326 pcm_config_hfp.rate = rate;
327 }
328 else if (rate == 16000){
329 hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
330 pcm_config_hfp.rate = rate;
331 }
332 else
333 ALOGE("Unsupported rate..");
334 }
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800335
336 if(hfpmod.is_hfp_running) {
337 memset(value, 0, sizeof(value));
338 ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
339 value, sizeof(value));
340 if (ret >= 0) {
341 val = atoi(value);
342 if(val > 0)
343 select_devices(adev, hfpmod.ucid);
344 }
345 }
Amit Shekhar967cab32014-02-07 17:03:21 -0800346
347 memset(value, 0, sizeof(value));
348 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
349 value, sizeof(value));
350 if (ret >= 0) {
351 if (sscanf(value, "%f", &vol) != 1){
352 ALOGE("%s: error in retrieving hfp volume", __func__);
353 ret = -EIO;
354 goto exit;
355 }
356 ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
357 hfp_set_volume(adev, vol);
358 }
359exit:
360 ALOGV("%s Exit",__func__);
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800361}
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800362#endif /*HFP_ENABLED*/