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