1/* 2 * Copyright (c) 2013 - 2014, The Linux Foundation. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 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 * 17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#define LOG_TAG "audio_hw_spkr_prot" 31/*#define LOG_NDEBUG 0*/ 32#define LOG_NDDEBUG 0 33 34#include <errno.h> 35#include <math.h> 36#include <cutils/log.h> 37#include <fcntl.h> 38#include "audio_hw.h" 39#include "platform.h" 40#include "platform_api.h" 41#include <sys/stat.h> 42#include <stdlib.h> 43#include <dlfcn.h> 44#include <math.h> 45#include <cutils/properties.h> 46#include "audio_extn.h" 47#include <linux/msm_audio_calibration.h> 48 49#ifdef SPKR_PROT_ENABLED 50 51/*Range of spkr temparatures -30C to 80C*/ 52#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6)) 53#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6)) 54#define VI_FEED_CHANNEL "VI_FEED_TX Channels" 55 56/*Set safe temp value to 40C*/ 57#define SAFE_SPKR_TEMP 40 58#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6)) 59 60/*Range of resistance values 2ohms to 40 ohms*/ 61#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24)) 62#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24)) 63 64/*Path where the calibration file will be stored*/ 65#define CALIB_FILE "/data/misc/audio/audio.cal" 66 67/*Time between retries for calibartion or intial wait time 68 after boot up*/ 69#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000) 70 71#define MIN_SPKR_IDLE_SEC (60 * 30) 72 73/*Once calibration is started sleep for 1 sec to allow 74 the calibration to kick off*/ 75#define SLEEP_AFTER_CALIB_START (3000) 76 77/*If calibration is in progress wait for 200 msec before querying 78 for status again*/ 79#define WAIT_FOR_GET_CALIB_STATUS (200 * 1000) 80 81/*Speaker states*/ 82#define SPKR_NOT_CALIBRATED -1 83#define SPKR_CALIBRATED 1 84 85/*Speaker processing state*/ 86#define SPKR_PROCESSING_IN_PROGRESS 1 87#define SPKR_PROCESSING_IN_IDLE 0 88 89/*Modes of Speaker Protection*/ 90enum speaker_protection_mode { 91 SPKR_PROTECTION_DISABLED = -1, 92 SPKR_PROTECTION_MODE_PROCESSING = 0, 93 SPKR_PROTECTION_MODE_CALIBRATE = 1, 94}; 95 96struct speaker_prot_session { 97 int spkr_prot_mode; 98 int spkr_processing_state; 99 int thermal_client_handle; 100 pthread_mutex_t mutex_spkr_prot; 101 pthread_t spkr_calibration_thread; 102 pthread_mutex_t spkr_prot_thermalsync_mutex; 103 pthread_cond_t spkr_prot_thermalsync; 104 int cancel_spkr_calib; 105 pthread_cond_t spkr_calib_cancel; 106 pthread_mutex_t spkr_calib_cancelack_mutex; 107 pthread_cond_t spkr_calibcancel_ack; 108 pthread_t speaker_prot_threadid; 109 void *thermal_handle; 110 void *adev_handle; 111 int spkr_prot_t0; 112 struct pcm *pcm_rx; 113 struct pcm *pcm_tx; 114 int (*client_register_callback) 115 (char *client_name, int (*callback)(int), void *data); 116 void (*thermal_client_unregister_callback)(int handle); 117 int (*thermal_client_request)(char *client_name, int req_data); 118 bool spkr_prot_enable; 119 bool spkr_in_use; 120 struct timespec spkr_last_time_used; 121}; 122 123static struct pcm_config pcm_config_skr_prot = { 124 .channels = 4, 125 .rate = 48000, 126 .period_size = 256, 127 .period_count = 4, 128 .format = PCM_FORMAT_S16_LE, 129 .start_threshold = 0, 130 .stop_threshold = INT_MAX, 131 .avail_min = 0, 132}; 133 134static struct speaker_prot_session handle; 135static int vi_feed_no_channels; 136 137static void spkr_prot_set_spkrstatus(bool enable) 138{ 139 struct timespec ts; 140 if (enable) 141 handle.spkr_in_use = true; 142 else { 143 handle.spkr_in_use = false; 144 clock_gettime(CLOCK_MONOTONIC, &handle.spkr_last_time_used); 145 } 146} 147 148void audio_extn_spkr_prot_calib_cancel(void *adev) 149{ 150 pthread_t threadid; 151 struct audio_usecase *uc_info; 152 int count = 0; 153 threadid = pthread_self(); 154 ALOGV("%s: Entry", __func__); 155 if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) { 156 ALOGE("%s: Invalid params", __func__); 157 return; 158 } 159 uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX); 160 if (uc_info) { 161 pthread_mutex_lock(&handle.mutex_spkr_prot); 162 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); 163 handle.cancel_spkr_calib = 1; 164 pthread_cond_signal(&handle.spkr_calib_cancel); 165 pthread_mutex_unlock(&handle.mutex_spkr_prot); 166 pthread_cond_wait(&handle.spkr_calibcancel_ack, 167 &handle.spkr_calib_cancelack_mutex); 168 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); 169 } 170 ALOGV("%s: Exit", __func__); 171} 172 173static bool is_speaker_in_use(unsigned long *sec) 174{ 175 struct timespec temp; 176 if (!sec) { 177 ALOGE("%s: Invalid params", __func__); 178 return true; 179 } 180 if (handle.spkr_in_use) { 181 *sec = 0; 182 return true; 183 } else { 184 clock_gettime(CLOCK_MONOTONIC, &temp); 185 *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec; 186 return false; 187 } 188} 189 190 191static int get_spkr_prot_cal(int cal_fd, 192 struct audio_cal_info_msm_spk_prot_status *status) 193{ 194 int ret = 0; 195 struct audio_cal_fb_spk_prot_status cal_data; 196 197 if (cal_fd < 0) { 198 ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd); 199 ret = -EINVAL; 200 goto done; 201 } 202 203 if (status == NULL) { 204 ALOGE("%s: Error: status NULL", __func__); 205 ret = -EINVAL; 206 goto done; 207 } 208 209 cal_data.hdr.data_size = sizeof(cal_data); 210 cal_data.hdr.version = VERSION_0_0; 211 cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE; 212 cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type); 213 cal_data.cal_type.cal_hdr.version = VERSION_0_0; 214 cal_data.cal_type.cal_hdr.buffer_number = 0; 215 cal_data.cal_type.cal_data.mem_handle = -1; 216 217 if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) { 218 ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!", 219 __func__); 220 ret = -ENODEV; 221 goto done; 222 } 223 224 status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1]; 225 status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2]; 226 status->status = cal_data.cal_type.cal_info.status; 227done: 228 return ret; 229} 230 231static int set_spkr_prot_cal(int cal_fd, 232 struct audio_cal_info_spk_prot_cfg *protCfg) 233{ 234 int ret = 0; 235 struct audio_cal_fb_spk_prot_cfg cal_data; 236 char value[PROPERTY_VALUE_MAX]; 237 238 if (cal_fd < 0) { 239 ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd); 240 ret = -EINVAL; 241 goto done; 242 } 243 244 if (protCfg == NULL) { 245 ALOGE("%s: Error: status NULL", __func__); 246 ret = -EINVAL; 247 goto done; 248 } 249 250 memset(&cal_data, 0, sizeof(cal_data)); 251 cal_data.hdr.data_size = sizeof(cal_data); 252 cal_data.hdr.version = VERSION_0_0; 253 cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE; 254 cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type); 255 cal_data.cal_type.cal_hdr.version = VERSION_0_0; 256 cal_data.cal_type.cal_hdr.buffer_number = 0; 257 cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1]; 258 cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2]; 259 cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1]; 260 cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2]; 261 cal_data.cal_type.cal_info.mode = protCfg->mode; 262 property_get("persist.spkr.cal.duration", value, "0"); 263 if (atoi(value) > 0) { 264 ALOGD("%s: quick calibration enabled", __func__); 265 cal_data.cal_type.cal_info.quick_calib_flag = 1; 266 } else { 267 ALOGD("%s: quick calibration disabled", __func__); 268 cal_data.cal_type.cal_info.quick_calib_flag = 0; 269 } 270 271 cal_data.cal_type.cal_data.mem_handle = -1; 272 273 if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) { 274 ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!", 275 __func__); 276 ret = -ENODEV; 277 goto done; 278 } 279done: 280 return ret; 281} 282 283static int vi_feed_get_channels(struct audio_device *adev) 284{ 285 struct mixer_ctl *ctl; 286 const char *mixer_ctl_name = VI_FEED_CHANNEL; 287 int value; 288 289 ALOGV("%s: entry", __func__); 290 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); 291 if (!ctl) { 292 ALOGE("%s: Could not get ctl for mixer cmd - %s", 293 __func__, mixer_ctl_name); 294 goto error; 295 } 296 value = mixer_ctl_get_value(ctl, 0); 297 if (value < 0) 298 goto error; 299 else 300 return value+1; 301error: 302 return -EINVAL; 303} 304 305static int spkr_calibrate(int t0) 306{ 307 struct audio_device *adev = handle.adev_handle; 308 struct audio_cal_info_spk_prot_cfg protCfg; 309 struct audio_cal_info_msm_spk_prot_status status; 310 bool cleanup = false, disable_rx = false, disable_tx = false; 311 int acdb_fd = -1; 312 struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL; 313 int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1; 314 struct timespec ts; 315 bool acquire_device = false; 316 317 if (!adev) { 318 ALOGE("%s: Invalid params", __func__); 319 return -EINVAL; 320 } 321 if (!list_empty(&adev->usecase_list)) { 322 ALOGD("%s: Usecase present retry speaker protection", __func__); 323 return -EAGAIN; 324 } 325 acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK); 326 if (acdb_fd < 0) { 327 ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__); 328 return -ENODEV; 329 } else { 330 protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS; 331 /* HAL for speaker protection gets only one Temperature */ 332 protCfg.t0[SP_V2_SPKR_1] = t0; 333 protCfg.t0[SP_V2_SPKR_2] = t0; 334 if (set_spkr_prot_cal(acdb_fd, &protCfg)) { 335 ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT", 336 __func__); 337 status.status = -ENODEV; 338 goto exit; 339 } 340 } 341 uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); 342 if (!uc_info_rx) { 343 return -ENOMEM; 344 } 345 uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX; 346 uc_info_rx->type = PCM_PLAYBACK; 347 uc_info_rx->in_snd_device = SND_DEVICE_NONE; 348 uc_info_rx->stream.out = adev->primary_output; 349 uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED; 350 disable_rx = true; 351 list_add_tail(&adev->usecase_list, &uc_info_rx->list); 352 enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED); 353 enable_audio_route(adev, uc_info_rx); 354 355 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK); 356 ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id); 357 if (pcm_dev_rx_id < 0) { 358 ALOGE("%s: Invalid pcm device for usecase (%d)", 359 __func__, uc_info_rx->id); 360 status.status = -ENODEV; 361 goto exit; 362 } 363 handle.pcm_rx = handle.pcm_tx = NULL; 364 handle.pcm_rx = pcm_open(adev->snd_card, 365 pcm_dev_rx_id, 366 PCM_OUT, &pcm_config_skr_prot); 367 if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) { 368 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx)); 369 status.status = -EIO; 370 goto exit; 371 } 372 uc_info_tx = (struct audio_usecase *) 373 calloc(1, sizeof(struct audio_usecase)); 374 if (!uc_info_tx) { 375 status.status = -ENOMEM; 376 goto exit; 377 } 378 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; 379 uc_info_tx->type = PCM_CAPTURE; 380 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; 381 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 382 383 disable_tx = true; 384 list_add_tail(&adev->usecase_list, &uc_info_tx->list); 385 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 386 enable_audio_route(adev, uc_info_tx); 387 388 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); 389 if (pcm_dev_tx_id < 0) { 390 ALOGE("%s: Invalid pcm device for usecase (%d)", 391 __func__, uc_info_tx->id); 392 status.status = -ENODEV; 393 goto exit; 394 } 395 handle.pcm_tx = pcm_open(adev->snd_card, 396 pcm_dev_tx_id, 397 PCM_IN, &pcm_config_skr_prot); 398 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { 399 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); 400 status.status = -EIO; 401 goto exit; 402 } 403 if (pcm_start(handle.pcm_rx) < 0) { 404 ALOGE("%s: pcm start for RX failed", __func__); 405 status.status = -EINVAL; 406 goto exit; 407 } 408 if (pcm_start(handle.pcm_tx) < 0) { 409 ALOGE("%s: pcm start for TX failed", __func__); 410 status.status = -EINVAL; 411 goto exit; 412 } 413 cleanup = true; 414 clock_gettime(CLOCK_REALTIME, &ts); 415 ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000); 416 ts.tv_nsec = 0; 417 pthread_mutex_lock(&handle.mutex_spkr_prot); 418 pthread_mutex_unlock(&adev->lock); 419 acquire_device = true; 420 (void)pthread_cond_timedwait(&handle.spkr_calib_cancel, 421 &handle.mutex_spkr_prot, &ts); 422 ALOGD("%s: Speaker calibration done", __func__); 423 cleanup = true; 424 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); 425 if (handle.cancel_spkr_calib) { 426 status.status = -EAGAIN; 427 goto exit; 428 } 429 if (acdb_fd > 0) { 430 status.status = -EINVAL; 431 while (!get_spkr_prot_cal(acdb_fd, &status)) { 432 /*sleep for 200 ms to check for status check*/ 433 if (!status.status) { 434 ALOGD("%s: spkr_prot_thread calib Success R0 %d %d", 435 __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]); 436 FILE *fp; 437 438 vi_feed_no_channels = vi_feed_get_channels(adev); 439 ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels); 440 if (vi_feed_no_channels < 0) { 441 ALOGE("%s: no of channels negative !!", __func__); 442 /* limit the number of channels to 2*/ 443 vi_feed_no_channels = 2; 444 } 445 446 fp = fopen(CALIB_FILE,"wb"); 447 if (!fp) { 448 ALOGE("%s: spkr_prot_thread File open failed %s", 449 __func__, strerror(errno)); 450 status.status = -ENODEV; 451 } else { 452 int i; 453 /* HAL for speaker protection is always calibrating for stereo usecase*/ 454 for (i = 0; i < vi_feed_no_channels; i++) { 455 fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp); 456 fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp); 457 } 458 fclose(fp); 459 } 460 break; 461 } else if (status.status == -EAGAIN) { 462 ALOGD("%s: spkr_prot_thread try again", __func__); 463 usleep(WAIT_FOR_GET_CALIB_STATUS); 464 } else { 465 ALOGE("%s: spkr_prot_thread get failed status %d", 466 __func__, status.status); 467 break; 468 } 469 } 470exit: 471 if (handle.pcm_rx) 472 pcm_close(handle.pcm_rx); 473 handle.pcm_rx = NULL; 474 if (handle.pcm_tx) 475 pcm_close(handle.pcm_tx); 476 handle.pcm_tx = NULL; 477 /* Clear TX calibration to handset mic */ 478 platform_send_audio_calibration(adev->platform, 479 SND_DEVICE_IN_HANDSET_MIC, 480 platform_get_default_app_type(adev->platform), 8000); 481 if (!status.status) { 482 protCfg.mode = MSM_SPKR_PROT_CALIBRATED; 483 protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1]; 484 protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2]; 485 if (set_spkr_prot_cal(acdb_fd, &protCfg)) 486 ALOGE("%s: spkr_prot_thread disable calib mode", __func__); 487 else 488 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; 489 } else { 490 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; 491 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; 492 if (set_spkr_prot_cal(acdb_fd, &protCfg)) 493 ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__); 494 } 495 if (acdb_fd > 0) 496 close(acdb_fd); 497 498 if (!handle.cancel_spkr_calib && cleanup) { 499 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); 500 pthread_cond_wait(&handle.spkr_calib_cancel, 501 &handle.mutex_spkr_prot); 502 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); 503 } 504 if (disable_rx) { 505 list_remove(&uc_info_rx->list); 506 disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED); 507 disable_audio_route(adev, uc_info_rx); 508 } 509 if (disable_tx) { 510 list_remove(&uc_info_tx->list); 511 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 512 disable_audio_route(adev, uc_info_tx); 513 } 514 if (uc_info_rx) free(uc_info_rx); 515 if (uc_info_tx) free(uc_info_tx); 516 if (cleanup) { 517 if (handle.cancel_spkr_calib) 518 pthread_cond_signal(&handle.spkr_calibcancel_ack); 519 handle.cancel_spkr_calib = 0; 520 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); 521 pthread_mutex_unlock(&handle.mutex_spkr_prot); 522 } 523 } 524 if (acquire_device) 525 pthread_mutex_lock(&adev->lock); 526 return status.status; 527} 528 529static void* spkr_calibration_thread() 530{ 531 unsigned long sec = 0; 532 int t0; 533 bool goahead = false; 534 struct audio_cal_info_spk_prot_cfg protCfg; 535 FILE *fp; 536 int acdb_fd; 537 struct audio_device *adev = handle.adev_handle; 538 unsigned long min_idle_time = MIN_SPKR_IDLE_SEC; 539 char value[PROPERTY_VALUE_MAX]; 540 541 /* If the value of this persist.spkr.cal.duration is 0 542 * then it means it will take 30min to calibrate 543 * and if the value is greater than zero then it would take 544 * that much amount of time to calibrate. 545 */ 546 property_get("persist.spkr.cal.duration", value, "0"); 547 if (atoi(value) > 0) 548 min_idle_time = atoi(value); 549 handle.speaker_prot_threadid = pthread_self(); 550 ALOGD("spkr_prot_thread enable prot Entry"); 551 acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK); 552 if (acdb_fd > 0) { 553 /*Set processing mode with t0/r0*/ 554 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; 555 if (set_spkr_prot_cal(acdb_fd, &protCfg)) { 556 ALOGE("%s: spkr_prot_thread enable prot failed", __func__); 557 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 558 close(acdb_fd); 559 } else 560 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; 561 } else { 562 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 563 ALOGE("%s: Failed to open acdb node", __func__); 564 } 565 if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) { 566 ALOGD("%s: Speaker protection disabled", __func__); 567 pthread_exit(0); 568 return NULL; 569 } 570 571 fp = fopen(CALIB_FILE,"rb"); 572 if (fp) { 573 int i; 574 bool spkr_calibrated = true; 575 /* HAL for speaker protection is always calibrating for stereo usecase*/ 576 vi_feed_no_channels = vi_feed_get_channels(adev); 577 ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels); 578 if (vi_feed_no_channels < 0) { 579 ALOGE("%s: no of channels negative !!", __func__); 580 /* limit the number of channels to 2*/ 581 vi_feed_no_channels = 2; 582 } 583 for (i = 0; i < vi_feed_no_channels; i++) { 584 fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp); 585 fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp); 586 } 587 ALOGD("%s: spkr_prot_thread r0 value %d %d", 588 __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]); 589 ALOGD("%s: spkr_prot_thread t0 value %d %d", 590 __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]); 591 fclose(fp); 592 /*Valid tempature range: -30C to 80C(in q6 format) 593 Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/ 594 for (i = 0; i < vi_feed_no_channels; i++) { 595 if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6) 596 && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24) 597 && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) { 598 spkr_calibrated = false; 599 break; 600 } 601 } 602 if (spkr_calibrated) { 603 ALOGD("%s: Spkr calibrated", __func__); 604 protCfg.mode = MSM_SPKR_PROT_CALIBRATED; 605 if (set_spkr_prot_cal(acdb_fd, &protCfg)) { 606 ALOGE("%s: enable prot failed", __func__); 607 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 608 } else 609 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; 610 close(acdb_fd); 611 pthread_exit(0); 612 return NULL; 613 } 614 close(acdb_fd); 615 } 616 617 while (1) { 618 ALOGV("%s: start calibration", __func__); 619 if (!handle.thermal_client_request("spkr",1)) { 620 ALOGD("%s: wait for callback from thermal daemon", __func__); 621 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); 622 pthread_cond_wait(&handle.spkr_prot_thermalsync, 623 &handle.spkr_prot_thermalsync_mutex); 624 /*Convert temp into q6 format*/ 625 t0 = (handle.spkr_prot_t0 * (1 << 6)); 626 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); 627 if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) { 628 ALOGE("%s: Calibration temparature error %d", __func__, 629 handle.spkr_prot_t0); 630 continue; 631 } 632 ALOGD("%s: Request t0 success value %d", __func__, 633 handle.spkr_prot_t0); 634 } else { 635 ALOGE("%s: Request t0 failed", __func__); 636 /*Assume safe value for temparature*/ 637 t0 = SAFE_SPKR_TEMP_Q6; 638 } 639 goahead = false; 640 pthread_mutex_lock(&adev->lock); 641 if (is_speaker_in_use(&sec)) { 642 ALOGD("%s: Speaker in use retry calibration", __func__); 643 pthread_mutex_unlock(&adev->lock); 644 continue; 645 } else { 646 ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time); 647 if (sec < min_idle_time) { 648 ALOGD("%s: speaker idle is less retry", __func__); 649 pthread_mutex_unlock(&adev->lock); 650 continue; 651 } 652 goahead = true; 653 } 654 if (!list_empty(&adev->usecase_list)) { 655 ALOGD("%s: Usecase active re-try calibration", __func__); 656 goahead = false; 657 pthread_mutex_unlock(&adev->lock); 658 } 659 if (goahead) { 660 int status; 661 status = spkr_calibrate(t0); 662 pthread_mutex_unlock(&adev->lock); 663 if (status == -EAGAIN) { 664 ALOGE("%s: failed to calibrate try again %s", 665 __func__, strerror(status)); 666 continue; 667 } else { 668 ALOGE("%s: calibrate status %s", __func__, strerror(status)); 669 } 670 ALOGD("%s: spkr_prot_thread end calibration", __func__); 671 break; 672 } 673 } 674 if (handle.thermal_client_handle) 675 handle.thermal_client_unregister_callback(handle.thermal_client_handle); 676 handle.thermal_client_handle = 0; 677 if (handle.thermal_handle) 678 dlclose(handle.thermal_handle); 679 handle.thermal_handle = NULL; 680 pthread_exit(0); 681 return NULL; 682} 683 684static int thermal_client_callback(int temp) 685{ 686 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); 687 ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp); 688 if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED) 689 handle.spkr_prot_t0 = temp; 690 pthread_cond_signal(&handle.spkr_prot_thermalsync); 691 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); 692 return 0; 693} 694 695void audio_extn_spkr_prot_init(void *adev) 696{ 697 char value[PROPERTY_VALUE_MAX]; 698 ALOGD("%s: Initialize speaker protection module", __func__); 699 memset(&handle, 0, sizeof(handle)); 700 if (!adev) { 701 ALOGE("%s: Invalid params", __func__); 702 return; 703 } 704 property_get("persist.speaker.prot.enable", value, ""); 705 handle.spkr_prot_enable = false; 706 if (!strncmp("true", value, 4)) 707 handle.spkr_prot_enable = true; 708 if (!handle.spkr_prot_enable) { 709 ALOGD("%s: Speaker protection disabled", __func__); 710 return; 711 } 712 handle.adev_handle = adev; 713 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 714 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; 715 handle.spkr_prot_t0 = -1; 716 pthread_cond_init(&handle.spkr_prot_thermalsync, NULL); 717 pthread_cond_init(&handle.spkr_calib_cancel, NULL); 718 pthread_cond_init(&handle.spkr_calibcancel_ack, NULL); 719 pthread_mutex_init(&handle.mutex_spkr_prot, NULL); 720 pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL); 721 pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL); 722 handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so", 723 RTLD_NOW); 724 if (!handle.thermal_handle) { 725 ALOGE("%s: DLOPEN for thermal client failed", __func__); 726 } else { 727 /*Query callback function symbol*/ 728 handle.client_register_callback = 729 (int (*)(char *, int (*)(int),void *)) 730 dlsym(handle.thermal_handle, "thermal_client_register_callback"); 731 handle.thermal_client_unregister_callback = 732 (void (*)(int) ) 733 dlsym(handle.thermal_handle, "thermal_client_unregister_callback"); 734 if (!handle.client_register_callback || 735 !handle.thermal_client_unregister_callback) { 736 ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__); 737 } else { 738 /*Register callback function*/ 739 handle.thermal_client_handle = 740 handle.client_register_callback("spkr", thermal_client_callback, NULL); 741 if (!handle.thermal_client_handle) { 742 ALOGE("%s: client_register_callback failed", __func__); 743 } else { 744 ALOGD("%s: spkr_prot client_register_callback success", __func__); 745 handle.thermal_client_request = (int (*)(char *, int)) 746 dlsym(handle.thermal_handle, "thermal_client_request"); 747 } 748 } 749 } 750 if (handle.thermal_client_request) { 751 ALOGD("%s: Create calibration thread", __func__); 752 (void)pthread_create(&handle.spkr_calibration_thread, 753 (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle); 754 } else { 755 ALOGE("%s: thermal_client_request failed", __func__); 756 if (handle.thermal_client_handle && 757 handle.thermal_client_unregister_callback) 758 handle.thermal_client_unregister_callback(handle.thermal_client_handle); 759 if (handle.thermal_handle) 760 dlclose(handle.thermal_handle); 761 handle.thermal_handle = NULL; 762 handle.spkr_prot_enable = false; 763 } 764 765 if (handle.spkr_prot_enable) { 766 char platform[PROPERTY_VALUE_MAX]; 767 property_get("ro.board.platform", platform, ""); 768 if (!strncmp("apq8084", platform, sizeof("apq8084"))) { 769 platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER, 770 "speaker-protected"); 771 } 772 } 773} 774 775int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device) 776{ 777 int acdb_id; 778 779 switch(snd_device) { 780 case SND_DEVICE_OUT_SPEAKER: 781 acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED); 782 break; 783 case SND_DEVICE_OUT_VOICE_SPEAKER: 784 acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED); 785 break; 786 default: 787 acdb_id = -EINVAL; 788 break; 789 } 790 return acdb_id; 791} 792 793int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device) 794{ 795 if (!handle.spkr_prot_enable) 796 return snd_device; 797 798 switch(snd_device) { 799 case SND_DEVICE_OUT_SPEAKER: 800 return SND_DEVICE_OUT_SPEAKER_PROTECTED; 801 case SND_DEVICE_OUT_VOICE_SPEAKER: 802 return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED; 803 default: 804 return snd_device; 805 } 806} 807 808int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) 809{ 810 struct audio_usecase *uc_info_tx; 811 struct audio_device *adev = handle.adev_handle; 812 int32_t pcm_dev_tx_id = -1, ret = 0; 813 814 ALOGV("%s: Entry", __func__); 815 /* cancel speaker calibration */ 816 if (!adev) { 817 ALOGE("%s: Invalid params", __func__); 818 return -EINVAL; 819 } 820 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device); 821 spkr_prot_set_spkrstatus(true); 822 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); 823 if (!uc_info_tx) { 824 return -ENOMEM; 825 } 826 ALOGV("%s: snd_device(%d: %s)", __func__, snd_device, 827 platform_get_snd_device_name(snd_device)); 828 audio_route_apply_and_update_path(adev->audio_route, 829 platform_get_snd_device_name(snd_device)); 830 831 pthread_mutex_lock(&handle.mutex_spkr_prot); 832 if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) { 833 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; 834 uc_info_tx->type = PCM_CAPTURE; 835 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; 836 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 837 handle.pcm_tx = NULL; 838 list_add_tail(&adev->usecase_list, &uc_info_tx->list); 839 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 840 enable_audio_route(adev, uc_info_tx); 841 842 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); 843 if (pcm_dev_tx_id < 0) { 844 ALOGE("%s: Invalid pcm device for usecase (%d)", 845 __func__, uc_info_tx->id); 846 ret = -ENODEV; 847 goto exit; 848 } 849 handle.pcm_tx = pcm_open(adev->snd_card, 850 pcm_dev_tx_id, 851 PCM_IN, &pcm_config_skr_prot); 852 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { 853 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); 854 ret = -EIO; 855 goto exit; 856 } 857 if (pcm_start(handle.pcm_tx) < 0) { 858 ALOGE("%s: pcm start for TX failed", __func__); 859 ret = -EINVAL; 860 } 861 } 862 863exit: 864 /* Clear VI feedback cal and replace with handset MIC */ 865 platform_send_audio_calibration(adev->platform, 866 SND_DEVICE_IN_HANDSET_MIC, 867 platform_get_default_app_type(adev->platform), 8000); 868 if (ret) { 869 if (handle.pcm_tx) 870 pcm_close(handle.pcm_tx); 871 handle.pcm_tx = NULL; 872 list_remove(&uc_info_tx->list); 873 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 874 disable_audio_route(adev, uc_info_tx); 875 free(uc_info_tx); 876 } else 877 handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS; 878 pthread_mutex_unlock(&handle.mutex_spkr_prot); 879 ALOGV("%s: Exit", __func__); 880 return ret; 881} 882 883void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device) 884{ 885 struct audio_usecase *uc_info_tx; 886 struct audio_device *adev = handle.adev_handle; 887 888 ALOGV("%s: Entry", __func__); 889 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device); 890 spkr_prot_set_spkrstatus(false); 891 pthread_mutex_lock(&handle.mutex_spkr_prot); 892 if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) { 893 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX); 894 if (handle.pcm_tx) 895 pcm_close(handle.pcm_tx); 896 handle.pcm_tx = NULL; 897 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 898 if (uc_info_tx) { 899 list_remove(&uc_info_tx->list); 900 disable_audio_route(adev, uc_info_tx); 901 free(uc_info_tx); 902 } 903 } 904 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; 905 pthread_mutex_unlock(&handle.mutex_spkr_prot); 906 if (adev) 907 audio_route_reset_and_update_path(adev->audio_route, 908 platform_get_snd_device_name(snd_device)); 909 ALOGV("%s: Exit", __func__); 910} 911 912bool audio_extn_spkr_prot_is_enabled() 913{ 914 return handle.spkr_prot_enable; 915} 916#endif /*SPKR_PROT_ENABLED*/ 917