Home | History | Annotate | Download | only in audio_extn
      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