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)
40 CODEC_ATTR(vendor_id
);
41 CODEC_ATTR(subsystem_id
);
42 CODEC_ATTR(revision_id
);
45 CODEC_ATTR_STR(vendor_name
);
46 CODEC_ATTR_STR(chip_name
);
48 static ssize_t
modalias_show(struct device
*dev
, struct device_attribute
*attr
,
51 return snd_hdac_codec_modalias(dev_to_hdac_dev(dev
), buf
, 256);
53 static DEVICE_ATTR_RO(modalias
);
55 static struct attribute
*hdac_dev_attrs
[] = {
57 &dev_attr_vendor_id
.attr
,
58 &dev_attr_subsystem_id
.attr
,
59 &dev_attr_revision_id
.attr
,
62 &dev_attr_vendor_name
.attr
,
63 &dev_attr_chip_name
.attr
,
64 &dev_attr_modalias
.attr
,
68 static struct attribute_group hdac_dev_attr_group
= {
69 .attrs
= hdac_dev_attrs
,
72 const struct attribute_group
*hdac_dev_attr_groups
[] = {
80 * This is a tree showing the attributes of each widget. It appears like
81 * /sys/bus/hdaudioC0D0/widgets/04/caps
84 struct widget_attribute
;
86 struct widget_attribute
{
87 struct attribute attr
;
88 ssize_t (*show
)(struct hdac_device
*codec
, hda_nid_t nid
,
89 struct widget_attribute
*attr
, char *buf
);
90 ssize_t (*store
)(struct hdac_device
*codec
, hda_nid_t nid
,
91 struct widget_attribute
*attr
,
92 const char *buf
, size_t count
);
95 static int get_codec_nid(struct kobject
*kobj
, struct hdac_device
**codecp
)
97 struct device
*dev
= kobj_to_dev(kobj
->parent
->parent
);
101 ret
= kstrtoint(kobj
->name
, 16, &nid
);
104 *codecp
= dev_to_hdac_dev(dev
);
108 static ssize_t
widget_attr_show(struct kobject
*kobj
, struct attribute
*attr
,
111 struct widget_attribute
*wid_attr
=
112 container_of(attr
, struct widget_attribute
, attr
);
113 struct hdac_device
*codec
;
118 nid
= get_codec_nid(kobj
, &codec
);
121 return wid_attr
->show(codec
, nid
, wid_attr
, buf
);
124 static ssize_t
widget_attr_store(struct kobject
*kobj
, struct attribute
*attr
,
125 const char *buf
, size_t count
)
127 struct widget_attribute
*wid_attr
=
128 container_of(attr
, struct widget_attribute
, attr
);
129 struct hdac_device
*codec
;
132 if (!wid_attr
->store
)
134 nid
= get_codec_nid(kobj
, &codec
);
137 return wid_attr
->store(codec
, nid
, wid_attr
, buf
, count
);
140 static const struct sysfs_ops widget_sysfs_ops
= {
141 .show
= widget_attr_show
,
142 .store
= widget_attr_store
,
145 static void widget_release(struct kobject
*kobj
)
150 static struct kobj_type widget_ktype
= {
151 .release
= widget_release
,
152 .sysfs_ops
= &widget_sysfs_ops
,
155 #define WIDGET_ATTR_RO(_name) \
156 struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
157 #define WIDGET_ATTR_RW(_name) \
158 struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
160 static ssize_t
caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
161 struct widget_attribute
*attr
, char *buf
)
163 return sprintf(buf
, "0x%08x\n", get_wcaps(codec
, nid
));
166 static ssize_t
pin_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
167 struct widget_attribute
*attr
, char *buf
)
169 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
171 return sprintf(buf
, "0x%08x\n",
172 snd_hdac_read_parm(codec
, nid
, AC_PAR_PIN_CAP
));
175 static ssize_t
pin_cfg_show(struct hdac_device
*codec
, hda_nid_t nid
,
176 struct widget_attribute
*attr
, char *buf
)
180 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
182 if (snd_hdac_read(codec
, nid
, AC_VERB_GET_CONFIG_DEFAULT
, 0, &val
))
184 return sprintf(buf
, "0x%08x\n", val
);
187 static bool has_pcm_cap(struct hdac_device
*codec
, hda_nid_t nid
)
189 if (nid
== codec
->afg
|| nid
== codec
->mfg
)
191 switch (get_wcaps_type(get_wcaps(codec
, nid
))) {
200 static ssize_t
pcm_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
201 struct widget_attribute
*attr
, char *buf
)
203 if (!has_pcm_cap(codec
, nid
))
205 return sprintf(buf
, "0x%08x\n",
206 snd_hdac_read_parm(codec
, nid
, AC_PAR_PCM
));
209 static ssize_t
pcm_formats_show(struct hdac_device
*codec
, hda_nid_t nid
,
210 struct widget_attribute
*attr
, char *buf
)
212 if (!has_pcm_cap(codec
, nid
))
214 return sprintf(buf
, "0x%08x\n",
215 snd_hdac_read_parm(codec
, nid
, AC_PAR_STREAM
));
218 static ssize_t
amp_in_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
219 struct widget_attribute
*attr
, char *buf
)
221 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_IN_AMP
))
223 return sprintf(buf
, "0x%08x\n",
224 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_IN_CAP
));
227 static ssize_t
amp_out_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
228 struct widget_attribute
*attr
, char *buf
)
230 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_OUT_AMP
))
232 return sprintf(buf
, "0x%08x\n",
233 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_OUT_CAP
));
236 static ssize_t
power_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
237 struct widget_attribute
*attr
, char *buf
)
239 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_POWER
))
241 return sprintf(buf
, "0x%08x\n",
242 snd_hdac_read_parm(codec
, nid
, AC_PAR_POWER_STATE
));
245 static ssize_t
gpio_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
246 struct widget_attribute
*attr
, char *buf
)
248 return sprintf(buf
, "0x%08x\n",
249 snd_hdac_read_parm(codec
, nid
, AC_PAR_GPIO_CAP
));
252 static ssize_t
connections_show(struct hdac_device
*codec
, hda_nid_t nid
,
253 struct widget_attribute
*attr
, char *buf
)
259 nconns
= snd_hdac_get_connections(codec
, nid
, list
, ARRAY_SIZE(list
));
262 for (i
= 0; i
< nconns
; i
++)
263 ret
+= sprintf(buf
+ ret
, "%s0x%02x", i
? " " : "", list
[i
]);
264 ret
+= sprintf(buf
+ ret
, "\n");
268 static WIDGET_ATTR_RO(caps
);
269 static WIDGET_ATTR_RO(pin_caps
);
270 static WIDGET_ATTR_RO(pin_cfg
);
271 static WIDGET_ATTR_RO(pcm_caps
);
272 static WIDGET_ATTR_RO(pcm_formats
);
273 static WIDGET_ATTR_RO(amp_in_caps
);
274 static WIDGET_ATTR_RO(amp_out_caps
);
275 static WIDGET_ATTR_RO(power_caps
);
276 static WIDGET_ATTR_RO(gpio_caps
);
277 static WIDGET_ATTR_RO(connections
);
279 static struct attribute
*widget_node_attrs
[] = {
281 &wid_attr_pin_caps
.attr
,
282 &wid_attr_pin_cfg
.attr
,
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_connections
.attr
,
292 static struct attribute
*widget_afg_attrs
[] = {
293 &wid_attr_pcm_caps
.attr
,
294 &wid_attr_pcm_formats
.attr
,
295 &wid_attr_amp_in_caps
.attr
,
296 &wid_attr_amp_out_caps
.attr
,
297 &wid_attr_power_caps
.attr
,
298 &wid_attr_gpio_caps
.attr
,
302 static const struct attribute_group widget_node_group
= {
303 .attrs
= widget_node_attrs
,
306 static const struct attribute_group widget_afg_group
= {
307 .attrs
= widget_afg_attrs
,
310 static void free_widget_node(struct kobject
*kobj
,
311 const struct attribute_group
*group
)
314 sysfs_remove_group(kobj
, group
);
319 static void widget_tree_free(struct hdac_device
*codec
)
321 struct hdac_widget_tree
*tree
= codec
->widgets
;
326 free_widget_node(tree
->afg
, &widget_afg_group
);
328 for (p
= tree
->nodes
; *p
; p
++)
329 free_widget_node(*p
, &widget_node_group
);
332 kobject_put(tree
->root
);
334 codec
->widgets
= NULL
;
337 static int add_widget_node(struct kobject
*parent
, hda_nid_t nid
,
338 const struct attribute_group
*group
,
339 struct kobject
**res
)
341 struct kobject
*kobj
= kzalloc(sizeof(*kobj
), GFP_KERNEL
);
346 kobject_init(kobj
, &widget_ktype
);
347 err
= kobject_add(kobj
, parent
, "%02x", nid
);
350 err
= sysfs_create_group(kobj
, group
);
360 static int widget_tree_create(struct hdac_device
*codec
)
362 struct hdac_widget_tree
*tree
;
366 tree
= codec
->widgets
= kzalloc(sizeof(*tree
), GFP_KERNEL
);
370 tree
->root
= kobject_create_and_add("widgets", &codec
->dev
.kobj
);
374 tree
->nodes
= kcalloc(codec
->num_nodes
+ 1, sizeof(*tree
->nodes
),
379 for (i
= 0, nid
= codec
->start_nid
; i
< codec
->num_nodes
; i
++, nid
++) {
380 err
= add_widget_node(tree
->root
, nid
, &widget_node_group
,
387 err
= add_widget_node(tree
->root
, codec
->afg
,
388 &widget_afg_group
, &tree
->afg
);
393 kobject_uevent(tree
->root
, KOBJ_CHANGE
);
397 int hda_widget_sysfs_init(struct hdac_device
*codec
)
402 return 0; /* already created */
404 err
= widget_tree_create(codec
);
406 widget_tree_free(codec
);
413 void hda_widget_sysfs_exit(struct hdac_device
*codec
)
415 widget_tree_free(codec
);