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