1 /* 2 * Copyright (C) 2017 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_cirrus_playback" 18 /*#define LOG_NDEBUG 0*/ 19 20 #include <errno.h> 21 #include <math.h> 22 #include <log/log.h> 23 #include <fcntl.h> 24 #include "../audio_hw.h" 25 #include "platform.h" 26 #include "platform_api.h" 27 #include <sys/stat.h> 28 #include <linux/types.h> 29 #include <linux/ioctl.h> 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <dlfcn.h> 33 #include <math.h> 34 #include <pthread.h> 35 #include <time.h> 36 #include <unistd.h> 37 #include <cutils/properties.h> 38 #include "audio_extn.h" 39 40 struct cirrus_playback_session { 41 void *adev_handle; 42 pthread_mutex_t fb_prot_mutex; 43 pthread_t calibration_thread; 44 #ifdef ENABLE_CIRRUS_DETECTION 45 pthread_t failure_detect_thread; 46 #endif 47 struct pcm *pcm_rx; 48 struct pcm *pcm_tx; 49 volatile int32_t state; 50 }; 51 52 enum cirrus_playback_state { 53 INIT = 0, 54 CALIBRATING = 1, 55 IDLE = 2, 56 PLAYBACK = 3 57 }; 58 59 struct crus_sp_ioctl_header { 60 uint32_t size; 61 uint32_t module_id; 62 uint32_t param_id; 63 uint32_t data_length; 64 void *data; 65 }; 66 67 /* Payload struct for getting calibration result from DSP module */ 68 struct cirrus_cal_result_t { 69 int32_t status_l; 70 int32_t checksum_l; 71 int32_t z_l; 72 int32_t status_r; 73 int32_t checksum_r; 74 int32_t z_r; 75 }; 76 77 /* Payload struct for setting the RX and TX use cases */ 78 struct crus_rx_run_case_ctrl_t { 79 int32_t value; 80 int32_t status_l; 81 int32_t checksum_l; 82 int32_t z_l; 83 int32_t status_r; 84 int32_t checksum_r; 85 int32_t z_r; 86 }; 87 88 #define CRUS_SP_FILE "/dev/msm_cirrus_playback" 89 #define CRUS_CAL_FILE "/persist/audio/audio.cal" 90 #define CRUS_TX_CONF_FILE "vendor/firmware/crus_sp_config_%s_tx.bin" 91 #define CRUS_RX_CONF_FILE "vendor/firmware/crus_sp_config_%s_rx.bin" 92 #define CONFIG_FILE_SIZE 128 93 94 #define CRUS_SP_USECASE_MIXER "Cirrus SP Usecase" 95 #define CRUS_SP_LOAD_CONF_MIXER "Cirrus SP Load Config" 96 #define CRUS_SP_FAIL_DET_MIXER "Cirrus SP Failure Detection" 97 98 #define CIRRUS_SP 0x10027053 99 100 #define CRUS_MODULE_ID_TX 0x00000002 101 #define CRUS_MODULE_ID_RX 0x00000001 102 103 #define CRUS_PARAM_RX_SET_USECASE 0x00A1AF02 104 #define CRUS_PARAM_TX_SET_USECASE 0x00A1BF0A 105 106 #define CRUS_PARAM_RX_SET_CALIB 0x00A1AF03 107 #define CRUS_PARAM_TX_SET_CALIB 0x00A1BF03 108 109 #define CRUS_PARAM_RX_SET_EXT_CONFIG 0x00A1AF05 110 #define CRUS_PARAM_TX_SET_EXT_CONFIG 0x00A1BF08 111 112 #define CRUS_PARAM_RX_GET_TEMP 0x00A1AF07 113 #define CRUS_PARAM_TX_GET_TEMP_CAL 0x00A1BF06 114 // variables based on CSPL tuning file, max parameter length is 96 integers (384 bytes) 115 #define CRUS_PARAM_TEMP_MAX_LENGTH 384 116 117 #define CRUS_AFE_PARAM_ID_ENABLE 0x00010203 118 119 #define FAIL_DETECT_INIT_WAIT_US 500000 120 #define FAIL_DETECT_LOOP_WAIT_US 300000 121 122 #define CRUS_DEFAULT_CAL_L 0x2A11 123 #define CRUS_DEFAULT_CAL_R 0x29CB 124 125 #define CRUS_SP_IOCTL_MAGIC 'a' 126 127 #define CRUS_SP_IOCTL_GET _IOWR(CRUS_SP_IOCTL_MAGIC, 219, void *) 128 #define CRUS_SP_IOCTL_SET _IOWR(CRUS_SP_IOCTL_MAGIC, 220, void *) 129 #define CRUS_SP_IOCTL_GET_CALIB _IOWR(CRUS_SP_IOCTL_MAGIC, 221, void *) 130 #define CRUS_SP_IOCTL_SET_CALIB _IOWR(CRUS_SP_IOCTL_MAGIC, 222, void *) 131 132 133 134 static struct pcm_config pcm_config_cirrus_tx = { 135 .channels = 2, 136 .rate = 48000, 137 .period_size = 320, 138 .period_count = 4, 139 .format = PCM_FORMAT_S16_LE, 140 .start_threshold = 0, 141 .stop_threshold = INT_MAX, 142 .avail_min = 0, 143 }; 144 145 static struct pcm_config pcm_config_cirrus_rx = { 146 .channels = 8, 147 .rate = 48000, 148 .period_size = 320, 149 .period_count = 4, 150 .format = PCM_FORMAT_S32_LE, 151 .start_threshold = 0, 152 .stop_threshold = INT_MAX, 153 .avail_min = 0, 154 }; 155 156 static struct cirrus_playback_session handle; 157 158 static void *audio_extn_cirrus_calibration_thread(); 159 160 #ifdef ENABLE_CIRRUS_DETECTION 161 static void *audio_extn_cirrus_failure_detect_thread(); 162 #endif 163 164 void audio_extn_spkr_prot_init(void *adev) { 165 ALOGI("%s: Initialize Cirrus Logic Playback module", __func__); 166 167 memset(&handle, 0, sizeof(handle)); 168 if (!adev) { 169 ALOGE("%s: Invalid params", __func__); 170 return; 171 } 172 173 handle.adev_handle = adev; 174 handle.state = INIT; 175 176 pthread_mutex_init(&handle.fb_prot_mutex, NULL); 177 178 (void)pthread_create(&handle.calibration_thread, 179 (const pthread_attr_t *) NULL, 180 audio_extn_cirrus_calibration_thread, &handle); 181 } 182 183 static int audio_extn_cirrus_run_calibration() { 184 struct audio_device *adev = handle.adev_handle; 185 struct crus_sp_ioctl_header header; 186 struct cirrus_cal_result_t result; 187 struct mixer_ctl *ctl = NULL; 188 FILE *cal_file = NULL; 189 int ret = 0, dev_file = -1; 190 char *buffer = NULL; 191 uint32_t option = 1; 192 193 ALOGI("%s: Running speaker calibration", __func__); 194 195 dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK); 196 if (dev_file < 0) { 197 ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)", 198 __func__, dev_file); 199 ret = -EINVAL; 200 goto exit; 201 } 202 203 buffer = calloc(1, CRUS_PARAM_TEMP_MAX_LENGTH); 204 if (!buffer) { 205 ALOGE("%s: allocate memory failed", __func__); 206 ret = -ENOMEM; 207 goto exit; 208 } 209 210 cal_file = fopen(CRUS_CAL_FILE, "r"); 211 if (cal_file) { 212 ret = fread(&result, sizeof(result), 1, cal_file); 213 if (ret != 1) { 214 ALOGE("%s: Cirrus SP calibration file cannot be read , read size: %lu file error: %d", 215 __func__, (unsigned long)ret * sizeof(result), ferror(cal_file)); 216 ret = -EINVAL; 217 fclose(cal_file); 218 goto exit; 219 } 220 221 fclose(cal_file); 222 } else { 223 224 ALOGV("%s: Calibrating...", __func__); 225 226 header.size = sizeof(header); 227 header.module_id = CRUS_MODULE_ID_RX; 228 header.param_id = CRUS_PARAM_RX_SET_CALIB; 229 header.data_length = sizeof(option); 230 header.data = &option; 231 232 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET, &header); 233 if (ret < 0) { 234 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)", 235 __func__, ret); 236 ret = -EINVAL; 237 goto exit; 238 } 239 240 header.size = sizeof(header); 241 header.module_id = CRUS_MODULE_ID_TX; 242 header.param_id = CRUS_PARAM_TX_SET_CALIB; 243 header.data_length = sizeof(option); 244 header.data = &option; 245 246 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET, &header); 247 if (ret < 0) { 248 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)", 249 __func__, ret); 250 ret = -EINVAL; 251 goto exit; 252 } 253 254 sleep(2); 255 256 header.size = sizeof(header); 257 header.module_id = CRUS_MODULE_ID_TX; 258 header.param_id = CRUS_PARAM_TX_GET_TEMP_CAL; 259 header.data_length = sizeof(result); 260 header.data = &result; 261 262 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header); 263 if (ret < 0) { 264 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)", 265 __func__, ret); 266 ret = -EINVAL; 267 goto exit; 268 } 269 270 if (result.status_l != 1) { 271 ALOGE("%s: Left calibration failure. Please check speakers", 272 __func__); 273 ret = -EINVAL; 274 } 275 276 if (result.status_r != 1) { 277 ALOGE("%s: Right calibration failure. Please check speakers", 278 __func__); 279 ret = -EINVAL; 280 } 281 282 if (ret < 0) 283 goto exit; 284 285 #ifdef ENABLED_CIRRUS_WRITE_CAL_FILE 286 cal_file = fopen(CRUS_CAL_FILE, "wb"); 287 if (cal_file == NULL) { 288 ALOGE("%s: Cannot create Cirrus SP calibration file (%s)", 289 __func__, strerror(errno)); 290 ret = -EINVAL; 291 goto exit; 292 } 293 294 ret = fwrite(&result, sizeof(result), 1, cal_file); 295 296 if (ret != 1) { 297 ALOGE("%s: Unable to save Cirrus SP calibration data, write size %lu, file error %d", 298 __func__, (unsigned long)ret * sizeof(result), ferror(cal_file)); 299 fclose(cal_file); 300 ret = -EINVAL; 301 goto exit; 302 } 303 304 fclose(cal_file); 305 306 ALOGI("%s: Cirrus calibration file successfully written", 307 __func__); 308 #endif 309 } 310 311 header.size = sizeof(header); 312 header.module_id = CRUS_MODULE_ID_TX; 313 header.param_id = 0; 314 header.data_length = sizeof(result); 315 header.data = &result; 316 317 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET_CALIB, &header); 318 319 if (ret < 0) { 320 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)", __func__, ret); 321 ret = -EINVAL; 322 goto exit; 323 } 324 325 ctl = mixer_get_ctl_by_name(adev->mixer, 326 CRUS_SP_USECASE_MIXER); 327 if (!ctl) { 328 ALOGE("%s: Could not get ctl for mixer cmd - %s", 329 __func__, CRUS_SP_USECASE_MIXER); 330 ret = -EINVAL; 331 goto exit; 332 } 333 334 ret = mixer_ctl_set_value(ctl, 0, 0); // Set RX external firmware config 335 if (ret < 0) { 336 ALOGE("%s: set default usecase failed", __func__); 337 goto exit; 338 } 339 340 sleep(1); 341 342 header.size = sizeof(header); 343 header.module_id = CRUS_MODULE_ID_RX; 344 header.param_id = CRUS_PARAM_RX_GET_TEMP; 345 header.data_length = CRUS_PARAM_TEMP_MAX_LENGTH; 346 header.data = buffer; 347 348 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header); 349 if (ret < 0) { 350 ALOGE("%s: Cirrus SP temperature IOCTL failure (%d)", __func__, ret); 351 ret = -EINVAL; 352 goto exit; 353 } 354 355 ALOGI("%s: Cirrus SP successfully calibrated", __func__); 356 357 exit: 358 if (dev_file >= 0) 359 close(dev_file); 360 free(buffer); 361 ALOGV("%s: Exit", __func__); 362 363 return ret; 364 } 365 366 static int audio_extn_cirrus_load_usecase_configs(void) { 367 struct audio_device *adev = handle.adev_handle; 368 struct mixer_ctl *ctl_uc = NULL, *ctl_config = NULL; 369 char *filename = NULL; 370 int ret = 0, default_uc = 0; 371 struct snd_card_split *snd_split_handle = NULL; 372 snd_split_handle = audio_extn_get_snd_card_split(); 373 374 ALOGI("%s: Loading usecase tuning configs", __func__); 375 376 ctl_uc = mixer_get_ctl_by_name(adev->mixer, CRUS_SP_USECASE_MIXER); 377 ctl_config = mixer_get_ctl_by_name(adev->mixer, 378 CRUS_SP_LOAD_CONF_MIXER); 379 if (!ctl_uc || !ctl_config) { 380 ALOGE("%s: Could not get ctl for mixer commands", __func__); 381 ret = -EINVAL; 382 goto exit; 383 } 384 385 filename = calloc(1 , CONFIG_FILE_SIZE); 386 if (!filename) { 387 ALOGE("%s: allocate memory failed", __func__); 388 ret = -ENOMEM; 389 goto exit; 390 } 391 392 default_uc = mixer_ctl_get_value(ctl_uc, 0); 393 394 ret = mixer_ctl_set_value(ctl_uc, 0, default_uc); 395 if (ret < 0) { 396 ALOGE("%s set uscase %d failed", __func__, default_uc); 397 goto exit; 398 } 399 400 /* Load TX Tuning Config (if available) */ 401 snprintf(filename, CONFIG_FILE_SIZE, CRUS_TX_CONF_FILE, snd_split_handle->form_factor); 402 if (access(filename, R_OK) == 0) { 403 ret = mixer_ctl_set_value(ctl_config, 0, 2); 404 if (ret < 0) { 405 ALOGE("%s set tx config failed", __func__); 406 goto exit; 407 } 408 } else { 409 ALOGE("%s: Tuning file not found (%s)", __func__, 410 filename); 411 ret = -EINVAL; 412 goto exit; 413 } 414 /* Load RX Tuning Config (if available) */ 415 snprintf(filename, CONFIG_FILE_SIZE, CRUS_RX_CONF_FILE, snd_split_handle->form_factor); 416 if (access(filename, R_OK) == 0) { 417 ret = mixer_ctl_set_value(ctl_config, 0, 1); 418 if (ret < 0) { 419 ALOGE("%s set rx config failed", __func__); 420 goto exit; 421 } 422 } else { 423 ALOGE("%s: Tuning file not found (%s)", __func__, 424 filename); 425 ret = -EINVAL; 426 goto exit; 427 } 428 429 ALOGI("%s: Cirrus SP loaded available usecase configs", __func__); 430 exit: 431 free(filename); 432 ALOGI("%s: Exit", __func__); 433 434 return ret; 435 } 436 437 static void *audio_extn_cirrus_calibration_thread() { 438 struct audio_device *adev = handle.adev_handle; 439 struct audio_usecase *uc_info_rx = NULL; 440 int ret = 0; 441 int32_t pcm_dev_rx_id, prev_state; 442 uint32_t retries = 5; 443 444 ALOGI("%s: PCM Stream thread", __func__); 445 446 while (!adev->platform && retries) { 447 sleep(1); 448 ALOGI("%s: Waiting...", __func__); 449 retries--; 450 } 451 452 prev_state = handle.state; 453 handle.state = CALIBRATING; 454 455 uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); 456 if (!uc_info_rx) { 457 ALOGE("%s: rx usecase can not be found", __func__); 458 goto exit; 459 } 460 pthread_mutex_lock(&adev->lock); 461 462 uc_info_rx->id = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER; 463 uc_info_rx->type = PCM_PLAYBACK; 464 uc_info_rx->in_snd_device = SND_DEVICE_NONE; 465 uc_info_rx->stream.out = adev->primary_output; 466 uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER; 467 list_add_tail(&adev->usecase_list, &uc_info_rx->list); 468 469 enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER); 470 enable_audio_route(adev, uc_info_rx); 471 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK); 472 473 if (pcm_dev_rx_id < 0) { 474 ALOGE("%s: Invalid pcm device for usecase (%d)", 475 __func__, uc_info_rx->id); 476 pthread_mutex_unlock(&adev->lock); 477 goto exit; 478 } 479 480 handle.pcm_rx = pcm_open(adev->snd_card, pcm_dev_rx_id, 481 PCM_OUT, &pcm_config_cirrus_rx); 482 483 if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) { 484 ALOGE("%s: PCM device not ready: %s", __func__, 485 pcm_get_error(handle.pcm_rx)); 486 pthread_mutex_unlock(&adev->lock); 487 goto close_stream; 488 } 489 490 if (pcm_start(handle.pcm_rx) < 0) { 491 ALOGE("%s: pcm start for RX failed; error = %s", __func__, 492 pcm_get_error(handle.pcm_rx)); 493 pthread_mutex_unlock(&adev->lock); 494 goto close_stream; 495 } 496 pthread_mutex_unlock(&adev->lock); 497 ALOGI("%s: PCM thread streaming", __func__); 498 499 ret = audio_extn_cirrus_run_calibration(); 500 ALOGE_IF(ret < 0, "%s: Calibration procedure failed (%d)", __func__, ret); 501 502 ret = audio_extn_cirrus_load_usecase_configs(); 503 ALOGE_IF(ret < 0, "%s: Set tuning configs failed (%d)", __func__, ret); 504 505 close_stream: 506 pthread_mutex_lock(&adev->lock); 507 if (handle.pcm_rx) { 508 ALOGI("%s: pcm_rx_close", __func__); 509 pcm_close(handle.pcm_rx); 510 handle.pcm_rx = NULL; 511 } 512 disable_audio_route(adev, uc_info_rx); 513 disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER); 514 list_remove(&uc_info_rx->list); 515 free(uc_info_rx); 516 pthread_mutex_unlock(&adev->lock); 517 exit: 518 handle.state = (prev_state == PLAYBACK) ? PLAYBACK : IDLE; 519 520 #ifdef ENABLE_CIRRUS_DETECTION 521 if (handle.state == PLAYBACK) 522 (void)pthread_create(&handle.failure_detect_thread, 523 (const pthread_attr_t *) NULL, 524 audio_extn_cirrus_failure_detect_thread, 525 &handle); 526 #endif 527 528 ALOGV("%s: Exit", __func__); 529 530 pthread_exit(0); 531 return NULL; 532 } 533 534 #ifdef ENABLE_CIRRUS_DETECTION 535 void *audio_extn_cirrus_failure_detect_thread() { 536 struct audio_device *adev = handle.adev_handle; 537 struct crus_sp_ioctl_header header; 538 struct mixer_ctl *ctl = NULL; 539 const int32_t r_scale_factor = 100000000; 540 const int32_t t_scale_factor = 100000; 541 const int32_t r_err_range = 70000000; 542 const int32_t t_err_range = 210000; 543 const int32_t amp_factor = 71498; 544 const int32_t material = 250; 545 int32_t *buffer = NULL; 546 int ret = 0, dev_file = -1, out_cal0 = 0, out_cal1 = 0; 547 int rL = 0, rR = 0, zL = 0, zR = 0, tL = 0, tR = 0; 548 int rdL = 0, rdR = 0, tdL = 0, tdR = 0, ambL = 0, ambR = 0; 549 bool left_cal_done = false, right_cal_done = false; 550 bool det_en = false; 551 552 ALOGI("%s: Entry", __func__); 553 554 ctl = mixer_get_ctl_by_name(adev->mixer, CRUS_SP_FAIL_DET_MIXER); 555 det_en = mixer_ctl_get_value(ctl, 0); 556 557 if (!det_en) 558 goto exit; 559 560 dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK); 561 if (dev_file < 0) { 562 ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)", 563 __func__, dev_file); 564 goto exit; 565 } 566 567 buffer = calloc(1, CRUS_PARAM_TEMP_MAX_LENGTH); 568 if (!buffer) { 569 ALOGE("%s: allocate memory failed", __func__); 570 goto exit; 571 } 572 573 header.size = sizeof(header); 574 header.module_id = CRUS_MODULE_ID_RX; 575 header.param_id = CRUS_PARAM_RX_GET_TEMP; 576 header.data_length = CRUS_PARAM_TEMP_MAX_LENGTH; 577 header.data = buffer; 578 579 usleep(FAIL_DETECT_INIT_WAIT_US); 580 581 pthread_mutex_lock(&handle.fb_prot_mutex); 582 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header); 583 pthread_mutex_unlock(&handle.fb_prot_mutex); 584 if (ret < 0) { 585 ALOGE("%s: Cirrus SP IOCTL failure (%d)", 586 __func__, ret); 587 goto exit; 588 } 589 590 zL = buffer[2] * amp_factor; 591 zR = buffer[4] * amp_factor; 592 593 ambL = buffer[10]; 594 ambR = buffer[6]; 595 596 out_cal0 = buffer[12]; 597 out_cal1 = buffer[13]; 598 599 left_cal_done = (out_cal0 == 2) && (out_cal1 == 2) && 600 (buffer[2] != CRUS_DEFAULT_CAL_L); 601 602 out_cal0 = buffer[14]; 603 out_cal1 = buffer[15]; 604 605 right_cal_done = (out_cal0 == 2) && (out_cal1 == 2) && 606 (buffer[4] != CRUS_DEFAULT_CAL_R); 607 608 if (left_cal_done) { 609 ALOGI("%s: L Speaker Impedance: %d.%08d ohms", __func__, 610 zL / r_scale_factor, abs(zL) % r_scale_factor); 611 ALOGI("%s: L Calibration Temperature: %d C", __func__, ambL); 612 } else 613 ALOGE("%s: Left speaker uncalibrated", __func__); 614 615 if (right_cal_done) { 616 ALOGI("%s: R Speaker Impedance: %d.%08d ohms", __func__, 617 zR / r_scale_factor, abs(zR) % r_scale_factor); 618 ALOGI("%s: R Calibration Temperature: %d C", __func__, ambR); 619 } else 620 ALOGE("%s: Right speaker uncalibrated", __func__); 621 622 if (!left_cal_done && !right_cal_done) 623 goto exit; 624 625 ALOGI("%s: Monitoring speaker impedance & temperature...", __func__); 626 627 while ((handle.state == PLAYBACK) && det_en) { 628 pthread_mutex_lock(&handle.fb_prot_mutex); 629 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header); 630 pthread_mutex_unlock(&handle.fb_prot_mutex); 631 if (ret < 0) { 632 ALOGE("%s: Cirrus SP IOCTL failure (%d)", 633 __func__, ret); 634 goto loop; 635 } 636 637 rL = buffer[3]; 638 rR = buffer[1]; 639 640 zL = buffer[2]; 641 zR = buffer[4]; 642 643 if ((zL == 0) || (zR == 0)) 644 goto loop; 645 646 tdL = (material * t_scale_factor * (rL-zL) / zL); 647 tdR = (material * t_scale_factor * (rR-zR) / zR); 648 649 rL *= amp_factor; 650 rR *= amp_factor; 651 652 zL *= amp_factor; 653 zR *= amp_factor; 654 655 tL = tdL + (ambL * t_scale_factor); 656 tR = tdR + (ambR * t_scale_factor); 657 658 rdL = abs(zL - rL); 659 rdR = abs(zR - rR); 660 661 if (left_cal_done && (rL != 0) && (rdL > r_err_range)) 662 ALOGI("%s: Left speaker impedance out of range (%d.%08d ohms)", 663 __func__, rL / r_scale_factor, 664 abs(rL % r_scale_factor)); 665 666 if (right_cal_done && (rR != 0) && (rdR > r_err_range)) 667 ALOGI("%s: Right speaker impedance out of range (%d.%08d ohms)", 668 __func__, rR / r_scale_factor, 669 abs(rR % r_scale_factor)); 670 671 if (left_cal_done && (rL != 0) && (tdL > t_err_range)) 672 ALOGI("%s: Left speaker temperature out of range (%d.%05d C)", 673 __func__, tL / t_scale_factor, 674 abs(tL % t_scale_factor)); 675 676 if (right_cal_done && (rR != 0) && (tdR > t_err_range)) 677 ALOGI("%s: Right speaker temperature out of range (%d.%05d C)", 678 __func__, tR / t_scale_factor, 679 abs(tR % t_scale_factor)); 680 681 loop: 682 det_en = mixer_ctl_get_value(ctl, 0); 683 usleep(FAIL_DETECT_LOOP_WAIT_US); 684 } 685 686 exit: 687 if (dev_file >= 0) 688 close(dev_file); 689 free(buffer); 690 ALOGI("%s: Exit ", __func__); 691 692 pthread_exit(0); 693 return NULL; 694 } 695 #endif 696 697 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) { 698 struct audio_usecase *uc_info_tx; 699 struct audio_device *adev = handle.adev_handle; 700 int32_t pcm_dev_tx_id = -1, ret = 0; 701 702 ALOGV("%s: Entry", __func__); 703 704 if (!adev) { 705 ALOGE("%s: Invalid params", __func__); 706 return -EINVAL; 707 } 708 709 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(*uc_info_tx)); 710 if (!uc_info_tx) { 711 ALOGE("%s: allocate memory failed", __func__); 712 return -ENOMEM; 713 } 714 715 audio_route_apply_and_update_path(adev->audio_route, 716 platform_get_snd_device_name(snd_device)); 717 718 pthread_mutex_lock(&handle.fb_prot_mutex); 719 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; 720 uc_info_tx->type = PCM_CAPTURE; 721 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; 722 uc_info_tx->out_snd_device = SND_DEVICE_NONE; 723 handle.pcm_tx = NULL; 724 725 list_add_tail(&adev->usecase_list, &uc_info_tx->list); 726 727 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 728 enable_audio_route(adev, uc_info_tx); 729 730 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); 731 732 if (pcm_dev_tx_id < 0) { 733 ALOGE("%s: Invalid pcm device for usecase (%d)", 734 __func__, uc_info_tx->id); 735 ret = -ENODEV; 736 goto exit; 737 } 738 739 handle.pcm_tx = pcm_open(adev->snd_card, 740 pcm_dev_tx_id, 741 PCM_IN, &pcm_config_cirrus_tx); 742 743 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { 744 ALOGE("%s: PCM device not ready: %s", __func__, pcm_get_error(handle.pcm_tx)); 745 ret = -EIO; 746 goto exit; 747 } 748 749 if (pcm_start(handle.pcm_tx) < 0) { 750 ALOGE("%s: pcm start for TX failed; error = %s", __func__, 751 pcm_get_error(handle.pcm_tx)); 752 ret = -EINVAL; 753 goto exit; 754 } 755 756 #ifdef ENABLE_CIRRUS_DETECTION 757 if (handle.state == IDLE) 758 (void)pthread_create(&handle.failure_detect_thread, 759 (const pthread_attr_t *) NULL, 760 audio_extn_cirrus_failure_detect_thread, 761 &handle); 762 #endif 763 764 handle.state = PLAYBACK; 765 exit: 766 if (ret) { 767 handle.state = IDLE; 768 if (handle.pcm_tx) { 769 ALOGI("%s: pcm_tx_close", __func__); 770 pcm_close(handle.pcm_tx); 771 handle.pcm_tx = NULL; 772 } 773 774 disable_audio_route(adev, uc_info_tx); 775 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 776 list_remove(&uc_info_tx->list); 777 free(uc_info_tx); 778 } 779 780 pthread_mutex_unlock(&handle.fb_prot_mutex); 781 ALOGV("%s: Exit", __func__); 782 return ret; 783 } 784 785 void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device) { 786 struct audio_usecase *uc_info_tx; 787 struct audio_device *adev = handle.adev_handle; 788 789 ALOGV("%s: Entry", __func__); 790 791 pthread_mutex_lock(&handle.fb_prot_mutex); 792 793 handle.state = IDLE; 794 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX); 795 796 if (uc_info_tx) { 797 if (handle.pcm_tx) { 798 ALOGI("%s: pcm_tx_close", __func__); 799 pcm_close(handle.pcm_tx); 800 handle.pcm_tx = NULL; 801 } 802 803 disable_audio_route(adev, uc_info_tx); 804 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); 805 list_remove(&uc_info_tx->list); 806 free(uc_info_tx); 807 808 audio_route_reset_path(adev->audio_route, 809 platform_get_snd_device_name(snd_device)); 810 } 811 812 pthread_mutex_unlock(&handle.fb_prot_mutex); 813 814 ALOGV("%s: Exit", __func__); 815 } 816 817 bool audio_extn_spkr_prot_is_enabled() { 818 return true; 819 } 820 821 int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device) { 822 switch(snd_device) { 823 case SND_DEVICE_OUT_SPEAKER: 824 case SND_DEVICE_OUT_SPEAKER_REVERSE: 825 return SND_DEVICE_OUT_SPEAKER_PROTECTED; 826 case SND_DEVICE_OUT_SPEAKER_SAFE: 827 return SND_DEVICE_OUT_SPEAKER_SAFE; 828 case SND_DEVICE_OUT_VOICE_SPEAKER: 829 return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED; 830 default: 831 return snd_device; 832 } 833 } 834 835 void audio_extn_spkr_prot_calib_cancel(__unused void *adev) { 836 // FIXME: wait or cancel audio_extn_cirrus_run_calibration 837 } 838