File Index
Files and directories
Sign in
FILE

sysfs.c

File preview with syntax highlighting.

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