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
14static void hdac_acomp_release(struct device *dev, void *res)
15{
16}
17
18static 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 */
36int 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}
52EXPORT_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 */
66void 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 guard(mutex)(T: &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 return;
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}
103EXPORT_SYMBOL_GPL(snd_hdac_display_power);
104
105/**
106 * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
107 * @codec: HDA codec
108 * @nid: the pin widget NID
109 * @dev_id: device identifier
110 * @rate: the sample rate to set
111 *
112 * This function is supposed to be used only by a HD-audio controller
113 * driver that needs the interaction with graphics driver.
114 *
115 * This function sets N/CTS value based on the given sample rate.
116 * Returns zero for success, or a negative error code.
117 */
118int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
119 int dev_id, int rate)
120{
121 struct hdac_bus *bus = codec->bus;
122 struct drm_audio_component *acomp = bus->audio_component;
123 int port, pipe;
124
125 if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
126 return -ENODEV;
127 port = nid;
128 if (acomp->audio_ops && acomp->audio_ops->pin2port) {
129 port = acomp->audio_ops->pin2port(codec, nid);
130 if (port < 0)
131 return -EINVAL;
132 }
133 pipe = dev_id;
134 return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
135}
136EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
137
138/**
139 * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
140 * @codec: HDA codec
141 * @nid: the pin widget NID
142 * @dev_id: device identifier
143 * @audio_enabled: the pointer to store the current audio state
144 * @buffer: the buffer pointer to store ELD bytes
145 * @max_bytes: the max bytes to be stored on @buffer
146 *
147 * This function is supposed to be used only by a HD-audio controller
148 * driver that needs the interaction with graphics driver.
149 *
150 * This function queries the current state of the audio on the given
151 * digital port and fetches the ELD bytes onto the given buffer.
152 * It returns the number of bytes for the total ELD data, zero for
153 * invalid ELD, or a negative error code.
154 *
155 * The return size is the total bytes required for the whole ELD bytes,
156 * thus it may be over @max_bytes. If it's over @max_bytes, it implies
157 * that only a part of ELD bytes have been fetched.
158 */
159int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
160 bool *audio_enabled, char *buffer, int max_bytes)
161{
162 struct hdac_bus *bus = codec->bus;
163 struct drm_audio_component *acomp = bus->audio_component;
164 int port, pipe;
165
166 if (!acomp || !acomp->ops || !acomp->ops->get_eld)
167 return -ENODEV;
168
169 port = nid;
170 if (acomp->audio_ops && acomp->audio_ops->pin2port) {
171 port = acomp->audio_ops->pin2port(codec, nid);
172 if (port < 0)
173 return -EINVAL;
174 }
175 pipe = dev_id;
176 return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
177 buffer, max_bytes);
178}
179EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
180
181static int hdac_component_master_bind(struct device *dev)
182{
183 struct drm_audio_component *acomp = hdac_get_acomp(dev);
184 int ret;
185
186 if (WARN_ON(!acomp))
187 return -EINVAL;
188
189 ret = component_bind_all(parent: dev, data: acomp);
190 if (ret < 0)
191 return ret;
192
193 if (WARN_ON(!(acomp->dev && acomp->ops))) {
194 ret = -EINVAL;
195 goto out_unbind;
196 }
197
198 /* pin the module to avoid dynamic unbinding, but only if given */
199 if (!try_module_get(module: acomp->ops->owner)) {
200 ret = -ENODEV;
201 goto out_unbind;
202 }
203
204 if (acomp->audio_ops && acomp->audio_ops->master_bind) {
205 ret = acomp->audio_ops->master_bind(dev, acomp);
206 if (ret < 0)
207 goto module_put;
208 }
209
210 complete_all(&acomp->master_bind_complete);
211 return 0;
212
213 module_put:
214 module_put(module: acomp->ops->owner);
215out_unbind:
216 component_unbind_all(parent: dev, data: acomp);
217 complete_all(&acomp->master_bind_complete);
218
219 return ret;
220}
221
222static void hdac_component_master_unbind(struct device *dev)
223{
224 struct drm_audio_component *acomp = hdac_get_acomp(dev);
225
226 if (acomp->audio_ops && acomp->audio_ops->master_unbind)
227 acomp->audio_ops->master_unbind(dev, acomp);
228 module_put(module: acomp->ops->owner);
229 component_unbind_all(parent: dev, data: acomp);
230 WARN_ON(acomp->ops || acomp->dev);
231}
232
233static const struct component_master_ops hdac_component_master_ops = {
234 .bind = hdac_component_master_bind,
235 .unbind = hdac_component_master_unbind,
236};
237
238/**
239 * snd_hdac_acomp_register_notifier - Register audio component ops
240 * @bus: HDA core bus
241 * @aops: audio component ops
242 *
243 * This function is supposed to be used only by a HD-audio controller
244 * driver that needs the interaction with graphics driver.
245 *
246 * This function sets the given ops to be called by the graphics driver.
247 *
248 * Returns zero for success or a negative error code.
249 */
250int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
251 const struct drm_audio_component_audio_ops *aops)
252{
253 if (!bus->audio_component)
254 return -ENODEV;
255
256 bus->audio_component->audio_ops = aops;
257 return 0;
258}
259EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
260
261/**
262 * snd_hdac_acomp_init - Initialize audio component
263 * @bus: HDA core bus
264 * @aops: audio component ops
265 * @match_master: match function for finding components
266 * @extra_size: Extra bytes to allocate
267 *
268 * This function is supposed to be used only by a HD-audio controller
269 * driver that needs the interaction with graphics driver.
270 *
271 * This function initializes and sets up the audio component to communicate
272 * with graphics driver.
273 *
274 * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
275 * binding with the DRM component. Each caller needs to sync via master_bind
276 * audio_ops.
277 *
278 * Returns zero for success or a negative error code.
279 */
280int snd_hdac_acomp_init(struct hdac_bus *bus,
281 const struct drm_audio_component_audio_ops *aops,
282 int (*match_master)(struct device *, int, void *),
283 size_t extra_size)
284{
285 struct component_match *match = NULL;
286 struct device *dev = bus->dev;
287 struct drm_audio_component *acomp;
288 int ret;
289
290 if (WARN_ON(hdac_get_acomp(dev)))
291 return -EBUSY;
292
293 acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
294 GFP_KERNEL);
295 if (!acomp)
296 return -ENOMEM;
297 acomp->audio_ops = aops;
298 init_completion(x: &acomp->master_bind_complete);
299 bus->audio_component = acomp;
300 devres_add(dev, res: acomp);
301
302 component_match_add_typed(parent: dev, matchptr: &match, compare_typed: match_master, compare_data: bus);
303 ret = component_master_add_with_match(dev, &hdac_component_master_ops,
304 match);
305 if (ret < 0)
306 goto out_err;
307
308 return 0;
309
310out_err:
311 bus->audio_component = NULL;
312 devres_destroy(dev, release: hdac_acomp_release, NULL, NULL);
313 dev_info(dev, "failed to add audio component master (%d)\n", ret);
314
315 return ret;
316}
317EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
318
319/**
320 * snd_hdac_acomp_exit - Finalize audio component
321 * @bus: HDA core bus
322 *
323 * This function is supposed to be used only by a HD-audio controller
324 * driver that needs the interaction with graphics driver.
325 *
326 * This function releases the audio component that has been used.
327 *
328 * Returns zero for success or a negative error code.
329 */
330int snd_hdac_acomp_exit(struct hdac_bus *bus)
331{
332 struct device *dev = bus->dev;
333 struct drm_audio_component *acomp = bus->audio_component;
334
335 if (!acomp)
336 return 0;
337
338 if (WARN_ON(bus->display_power_active) && acomp->ops)
339 acomp->ops->put_power(acomp->dev, bus->display_power_active);
340
341 bus->display_power_active = 0;
342 bus->display_power_status = 0;
343
344 component_master_del(dev, &hdac_component_master_ops);
345
346 bus->audio_component = NULL;
347 devres_destroy(dev, release: hdac_acomp_release, NULL, NULL);
348
349 return 0;
350}
351EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
352