1 /*
2 * Copyright (c) 2013 - 2014, 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_calibration.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 #define VI_FEED_CHANNEL "VI_FEED_TX Channels"
55
56 /*Set safe temp value to 40C*/
57 #define SAFE_SPKR_TEMP 40
58 #define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
59
60 /*Range of resistance values 2ohms to 40 ohms*/
61 #define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24))
62 #define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
63
64 /*Path where the calibration file will be stored*/
65 #define CALIB_FILE "/data/misc/audio/audio.cal"
66
67 /*Time between retries for calibartion or intial wait time
68 after boot up*/
69 #define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000)
70
71 #define MIN_SPKR_IDLE_SEC (60 * 30)
72
73 /*Once calibration is started sleep for 1 sec to allow
74 the calibration to kick off*/
75 #define SLEEP_AFTER_CALIB_START (3000)
76
77 /*If calibration is in progress wait for 200 msec before querying
78 for status again*/
79 #define WAIT_FOR_GET_CALIB_STATUS (200 * 1000)
80
81 /*Speaker states*/
82 #define SPKR_NOT_CALIBRATED -1
83 #define SPKR_CALIBRATED 1
84
85 /*Speaker processing state*/
86 #define SPKR_PROCESSING_IN_PROGRESS 1
87 #define SPKR_PROCESSING_IN_IDLE 0
88
89 /*Modes of Speaker Protection*/
90 enum speaker_protection_mode {
91 SPKR_PROTECTION_DISABLED = -1,
92 SPKR_PROTECTION_MODE_PROCESSING = 0,
93 SPKR_PROTECTION_MODE_CALIBRATE = 1,
94 };
95
96 struct speaker_prot_session {
97 int spkr_prot_mode;
98 int spkr_processing_state;
99 int thermal_client_handle;
100 pthread_mutex_t mutex_spkr_prot;
101 pthread_t spkr_calibration_thread;
102 pthread_mutex_t spkr_prot_thermalsync_mutex;
103 pthread_cond_t spkr_prot_thermalsync;
104 int cancel_spkr_calib;
105 pthread_cond_t spkr_calib_cancel;
106 pthread_mutex_t spkr_calib_cancelack_mutex;
107 pthread_cond_t spkr_calibcancel_ack;
108 pthread_t speaker_prot_threadid;
109 void *thermal_handle;
110 void *adev_handle;
111 int spkr_prot_t0;
112 struct pcm *pcm_rx;
113 struct pcm *pcm_tx;
114 int (*client_register_callback)
115 (char *client_name, int (*callback)(int), void *data);
116 void (*thermal_client_unregister_callback)(int handle);
117 int (*thermal_client_request)(char *client_name, int req_data);
118 bool spkr_prot_enable;
119 bool spkr_in_use;
120 struct timespec spkr_last_time_used;
121 };
122
123 static struct pcm_config pcm_config_skr_prot = {
124 .channels = 4,
125 .rate = 48000,
126 .period_size = 256,
127 .period_count = 4,
128 .format = PCM_FORMAT_S16_LE,
129 .start_threshold = 0,
130 .stop_threshold = INT_MAX,
131 .avail_min = 0,
132 };
133
134 static struct speaker_prot_session handle;
135 static int vi_feed_no_channels;
136
spkr_prot_set_spkrstatus(bool enable)137 static void spkr_prot_set_spkrstatus(bool enable)
138 {
139 struct timespec ts;
140 if (enable)
141 handle.spkr_in_use = true;
142 else {
143 handle.spkr_in_use = false;
144 clock_gettime(CLOCK_MONOTONIC, &handle.spkr_last_time_used);
145 }
146 }
147
audio_extn_spkr_prot_calib_cancel(void * adev)148 void audio_extn_spkr_prot_calib_cancel(void *adev)
149 {
150 pthread_t threadid;
151 struct audio_usecase *uc_info;
152 int count = 0;
153 threadid = pthread_self();
154 ALOGV("%s: Entry", __func__);
155 if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
156 ALOGE("%s: Invalid params", __func__);
157 return;
158 }
159 uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX);
160 if (uc_info) {
161 pthread_mutex_lock(&handle.mutex_spkr_prot);
162 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
163 handle.cancel_spkr_calib = 1;
164 pthread_cond_signal(&handle.spkr_calib_cancel);
165 pthread_mutex_unlock(&handle.mutex_spkr_prot);
166 pthread_cond_wait(&handle.spkr_calibcancel_ack,
167 &handle.spkr_calib_cancelack_mutex);
168 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
169 }
170 ALOGV("%s: Exit", __func__);
171 }
172
is_speaker_in_use(unsigned long * sec)173 static bool is_speaker_in_use(unsigned long *sec)
174 {
175 struct timespec temp;
176 if (!sec) {
177 ALOGE("%s: Invalid params", __func__);
178 return true;
179 }
180 if (handle.spkr_in_use) {
181 *sec = 0;
182 return true;
183 } else {
184 clock_gettime(CLOCK_MONOTONIC, &temp);
185 *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec;
186 return false;
187 }
188 }
189
190
get_spkr_prot_cal(int cal_fd,struct audio_cal_info_msm_spk_prot_status * status)191 static int get_spkr_prot_cal(int cal_fd,
192 struct audio_cal_info_msm_spk_prot_status *status)
193 {
194 int ret = 0;
195 struct audio_cal_fb_spk_prot_status cal_data;
196
197 if (cal_fd < 0) {
198 ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
199 ret = -EINVAL;
200 goto done;
201 }
202
203 if (status == NULL) {
204 ALOGE("%s: Error: status NULL", __func__);
205 ret = -EINVAL;
206 goto done;
207 }
208
209 cal_data.hdr.data_size = sizeof(cal_data);
210 cal_data.hdr.version = VERSION_0_0;
211 cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
212 cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
213 cal_data.cal_type.cal_hdr.version = VERSION_0_0;
214 cal_data.cal_type.cal_hdr.buffer_number = 0;
215 cal_data.cal_type.cal_data.mem_handle = -1;
216
217 if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) {
218 ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!",
219 __func__);
220 ret = -ENODEV;
221 goto done;
222 }
223
224 status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1];
225 status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2];
226 status->status = cal_data.cal_type.cal_info.status;
227 done:
228 return ret;
229 }
230
set_spkr_prot_cal(int cal_fd,struct audio_cal_info_spk_prot_cfg * protCfg)231 static int set_spkr_prot_cal(int cal_fd,
232 struct audio_cal_info_spk_prot_cfg *protCfg)
233 {
234 int ret = 0;
235 struct audio_cal_fb_spk_prot_cfg cal_data;
236 char value[PROPERTY_VALUE_MAX];
237
238 if (cal_fd < 0) {
239 ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
240 ret = -EINVAL;
241 goto done;
242 }
243
244 if (protCfg == NULL) {
245 ALOGE("%s: Error: status NULL", __func__);
246 ret = -EINVAL;
247 goto done;
248 }
249
250 memset(&cal_data, 0, sizeof(cal_data));
251 cal_data.hdr.data_size = sizeof(cal_data);
252 cal_data.hdr.version = VERSION_0_0;
253 cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
254 cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
255 cal_data.cal_type.cal_hdr.version = VERSION_0_0;
256 cal_data.cal_type.cal_hdr.buffer_number = 0;
257 cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1];
258 cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2];
259 cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1];
260 cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2];
261 cal_data.cal_type.cal_info.mode = protCfg->mode;
262 property_get("persist.spkr.cal.duration", value, "0");
263 if (atoi(value) > 0) {
264 ALOGD("%s: quick calibration enabled", __func__);
265 cal_data.cal_type.cal_info.quick_calib_flag = 1;
266 } else {
267 ALOGD("%s: quick calibration disabled", __func__);
268 cal_data.cal_type.cal_info.quick_calib_flag = 0;
269 }
270
271 cal_data.cal_type.cal_data.mem_handle = -1;
272
273 if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) {
274 ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!",
275 __func__);
276 ret = -ENODEV;
277 goto done;
278 }
279 done:
280 return ret;
281 }
282
vi_feed_get_channels(struct audio_device * adev)283 static int vi_feed_get_channels(struct audio_device *adev)
284 {
285 struct mixer_ctl *ctl;
286 const char *mixer_ctl_name = VI_FEED_CHANNEL;
287 int value;
288
289 ALOGV("%s: entry", __func__);
290 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
291 if (!ctl) {
292 ALOGE("%s: Could not get ctl for mixer cmd - %s",
293 __func__, mixer_ctl_name);
294 goto error;
295 }
296 value = mixer_ctl_get_value(ctl, 0);
297 if (value < 0)
298 goto error;
299 else
300 return value+1;
301 error:
302 return -EINVAL;
303 }
304
spkr_calibrate(int t0)305 static int spkr_calibrate(int t0)
306 {
307 struct audio_device *adev = handle.adev_handle;
308 struct audio_cal_info_spk_prot_cfg protCfg;
309 struct audio_cal_info_msm_spk_prot_status status;
310 bool cleanup = false, disable_rx = false, disable_tx = false;
311 int acdb_fd = -1;
312 struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
313 int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
314 struct timespec ts;
315 bool acquire_device = false;
316
317 if (!adev) {
318 ALOGE("%s: Invalid params", __func__);
319 return -EINVAL;
320 }
321 if (!list_empty(&adev->usecase_list)) {
322 ALOGD("%s: Usecase present retry speaker protection", __func__);
323 return -EAGAIN;
324 }
325 acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
326 if (acdb_fd < 0) {
327 ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
328 return -ENODEV;
329 } else {
330 protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
331 /* HAL for speaker protection gets only one Temperature */
332 protCfg.t0[SP_V2_SPKR_1] = t0;
333 protCfg.t0[SP_V2_SPKR_2] = t0;
334 if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
335 ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT",
336 __func__);
337 status.status = -ENODEV;
338 goto exit;
339 }
340 }
341 uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
342 if (!uc_info_rx) {
343 return -ENOMEM;
344 }
345 uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX;
346 uc_info_rx->type = PCM_PLAYBACK;
347 uc_info_rx->in_snd_device = SND_DEVICE_NONE;
348 uc_info_rx->stream.out = adev->primary_output;
349 uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED;
350 disable_rx = true;
351 list_add_tail(&adev->usecase_list, &uc_info_rx->list);
352 enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
353 enable_audio_route(adev, uc_info_rx);
354
355 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
356 ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id);
357 if (pcm_dev_rx_id < 0) {
358 ALOGE("%s: Invalid pcm device for usecase (%d)",
359 __func__, uc_info_rx->id);
360 status.status = -ENODEV;
361 goto exit;
362 }
363 handle.pcm_rx = handle.pcm_tx = NULL;
364 handle.pcm_rx = pcm_open(adev->snd_card,
365 pcm_dev_rx_id,
366 PCM_OUT, &pcm_config_skr_prot);
367 if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
368 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx));
369 status.status = -EIO;
370 goto exit;
371 }
372 uc_info_tx = (struct audio_usecase *)
373 calloc(1, sizeof(struct audio_usecase));
374 if (!uc_info_tx) {
375 status.status = -ENOMEM;
376 goto exit;
377 }
378 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
379 uc_info_tx->type = PCM_CAPTURE;
380 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
381 uc_info_tx->out_snd_device = SND_DEVICE_NONE;
382
383 disable_tx = true;
384 list_add_tail(&adev->usecase_list, &uc_info_tx->list);
385 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
386 enable_audio_route(adev, uc_info_tx);
387
388 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
389 if (pcm_dev_tx_id < 0) {
390 ALOGE("%s: Invalid pcm device for usecase (%d)",
391 __func__, uc_info_tx->id);
392 status.status = -ENODEV;
393 goto exit;
394 }
395 handle.pcm_tx = pcm_open(adev->snd_card,
396 pcm_dev_tx_id,
397 PCM_IN, &pcm_config_skr_prot);
398 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
399 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
400 status.status = -EIO;
401 goto exit;
402 }
403 if (pcm_start(handle.pcm_rx) < 0) {
404 ALOGE("%s: pcm start for RX failed", __func__);
405 status.status = -EINVAL;
406 goto exit;
407 }
408 if (pcm_start(handle.pcm_tx) < 0) {
409 ALOGE("%s: pcm start for TX failed", __func__);
410 status.status = -EINVAL;
411 goto exit;
412 }
413 cleanup = true;
414 clock_gettime(CLOCK_REALTIME, &ts);
415 ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
416 ts.tv_nsec = 0;
417 pthread_mutex_lock(&handle.mutex_spkr_prot);
418 pthread_mutex_unlock(&adev->lock);
419 acquire_device = true;
420 (void)pthread_cond_timedwait(&handle.spkr_calib_cancel,
421 &handle.mutex_spkr_prot, &ts);
422 ALOGD("%s: Speaker calibration done", __func__);
423 cleanup = true;
424 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
425 if (handle.cancel_spkr_calib) {
426 status.status = -EAGAIN;
427 goto exit;
428 }
429 if (acdb_fd > 0) {
430 status.status = -EINVAL;
431 while (!get_spkr_prot_cal(acdb_fd, &status)) {
432 /*sleep for 200 ms to check for status check*/
433 if (!status.status) {
434 ALOGD("%s: spkr_prot_thread calib Success R0 %d %d",
435 __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]);
436 FILE *fp;
437
438 vi_feed_no_channels = vi_feed_get_channels(adev);
439 ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
440 if (vi_feed_no_channels < 0) {
441 ALOGE("%s: no of channels negative !!", __func__);
442 /* limit the number of channels to 2*/
443 vi_feed_no_channels = 2;
444 }
445
446 fp = fopen(CALIB_FILE,"wb");
447 if (!fp) {
448 ALOGE("%s: spkr_prot_thread File open failed %s",
449 __func__, strerror(errno));
450 status.status = -ENODEV;
451 } else {
452 int i;
453 /* HAL for speaker protection is always calibrating for stereo usecase*/
454 for (i = 0; i < vi_feed_no_channels; i++) {
455 fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp);
456 fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
457 }
458 fclose(fp);
459 }
460 break;
461 } else if (status.status == -EAGAIN) {
462 ALOGD("%s: spkr_prot_thread try again", __func__);
463 usleep(WAIT_FOR_GET_CALIB_STATUS);
464 } else {
465 ALOGE("%s: spkr_prot_thread get failed status %d",
466 __func__, status.status);
467 break;
468 }
469 }
470 exit:
471 if (handle.pcm_rx)
472 pcm_close(handle.pcm_rx);
473 handle.pcm_rx = NULL;
474 if (handle.pcm_tx)
475 pcm_close(handle.pcm_tx);
476 handle.pcm_tx = NULL;
477 /* Clear TX calibration to handset mic */
478 platform_send_audio_calibration(adev->platform,
479 SND_DEVICE_IN_HANDSET_MIC,
480 platform_get_default_app_type(adev->platform), 8000);
481 if (!status.status) {
482 protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
483 protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
484 protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
485 if (set_spkr_prot_cal(acdb_fd, &protCfg))
486 ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
487 else
488 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
489 } else {
490 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
491 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
492 if (set_spkr_prot_cal(acdb_fd, &protCfg))
493 ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
494 }
495 if (acdb_fd > 0)
496 close(acdb_fd);
497
498 if (!handle.cancel_spkr_calib && cleanup) {
499 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
500 pthread_cond_wait(&handle.spkr_calib_cancel,
501 &handle.mutex_spkr_prot);
502 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
503 }
504 if (disable_rx) {
505 list_remove(&uc_info_rx->list);
506 disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
507 disable_audio_route(adev, uc_info_rx);
508 }
509 if (disable_tx) {
510 list_remove(&uc_info_tx->list);
511 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
512 disable_audio_route(adev, uc_info_tx);
513 }
514 if (uc_info_rx) free(uc_info_rx);
515 if (uc_info_tx) free(uc_info_tx);
516 if (cleanup) {
517 if (handle.cancel_spkr_calib)
518 pthread_cond_signal(&handle.spkr_calibcancel_ack);
519 handle.cancel_spkr_calib = 0;
520 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
521 pthread_mutex_unlock(&handle.mutex_spkr_prot);
522 }
523 }
524 if (acquire_device)
525 pthread_mutex_lock(&adev->lock);
526 return status.status;
527 }
528
spkr_calibration_thread()529 static void* spkr_calibration_thread()
530 {
531 unsigned long sec = 0;
532 int t0;
533 bool goahead = false;
534 struct audio_cal_info_spk_prot_cfg protCfg;
535 FILE *fp;
536 int acdb_fd;
537 struct audio_device *adev = handle.adev_handle;
538 unsigned long min_idle_time = MIN_SPKR_IDLE_SEC;
539 char value[PROPERTY_VALUE_MAX];
540
541 /* If the value of this persist.spkr.cal.duration is 0
542 * then it means it will take 30min to calibrate
543 * and if the value is greater than zero then it would take
544 * that much amount of time to calibrate.
545 */
546 property_get("persist.spkr.cal.duration", value, "0");
547 if (atoi(value) > 0)
548 min_idle_time = atoi(value);
549 handle.speaker_prot_threadid = pthread_self();
550 ALOGD("spkr_prot_thread enable prot Entry");
551 acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
552 if (acdb_fd > 0) {
553 /*Set processing mode with t0/r0*/
554 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
555 if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
556 ALOGE("%s: spkr_prot_thread enable prot failed", __func__);
557 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
558 close(acdb_fd);
559 } else
560 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
561 } else {
562 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
563 ALOGE("%s: Failed to open acdb node", __func__);
564 }
565 if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) {
566 ALOGD("%s: Speaker protection disabled", __func__);
567 pthread_exit(0);
568 return NULL;
569 }
570
571 fp = fopen(CALIB_FILE,"rb");
572 if (fp) {
573 int i;
574 bool spkr_calibrated = true;
575 /* HAL for speaker protection is always calibrating for stereo usecase*/
576 vi_feed_no_channels = vi_feed_get_channels(adev);
577 ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
578 if (vi_feed_no_channels < 0) {
579 ALOGE("%s: no of channels negative !!", __func__);
580 /* limit the number of channels to 2*/
581 vi_feed_no_channels = 2;
582 }
583 for (i = 0; i < vi_feed_no_channels; i++) {
584 fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
585 fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
586 }
587 ALOGD("%s: spkr_prot_thread r0 value %d %d",
588 __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
589 ALOGD("%s: spkr_prot_thread t0 value %d %d",
590 __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
591 fclose(fp);
592 /*Valid tempature range: -30C to 80C(in q6 format)
593 Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
594 for (i = 0; i < vi_feed_no_channels; i++) {
595 if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
596 && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
597 && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
598 spkr_calibrated = false;
599 break;
600 }
601 }
602 if (spkr_calibrated) {
603 ALOGD("%s: Spkr calibrated", __func__);
604 protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
605 if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
606 ALOGE("%s: enable prot failed", __func__);
607 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
608 } else
609 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
610 close(acdb_fd);
611 pthread_exit(0);
612 return NULL;
613 }
614 close(acdb_fd);
615 }
616
617 while (1) {
618 ALOGV("%s: start calibration", __func__);
619 if (!handle.thermal_client_request("spkr",1)) {
620 ALOGD("%s: wait for callback from thermal daemon", __func__);
621 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
622 pthread_cond_wait(&handle.spkr_prot_thermalsync,
623 &handle.spkr_prot_thermalsync_mutex);
624 /*Convert temp into q6 format*/
625 t0 = (handle.spkr_prot_t0 * (1 << 6));
626 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
627 if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) {
628 ALOGE("%s: Calibration temparature error %d", __func__,
629 handle.spkr_prot_t0);
630 continue;
631 }
632 ALOGD("%s: Request t0 success value %d", __func__,
633 handle.spkr_prot_t0);
634 } else {
635 ALOGE("%s: Request t0 failed", __func__);
636 /*Assume safe value for temparature*/
637 t0 = SAFE_SPKR_TEMP_Q6;
638 }
639 goahead = false;
640 pthread_mutex_lock(&adev->lock);
641 if (is_speaker_in_use(&sec)) {
642 ALOGD("%s: Speaker in use retry calibration", __func__);
643 pthread_mutex_unlock(&adev->lock);
644 continue;
645 } else {
646 ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time);
647 if (sec < min_idle_time) {
648 ALOGD("%s: speaker idle is less retry", __func__);
649 pthread_mutex_unlock(&adev->lock);
650 continue;
651 }
652 goahead = true;
653 }
654 if (!list_empty(&adev->usecase_list)) {
655 ALOGD("%s: Usecase active re-try calibration", __func__);
656 goahead = false;
657 pthread_mutex_unlock(&adev->lock);
658 }
659 if (goahead) {
660 int status;
661 status = spkr_calibrate(t0);
662 pthread_mutex_unlock(&adev->lock);
663 if (status == -EAGAIN) {
664 ALOGE("%s: failed to calibrate try again %s",
665 __func__, strerror(status));
666 continue;
667 } else {
668 ALOGE("%s: calibrate status %s", __func__, strerror(status));
669 }
670 ALOGD("%s: spkr_prot_thread end calibration", __func__);
671 break;
672 }
673 }
674 if (handle.thermal_client_handle)
675 handle.thermal_client_unregister_callback(handle.thermal_client_handle);
676 handle.thermal_client_handle = 0;
677 if (handle.thermal_handle)
678 dlclose(handle.thermal_handle);
679 handle.thermal_handle = NULL;
680 pthread_exit(0);
681 return NULL;
682 }
683
thermal_client_callback(int temp)684 static int thermal_client_callback(int temp)
685 {
686 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
687 ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp);
688 if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED)
689 handle.spkr_prot_t0 = temp;
690 pthread_cond_signal(&handle.spkr_prot_thermalsync);
691 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
692 return 0;
693 }
694
audio_extn_spkr_prot_init(void * adev)695 void audio_extn_spkr_prot_init(void *adev)
696 {
697 char value[PROPERTY_VALUE_MAX];
698 ALOGD("%s: Initialize speaker protection module", __func__);
699 memset(&handle, 0, sizeof(handle));
700 if (!adev) {
701 ALOGE("%s: Invalid params", __func__);
702 return;
703 }
704 property_get("persist.speaker.prot.enable", value, "");
705 handle.spkr_prot_enable = false;
706 if (!strncmp("true", value, 4))
707 handle.spkr_prot_enable = true;
708 if (!handle.spkr_prot_enable) {
709 ALOGD("%s: Speaker protection disabled", __func__);
710 return;
711 }
712 handle.adev_handle = adev;
713 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
714 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
715 handle.spkr_prot_t0 = -1;
716 pthread_cond_init(&handle.spkr_prot_thermalsync, NULL);
717 pthread_cond_init(&handle.spkr_calib_cancel, NULL);
718 pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
719 pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
720 pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
721 pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL);
722 handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so",
723 RTLD_NOW);
724 if (!handle.thermal_handle) {
725 ALOGE("%s: DLOPEN for thermal client failed", __func__);
726 } else {
727 /*Query callback function symbol*/
728 handle.client_register_callback =
729 (int (*)(char *, int (*)(int),void *))
730 dlsym(handle.thermal_handle, "thermal_client_register_callback");
731 handle.thermal_client_unregister_callback =
732 (void (*)(int) )
733 dlsym(handle.thermal_handle, "thermal_client_unregister_callback");
734 if (!handle.client_register_callback ||
735 !handle.thermal_client_unregister_callback) {
736 ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__);
737 } else {
738 /*Register callback function*/
739 handle.thermal_client_handle =
740 handle.client_register_callback("spkr", thermal_client_callback, NULL);
741 if (!handle.thermal_client_handle) {
742 ALOGE("%s: client_register_callback failed", __func__);
743 } else {
744 ALOGD("%s: spkr_prot client_register_callback success", __func__);
745 handle.thermal_client_request = (int (*)(char *, int))
746 dlsym(handle.thermal_handle, "thermal_client_request");
747 }
748 }
749 }
750 if (handle.thermal_client_request) {
751 ALOGD("%s: Create calibration thread", __func__);
752 (void)pthread_create(&handle.spkr_calibration_thread,
753 (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
754 } else {
755 ALOGE("%s: thermal_client_request failed", __func__);
756 if (handle.thermal_client_handle &&
757 handle.thermal_client_unregister_callback)
758 handle.thermal_client_unregister_callback(handle.thermal_client_handle);
759 if (handle.thermal_handle)
760 dlclose(handle.thermal_handle);
761 handle.thermal_handle = NULL;
762 handle.spkr_prot_enable = false;
763 }
764
765 if (handle.spkr_prot_enable) {
766 char platform[PROPERTY_VALUE_MAX];
767 property_get("ro.board.platform", platform, "");
768 if (!strncmp("apq8084", platform, sizeof("apq8084"))) {
769 platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER,
770 "speaker-protected");
771 }
772 }
773 }
774
audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)775 int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
776 {
777 int acdb_id;
778
779 switch(snd_device) {
780 case SND_DEVICE_OUT_SPEAKER:
781 acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
782 break;
783 case SND_DEVICE_OUT_VOICE_SPEAKER:
784 acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
785 break;
786 default:
787 acdb_id = -EINVAL;
788 break;
789 }
790 return acdb_id;
791 }
792
audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)793 int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
794 {
795 if (!handle.spkr_prot_enable)
796 return snd_device;
797
798 switch(snd_device) {
799 case SND_DEVICE_OUT_SPEAKER:
800 return SND_DEVICE_OUT_SPEAKER_PROTECTED;
801 case SND_DEVICE_OUT_VOICE_SPEAKER:
802 return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
803 default:
804 return snd_device;
805 }
806 }
807
audio_extn_spkr_prot_start_processing(snd_device_t snd_device)808 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
809 {
810 struct audio_usecase *uc_info_tx;
811 struct audio_device *adev = handle.adev_handle;
812 int32_t pcm_dev_tx_id = -1, ret = 0;
813
814 ALOGV("%s: Entry", __func__);
815 /* cancel speaker calibration */
816 if (!adev) {
817 ALOGE("%s: Invalid params", __func__);
818 return -EINVAL;
819 }
820 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
821 spkr_prot_set_spkrstatus(true);
822 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
823 if (!uc_info_tx) {
824 return -ENOMEM;
825 }
826 ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
827 platform_get_snd_device_name(snd_device));
828 audio_route_apply_and_update_path(adev->audio_route,
829 platform_get_snd_device_name(snd_device));
830
831 pthread_mutex_lock(&handle.mutex_spkr_prot);
832 if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
833 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
834 uc_info_tx->type = PCM_CAPTURE;
835 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
836 uc_info_tx->out_snd_device = SND_DEVICE_NONE;
837 handle.pcm_tx = NULL;
838 list_add_tail(&adev->usecase_list, &uc_info_tx->list);
839 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
840 enable_audio_route(adev, uc_info_tx);
841
842 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
843 if (pcm_dev_tx_id < 0) {
844 ALOGE("%s: Invalid pcm device for usecase (%d)",
845 __func__, uc_info_tx->id);
846 ret = -ENODEV;
847 goto exit;
848 }
849 handle.pcm_tx = pcm_open(adev->snd_card,
850 pcm_dev_tx_id,
851 PCM_IN, &pcm_config_skr_prot);
852 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
853 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
854 ret = -EIO;
855 goto exit;
856 }
857 if (pcm_start(handle.pcm_tx) < 0) {
858 ALOGE("%s: pcm start for TX failed", __func__);
859 ret = -EINVAL;
860 }
861 }
862
863 exit:
864 /* Clear VI feedback cal and replace with handset MIC */
865 platform_send_audio_calibration(adev->platform,
866 SND_DEVICE_IN_HANDSET_MIC,
867 platform_get_default_app_type(adev->platform), 8000);
868 if (ret) {
869 if (handle.pcm_tx)
870 pcm_close(handle.pcm_tx);
871 handle.pcm_tx = NULL;
872 list_remove(&uc_info_tx->list);
873 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
874 disable_audio_route(adev, uc_info_tx);
875 free(uc_info_tx);
876 } else
877 handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
878 pthread_mutex_unlock(&handle.mutex_spkr_prot);
879 ALOGV("%s: Exit", __func__);
880 return ret;
881 }
882
audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)883 void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
884 {
885 struct audio_usecase *uc_info_tx;
886 struct audio_device *adev = handle.adev_handle;
887
888 ALOGV("%s: Entry", __func__);
889 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
890 spkr_prot_set_spkrstatus(false);
891 pthread_mutex_lock(&handle.mutex_spkr_prot);
892 if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
893 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
894 if (handle.pcm_tx)
895 pcm_close(handle.pcm_tx);
896 handle.pcm_tx = NULL;
897 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
898 if (uc_info_tx) {
899 list_remove(&uc_info_tx->list);
900 disable_audio_route(adev, uc_info_tx);
901 free(uc_info_tx);
902 }
903 }
904 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
905 pthread_mutex_unlock(&handle.mutex_spkr_prot);
906 if (adev)
907 audio_route_reset_and_update_path(adev->audio_route,
908 platform_get_snd_device_name(snd_device));
909 ALOGV("%s: Exit", __func__);
910 }
911
audio_extn_spkr_prot_is_enabled()912 bool audio_extn_spkr_prot_is_enabled()
913 {
914 return handle.spkr_prot_enable;
915 }
916 #endif /*SPKR_PROT_ENABLED*/
917