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