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*/
90enum speaker_protection_mode {
91    SPKR_PROTECTION_DISABLED = -1,
92    SPKR_PROTECTION_MODE_PROCESSING = 0,
93    SPKR_PROTECTION_MODE_CALIBRATE = 1,
94};
95
96struct 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
123static 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
134static struct speaker_prot_session handle;
135static int vi_feed_no_channels;
136
137static 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
148void 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
173static 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
191static 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;
227done:
228    return ret;
229}
230
231static 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    }
279done:
280    return ret;
281}
282
283static 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;
301error:
302     return -EINVAL;
303}
304
305static 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        }
470exit:
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
529static 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
684static 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
695void 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
775int 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
793int 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
808int 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
863exit:
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
883void 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
912bool audio_extn_spkr_prot_is_enabled()
913{
914    return handle.spkr_prot_enable;
915}
916#endif /*SPKR_PROT_ENABLED*/
917