2 * sysfs support for HD-audio core device
5 #include <linux/slab.h>
6 #include <linux/sysfs.h>
7 #include <linux/device.h>
8 #include <sound/core.h>
9 #include <sound/hdaudio.h>
12 struct hdac_widget_tree
{
15 struct kobject
**nodes
;
18 #define CODEC_ATTR(type) \
19 static ssize_t type##_show(struct device *dev, \
20 struct device_attribute *attr, \
23 struct hdac_device *codec = dev_to_hdac_dev(dev); \
24 return sprintf(buf, "0x%x\n", codec->type); \
26 static DEVICE_ATTR_RO(type)
28 #define CODEC_ATTR_STR(type) \
29 static ssize_t type##_show(struct device *dev, \
30 struct device_attribute *attr, \
33 struct hdac_device *codec = dev_to_hdac_dev(dev); \
34 return sprintf(buf, "%s\n", \
35 codec->type ? codec->type : ""); \
37 static DEVICE_ATTR_RO(type)
39 CODEC_ATTR(vendor_id
);
40 CODEC_ATTR(subsystem_id
);
41 CODEC_ATTR(revision_id
);
44 CODEC_ATTR_STR(vendor_name
);
45 CODEC_ATTR_STR(chip_name
);
47 static struct attribute
*hdac_dev_attrs
[] = {
48 &dev_attr_vendor_id
.attr
,
49 &dev_attr_subsystem_id
.attr
,
50 &dev_attr_revision_id
.attr
,
53 &dev_attr_vendor_name
.attr
,
54 &dev_attr_chip_name
.attr
,
58 static struct attribute_group hdac_dev_attr_group
= {
59 .attrs
= hdac_dev_attrs
,
62 const struct attribute_group
*hdac_dev_attr_groups
[] = {
70 * This is a tree showing the attributes of each widget. It appears like
71 * /sys/bus/hdaudioC0D0/widgets/04/caps
74 struct widget_attribute
;
76 struct widget_attribute
{
77 struct attribute attr
;
78 ssize_t (*show
)(struct hdac_device
*codec
, hda_nid_t nid
,
79 struct widget_attribute
*attr
, char *buf
);
80 ssize_t (*store
)(struct hdac_device
*codec
, hda_nid_t nid
,
81 struct widget_attribute
*attr
,
82 const char *buf
, size_t count
);
85 static int get_codec_nid(struct kobject
*kobj
, struct hdac_device
**codecp
)
87 struct device
*dev
= kobj_to_dev(kobj
->parent
->parent
);
91 ret
= kstrtoint(kobj
->name
, 16, &nid
);
94 *codecp
= dev_to_hdac_dev(dev
);
98 static ssize_t
widget_attr_show(struct kobject
*kobj
, struct attribute
*attr
,
101 struct widget_attribute
*wid_attr
=
102 container_of(attr
, struct widget_attribute
, attr
);
103 struct hdac_device
*codec
;
108 nid
= get_codec_nid(kobj
, &codec
);
111 return wid_attr
->show(codec
, nid
, wid_attr
, buf
);
114 static ssize_t
widget_attr_store(struct kobject
*kobj
, struct attribute
*attr
,
115 const char *buf
, size_t count
)
117 struct widget_attribute
*wid_attr
=
118 container_of(attr
, struct widget_attribute
, attr
);
119 struct hdac_device
*codec
;
122 if (!wid_attr
->store
)
124 nid
= get_codec_nid(kobj
, &codec
);
127 return wid_attr
->store(codec
, nid
, wid_attr
, buf
, count
);
130 static const struct sysfs_ops widget_sysfs_ops
= {
131 .show
= widget_attr_show
,
132 .store
= widget_attr_store
,
135 static void widget_release(struct kobject
*kobj
)
140 static struct kobj_type widget_ktype
= {
141 .release
= widget_release
,
142 .sysfs_ops
= &widget_sysfs_ops
,
145 #define WIDGET_ATTR_RO(_name) \
146 struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
147 #define WIDGET_ATTR_RW(_name) \
148 struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
150 static ssize_t
caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
151 struct widget_attribute
*attr
, char *buf
)
153 return sprintf(buf
, "0x%08x\n", get_wcaps(codec
, nid
));
156 static ssize_t
pin_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
157 struct widget_attribute
*attr
, char *buf
)
159 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
161 return sprintf(buf
, "0x%08x\n",
162 snd_hdac_read_parm(codec
, nid
, AC_PAR_PIN_CAP
));
165 static ssize_t
pin_cfg_show(struct hdac_device
*codec
, hda_nid_t nid
,
166 struct widget_attribute
*attr
, char *buf
)
170 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
172 if (snd_hdac_read(codec
, nid
, AC_VERB_GET_CONFIG_DEFAULT
, 0, &val
))
174 return sprintf(buf
, "0x%08x\n", val
);
177 static bool has_pcm_cap(struct hdac_device
*codec
, hda_nid_t nid
)
179 if (nid
== codec
->afg
|| nid
== codec
->mfg
)
181 switch (get_wcaps_type(get_wcaps(codec
, nid
))) {
190 static ssize_t
pcm_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
191 struct widget_attribute
*attr
, char *buf
)
193 if (!has_pcm_cap(codec
, nid
))
195 return sprintf(buf
, "0x%08x\n",
196 snd_hdac_read_parm(codec
, nid
, AC_PAR_PCM
));
199 static ssize_t
pcm_formats_show(struct hdac_device
*codec
, hda_nid_t nid
,
200 struct widget_attribute
*attr
, char *buf
)
202 if (!has_pcm_cap(codec
, nid
))
204 return sprintf(buf
, "0x%08x\n",
205 snd_hdac_read_parm(codec
, nid
, AC_PAR_STREAM
));
208 static ssize_t
amp_in_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
209 struct widget_attribute
*attr
, char *buf
)
211 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_IN_AMP
))
213 return sprintf(buf
, "0x%08x\n",
214 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_IN_CAP
));
217 static ssize_t
amp_out_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
218 struct widget_attribute
*attr
, char *buf
)
220 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_OUT_AMP
))
222 return sprintf(buf
, "0x%08x\n",
223 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_OUT_CAP
));
226 static ssize_t
power_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
227 struct widget_attribute
*attr
, char *buf
)
229 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_POWER
))
231 return sprintf(buf
, "0x%08x\n",
232 snd_hdac_read_parm(codec
, nid
, AC_PAR_POWER_STATE
));
235 static ssize_t
gpio_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
236 struct widget_attribute
*attr
, char *buf
)
238 return sprintf(buf
, "0x%08x\n",
239 snd_hdac_read_parm(codec
, nid
, AC_PAR_GPIO_CAP
));
242 static ssize_t
connections_show(struct hdac_device
*codec
, hda_nid_t nid
,
243 struct widget_attribute
*attr
, char *buf
)
249 nconns
= snd_hdac_get_connections(codec
, nid
, list
, ARRAY_SIZE(list
));
252 for (i
= 0; i
< nconns
; i
++)
253 ret
+= sprintf(buf
+ ret
, "%s0x%02x", i
? " " : "", list
[i
]);
254 ret
+= sprintf(buf
+ ret
, "\n");
258 static WIDGET_ATTR_RO(caps
);
259 static WIDGET_ATTR_RO(pin_caps
);
260 static WIDGET_ATTR_RO(pin_cfg
);
261 static WIDGET_ATTR_RO(pcm_caps
);
262 static WIDGET_ATTR_RO(pcm_formats
);
263 static WIDGET_ATTR_RO(amp_in_caps
);
264 static WIDGET_ATTR_RO(amp_out_caps
);
265 static WIDGET_ATTR_RO(power_caps
);
266 static WIDGET_ATTR_RO(gpio_caps
);
267 static WIDGET_ATTR_RO(connections
);
269 static struct attribute
*widget_node_attrs
[] = {
271 &wid_attr_pin_caps
.attr
,
272 &wid_attr_pin_cfg
.attr
,
273 &wid_attr_pcm_caps
.attr
,
274 &wid_attr_pcm_formats
.attr
,
275 &wid_attr_amp_in_caps
.attr
,
276 &wid_attr_amp_out_caps
.attr
,
277 &wid_attr_power_caps
.attr
,
278 &wid_attr_connections
.attr
,
282 static struct attribute
*widget_afg_attrs
[] = {
283 &wid_attr_pcm_caps
.attr
,
284 &wid_attr_pcm_formats
.attr
,
285 &wid_attr_amp_in_caps
.attr
,
286 &wid_attr_amp_out_caps
.attr
,
287 &wid_attr_power_caps
.attr
,
288 &wid_attr_gpio_caps
.attr
,
292 static const struct attribute_group widget_node_group
= {
293 .attrs
= widget_node_attrs
,
296 static const struct attribute_group widget_afg_group
= {
297 .attrs
= widget_afg_attrs
,
300 static void free_widget_node(struct kobject
*kobj
,
301 const struct attribute_group
*group
)
304 sysfs_remove_group(kobj
, group
);
309 static void widget_tree_free(struct hdac_device
*codec
)
311 struct hdac_widget_tree
*tree
= codec
->widgets
;
316 free_widget_node(tree
->afg
, &widget_afg_group
);
318 for (p
= tree
->nodes
; *p
; p
++)
319 free_widget_node(*p
, &widget_node_group
);
323 kobject_put(tree
->root
);
325 codec
->widgets
= NULL
;
328 static int add_widget_node(struct kobject
*parent
, hda_nid_t nid
,
329 const struct attribute_group
*group
,
330 struct kobject
**res
)
332 struct kobject
*kobj
= kzalloc(sizeof(*kobj
), GFP_KERNEL
);
337 kobject_init(kobj
, &widget_ktype
);
338 err
= kobject_add(kobj
, parent
, "%02x", nid
);
341 err
= sysfs_create_group(kobj
, group
);
351 static int widget_tree_create(struct hdac_device
*codec
)
353 struct hdac_widget_tree
*tree
;
357 tree
= codec
->widgets
= kzalloc(sizeof(*tree
), GFP_KERNEL
);
361 tree
->root
= kobject_create_and_add("widgets", &codec
->dev
.kobj
);
365 tree
->nodes
= kcalloc(codec
->num_nodes
+ 1, sizeof(*tree
->nodes
),
370 for (i
= 0, nid
= codec
->start_nid
; i
< codec
->num_nodes
; i
++, nid
++) {
371 err
= add_widget_node(tree
->root
, nid
, &widget_node_group
,
378 err
= add_widget_node(tree
->root
, codec
->afg
,
379 &widget_afg_group
, &tree
->afg
);
384 kobject_uevent(tree
->root
, KOBJ_CHANGE
);
388 int hda_widget_sysfs_init(struct hdac_device
*codec
)
392 err
= widget_tree_create(codec
);
394 widget_tree_free(codec
);
401 void hda_widget_sysfs_exit(struct hdac_device
*codec
)
403 widget_tree_free(codec
);