2 * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "audio_hw_amplifier_tfa"
20 #include <cutils/log.h>
21 #include <sys/types.h>
29 #include <tinyalsa/asoundlib.h>
33 static void * write_dummy_data(void *param
) {
34 tfa_device_t
*t
= (tfa_device_t
*) param
;
38 bool signaled
= false;
40 struct pcm_config config
= {
45 .format
= PCM_FORMAT_S16_LE
,
46 .start_threshold
= config
.period_size
* config
.period_count
- 1,
47 .stop_threshold
= UINT_MAX
,
48 .silence_threshold
= 0,
52 pcm
= pcm_open(0, 0, PCM_OUT
| PCM_MONOTONIC
, &config
);
53 if (!pcm
|| !pcm_is_ready(pcm
)) {
54 ALOGE("pcm_open failed: %s", pcm_get_error(pcm
));
55 if (pcm
&& errno
!= EBUSY
) {
62 buffer
= calloc(1, size
);
64 ALOGE("%s: failed to allocate buffer", __func__
);
69 if (pcm_write(pcm
, buffer
, size
)) {
70 ALOGE("%s: pcm_write failed", __func__
);
73 pthread_mutex_lock(&t
->mutex
);
75 pthread_cond_signal(&t
->cond
);
76 pthread_mutex_unlock(&t
->mutex
);
79 } while (t
->initializing
);
89 pthread_mutex_lock(&t
->mutex
);
91 pthread_cond_signal(&t
->cond
);
92 pthread_mutex_unlock(&t
->mutex
);
98 static int tfa_clock_on(tfa_device_t
*tfa_dev
)
100 if (tfa_dev
->clock_enabled
) {
101 ALOGW("%s: clocks already on", __func__
);
105 tfa_dev
->initializing
= true;
106 pthread_create(&tfa_dev
->write_thread
, NULL
, write_dummy_data
, tfa_dev
);
107 pthread_mutex_lock(&tfa_dev
->mutex
);
108 while (!tfa_dev
->writing
) {
109 pthread_cond_wait(&tfa_dev
->cond
, &tfa_dev
->mutex
);
111 pthread_mutex_unlock(&tfa_dev
->mutex
);
112 tfa_dev
->clock_enabled
= true;
114 ALOGI("%s: clocks enabled", __func__
);
119 static int tfa_clock_off(tfa_device_t
*tfa_dev
)
121 if (!tfa_dev
->clock_enabled
) {
122 ALOGW("%s: clocks already off", __func__
);
126 tfa_dev
->initializing
= false;
127 pthread_join(tfa_dev
->write_thread
, NULL
);
128 tfa_dev
->clock_enabled
= false;
130 ALOGI("%s: clocks disabled", __func__
);
136 * Loads the vendor amplifier library and grabs the needed functions.
138 * @param tfa_dev Device handle.
140 * @return 0 on success, <0 on error.
142 static int load_tfa_lib(tfa_device_t
*tfa_dev
) {
143 if (access(TFA_LIBRARY_PATH
, R_OK
) < 0) {
144 ALOGE("%s: amplifier library %s not found", __func__
, TFA_LIBRARY_PATH
);
148 tfa_dev
->lib_handle
= dlopen(TFA_LIBRARY_PATH
, RTLD_NOW
);
149 if (tfa_dev
->lib_handle
== NULL
) {
150 ALOGE("%s: dlopen failed for %s (%s)", __func__
, TFA_LIBRARY_PATH
, dlerror());
153 ALOGV("%s: dlopen successful for %s", __func__
, TFA_LIBRARY_PATH
);
156 tfa_dev
->tfa_device_open
= (tfa_device_open_t
)dlsym(tfa_dev
->lib_handle
, "tfa_device_open");
157 if (tfa_dev
->tfa_device_open
== NULL
) {
158 ALOGE("%s: dlsym error %s for tfa_device_open", __func__
, dlerror());
159 tfa_dev
->tfa_device_open
= 0;
163 tfa_dev
->tfa_enable
= (tfa_enable_t
)dlsym(tfa_dev
->lib_handle
, "tfa_enable");
164 if (tfa_dev
->tfa_enable
== NULL
) {
165 ALOGE("%s: dlsym error %s for tfa_enable", __func__
, dlerror());
166 tfa_dev
->tfa_enable
= 0;
174 * Hooks into the vendor library and enables/disables the amplifier IC.
176 * @param tfa_dev Device handle.
177 * @param on true or false for enabling/disabling of the IC.
179 * @return 0 on success, != 0 on error.
181 int tfa_power(tfa_device_t
*tfa_dev
, bool on
) {
184 ALOGV("%s: %s amplifier device", __func__
, on
? "Enabling" : "Disabling");
185 pthread_mutex_lock(&tfa_dev
->tfa_lock
);
187 if (tfa_dev
->tfa_handle
->a1
!= 0) {
188 tfa_dev
->tfa_handle
->a1
= 0;
192 // this implementation requires explicit clock control
194 tfa_clock_on(tfa_dev
);
197 rc
= tfa_dev
->tfa_enable(tfa_dev
->tfa_handle
, on
? 1 : 0);
199 ALOGE("%s: Failed to %s amplifier device", __func__
, on
? "enable" : "disable");
202 if (tfa_dev
->clock_enabled
) {
203 tfa_clock_off(tfa_dev
);
205 pthread_mutex_unlock(&tfa_dev
->tfa_lock
);
211 * Initializes the amplifier device and local class data.
213 * @return tfa_device_t on success, NULL on error.
215 tfa_device_t
* tfa_device_open() {
216 tfa_device_t
*tfa_dev
;
219 ALOGV("Opening amplifier device");
221 tfa_dev
= (tfa_device_t
*) malloc(sizeof(tfa_device_t
));
222 if (tfa_dev
== NULL
) {
223 ALOGE("%s: Not enough memory to load the lib handle", __func__
);
227 // allocate memory for tfa handle
228 tfa_dev
->tfa_handle
= malloc(sizeof(tfa_handle_t
));
229 if (tfa_dev
->tfa_handle
== NULL
) {
230 ALOGE("%s: Not enough memory to load the tfa handle", __func__
);
234 rc
= load_tfa_lib(tfa_dev
);
236 ALOGE("%s: Failed to load amplifier library", __func__
);
240 rc
= tfa_dev
->tfa_device_open(tfa_dev
->tfa_handle
, 0);
242 ALOGE("%s: Failed to open amplifier device", __func__
);
246 pthread_mutex_init(&tfa_dev
->tfa_lock
, (const pthread_mutexattr_t
*) NULL
);
248 rc
= tfa_power(tfa_dev
, false);
250 ALOGE("%s: Failed to do initial amplifier powerdown", __func__
);
254 // do a full powerup - powerdown cycle and initialize clocks
255 tfa_dev
->writing
= false;
256 tfa_dev
->clock_enabled
= false;
257 pthread_mutex_init(&tfa_dev
->mutex
, NULL
);
258 pthread_cond_init(&tfa_dev
->cond
, NULL
);
260 rc
= tfa_power(tfa_dev
, true);
262 ALOGE("%s: Failed to do initial amplifier powerup", __func__
);
265 rc
= tfa_power(tfa_dev
, false);
267 ALOGE("%s: Failed to do initial amplifier powerdown (2)", __func__
);
276 * De-Initializes the amplifier device.
278 void tfa_device_close(tfa_device_t
*tfa_dev
) {
279 ALOGV("%s: Closing amplifier device", __func__
);
280 tfa_power(tfa_dev
, false);
282 pthread_mutex_destroy(&tfa_dev
->tfa_lock
);
284 if (tfa_dev
->tfa_handle
) {
285 free(tfa_dev
->tfa_handle
);
288 if (tfa_dev
->lib_handle
) {
289 dlclose(tfa_dev
->lib_handle
);