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