Home | History | Annotate | Download | only in audio_extn
      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*/
     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     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 
    466 exit:
    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 
    536 static 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 
    691 static 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 
    702 void 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 
    783 int 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 
    798 int 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 
    853 exit:
    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 
    884 void 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 
    913 bool audio_extn_spkr_prot_is_enabled()
    914 {
    915     return handle.spkr_prot_enable;
    916 }
    917 #endif /*SPKR_PROT_ENABLED*/
    918