1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // hdac_component.c - routines for sync between HD-A core and DRM driver |
3 | |
4 | #include <linux/init.h> |
5 | #include <linux/module.h> |
6 | #include <linux/pci.h> |
7 | #include <linux/component.h> |
8 | #include <linux/string_choices.h> |
9 | #include <sound/core.h> |
10 | #include <sound/hdaudio.h> |
11 | #include <sound/hda_component.h> |
12 | #include <sound/hda_register.h> |
13 | |
14 | static void hdac_acomp_release(struct device *dev, void *res) |
15 | { |
16 | } |
17 | |
18 | static struct drm_audio_component *hdac_get_acomp(struct device *dev) |
19 | { |
20 | return devres_find(dev, release: hdac_acomp_release, NULL, NULL); |
21 | } |
22 | |
23 | /** |
24 | * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup |
25 | * @bus: HDA core bus |
26 | * @enable: enable or disable the wakeup |
27 | * |
28 | * This function is supposed to be used only by a HD-audio controller |
29 | * driver that needs the interaction with graphics driver. |
30 | * |
31 | * This function should be called during the chip reset, also called at |
32 | * resume for updating STATESTS register read. |
33 | * |
34 | * Returns zero for success or a negative error code. |
35 | */ |
36 | int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) |
37 | { |
38 | struct drm_audio_component *acomp = bus->audio_component; |
39 | |
40 | if (!acomp || !acomp->ops) |
41 | return -ENODEV; |
42 | |
43 | if (!acomp->ops->codec_wake_override) |
44 | return 0; |
45 | |
46 | dev_dbg(bus->dev, "%s codec wakeup\n" , str_enable_disable(enable)); |
47 | |
48 | acomp->ops->codec_wake_override(acomp->dev, enable); |
49 | |
50 | return 0; |
51 | } |
52 | EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup); |
53 | |
54 | /** |
55 | * snd_hdac_display_power - Power up / down the power refcount |
56 | * @bus: HDA core bus |
57 | * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller |
58 | * @enable: power up or down |
59 | * |
60 | * This function is used by either HD-audio controller or codec driver that |
61 | * needs the interaction with graphics driver. |
62 | * |
63 | * This function updates the power status, and calls the get_power() and |
64 | * put_power() ops accordingly, toggling the codec wakeup, too. |
65 | */ |
66 | void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable) |
67 | { |
68 | struct drm_audio_component *acomp = bus->audio_component; |
69 | |
70 | dev_dbg(bus->dev, "display power %s\n" , str_enable_disable(enable)); |
71 | |
72 | mutex_lock(&bus->lock); |
73 | if (enable) |
74 | set_bit(nr: idx, addr: &bus->display_power_status); |
75 | else |
76 | clear_bit(nr: idx, addr: &bus->display_power_status); |
77 | |
78 | if (!acomp || !acomp->ops) |
79 | goto unlock; |
80 | |
81 | if (bus->display_power_status) { |
82 | if (!bus->display_power_active) { |
83 | unsigned long cookie = -1; |
84 | |
85 | if (acomp->ops->get_power) |
86 | cookie = acomp->ops->get_power(acomp->dev); |
87 | |
88 | snd_hdac_set_codec_wakeup(bus, true); |
89 | snd_hdac_set_codec_wakeup(bus, false); |
90 | bus->display_power_active = cookie; |
91 | } |
92 | } else { |
93 | if (bus->display_power_active) { |
94 | unsigned long cookie = bus->display_power_active; |
95 | |
96 | if (acomp->ops->put_power) |
97 | acomp->ops->put_power(acomp->dev, cookie); |
98 | |
99 | bus->display_power_active = 0; |
100 | } |
101 | } |
102 | unlock: |
103 | mutex_unlock(lock: &bus->lock); |
104 | } |
105 | EXPORT_SYMBOL_GPL(snd_hdac_display_power); |
106 | |
107 | /** |
108 | * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate |
109 | * @codec: HDA codec |
110 | * @nid: the pin widget NID |
111 | * @dev_id: device identifier |
112 | * @rate: the sample rate to set |
113 | * |
114 | * This function is supposed to be used only by a HD-audio controller |
115 | * driver that needs the interaction with graphics driver. |
116 | * |
117 | * This function sets N/CTS value based on the given sample rate. |
118 | * Returns zero for success, or a negative error code. |
119 | */ |
120 | int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, |
121 | int dev_id, int rate) |
122 | { |
123 | struct hdac_bus *bus = codec->bus; |
124 | struct drm_audio_component *acomp = bus->audio_component; |
125 | int port, pipe; |
126 | |
127 | if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) |
128 | return -ENODEV; |
129 | port = nid; |
130 | if (acomp->audio_ops && acomp->audio_ops->pin2port) { |
131 | port = acomp->audio_ops->pin2port(codec, nid); |
132 | if (port < 0) |
133 | return -EINVAL; |
134 | } |
135 | pipe = dev_id; |
136 | return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate); |
137 | } |
138 | EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); |
139 | |
140 | /** |
141 | * snd_hdac_acomp_get_eld - Get the audio state and ELD via component |
142 | * @codec: HDA codec |
143 | * @nid: the pin widget NID |
144 | * @dev_id: device identifier |
145 | * @audio_enabled: the pointer to store the current audio state |
146 | * @buffer: the buffer pointer to store ELD bytes |
147 | * @max_bytes: the max bytes to be stored on @buffer |
148 | * |
149 | * This function is supposed to be used only by a HD-audio controller |
150 | * driver that needs the interaction with graphics driver. |
151 | * |
152 | * This function queries the current state of the audio on the given |
153 | * digital port and fetches the ELD bytes onto the given buffer. |
154 | * It returns the number of bytes for the total ELD data, zero for |
155 | * invalid ELD, or a negative error code. |
156 | * |
157 | * The return size is the total bytes required for the whole ELD bytes, |
158 | * thus it may be over @max_bytes. If it's over @max_bytes, it implies |
159 | * that only a part of ELD bytes have been fetched. |
160 | */ |
161 | int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id, |
162 | bool *audio_enabled, char *buffer, int max_bytes) |
163 | { |
164 | struct hdac_bus *bus = codec->bus; |
165 | struct drm_audio_component *acomp = bus->audio_component; |
166 | int port, pipe; |
167 | |
168 | if (!acomp || !acomp->ops || !acomp->ops->get_eld) |
169 | return -ENODEV; |
170 | |
171 | port = nid; |
172 | if (acomp->audio_ops && acomp->audio_ops->pin2port) { |
173 | port = acomp->audio_ops->pin2port(codec, nid); |
174 | if (port < 0) |
175 | return -EINVAL; |
176 | } |
177 | pipe = dev_id; |
178 | return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled, |
179 | buffer, max_bytes); |
180 | } |
181 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); |
182 | |
183 | static int hdac_component_master_bind(struct device *dev) |
184 | { |
185 | struct drm_audio_component *acomp = hdac_get_acomp(dev); |
186 | int ret; |
187 | |
188 | if (WARN_ON(!acomp)) |
189 | return -EINVAL; |
190 | |
191 | ret = component_bind_all(parent: dev, data: acomp); |
192 | if (ret < 0) |
193 | return ret; |
194 | |
195 | if (WARN_ON(!(acomp->dev && acomp->ops))) { |
196 | ret = -EINVAL; |
197 | goto out_unbind; |
198 | } |
199 | |
200 | /* pin the module to avoid dynamic unbinding, but only if given */ |
201 | if (!try_module_get(module: acomp->ops->owner)) { |
202 | ret = -ENODEV; |
203 | goto out_unbind; |
204 | } |
205 | |
206 | if (acomp->audio_ops && acomp->audio_ops->master_bind) { |
207 | ret = acomp->audio_ops->master_bind(dev, acomp); |
208 | if (ret < 0) |
209 | goto module_put; |
210 | } |
211 | |
212 | complete_all(&acomp->master_bind_complete); |
213 | return 0; |
214 | |
215 | module_put: |
216 | module_put(module: acomp->ops->owner); |
217 | out_unbind: |
218 | component_unbind_all(parent: dev, data: acomp); |
219 | complete_all(&acomp->master_bind_complete); |
220 | |
221 | return ret; |
222 | } |
223 | |
224 | static void hdac_component_master_unbind(struct device *dev) |
225 | { |
226 | struct drm_audio_component *acomp = hdac_get_acomp(dev); |
227 | |
228 | if (acomp->audio_ops && acomp->audio_ops->master_unbind) |
229 | acomp->audio_ops->master_unbind(dev, acomp); |
230 | module_put(module: acomp->ops->owner); |
231 | component_unbind_all(parent: dev, data: acomp); |
232 | WARN_ON(acomp->ops || acomp->dev); |
233 | } |
234 | |
235 | static const struct component_master_ops hdac_component_master_ops = { |
236 | .bind = hdac_component_master_bind, |
237 | .unbind = hdac_component_master_unbind, |
238 | }; |
239 | |
240 | /** |
241 | * snd_hdac_acomp_register_notifier - Register audio component ops |
242 | * @bus: HDA core bus |
243 | * @aops: audio component ops |
244 | * |
245 | * This function is supposed to be used only by a HD-audio controller |
246 | * driver that needs the interaction with graphics driver. |
247 | * |
248 | * This function sets the given ops to be called by the graphics driver. |
249 | * |
250 | * Returns zero for success or a negative error code. |
251 | */ |
252 | int snd_hdac_acomp_register_notifier(struct hdac_bus *bus, |
253 | const struct drm_audio_component_audio_ops *aops) |
254 | { |
255 | if (!bus->audio_component) |
256 | return -ENODEV; |
257 | |
258 | bus->audio_component->audio_ops = aops; |
259 | return 0; |
260 | } |
261 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier); |
262 | |
263 | /** |
264 | * snd_hdac_acomp_init - Initialize audio component |
265 | * @bus: HDA core bus |
266 | * @aops: audio component ops |
267 | * @match_master: match function for finding components |
268 | * @extra_size: Extra bytes to allocate |
269 | * |
270 | * This function is supposed to be used only by a HD-audio controller |
271 | * driver that needs the interaction with graphics driver. |
272 | * |
273 | * This function initializes and sets up the audio component to communicate |
274 | * with graphics driver. |
275 | * |
276 | * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the |
277 | * binding with the DRM component. Each caller needs to sync via master_bind |
278 | * audio_ops. |
279 | * |
280 | * Returns zero for success or a negative error code. |
281 | */ |
282 | int snd_hdac_acomp_init(struct hdac_bus *bus, |
283 | const struct drm_audio_component_audio_ops *aops, |
284 | int (*match_master)(struct device *, int, void *), |
285 | size_t ) |
286 | { |
287 | struct component_match *match = NULL; |
288 | struct device *dev = bus->dev; |
289 | struct drm_audio_component *acomp; |
290 | int ret; |
291 | |
292 | if (WARN_ON(hdac_get_acomp(dev))) |
293 | return -EBUSY; |
294 | |
295 | acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size, |
296 | GFP_KERNEL); |
297 | if (!acomp) |
298 | return -ENOMEM; |
299 | acomp->audio_ops = aops; |
300 | init_completion(x: &acomp->master_bind_complete); |
301 | bus->audio_component = acomp; |
302 | devres_add(dev, res: acomp); |
303 | |
304 | component_match_add_typed(parent: dev, matchptr: &match, compare_typed: match_master, compare_data: bus); |
305 | ret = component_master_add_with_match(dev, &hdac_component_master_ops, |
306 | match); |
307 | if (ret < 0) |
308 | goto out_err; |
309 | |
310 | return 0; |
311 | |
312 | out_err: |
313 | bus->audio_component = NULL; |
314 | devres_destroy(dev, release: hdac_acomp_release, NULL, NULL); |
315 | dev_info(dev, "failed to add audio component master (%d)\n" , ret); |
316 | |
317 | return ret; |
318 | } |
319 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_init); |
320 | |
321 | /** |
322 | * snd_hdac_acomp_exit - Finalize audio component |
323 | * @bus: HDA core bus |
324 | * |
325 | * This function is supposed to be used only by a HD-audio controller |
326 | * driver that needs the interaction with graphics driver. |
327 | * |
328 | * This function releases the audio component that has been used. |
329 | * |
330 | * Returns zero for success or a negative error code. |
331 | */ |
332 | int snd_hdac_acomp_exit(struct hdac_bus *bus) |
333 | { |
334 | struct device *dev = bus->dev; |
335 | struct drm_audio_component *acomp = bus->audio_component; |
336 | |
337 | if (!acomp) |
338 | return 0; |
339 | |
340 | if (WARN_ON(bus->display_power_active) && acomp->ops) |
341 | acomp->ops->put_power(acomp->dev, bus->display_power_active); |
342 | |
343 | bus->display_power_active = 0; |
344 | bus->display_power_status = 0; |
345 | |
346 | component_master_del(dev, &hdac_component_master_ops); |
347 | |
348 | bus->audio_component = NULL; |
349 | devres_destroy(dev, release: hdac_acomp_release, NULL, NULL); |
350 | |
351 | return 0; |
352 | } |
353 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit); |
354 | |