Commit | Line | Data |
---|---|---|
85a78b61 CH |
1 | /* |
2 | * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com> | |
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_amplifier" | |
18 | #define LOG_NDEBUG 0 | |
19 | ||
20 | #include <cutils/log.h> | |
21 | #include <sys/types.h> | |
22 | #include <sys/stat.h> | |
23 | #include <errno.h> | |
24 | #include <dlfcn.h> | |
25 | #include <fcntl.h> | |
26 | #include <stdlib.h> | |
27 | #include <unistd.h> | |
28 | ||
29 | #include <audio_hw.h> | |
30 | #include <hardware/audio_amplifier.h> | |
31 | ||
32 | #include "tfa.h" | |
33 | ||
db132bfa CH |
34 | typedef enum amp_state { |
35 | AMP_STATE_DISABLED = 0, | |
36 | AMP_STATE_ENABLED, | |
37 | AMP_STATE_MAX = AMP_STATE_ENABLED | |
38 | } amp_state_t; | |
39 | ||
85a78b61 CH |
40 | typedef struct amp_device { |
41 | amplifier_device_t amp_dev; | |
42 | tfa_device_t *tfa_dev; | |
43 | audio_mode_t current_mode; | |
db132bfa CH |
44 | int refcount[Audio_Mode_Max]; |
45 | amp_state_t amp_state; | |
85a78b61 CH |
46 | } amp_device_t; |
47 | ||
48 | static amp_device_t *amp_dev = NULL; | |
49 | ||
50 | /* | |
51 | * Returns the internal TFA mode appropriate for the device. | |
52 | * | |
53 | * @param snd_device The current sound device. | |
54 | * | |
55 | * @return tfa_mode_t identifying the internal amplifier mode. | |
56 | */ | |
57 | static tfa_mode_t classify_snd_device(uint32_t snd_device) { | |
58 | tfa_mode_t mode = Audio_Mode_None; | |
59 | ||
60 | switch (snd_device) { | |
61 | case SND_DEVICE_OUT_SPEAKER: | |
62 | // our audio HAL splits this up | |
63 | //case SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES: | |
64 | mode = Audio_Mode_Music_Normal; | |
65 | break; | |
66 | case SND_DEVICE_OUT_VOICE_SPEAKER: | |
67 | case SND_DEVICE_OUT_VOICE_SPEAKER_WB: | |
68 | mode = Audio_Mode_Voice; | |
69 | break; | |
70 | default: | |
71 | break; | |
72 | } | |
73 | ||
74 | return mode; | |
75 | } | |
76 | ||
77 | /* | |
78 | * Hook into amplifier HAL | |
79 | */ | |
80 | static int amp_enable_output_devices(amplifier_device_t *device, | |
81 | uint32_t devices, bool enable) | |
82 | { | |
83 | amp_device_t *dev = (amp_device_t *) device; | |
84 | tfa_mode_t tfa_mode = classify_snd_device(devices); | |
85a78b61 CH |
85 | int rc = 0; |
86 | ||
87 | ALOGV("%s: devices=0x%x, enable=%d, tfa_mode=%d", __func__, devices, enable, tfa_mode); | |
88 | ||
db132bfa | 89 | if (tfa_mode == Audio_Mode_None) { |
85a78b61 CH |
90 | return 0; |
91 | } | |
92 | ||
93 | if (enable) { | |
db132bfa CH |
94 | dev->refcount[tfa_mode]++; |
95 | } else if (!enable && dev->refcount[tfa_mode] > 0) { | |
96 | dev->refcount[tfa_mode]--; | |
85a78b61 CH |
97 | } |
98 | ||
db132bfa CH |
99 | ALOGV("%s: enable=%d, dev->refcount=%d, amp_state=%d", __func__, enable, |
100 | dev->refcount[tfa_mode], dev->amp_state); | |
85a78b61 | 101 | |
db132bfa | 102 | if (enable && dev->amp_state == AMP_STATE_DISABLED && dev->refcount[tfa_mode] == 1) { |
85a78b61 | 103 | rc = tfa_power(dev->tfa_dev, true); |
db132bfa CH |
104 | if (rc == 0) { |
105 | dev->amp_state = AMP_STATE_ENABLED; | |
106 | } | |
107 | ALOGV("%s: tfa_power(true) with rc=%d", __func__, rc); | |
108 | } else if (!enable && dev->amp_state == AMP_STATE_ENABLED && dev->refcount[tfa_mode] == 0) { | |
85a78b61 | 109 | rc = tfa_power(dev->tfa_dev, false); |
db132bfa CH |
110 | if (rc == 0) { |
111 | dev->amp_state = AMP_STATE_DISABLED; | |
112 | } | |
113 | ALOGV("%s: tfa_power(false) with rc=%d", __func__, rc); | |
85a78b61 CH |
114 | } |
115 | ||
116 | // TODO: Distinguish between tfa modes | |
117 | ||
118 | return rc; | |
119 | } | |
120 | ||
121 | static int amp_set_mode(amplifier_device_t *device, audio_mode_t mode) | |
122 | { | |
123 | amp_device_t *dev = (amp_device_t *) device; | |
124 | ||
125 | dev->current_mode = mode; | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | static int amp_dev_close(hw_device_t *device) | |
131 | { | |
132 | amp_device_t *dev = (amp_device_t *) device; | |
133 | ||
134 | tfa_device_close(dev->tfa_dev); | |
135 | ||
136 | free(dev); | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | static int amp_module_open(const hw_module_t *module, const char *name, | |
142 | hw_device_t **device) | |
143 | { | |
144 | tfa_device_t *tfa_dev; | |
145 | ||
146 | if (strcmp(name, AMPLIFIER_HARDWARE_INTERFACE)) { | |
147 | ALOGE("%s: %s does not match amplifier hardware interface name", | |
148 | __func__, name); | |
149 | return -ENODEV; | |
150 | } | |
151 | ||
152 | if (amp_dev) { | |
153 | ALOGE("%s: Unable to open second instance of the amplifier", __func__); | |
154 | return -EBUSY; | |
155 | } | |
156 | ||
157 | tfa_dev = tfa_device_open(); | |
158 | if (tfa_dev == NULL) { | |
159 | ALOGE("%s: Unable to open amplifier device", __func__); | |
160 | return -ENOENT; | |
161 | } | |
162 | ||
163 | amp_dev = calloc(1, sizeof(amp_device_t)); | |
164 | if (!amp_dev) { | |
165 | ALOGE("%s: Unable to allocate memory for amplifier device", __func__); | |
166 | return -ENOMEM; | |
167 | } | |
168 | ||
169 | amp_dev->amp_dev.common.tag = HARDWARE_DEVICE_TAG; | |
170 | amp_dev->amp_dev.common.module = (hw_module_t *) module; | |
171 | amp_dev->amp_dev.common.version = HARDWARE_DEVICE_API_VERSION(1, 0); | |
172 | amp_dev->amp_dev.common.close = amp_dev_close; | |
173 | ||
174 | amp_dev->amp_dev.set_input_devices = NULL; | |
175 | amp_dev->amp_dev.set_output_devices = NULL; | |
176 | amp_dev->amp_dev.enable_input_devices = NULL; | |
177 | amp_dev->amp_dev.enable_output_devices = amp_enable_output_devices; | |
178 | amp_dev->amp_dev.set_mode = amp_set_mode; | |
179 | amp_dev->amp_dev.output_stream_start = NULL; | |
180 | amp_dev->amp_dev.input_stream_start = NULL; | |
181 | amp_dev->amp_dev.output_stream_standby = NULL; | |
182 | amp_dev->amp_dev.input_stream_standby = NULL; | |
183 | amp_dev->amp_dev.set_parameters = NULL; | |
184 | ||
185 | amp_dev->current_mode = AUDIO_MODE_NORMAL; | |
db132bfa CH |
186 | memset(amp_dev->refcount, 0, Audio_Mode_Max); |
187 | amp_dev->amp_state = AMP_STATE_DISABLED; | |
85a78b61 CH |
188 | |
189 | amp_dev->tfa_dev = tfa_dev; | |
190 | ||
191 | *device = (hw_device_t *) amp_dev; | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
196 | static struct hw_module_methods_t hal_module_methods = { | |
197 | .open = amp_module_open, | |
198 | }; | |
199 | ||
200 | amplifier_module_t HAL_MODULE_INFO_SYM = { | |
201 | .common = { | |
202 | .tag = HARDWARE_MODULE_TAG, | |
203 | .module_api_version = AMPLIFIER_MODULE_API_VERSION_0_1, | |
204 | .hal_api_version = HARDWARE_HAL_API_VERSION, | |
205 | .id = AMPLIFIER_HARDWARE_MODULE_ID, | |
206 | .name = "Samsung TFA9896 amplifier HAL", | |
207 | .author = "Christopher N. Hesse", | |
208 | .methods = &hal_module_methods, | |
209 | }, | |
210 | }; |