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