blob: 0fc790aa02c5b217b7e547d7f600ef864b102e22 [file] [log] [blame]
Gopikrishnaiah Anandanf538cef2013-10-28 14:06:03 -07001/*
2 * Copyright (c) 2013, 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_acdb.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
55/*Set safe temp value to 40C*/
56#define SAFE_SPKR_TEMP 40
57#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
58
59/*Range of resistance values 2ohms to 40 ohms*/
60#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24))
61#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
62
63/*Path where the calibration file will be stored*/
64#define CALIB_FILE "/data/misc/audio/audio.cal"
65
66/*Time between retries for calibartion or intial wait time
67 after boot up*/
68#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000)
69
70#define MIN_SPKR_IDLE_SEC (60 * 30)
71
72/*Once calibration is started sleep for 1 sec to allow
73 the calibration to kick off*/
74#define SLEEP_AFTER_CALIB_START (3000)
75
76/*If calibration is in progress wait for 200 msec before querying
77 for status again*/
78#define WAIT_FOR_GET_CALIB_STATUS (200 * 1000)
79
80/*Speaker states*/
81#define SPKR_NOT_CALIBRATED -1
82#define SPKR_CALIBRATED 1
83
84/*Speaker processing state*/
85#define SPKR_PROCESSING_IN_PROGRESS 1
86#define SPKR_PROCESSING_IN_IDLE 0
87
88/*Modes of Speaker Protection*/
89enum speaker_protection_mode {
90 SPKR_PROTECTION_DISABLED = -1,
91 SPKR_PROTECTION_MODE_PROCESSING = 0,
92 SPKR_PROTECTION_MODE_CALIBRATE = 1,
93};
94
95struct speaker_prot_session {
96 int spkr_prot_mode;
97 int spkr_processing_state;
98 int thermal_client_handle;
99 pthread_mutex_t mutex_spkr_prot;
100 pthread_t spkr_calibration_thread;
101 pthread_mutex_t spkr_prot_thermalsync_mutex;
102 pthread_cond_t spkr_prot_thermalsync;
103 int cancel_spkr_calib;
104 pthread_cond_t spkr_calib_cancel;
105 pthread_mutex_t spkr_calib_cancelack_mutex;
106 pthread_cond_t spkr_calibcancel_ack;
107 pthread_t speaker_prot_threadid;
108 void *thermal_handle;
109 void *adev_handle;
110 int spkr_prot_t0;
111 struct pcm *pcm_rx;
112 struct pcm *pcm_tx;
113 int (*client_register_callback)
114 (char *client_name, int (*callback)(int, void *, void *), void *data);
115 void (*thermal_client_unregister_callback)(int handle);
116 int (*thermal_client_request)(char *client_name, int req_data);
117 bool spkr_prot_enable;
118 bool spkr_in_use;
119 struct timespec spkr_last_time_used;
120};
121
122static struct pcm_config pcm_config_skr_prot = {
123 .channels = 2,
124 .rate = 48000,
125 .period_size = 256,
126 .period_count = 4,
127 .format = PCM_FORMAT_S16_LE,
128 .start_threshold = 0,
129 .stop_threshold = INT_MAX,
130 .avail_min = 0,
131};
132
133static struct speaker_prot_session handle;
134
135static void spkr_prot_set_spkrstatus(bool enable)
136{
137 struct timespec ts;
138 if (enable)
139 handle.spkr_in_use = true;
140 else {
141 handle.spkr_in_use = false;
142 clock_gettime(CLOCK_MONOTONIC, &handle.spkr_last_time_used);
143 }
144}
145
146static void spkr_prot_calib_cancel(void *adev)
147{
148 pthread_t threadid;
149 struct audio_usecase *uc_info;
150 int count = 0;
151 threadid = pthread_self();
152 ALOGV("%s: Entry", __func__);
153 if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
154 ALOGE("%s: Invalid params", __func__);
155 return;
156 }
157 uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX);
158 if (uc_info) {
159 pthread_mutex_lock(&handle.mutex_spkr_prot);
160 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
161 handle.cancel_spkr_calib = 1;
162 pthread_cond_signal(&handle.spkr_calib_cancel);
163 pthread_mutex_unlock(&handle.mutex_spkr_prot);
164 pthread_cond_wait(&handle.spkr_calibcancel_ack,
165 &handle.spkr_calib_cancelack_mutex);
166 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
167 pthread_mutex_unlock(&handle.mutex_spkr_prot);
168 }
169 ALOGV("%s: Exit", __func__);
170}
171
172static bool is_speaker_in_use(unsigned long *sec)
173{
174 struct timespec temp;
175 if (!sec) {
176 ALOGE("%s: Invalid params", __func__);
177 return true;
178 }
179 if (handle.spkr_in_use) {
180 *sec = 0;
181 return true;
182 } else {
183 clock_gettime(CLOCK_MONOTONIC, &temp);
184 *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec;
185 return false;
186 }
187}
188
189
190static int spkr_calibrate(int t0)
191{
192 struct audio_device *adev = handle.adev_handle;
193 struct msm_spk_prot_cfg protCfg;
194 struct msm_spk_prot_status status;
195 bool cleanup = false, disable_rx = false, disable_tx = false;
196 int acdb_fd = -1;
197 struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
198 int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
199 struct timespec ts;
200
201 if (!adev) {
202 ALOGE("%s: Invalid params", __func__);
203 return -EINVAL;
204 }
205 if (!list_empty(&adev->usecase_list)) {
206 ALOGD("%s: Usecase present retry speaker protection", __func__);
207 return -EAGAIN;
208 }
209 acdb_fd = open("/dev/msm_acdb",O_RDWR | O_NONBLOCK);
210 if (acdb_fd < 0) {
211 ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
212 return -ENODEV;
213 } else {
214 protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
215 protCfg.t0 = t0;
216 if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) {
217 ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT",
218 __func__);
219 status.status = -ENODEV;
220 goto exit;
221 }
222 }
223 uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
224 uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX;
225 uc_info_rx->type = PCM_PLAYBACK;
226 uc_info_rx->in_snd_device = SND_DEVICE_NONE;
227 uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED;
228 pthread_mutex_lock(&adev->lock);
229 disable_rx = true;
230 enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED, true);
231 enable_audio_route(adev, uc_info_rx, true);
232 pthread_mutex_unlock(&adev->lock);
233
234 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
235 ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id);
236 if (pcm_dev_rx_id < 0) {
237 ALOGE("%s: Invalid pcm device for usecase (%d)",
238 __func__, uc_info_rx->id);
239 status.status = -ENODEV;
240 goto exit;
241 }
242 handle.pcm_rx = handle.pcm_tx = NULL;
243 handle.pcm_rx = pcm_open(SOUND_CARD, pcm_dev_rx_id,
244 PCM_OUT, &pcm_config_skr_prot);
245 if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
246 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx));
247 status.status = -EIO;
248 goto exit;
249 }
250 uc_info_tx = (struct audio_usecase *)
251 calloc(1, sizeof(struct audio_usecase));
252 uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
253 uc_info_tx->type = PCM_CAPTURE;
254 uc_info_tx->in_snd_device = SND_DEVICE_NONE;
255 uc_info_tx->out_snd_device = SND_DEVICE_NONE;
256
257 pthread_mutex_lock(&adev->lock);
258 disable_tx = true;
259 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true);
260 enable_audio_route(adev, uc_info_tx, true);
261 pthread_mutex_unlock(&adev->lock);
262
263 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
264 if (pcm_dev_tx_id < 0) {
265 ALOGE("%s: Invalid pcm device for usecase (%d)",
266 __func__, uc_info_tx->id);
267 status.status = -ENODEV;
268 goto exit;
269 }
270 handle.pcm_tx = pcm_open(SOUND_CARD, pcm_dev_tx_id,
271 PCM_IN, &pcm_config_skr_prot);
272 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
273 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
274 status.status = -EIO;
275 goto exit;
276 }
277 if (pcm_start(handle.pcm_rx) < 0) {
278 ALOGE("%s: pcm start for RX failed", __func__);
279 status.status = -EINVAL;
280 goto exit;
281 }
282 if (pcm_start(handle.pcm_tx) < 0) {
283 ALOGE("%s: pcm start for TX failed", __func__);
284 status.status = -EINVAL;
285 goto exit;
286 }
287 cleanup = true;
288 clock_gettime(CLOCK_REALTIME, &ts);
289 ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
290 ts.tv_nsec = 0;
291 (void)pthread_cond_timedwait(&handle.spkr_calib_cancel,
292 &handle.mutex_spkr_prot, &ts);
293 ALOGD("%s: Speaker calibration done", __func__);
294 cleanup = true;
295 pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
296 if (handle.cancel_spkr_calib) {
297 status.status = -EAGAIN;
298 goto exit;
299 }
300 if (acdb_fd > 0) {
301 status.status = -EINVAL;
302 while (!ioctl(acdb_fd, AUDIO_GET_SPEAKER_PROT,&status)) {
303 /*sleep for 200 ms to check for status check*/
304 if (!status.status) {
305 ALOGD("%s: spkr_prot_thread calib Success R0 %d",
306 __func__, status.r0);
307 FILE *fp;
308 fp = fopen(CALIB_FILE,"wb");
309 if (!fp) {
310 ALOGE("%s: spkr_prot_thread File open failed %s",
311 __func__, strerror(errno));
312 status.status = -ENODEV;
313 } else {
314 fwrite(&status.r0, sizeof(status.r0),1,fp);
315 fwrite(&protCfg.t0, sizeof(protCfg.t0),1,fp);
316 fclose(fp);
317 }
318 break;
319 } else if (status.status == -EAGAIN) {
320 ALOGD("%s: spkr_prot_thread try again", __func__);
321 usleep(WAIT_FOR_GET_CALIB_STATUS);
322 } else {
323 ALOGE("%s: spkr_prot_thread get failed status %d",
324 __func__, status.status);
325 break;
326 }
327 }
328exit:
329 if (handle.pcm_rx)
330 pcm_close(handle.pcm_rx);
331 handle.pcm_rx = NULL;
332 if (handle.pcm_tx)
333 pcm_close(handle.pcm_tx);
334 handle.pcm_tx = NULL;
335 pthread_mutex_lock(&adev->lock);
336 if (disable_rx) {
337 disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED, true);
338 disable_audio_route(adev, uc_info_rx, true);
339 }
340 if (disable_tx) {
341 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true);
342 disable_audio_route(adev, uc_info_tx, true);
343 }
344 pthread_mutex_unlock(&adev->lock);
345
346 if (!status.status) {
347 protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
348 protCfg.r0 = status.r0;
349 if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg))
350 ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
351 else
352 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
353 } else {
354 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
355 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
356 if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg))
357 ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
358 }
359 if (acdb_fd > 0)
360 close(acdb_fd);
361 if (uc_info_rx) free(uc_info_rx);
362 if (uc_info_tx) free(uc_info_tx);
363 if (cleanup) {
364 if (handle.cancel_spkr_calib)
365 pthread_cond_signal(&handle.spkr_calibcancel_ack);
366 handle.cancel_spkr_calib = 0;
367 pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
368 }
369 }
370 return status.status;
371}
372
373static void* spkr_calibration_thread(void *context)
374{
375 unsigned long sec = 0;
376 int t0;
377 bool goahead = false;
378 struct msm_spk_prot_cfg protCfg;
379 FILE *fp;
380 int acdb_fd;
381 struct audio_device *adev = handle.adev_handle;
382
383 handle.speaker_prot_threadid = pthread_self();
384 ALOGD("spkr_prot_thread enable prot Entry");
385 acdb_fd = open("/dev/msm_acdb",O_RDWR | O_NONBLOCK);
386 if (acdb_fd > 0) {
387 /*Set processing mode with t0/r0*/
388 protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
389 if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) {
390 ALOGE("%s: spkr_prot_thread enable prot failed", __func__);
391 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
392 close(acdb_fd);
393 } else
394 handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
395 } else {
396 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
397 ALOGE("%s: Failed to open acdb node", __func__);
398 }
399 if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) {
400 ALOGD("%s: Speaker protection disabled", __func__);
401 pthread_exit(0);
402 return NULL;
403 }
404
405 fp = fopen(CALIB_FILE,"rb");
406 if (fp) {
407 fread(&protCfg.r0,sizeof(protCfg.r0),1,fp);
408 ALOGD("%s: spkr_prot_thread r0 value %d", __func__, protCfg.r0);
409 fread(&protCfg.t0, sizeof(protCfg.t0), 1, fp);
410 ALOGD("%s: spkr_prot_thread t0 value %d", __func__, protCfg.t0);
411 fclose(fp);
412 /*Valid tempature range: -30C to 80C(in q6 format)
413 Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
414 if (protCfg.t0 > MIN_SPKR_TEMP_Q6 &&
415 protCfg.t0 < MAX_SPKR_TEMP_Q6 &&
416 protCfg.r0 >= MIN_RESISTANCE_SPKR_Q24
417 && protCfg.r0 < MAX_RESISTANCE_SPKR_Q24) {
418 ALOGD("%s: Spkr calibrated", __func__);
419 protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
420 if (ioctl(acdb_fd, AUDIO_SET_SPEAKER_PROT, &protCfg)) {
421 ALOGE("%s: enable prot failed", __func__);
422 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
423 } else
424 handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
425 close(acdb_fd);
426 pthread_exit(0);
427 return NULL;
428 }
429 close(acdb_fd);
430 }
431
432 while (1) {
433 ALOGV("%s: start calibration", __func__);
434 if (!handle.thermal_client_request("spkr",1)) {
435 ALOGD("%s: wait for callback from thermal daemon", __func__);
436 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
437 pthread_cond_wait(&handle.spkr_prot_thermalsync,
438 &handle.spkr_prot_thermalsync_mutex);
439 /*Convert temp into q6 format*/
440 t0 = (handle.spkr_prot_t0 * (1 << 6));
441 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
442 if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) {
443 ALOGE("%s: Calibration temparature error %d", __func__,
444 handle.spkr_prot_t0);
445 continue;
446 }
447 ALOGD("%s: Request t0 success value %d", __func__,
448 handle.spkr_prot_t0);
449 } else {
450 ALOGE("%s: Request t0 failed", __func__);
451 /*Assume safe value for temparature*/
452 t0 = SAFE_SPKR_TEMP_Q6;
453 }
454 goahead = false;
455 pthread_mutex_lock(&handle.mutex_spkr_prot);
456 if (is_speaker_in_use(&sec)) {
457 ALOGD("%s: Speaker in use retry calibration", __func__);
458 pthread_mutex_unlock(&handle.mutex_spkr_prot);
459 continue;
460 } else {
461 ALOGD("%s: speaker idle %ld", __func__, sec);
462 if (sec < MIN_SPKR_IDLE_SEC) {
463 ALOGD("%s: speaker idle is less retry", __func__);
464 pthread_mutex_unlock(&handle.mutex_spkr_prot);
465 continue;
466 }
467 goahead = true;
468 }
469 if (!list_empty(&adev->usecase_list))
470 goahead = false;
471 if (goahead) {
472 int status;
473 status = spkr_calibrate(t0);
474 if (status == -EAGAIN) {
475 ALOGE("%s: failed to calibrate try again %s",
476 __func__, strerror(status));
477 pthread_mutex_unlock(&handle.mutex_spkr_prot);
478 continue;
479 } else {
480 ALOGE("%s: calibrate status %s", __func__, strerror(status));
481 }
482 ALOGD("%s: spkr_prot_thread end calibration", __func__);
483 pthread_mutex_unlock(&handle.mutex_spkr_prot);
484 break;
485 }
486 }
487 if (handle.thermal_client_handle)
488 handle.thermal_client_unregister_callback(handle.thermal_client_handle);
489 handle.thermal_client_handle = 0;
490 if (handle.thermal_handle)
491 dlclose(handle.thermal_handle);
492 handle.thermal_handle = NULL;
493 pthread_exit(0);
494 return NULL;
495}
496
497static int thermal_client_callback(int temp, void *user_data, void *reserved)
498{
499 pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
500 ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp);
501 if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED)
502 handle.spkr_prot_t0 = temp;
503 pthread_cond_signal(&handle.spkr_prot_thermalsync);
504 pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
505 return 0;
506}
507
508void audio_extn_spkr_prot_init(void *adev)
509{
510 char value[PROPERTY_VALUE_MAX];
511 ALOGD("%s: Initialize speaker protection module", __func__);
512 memset(&handle, 0, sizeof(handle));
513 if (!adev) {
514 ALOGE("%s: Invalid params", __func__);
515 return;
516 }
517 property_get("persist.speaker.prot.enable", value, "");
518 handle.spkr_prot_enable = false;
519 if (!strncmp("true", value, 4))
520 handle.spkr_prot_enable = true;
521 if (!handle.spkr_prot_enable) {
522 ALOGD("%s: Speaker protection disabled", __func__);
523 return;
524 }
525 handle.adev_handle = adev;
526 handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
527 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
528 handle.spkr_prot_t0 = -1;
529 pthread_cond_init(&handle.spkr_prot_thermalsync, NULL);
530 pthread_cond_init(&handle.spkr_calib_cancel, NULL);
531 pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
532 pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
533 pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
534 pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL);
535 handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so",
536 RTLD_NOW);
537 if (!handle.thermal_handle) {
538 ALOGE("%s: DLOPEN for thermal client failed", __func__);
539 } else {
540 /*Query callback function symbol*/
541 handle.client_register_callback =
542 (int (*)(char *, int (*)(int, void *, void *),void *))
543 dlsym(handle.thermal_handle, "thermal_client_register_callback");
544 handle.thermal_client_unregister_callback =
545 (void (*)(int) )
546 dlsym(handle.thermal_handle, "thermal_client_unregister_callback");
547 if (!handle.client_register_callback ||
548 !handle.thermal_client_unregister_callback) {
549 ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__);
550 } else {
551 /*Register callback function*/
552 handle.thermal_client_handle =
553 handle.client_register_callback("spkr", thermal_client_callback, NULL);
554 if (!handle.thermal_client_handle) {
555 ALOGE("%s: client_register_callback failed", __func__);
556 } else {
557 ALOGD("%s: spkr_prot client_register_callback success", __func__);
558 handle.thermal_client_request = (int (*)(char *, int))
559 dlsym(handle.thermal_handle, "thermal_client_request");
560 }
561 }
562 }
563 if (handle.thermal_client_request) {
564 ALOGD("%s: Create calibration thread", __func__);
565 (void)pthread_create(&handle.spkr_calibration_thread,
566 (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
567 } else {
568 ALOGE("%s: thermal_client_request failed", __func__);
569 if (handle.thermal_client_handle)
570 handle.thermal_client_unregister_callback(handle.thermal_client_handle);
571 if (handle.thermal_handle)
572 dlclose(handle.thermal_handle);
573 handle.thermal_handle = NULL;
574 handle.spkr_prot_enable = false;
575 }
576}
577
578int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
579{
580 struct audio_usecase uc_info_tx;
581 struct audio_device *adev = handle.adev_handle;
582 int32_t pcm_dev_tx_id = -1, ret = 0;
583
584 ALOGV("%s: Entry", __func__);
585 /* cancel speaker calibration */
586 if (!adev) {
587 ALOGE("%s: Invalid params", __func__);
588 return -EINVAL;
589 }
590 spkr_prot_calib_cancel(adev);
591 spkr_prot_set_spkrstatus(true);
592 if (platform_send_audio_calibration(adev->platform,
593 SND_DEVICE_OUT_SPEAKER_PROTECTED) < 0) {
594 adev->snd_dev_ref_cnt[snd_device]--;
595 return -EINVAL;
596 }
597 ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
598 platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_PROTECTED));
599 audio_route_apply_path(adev->audio_route,
600 platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_PROTECTED));
601
602 pthread_mutex_lock(&handle.mutex_spkr_prot);
603 if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
604 memset(&uc_info_tx, 0 , sizeof(uc_info_tx));
605 uc_info_tx.id = USECASE_AUDIO_SPKR_CALIB_TX;
606 uc_info_tx.type = PCM_CAPTURE;
607 uc_info_tx.in_snd_device = SND_DEVICE_NONE;
608 uc_info_tx.out_snd_device = SND_DEVICE_NONE;
609 handle.pcm_tx = NULL;
610
611 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true);
612 enable_audio_route(adev, &uc_info_tx, true);
613
614 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx.id, PCM_CAPTURE);
615 if (pcm_dev_tx_id < 0) {
616 ALOGE("%s: Invalid pcm device for usecase (%d)",
617 __func__, uc_info_tx.id);
618 ret = -ENODEV;
619 goto exit;
620 }
621 handle.pcm_tx = pcm_open(SOUND_CARD, pcm_dev_tx_id,
622 PCM_IN, &pcm_config_skr_prot);
623 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
624 ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
625 ret = -EIO;
626 goto exit;
627 }
628 if (pcm_start(handle.pcm_tx) < 0) {
629 ALOGE("%s: pcm start for TX failed", __func__);
630 ret = -EINVAL;
631 }
632 }
633exit:
634 if (ret) {
635 if (handle.pcm_tx)
636 pcm_close(handle.pcm_tx);
637 handle.pcm_tx = NULL;
638 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true);
639 disable_audio_route(adev, &uc_info_tx, true);
640 } else
641 handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
642 pthread_mutex_unlock(&handle.mutex_spkr_prot);
643 ALOGV("%s: Exit", __func__);
644 return ret;
645}
646
647void audio_extn_spkr_prot_stop_processing()
648{
649 struct audio_usecase uc_info_tx;
650 struct audio_device *adev = handle.adev_handle;
651 ALOGV("%s: Entry", __func__);
652 spkr_prot_set_spkrstatus(false);
653 pthread_mutex_lock(&handle.mutex_spkr_prot);
654 if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
655 memset(&uc_info_tx, 0 , sizeof(uc_info_tx));
656 uc_info_tx.id = USECASE_AUDIO_SPKR_CALIB_TX;
657 uc_info_tx.type = PCM_CAPTURE;
658 uc_info_tx.in_snd_device = SND_DEVICE_NONE;
659 uc_info_tx.out_snd_device = SND_DEVICE_NONE;
660 if (handle.pcm_tx)
661 pcm_close(handle.pcm_tx);
662 handle.pcm_tx = NULL;
663 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, true);
664 disable_audio_route(adev, &uc_info_tx, true);
665 }
666 handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
667 pthread_mutex_unlock(&handle.mutex_spkr_prot);
668 audio_route_reset_path(adev->audio_route,
669 platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_PROTECTED));
670 ALOGV("%s: Exit", __func__);
671}
672
673bool audio_extn_spkr_prot_is_enabled()
674{
675 return handle.spkr_prot_enable;
676}
677#endif /*SPKR_PROT_ENABLED*/