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
spkr_prot_set_spkrstatus(bool enable)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
audio_extn_spkr_prot_calib_cancel(void * adev)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
is_speaker_in_use(unsigned long * sec)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
get_spkr_prot_cal(int cal_fd,struct audio_cal_info_msm_spk_prot_status * status)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
set_spkr_prot_cal(int cal_fd,struct audio_cal_info_spk_prot_cfg * protCfg)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
vi_feed_get_channels(struct audio_device * adev)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
spkr_calibrate(int t0)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
spkr_calibration_thread()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
thermal_client_callback(int temp)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
audio_extn_spkr_prot_init(void * adev)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
audio_extn_spkr_prot_deinit(void * adev __unused)783 void audio_extn_spkr_prot_deinit(void *adev __unused)
784 {
785 ALOGV("%s: Entry", __func__);
786 }
787
audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)788 int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
789 {
790 if (!handle.spkr_prot_enable)
791 return snd_device;
792
793 switch(snd_device) {
794 case SND_DEVICE_OUT_SPEAKER:
795 return SND_DEVICE_OUT_SPEAKER_PROTECTED;
796 case SND_DEVICE_OUT_VOICE_SPEAKER:
797 return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
798 default:
799 return snd_device;
800 }
801 }
802
audio_extn_spkr_prot_start_processing(snd_device_t snd_device)803 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
804 {
805 struct audio_usecase *uc_info_tx;
806 struct audio_device *adev = handle.adev_handle;
807 int32_t pcm_dev_tx_id = -1, ret = 0;
808 int app_type = 0;
809
810 ALOGV("%s: Entry", __func__);
811 if (!adev) {
812 ALOGE("%s: Invalid params", __func__);
813 return -EINVAL;
814 }
815 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
816 spkr_prot_set_spkrstatus(true);
817 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
818 if (!uc_info_tx) {
819 return -ENOMEM;
820 }
821 ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
822 platform_get_snd_device_name(snd_device));
823 audio_route_apply_and_update_path(adev->audio_route,
824 platform_get_snd_device_name(snd_device));
825
826 pthread_mutex_lock(&handle.mutex_spkr_prot);
827 if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
828 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
829 uc_info_tx->type = PCM_CAPTURE;
830 uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
831 uc_info_tx->out_snd_device = SND_DEVICE_NONE;
832 handle.pcm_tx = NULL;
833 list_add_tail(&adev->usecase_list, &uc_info_tx->list);
834 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
835 enable_audio_route(adev, uc_info_tx);
836
837 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
838 if (pcm_dev_tx_id < 0) {
839 ALOGE("%s: Invalid pcm device for usecase (%d)",
840 __func__, uc_info_tx->id);
841 ret = -ENODEV;
842 goto exit;
843 }
844 handle.pcm_tx = pcm_open(adev->snd_card,
845 pcm_dev_tx_id,
846 PCM_IN, &pcm_config_skr_prot);
847 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
848 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
849 ret = -EIO;
850 goto exit;
851 }
852 if (pcm_start(handle.pcm_tx) < 0) {
853 ALOGE("%s: pcm start for TX failed", __func__);
854 ret = -EINVAL;
855 }
856 }
857
858 exit:
859 /* Clear VI feedback cal and replace with handset MIC */
860 if (platform_supports_app_type_cfg()) {
861 ALOGD("%s: Platform supports APP type configuration, using V2\n", __func__);
862 if (uc_info_tx != NULL) {
863 ALOGD("%s: UC Info TX is not NULL, updating and sending calibration\n", __func__);
864 uc_info_tx->in_snd_device = SND_DEVICE_IN_HANDSET_MIC;
865 uc_info_tx->out_snd_device = SND_DEVICE_NONE;
866 platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
867 platform_send_audio_calibration_v2(adev->platform, uc_info_tx,
868 app_type, 8000);
869 }
870 } else {
871 ALOGW("%s: Platform does not support APP type configuration, using V1\n", __func__);
872 platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
873 }
874 if (ret) {
875 if (handle.pcm_tx)
876 pcm_close(handle.pcm_tx);
877 handle.pcm_tx = NULL;
878 list_remove(&uc_info_tx->list);
879 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
880 disable_audio_route(adev, uc_info_tx);
881 free(uc_info_tx);
882 } else
883 handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
884 pthread_mutex_unlock(&handle.mutex_spkr_prot);
885 ALOGV("%s: Exit", __func__);
886 return ret;
887 }
888
audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)889 void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
890 {
891 struct audio_usecase *uc_info_tx;
892 struct audio_device *adev = handle.adev_handle;
893
894 ALOGV("%s: Entry", __func__);
895 snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
896 spkr_prot_set_spkrstatus(false);
897 pthread_mutex_lock(&handle.mutex_spkr_prot);
898 if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
899 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
900 if (handle.pcm_tx)
901 pcm_close(handle.pcm_tx);
902 handle.pcm_tx = NULL;
903 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
904 if (uc_info_tx) {
905 list_remove(&uc_info_tx->list);
906 disable_audio_route(adev, uc_info_tx);
907 free(uc_info_tx);
908 }
909 }
910 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
911 pthread_mutex_unlock(&handle.mutex_spkr_prot);
912 if (adev)
913 audio_route_reset_and_update_path(adev->audio_route,
914 platform_get_snd_device_name(snd_device));
915 ALOGV("%s: Exit", __func__);
916 }
917
audio_extn_spkr_prot_is_enabled()918 bool audio_extn_spkr_prot_is_enabled()
919 {
920 return handle.spkr_prot_enable;
921 }
922 #endif /*SPKR_PROT_ENABLED*/
923