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 <log/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/vendor/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*/ 80enum speaker_protection_mode { 81 SPKR_PROTECTION_DISABLED = -1, 82 SPKR_PROTECTION_MODE_PROCESSING = 0, 83 SPKR_PROTECTION_MODE_CALIBRATE = 1, 84}; 85 86struct 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 113static 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 124static struct speaker_prot_session handle; 125static int vi_feed_no_channels; 126 127static 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 138void 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 163static 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 181static 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; 217done: 218 return ret; 219} 220 221static 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 } 269done: 270 return ret; 271} 272 273static 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; 291error: 292 return -EINVAL; 293} 294 295// must be called with adev->lock acquired 296static 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 int app_type = 0; 308 309 if (!adev) { 310 ALOGE("%s: Invalid params", __func__); 311 return -EINVAL; 312 } 313 if (!list_empty(&adev->usecase_list)) { 314 ALOGD("%s: Usecase present retry speaker protection", __func__); 315 return -EAGAIN; 316 } 317 acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK); 318 if (acdb_fd < 0) { 319 ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__); 320 return -ENODEV; 321 } else { 322 protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS; 323 /* HAL for speaker protection gets only one Temperature */ 324 protCfg.t0[SP_V2_SPKR_1] = t0; 325 protCfg.t0[SP_V2_SPKR_2] = t0; 326 if (set_spkr_prot_cal(acdb_fd, &protCfg)) { 327 ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT", 328 __func__); 329 status.status = -ENODEV; 330 goto exit; 331 } 332 } 333 uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); 334 if (!uc_info_rx) { 335 return -ENOMEM; 336 } 337 uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX; 338 uc_info_rx->type = PCM_PLAYBACK; 339 uc_info_rx->in_snd_device = SND_DEVICE_NONE; 340 uc_info_rx->stream.out = adev->primary_output; 341 uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED; 342 disable_rx = true; 343 list_add_tail(&adev->usecase_list, &uc_info_rx->list); 344 enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED); 345 enable_audio_route(adev, uc_info_rx); 346 347 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK); 348 ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id); 349 if (pcm_dev_rx_id < 0) { 350 ALOGE("%s: Invalid pcm device for usecase (%d)", 351 __func__, uc_info_rx->id); 352 status.status = -ENODEV; 353 goto exit; 354 } 355 handle.pcm_rx = handle.pcm_tx = NULL; 356 handle.pcm_rx = pcm_open(adev->snd_card, 357 pcm_dev_rx_id, 358 PCM_OUT, &pcm_config_skr_prot); 359 if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) { 360 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx)); 361 status.status = -EIO; 362 goto exit; 363 } 364 uc_info_tx = (struct audio_usecase *) 365 calloc(1, sizeof(struct audio_usecase)); 366 if (!uc_info_tx) { 367 status.status = -ENOMEM; 368 goto exit; 369 } 370 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; 371 uc_info_tx->type = PCM_CAPTURE; 372 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; 373 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 374 375 disable_tx = true; 376 list_add_tail(&adev->usecase_list, &uc_info_tx->list); 377 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 378 enable_audio_route(adev, uc_info_tx); 379 380 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); 381 if (pcm_dev_tx_id < 0) { 382 ALOGE("%s: Invalid pcm device for usecase (%d)", 383 __func__, uc_info_tx->id); 384 status.status = -ENODEV; 385 goto exit; 386 } 387 handle.pcm_tx = pcm_open(adev->snd_card, 388 pcm_dev_tx_id, 389 PCM_IN, &pcm_config_skr_prot); 390 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { 391 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); 392 status.status = -EIO; 393 goto exit; 394 } 395 if (pcm_start(handle.pcm_rx) < 0) { 396 ALOGE("%s: pcm start for RX failed", __func__); 397 status.status = -EINVAL; 398 goto exit; 399 } 400 if (pcm_start(handle.pcm_tx) < 0) { 401 ALOGE("%s: pcm start for TX failed", __func__); 402 status.status = -EINVAL; 403 goto exit; 404 } 405 cleanup = true; 406 clock_gettime(CLOCK_REALTIME, &ts); 407 ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000); 408 ts.tv_nsec = 0; 409 pthread_mutex_lock(&handle.mutex_spkr_prot); 410 pthread_mutex_unlock(&adev->lock); 411 412 (void)pthread_cond_timedwait(&handle.spkr_calib_cancel, 413 &handle.mutex_spkr_prot, &ts); 414 ALOGD("%s: Speaker calibration done", __func__); 415 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); 416 if (handle.cancel_spkr_calib) { 417 status.status = -EAGAIN; 418 goto exit; 419 } 420 421 if (acdb_fd >= 0) { 422 status.status = -EINVAL; 423 retry_duration = 0; 424 while (!get_spkr_prot_cal(acdb_fd, &status) && 425 retry_duration < GET_SPKR_PROT_CAL_TIMEOUT_MSEC) { 426 if (!status.status) { 427 ALOGD("%s: spkr_prot_thread calib Success R0 %d %d", 428 __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]); 429 FILE *fp; 430 431 vi_feed_no_channels = vi_feed_get_channels(adev); 432 ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels); 433 if (vi_feed_no_channels < 0) { 434 ALOGE("%s: no of channels negative !!", __func__); 435 /* limit the number of channels to 2*/ 436 vi_feed_no_channels = 2; 437 } 438 439 fp = fopen(CALIB_FILE,"wb"); 440 if (!fp) { 441 ALOGE("%s: spkr_prot_thread File open failed %s", 442 __func__, strerror(errno)); 443 status.status = -ENODEV; 444 } else { 445 int i; 446 /* HAL for speaker protection is always calibrating for stereo usecase*/ 447 for (i = 0; i < vi_feed_no_channels; i++) { 448 fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp); 449 fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp); 450 } 451 fclose(fp); 452 } 453 break; 454 } else if (status.status == -EAGAIN) { 455 ALOGD("%s: spkr_prot_thread try again", __func__); 456 usleep(WAIT_FOR_GET_CALIB_STATUS * 1000); 457 retry_duration += WAIT_FOR_GET_CALIB_STATUS; 458 } else { 459 ALOGE("%s: spkr_prot_thread get failed status %d", 460 __func__, status.status); 461 break; 462 } 463 } 464 } 465 466exit: 467 if (handle.pcm_rx) 468 pcm_close(handle.pcm_rx); 469 handle.pcm_rx = NULL; 470 471 if (handle.pcm_tx) 472 pcm_close(handle.pcm_tx); 473 handle.pcm_tx = NULL; 474 475 /* Clear TX calibration to handset mic */ 476 if (platform_supports_app_type_cfg()) { 477 ALOGD("%s: Platform supports APP type configuration, using V2\n", __func__); 478 if (uc_info_tx != NULL) { 479 ALOGD("%s: UC Info TX is not NULL, updating and sending calibration\n", __func__); 480 uc_info_tx->in_snd_device = SND_DEVICE_IN_HANDSET_MIC; 481 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 482 platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type); 483 platform_send_audio_calibration_v2(adev->platform, uc_info_tx, 484 app_type, 8000); 485 } 486 } else { 487 ALOGW("%s: Platform does NOT support APP type configuration, using V1\n", __func__); 488 platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC); 489 } 490 if (!status.status) { 491 protCfg.mode = MSM_SPKR_PROT_CALIBRATED; 492 protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1]; 493 protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2]; 494 if (set_spkr_prot_cal(acdb_fd, &protCfg)) 495 ALOGE("%s: spkr_prot_thread disable calib mode", __func__); 496 else 497 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; 498 } else { 499 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; 500 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; 501 if (set_spkr_prot_cal(acdb_fd, &protCfg)) 502 ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__); 503 } 504 if (acdb_fd >= 0) 505 close(acdb_fd); 506 507 if (!handle.cancel_spkr_calib && cleanup) { 508 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); 509 pthread_cond_wait(&handle.spkr_calib_cancel, &handle.mutex_spkr_prot); 510 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); 511 } 512 if (disable_rx) { 513 list_remove(&uc_info_rx->list); 514 disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED); 515 disable_audio_route(adev, uc_info_rx); 516 } 517 if (disable_tx) { 518 list_remove(&uc_info_tx->list); 519 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 520 disable_audio_route(adev, uc_info_tx); 521 } 522 if (uc_info_rx) free(uc_info_rx); 523 if (uc_info_tx) free(uc_info_tx); 524 if (cleanup) { 525 if (handle.cancel_spkr_calib) 526 pthread_cond_signal(&handle.spkr_calibcancel_ack); 527 handle.cancel_spkr_calib = 0; 528 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); 529 pthread_mutex_unlock(&handle.mutex_spkr_prot); 530 pthread_mutex_lock(&adev->lock); 531 } 532 533 return status.status; 534} 535 536static void* spkr_calibration_thread() 537{ 538 unsigned long sec = 0; 539 int t0; 540 bool goahead = false; 541 struct audio_cal_info_spk_prot_cfg protCfg; 542 FILE *fp; 543 int acdb_fd; 544 struct audio_device *adev = handle.adev_handle; 545 unsigned long min_idle_time = MIN_SPKR_IDLE_SEC; 546 char value[PROPERTY_VALUE_MAX]; 547 548 /* If the value of this persist.spkr.cal.duration is 0 549 * then it means it will take 30min to calibrate 550 * and if the value is greater than zero then it would take 551 * that much amount of time to calibrate. 552 */ 553 property_get("persist.spkr.cal.duration", value, "0"); 554 if (atoi(value) > 0) 555 min_idle_time = atoi(value); 556 handle.speaker_prot_threadid = pthread_self(); 557 ALOGD("spkr_prot_thread enable prot Entry"); 558 acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK); 559 if (acdb_fd >= 0) { 560 /*Set processing mode with t0/r0*/ 561 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; 562 if (set_spkr_prot_cal(acdb_fd, &protCfg)) { 563 ALOGE("%s: spkr_prot_thread enable prot failed", __func__); 564 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 565 close(acdb_fd); 566 } else 567 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; 568 } else { 569 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 570 ALOGE("%s: Failed to open acdb node", __func__); 571 } 572 if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) { 573 ALOGD("%s: Speaker protection disabled", __func__); 574 pthread_exit(0); 575 return NULL; 576 } 577 578 fp = fopen(CALIB_FILE,"rb"); 579 if (fp) { 580 int i; 581 bool spkr_calibrated = true; 582 /* HAL for speaker protection is always calibrating for stereo usecase*/ 583 vi_feed_no_channels = vi_feed_get_channels(adev); 584 ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels); 585 if (vi_feed_no_channels < 0) { 586 ALOGE("%s: no of channels negative !!", __func__); 587 /* limit the number of channels to 2*/ 588 vi_feed_no_channels = 2; 589 } 590 for (i = 0; i < vi_feed_no_channels; i++) { 591 fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp); 592 fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp); 593 } 594 ALOGD("%s: spkr_prot_thread r0 value %d %d", 595 __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]); 596 ALOGD("%s: spkr_prot_thread t0 value %d %d", 597 __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]); 598 fclose(fp); 599 /*Valid tempature range: -30C to 80C(in q6 format) 600 Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/ 601 for (i = 0; i < vi_feed_no_channels; i++) { 602 if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6) 603 && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24) 604 && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) { 605 spkr_calibrated = false; 606 break; 607 } 608 } 609 if (spkr_calibrated) { 610 ALOGD("%s: Spkr calibrated", __func__); 611 protCfg.mode = MSM_SPKR_PROT_CALIBRATED; 612 if (set_spkr_prot_cal(acdb_fd, &protCfg)) { 613 ALOGE("%s: enable prot failed", __func__); 614 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 615 } else 616 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; 617 close(acdb_fd); 618 pthread_exit(0); 619 return NULL; 620 } 621 close(acdb_fd); 622 } 623 624 while (1) { 625 ALOGV("%s: start calibration", __func__); 626 if (!handle.thermal_client_request("spkr",1)) { 627 ALOGD("%s: wait for callback from thermal daemon", __func__); 628 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); 629 pthread_cond_wait(&handle.spkr_prot_thermalsync, 630 &handle.spkr_prot_thermalsync_mutex); 631 /*Convert temp into q6 format*/ 632 t0 = (handle.spkr_prot_t0 * (1 << 6)); 633 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); 634 if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) { 635 ALOGE("%s: Calibration temparature error %d", __func__, 636 handle.spkr_prot_t0); 637 continue; 638 } 639 ALOGD("%s: Request t0 success value %d", __func__, 640 handle.spkr_prot_t0); 641 } else { 642 ALOGE("%s: Request t0 failed", __func__); 643 /*Assume safe value for temparature*/ 644 t0 = SAFE_SPKR_TEMP_Q6; 645 } 646 goahead = false; 647 pthread_mutex_lock(&adev->lock); 648 if (is_speaker_in_use(&sec)) { 649 ALOGD("%s: Speaker in use retry calibration", __func__); 650 pthread_mutex_unlock(&adev->lock); 651 continue; 652 } else { 653 ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time); 654 if (sec < min_idle_time) { 655 ALOGD("%s: speaker idle is less retry", __func__); 656 pthread_mutex_unlock(&adev->lock); 657 continue; 658 } 659 goahead = true; 660 } 661 if (!list_empty(&adev->usecase_list)) { 662 ALOGD("%s: Usecase active re-try calibration", __func__); 663 goahead = false; 664 pthread_mutex_unlock(&adev->lock); 665 } 666 if (goahead) { 667 int status; 668 status = spkr_calibrate(t0); 669 pthread_mutex_unlock(&adev->lock); 670 if (status == -EAGAIN) { 671 ALOGE("%s: failed to calibrate try again %s", 672 __func__, strerror(status)); 673 continue; 674 } else { 675 ALOGE("%s: calibrate status %s", __func__, strerror(status)); 676 } 677 ALOGD("%s: spkr_prot_thread end calibration", __func__); 678 break; 679 } 680 } 681 if (handle.thermal_client_handle) 682 handle.thermal_client_unregister_callback(handle.thermal_client_handle); 683 handle.thermal_client_handle = 0; 684 if (handle.thermal_handle) 685 dlclose(handle.thermal_handle); 686 handle.thermal_handle = NULL; 687 pthread_exit(0); 688 return NULL; 689} 690 691static int thermal_client_callback(int temp) 692{ 693 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); 694 ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp); 695 if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED) 696 handle.spkr_prot_t0 = temp; 697 pthread_cond_signal(&handle.spkr_prot_thermalsync); 698 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); 699 return 0; 700} 701 702void audio_extn_spkr_prot_init(void *adev) 703{ 704 char value[PROPERTY_VALUE_MAX]; 705 ALOGD("%s: Initialize speaker protection module", __func__); 706 memset(&handle, 0, sizeof(handle)); 707 if (!adev) { 708 ALOGE("%s: Invalid params", __func__); 709 return; 710 } 711 property_get("persist.speaker.prot.enable", value, ""); 712 handle.spkr_prot_enable = false; 713 if (!strncmp("true", value, 4)) 714 handle.spkr_prot_enable = true; 715 if (!handle.spkr_prot_enable) { 716 ALOGD("%s: Speaker protection disabled", __func__); 717 return; 718 } 719 handle.adev_handle = adev; 720 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; 721 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; 722 handle.spkr_prot_t0 = -1; 723 pthread_cond_init(&handle.spkr_prot_thermalsync, NULL); 724 pthread_cond_init(&handle.spkr_calib_cancel, NULL); 725 pthread_cond_init(&handle.spkr_calibcancel_ack, NULL); 726 pthread_mutex_init(&handle.mutex_spkr_prot, NULL); 727 pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL); 728 pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL); 729 handle.thermal_handle = dlopen(THERMAL_CLIENT_LIBRARY_PATH, 730 RTLD_NOW); 731 if (!handle.thermal_handle) { 732 ALOGE("%s: DLOPEN for thermal client failed", __func__); 733 } else { 734 /*Query callback function symbol*/ 735 handle.thermal_client_register_callback = 736 (int (*)(char *, int (*)(int),void *)) 737 dlsym(handle.thermal_handle, "thermal_client_register_callback"); 738 handle.thermal_client_unregister_callback = 739 (void (*)(int) ) 740 dlsym(handle.thermal_handle, "thermal_client_unregister_callback"); 741 if (!handle.thermal_client_register_callback || 742 !handle.thermal_client_unregister_callback) { 743 ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__); 744 } else { 745 /*Register callback function*/ 746 handle.thermal_client_handle = 747 handle.thermal_client_register_callback("spkr", thermal_client_callback, NULL); 748 if (!handle.thermal_client_handle) { 749 ALOGE("%s: thermal_client_register_callback failed", __func__); 750 } else { 751 ALOGD("%s: spkr_prot thermal_client_register_callback success", __func__); 752 handle.thermal_client_request = (int (*)(char *, int)) 753 dlsym(handle.thermal_handle, "thermal_client_request"); 754 } 755 } 756 } 757 if (handle.thermal_client_request) { 758 ALOGD("%s: Create calibration thread", __func__); 759 (void)pthread_create(&handle.spkr_calibration_thread, 760 (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle); 761 } else { 762 ALOGE("%s: thermal_client_request failed", __func__); 763 if (handle.thermal_client_handle && 764 handle.thermal_client_unregister_callback) 765 handle.thermal_client_unregister_callback(handle.thermal_client_handle); 766 if (handle.thermal_handle) 767 dlclose(handle.thermal_handle); 768 handle.thermal_handle = NULL; 769 handle.spkr_prot_enable = false; 770 } 771 772 if (handle.spkr_prot_enable) { 773 char platform[PROPERTY_VALUE_MAX]; 774 property_get("ro.board.platform", platform, ""); 775 if (!strncmp("apq8084", platform, sizeof("apq8084"))) { 776 platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER, 777 "speaker-protected", 778 "SLIMBUS_0_RX"); 779 } 780 } 781} 782 783int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device) 784{ 785 if (!handle.spkr_prot_enable) 786 return snd_device; 787 788 switch(snd_device) { 789 case SND_DEVICE_OUT_SPEAKER: 790 return SND_DEVICE_OUT_SPEAKER_PROTECTED; 791 case SND_DEVICE_OUT_VOICE_SPEAKER: 792 return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED; 793 default: 794 return snd_device; 795 } 796} 797 798int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) 799{ 800 struct audio_usecase *uc_info_tx; 801 struct audio_device *adev = handle.adev_handle; 802 int32_t pcm_dev_tx_id = -1, ret = 0; 803 int app_type = 0; 804 805 ALOGV("%s: Entry", __func__); 806 if (!adev) { 807 ALOGE("%s: Invalid params", __func__); 808 return -EINVAL; 809 } 810 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device); 811 spkr_prot_set_spkrstatus(true); 812 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); 813 if (!uc_info_tx) { 814 return -ENOMEM; 815 } 816 ALOGV("%s: snd_device(%d: %s)", __func__, snd_device, 817 platform_get_snd_device_name(snd_device)); 818 audio_route_apply_and_update_path(adev->audio_route, 819 platform_get_snd_device_name(snd_device)); 820 821 pthread_mutex_lock(&handle.mutex_spkr_prot); 822 if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) { 823 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; 824 uc_info_tx->type = PCM_CAPTURE; 825 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; 826 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 827 handle.pcm_tx = NULL; 828 list_add_tail(&adev->usecase_list, &uc_info_tx->list); 829 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 830 enable_audio_route(adev, uc_info_tx); 831 832 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); 833 if (pcm_dev_tx_id < 0) { 834 ALOGE("%s: Invalid pcm device for usecase (%d)", 835 __func__, uc_info_tx->id); 836 ret = -ENODEV; 837 goto exit; 838 } 839 handle.pcm_tx = pcm_open(adev->snd_card, 840 pcm_dev_tx_id, 841 PCM_IN, &pcm_config_skr_prot); 842 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { 843 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); 844 ret = -EIO; 845 goto exit; 846 } 847 if (pcm_start(handle.pcm_tx) < 0) { 848 ALOGE("%s: pcm start for TX failed", __func__); 849 ret = -EINVAL; 850 } 851 } 852 853exit: 854 /* Clear VI feedback cal and replace with handset MIC */ 855 if (platform_supports_app_type_cfg()) { 856 ALOGD("%s: Platform supports APP type configuration, using V2\n", __func__); 857 if (uc_info_tx != NULL) { 858 ALOGD("%s: UC Info TX is not NULL, updating and sending calibration\n", __func__); 859 uc_info_tx->in_snd_device = SND_DEVICE_IN_HANDSET_MIC; 860 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 861 platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type); 862 platform_send_audio_calibration_v2(adev->platform, uc_info_tx, 863 app_type, 8000); 864 } 865 } else { 866 ALOGW("%s: Platform does not support APP type configuration, using V1\n", __func__); 867 platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC); 868 } 869 if (ret) { 870 if (handle.pcm_tx) 871 pcm_close(handle.pcm_tx); 872 handle.pcm_tx = NULL; 873 list_remove(&uc_info_tx->list); 874 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 875 disable_audio_route(adev, uc_info_tx); 876 free(uc_info_tx); 877 } else 878 handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS; 879 pthread_mutex_unlock(&handle.mutex_spkr_prot); 880 ALOGV("%s: Exit", __func__); 881 return ret; 882} 883 884void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device) 885{ 886 struct audio_usecase *uc_info_tx; 887 struct audio_device *adev = handle.adev_handle; 888 889 ALOGV("%s: Entry", __func__); 890 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device); 891 spkr_prot_set_spkrstatus(false); 892 pthread_mutex_lock(&handle.mutex_spkr_prot); 893 if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) { 894 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX); 895 if (handle.pcm_tx) 896 pcm_close(handle.pcm_tx); 897 handle.pcm_tx = NULL; 898 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 899 if (uc_info_tx) { 900 list_remove(&uc_info_tx->list); 901 disable_audio_route(adev, uc_info_tx); 902 free(uc_info_tx); 903 } 904 } 905 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; 906 pthread_mutex_unlock(&handle.mutex_spkr_prot); 907 if (adev) 908 audio_route_reset_and_update_path(adev->audio_route, 909 platform_get_snd_device_name(snd_device)); 910 ALOGV("%s: Exit", __func__); 911} 912 913bool audio_extn_spkr_prot_is_enabled() 914{ 915 return handle.spkr_prot_enable; 916} 917#endif /*SPKR_PROT_ENABLED*/ 918