Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/core/component.c
26424 views
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, 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(idx, &bus->display_power_status);
75
else
76
clear_bit(idx, &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(&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(dev, 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(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(acomp->ops->owner);
217
out_unbind:
218
component_unbind_all(dev, 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(acomp->ops->owner);
231
component_unbind_all(dev, 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 extra_size)
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(&acomp->master_bind_complete);
301
bus->audio_component = acomp;
302
devres_add(dev, acomp);
303
304
component_match_add_typed(dev, &match, match_master, 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, 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, hdac_acomp_release, NULL, NULL);
350
351
return 0;
352
}
353
EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
354
355