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 struct attribute
*hdac_dev_attrs
[] = {
50 &dev_attr_vendor_id
.attr
,
51 &dev_attr_subsystem_id
.attr
,
52 &dev_attr_revision_id
.attr
,
55 &dev_attr_vendor_name
.attr
,
56 &dev_attr_chip_name
.attr
,
60 static struct attribute_group hdac_dev_attr_group
= {
61 .attrs
= hdac_dev_attrs
,
64 const struct attribute_group
*hdac_dev_attr_groups
[] = {
72 * This is a tree showing the attributes of each widget. It appears like
73 * /sys/bus/hdaudioC0D0/widgets/04/caps
76 struct widget_attribute
;
78 struct widget_attribute
{
79 struct attribute attr
;
80 ssize_t (*show
)(struct hdac_device
*codec
, hda_nid_t nid
,
81 struct widget_attribute
*attr
, char *buf
);
82 ssize_t (*store
)(struct hdac_device
*codec
, hda_nid_t nid
,
83 struct widget_attribute
*attr
,
84 const char *buf
, size_t count
);
87 static int get_codec_nid(struct kobject
*kobj
, struct hdac_device
**codecp
)
89 struct device
*dev
= kobj_to_dev(kobj
->parent
->parent
);
93 ret
= kstrtoint(kobj
->name
, 16, &nid
);
96 *codecp
= dev_to_hdac_dev(dev
);
100 static ssize_t
widget_attr_show(struct kobject
*kobj
, struct attribute
*attr
,
103 struct widget_attribute
*wid_attr
=
104 container_of(attr
, struct widget_attribute
, attr
);
105 struct hdac_device
*codec
;
110 nid
= get_codec_nid(kobj
, &codec
);
113 return wid_attr
->show(codec
, nid
, wid_attr
, buf
);
116 static ssize_t
widget_attr_store(struct kobject
*kobj
, struct attribute
*attr
,
117 const char *buf
, size_t count
)
119 struct widget_attribute
*wid_attr
=
120 container_of(attr
, struct widget_attribute
, attr
);
121 struct hdac_device
*codec
;
124 if (!wid_attr
->store
)
126 nid
= get_codec_nid(kobj
, &codec
);
129 return wid_attr
->store(codec
, nid
, wid_attr
, buf
, count
);
132 static const struct sysfs_ops widget_sysfs_ops
= {
133 .show
= widget_attr_show
,
134 .store
= widget_attr_store
,
137 static void widget_release(struct kobject
*kobj
)
142 static struct kobj_type widget_ktype
= {
143 .release
= widget_release
,
144 .sysfs_ops
= &widget_sysfs_ops
,
147 #define WIDGET_ATTR_RO(_name) \
148 struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
149 #define WIDGET_ATTR_RW(_name) \
150 struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
152 static ssize_t
caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
153 struct widget_attribute
*attr
, char *buf
)
155 return sprintf(buf
, "0x%08x\n", get_wcaps(codec
, nid
));
158 static ssize_t
pin_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
159 struct widget_attribute
*attr
, char *buf
)
161 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
163 return sprintf(buf
, "0x%08x\n",
164 snd_hdac_read_parm(codec
, nid
, AC_PAR_PIN_CAP
));
167 static ssize_t
pin_cfg_show(struct hdac_device
*codec
, hda_nid_t nid
,
168 struct widget_attribute
*attr
, char *buf
)
172 if (get_wcaps_type(get_wcaps(codec
, nid
)) != AC_WID_PIN
)
174 if (snd_hdac_read(codec
, nid
, AC_VERB_GET_CONFIG_DEFAULT
, 0, &val
))
176 return sprintf(buf
, "0x%08x\n", val
);
179 static bool has_pcm_cap(struct hdac_device
*codec
, hda_nid_t nid
)
181 if (nid
== codec
->afg
|| nid
== codec
->mfg
)
183 switch (get_wcaps_type(get_wcaps(codec
, nid
))) {
192 static ssize_t
pcm_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
193 struct widget_attribute
*attr
, char *buf
)
195 if (!has_pcm_cap(codec
, nid
))
197 return sprintf(buf
, "0x%08x\n",
198 snd_hdac_read_parm(codec
, nid
, AC_PAR_PCM
));
201 static ssize_t
pcm_formats_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_STREAM
));
210 static ssize_t
amp_in_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
211 struct widget_attribute
*attr
, char *buf
)
213 if (nid
!= codec
->afg
&& !(get_wcaps(codec
, nid
) & AC_WCAP_IN_AMP
))
215 return sprintf(buf
, "0x%08x\n",
216 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_IN_CAP
));
219 static ssize_t
amp_out_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_OUT_AMP
))
224 return sprintf(buf
, "0x%08x\n",
225 snd_hdac_read_parm(codec
, nid
, AC_PAR_AMP_OUT_CAP
));
228 static ssize_t
power_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_POWER
))
233 return sprintf(buf
, "0x%08x\n",
234 snd_hdac_read_parm(codec
, nid
, AC_PAR_POWER_STATE
));
237 static ssize_t
gpio_caps_show(struct hdac_device
*codec
, hda_nid_t nid
,
238 struct widget_attribute
*attr
, char *buf
)
240 return sprintf(buf
, "0x%08x\n",
241 snd_hdac_read_parm(codec
, nid
, AC_PAR_GPIO_CAP
));
244 static ssize_t
connections_show(struct hdac_device
*codec
, hda_nid_t nid
,
245 struct widget_attribute
*attr
, char *buf
)
251 nconns
= snd_hdac_get_connections(codec
, nid
, list
, ARRAY_SIZE(list
));
254 for (i
= 0; i
< nconns
; i
++)
255 ret
+= sprintf(buf
+ ret
, "%s0x%02x", i
? " " : "", list
[i
]);
256 ret
+= sprintf(buf
+ ret
, "\n");
260 static WIDGET_ATTR_RO(caps
);
261 static WIDGET_ATTR_RO(pin_caps
);
262 static WIDGET_ATTR_RO(pin_cfg
);
263 static WIDGET_ATTR_RO(pcm_caps
);
264 static WIDGET_ATTR_RO(pcm_formats
);
265 static WIDGET_ATTR_RO(amp_in_caps
);
266 static WIDGET_ATTR_RO(amp_out_caps
);
267 static WIDGET_ATTR_RO(power_caps
);
268 static WIDGET_ATTR_RO(gpio_caps
);
269 static WIDGET_ATTR_RO(connections
);
271 static struct attribute
*widget_node_attrs
[] = {
273 &wid_attr_pin_caps
.attr
,
274 &wid_attr_pin_cfg
.attr
,
275 &wid_attr_pcm_caps
.attr
,
276 &wid_attr_pcm_formats
.attr
,
277 &wid_attr_amp_in_caps
.attr
,
278 &wid_attr_amp_out_caps
.attr
,
279 &wid_attr_power_caps
.attr
,
280 &wid_attr_connections
.attr
,
284 static struct attribute
*widget_afg_attrs
[] = {
285 &wid_attr_pcm_caps
.attr
,
286 &wid_attr_pcm_formats
.attr
,
287 &wid_attr_amp_in_caps
.attr
,
288 &wid_attr_amp_out_caps
.attr
,
289 &wid_attr_power_caps
.attr
,
290 &wid_attr_gpio_caps
.attr
,
294 static const struct attribute_group widget_node_group
= {
295 .attrs
= widget_node_attrs
,
298 static const struct attribute_group widget_afg_group
= {
299 .attrs
= widget_afg_attrs
,
302 static void free_widget_node(struct kobject
*kobj
,
303 const struct attribute_group
*group
)
306 sysfs_remove_group(kobj
, group
);
311 static void widget_tree_free(struct hdac_device
*codec
)
313 struct hdac_widget_tree
*tree
= codec
->widgets
;
318 free_widget_node(tree
->afg
, &widget_afg_group
);
320 for (p
= tree
->nodes
; *p
; p
++)
321 free_widget_node(*p
, &widget_node_group
);
324 kobject_put(tree
->root
);
326 codec
->widgets
= NULL
;
329 static int add_widget_node(struct kobject
*parent
, hda_nid_t nid
,
330 const struct attribute_group
*group
,
331 struct kobject
**res
)
333 struct kobject
*kobj
= kzalloc(sizeof(*kobj
), GFP_KERNEL
);
338 kobject_init(kobj
, &widget_ktype
);
339 err
= kobject_add(kobj
, parent
, "%02x", nid
);
342 err
= sysfs_create_group(kobj
, group
);
352 static int widget_tree_create(struct hdac_device
*codec
)
354 struct hdac_widget_tree
*tree
;
358 tree
= codec
->widgets
= kzalloc(sizeof(*tree
), GFP_KERNEL
);
362 tree
->root
= kobject_create_and_add("widgets", &codec
->dev
.kobj
);
366 tree
->nodes
= kcalloc(codec
->num_nodes
+ 1, sizeof(*tree
->nodes
),
371 for (i
= 0, nid
= codec
->start_nid
; i
< codec
->num_nodes
; i
++, nid
++) {
372 err
= add_widget_node(tree
->root
, nid
, &widget_node_group
,
379 err
= add_widget_node(tree
->root
, codec
->afg
,
380 &widget_afg_group
, &tree
->afg
);
385 kobject_uevent(tree
->root
, KOBJ_CHANGE
);
389 int hda_widget_sysfs_init(struct hdac_device
*codec
)
394 return 0; /* already created */
396 err
= widget_tree_create(codec
);
398 widget_tree_free(codec
);
405 void hda_widget_sysfs_exit(struct hdac_device
*codec
)
407 widget_tree_free(codec
);