1 /*
2 * Copyright (C) 2017 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_cirrus_playback"
18 /*#define LOG_NDEBUG 0*/
19
20 #include <errno.h>
21 #include <math.h>
22 #include <log/log.h>
23 #include <fcntl.h>
24 #include "../audio_hw.h"
25 #include "platform.h"
26 #include "platform_api.h"
27 #include <sys/stat.h>
28 #include <linux/types.h>
29 #include <linux/ioctl.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <dlfcn.h>
33 #include <math.h>
34 #include <pthread.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <cutils/properties.h>
38 #include "audio_extn.h"
39
40 struct cirrus_playback_session {
41 void *adev_handle;
42 pthread_mutex_t fb_prot_mutex;
43 pthread_t calibration_thread;
44 #ifdef ENABLE_CIRRUS_DETECTION
45 pthread_t failure_detect_thread;
46 #endif
47 struct pcm *pcm_rx;
48 struct pcm *pcm_tx;
49 volatile int32_t state;
50 };
51
52 enum cirrus_playback_state {
53 INIT = 0,
54 CALIBRATING = 1,
55 IDLE = 2,
56 PLAYBACK = 3
57 };
58
59 struct crus_sp_ioctl_header {
60 uint32_t size;
61 uint32_t module_id;
62 uint32_t param_id;
63 uint32_t data_length;
64 void *data;
65 };
66
67 /* Payload struct for getting calibration result from DSP module */
68 struct cirrus_cal_result_t {
69 int32_t status_l;
70 int32_t checksum_l;
71 int32_t z_l;
72 int32_t status_r;
73 int32_t checksum_r;
74 int32_t z_r;
75 };
76
77 /* Payload struct for setting the RX and TX use cases */
78 struct crus_rx_run_case_ctrl_t {
79 int32_t value;
80 int32_t status_l;
81 int32_t checksum_l;
82 int32_t z_l;
83 int32_t status_r;
84 int32_t checksum_r;
85 int32_t z_r;
86 };
87
88 #define CRUS_SP_FILE "/dev/msm_cirrus_playback"
89 #define CRUS_CAL_FILE "/persist/audio/audio.cal"
90 #define CRUS_TX_CONF_FILE "vendor/firmware/crus_sp_config_%s_tx.bin"
91 #define CRUS_RX_CONF_FILE "vendor/firmware/crus_sp_config_%s_rx.bin"
92 #define CONFIG_FILE_SIZE 128
93
94 #define CRUS_SP_USECASE_MIXER "Cirrus SP Usecase"
95 #define CRUS_SP_LOAD_CONF_MIXER "Cirrus SP Load Config"
96 #define CRUS_SP_FAIL_DET_MIXER "Cirrus SP Failure Detection"
97
98 #define CIRRUS_SP 0x10027053
99
100 #define CRUS_MODULE_ID_TX 0x00000002
101 #define CRUS_MODULE_ID_RX 0x00000001
102
103 #define CRUS_PARAM_RX_SET_USECASE 0x00A1AF02
104 #define CRUS_PARAM_TX_SET_USECASE 0x00A1BF0A
105
106 #define CRUS_PARAM_RX_SET_CALIB 0x00A1AF03
107 #define CRUS_PARAM_TX_SET_CALIB 0x00A1BF03
108
109 #define CRUS_PARAM_RX_SET_EXT_CONFIG 0x00A1AF05
110 #define CRUS_PARAM_TX_SET_EXT_CONFIG 0x00A1BF08
111
112 #define CRUS_PARAM_RX_GET_TEMP 0x00A1AF07
113 #define CRUS_PARAM_TX_GET_TEMP_CAL 0x00A1BF06
114 // variables based on CSPL tuning file, max parameter length is 96 integers (384 bytes)
115 #define CRUS_PARAM_TEMP_MAX_LENGTH 384
116
117 #define CRUS_AFE_PARAM_ID_ENABLE 0x00010203
118
119 #define FAIL_DETECT_INIT_WAIT_US 500000
120 #define FAIL_DETECT_LOOP_WAIT_US 300000
121
122 #define CRUS_DEFAULT_CAL_L 0x2A11
123 #define CRUS_DEFAULT_CAL_R 0x29CB
124
125 #define CRUS_SP_IOCTL_MAGIC 'a'
126
127 #define CRUS_SP_IOCTL_GET _IOWR(CRUS_SP_IOCTL_MAGIC, 219, void *)
128 #define CRUS_SP_IOCTL_SET _IOWR(CRUS_SP_IOCTL_MAGIC, 220, void *)
129 #define CRUS_SP_IOCTL_GET_CALIB _IOWR(CRUS_SP_IOCTL_MAGIC, 221, void *)
130 #define CRUS_SP_IOCTL_SET_CALIB _IOWR(CRUS_SP_IOCTL_MAGIC, 222, void *)
131
132
133
134 static struct pcm_config pcm_config_cirrus_tx = {
135 .channels = 2,
136 .rate = 48000,
137 .period_size = 320,
138 .period_count = 4,
139 .format = PCM_FORMAT_S16_LE,
140 .start_threshold = 0,
141 .stop_threshold = INT_MAX,
142 .avail_min = 0,
143 };
144
145 static struct pcm_config pcm_config_cirrus_rx = {
146 .channels = 8,
147 .rate = 48000,
148 .period_size = 320,
149 .period_count = 4,
150 .format = PCM_FORMAT_S32_LE,
151 .start_threshold = 0,
152 .stop_threshold = INT_MAX,
153 .avail_min = 0,
154 };
155
156 static struct cirrus_playback_session handle;
157
158 #ifdef CIRRUS_FACTORY_CALIBRATION
159 static void *audio_extn_cirrus_calibration_thread();
160 #else
161 static void *audio_extn_cirrus_config_thread();
162 #endif
163
164 #ifdef ENABLE_CIRRUS_DETECTION
165 static void *audio_extn_cirrus_failure_detect_thread();
166 #endif
167
audio_extn_spkr_prot_init(void * adev)168 void audio_extn_spkr_prot_init(void *adev) {
169 ALOGI("%s: Initialize Cirrus Logic Playback module", __func__);
170
171 memset(&handle, 0, sizeof(handle));
172 if (!adev) {
173 ALOGE("%s: Invalid params", __func__);
174 return;
175 }
176
177 handle.adev_handle = adev;
178 handle.state = INIT;
179
180 pthread_mutex_init(&handle.fb_prot_mutex, NULL);
181
182 #ifdef CIRRUS_FACTORY_CALIBRATION
183 (void)pthread_create(&handle.calibration_thread,
184 (const pthread_attr_t *) NULL,
185 audio_extn_cirrus_calibration_thread, &handle);
186 #else
187 (void)pthread_create(&handle.calibration_thread,
188 (const pthread_attr_t *) NULL,
189 audio_extn_cirrus_config_thread, &handle);
190 #endif
191 }
192
audio_extn_spkr_prot_deinit(void * adev __unused)193 void audio_extn_spkr_prot_deinit(void *adev __unused) {
194 ALOGV("%s: Entry", __func__);
195
196 #ifdef ENABLE_CIRRUS_DETECTION
197 pthread_join(handle.failure_detect_thread, NULL);
198 #endif
199 pthread_join(handle.calibration_thread, NULL);
200 pthread_mutex_destroy(&handle.fb_prot_mutex);
201
202 ALOGV("%s: Exit", __func__);
203 }
204
205 #ifdef CIRRUS_FACTORY_CALIBRATION
audio_extn_cirrus_run_calibration()206 static int audio_extn_cirrus_run_calibration() {
207 struct audio_device *adev = handle.adev_handle;
208 struct crus_sp_ioctl_header header;
209 struct cirrus_cal_result_t result;
210 struct mixer_ctl *ctl = NULL;
211 FILE *cal_file = NULL;
212 int ret = 0, dev_file = -1;
213 char *buffer = NULL;
214 uint32_t option = 1;
215
216 ALOGI("%s: Running speaker calibration", __func__);
217
218 dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK);
219 if (dev_file < 0) {
220 ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)",
221 __func__, dev_file);
222 ret = -EINVAL;
223 goto exit;
224 }
225
226 buffer = calloc(1, CRUS_PARAM_TEMP_MAX_LENGTH);
227 if (!buffer) {
228 ALOGE("%s: allocate memory failed", __func__);
229 ret = -ENOMEM;
230 goto exit;
231 }
232
233 cal_file = fopen(CRUS_CAL_FILE, "r");
234 if (cal_file) {
235 ret = fread(&result, sizeof(result), 1, cal_file);
236 if (ret != 1) {
237 ALOGE("%s: Cirrus SP calibration file cannot be read , read size: %lu file error: %d",
238 __func__, (unsigned long)ret * sizeof(result), ferror(cal_file));
239 ret = -EINVAL;
240 fclose(cal_file);
241 goto exit;
242 }
243
244 fclose(cal_file);
245 } else {
246
247 ALOGV("%s: Calibrating...", __func__);
248
249 header.size = sizeof(header);
250 header.module_id = CRUS_MODULE_ID_RX;
251 header.param_id = CRUS_PARAM_RX_SET_CALIB;
252 header.data_length = sizeof(option);
253 header.data = &option;
254
255 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET, &header);
256 if (ret < 0) {
257 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)",
258 __func__, ret);
259 ret = -EINVAL;
260 goto exit;
261 }
262
263 header.size = sizeof(header);
264 header.module_id = CRUS_MODULE_ID_TX;
265 header.param_id = CRUS_PARAM_TX_SET_CALIB;
266 header.data_length = sizeof(option);
267 header.data = &option;
268
269 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET, &header);
270 if (ret < 0) {
271 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)",
272 __func__, ret);
273 ret = -EINVAL;
274 goto exit;
275 }
276
277 sleep(2);
278
279 header.size = sizeof(header);
280 header.module_id = CRUS_MODULE_ID_TX;
281 header.param_id = CRUS_PARAM_TX_GET_TEMP_CAL;
282 header.data_length = sizeof(result);
283 header.data = &result;
284
285 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
286 if (ret < 0) {
287 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)",
288 __func__, ret);
289 ret = -EINVAL;
290 goto exit;
291 }
292
293 if (result.status_l != 1) {
294 ALOGE("%s: Left calibration failure. Please check speakers",
295 __func__);
296 ret = -EINVAL;
297 }
298
299 if (result.status_r != 1) {
300 ALOGE("%s: Right calibration failure. Please check speakers",
301 __func__);
302 ret = -EINVAL;
303 }
304
305 if (ret < 0)
306 goto exit;
307
308 cal_file = fopen(CRUS_CAL_FILE, "wb");
309 if (cal_file == NULL) {
310 ALOGE("%s: Cannot create Cirrus SP calibration file (%s)",
311 __func__, strerror(errno));
312 ret = -EINVAL;
313 goto exit;
314 }
315
316 ret = fwrite(&result, sizeof(result), 1, cal_file);
317
318 if (ret != 1) {
319 ALOGE("%s: Unable to save Cirrus SP calibration data, write size %lu, file error %d",
320 __func__, (unsigned long)ret * sizeof(result), ferror(cal_file));
321 fclose(cal_file);
322 ret = -EINVAL;
323 goto exit;
324 }
325
326 fclose(cal_file);
327
328 ALOGI("%s: Cirrus calibration file successfully written",
329 __func__);
330 }
331
332 header.size = sizeof(header);
333 header.module_id = CRUS_MODULE_ID_TX;
334 header.param_id = 0;
335 header.data_length = sizeof(result);
336 header.data = &result;
337
338 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET_CALIB, &header);
339
340 if (ret < 0) {
341 ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)", __func__, ret);
342 ret = -EINVAL;
343 goto exit;
344 }
345
346 ctl = mixer_get_ctl_by_name(adev->mixer,
347 CRUS_SP_USECASE_MIXER);
348 if (!ctl) {
349 ALOGE("%s: Could not get ctl for mixer cmd - %s",
350 __func__, CRUS_SP_USECASE_MIXER);
351 ret = -EINVAL;
352 goto exit;
353 }
354
355 ret = mixer_ctl_set_value(ctl, 0, 0); // Set RX external firmware config
356 if (ret < 0) {
357 ALOGE("%s: set default usecase failed", __func__);
358 goto exit;
359 }
360
361 sleep(1);
362
363 header.size = sizeof(header);
364 header.module_id = CRUS_MODULE_ID_RX;
365 header.param_id = CRUS_PARAM_RX_GET_TEMP;
366 header.data_length = CRUS_PARAM_TEMP_MAX_LENGTH;
367 header.data = buffer;
368
369 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
370 if (ret < 0) {
371 ALOGE("%s: Cirrus SP temperature IOCTL failure (%d)", __func__, ret);
372 ret = -EINVAL;
373 goto exit;
374 }
375
376 ALOGI("%s: Cirrus SP successfully calibrated", __func__);
377
378 exit:
379 if (dev_file >= 0)
380 close(dev_file);
381 free(buffer);
382 ALOGV("%s: Exit", __func__);
383
384 return ret;
385 }
386
audio_extn_cirrus_load_usecase_configs(void)387 static int audio_extn_cirrus_load_usecase_configs(void) {
388 struct audio_device *adev = handle.adev_handle;
389 struct mixer_ctl *ctl_uc = NULL, *ctl_config = NULL;
390 char *filename = NULL;
391 int ret = 0, default_uc = 0;
392 struct snd_card_split *snd_split_handle = NULL;
393 snd_split_handle = audio_extn_get_snd_card_split();
394
395 ALOGI("%s: Loading usecase tuning configs", __func__);
396
397 ctl_uc = mixer_get_ctl_by_name(adev->mixer, CRUS_SP_USECASE_MIXER);
398 ctl_config = mixer_get_ctl_by_name(adev->mixer,
399 CRUS_SP_LOAD_CONF_MIXER);
400 if (!ctl_uc || !ctl_config) {
401 ALOGE("%s: Could not get ctl for mixer commands", __func__);
402 ret = -EINVAL;
403 goto exit;
404 }
405
406 filename = calloc(1 , CONFIG_FILE_SIZE);
407 if (!filename) {
408 ALOGE("%s: allocate memory failed", __func__);
409 ret = -ENOMEM;
410 goto exit;
411 }
412
413 default_uc = mixer_ctl_get_value(ctl_uc, 0);
414
415 ret = mixer_ctl_set_value(ctl_uc, 0, default_uc);
416 if (ret < 0) {
417 ALOGE("%s set uscase %d failed", __func__, default_uc);
418 goto exit;
419 }
420
421 /* Load TX Tuning Config (if available) */
422 snprintf(filename, CONFIG_FILE_SIZE, CRUS_TX_CONF_FILE, snd_split_handle->form_factor);
423 if (access(filename, R_OK) == 0) {
424 ret = mixer_ctl_set_value(ctl_config, 0, 2);
425 if (ret < 0) {
426 ALOGE("%s set tx config failed", __func__);
427 goto exit;
428 }
429 } else {
430 ALOGE("%s: Tuning file not found (%s)", __func__,
431 filename);
432 ret = -EINVAL;
433 goto exit;
434 }
435 /* Load RX Tuning Config (if available) */
436 snprintf(filename, CONFIG_FILE_SIZE, CRUS_RX_CONF_FILE, snd_split_handle->form_factor);
437 if (access(filename, R_OK) == 0) {
438 ret = mixer_ctl_set_value(ctl_config, 0, 1);
439 if (ret < 0) {
440 ALOGE("%s set rx config failed", __func__);
441 goto exit;
442 }
443 } else {
444 ALOGE("%s: Tuning file not found (%s)", __func__,
445 filename);
446 ret = -EINVAL;
447 goto exit;
448 }
449
450 ALOGI("%s: Cirrus SP loaded available usecase configs", __func__);
451 exit:
452 free(filename);
453 ALOGI("%s: Exit", __func__);
454
455 return ret;
456 }
457
audio_extn_cirrus_calibration_thread()458 static void *audio_extn_cirrus_calibration_thread() {
459 struct audio_device *adev = handle.adev_handle;
460 struct audio_usecase *uc_info_rx = NULL;
461 int ret = 0;
462 int32_t pcm_dev_rx_id, prev_state;
463 uint32_t retries = 5;
464
465 ALOGI("%s: PCM Stream thread", __func__);
466
467 while (!adev->platform && retries) {
468 sleep(1);
469 ALOGI("%s: Waiting...", __func__);
470 retries--;
471 }
472
473 prev_state = handle.state;
474 handle.state = CALIBRATING;
475
476 uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
477 if (!uc_info_rx) {
478 ALOGE("%s: rx usecase can not be found", __func__);
479 goto exit;
480 }
481 pthread_mutex_lock(&adev->lock);
482
483 uc_info_rx->id = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
484 uc_info_rx->type = PCM_PLAYBACK;
485 uc_info_rx->in_snd_device = SND_DEVICE_NONE;
486 uc_info_rx->stream.out = adev->primary_output;
487 uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER;
488 list_add_tail(&adev->usecase_list, &uc_info_rx->list);
489
490 enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER);
491 enable_audio_route(adev, uc_info_rx);
492 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
493
494 if (pcm_dev_rx_id < 0) {
495 ALOGE("%s: Invalid pcm device for usecase (%d)",
496 __func__, uc_info_rx->id);
497 pthread_mutex_unlock(&adev->lock);
498 goto exit;
499 }
500
501 handle.pcm_rx = pcm_open(adev->snd_card, pcm_dev_rx_id,
502 PCM_OUT, &pcm_config_cirrus_rx);
503
504 if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
505 ALOGE("%s: PCM device not ready: %s", __func__,
506 pcm_get_error(handle.pcm_rx));
507 pthread_mutex_unlock(&adev->lock);
508 goto close_stream;
509 }
510
511 if (pcm_start(handle.pcm_rx) < 0) {
512 ALOGE("%s: pcm start for RX failed; error = %s", __func__,
513 pcm_get_error(handle.pcm_rx));
514 pthread_mutex_unlock(&adev->lock);
515 goto close_stream;
516 }
517 pthread_mutex_unlock(&adev->lock);
518 ALOGI("%s: PCM thread streaming", __func__);
519
520 ret = audio_extn_cirrus_run_calibration();
521 ALOGE_IF(ret < 0, "%s: Calibration procedure failed (%d)", __func__, ret);
522
523 ret = audio_extn_cirrus_load_usecase_configs();
524 ALOGE_IF(ret < 0, "%s: Set tuning configs failed (%d)", __func__, ret);
525
526 close_stream:
527 pthread_mutex_lock(&adev->lock);
528 if (handle.pcm_rx) {
529 ALOGI("%s: pcm_rx_close", __func__);
530 pcm_close(handle.pcm_rx);
531 handle.pcm_rx = NULL;
532 }
533 disable_audio_route(adev, uc_info_rx);
534 disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER);
535 list_remove(&uc_info_rx->list);
536 free(uc_info_rx);
537 pthread_mutex_unlock(&adev->lock);
538 exit:
539 handle.state = (prev_state == PLAYBACK) ? PLAYBACK : IDLE;
540
541 #ifdef ENABLE_CIRRUS_DETECTION
542 if (handle.state == PLAYBACK)
543 (void)pthread_create(&handle.failure_detect_thread,
544 (const pthread_attr_t *) NULL,
545 audio_extn_cirrus_failure_detect_thread,
546 &handle);
547 #endif
548
549 ALOGV("%s: Exit", __func__);
550
551 pthread_exit(0);
552 return NULL;
553 }
554
555 #else
audio_extn_cirrus_config_thread(void)556 static void *audio_extn_cirrus_config_thread(void) {
557 struct audio_device *adev = handle.adev_handle;
558 struct crus_sp_ioctl_header header;
559 struct cirrus_cal_result_t result;
560 struct mixer_ctl *ctl_config = NULL;
561 FILE *cal_file = NULL;
562 int ret = 0, dev_file = -1;
563
564 ALOGI("%s: ++", __func__);
565
566 memset(&result, 0, sizeof(result));
567
568 dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK);
569 if (dev_file < 0) {
570 ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)",
571 __func__, dev_file);
572 ret = -EINVAL;
573 goto exit;
574 }
575
576 cal_file = fopen(CRUS_CAL_FILE, "r");
577 if (cal_file) {
578 ret = fread(&result, sizeof(result), 1, cal_file);
579
580 if (ret != 1) {
581 ALOGE("%s: Cirrus SP calibration file cannot be read , read size: %lu file error: %d",
582 __func__, (unsigned long)ret * sizeof(result), ferror(cal_file));
583 ret = -EINVAL;
584 goto exit;
585 }
586 }
587
588 header.size = sizeof(header);
589 header.module_id = CRUS_MODULE_ID_TX;
590 header.param_id = 0;
591 header.data_length = sizeof(result);
592 header.data = &result;
593
594 ret = ioctl(dev_file, CRUS_SP_IOCTL_SET_CALIB, &header);
595
596 if (ret < 0) {
597 ALOGE("%s: Cirrus SP calibration IOCTL failure", __func__);
598 goto exit;
599 }
600
601 ctl_config = mixer_get_ctl_by_name(adev->mixer,
602 CRUS_SP_LOAD_CONF_MIXER);
603 if (!ctl_config) {
604 ALOGE("%s: Could not get ctl for mixer commands", __func__);
605 ret = -EINVAL;
606 goto exit;
607 }
608
609 ret = mixer_ctl_set_value(ctl_config, 0, 2);
610 if (ret < 0) {
611 ALOGE("%s load tx config failed", __func__);
612 goto exit;
613 }
614
615 ret = mixer_ctl_set_value(ctl_config, 0, 1);
616 if (ret < 0) {
617 ALOGE("%s load rx config failed", __func__);
618 goto exit;
619 }
620
621 ret = mixer_ctl_set_value(ctl_config, 0, 0);
622 if (ret < 0) {
623 ALOGE("%s set idle state failed", __func__);
624 goto exit;
625 }
626
627 exit:
628 if (dev_file >= 0)
629 close(dev_file);
630 if (cal_file)
631 fclose(cal_file);
632
633 ALOGI("%s: ret: %d --", __func__, ret);
634 return NULL;
635 }
636 #endif
637
638 #ifdef ENABLE_CIRRUS_DETECTION
audio_extn_cirrus_failure_detect_thread()639 void *audio_extn_cirrus_failure_detect_thread() {
640 struct audio_device *adev = handle.adev_handle;
641 struct crus_sp_ioctl_header header;
642 struct mixer_ctl *ctl = NULL;
643 const int32_t r_scale_factor = 100000000;
644 const int32_t t_scale_factor = 100000;
645 const int32_t r_err_range = 70000000;
646 const int32_t t_err_range = 210000;
647 const int32_t amp_factor = 71498;
648 const int32_t material = 250;
649 int32_t *buffer = NULL;
650 int ret = 0, dev_file = -1, out_cal0 = 0, out_cal1 = 0;
651 int rL = 0, rR = 0, zL = 0, zR = 0, tL = 0, tR = 0;
652 int rdL = 0, rdR = 0, tdL = 0, tdR = 0, ambL = 0, ambR = 0;
653 bool left_cal_done = false, right_cal_done = false;
654 bool det_en = false;
655
656 ALOGI("%s: Entry", __func__);
657
658 ctl = mixer_get_ctl_by_name(adev->mixer, CRUS_SP_FAIL_DET_MIXER);
659 det_en = mixer_ctl_get_value(ctl, 0);
660
661 if (!det_en)
662 goto exit;
663
664 dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK);
665 if (dev_file < 0) {
666 ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)",
667 __func__, dev_file);
668 goto exit;
669 }
670
671 buffer = calloc(1, CRUS_PARAM_TEMP_MAX_LENGTH);
672 if (!buffer) {
673 ALOGE("%s: allocate memory failed", __func__);
674 goto exit;
675 }
676
677 header.size = sizeof(header);
678 header.module_id = CRUS_MODULE_ID_RX;
679 header.param_id = CRUS_PARAM_RX_GET_TEMP;
680 header.data_length = CRUS_PARAM_TEMP_MAX_LENGTH;
681 header.data = buffer;
682
683 usleep(FAIL_DETECT_INIT_WAIT_US);
684
685 pthread_mutex_lock(&handle.fb_prot_mutex);
686 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
687 pthread_mutex_unlock(&handle.fb_prot_mutex);
688 if (ret < 0) {
689 ALOGE("%s: Cirrus SP IOCTL failure (%d)",
690 __func__, ret);
691 goto exit;
692 }
693
694 zL = buffer[2] * amp_factor;
695 zR = buffer[4] * amp_factor;
696
697 ambL = buffer[10];
698 ambR = buffer[6];
699
700 out_cal0 = buffer[12];
701 out_cal1 = buffer[13];
702
703 left_cal_done = (out_cal0 == 2) && (out_cal1 == 2) &&
704 (buffer[2] != CRUS_DEFAULT_CAL_L);
705
706 out_cal0 = buffer[14];
707 out_cal1 = buffer[15];
708
709 right_cal_done = (out_cal0 == 2) && (out_cal1 == 2) &&
710 (buffer[4] != CRUS_DEFAULT_CAL_R);
711
712 if (left_cal_done) {
713 ALOGI("%s: L Speaker Impedance: %d.%08d ohms", __func__,
714 zL / r_scale_factor, abs(zL) % r_scale_factor);
715 ALOGI("%s: L Calibration Temperature: %d C", __func__, ambL);
716 } else
717 ALOGE("%s: Left speaker uncalibrated", __func__);
718
719 if (right_cal_done) {
720 ALOGI("%s: R Speaker Impedance: %d.%08d ohms", __func__,
721 zR / r_scale_factor, abs(zR) % r_scale_factor);
722 ALOGI("%s: R Calibration Temperature: %d C", __func__, ambR);
723 } else
724 ALOGE("%s: Right speaker uncalibrated", __func__);
725
726 if (!left_cal_done && !right_cal_done)
727 goto exit;
728
729 ALOGI("%s: Monitoring speaker impedance & temperature...", __func__);
730
731 while ((handle.state == PLAYBACK) && det_en) {
732 pthread_mutex_lock(&handle.fb_prot_mutex);
733 ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
734 pthread_mutex_unlock(&handle.fb_prot_mutex);
735 if (ret < 0) {
736 ALOGE("%s: Cirrus SP IOCTL failure (%d)",
737 __func__, ret);
738 goto loop;
739 }
740
741 rL = buffer[3];
742 rR = buffer[1];
743
744 zL = buffer[2];
745 zR = buffer[4];
746
747 if ((zL == 0) || (zR == 0))
748 goto loop;
749
750 tdL = (material * t_scale_factor * (rL-zL) / zL);
751 tdR = (material * t_scale_factor * (rR-zR) / zR);
752
753 rL *= amp_factor;
754 rR *= amp_factor;
755
756 zL *= amp_factor;
757 zR *= amp_factor;
758
759 tL = tdL + (ambL * t_scale_factor);
760 tR = tdR + (ambR * t_scale_factor);
761
762 rdL = abs(zL - rL);
763 rdR = abs(zR - rR);
764
765 if (left_cal_done && (rL != 0) && (rdL > r_err_range))
766 ALOGI("%s: Left speaker impedance out of range (%d.%08d ohms)",
767 __func__, rL / r_scale_factor,
768 abs(rL % r_scale_factor));
769
770 if (right_cal_done && (rR != 0) && (rdR > r_err_range))
771 ALOGI("%s: Right speaker impedance out of range (%d.%08d ohms)",
772 __func__, rR / r_scale_factor,
773 abs(rR % r_scale_factor));
774
775 if (left_cal_done && (rL != 0) && (tdL > t_err_range))
776 ALOGI("%s: Left speaker temperature out of range (%d.%05d C)",
777 __func__, tL / t_scale_factor,
778 abs(tL % t_scale_factor));
779
780 if (right_cal_done && (rR != 0) && (tdR > t_err_range))
781 ALOGI("%s: Right speaker temperature out of range (%d.%05d C)",
782 __func__, tR / t_scale_factor,
783 abs(tR % t_scale_factor));
784
785 loop:
786 det_en = mixer_ctl_get_value(ctl, 0);
787 usleep(FAIL_DETECT_LOOP_WAIT_US);
788 }
789
790 exit:
791 if (dev_file >= 0)
792 close(dev_file);
793 free(buffer);
794 ALOGI("%s: Exit ", __func__);
795
796 pthread_exit(0);
797 return NULL;
798 }
799 #endif
800
audio_extn_spkr_prot_start_processing(snd_device_t snd_device)801 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) {
802 struct audio_usecase *uc_info_tx;
803 struct audio_device *adev = handle.adev_handle;
804 int32_t pcm_dev_tx_id = -1, ret = 0;
805
806 ALOGV("%s: Entry", __func__);
807
808 if (!adev) {
809 ALOGE("%s: Invalid params", __func__);
810 return -EINVAL;
811 }
812
813 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(*uc_info_tx));
814 if (!uc_info_tx) {
815 ALOGE("%s: allocate memory failed", __func__);
816 return -ENOMEM;
817 }
818
819 audio_route_apply_and_update_path(adev->audio_route,
820 platform_get_snd_device_name(snd_device));
821
822 pthread_mutex_lock(&handle.fb_prot_mutex);
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
829 list_add_tail(&adev->usecase_list, &uc_info_tx->list);
830
831 enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
832 enable_audio_route(adev, uc_info_tx);
833
834 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
835
836 if (pcm_dev_tx_id < 0) {
837 ALOGE("%s: Invalid pcm device for usecase (%d)",
838 __func__, uc_info_tx->id);
839 ret = -ENODEV;
840 goto exit;
841 }
842
843 handle.pcm_tx = pcm_open(adev->snd_card,
844 pcm_dev_tx_id,
845 PCM_IN, &pcm_config_cirrus_tx);
846
847 if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
848 ALOGE("%s: PCM device not ready: %s", __func__, pcm_get_error(handle.pcm_tx));
849 ret = -EIO;
850 goto exit;
851 }
852
853 if (pcm_start(handle.pcm_tx) < 0) {
854 ALOGE("%s: pcm start for TX failed; error = %s", __func__,
855 pcm_get_error(handle.pcm_tx));
856 ret = -EINVAL;
857 goto exit;
858 }
859
860 #ifdef ENABLE_CIRRUS_DETECTION
861 if (handle.state == IDLE)
862 (void)pthread_create(&handle.failure_detect_thread,
863 (const pthread_attr_t *) NULL,
864 audio_extn_cirrus_failure_detect_thread,
865 &handle);
866 #endif
867
868 handle.state = PLAYBACK;
869 exit:
870 if (ret) {
871 handle.state = IDLE;
872 if (handle.pcm_tx) {
873 ALOGI("%s: pcm_tx_close", __func__);
874 pcm_close(handle.pcm_tx);
875 handle.pcm_tx = NULL;
876 }
877
878 disable_audio_route(adev, uc_info_tx);
879 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
880 list_remove(&uc_info_tx->list);
881 free(uc_info_tx);
882 }
883
884 pthread_mutex_unlock(&handle.fb_prot_mutex);
885 ALOGV("%s: Exit", __func__);
886 return ret;
887 }
888
audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)889 void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device) {
890 struct audio_usecase *uc_info_tx;
891 struct audio_device *adev = handle.adev_handle;
892
893 ALOGV("%s: Entry", __func__);
894
895 pthread_mutex_lock(&handle.fb_prot_mutex);
896
897 handle.state = IDLE;
898 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
899
900 if (uc_info_tx) {
901 if (handle.pcm_tx) {
902 ALOGI("%s: pcm_tx_close", __func__);
903 pcm_close(handle.pcm_tx);
904 handle.pcm_tx = NULL;
905 }
906
907 disable_audio_route(adev, uc_info_tx);
908 disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
909 list_remove(&uc_info_tx->list);
910 free(uc_info_tx);
911
912 audio_route_reset_path(adev->audio_route,
913 platform_get_snd_device_name(snd_device));
914 }
915
916 pthread_mutex_unlock(&handle.fb_prot_mutex);
917
918 ALOGV("%s: Exit", __func__);
919 }
920
audio_extn_spkr_prot_is_enabled()921 bool audio_extn_spkr_prot_is_enabled() {
922 return true;
923 }
924
audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)925 int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device) {
926 switch(snd_device) {
927 case SND_DEVICE_OUT_SPEAKER:
928 case SND_DEVICE_OUT_SPEAKER_REVERSE:
929 return SND_DEVICE_OUT_SPEAKER_PROTECTED;
930 case SND_DEVICE_OUT_SPEAKER_SAFE:
931 return SND_DEVICE_OUT_SPEAKER_SAFE;
932 case SND_DEVICE_OUT_VOICE_SPEAKER:
933 return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
934 default:
935 return snd_device;
936 }
937 }
938
audio_extn_spkr_prot_calib_cancel(__unused void * adev)939 void audio_extn_spkr_prot_calib_cancel(__unused void *adev) {
940 // FIXME: wait or cancel audio_extn_cirrus_run_calibration
941 }
942