1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2648a8d27STakashi Iwai /*
3648a8d27STakashi Iwai * sysfs interface for HD-audio codec
4648a8d27STakashi Iwai *
5648a8d27STakashi Iwai * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
6648a8d27STakashi Iwai *
7648a8d27STakashi Iwai * split from hda_hwdep.c
8648a8d27STakashi Iwai */
9648a8d27STakashi Iwai
10648a8d27STakashi Iwai #include <linux/init.h>
11648a8d27STakashi Iwai #include <linux/slab.h>
12648a8d27STakashi Iwai #include <linux/compat.h>
13648a8d27STakashi Iwai #include <linux/mutex.h>
14648a8d27STakashi Iwai #include <linux/ctype.h>
15648a8d27STakashi Iwai #include <linux/string.h>
16648a8d27STakashi Iwai #include <linux/export.h>
17648a8d27STakashi Iwai #include <sound/core.h>
18be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h>
19648a8d27STakashi Iwai #include "hda_local.h"
20648a8d27STakashi Iwai #include <sound/hda_hwdep.h>
21648a8d27STakashi Iwai #include <sound/minors.h>
22648a8d27STakashi Iwai
23648a8d27STakashi Iwai /* hint string pair */
24648a8d27STakashi Iwai struct hda_hint {
25648a8d27STakashi Iwai const char *key;
26648a8d27STakashi Iwai const char *val; /* contained in the same alloc as key */
27648a8d27STakashi Iwai };
28648a8d27STakashi Iwai
29648a8d27STakashi Iwai #ifdef CONFIG_PM
power_on_acct_show(struct device * dev,struct device_attribute * attr,char * buf)30648a8d27STakashi Iwai static ssize_t power_on_acct_show(struct device *dev,
31648a8d27STakashi Iwai struct device_attribute *attr,
32648a8d27STakashi Iwai char *buf)
33648a8d27STakashi Iwai {
34648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
35648a8d27STakashi Iwai snd_hda_update_power_acct(codec);
36*17daae7aSTakashi Iwai return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
37648a8d27STakashi Iwai }
38648a8d27STakashi Iwai
power_off_acct_show(struct device * dev,struct device_attribute * attr,char * buf)39648a8d27STakashi Iwai static ssize_t power_off_acct_show(struct device *dev,
40648a8d27STakashi Iwai struct device_attribute *attr,
41648a8d27STakashi Iwai char *buf)
42648a8d27STakashi Iwai {
43648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
44648a8d27STakashi Iwai snd_hda_update_power_acct(codec);
45*17daae7aSTakashi Iwai return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
46648a8d27STakashi Iwai }
47648a8d27STakashi Iwai
48648a8d27STakashi Iwai static DEVICE_ATTR_RO(power_on_acct);
49648a8d27STakashi Iwai static DEVICE_ATTR_RO(power_off_acct);
50648a8d27STakashi Iwai #endif /* CONFIG_PM */
51648a8d27STakashi Iwai
527639a06cSTakashi Iwai #define CODEC_INFO_SHOW(type, field) \
53b989d044STakashi Iwai static ssize_t type##_show(struct device *dev, \
54b989d044STakashi Iwai struct device_attribute *attr, \
55b989d044STakashi Iwai char *buf) \
56b989d044STakashi Iwai { \
57b989d044STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev); \
58*17daae7aSTakashi Iwai return sysfs_emit(buf, "0x%x\n", codec->field); \
59b989d044STakashi Iwai }
60b989d044STakashi Iwai
617639a06cSTakashi Iwai #define CODEC_INFO_STR_SHOW(type, field) \
62b989d044STakashi Iwai static ssize_t type##_show(struct device *dev, \
63b989d044STakashi Iwai struct device_attribute *attr, \
64b989d044STakashi Iwai char *buf) \
65b989d044STakashi Iwai { \
66b989d044STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev); \
67*17daae7aSTakashi Iwai return sysfs_emit(buf, "%s\n", \
687639a06cSTakashi Iwai codec->field ? codec->field : ""); \
69b989d044STakashi Iwai }
70b989d044STakashi Iwai
717639a06cSTakashi Iwai CODEC_INFO_SHOW(vendor_id, core.vendor_id);
727639a06cSTakashi Iwai CODEC_INFO_SHOW(subsystem_id, core.subsystem_id);
737639a06cSTakashi Iwai CODEC_INFO_SHOW(revision_id, core.revision_id);
747639a06cSTakashi Iwai CODEC_INFO_SHOW(afg, core.afg);
757639a06cSTakashi Iwai CODEC_INFO_SHOW(mfg, core.mfg);
767639a06cSTakashi Iwai CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name);
777639a06cSTakashi Iwai CODEC_INFO_STR_SHOW(chip_name, core.chip_name);
787639a06cSTakashi Iwai CODEC_INFO_STR_SHOW(modelname, modelname);
79b989d044STakashi Iwai
pin_configs_show(struct hda_codec * codec,struct snd_array * list,char * buf)80b989d044STakashi Iwai static ssize_t pin_configs_show(struct hda_codec *codec,
81b989d044STakashi Iwai struct snd_array *list,
82b989d044STakashi Iwai char *buf)
83b989d044STakashi Iwai {
84a9c2dfc8STakashi Iwai const struct hda_pincfg *pin;
85b989d044STakashi Iwai int i, len = 0;
86b989d044STakashi Iwai mutex_lock(&codec->user_mutex);
87a9c2dfc8STakashi Iwai snd_array_for_each(list, i, pin) {
88*17daae7aSTakashi Iwai len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n",
89b989d044STakashi Iwai pin->nid, pin->cfg);
90b989d044STakashi Iwai }
91b989d044STakashi Iwai mutex_unlock(&codec->user_mutex);
92b989d044STakashi Iwai return len;
93b989d044STakashi Iwai }
94b989d044STakashi Iwai
init_pin_configs_show(struct device * dev,struct device_attribute * attr,char * buf)95b989d044STakashi Iwai static ssize_t init_pin_configs_show(struct device *dev,
96b989d044STakashi Iwai struct device_attribute *attr,
97b989d044STakashi Iwai char *buf)
98b989d044STakashi Iwai {
99b989d044STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
100b989d044STakashi Iwai return pin_configs_show(codec, &codec->init_pins, buf);
101b989d044STakashi Iwai }
102b989d044STakashi Iwai
driver_pin_configs_show(struct device * dev,struct device_attribute * attr,char * buf)103b989d044STakashi Iwai static ssize_t driver_pin_configs_show(struct device *dev,
104b989d044STakashi Iwai struct device_attribute *attr,
105b989d044STakashi Iwai char *buf)
106b989d044STakashi Iwai {
107b989d044STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
108b989d044STakashi Iwai return pin_configs_show(codec, &codec->driver_pins, buf);
109b989d044STakashi Iwai }
110b989d044STakashi Iwai
111648a8d27STakashi Iwai #ifdef CONFIG_SND_HDA_RECONFIG
112648a8d27STakashi Iwai
113648a8d27STakashi Iwai /*
114648a8d27STakashi Iwai * sysfs interface
115648a8d27STakashi Iwai */
116648a8d27STakashi Iwai
clear_codec(struct hda_codec * codec)117648a8d27STakashi Iwai static int clear_codec(struct hda_codec *codec)
118648a8d27STakashi Iwai {
119648a8d27STakashi Iwai int err;
120648a8d27STakashi Iwai
121648a8d27STakashi Iwai err = snd_hda_codec_reset(codec);
122648a8d27STakashi Iwai if (err < 0) {
1234e76a883STakashi Iwai codec_err(codec, "The codec is being used, can't free.\n");
124648a8d27STakashi Iwai return err;
125648a8d27STakashi Iwai }
126648a8d27STakashi Iwai snd_hda_sysfs_clear(codec);
127648a8d27STakashi Iwai return 0;
128648a8d27STakashi Iwai }
129648a8d27STakashi Iwai
reconfig_codec(struct hda_codec * codec)130648a8d27STakashi Iwai static int reconfig_codec(struct hda_codec *codec)
131648a8d27STakashi Iwai {
132648a8d27STakashi Iwai int err;
133648a8d27STakashi Iwai
134648a8d27STakashi Iwai snd_hda_power_up(codec);
1354e76a883STakashi Iwai codec_info(codec, "hda-codec: reconfiguring\n");
136648a8d27STakashi Iwai err = snd_hda_codec_reset(codec);
137648a8d27STakashi Iwai if (err < 0) {
1384e76a883STakashi Iwai codec_err(codec,
139648a8d27STakashi Iwai "The codec is being used, can't reconfigure.\n");
140648a8d27STakashi Iwai goto error;
141648a8d27STakashi Iwai }
1422506318eSTakashi Iwai err = device_reprobe(hda_codec_dev(codec));
143648a8d27STakashi Iwai if (err < 0)
144648a8d27STakashi Iwai goto error;
1456efdd851STakashi Iwai err = snd_card_register(codec->card);
146648a8d27STakashi Iwai error:
147648a8d27STakashi Iwai snd_hda_power_down(codec);
148648a8d27STakashi Iwai return err;
149648a8d27STakashi Iwai }
150648a8d27STakashi Iwai
151648a8d27STakashi Iwai /*
152648a8d27STakashi Iwai * allocate a string at most len chars, and remove the trailing EOL
153648a8d27STakashi Iwai */
kstrndup_noeol(const char * src,size_t len)154648a8d27STakashi Iwai static char *kstrndup_noeol(const char *src, size_t len)
155648a8d27STakashi Iwai {
156648a8d27STakashi Iwai char *s = kstrndup(src, len, GFP_KERNEL);
157648a8d27STakashi Iwai char *p;
158648a8d27STakashi Iwai if (!s)
159648a8d27STakashi Iwai return NULL;
160648a8d27STakashi Iwai p = strchr(s, '\n');
161648a8d27STakashi Iwai if (p)
162648a8d27STakashi Iwai *p = 0;
163648a8d27STakashi Iwai return s;
164648a8d27STakashi Iwai }
165648a8d27STakashi Iwai
1667639a06cSTakashi Iwai #define CODEC_INFO_STORE(type, field) \
167648a8d27STakashi Iwai static ssize_t type##_store(struct device *dev, \
168648a8d27STakashi Iwai struct device_attribute *attr, \
169648a8d27STakashi Iwai const char *buf, size_t count) \
170648a8d27STakashi Iwai { \
171648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev); \
172648a8d27STakashi Iwai unsigned long val; \
173648a8d27STakashi Iwai int err = kstrtoul(buf, 0, &val); \
174648a8d27STakashi Iwai if (err < 0) \
175648a8d27STakashi Iwai return err; \
1767639a06cSTakashi Iwai codec->field = val; \
177648a8d27STakashi Iwai return count; \
178648a8d27STakashi Iwai }
179648a8d27STakashi Iwai
1807639a06cSTakashi Iwai #define CODEC_INFO_STR_STORE(type, field) \
181648a8d27STakashi Iwai static ssize_t type##_store(struct device *dev, \
182648a8d27STakashi Iwai struct device_attribute *attr, \
183648a8d27STakashi Iwai const char *buf, size_t count) \
184648a8d27STakashi Iwai { \
185648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev); \
186648a8d27STakashi Iwai char *s = kstrndup_noeol(buf, 64); \
187648a8d27STakashi Iwai if (!s) \
188648a8d27STakashi Iwai return -ENOMEM; \
1897639a06cSTakashi Iwai kfree(codec->field); \
1907639a06cSTakashi Iwai codec->field = s; \
191648a8d27STakashi Iwai return count; \
192648a8d27STakashi Iwai }
193648a8d27STakashi Iwai
1947639a06cSTakashi Iwai CODEC_INFO_STORE(vendor_id, core.vendor_id);
1957639a06cSTakashi Iwai CODEC_INFO_STORE(subsystem_id, core.subsystem_id);
1967639a06cSTakashi Iwai CODEC_INFO_STORE(revision_id, core.revision_id);
1977639a06cSTakashi Iwai CODEC_INFO_STR_STORE(vendor_name, core.vendor_name);
1987639a06cSTakashi Iwai CODEC_INFO_STR_STORE(chip_name, core.chip_name);
1997639a06cSTakashi Iwai CODEC_INFO_STR_STORE(modelname, modelname);
200648a8d27STakashi Iwai
201648a8d27STakashi Iwai #define CODEC_ACTION_STORE(type) \
202648a8d27STakashi Iwai static ssize_t type##_store(struct device *dev, \
203648a8d27STakashi Iwai struct device_attribute *attr, \
204648a8d27STakashi Iwai const char *buf, size_t count) \
205648a8d27STakashi Iwai { \
206648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev); \
207648a8d27STakashi Iwai int err = 0; \
208648a8d27STakashi Iwai if (*buf) \
209648a8d27STakashi Iwai err = type##_codec(codec); \
210648a8d27STakashi Iwai return err < 0 ? err : count; \
211648a8d27STakashi Iwai }
212648a8d27STakashi Iwai
213648a8d27STakashi Iwai CODEC_ACTION_STORE(reconfig);
214648a8d27STakashi Iwai CODEC_ACTION_STORE(clear);
215648a8d27STakashi Iwai
init_verbs_show(struct device * dev,struct device_attribute * attr,char * buf)216648a8d27STakashi Iwai static ssize_t init_verbs_show(struct device *dev,
217648a8d27STakashi Iwai struct device_attribute *attr,
218648a8d27STakashi Iwai char *buf)
219648a8d27STakashi Iwai {
220648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
221a9c2dfc8STakashi Iwai const struct hda_verb *v;
222648a8d27STakashi Iwai int i, len = 0;
223648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
224a9c2dfc8STakashi Iwai snd_array_for_each(&codec->init_verbs, i, v) {
225*17daae7aSTakashi Iwai len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n",
226648a8d27STakashi Iwai v->nid, v->verb, v->param);
227648a8d27STakashi Iwai }
228648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
229648a8d27STakashi Iwai return len;
230648a8d27STakashi Iwai }
231648a8d27STakashi Iwai
parse_init_verbs(struct hda_codec * codec,const char * buf)232648a8d27STakashi Iwai static int parse_init_verbs(struct hda_codec *codec, const char *buf)
233648a8d27STakashi Iwai {
234648a8d27STakashi Iwai struct hda_verb *v;
235648a8d27STakashi Iwai int nid, verb, param;
236648a8d27STakashi Iwai
237648a8d27STakashi Iwai if (sscanf(buf, "%i %i %i", &nid, &verb, ¶m) != 3)
238648a8d27STakashi Iwai return -EINVAL;
239648a8d27STakashi Iwai if (!nid || !verb)
240648a8d27STakashi Iwai return -EINVAL;
241648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
242648a8d27STakashi Iwai v = snd_array_new(&codec->init_verbs);
243648a8d27STakashi Iwai if (!v) {
244648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
245648a8d27STakashi Iwai return -ENOMEM;
246648a8d27STakashi Iwai }
247648a8d27STakashi Iwai v->nid = nid;
248648a8d27STakashi Iwai v->verb = verb;
249648a8d27STakashi Iwai v->param = param;
250648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
251648a8d27STakashi Iwai return 0;
252648a8d27STakashi Iwai }
253648a8d27STakashi Iwai
init_verbs_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)254648a8d27STakashi Iwai static ssize_t init_verbs_store(struct device *dev,
255648a8d27STakashi Iwai struct device_attribute *attr,
256648a8d27STakashi Iwai const char *buf, size_t count)
257648a8d27STakashi Iwai {
258648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
259648a8d27STakashi Iwai int err = parse_init_verbs(codec, buf);
260648a8d27STakashi Iwai if (err < 0)
261648a8d27STakashi Iwai return err;
262648a8d27STakashi Iwai return count;
263648a8d27STakashi Iwai }
264648a8d27STakashi Iwai
hints_show(struct device * dev,struct device_attribute * attr,char * buf)265648a8d27STakashi Iwai static ssize_t hints_show(struct device *dev,
266648a8d27STakashi Iwai struct device_attribute *attr,
267648a8d27STakashi Iwai char *buf)
268648a8d27STakashi Iwai {
269648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
270a9c2dfc8STakashi Iwai const struct hda_hint *hint;
271648a8d27STakashi Iwai int i, len = 0;
272648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
273a9c2dfc8STakashi Iwai snd_array_for_each(&codec->hints, i, hint) {
274*17daae7aSTakashi Iwai len += sysfs_emit_at(buf, len, "%s = %s\n",
275*17daae7aSTakashi Iwai hint->key, hint->val);
276648a8d27STakashi Iwai }
277648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
278648a8d27STakashi Iwai return len;
279648a8d27STakashi Iwai }
280648a8d27STakashi Iwai
get_hint(struct hda_codec * codec,const char * key)281648a8d27STakashi Iwai static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
282648a8d27STakashi Iwai {
283a9c2dfc8STakashi Iwai struct hda_hint *hint;
284648a8d27STakashi Iwai int i;
285648a8d27STakashi Iwai
286a9c2dfc8STakashi Iwai snd_array_for_each(&codec->hints, i, hint) {
287648a8d27STakashi Iwai if (!strcmp(hint->key, key))
288648a8d27STakashi Iwai return hint;
289648a8d27STakashi Iwai }
290648a8d27STakashi Iwai return NULL;
291648a8d27STakashi Iwai }
292648a8d27STakashi Iwai
remove_trail_spaces(char * str)293648a8d27STakashi Iwai static void remove_trail_spaces(char *str)
294648a8d27STakashi Iwai {
295648a8d27STakashi Iwai char *p;
296648a8d27STakashi Iwai if (!*str)
297648a8d27STakashi Iwai return;
298648a8d27STakashi Iwai p = str + strlen(str) - 1;
299648a8d27STakashi Iwai for (; isspace(*p); p--) {
300648a8d27STakashi Iwai *p = 0;
301648a8d27STakashi Iwai if (p == str)
302648a8d27STakashi Iwai return;
303648a8d27STakashi Iwai }
304648a8d27STakashi Iwai }
305648a8d27STakashi Iwai
306648a8d27STakashi Iwai #define MAX_HINTS 1024
307648a8d27STakashi Iwai
parse_hints(struct hda_codec * codec,const char * buf)308648a8d27STakashi Iwai static int parse_hints(struct hda_codec *codec, const char *buf)
309648a8d27STakashi Iwai {
310648a8d27STakashi Iwai char *key, *val;
311648a8d27STakashi Iwai struct hda_hint *hint;
312648a8d27STakashi Iwai int err = 0;
313648a8d27STakashi Iwai
314648a8d27STakashi Iwai buf = skip_spaces(buf);
315648a8d27STakashi Iwai if (!*buf || *buf == '#' || *buf == '\n')
316648a8d27STakashi Iwai return 0;
317648a8d27STakashi Iwai if (*buf == '=')
318648a8d27STakashi Iwai return -EINVAL;
319648a8d27STakashi Iwai key = kstrndup_noeol(buf, 1024);
320648a8d27STakashi Iwai if (!key)
321648a8d27STakashi Iwai return -ENOMEM;
322648a8d27STakashi Iwai /* extract key and val */
323648a8d27STakashi Iwai val = strchr(key, '=');
324648a8d27STakashi Iwai if (!val) {
325648a8d27STakashi Iwai kfree(key);
326648a8d27STakashi Iwai return -EINVAL;
327648a8d27STakashi Iwai }
328648a8d27STakashi Iwai *val++ = 0;
329648a8d27STakashi Iwai val = skip_spaces(val);
330648a8d27STakashi Iwai remove_trail_spaces(key);
331648a8d27STakashi Iwai remove_trail_spaces(val);
332648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
333648a8d27STakashi Iwai hint = get_hint(codec, key);
334648a8d27STakashi Iwai if (hint) {
335648a8d27STakashi Iwai /* replace */
336648a8d27STakashi Iwai kfree(hint->key);
337648a8d27STakashi Iwai hint->key = key;
338648a8d27STakashi Iwai hint->val = val;
339648a8d27STakashi Iwai goto unlock;
340648a8d27STakashi Iwai }
341648a8d27STakashi Iwai /* allocate a new hint entry */
342648a8d27STakashi Iwai if (codec->hints.used >= MAX_HINTS)
343648a8d27STakashi Iwai hint = NULL;
344648a8d27STakashi Iwai else
345648a8d27STakashi Iwai hint = snd_array_new(&codec->hints);
346648a8d27STakashi Iwai if (hint) {
347648a8d27STakashi Iwai hint->key = key;
348648a8d27STakashi Iwai hint->val = val;
349648a8d27STakashi Iwai } else {
350648a8d27STakashi Iwai err = -ENOMEM;
351648a8d27STakashi Iwai }
352648a8d27STakashi Iwai unlock:
353648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
354648a8d27STakashi Iwai if (err)
355648a8d27STakashi Iwai kfree(key);
356648a8d27STakashi Iwai return err;
357648a8d27STakashi Iwai }
358648a8d27STakashi Iwai
hints_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)359648a8d27STakashi Iwai static ssize_t hints_store(struct device *dev,
360648a8d27STakashi Iwai struct device_attribute *attr,
361648a8d27STakashi Iwai const char *buf, size_t count)
362648a8d27STakashi Iwai {
363648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
364648a8d27STakashi Iwai int err = parse_hints(codec, buf);
365648a8d27STakashi Iwai if (err < 0)
366648a8d27STakashi Iwai return err;
367648a8d27STakashi Iwai return count;
368648a8d27STakashi Iwai }
369648a8d27STakashi Iwai
user_pin_configs_show(struct device * dev,struct device_attribute * attr,char * buf)370648a8d27STakashi Iwai static ssize_t user_pin_configs_show(struct device *dev,
371648a8d27STakashi Iwai struct device_attribute *attr,
372648a8d27STakashi Iwai char *buf)
373648a8d27STakashi Iwai {
374648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
375648a8d27STakashi Iwai return pin_configs_show(codec, &codec->user_pins, buf);
376648a8d27STakashi Iwai }
377648a8d27STakashi Iwai
parse_user_pin_configs(struct hda_codec * codec,const char * buf)378648a8d27STakashi Iwai static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
379648a8d27STakashi Iwai {
380648a8d27STakashi Iwai int nid, cfg, err;
381648a8d27STakashi Iwai
382648a8d27STakashi Iwai if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
383648a8d27STakashi Iwai return -EINVAL;
384648a8d27STakashi Iwai if (!nid)
385648a8d27STakashi Iwai return -EINVAL;
386648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
387648a8d27STakashi Iwai err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
388648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
389648a8d27STakashi Iwai return err;
390648a8d27STakashi Iwai }
391648a8d27STakashi Iwai
user_pin_configs_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)392648a8d27STakashi Iwai static ssize_t user_pin_configs_store(struct device *dev,
393648a8d27STakashi Iwai struct device_attribute *attr,
394648a8d27STakashi Iwai const char *buf, size_t count)
395648a8d27STakashi Iwai {
396648a8d27STakashi Iwai struct hda_codec *codec = dev_get_drvdata(dev);
397648a8d27STakashi Iwai int err = parse_user_pin_configs(codec, buf);
398648a8d27STakashi Iwai if (err < 0)
399648a8d27STakashi Iwai return err;
400648a8d27STakashi Iwai return count;
401648a8d27STakashi Iwai }
402648a8d27STakashi Iwai
403b989d044STakashi Iwai /* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
404648a8d27STakashi Iwai static DEVICE_ATTR_RW(init_verbs);
405648a8d27STakashi Iwai static DEVICE_ATTR_RW(hints);
406648a8d27STakashi Iwai static DEVICE_ATTR_RW(user_pin_configs);
407648a8d27STakashi Iwai static DEVICE_ATTR_WO(reconfig);
408648a8d27STakashi Iwai static DEVICE_ATTR_WO(clear);
409648a8d27STakashi Iwai
41095a962c3STakashi Iwai /**
41195a962c3STakashi Iwai * snd_hda_get_hint - Look for hint string
41295a962c3STakashi Iwai * @codec: the HDA codec
41395a962c3STakashi Iwai * @key: the hint key string
41495a962c3STakashi Iwai *
41595a962c3STakashi Iwai * Look for a hint key/value pair matching with the given key string
41695a962c3STakashi Iwai * and returns the value string. If nothing found, returns NULL.
417648a8d27STakashi Iwai */
snd_hda_get_hint(struct hda_codec * codec,const char * key)418648a8d27STakashi Iwai const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
419648a8d27STakashi Iwai {
420648a8d27STakashi Iwai struct hda_hint *hint = get_hint(codec, key);
421648a8d27STakashi Iwai return hint ? hint->val : NULL;
422648a8d27STakashi Iwai }
423648a8d27STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_get_hint);
424648a8d27STakashi Iwai
42595a962c3STakashi Iwai /**
42695a962c3STakashi Iwai * snd_hda_get_bool_hint - Get a boolean hint value
42795a962c3STakashi Iwai * @codec: the HDA codec
42895a962c3STakashi Iwai * @key: the hint key string
42995a962c3STakashi Iwai *
43095a962c3STakashi Iwai * Look for a hint key/value pair matching with the given key string
43195a962c3STakashi Iwai * and returns a boolean value parsed from the value. If no matching
43295a962c3STakashi Iwai * key is found, return a negative value.
43395a962c3STakashi Iwai */
snd_hda_get_bool_hint(struct hda_codec * codec,const char * key)434648a8d27STakashi Iwai int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
435648a8d27STakashi Iwai {
436648a8d27STakashi Iwai const char *p;
437648a8d27STakashi Iwai int ret;
438648a8d27STakashi Iwai
439648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
440648a8d27STakashi Iwai p = snd_hda_get_hint(codec, key);
441648a8d27STakashi Iwai if (!p || !*p)
442648a8d27STakashi Iwai ret = -ENOENT;
443648a8d27STakashi Iwai else {
444648a8d27STakashi Iwai switch (toupper(*p)) {
445648a8d27STakashi Iwai case 'T': /* true */
446648a8d27STakashi Iwai case 'Y': /* yes */
447648a8d27STakashi Iwai case '1':
448648a8d27STakashi Iwai ret = 1;
449648a8d27STakashi Iwai break;
450648a8d27STakashi Iwai default:
451648a8d27STakashi Iwai ret = 0;
452648a8d27STakashi Iwai break;
453648a8d27STakashi Iwai }
454648a8d27STakashi Iwai }
455648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
456648a8d27STakashi Iwai return ret;
457648a8d27STakashi Iwai }
458648a8d27STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
459648a8d27STakashi Iwai
46095a962c3STakashi Iwai /**
461c10b11f6STakashi Iwai * snd_hda_get_int_hint - Get an integer hint value
46295a962c3STakashi Iwai * @codec: the HDA codec
46395a962c3STakashi Iwai * @key: the hint key string
46495a962c3STakashi Iwai * @valp: pointer to store a value
46595a962c3STakashi Iwai *
46695a962c3STakashi Iwai * Look for a hint key/value pair matching with the given key string
46795a962c3STakashi Iwai * and stores the integer value to @valp. If no matching key is found,
46895a962c3STakashi Iwai * return a negative error code. Otherwise it returns zero.
46995a962c3STakashi Iwai */
snd_hda_get_int_hint(struct hda_codec * codec,const char * key,int * valp)470648a8d27STakashi Iwai int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
471648a8d27STakashi Iwai {
472648a8d27STakashi Iwai const char *p;
473648a8d27STakashi Iwai unsigned long val;
474648a8d27STakashi Iwai int ret;
475648a8d27STakashi Iwai
476648a8d27STakashi Iwai mutex_lock(&codec->user_mutex);
477648a8d27STakashi Iwai p = snd_hda_get_hint(codec, key);
478648a8d27STakashi Iwai if (!p)
479648a8d27STakashi Iwai ret = -ENOENT;
480648a8d27STakashi Iwai else if (kstrtoul(p, 0, &val))
481648a8d27STakashi Iwai ret = -EINVAL;
482648a8d27STakashi Iwai else {
483648a8d27STakashi Iwai *valp = val;
484648a8d27STakashi Iwai ret = 0;
485648a8d27STakashi Iwai }
486648a8d27STakashi Iwai mutex_unlock(&codec->user_mutex);
487648a8d27STakashi Iwai return ret;
488648a8d27STakashi Iwai }
489648a8d27STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
490648a8d27STakashi Iwai #endif /* CONFIG_SND_HDA_RECONFIG */
491648a8d27STakashi Iwai
492b989d044STakashi Iwai /*
493b989d044STakashi Iwai * common sysfs attributes
494b989d044STakashi Iwai */
495b989d044STakashi Iwai #ifdef CONFIG_SND_HDA_RECONFIG
496b989d044STakashi Iwai #define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name)
497b989d044STakashi Iwai #else
498b989d044STakashi Iwai #define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name)
499b989d044STakashi Iwai #endif
500b989d044STakashi Iwai static RECONFIG_DEVICE_ATTR(vendor_id);
501b989d044STakashi Iwai static RECONFIG_DEVICE_ATTR(subsystem_id);
502b989d044STakashi Iwai static RECONFIG_DEVICE_ATTR(revision_id);
503b989d044STakashi Iwai static DEVICE_ATTR_RO(afg);
504b989d044STakashi Iwai static DEVICE_ATTR_RO(mfg);
505b989d044STakashi Iwai static RECONFIG_DEVICE_ATTR(vendor_name);
506b989d044STakashi Iwai static RECONFIG_DEVICE_ATTR(chip_name);
507b989d044STakashi Iwai static RECONFIG_DEVICE_ATTR(modelname);
508b989d044STakashi Iwai static DEVICE_ATTR_RO(init_pin_configs);
509b989d044STakashi Iwai static DEVICE_ATTR_RO(driver_pin_configs);
510b989d044STakashi Iwai
511b989d044STakashi Iwai
512648a8d27STakashi Iwai #ifdef CONFIG_SND_HDA_PATCH_LOADER
513648a8d27STakashi Iwai
514648a8d27STakashi Iwai /* parser mode */
515648a8d27STakashi Iwai enum {
516648a8d27STakashi Iwai LINE_MODE_NONE,
517648a8d27STakashi Iwai LINE_MODE_CODEC,
518648a8d27STakashi Iwai LINE_MODE_MODEL,
519648a8d27STakashi Iwai LINE_MODE_PINCFG,
520648a8d27STakashi Iwai LINE_MODE_VERB,
521648a8d27STakashi Iwai LINE_MODE_HINT,
522648a8d27STakashi Iwai LINE_MODE_VENDOR_ID,
523648a8d27STakashi Iwai LINE_MODE_SUBSYSTEM_ID,
524648a8d27STakashi Iwai LINE_MODE_REVISION_ID,
525648a8d27STakashi Iwai LINE_MODE_CHIP_NAME,
526648a8d27STakashi Iwai NUM_LINE_MODES,
527648a8d27STakashi Iwai };
528648a8d27STakashi Iwai
strmatch(const char * a,const char * b)529648a8d27STakashi Iwai static inline int strmatch(const char *a, const char *b)
530648a8d27STakashi Iwai {
5318bb1ffdfSRasmus Villemoes return strncasecmp(a, b, strlen(b)) == 0;
532648a8d27STakashi Iwai }
533648a8d27STakashi Iwai
534648a8d27STakashi Iwai /* parse the contents after the line "[codec]"
535648a8d27STakashi Iwai * accept only the line with three numbers, and assign the current codec
536648a8d27STakashi Iwai */
parse_codec_mode(char * buf,struct hda_bus * bus,struct hda_codec ** codecp)537648a8d27STakashi Iwai static void parse_codec_mode(char *buf, struct hda_bus *bus,
538648a8d27STakashi Iwai struct hda_codec **codecp)
539648a8d27STakashi Iwai {
540648a8d27STakashi Iwai int vendorid, subid, caddr;
541648a8d27STakashi Iwai struct hda_codec *codec;
542648a8d27STakashi Iwai
543648a8d27STakashi Iwai *codecp = NULL;
544648a8d27STakashi Iwai if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
545d068ebc2STakashi Iwai list_for_each_codec(codec, bus) {
5467639a06cSTakashi Iwai if ((vendorid <= 0 || codec->core.vendor_id == vendorid) &&
5477639a06cSTakashi Iwai (subid <= 0 || codec->core.subsystem_id == subid) &&
5487639a06cSTakashi Iwai codec->core.addr == caddr) {
549648a8d27STakashi Iwai *codecp = codec;
550648a8d27STakashi Iwai break;
551648a8d27STakashi Iwai }
552648a8d27STakashi Iwai }
553648a8d27STakashi Iwai }
554648a8d27STakashi Iwai }
555648a8d27STakashi Iwai
556648a8d27STakashi Iwai /* parse the contents after the other command tags, [pincfg], [verb],
557648a8d27STakashi Iwai * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
558648a8d27STakashi Iwai * just pass to the sysfs helper (only when any codec was specified)
559648a8d27STakashi Iwai */
parse_pincfg_mode(char * buf,struct hda_bus * bus,struct hda_codec ** codecp)560648a8d27STakashi Iwai static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
561648a8d27STakashi Iwai struct hda_codec **codecp)
562648a8d27STakashi Iwai {
563648a8d27STakashi Iwai parse_user_pin_configs(*codecp, buf);
564648a8d27STakashi Iwai }
565648a8d27STakashi Iwai
parse_verb_mode(char * buf,struct hda_bus * bus,struct hda_codec ** codecp)566648a8d27STakashi Iwai static void parse_verb_mode(char *buf, struct hda_bus *bus,
567648a8d27STakashi Iwai struct hda_codec **codecp)
568648a8d27STakashi Iwai {
569648a8d27STakashi Iwai parse_init_verbs(*codecp, buf);
570648a8d27STakashi Iwai }
571648a8d27STakashi Iwai
parse_hint_mode(char * buf,struct hda_bus * bus,struct hda_codec ** codecp)572648a8d27STakashi Iwai static void parse_hint_mode(char *buf, struct hda_bus *bus,
573648a8d27STakashi Iwai struct hda_codec **codecp)
574648a8d27STakashi Iwai {
575648a8d27STakashi Iwai parse_hints(*codecp, buf);
576648a8d27STakashi Iwai }
577648a8d27STakashi Iwai
parse_model_mode(char * buf,struct hda_bus * bus,struct hda_codec ** codecp)578648a8d27STakashi Iwai static void parse_model_mode(char *buf, struct hda_bus *bus,
579648a8d27STakashi Iwai struct hda_codec **codecp)
580648a8d27STakashi Iwai {
581648a8d27STakashi Iwai kfree((*codecp)->modelname);
582648a8d27STakashi Iwai (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
583648a8d27STakashi Iwai }
584648a8d27STakashi Iwai
parse_chip_name_mode(char * buf,struct hda_bus * bus,struct hda_codec ** codecp)585648a8d27STakashi Iwai static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
586648a8d27STakashi Iwai struct hda_codec **codecp)
587648a8d27STakashi Iwai {
588ded255beSTakashi Iwai snd_hda_codec_set_name(*codecp, buf);
589648a8d27STakashi Iwai }
590648a8d27STakashi Iwai
591648a8d27STakashi Iwai #define DEFINE_PARSE_ID_MODE(name) \
592648a8d27STakashi Iwai static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
593648a8d27STakashi Iwai struct hda_codec **codecp) \
594648a8d27STakashi Iwai { \
595648a8d27STakashi Iwai unsigned long val; \
596648a8d27STakashi Iwai if (!kstrtoul(buf, 0, &val)) \
5977639a06cSTakashi Iwai (*codecp)->core.name = val; \
598648a8d27STakashi Iwai }
599648a8d27STakashi Iwai
600648a8d27STakashi Iwai DEFINE_PARSE_ID_MODE(vendor_id);
601648a8d27STakashi Iwai DEFINE_PARSE_ID_MODE(subsystem_id);
602648a8d27STakashi Iwai DEFINE_PARSE_ID_MODE(revision_id);
603648a8d27STakashi Iwai
604648a8d27STakashi Iwai
605648a8d27STakashi Iwai struct hda_patch_item {
606648a8d27STakashi Iwai const char *tag;
607648a8d27STakashi Iwai const char *alias;
608648a8d27STakashi Iwai void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
609648a8d27STakashi Iwai };
610648a8d27STakashi Iwai
611bf82326fSTakashi Iwai static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
612648a8d27STakashi Iwai [LINE_MODE_CODEC] = {
613648a8d27STakashi Iwai .tag = "[codec]",
614648a8d27STakashi Iwai .parser = parse_codec_mode,
615648a8d27STakashi Iwai },
616648a8d27STakashi Iwai [LINE_MODE_MODEL] = {
617648a8d27STakashi Iwai .tag = "[model]",
618648a8d27STakashi Iwai .parser = parse_model_mode,
619648a8d27STakashi Iwai },
620648a8d27STakashi Iwai [LINE_MODE_VERB] = {
621648a8d27STakashi Iwai .tag = "[verb]",
622648a8d27STakashi Iwai .alias = "[init_verbs]",
623648a8d27STakashi Iwai .parser = parse_verb_mode,
624648a8d27STakashi Iwai },
625648a8d27STakashi Iwai [LINE_MODE_PINCFG] = {
626648a8d27STakashi Iwai .tag = "[pincfg]",
627648a8d27STakashi Iwai .alias = "[user_pin_configs]",
628648a8d27STakashi Iwai .parser = parse_pincfg_mode,
629648a8d27STakashi Iwai },
630648a8d27STakashi Iwai [LINE_MODE_HINT] = {
631648a8d27STakashi Iwai .tag = "[hint]",
632648a8d27STakashi Iwai .alias = "[hints]",
633648a8d27STakashi Iwai .parser = parse_hint_mode
634648a8d27STakashi Iwai },
635648a8d27STakashi Iwai [LINE_MODE_VENDOR_ID] = {
636648a8d27STakashi Iwai .tag = "[vendor_id]",
637648a8d27STakashi Iwai .parser = parse_vendor_id_mode,
638648a8d27STakashi Iwai },
639648a8d27STakashi Iwai [LINE_MODE_SUBSYSTEM_ID] = {
640648a8d27STakashi Iwai .tag = "[subsystem_id]",
641648a8d27STakashi Iwai .parser = parse_subsystem_id_mode,
642648a8d27STakashi Iwai },
643648a8d27STakashi Iwai [LINE_MODE_REVISION_ID] = {
644648a8d27STakashi Iwai .tag = "[revision_id]",
645648a8d27STakashi Iwai .parser = parse_revision_id_mode,
646648a8d27STakashi Iwai },
647648a8d27STakashi Iwai [LINE_MODE_CHIP_NAME] = {
648648a8d27STakashi Iwai .tag = "[chip_name]",
649648a8d27STakashi Iwai .parser = parse_chip_name_mode,
650648a8d27STakashi Iwai },
651648a8d27STakashi Iwai };
652648a8d27STakashi Iwai
653648a8d27STakashi Iwai /* check the line starting with '[' -- change the parser mode accodingly */
parse_line_mode(char * buf,struct hda_bus * bus)654648a8d27STakashi Iwai static int parse_line_mode(char *buf, struct hda_bus *bus)
655648a8d27STakashi Iwai {
656648a8d27STakashi Iwai int i;
657648a8d27STakashi Iwai for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
658648a8d27STakashi Iwai if (!patch_items[i].tag)
659648a8d27STakashi Iwai continue;
660648a8d27STakashi Iwai if (strmatch(buf, patch_items[i].tag))
661648a8d27STakashi Iwai return i;
662648a8d27STakashi Iwai if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
663648a8d27STakashi Iwai return i;
664648a8d27STakashi Iwai }
665648a8d27STakashi Iwai return LINE_MODE_NONE;
666648a8d27STakashi Iwai }
667648a8d27STakashi Iwai
668648a8d27STakashi Iwai /* copy one line from the buffer in fw, and update the fields in fw
669648a8d27STakashi Iwai * return zero if it reaches to the end of the buffer, or non-zero
670648a8d27STakashi Iwai * if successfully copied a line
671648a8d27STakashi Iwai *
672648a8d27STakashi Iwai * the spaces at the beginning and the end of the line are stripped
673648a8d27STakashi Iwai */
get_line_from_fw(char * buf,int size,size_t * fw_size_p,const void ** fw_data_p)674648a8d27STakashi Iwai static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
675648a8d27STakashi Iwai const void **fw_data_p)
676648a8d27STakashi Iwai {
677648a8d27STakashi Iwai int len;
678648a8d27STakashi Iwai size_t fw_size = *fw_size_p;
679648a8d27STakashi Iwai const char *p = *fw_data_p;
680648a8d27STakashi Iwai
681648a8d27STakashi Iwai while (isspace(*p) && fw_size) {
682648a8d27STakashi Iwai p++;
683648a8d27STakashi Iwai fw_size--;
684648a8d27STakashi Iwai }
685648a8d27STakashi Iwai if (!fw_size)
686648a8d27STakashi Iwai return 0;
687648a8d27STakashi Iwai
688648a8d27STakashi Iwai for (len = 0; len < fw_size; len++) {
689648a8d27STakashi Iwai if (!*p)
690648a8d27STakashi Iwai break;
691648a8d27STakashi Iwai if (*p == '\n') {
692648a8d27STakashi Iwai p++;
693648a8d27STakashi Iwai len++;
694648a8d27STakashi Iwai break;
695648a8d27STakashi Iwai }
696648a8d27STakashi Iwai if (len < size)
697648a8d27STakashi Iwai *buf++ = *p++;
698648a8d27STakashi Iwai }
699648a8d27STakashi Iwai *buf = 0;
700648a8d27STakashi Iwai *fw_size_p = fw_size - len;
701648a8d27STakashi Iwai *fw_data_p = p;
702648a8d27STakashi Iwai remove_trail_spaces(buf);
703648a8d27STakashi Iwai return 1;
704648a8d27STakashi Iwai }
705648a8d27STakashi Iwai
70695a962c3STakashi Iwai /**
70795a962c3STakashi Iwai * snd_hda_load_patch - load a "patch" firmware file and parse it
70895a962c3STakashi Iwai * @bus: HD-audio bus
70995a962c3STakashi Iwai * @fw_size: the firmware byte size
71095a962c3STakashi Iwai * @fw_buf: the firmware data
711648a8d27STakashi Iwai */
snd_hda_load_patch(struct hda_bus * bus,size_t fw_size,const void * fw_buf)712648a8d27STakashi Iwai int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
713648a8d27STakashi Iwai {
714648a8d27STakashi Iwai char buf[128];
715648a8d27STakashi Iwai struct hda_codec *codec;
716648a8d27STakashi Iwai int line_mode;
717648a8d27STakashi Iwai
718648a8d27STakashi Iwai line_mode = LINE_MODE_NONE;
719648a8d27STakashi Iwai codec = NULL;
720648a8d27STakashi Iwai while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
721648a8d27STakashi Iwai if (!*buf || *buf == '#' || *buf == '\n')
722648a8d27STakashi Iwai continue;
723648a8d27STakashi Iwai if (*buf == '[')
724648a8d27STakashi Iwai line_mode = parse_line_mode(buf, bus);
725648a8d27STakashi Iwai else if (patch_items[line_mode].parser &&
726648a8d27STakashi Iwai (codec || line_mode <= LINE_MODE_CODEC))
727648a8d27STakashi Iwai patch_items[line_mode].parser(buf, bus, &codec);
728648a8d27STakashi Iwai }
729648a8d27STakashi Iwai return 0;
730648a8d27STakashi Iwai }
731648a8d27STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_load_patch);
732648a8d27STakashi Iwai #endif /* CONFIG_SND_HDA_PATCH_LOADER */
733648a8d27STakashi Iwai
734648a8d27STakashi Iwai /*
735648a8d27STakashi Iwai * sysfs entries
736648a8d27STakashi Iwai */
737648a8d27STakashi Iwai static struct attribute *hda_dev_attrs[] = {
738648a8d27STakashi Iwai &dev_attr_vendor_id.attr,
739648a8d27STakashi Iwai &dev_attr_subsystem_id.attr,
740648a8d27STakashi Iwai &dev_attr_revision_id.attr,
741648a8d27STakashi Iwai &dev_attr_afg.attr,
742648a8d27STakashi Iwai &dev_attr_mfg.attr,
743648a8d27STakashi Iwai &dev_attr_vendor_name.attr,
744648a8d27STakashi Iwai &dev_attr_chip_name.attr,
745648a8d27STakashi Iwai &dev_attr_modelname.attr,
746b989d044STakashi Iwai &dev_attr_init_pin_configs.attr,
747b989d044STakashi Iwai &dev_attr_driver_pin_configs.attr,
748b989d044STakashi Iwai #ifdef CONFIG_PM
749b989d044STakashi Iwai &dev_attr_power_on_acct.attr,
750b989d044STakashi Iwai &dev_attr_power_off_acct.attr,
751b989d044STakashi Iwai #endif
752b989d044STakashi Iwai #ifdef CONFIG_SND_HDA_RECONFIG
753648a8d27STakashi Iwai &dev_attr_init_verbs.attr,
754648a8d27STakashi Iwai &dev_attr_hints.attr,
755648a8d27STakashi Iwai &dev_attr_user_pin_configs.attr,
756648a8d27STakashi Iwai &dev_attr_reconfig.attr,
757648a8d27STakashi Iwai &dev_attr_clear.attr,
758648a8d27STakashi Iwai #endif
759648a8d27STakashi Iwai NULL
760648a8d27STakashi Iwai };
761648a8d27STakashi Iwai
762a5a041b6SArvind Yadav static const struct attribute_group hda_dev_attr_group = {
763648a8d27STakashi Iwai .attrs = hda_dev_attrs,
764648a8d27STakashi Iwai };
765648a8d27STakashi Iwai
766648a8d27STakashi Iwai const struct attribute_group *snd_hda_dev_attr_groups[] = {
767648a8d27STakashi Iwai &hda_dev_attr_group,
768648a8d27STakashi Iwai NULL
769648a8d27STakashi Iwai };
770648a8d27STakashi Iwai
snd_hda_sysfs_init(struct hda_codec * codec)771648a8d27STakashi Iwai void snd_hda_sysfs_init(struct hda_codec *codec)
772648a8d27STakashi Iwai {
773648a8d27STakashi Iwai mutex_init(&codec->user_mutex);
774b989d044STakashi Iwai #ifdef CONFIG_SND_HDA_RECONFIG
775648a8d27STakashi Iwai snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
776648a8d27STakashi Iwai snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
777648a8d27STakashi Iwai snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
778648a8d27STakashi Iwai #endif
779648a8d27STakashi Iwai }
780648a8d27STakashi Iwai
snd_hda_sysfs_clear(struct hda_codec * codec)781648a8d27STakashi Iwai void snd_hda_sysfs_clear(struct hda_codec *codec)
782648a8d27STakashi Iwai {
783648a8d27STakashi Iwai #ifdef CONFIG_SND_HDA_RECONFIG
784a9c2dfc8STakashi Iwai struct hda_hint *hint;
785648a8d27STakashi Iwai int i;
786648a8d27STakashi Iwai
787648a8d27STakashi Iwai /* clear init verbs */
788648a8d27STakashi Iwai snd_array_free(&codec->init_verbs);
789648a8d27STakashi Iwai /* clear hints */
790a9c2dfc8STakashi Iwai snd_array_for_each(&codec->hints, i, hint) {
791648a8d27STakashi Iwai kfree(hint->key); /* we don't need to free hint->val */
792648a8d27STakashi Iwai }
793648a8d27STakashi Iwai snd_array_free(&codec->hints);
794648a8d27STakashi Iwai snd_array_free(&codec->user_pins);
795648a8d27STakashi Iwai #endif
796648a8d27STakashi Iwai }
797