File Index
Files and directories
Sign in
FILE

component.c

File preview with syntax highlighting.

linux-6.19.14/sound/hda/core/component.c C
  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, 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)(&bus->lock);
 73	if (enable)
 74		set_bit(idx, &bus->display_power_status);
 75	else
 76		clear_bit(idx, &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(dev, 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(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(acomp->ops->owner);
215out_unbind:
216	component_unbind_all(dev, 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(acomp->ops->owner);
229	component_unbind_all(dev, 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(&acomp->master_bind_complete);
299	bus->audio_component = acomp;
300	devres_add(dev, acomp);
301
302	component_match_add_typed(dev, &match, match_master, 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, 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, hdac_acomp_release, NULL, NULL);
348
349	return 0;
350}
351EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
~/ Read-only file preview.