blob: 239c975a42e4f6fdc470265692f5e625042b7b7d [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
98 if (value < 0.0) {
99 ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
100 value = 0.0;
101 } else {
102 value = ((value > 15.000000) ? 1.0 : (value / 15));
103 ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
104 }
105 vol = lrint((value * 0x2000) + 0.5);
106 hfpmod.hfp_volume = value;
107
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));
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800139 uc_info->id = hfpmod.ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800140 uc_info->type = PCM_HFP_CALL;
141 uc_info->stream.out = adev->primary_output;
142 uc_info->devices = adev->primary_output->devices;
143 uc_info->in_snd_device = SND_DEVICE_NONE;
144 uc_info->out_snd_device = SND_DEVICE_NONE;
145
146 list_add_tail(&adev->usecase_list, &uc_info->list);
147
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800148 select_devices(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800149
150 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
151 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
152 pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
153 pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
154 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
155 pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
156 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
157 __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
158 ret = -EIO;
159 goto exit;
160 }
161
162 ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
163 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
164
165 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800166 __func__, adev->snd_card, pcm_dev_rx_id);
167 hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800168 pcm_dev_asm_rx_id,
169 PCM_OUT, &pcm_config_hfp);
170 if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
171 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
172 ret = -EIO;
173 goto exit;
174 }
175 ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800176 __func__, adev->snd_card, pcm_dev_tx_id);
177 hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800178 pcm_dev_rx_id,
179 PCM_OUT, &pcm_config_hfp);
180 if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
181 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
182 ret = -EIO;
183 goto exit;
184 }
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800185 hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800186 pcm_dev_asm_tx_id,
187 PCM_IN, &pcm_config_hfp);
188 if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
189 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
190 ret = -EIO;
191 goto exit;
192 }
193 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800194 __func__, adev->snd_card, pcm_dev_tx_id);
195 hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800196 pcm_dev_tx_id,
197 PCM_IN, &pcm_config_hfp);
198 if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
199 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
200 ret = -EIO;
201 goto exit;
202 }
203 pcm_start(hfpmod.hfp_sco_rx);
204 pcm_start(hfpmod.hfp_sco_tx);
205 pcm_start(hfpmod.hfp_pcm_rx);
206 pcm_start(hfpmod.hfp_pcm_tx);
207
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800208 hfpmod.is_hfp_running = true;
Amit Shekhar967cab32014-02-07 17:03:21 -0800209 hfp_set_volume(adev, hfpmod.hfp_volume);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800210
211 ALOGD("%s: exit: status(%d)", __func__, ret);
212 return 0;
213
214exit:
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800215 stop_hfp(adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800216 ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
217 return ret;
218}
219
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800220static int32_t stop_hfp(struct audio_device *adev)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800221{
222 int32_t i, ret = 0;
223 struct audio_usecase *uc_info;
224
225 ALOGD("%s: enter", __func__);
226 hfpmod.is_hfp_running = false;
227
228 /* 1. Close the PCM devices */
229 if (hfpmod.hfp_sco_rx) {
230 pcm_close(hfpmod.hfp_sco_rx);
231 hfpmod.hfp_sco_rx = NULL;
232 }
233 if (hfpmod.hfp_sco_tx) {
234 pcm_close(hfpmod.hfp_sco_tx);
235 hfpmod.hfp_sco_tx = NULL;
236 }
237 if (hfpmod.hfp_pcm_rx) {
238 pcm_close(hfpmod.hfp_pcm_rx);
239 hfpmod.hfp_pcm_rx = NULL;
240 }
241 if (hfpmod.hfp_pcm_tx) {
242 pcm_close(hfpmod.hfp_pcm_tx);
243 hfpmod.hfp_pcm_tx = NULL;
244 }
245
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800246 uc_info = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800247 if (uc_info == NULL) {
248 ALOGE("%s: Could not find the usecase (%d) in the list",
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800249 __func__, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800250 return -EINVAL;
251 }
252
253 /* 2. Get and set stream specific mixer controls */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700254 disable_audio_route(adev, uc_info);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800255
256 /* 3. Disable the rx and tx devices */
Haynes Mathew George1376ca62014-04-24 11:55:48 -0700257 disable_snd_device(adev, uc_info->out_snd_device);
258 disable_snd_device(adev, uc_info->in_snd_device);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800259
260 list_remove(&uc_info->list);
261 free(uc_info);
262
263 ALOGD("%s: exit: status(%d)", __func__, ret);
264 return ret;
265}
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800266
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800267bool audio_extn_hfp_is_active(struct audio_device *adev)
268{
269 struct audio_usecase *hfp_usecase = NULL;
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800270 hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800271
272 if (hfp_usecase != NULL)
273 return true;
274 else
275 return false;
276}
277
Vimal Puthanveed41fcff22014-01-23 15:56:53 -0800278audio_usecase_t audio_extn_hfp_get_usecase()
279{
280 return hfpmod.ucid;
281}
282
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800283void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
284{
285 int ret;
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800286 int rate;
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800287 int val;
Amit Shekhar967cab32014-02-07 17:03:21 -0800288 float vol;
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800289 char value[32]={0};
290
291 ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
292 sizeof(value));
293 if (ret >= 0) {
294 if(!strncmp(value,"true",sizeof(value)))
295 ret = start_hfp(adev,parms);
296 else
297 stop_hfp(adev);
298 }
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800299 memset(value, 0, sizeof(value));
300 ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
301 sizeof(value));
302 if (ret >= 0) {
303 rate = atoi(value);
304 if (rate == 8000){
305 hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
306 pcm_config_hfp.rate = rate;
307 }
308 else if (rate == 16000){
309 hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
310 pcm_config_hfp.rate = rate;
311 }
312 else
313 ALOGE("Unsupported rate..");
314 }
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800315
316 if(hfpmod.is_hfp_running) {
317 memset(value, 0, sizeof(value));
318 ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
319 value, sizeof(value));
320 if (ret >= 0) {
321 val = atoi(value);
322 if(val > 0)
323 select_devices(adev, hfpmod.ucid);
324 }
325 }
Amit Shekhar967cab32014-02-07 17:03:21 -0800326
327 memset(value, 0, sizeof(value));
328 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
329 value, sizeof(value));
330 if (ret >= 0) {
331 if (sscanf(value, "%f", &vol) != 1){
332 ALOGE("%s: error in retrieving hfp volume", __func__);
333 ret = -EIO;
334 goto exit;
335 }
336 ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
337 hfp_set_volume(adev, vol);
338 }
339exit:
340 ALOGV("%s Exit",__func__);
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800341}
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800342#endif /*HFP_ENABLED*/