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