ALSA: hda - Add sysfs to codec object, too
[deliverable/linux.git] / sound / pci / hda / hda_sysfs.c
CommitLineData
648a8d27
TI
1/*
2 * sysfs interface for HD-audio codec
3 *
4 * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
5 *
6 * split from hda_hwdep.c
7 */
8
9#include <linux/init.h>
10#include <linux/slab.h>
11#include <linux/compat.h>
12#include <linux/mutex.h>
13#include <linux/ctype.h>
14#include <linux/string.h>
15#include <linux/export.h>
16#include <sound/core.h>
17#include "hda_codec.h"
18#include "hda_local.h"
19#include <sound/hda_hwdep.h>
20#include <sound/minors.h>
21
22/* hint string pair */
23struct hda_hint {
24 const char *key;
25 const char *val; /* contained in the same alloc as key */
26};
27
28#ifdef CONFIG_PM
29static ssize_t power_on_acct_show(struct device *dev,
30 struct device_attribute *attr,
31 char *buf)
32{
33 struct hda_codec *codec = dev_get_drvdata(dev);
34 snd_hda_update_power_acct(codec);
35 return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
36}
37
38static ssize_t power_off_acct_show(struct device *dev,
39 struct device_attribute *attr,
40 char *buf)
41{
42 struct hda_codec *codec = dev_get_drvdata(dev);
43 snd_hda_update_power_acct(codec);
44 return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
45}
46
47static DEVICE_ATTR_RO(power_on_acct);
48static DEVICE_ATTR_RO(power_off_acct);
49#endif /* CONFIG_PM */
50
51#ifdef CONFIG_SND_HDA_RECONFIG
52
53/*
54 * sysfs interface
55 */
56
57static int clear_codec(struct hda_codec *codec)
58{
59 int err;
60
61 err = snd_hda_codec_reset(codec);
62 if (err < 0) {
63 snd_printk(KERN_ERR "The codec is being used, can't free.\n");
64 return err;
65 }
66 snd_hda_sysfs_clear(codec);
67 return 0;
68}
69
70static int reconfig_codec(struct hda_codec *codec)
71{
72 int err;
73
74 snd_hda_power_up(codec);
75 snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
76 err = snd_hda_codec_reset(codec);
77 if (err < 0) {
78 snd_printk(KERN_ERR
79 "The codec is being used, can't reconfigure.\n");
80 goto error;
81 }
82 err = snd_hda_codec_configure(codec);
83 if (err < 0)
84 goto error;
85 /* rebuild PCMs */
86 err = snd_hda_codec_build_pcms(codec);
87 if (err < 0)
88 goto error;
89 /* rebuild mixers */
90 err = snd_hda_codec_build_controls(codec);
91 if (err < 0)
92 goto error;
93 err = snd_card_register(codec->bus->card);
94 error:
95 snd_hda_power_down(codec);
96 return err;
97}
98
99/*
100 * allocate a string at most len chars, and remove the trailing EOL
101 */
102static char *kstrndup_noeol(const char *src, size_t len)
103{
104 char *s = kstrndup(src, len, GFP_KERNEL);
105 char *p;
106 if (!s)
107 return NULL;
108 p = strchr(s, '\n');
109 if (p)
110 *p = 0;
111 return s;
112}
113
114#define CODEC_INFO_SHOW(type) \
115static ssize_t type##_show(struct device *dev, \
116 struct device_attribute *attr, \
117 char *buf) \
118{ \
119 struct hda_codec *codec = dev_get_drvdata(dev); \
120 return sprintf(buf, "0x%x\n", codec->type); \
121}
122
123#define CODEC_INFO_STR_SHOW(type) \
124static ssize_t type##_show(struct device *dev, \
125 struct device_attribute *attr, \
126 char *buf) \
127{ \
128 struct hda_codec *codec = dev_get_drvdata(dev); \
129 return sprintf(buf, "%s\n", \
130 codec->type ? codec->type : ""); \
131}
132
133CODEC_INFO_SHOW(vendor_id);
134CODEC_INFO_SHOW(subsystem_id);
135CODEC_INFO_SHOW(revision_id);
136CODEC_INFO_SHOW(afg);
137CODEC_INFO_SHOW(mfg);
138CODEC_INFO_STR_SHOW(vendor_name);
139CODEC_INFO_STR_SHOW(chip_name);
140CODEC_INFO_STR_SHOW(modelname);
141
142#define CODEC_INFO_STORE(type) \
143static ssize_t type##_store(struct device *dev, \
144 struct device_attribute *attr, \
145 const char *buf, size_t count) \
146{ \
147 struct hda_codec *codec = dev_get_drvdata(dev); \
148 unsigned long val; \
149 int err = kstrtoul(buf, 0, &val); \
150 if (err < 0) \
151 return err; \
152 codec->type = val; \
153 return count; \
154}
155
156#define CODEC_INFO_STR_STORE(type) \
157static ssize_t type##_store(struct device *dev, \
158 struct device_attribute *attr, \
159 const char *buf, size_t count) \
160{ \
161 struct hda_codec *codec = dev_get_drvdata(dev); \
162 char *s = kstrndup_noeol(buf, 64); \
163 if (!s) \
164 return -ENOMEM; \
165 kfree(codec->type); \
166 codec->type = s; \
167 return count; \
168}
169
170CODEC_INFO_STORE(vendor_id);
171CODEC_INFO_STORE(subsystem_id);
172CODEC_INFO_STORE(revision_id);
173CODEC_INFO_STR_STORE(vendor_name);
174CODEC_INFO_STR_STORE(chip_name);
175CODEC_INFO_STR_STORE(modelname);
176
177#define CODEC_ACTION_STORE(type) \
178static ssize_t type##_store(struct device *dev, \
179 struct device_attribute *attr, \
180 const char *buf, size_t count) \
181{ \
182 struct hda_codec *codec = dev_get_drvdata(dev); \
183 int err = 0; \
184 if (*buf) \
185 err = type##_codec(codec); \
186 return err < 0 ? err : count; \
187}
188
189CODEC_ACTION_STORE(reconfig);
190CODEC_ACTION_STORE(clear);
191
192static ssize_t init_verbs_show(struct device *dev,
193 struct device_attribute *attr,
194 char *buf)
195{
196 struct hda_codec *codec = dev_get_drvdata(dev);
197 int i, len = 0;
198 mutex_lock(&codec->user_mutex);
199 for (i = 0; i < codec->init_verbs.used; i++) {
200 struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
201 len += snprintf(buf + len, PAGE_SIZE - len,
202 "0x%02x 0x%03x 0x%04x\n",
203 v->nid, v->verb, v->param);
204 }
205 mutex_unlock(&codec->user_mutex);
206 return len;
207}
208
209static int parse_init_verbs(struct hda_codec *codec, const char *buf)
210{
211 struct hda_verb *v;
212 int nid, verb, param;
213
214 if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
215 return -EINVAL;
216 if (!nid || !verb)
217 return -EINVAL;
218 mutex_lock(&codec->user_mutex);
219 v = snd_array_new(&codec->init_verbs);
220 if (!v) {
221 mutex_unlock(&codec->user_mutex);
222 return -ENOMEM;
223 }
224 v->nid = nid;
225 v->verb = verb;
226 v->param = param;
227 mutex_unlock(&codec->user_mutex);
228 return 0;
229}
230
231static ssize_t init_verbs_store(struct device *dev,
232 struct device_attribute *attr,
233 const char *buf, size_t count)
234{
235 struct hda_codec *codec = dev_get_drvdata(dev);
236 int err = parse_init_verbs(codec, buf);
237 if (err < 0)
238 return err;
239 return count;
240}
241
242static ssize_t hints_show(struct device *dev,
243 struct device_attribute *attr,
244 char *buf)
245{
246 struct hda_codec *codec = dev_get_drvdata(dev);
247 int i, len = 0;
248 mutex_lock(&codec->user_mutex);
249 for (i = 0; i < codec->hints.used; i++) {
250 struct hda_hint *hint = snd_array_elem(&codec->hints, i);
251 len += snprintf(buf + len, PAGE_SIZE - len,
252 "%s = %s\n", hint->key, hint->val);
253 }
254 mutex_unlock(&codec->user_mutex);
255 return len;
256}
257
258static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
259{
260 int i;
261
262 for (i = 0; i < codec->hints.used; i++) {
263 struct hda_hint *hint = snd_array_elem(&codec->hints, i);
264 if (!strcmp(hint->key, key))
265 return hint;
266 }
267 return NULL;
268}
269
270static void remove_trail_spaces(char *str)
271{
272 char *p;
273 if (!*str)
274 return;
275 p = str + strlen(str) - 1;
276 for (; isspace(*p); p--) {
277 *p = 0;
278 if (p == str)
279 return;
280 }
281}
282
283#define MAX_HINTS 1024
284
285static int parse_hints(struct hda_codec *codec, const char *buf)
286{
287 char *key, *val;
288 struct hda_hint *hint;
289 int err = 0;
290
291 buf = skip_spaces(buf);
292 if (!*buf || *buf == '#' || *buf == '\n')
293 return 0;
294 if (*buf == '=')
295 return -EINVAL;
296 key = kstrndup_noeol(buf, 1024);
297 if (!key)
298 return -ENOMEM;
299 /* extract key and val */
300 val = strchr(key, '=');
301 if (!val) {
302 kfree(key);
303 return -EINVAL;
304 }
305 *val++ = 0;
306 val = skip_spaces(val);
307 remove_trail_spaces(key);
308 remove_trail_spaces(val);
309 mutex_lock(&codec->user_mutex);
310 hint = get_hint(codec, key);
311 if (hint) {
312 /* replace */
313 kfree(hint->key);
314 hint->key = key;
315 hint->val = val;
316 goto unlock;
317 }
318 /* allocate a new hint entry */
319 if (codec->hints.used >= MAX_HINTS)
320 hint = NULL;
321 else
322 hint = snd_array_new(&codec->hints);
323 if (hint) {
324 hint->key = key;
325 hint->val = val;
326 } else {
327 err = -ENOMEM;
328 }
329 unlock:
330 mutex_unlock(&codec->user_mutex);
331 if (err)
332 kfree(key);
333 return err;
334}
335
336static ssize_t hints_store(struct device *dev,
337 struct device_attribute *attr,
338 const char *buf, size_t count)
339{
340 struct hda_codec *codec = dev_get_drvdata(dev);
341 int err = parse_hints(codec, buf);
342 if (err < 0)
343 return err;
344 return count;
345}
346
347static ssize_t pin_configs_show(struct hda_codec *codec,
348 struct snd_array *list,
349 char *buf)
350{
351 int i, len = 0;
352 mutex_lock(&codec->user_mutex);
353 for (i = 0; i < list->used; i++) {
354 struct hda_pincfg *pin = snd_array_elem(list, i);
355 len += sprintf(buf + len, "0x%02x 0x%08x\n",
356 pin->nid, pin->cfg);
357 }
358 mutex_unlock(&codec->user_mutex);
359 return len;
360}
361
362static ssize_t init_pin_configs_show(struct device *dev,
363 struct device_attribute *attr,
364 char *buf)
365{
366 struct hda_codec *codec = dev_get_drvdata(dev);
367 return pin_configs_show(codec, &codec->init_pins, buf);
368}
369
370static ssize_t user_pin_configs_show(struct device *dev,
371 struct device_attribute *attr,
372 char *buf)
373{
374 struct hda_codec *codec = dev_get_drvdata(dev);
375 return pin_configs_show(codec, &codec->user_pins, buf);
376}
377
378static ssize_t driver_pin_configs_show(struct device *dev,
379 struct device_attribute *attr,
380 char *buf)
381{
382 struct hda_codec *codec = dev_get_drvdata(dev);
383 return pin_configs_show(codec, &codec->driver_pins, buf);
384}
385
386#define MAX_PIN_CONFIGS 32
387
388static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
389{
390 int nid, cfg, err;
391
392 if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
393 return -EINVAL;
394 if (!nid)
395 return -EINVAL;
396 mutex_lock(&codec->user_mutex);
397 err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
398 mutex_unlock(&codec->user_mutex);
399 return err;
400}
401
402static ssize_t user_pin_configs_store(struct device *dev,
403 struct device_attribute *attr,
404 const char *buf, size_t count)
405{
406 struct hda_codec *codec = dev_get_drvdata(dev);
407 int err = parse_user_pin_configs(codec, buf);
408 if (err < 0)
409 return err;
410 return count;
411}
412
413static DEVICE_ATTR_RW(vendor_id);
414static DEVICE_ATTR_RW(subsystem_id);
415static DEVICE_ATTR_RW(revision_id);
416static DEVICE_ATTR_RO(afg);
417static DEVICE_ATTR_RO(mfg);
418static DEVICE_ATTR_RW(vendor_name);
419static DEVICE_ATTR_RW(chip_name);
420static DEVICE_ATTR_RW(modelname);
421static DEVICE_ATTR_RW(init_verbs);
422static DEVICE_ATTR_RW(hints);
423static DEVICE_ATTR_RO(init_pin_configs);
424static DEVICE_ATTR_RW(user_pin_configs);
425static DEVICE_ATTR_RO(driver_pin_configs);
426static DEVICE_ATTR_WO(reconfig);
427static DEVICE_ATTR_WO(clear);
428
429/*
430 * Look for hint string
431 */
432const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
433{
434 struct hda_hint *hint = get_hint(codec, key);
435 return hint ? hint->val : NULL;
436}
437EXPORT_SYMBOL_GPL(snd_hda_get_hint);
438
439int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
440{
441 const char *p;
442 int ret;
443
444 mutex_lock(&codec->user_mutex);
445 p = snd_hda_get_hint(codec, key);
446 if (!p || !*p)
447 ret = -ENOENT;
448 else {
449 switch (toupper(*p)) {
450 case 'T': /* true */
451 case 'Y': /* yes */
452 case '1':
453 ret = 1;
454 break;
455 default:
456 ret = 0;
457 break;
458 }
459 }
460 mutex_unlock(&codec->user_mutex);
461 return ret;
462}
463EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
464
465int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
466{
467 const char *p;
468 unsigned long val;
469 int ret;
470
471 mutex_lock(&codec->user_mutex);
472 p = snd_hda_get_hint(codec, key);
473 if (!p)
474 ret = -ENOENT;
475 else if (kstrtoul(p, 0, &val))
476 ret = -EINVAL;
477 else {
478 *valp = val;
479 ret = 0;
480 }
481 mutex_unlock(&codec->user_mutex);
482 return ret;
483}
484EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
485#endif /* CONFIG_SND_HDA_RECONFIG */
486
487#ifdef CONFIG_SND_HDA_PATCH_LOADER
488
489/* parser mode */
490enum {
491 LINE_MODE_NONE,
492 LINE_MODE_CODEC,
493 LINE_MODE_MODEL,
494 LINE_MODE_PINCFG,
495 LINE_MODE_VERB,
496 LINE_MODE_HINT,
497 LINE_MODE_VENDOR_ID,
498 LINE_MODE_SUBSYSTEM_ID,
499 LINE_MODE_REVISION_ID,
500 LINE_MODE_CHIP_NAME,
501 NUM_LINE_MODES,
502};
503
504static inline int strmatch(const char *a, const char *b)
505{
506 return strnicmp(a, b, strlen(b)) == 0;
507}
508
509/* parse the contents after the line "[codec]"
510 * accept only the line with three numbers, and assign the current codec
511 */
512static void parse_codec_mode(char *buf, struct hda_bus *bus,
513 struct hda_codec **codecp)
514{
515 int vendorid, subid, caddr;
516 struct hda_codec *codec;
517
518 *codecp = NULL;
519 if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
520 list_for_each_entry(codec, &bus->codec_list, list) {
521 if ((vendorid <= 0 || codec->vendor_id == vendorid) &&
522 (subid <= 0 || codec->subsystem_id == subid) &&
523 codec->addr == caddr) {
524 *codecp = codec;
525 break;
526 }
527 }
528 }
529}
530
531/* parse the contents after the other command tags, [pincfg], [verb],
532 * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
533 * just pass to the sysfs helper (only when any codec was specified)
534 */
535static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
536 struct hda_codec **codecp)
537{
538 parse_user_pin_configs(*codecp, buf);
539}
540
541static void parse_verb_mode(char *buf, struct hda_bus *bus,
542 struct hda_codec **codecp)
543{
544 parse_init_verbs(*codecp, buf);
545}
546
547static void parse_hint_mode(char *buf, struct hda_bus *bus,
548 struct hda_codec **codecp)
549{
550 parse_hints(*codecp, buf);
551}
552
553static void parse_model_mode(char *buf, struct hda_bus *bus,
554 struct hda_codec **codecp)
555{
556 kfree((*codecp)->modelname);
557 (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
558}
559
560static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
561 struct hda_codec **codecp)
562{
563 kfree((*codecp)->chip_name);
564 (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL);
565}
566
567#define DEFINE_PARSE_ID_MODE(name) \
568static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
569 struct hda_codec **codecp) \
570{ \
571 unsigned long val; \
572 if (!kstrtoul(buf, 0, &val)) \
573 (*codecp)->name = val; \
574}
575
576DEFINE_PARSE_ID_MODE(vendor_id);
577DEFINE_PARSE_ID_MODE(subsystem_id);
578DEFINE_PARSE_ID_MODE(revision_id);
579
580
581struct hda_patch_item {
582 const char *tag;
583 const char *alias;
584 void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
585};
586
587static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
588 [LINE_MODE_CODEC] = {
589 .tag = "[codec]",
590 .parser = parse_codec_mode,
591 },
592 [LINE_MODE_MODEL] = {
593 .tag = "[model]",
594 .parser = parse_model_mode,
595 },
596 [LINE_MODE_VERB] = {
597 .tag = "[verb]",
598 .alias = "[init_verbs]",
599 .parser = parse_verb_mode,
600 },
601 [LINE_MODE_PINCFG] = {
602 .tag = "[pincfg]",
603 .alias = "[user_pin_configs]",
604 .parser = parse_pincfg_mode,
605 },
606 [LINE_MODE_HINT] = {
607 .tag = "[hint]",
608 .alias = "[hints]",
609 .parser = parse_hint_mode
610 },
611 [LINE_MODE_VENDOR_ID] = {
612 .tag = "[vendor_id]",
613 .parser = parse_vendor_id_mode,
614 },
615 [LINE_MODE_SUBSYSTEM_ID] = {
616 .tag = "[subsystem_id]",
617 .parser = parse_subsystem_id_mode,
618 },
619 [LINE_MODE_REVISION_ID] = {
620 .tag = "[revision_id]",
621 .parser = parse_revision_id_mode,
622 },
623 [LINE_MODE_CHIP_NAME] = {
624 .tag = "[chip_name]",
625 .parser = parse_chip_name_mode,
626 },
627};
628
629/* check the line starting with '[' -- change the parser mode accodingly */
630static int parse_line_mode(char *buf, struct hda_bus *bus)
631{
632 int i;
633 for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
634 if (!patch_items[i].tag)
635 continue;
636 if (strmatch(buf, patch_items[i].tag))
637 return i;
638 if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
639 return i;
640 }
641 return LINE_MODE_NONE;
642}
643
644/* copy one line from the buffer in fw, and update the fields in fw
645 * return zero if it reaches to the end of the buffer, or non-zero
646 * if successfully copied a line
647 *
648 * the spaces at the beginning and the end of the line are stripped
649 */
650static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
651 const void **fw_data_p)
652{
653 int len;
654 size_t fw_size = *fw_size_p;
655 const char *p = *fw_data_p;
656
657 while (isspace(*p) && fw_size) {
658 p++;
659 fw_size--;
660 }
661 if (!fw_size)
662 return 0;
663
664 for (len = 0; len < fw_size; len++) {
665 if (!*p)
666 break;
667 if (*p == '\n') {
668 p++;
669 len++;
670 break;
671 }
672 if (len < size)
673 *buf++ = *p++;
674 }
675 *buf = 0;
676 *fw_size_p = fw_size - len;
677 *fw_data_p = p;
678 remove_trail_spaces(buf);
679 return 1;
680}
681
682/*
683 * load a "patch" firmware file and parse it
684 */
685int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
686{
687 char buf[128];
688 struct hda_codec *codec;
689 int line_mode;
690
691 line_mode = LINE_MODE_NONE;
692 codec = NULL;
693 while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
694 if (!*buf || *buf == '#' || *buf == '\n')
695 continue;
696 if (*buf == '[')
697 line_mode = parse_line_mode(buf, bus);
698 else if (patch_items[line_mode].parser &&
699 (codec || line_mode <= LINE_MODE_CODEC))
700 patch_items[line_mode].parser(buf, bus, &codec);
701 }
702 return 0;
703}
704EXPORT_SYMBOL_GPL(snd_hda_load_patch);
705#endif /* CONFIG_SND_HDA_PATCH_LOADER */
706
707/*
708 * sysfs entries
709 */
710static struct attribute *hda_dev_attrs[] = {
711#ifdef CONFIG_PM
712 &dev_attr_power_on_acct.attr,
713 &dev_attr_power_off_acct.attr,
714#endif
715#ifdef CONFIG_SND_HDA_RECONFIG
716 &dev_attr_vendor_id.attr,
717 &dev_attr_subsystem_id.attr,
718 &dev_attr_revision_id.attr,
719 &dev_attr_afg.attr,
720 &dev_attr_mfg.attr,
721 &dev_attr_vendor_name.attr,
722 &dev_attr_chip_name.attr,
723 &dev_attr_modelname.attr,
724 &dev_attr_init_verbs.attr,
725 &dev_attr_hints.attr,
726 &dev_attr_init_pin_configs.attr,
727 &dev_attr_user_pin_configs.attr,
728 &dev_attr_driver_pin_configs.attr,
729 &dev_attr_reconfig.attr,
730 &dev_attr_clear.attr,
731#endif
732 NULL
733};
734
735static struct attribute_group hda_dev_attr_group = {
736 .attrs = hda_dev_attrs,
737};
738
739const struct attribute_group *snd_hda_dev_attr_groups[] = {
740 &hda_dev_attr_group,
741 NULL
742};
743
744void snd_hda_sysfs_init(struct hda_codec *codec)
745{
746#ifdef CONFIG_SND_HDA_RECONFIG
747 mutex_init(&codec->user_mutex);
748 snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
749 snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
750 snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
751#endif
752}
753
754void snd_hda_sysfs_clear(struct hda_codec *codec)
755{
756#ifdef CONFIG_SND_HDA_RECONFIG
757 int i;
758
759 /* clear init verbs */
760 snd_array_free(&codec->init_verbs);
761 /* clear hints */
762 for (i = 0; i < codec->hints.used; i++) {
763 struct hda_hint *hint = snd_array_elem(&codec->hints, i);
764 kfree(hint->key); /* we don't need to free hint->val */
765 }
766 snd_array_free(&codec->hints);
767 snd_array_free(&codec->user_pins);
768#endif
769}
This page took 0.115003 seconds and 5 git commands to generate.