Gopikrishnaiah Anandan | f538cef | 2013-10-28 14:06:03 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions are |
| 6 | * met: |
| 7 | * * Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * * Redistributions in binary form must reproduce the above |
| 10 | * copyright notice, this list of conditions and the following |
| 11 | * disclaimer in the documentation and/or other materials provided |
| 12 | * with the distribution. |
| 13 | * * Neither the name of The Linux Foundation nor the names of its |
| 14 | * contributors may be used to endorse or promote products derived |
| 15 | * from this software without specific prior written permission. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| 21 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| 24 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| 26 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| 27 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | */ |
| 29 | |
| 30 | #define LOG_TAG "audio_hw_spkr_prot" |
| 31 | /*#define LOG_NDEBUG 0*/ |
| 32 | #define LOG_NDDEBUG 0 |
| 33 | |
| 34 | #include <errno.h> |
| 35 | #include <math.h> |
| 36 | #include <cutils/log.h> |
| 37 | #include <fcntl.h> |
| 38 | #include "audio_hw.h" |
| 39 | #include "platform.h" |
| 40 | #include "platform_api.h" |
| 41 | #include <sys/stat.h> |
| 42 | #include <stdlib.h> |
| 43 | #include <dlfcn.h> |
| 44 | #include <math.h> |
| 45 | #include <cutils/properties.h> |
| 46 | #include "audio_extn.h" |
| 47 | #include <linux/msm_audio_acdb.h> |
| 48 | |
| 49 | #ifdef SPKR_PROT_ENABLED |
| 50 | |
| 51 | /*Range of spkr temparatures -30C to 80C*/ |
| 52 | #define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6)) |
| 53 | #define MAX_SPKR_TEMP_Q6 (80 * (1 << 6)) |
| 54 | |
| 55 | /*Set safe temp value to 40C*/ |
| 56 | #define SAFE_SPKR_TEMP 40 |
| 57 | #define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6)) |
| 58 | |
| 59 | /*Range of resistance values 2ohms to 40 ohms*/ |
| 60 | #define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24)) |
| 61 | #define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24)) |
| 62 | |
| 63 | /*Path where the calibration file will be stored*/ |
| 64 | #define CALIB_FILE "/data/misc/audio/audio.cal" |
| 65 | |
| 66 | /*Time between retries for calibartion or intial wait time |
| 67 | after boot up*/ |
| 68 | #define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000) |
| 69 | |
| 70 | #define MIN_SPKR_IDLE_SEC (60 * 30) |
| 71 | |
| 72 | /*Once calibration is started sleep for 1 sec to allow |
| 73 | the calibration to kick off*/ |
| 74 | #define SLEEP_AFTER_CALIB_START (3000) |
| 75 | |
| 76 | /*If calibration is in progress wait for 200 msec before querying |
| 77 | for status again*/ |
| 78 | #define WAIT_FOR_GET_CALIB_STATUS (200 * 1000) |
| 79 | |
| 80 | /*Speaker states*/ |
| 81 | #define SPKR_NOT_CALIBRATED -1 |
| 82 | #define SPKR_CALIBRATED 1 |
| 83 | |
| 84 | /*Speaker processing state*/ |
| 85 | #define SPKR_PROCESSING_IN_PROGRESS 1 |
| 86 | #define SPKR_PROCESSING_IN_IDLE 0 |
| 87 | |
| 88 | /*Modes of Speaker Protection*/ |
| 89 | enum speaker_protection_mode { |
| 90 | SPKR_PROTECTION_DISABLED = -1, |
| 91 | SPKR_PROTECTION_MODE_PROCESSING = 0, |
| 92 | SPKR_PROTECTION_MODE_CALIBRATE = 1, |
| 93 | }; |
| 94 | |
| 95 | struct speaker_prot_session { |
| 96 | int spkr_prot_mode; |
| 97 | int spkr_processing_state; |
| 98 | int thermal_client_handle; |
| 99 | pthread_mutex_t mutex_spkr_prot; |
| 100 | pthread_t spkr_calibration_thread; |
| 101 | pthread_mutex_t spkr_prot_thermalsync_mutex; |
| 102 | pthread_cond_t spkr_prot_thermalsync; |
| 103 | int cancel_spkr_calib; |
| 104 | pthread_cond_t spkr_calib_cancel; |
| 105 | pthread_mutex_t spkr_calib_cancelack_mutex; |
| 106 | pthread_cond_t spkr_calibcancel_ack; |
| 107 | pthread_t speaker_prot_threadid; |
| 108 | void *thermal_handle; |
| 109 | void *adev_handle; |
| 110 | int spkr_prot_t0; |
| 111 | struct pcm *pcm_rx; |
| 112 | struct pcm *pcm_tx; |
| 113 | int (*client_register_callback) |
| 114 | (char *client_name, int (*callback)(int, void *, void *), void *data); |
| 115 | void (*thermal_client_unregister_callback)(int handle); |
| 116 | int (*thermal_client_request)(char *client_name, int req_data); |
| 117 | bool spkr_prot_enable; |
| 118 | bool spkr_in_use; |
| 119 | struct timespec spkr_last_time_used; |
| 120 | }; |
| 121 | |
| 122 | static struct pcm_config pcm_config_skr_prot = { |
| 123 | .channels = 2, |
| 124 | .rate = 48000, |
| 125 | .period_size = 256, |
| 126 | .period_count = 4, |
| 127 | .format = PCM_FORMAT_S16_LE, |
| 128 | .start_threshold = 0, |
| 129 | .stop_threshold = INT_MAX, |
| 130 | .avail_min = 0, |
| 131 | }; |
| 132 | |
| 133 | static struct speaker_prot_session handle; |
| 134 | |
| 135 | static void spkr_prot_set_spkrstatus(bool enable) |
| 136 | { |
| 137 | struct timespec ts; |
| 138 | if (enable) |
| 139 | handle.spkr_in_use = true; |
| 140 | else { |
| 141 | handle.spkr_in_use = false; |
| 142 | clock_gettime(CLOCK_MONOTONIC, &handle.spkr_last_time_used); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | static void spkr_prot_calib_cancel(void *adev) |
| 147 | { |
| 148 | pthread_t threadid; |
| 149 | struct audio_usecase *uc_info; |
| 150 | int count = 0; |
| 151 | threadid = pthread_self(); |
| 152 | ALOGV("%s: Entry", __func__); |
| 153 | if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) { |
| 154 | ALOGE("%s: Invalid params", __func__); |
| 155 | return; |
| 156 | } |
| 157 | uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX); |
| 158 | if (uc_info) { |
| 159 | pthread_mutex_lock(&handle.mutex_spkr_prot); |
| 160 | pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); |
| 161 | handle.cancel_spkr_calib = 1; |
| 162 | pthread_cond_signal(&handle.spkr_calib_cancel); |
| 163 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 164 | pthread_cond_wait(&handle.spkr_calibcancel_ack, |
| 165 | &handle.spkr_calib_cancelack_mutex); |
| 166 | pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); |
| 167 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 168 | } |
| 169 | ALOGV("%s: Exit", __func__); |
| 170 | } |
| 171 | |
| 172 | static bool is_speaker_in_use(unsigned long *sec) |
| 173 | { |
| 174 | struct timespec temp; |
| 175 | if (!sec) { |
| 176 | ALOGE("%s: Invalid params", __func__); |
| 177 | return true; |
| 178 | } |
| 179 | if (handle.spkr_in_use) { |
| 180 | *sec = 0; |
| 181 | return true; |
| 182 | } else { |
| 183 | clock_gettime(CLOCK_MONOTONIC, &temp); |
| 184 | *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec; |
| 185 | return false; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | |
| 190 | static int spkr_calibrate(int t0) |
| 191 | { |
| 192 | struct audio_device *adev = handle.adev_handle; |
| 193 | struct msm_spk_prot_cfg protCfg; |
| 194 | struct msm_spk_prot_status status; |
| 195 | bool cleanup = false, disable_rx = false, disable_tx = false; |
| 196 | int acdb_fd = -1; |
| 197 | struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL; |
| 198 | int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1; |
| 199 | struct timespec ts; |
| 200 | |
| 201 | if (!adev) { |
| 202 | ALOGE("%s: Invalid params", __func__); |
| 203 | return -EINVAL; |
| 204 | } |
| 205 | if (!list_empty(&adev->usecase_list)) { |
| 206 | ALOGD("%s: Usecase present retry speaker protection", __func__); |
| 207 | return -EAGAIN; |
| 208 | } |
| 209 | acdb_fd = open("/dev/msm_acdb",O_RDWR | O_NONBLOCK); |
| 210 | if (acdb_fd < 0) { |
| 211 | ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__); |
| 212 | return -ENODEV; |
| 213 | } else { |
| 214 | protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS; |
| 215 | protCfg.t0 = t0; |
| 216 | if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) { |
| 217 | ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT", |
| 218 | __func__); |
| 219 | status.status = -ENODEV; |
| 220 | goto exit; |
| 221 | } |
| 222 | } |
| 223 | uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); |
| 224 | uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX; |
| 225 | uc_info_rx->type = PCM_PLAYBACK; |
| 226 | uc_info_rx->in_snd_device = SND_DEVICE_NONE; |
| 227 | uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED; |
| 228 | pthread_mutex_lock(&adev->lock); |
| 229 | disable_rx = true; |
| 230 | enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED, true); |
| 231 | enable_audio_route(adev, uc_info_rx, true); |
| 232 | pthread_mutex_unlock(&adev->lock); |
| 233 | |
| 234 | pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK); |
| 235 | ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id); |
| 236 | if (pcm_dev_rx_id < 0) { |
| 237 | ALOGE("%s: Invalid pcm device for usecase (%d)", |
| 238 | __func__, uc_info_rx->id); |
| 239 | status.status = -ENODEV; |
| 240 | goto exit; |
| 241 | } |
| 242 | handle.pcm_rx = handle.pcm_tx = NULL; |
| 243 | handle.pcm_rx = pcm_open(SOUND_CARD, pcm_dev_rx_id, |
| 244 | PCM_OUT, &pcm_config_skr_prot); |
| 245 | if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) { |
| 246 | ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx)); |
| 247 | status.status = -EIO; |
| 248 | goto exit; |
| 249 | } |
| 250 | uc_info_tx = (struct audio_usecase *) |
| 251 | calloc(1, sizeof(struct audio_usecase)); |
| 252 | uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; |
| 253 | uc_info_tx->type = PCM_CAPTURE; |
| 254 | uc_info_tx->in_snd_device = SND_DEVICE_NONE; |
| 255 | uc_info_tx->out_snd_device = SND_DEVICE_NONE; |
| 256 | |
| 257 | pthread_mutex_lock(&adev->lock); |
| 258 | disable_tx = true; |
| 259 | enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true); |
| 260 | enable_audio_route(adev, uc_info_tx, true); |
| 261 | pthread_mutex_unlock(&adev->lock); |
| 262 | |
| 263 | pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); |
| 264 | if (pcm_dev_tx_id < 0) { |
| 265 | ALOGE("%s: Invalid pcm device for usecase (%d)", |
| 266 | __func__, uc_info_tx->id); |
| 267 | status.status = -ENODEV; |
| 268 | goto exit; |
| 269 | } |
| 270 | handle.pcm_tx = pcm_open(SOUND_CARD, pcm_dev_tx_id, |
| 271 | PCM_IN, &pcm_config_skr_prot); |
| 272 | if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { |
| 273 | ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); |
| 274 | status.status = -EIO; |
| 275 | goto exit; |
| 276 | } |
| 277 | if (pcm_start(handle.pcm_rx) < 0) { |
| 278 | ALOGE("%s: pcm start for RX failed", __func__); |
| 279 | status.status = -EINVAL; |
| 280 | goto exit; |
| 281 | } |
| 282 | if (pcm_start(handle.pcm_tx) < 0) { |
| 283 | ALOGE("%s: pcm start for TX failed", __func__); |
| 284 | status.status = -EINVAL; |
| 285 | goto exit; |
| 286 | } |
| 287 | cleanup = true; |
| 288 | clock_gettime(CLOCK_REALTIME, &ts); |
| 289 | ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000); |
| 290 | ts.tv_nsec = 0; |
| 291 | (void)pthread_cond_timedwait(&handle.spkr_calib_cancel, |
| 292 | &handle.mutex_spkr_prot, &ts); |
| 293 | ALOGD("%s: Speaker calibration done", __func__); |
| 294 | cleanup = true; |
| 295 | pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); |
| 296 | if (handle.cancel_spkr_calib) { |
| 297 | status.status = -EAGAIN; |
| 298 | goto exit; |
| 299 | } |
| 300 | if (acdb_fd > 0) { |
| 301 | status.status = -EINVAL; |
| 302 | while (!ioctl(acdb_fd, AUDIO_GET_SPEAKER_PROT,&status)) { |
| 303 | /*sleep for 200 ms to check for status check*/ |
| 304 | if (!status.status) { |
| 305 | ALOGD("%s: spkr_prot_thread calib Success R0 %d", |
| 306 | __func__, status.r0); |
| 307 | FILE *fp; |
| 308 | fp = fopen(CALIB_FILE,"wb"); |
| 309 | if (!fp) { |
| 310 | ALOGE("%s: spkr_prot_thread File open failed %s", |
| 311 | __func__, strerror(errno)); |
| 312 | status.status = -ENODEV; |
| 313 | } else { |
| 314 | fwrite(&status.r0, sizeof(status.r0),1,fp); |
| 315 | fwrite(&protCfg.t0, sizeof(protCfg.t0),1,fp); |
| 316 | fclose(fp); |
| 317 | } |
| 318 | break; |
| 319 | } else if (status.status == -EAGAIN) { |
| 320 | ALOGD("%s: spkr_prot_thread try again", __func__); |
| 321 | usleep(WAIT_FOR_GET_CALIB_STATUS); |
| 322 | } else { |
| 323 | ALOGE("%s: spkr_prot_thread get failed status %d", |
| 324 | __func__, status.status); |
| 325 | break; |
| 326 | } |
| 327 | } |
| 328 | exit: |
| 329 | if (handle.pcm_rx) |
| 330 | pcm_close(handle.pcm_rx); |
| 331 | handle.pcm_rx = NULL; |
| 332 | if (handle.pcm_tx) |
| 333 | pcm_close(handle.pcm_tx); |
| 334 | handle.pcm_tx = NULL; |
| 335 | pthread_mutex_lock(&adev->lock); |
| 336 | if (disable_rx) { |
| 337 | disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED, true); |
| 338 | disable_audio_route(adev, uc_info_rx, true); |
| 339 | } |
| 340 | if (disable_tx) { |
| 341 | disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true); |
| 342 | disable_audio_route(adev, uc_info_tx, true); |
| 343 | } |
| 344 | pthread_mutex_unlock(&adev->lock); |
| 345 | |
| 346 | if (!status.status) { |
| 347 | protCfg.mode = MSM_SPKR_PROT_CALIBRATED; |
| 348 | protCfg.r0 = status.r0; |
| 349 | if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) |
| 350 | ALOGE("%s: spkr_prot_thread disable calib mode", __func__); |
| 351 | else |
| 352 | handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; |
| 353 | } else { |
| 354 | protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; |
| 355 | handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; |
| 356 | if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) |
| 357 | ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__); |
| 358 | } |
| 359 | if (acdb_fd > 0) |
| 360 | close(acdb_fd); |
| 361 | if (uc_info_rx) free(uc_info_rx); |
| 362 | if (uc_info_tx) free(uc_info_tx); |
| 363 | if (cleanup) { |
| 364 | if (handle.cancel_spkr_calib) |
| 365 | pthread_cond_signal(&handle.spkr_calibcancel_ack); |
| 366 | handle.cancel_spkr_calib = 0; |
| 367 | pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); |
| 368 | } |
| 369 | } |
| 370 | return status.status; |
| 371 | } |
| 372 | |
| 373 | static void* spkr_calibration_thread(void *context) |
| 374 | { |
| 375 | unsigned long sec = 0; |
| 376 | int t0; |
| 377 | bool goahead = false; |
| 378 | struct msm_spk_prot_cfg protCfg; |
| 379 | FILE *fp; |
| 380 | int acdb_fd; |
| 381 | struct audio_device *adev = handle.adev_handle; |
| 382 | |
| 383 | handle.speaker_prot_threadid = pthread_self(); |
| 384 | ALOGD("spkr_prot_thread enable prot Entry"); |
| 385 | acdb_fd = open("/dev/msm_acdb",O_RDWR | O_NONBLOCK); |
| 386 | if (acdb_fd > 0) { |
| 387 | /*Set processing mode with t0/r0*/ |
| 388 | protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; |
| 389 | if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) { |
| 390 | ALOGE("%s: spkr_prot_thread enable prot failed", __func__); |
| 391 | handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; |
| 392 | close(acdb_fd); |
| 393 | } else |
| 394 | handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; |
| 395 | } else { |
| 396 | handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; |
| 397 | ALOGE("%s: Failed to open acdb node", __func__); |
| 398 | } |
| 399 | if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) { |
| 400 | ALOGD("%s: Speaker protection disabled", __func__); |
| 401 | pthread_exit(0); |
| 402 | return NULL; |
| 403 | } |
| 404 | |
| 405 | fp = fopen(CALIB_FILE,"rb"); |
| 406 | if (fp) { |
| 407 | fread(&protCfg.r0,sizeof(protCfg.r0),1,fp); |
| 408 | ALOGD("%s: spkr_prot_thread r0 value %d", __func__, protCfg.r0); |
| 409 | fread(&protCfg.t0, sizeof(protCfg.t0), 1, fp); |
| 410 | ALOGD("%s: spkr_prot_thread t0 value %d", __func__, protCfg.t0); |
| 411 | fclose(fp); |
| 412 | /*Valid tempature range: -30C to 80C(in q6 format) |
| 413 | Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/ |
| 414 | if (protCfg.t0 > MIN_SPKR_TEMP_Q6 && |
| 415 | protCfg.t0 < MAX_SPKR_TEMP_Q6 && |
| 416 | protCfg.r0 >= MIN_RESISTANCE_SPKR_Q24 |
| 417 | && protCfg.r0 < MAX_RESISTANCE_SPKR_Q24) { |
| 418 | ALOGD("%s: Spkr calibrated", __func__); |
| 419 | protCfg.mode = MSM_SPKR_PROT_CALIBRATED; |
| 420 | if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) { |
| 421 | ALOGE("%s: enable prot failed", __func__); |
| 422 | handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; |
| 423 | } else |
| 424 | handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; |
| 425 | close(acdb_fd); |
| 426 | pthread_exit(0); |
| 427 | return NULL; |
| 428 | } |
| 429 | close(acdb_fd); |
| 430 | } |
| 431 | |
| 432 | while (1) { |
| 433 | ALOGV("%s: start calibration", __func__); |
| 434 | if (!handle.thermal_client_request("spkr",1)) { |
| 435 | ALOGD("%s: wait for callback from thermal daemon", __func__); |
| 436 | pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); |
| 437 | pthread_cond_wait(&handle.spkr_prot_thermalsync, |
| 438 | &handle.spkr_prot_thermalsync_mutex); |
| 439 | /*Convert temp into q6 format*/ |
| 440 | t0 = (handle.spkr_prot_t0 * (1 << 6)); |
| 441 | pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); |
| 442 | if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) { |
| 443 | ALOGE("%s: Calibration temparature error %d", __func__, |
| 444 | handle.spkr_prot_t0); |
| 445 | continue; |
| 446 | } |
| 447 | ALOGD("%s: Request t0 success value %d", __func__, |
| 448 | handle.spkr_prot_t0); |
| 449 | } else { |
| 450 | ALOGE("%s: Request t0 failed", __func__); |
| 451 | /*Assume safe value for temparature*/ |
| 452 | t0 = SAFE_SPKR_TEMP_Q6; |
| 453 | } |
| 454 | goahead = false; |
| 455 | pthread_mutex_lock(&handle.mutex_spkr_prot); |
| 456 | if (is_speaker_in_use(&sec)) { |
| 457 | ALOGD("%s: Speaker in use retry calibration", __func__); |
| 458 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 459 | continue; |
| 460 | } else { |
| 461 | ALOGD("%s: speaker idle %ld", __func__, sec); |
| 462 | if (sec < MIN_SPKR_IDLE_SEC) { |
| 463 | ALOGD("%s: speaker idle is less retry", __func__); |
| 464 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 465 | continue; |
| 466 | } |
| 467 | goahead = true; |
| 468 | } |
| 469 | if (!list_empty(&adev->usecase_list)) |
| 470 | goahead = false; |
| 471 | if (goahead) { |
| 472 | int status; |
| 473 | status = spkr_calibrate(t0); |
| 474 | if (status == -EAGAIN) { |
| 475 | ALOGE("%s: failed to calibrate try again %s", |
| 476 | __func__, strerror(status)); |
| 477 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 478 | continue; |
| 479 | } else { |
| 480 | ALOGE("%s: calibrate status %s", __func__, strerror(status)); |
| 481 | } |
| 482 | ALOGD("%s: spkr_prot_thread end calibration", __func__); |
| 483 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 484 | break; |
| 485 | } |
| 486 | } |
| 487 | if (handle.thermal_client_handle) |
| 488 | handle.thermal_client_unregister_callback(handle.thermal_client_handle); |
| 489 | handle.thermal_client_handle = 0; |
| 490 | if (handle.thermal_handle) |
| 491 | dlclose(handle.thermal_handle); |
| 492 | handle.thermal_handle = NULL; |
| 493 | pthread_exit(0); |
| 494 | return NULL; |
| 495 | } |
| 496 | |
| 497 | static int thermal_client_callback(int temp, void *user_data, void *reserved) |
| 498 | { |
| 499 | pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); |
| 500 | ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp); |
| 501 | if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED) |
| 502 | handle.spkr_prot_t0 = temp; |
| 503 | pthread_cond_signal(&handle.spkr_prot_thermalsync); |
| 504 | pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); |
| 505 | return 0; |
| 506 | } |
| 507 | |
| 508 | void audio_extn_spkr_prot_init(void *adev) |
| 509 | { |
| 510 | char value[PROPERTY_VALUE_MAX]; |
| 511 | ALOGD("%s: Initialize speaker protection module", __func__); |
| 512 | memset(&handle, 0, sizeof(handle)); |
| 513 | if (!adev) { |
| 514 | ALOGE("%s: Invalid params", __func__); |
| 515 | return; |
| 516 | } |
| 517 | property_get("persist.speaker.prot.enable", value, ""); |
| 518 | handle.spkr_prot_enable = false; |
| 519 | if (!strncmp("true", value, 4)) |
| 520 | handle.spkr_prot_enable = true; |
| 521 | if (!handle.spkr_prot_enable) { |
| 522 | ALOGD("%s: Speaker protection disabled", __func__); |
| 523 | return; |
| 524 | } |
| 525 | handle.adev_handle = adev; |
| 526 | handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; |
| 527 | handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; |
| 528 | handle.spkr_prot_t0 = -1; |
| 529 | pthread_cond_init(&handle.spkr_prot_thermalsync, NULL); |
| 530 | pthread_cond_init(&handle.spkr_calib_cancel, NULL); |
| 531 | pthread_cond_init(&handle.spkr_calibcancel_ack, NULL); |
| 532 | pthread_mutex_init(&handle.mutex_spkr_prot, NULL); |
| 533 | pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL); |
| 534 | pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL); |
| 535 | handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so", |
| 536 | RTLD_NOW); |
| 537 | if (!handle.thermal_handle) { |
| 538 | ALOGE("%s: DLOPEN for thermal client failed", __func__); |
| 539 | } else { |
| 540 | /*Query callback function symbol*/ |
| 541 | handle.client_register_callback = |
| 542 | (int (*)(char *, int (*)(int, void *, void *),void *)) |
| 543 | dlsym(handle.thermal_handle, "thermal_client_register_callback"); |
| 544 | handle.thermal_client_unregister_callback = |
| 545 | (void (*)(int) ) |
| 546 | dlsym(handle.thermal_handle, "thermal_client_unregister_callback"); |
| 547 | if (!handle.client_register_callback || |
| 548 | !handle.thermal_client_unregister_callback) { |
| 549 | ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__); |
| 550 | } else { |
| 551 | /*Register callback function*/ |
| 552 | handle.thermal_client_handle = |
| 553 | handle.client_register_callback("spkr", thermal_client_callback, NULL); |
| 554 | if (!handle.thermal_client_handle) { |
| 555 | ALOGE("%s: client_register_callback failed", __func__); |
| 556 | } else { |
| 557 | ALOGD("%s: spkr_prot client_register_callback success", __func__); |
| 558 | handle.thermal_client_request = (int (*)(char *, int)) |
| 559 | dlsym(handle.thermal_handle, "thermal_client_request"); |
| 560 | } |
| 561 | } |
| 562 | } |
| 563 | if (handle.thermal_client_request) { |
| 564 | ALOGD("%s: Create calibration thread", __func__); |
| 565 | (void)pthread_create(&handle.spkr_calibration_thread, |
| 566 | (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle); |
| 567 | } else { |
| 568 | ALOGE("%s: thermal_client_request failed", __func__); |
| 569 | if (handle.thermal_client_handle) |
| 570 | handle.thermal_client_unregister_callback(handle.thermal_client_handle); |
| 571 | if (handle.thermal_handle) |
| 572 | dlclose(handle.thermal_handle); |
| 573 | handle.thermal_handle = NULL; |
| 574 | handle.spkr_prot_enable = false; |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) |
| 579 | { |
| 580 | struct audio_usecase uc_info_tx; |
| 581 | struct audio_device *adev = handle.adev_handle; |
| 582 | int32_t pcm_dev_tx_id = -1, ret = 0; |
| 583 | |
| 584 | ALOGV("%s: Entry", __func__); |
| 585 | /* cancel speaker calibration */ |
| 586 | if (!adev) { |
| 587 | ALOGE("%s: Invalid params", __func__); |
| 588 | return -EINVAL; |
| 589 | } |
| 590 | spkr_prot_calib_cancel(adev); |
| 591 | spkr_prot_set_spkrstatus(true); |
| 592 | if (platform_send_audio_calibration(adev->platform, |
| 593 | SND_DEVICE_OUT_SPEAKER_PROTECTED) < 0) { |
| 594 | adev->snd_dev_ref_cnt[snd_device]--; |
| 595 | return -EINVAL; |
| 596 | } |
| 597 | ALOGV("%s: snd_device(%d: %s)", __func__, snd_device, |
| 598 | platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_PROTECTED)); |
| 599 | audio_route_apply_path(adev->audio_route, |
| 600 | platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_PROTECTED)); |
| 601 | |
| 602 | pthread_mutex_lock(&handle.mutex_spkr_prot); |
| 603 | if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) { |
| 604 | memset(&uc_info_tx, 0 , sizeof(uc_info_tx)); |
| 605 | uc_info_tx.id = USECASE_AUDIO_SPKR_CALIB_TX; |
| 606 | uc_info_tx.type = PCM_CAPTURE; |
| 607 | uc_info_tx.in_snd_device = SND_DEVICE_NONE; |
| 608 | uc_info_tx.out_snd_device = SND_DEVICE_NONE; |
| 609 | handle.pcm_tx = NULL; |
| 610 | |
| 611 | enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true); |
| 612 | enable_audio_route(adev, &uc_info_tx, true); |
| 613 | |
| 614 | pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx.id, PCM_CAPTURE); |
| 615 | if (pcm_dev_tx_id < 0) { |
| 616 | ALOGE("%s: Invalid pcm device for usecase (%d)", |
| 617 | __func__, uc_info_tx.id); |
| 618 | ret = -ENODEV; |
| 619 | goto exit; |
| 620 | } |
| 621 | handle.pcm_tx = pcm_open(SOUND_CARD, pcm_dev_tx_id, |
| 622 | PCM_IN, &pcm_config_skr_prot); |
| 623 | if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { |
| 624 | ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); |
| 625 | ret = -EIO; |
| 626 | goto exit; |
| 627 | } |
| 628 | if (pcm_start(handle.pcm_tx) < 0) { |
| 629 | ALOGE("%s: pcm start for TX failed", __func__); |
| 630 | ret = -EINVAL; |
| 631 | } |
| 632 | } |
| 633 | exit: |
| 634 | if (ret) { |
| 635 | if (handle.pcm_tx) |
| 636 | pcm_close(handle.pcm_tx); |
| 637 | handle.pcm_tx = NULL; |
| 638 | disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true); |
| 639 | disable_audio_route(adev, &uc_info_tx, true); |
| 640 | } else |
| 641 | handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS; |
| 642 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 643 | ALOGV("%s: Exit", __func__); |
| 644 | return ret; |
| 645 | } |
| 646 | |
| 647 | void audio_extn_spkr_prot_stop_processing() |
| 648 | { |
| 649 | struct audio_usecase uc_info_tx; |
| 650 | struct audio_device *adev = handle.adev_handle; |
| 651 | ALOGV("%s: Entry", __func__); |
| 652 | spkr_prot_set_spkrstatus(false); |
| 653 | pthread_mutex_lock(&handle.mutex_spkr_prot); |
| 654 | if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) { |
| 655 | memset(&uc_info_tx, 0 , sizeof(uc_info_tx)); |
| 656 | uc_info_tx.id = USECASE_AUDIO_SPKR_CALIB_TX; |
| 657 | uc_info_tx.type = PCM_CAPTURE; |
| 658 | uc_info_tx.in_snd_device = SND_DEVICE_NONE; |
| 659 | uc_info_tx.out_snd_device = SND_DEVICE_NONE; |
| 660 | if (handle.pcm_tx) |
| 661 | pcm_close(handle.pcm_tx); |
| 662 | handle.pcm_tx = NULL; |
| 663 | disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true); |
| 664 | disable_audio_route(adev, &uc_info_tx, true); |
| 665 | } |
| 666 | handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; |
| 667 | pthread_mutex_unlock(&handle.mutex_spkr_prot); |
| 668 | audio_route_reset_path(adev->audio_route, |
| 669 | platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_PROTECTED)); |
| 670 | ALOGV("%s: Exit", __func__); |
| 671 | } |
| 672 | |
| 673 | bool audio_extn_spkr_prot_is_enabled() |
| 674 | { |
| 675 | return handle.spkr_prot_enable; |
| 676 | } |
| 677 | #endif /*SPKR_PROT_ENABLED*/ |