1 // SPDX-License-Identifier: GPL-2.0
3 * sysfs support for HD-audio core device
6 #include <linux/slab.h>
7 #include <linux/sysfs.h>
8 #include <linux/device.h>
9 #include <sound/core.h>
10 #include <sound/hdaudio.h>
13 struct hdac_widget_tree
{
16 struct kobject
**nodes
;
19 #define CODEC_ATTR(type) \
20 static ssize_t type##_show(struct device *dev, \
21 struct device_attribute *attr, \
24 struct hdac_device *codec = dev_to_hdac_dev(dev); \
25 return sprintf(buf, "0x%x\n", codec->type); \
27 static DEVICE_ATTR_RO(type)
29 #define CODEC_ATTR_STR(type) \
30 static ssize_t type##_show(struct device *dev, \
31 struct device_attribute *attr, \
34 struct hdac_device *codec = dev_to_hdac_dev(dev); \
35 return sprintf(buf, "%s\n", \
36 codec->type ? codec->type : ""); \
38 static DEVICE_ATTR_RO(type)
41 CODEC_ATTR(vendor_id
);
42 CODEC_ATTR(subsystem_id
);
43 CODEC_ATTR(revision_id
);
46 CODEC_ATTR_STR(vendor_name
);
47 CODEC_ATTR_STR(chip_name
);
49 static ssize_t
modalias_show(struct device
*dev
, struct device_attribute
*attr
,
52 return snd_hdac_codec_modalias(dev_to_hdac_dev(dev
), buf
, 256);
54 static DEVICE_ATTR_RO(modalias
);
56 static struct attribute
*hdac_dev_attrs
[] = {
58 &dev_attr_vendor_id
.attr
,
59 &dev_attr_subsystem_id
.attr
,
60 &dev_attr_revision_id
.attr
,
63 &dev_attr_vendor_name
.attr
,
64 &dev_attr_chip_name
.attr
,
65 &dev_attr_modalias
.attr
,
69 static struct attribute_group hdac_dev_attr_group
= {
70 .attrs
= hdac_dev_attrs
,
73 const struct attribute_group
*hdac_dev_attr_groups
[] = {
81 * This is a tree showing the attributes of each widget. It appears like
82 * /sys/bus/hdaudioC0D0/widgets/04/caps
85 struct widget_attribute
;
87 struct widget_attribute
{
88 struct attribute attr
;
89 ssize_t (*show
)(struct hdac_device
*codec
, hda_nid_t nid
,
90 struct widget_attribute
*attr
, char *buf
);
91 ssize_t (*store
)(struct hdac_device
*codec
, hda_nid_t nid
,
92 struct widget_attribute
*attr
,
93 const char *buf
, size_t count
);
96 static int get_codec_nid(struct kobject
*kobj
, struct hdac_device
**codecp
)
98 struct device
*dev
= kobj_to_dev(kobj
->parent
->parent
);
102 ret
= kstrtoint(kobj
->name
, 16, &nid
);
105 *codecp
= dev_to_hdac_dev(dev
);
109 static ssize_t
widget_attr_show(struct kobject
*kobj
, struct attribute
*attr
,
112 struct widget_attribute
*wid_attr
=
113 container_of(attr
, struct widget_attribute
, attr
);
114 struct hdac_device
*codec
;
119 nid
= get_codec_nid(kobj
, &codec
);
122 return wid_attr
->show(codec
, nid
, wid_attr
, buf
);
125 static ssize_t
widget_attr_store(struct kobject
*kobj
, struct attribute
*attr
,
126 const char *buf
, size_t count
)
128 struct widget_attribute
*wid_attr
=
129 container_of(attr
, struct widget_attribute
, attr
);
130 struct hdac_device
*codec
;
133 if (!wid_attr
->store
)
135 nid
= get_codec_nid(kobj
, &codec
);
138 return wid_attr
->store(codec
, nid
, wid_attr
, buf
, count
);
141 static const struct sysfs_ops widget_sysfs_ops
= {
142 .show
= widget_attr_show
,
143 .store
= widget_attr_store
,
146 static void widget_release(struct kobject
*kobj
)
151 static struct kobj_type widget_ktype
= {
152 .release
= widget_release
,
153 .sysfs_ops
= &widget_sysfs_ops
,
156 #define WIDGET_ATTR_RO(_name) \
157 struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
158 #define WIDGET_ATTR_RW(_name) \
159 struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
161 static ssize_t
caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
162 struct widget_attribute
*attr
, char *buf
)
164 return sprintf(buf
, "0x%08x\n", get_wcaps(codec
, nid
));
167 static ssize_t
pin_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
168 struct widget_attribute
*attr
, char *buf
)
170 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
172 return sprintf(buf
, "0x%08x\n",
173 snd_hdac_read_parm(codec
, nid
, AC_PAR_PIN_CAP
));
176 static ssize_t
pin_cfg_show(struct hdac_device
*codec
, hda_nid_t nid
,
177 struct widget_attribute
*attr
, char *buf
)
181 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
183 if (snd_hdac_read(codec
, nid
, AC_VERB_GET_CONFIG_DEFAULT
, 0, &val
))
185 return sprintf(buf
, "0x%08x\n", val
);
188 static bool has_pcm_cap(struct hdac_device
*codec
, hda_nid_t nid
)
190 if (nid
== codec
->afg
|| nid
== codec
->mfg
)
192 switch (get_wcaps_type(get_wcaps(codec
, nid
))) {
201 static ssize_t
pcm_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
202 struct widget_attribute
*attr
, char *buf
)
204 if (!has_pcm_cap(codec
, nid
))
206 return sprintf(buf
, "0x%08x\n",
207 snd_hdac_read_parm(codec
, nid
, AC_PAR_PCM
));
210 static ssize_t
pcm_formats_show(struct hdac_device
*codec
, hda_nid_t nid
,
211 struct widget_attribute
*attr
, char *buf
)
213 if (!has_pcm_cap(codec
, nid
))
215 return sprintf(buf
, "0x%08x\n",
216 snd_hdac_read_parm(codec
, nid
, AC_PAR_STREAM
));
219 static ssize_t
amp_in_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
220 struct widget_attribute
*attr
, char *buf
)
222 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_IN_AMP
))
224 return sprintf(buf
, "0x%08x\n",
225 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_IN_CAP
));
228 static ssize_t
amp_out_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
229 struct widget_attribute
*attr
, char *buf
)
231 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_OUT_AMP
))
233 return sprintf(buf
, "0x%08x\n",
234 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_OUT_CAP
));
237 static ssize_t
power_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
238 struct widget_attribute
*attr
, char *buf
)
240 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_POWER
))
242 return sprintf(buf
, "0x%08x\n",
243 snd_hdac_read_parm(codec
, nid
, AC_PAR_POWER_STATE
));
246 static ssize_t
gpio_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
247 struct widget_attribute
*attr
, char *buf
)
249 return sprintf(buf
, "0x%08x\n",
250 snd_hdac_read_parm(codec
, nid
, AC_PAR_GPIO_CAP
));
253 static ssize_t
connections_show(struct hdac_device
*codec
, hda_nid_t nid
,
254 struct widget_attribute
*attr
, char *buf
)
260 nconns
= snd_hdac_get_connections(codec
, nid
, list
, ARRAY_SIZE(list
));
263 for (i
= 0; i
< nconns
; i
++)
264 ret
+= sprintf(buf
+ ret
, "%s0x%02x", i
? " " : "", list
[i
]);
265 ret
+= sprintf(buf
+ ret
, "\n");
269 static WIDGET_ATTR_RO(caps
);
270 static WIDGET_ATTR_RO(pin_caps
);
271 static WIDGET_ATTR_RO(pin_cfg
);
272 static WIDGET_ATTR_RO(pcm_caps
);
273 static WIDGET_ATTR_RO(pcm_formats
);
274 static WIDGET_ATTR_RO(amp_in_caps
);
275 static WIDGET_ATTR_RO(amp_out_caps
);
276 static WIDGET_ATTR_RO(power_caps
);
277 static WIDGET_ATTR_RO(gpio_caps
);
278 static WIDGET_ATTR_RO(connections
);
280 static struct attribute
*widget_node_attrs
[] = {
282 &wid_attr_pin_caps
.attr
,
283 &wid_attr_pin_cfg
.attr
,
284 &wid_attr_pcm_caps
.attr
,
285 &wid_attr_pcm_formats
.attr
,
286 &wid_attr_amp_in_caps
.attr
,
287 &wid_attr_amp_out_caps
.attr
,
288 &wid_attr_power_caps
.attr
,
289 &wid_attr_connections
.attr
,
293 static struct attribute
*widget_afg_attrs
[] = {
294 &wid_attr_pcm_caps
.attr
,
295 &wid_attr_pcm_formats
.attr
,
296 &wid_attr_amp_in_caps
.attr
,
297 &wid_attr_amp_out_caps
.attr
,
298 &wid_attr_power_caps
.attr
,
299 &wid_attr_gpio_caps
.attr
,
303 static const struct attribute_group widget_node_group
= {
304 .attrs
= widget_node_attrs
,
307 static const struct attribute_group widget_afg_group
= {
308 .attrs
= widget_afg_attrs
,
311 static void free_widget_node(struct kobject
*kobj
,
312 const struct attribute_group
*group
)
315 sysfs_remove_group(kobj
, group
);
320 static void widget_tree_free(struct hdac_device
*codec
)
322 struct hdac_widget_tree
*tree
= codec
->widgets
;
327 free_widget_node(tree
->afg
, &widget_afg_group
);
329 for (p
= tree
->nodes
; *p
; p
++)
330 free_widget_node(*p
, &widget_node_group
);
333 kobject_put(tree
->root
);
335 codec
->widgets
= NULL
;
338 static int add_widget_node(struct kobject
*parent
, hda_nid_t nid
,
339 const struct attribute_group
*group
,
340 struct kobject
**res
)
342 struct kobject
*kobj
= kzalloc(sizeof(*kobj
), GFP_KERNEL
);
347 kobject_init(kobj
, &widget_ktype
);
348 err
= kobject_add(kobj
, parent
, "%02x", nid
);
351 err
= sysfs_create_group(kobj
, group
);
361 static int widget_tree_create(struct hdac_device
*codec
)
363 struct hdac_widget_tree
*tree
;
367 tree
= codec
->widgets
= kzalloc(sizeof(*tree
), GFP_KERNEL
);
371 tree
->root
= kobject_create_and_add("widgets", &codec
->dev
.kobj
);
375 tree
->nodes
= kcalloc(codec
->num_nodes
+ 1, sizeof(*tree
->nodes
),
380 for (i
= 0, nid
= codec
->start_nid
; i
< codec
->num_nodes
; i
++, nid
++) {
381 err
= add_widget_node(tree
->root
, nid
, &widget_node_group
,
388 err
= add_widget_node(tree
->root
, codec
->afg
,
389 &widget_afg_group
, &tree
->afg
);
394 kobject_uevent(tree
->root
, KOBJ_CHANGE
);
398 int hda_widget_sysfs_init(struct hdac_device
*codec
)
403 return 0; /* already created */
405 err
= widget_tree_create(codec
);
407 widget_tree_free(codec
);
414 void hda_widget_sysfs_exit(struct hdac_device
*codec
)
416 widget_tree_free(codec
);
419 int hda_widget_sysfs_reinit(struct hdac_device
*codec
,
420 hda_nid_t start_nid
, int num_nodes
)
422 struct hdac_widget_tree
*tree
;
423 hda_nid_t end_nid
= start_nid
+ num_nodes
;
428 return hda_widget_sysfs_init(codec
);
430 tree
= kmemdup(codec
->widgets
, sizeof(*tree
), GFP_KERNEL
);
434 tree
->nodes
= kcalloc(num_nodes
+ 1, sizeof(*tree
->nodes
), GFP_KERNEL
);
440 /* prune non-existing nodes */
441 for (i
= 0, nid
= codec
->start_nid
; i
< codec
->num_nodes
; i
++, nid
++) {
442 if (nid
< start_nid
|| nid
>= end_nid
)
443 free_widget_node(codec
->widgets
->nodes
[i
],
448 for (i
= 0, nid
= start_nid
; i
< num_nodes
; i
++, nid
++) {
449 if (nid
< codec
->start_nid
|| nid
>= codec
->end_nid
)
450 add_widget_node(tree
->root
, nid
, &widget_node_group
,
454 codec
->widgets
->nodes
[nid
- codec
->start_nid
];
457 /* replace with the new tree */
458 kfree(codec
->widgets
->nodes
);
459 kfree(codec
->widgets
);
460 codec
->widgets
= tree
;
462 kobject_uevent(tree
->root
, KOBJ_CHANGE
);