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*/
80enum speaker_protection_mode {
81    SPKR_PROTECTION_DISABLED = -1,
82    SPKR_PROTECTION_MODE_PROCESSING = 0,
83    SPKR_PROTECTION_MODE_CALIBRATE = 1,
84};
85
86struct 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
113static 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
124static struct speaker_prot_session handle;
125static int vi_feed_no_channels;
126
127static 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
138void 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
163static 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
181static 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;
217done:
218    return ret;
219}
220
221static 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    }
269done:
270    return ret;
271}
272
273static 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;
291error:
292     return -EINVAL;
293}
294
295// must be called with adev->lock acquired
296static 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
466exit:
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
536static 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
691static 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
702void 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
783int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
784{
785    if (!handle.spkr_prot_enable)
786        return snd_device;
787
788    switch(snd_device) {
789    case SND_DEVICE_OUT_SPEAKER:
790        return SND_DEVICE_OUT_SPEAKER_PROTECTED;
791    case SND_DEVICE_OUT_VOICE_SPEAKER:
792        return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
793    default:
794        return snd_device;
795    }
796}
797
798int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
799{
800    struct audio_usecase *uc_info_tx;
801    struct audio_device *adev = handle.adev_handle;
802    int32_t pcm_dev_tx_id = -1, ret = 0;
803    int app_type = 0;
804
805    ALOGV("%s: Entry", __func__);
806    if (!adev) {
807       ALOGE("%s: Invalid params", __func__);
808       return -EINVAL;
809    }
810    snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
811    spkr_prot_set_spkrstatus(true);
812    uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
813    if (!uc_info_tx) {
814        return -ENOMEM;
815    }
816    ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
817           platform_get_snd_device_name(snd_device));
818    audio_route_apply_and_update_path(adev->audio_route,
819           platform_get_snd_device_name(snd_device));
820
821    pthread_mutex_lock(&handle.mutex_spkr_prot);
822    if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
823        uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
824        uc_info_tx->type = PCM_CAPTURE;
825        uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
826        uc_info_tx->out_snd_device = SND_DEVICE_NONE;
827        handle.pcm_tx = NULL;
828        list_add_tail(&adev->usecase_list, &uc_info_tx->list);
829        enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
830        enable_audio_route(adev, uc_info_tx);
831
832        pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
833        if (pcm_dev_tx_id < 0) {
834            ALOGE("%s: Invalid pcm device for usecase (%d)",
835                  __func__, uc_info_tx->id);
836            ret = -ENODEV;
837            goto exit;
838        }
839        handle.pcm_tx = pcm_open(adev->snd_card,
840                                 pcm_dev_tx_id,
841                                 PCM_IN, &pcm_config_skr_prot);
842        if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
843            ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
844            ret = -EIO;
845            goto exit;
846        }
847        if (pcm_start(handle.pcm_tx) < 0) {
848            ALOGE("%s: pcm start for TX failed", __func__);
849            ret = -EINVAL;
850        }
851    }
852
853exit:
854    /* Clear VI feedback cal and replace with handset MIC  */
855    if (platform_supports_app_type_cfg()) {
856        ALOGD("%s: Platform supports APP type configuration, using V2\n", __func__);
857        if (uc_info_tx != NULL) {
858            ALOGD("%s: UC Info TX is not NULL, updating and sending calibration\n", __func__);
859            uc_info_tx->in_snd_device = SND_DEVICE_IN_HANDSET_MIC;
860            uc_info_tx->out_snd_device = SND_DEVICE_NONE;
861            platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
862            platform_send_audio_calibration_v2(adev->platform, uc_info_tx,
863                                               app_type, 8000);
864        }
865    } else {
866        ALOGW("%s: Platform does not support APP type configuration, using V1\n", __func__);
867        platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
868    }
869    if (ret) {
870        if (handle.pcm_tx)
871            pcm_close(handle.pcm_tx);
872        handle.pcm_tx = NULL;
873        list_remove(&uc_info_tx->list);
874        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
875        disable_audio_route(adev, uc_info_tx);
876        free(uc_info_tx);
877    } else
878        handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
879    pthread_mutex_unlock(&handle.mutex_spkr_prot);
880    ALOGV("%s: Exit", __func__);
881    return ret;
882}
883
884void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
885{
886    struct audio_usecase *uc_info_tx;
887    struct audio_device *adev = handle.adev_handle;
888
889    ALOGV("%s: Entry", __func__);
890    snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
891    spkr_prot_set_spkrstatus(false);
892    pthread_mutex_lock(&handle.mutex_spkr_prot);
893    if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
894        uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
895        if (handle.pcm_tx)
896            pcm_close(handle.pcm_tx);
897        handle.pcm_tx = NULL;
898        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
899        if (uc_info_tx) {
900            list_remove(&uc_info_tx->list);
901            disable_audio_route(adev, uc_info_tx);
902            free(uc_info_tx);
903        }
904    }
905    handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
906    pthread_mutex_unlock(&handle.mutex_spkr_prot);
907    if (adev)
908        audio_route_reset_and_update_path(adev->audio_route,
909                                      platform_get_snd_device_name(snd_device));
910    ALOGV("%s: Exit", __func__);
911}
912
913bool audio_extn_spkr_prot_is_enabled()
914{
915    return handle.spkr_prot_enable;
916}
917#endif /*SPKR_PROT_ENABLED*/
918