162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sysfs interface for HD-audio codec 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * split from hda_hwdep.c 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/compat.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/ctype.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/export.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/hda_codec.h> 1962306a36Sopenharmony_ci#include "hda_local.h" 2062306a36Sopenharmony_ci#include <sound/hda_hwdep.h> 2162306a36Sopenharmony_ci#include <sound/minors.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* hint string pair */ 2462306a36Sopenharmony_cistruct hda_hint { 2562306a36Sopenharmony_ci const char *key; 2662306a36Sopenharmony_ci const char *val; /* contained in the same alloc as key */ 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#ifdef CONFIG_PM 3062306a36Sopenharmony_cistatic ssize_t power_on_acct_show(struct device *dev, 3162306a36Sopenharmony_ci struct device_attribute *attr, 3262306a36Sopenharmony_ci char *buf) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 3562306a36Sopenharmony_ci snd_hda_update_power_acct(codec); 3662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct)); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic ssize_t power_off_acct_show(struct device *dev, 4062306a36Sopenharmony_ci struct device_attribute *attr, 4162306a36Sopenharmony_ci char *buf) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 4462306a36Sopenharmony_ci snd_hda_update_power_acct(codec); 4562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(power_on_acct); 4962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(power_off_acct); 5062306a36Sopenharmony_ci#endif /* CONFIG_PM */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define CODEC_INFO_SHOW(type, field) \ 5362306a36Sopenharmony_cistatic ssize_t type##_show(struct device *dev, \ 5462306a36Sopenharmony_ci struct device_attribute *attr, \ 5562306a36Sopenharmony_ci char *buf) \ 5662306a36Sopenharmony_ci{ \ 5762306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); \ 5862306a36Sopenharmony_ci return sysfs_emit(buf, "0x%x\n", codec->field); \ 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define CODEC_INFO_STR_SHOW(type, field) \ 6262306a36Sopenharmony_cistatic ssize_t type##_show(struct device *dev, \ 6362306a36Sopenharmony_ci struct device_attribute *attr, \ 6462306a36Sopenharmony_ci char *buf) \ 6562306a36Sopenharmony_ci{ \ 6662306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); \ 6762306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", \ 6862306a36Sopenharmony_ci codec->field ? codec->field : ""); \ 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciCODEC_INFO_SHOW(vendor_id, core.vendor_id); 7262306a36Sopenharmony_ciCODEC_INFO_SHOW(subsystem_id, core.subsystem_id); 7362306a36Sopenharmony_ciCODEC_INFO_SHOW(revision_id, core.revision_id); 7462306a36Sopenharmony_ciCODEC_INFO_SHOW(afg, core.afg); 7562306a36Sopenharmony_ciCODEC_INFO_SHOW(mfg, core.mfg); 7662306a36Sopenharmony_ciCODEC_INFO_STR_SHOW(vendor_name, core.vendor_name); 7762306a36Sopenharmony_ciCODEC_INFO_STR_SHOW(chip_name, core.chip_name); 7862306a36Sopenharmony_ciCODEC_INFO_STR_SHOW(modelname, modelname); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic ssize_t pin_configs_show(struct hda_codec *codec, 8162306a36Sopenharmony_ci struct snd_array *list, 8262306a36Sopenharmony_ci char *buf) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci const struct hda_pincfg *pin; 8562306a36Sopenharmony_ci int i, len = 0; 8662306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 8762306a36Sopenharmony_ci snd_array_for_each(list, i, pin) { 8862306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n", 8962306a36Sopenharmony_ci pin->nid, pin->cfg); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 9262306a36Sopenharmony_ci return len; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic ssize_t init_pin_configs_show(struct device *dev, 9662306a36Sopenharmony_ci struct device_attribute *attr, 9762306a36Sopenharmony_ci char *buf) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 10062306a36Sopenharmony_ci return pin_configs_show(codec, &codec->init_pins, buf); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic ssize_t driver_pin_configs_show(struct device *dev, 10462306a36Sopenharmony_ci struct device_attribute *attr, 10562306a36Sopenharmony_ci char *buf) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 10862306a36Sopenharmony_ci return pin_configs_show(codec, &codec->driver_pins, buf); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * sysfs interface 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int clear_codec(struct hda_codec *codec) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int err; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci err = snd_hda_codec_reset(codec); 12262306a36Sopenharmony_ci if (err < 0) { 12362306a36Sopenharmony_ci codec_err(codec, "The codec is being used, can't free.\n"); 12462306a36Sopenharmony_ci return err; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci snd_hda_sysfs_clear(codec); 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int reconfig_codec(struct hda_codec *codec) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int err; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci snd_hda_power_up(codec); 13562306a36Sopenharmony_ci codec_info(codec, "hda-codec: reconfiguring\n"); 13662306a36Sopenharmony_ci err = snd_hda_codec_reset(codec); 13762306a36Sopenharmony_ci if (err < 0) { 13862306a36Sopenharmony_ci codec_err(codec, 13962306a36Sopenharmony_ci "The codec is being used, can't reconfigure.\n"); 14062306a36Sopenharmony_ci goto error; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci err = device_reprobe(hda_codec_dev(codec)); 14362306a36Sopenharmony_ci if (err < 0) 14462306a36Sopenharmony_ci goto error; 14562306a36Sopenharmony_ci err = snd_card_register(codec->card); 14662306a36Sopenharmony_ci error: 14762306a36Sopenharmony_ci snd_hda_power_down(codec); 14862306a36Sopenharmony_ci return err; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * allocate a string at most len chars, and remove the trailing EOL 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic char *kstrndup_noeol(const char *src, size_t len) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci char *s = kstrndup(src, len, GFP_KERNEL); 15762306a36Sopenharmony_ci char *p; 15862306a36Sopenharmony_ci if (!s) 15962306a36Sopenharmony_ci return NULL; 16062306a36Sopenharmony_ci p = strchr(s, '\n'); 16162306a36Sopenharmony_ci if (p) 16262306a36Sopenharmony_ci *p = 0; 16362306a36Sopenharmony_ci return s; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define CODEC_INFO_STORE(type, field) \ 16762306a36Sopenharmony_cistatic ssize_t type##_store(struct device *dev, \ 16862306a36Sopenharmony_ci struct device_attribute *attr, \ 16962306a36Sopenharmony_ci const char *buf, size_t count) \ 17062306a36Sopenharmony_ci{ \ 17162306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); \ 17262306a36Sopenharmony_ci unsigned long val; \ 17362306a36Sopenharmony_ci int err = kstrtoul(buf, 0, &val); \ 17462306a36Sopenharmony_ci if (err < 0) \ 17562306a36Sopenharmony_ci return err; \ 17662306a36Sopenharmony_ci codec->field = val; \ 17762306a36Sopenharmony_ci return count; \ 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#define CODEC_INFO_STR_STORE(type, field) \ 18162306a36Sopenharmony_cistatic ssize_t type##_store(struct device *dev, \ 18262306a36Sopenharmony_ci struct device_attribute *attr, \ 18362306a36Sopenharmony_ci const char *buf, size_t count) \ 18462306a36Sopenharmony_ci{ \ 18562306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); \ 18662306a36Sopenharmony_ci char *s = kstrndup_noeol(buf, 64); \ 18762306a36Sopenharmony_ci if (!s) \ 18862306a36Sopenharmony_ci return -ENOMEM; \ 18962306a36Sopenharmony_ci kfree(codec->field); \ 19062306a36Sopenharmony_ci codec->field = s; \ 19162306a36Sopenharmony_ci return count; \ 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciCODEC_INFO_STORE(vendor_id, core.vendor_id); 19562306a36Sopenharmony_ciCODEC_INFO_STORE(subsystem_id, core.subsystem_id); 19662306a36Sopenharmony_ciCODEC_INFO_STORE(revision_id, core.revision_id); 19762306a36Sopenharmony_ciCODEC_INFO_STR_STORE(vendor_name, core.vendor_name); 19862306a36Sopenharmony_ciCODEC_INFO_STR_STORE(chip_name, core.chip_name); 19962306a36Sopenharmony_ciCODEC_INFO_STR_STORE(modelname, modelname); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define CODEC_ACTION_STORE(type) \ 20262306a36Sopenharmony_cistatic ssize_t type##_store(struct device *dev, \ 20362306a36Sopenharmony_ci struct device_attribute *attr, \ 20462306a36Sopenharmony_ci const char *buf, size_t count) \ 20562306a36Sopenharmony_ci{ \ 20662306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); \ 20762306a36Sopenharmony_ci int err = 0; \ 20862306a36Sopenharmony_ci if (*buf) \ 20962306a36Sopenharmony_ci err = type##_codec(codec); \ 21062306a36Sopenharmony_ci return err < 0 ? err : count; \ 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciCODEC_ACTION_STORE(reconfig); 21462306a36Sopenharmony_ciCODEC_ACTION_STORE(clear); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic ssize_t init_verbs_show(struct device *dev, 21762306a36Sopenharmony_ci struct device_attribute *attr, 21862306a36Sopenharmony_ci char *buf) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 22162306a36Sopenharmony_ci const struct hda_verb *v; 22262306a36Sopenharmony_ci int i, len = 0; 22362306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 22462306a36Sopenharmony_ci snd_array_for_each(&codec->init_verbs, i, v) { 22562306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n", 22662306a36Sopenharmony_ci v->nid, v->verb, v->param); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 22962306a36Sopenharmony_ci return len; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int parse_init_verbs(struct hda_codec *codec, const char *buf) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct hda_verb *v; 23562306a36Sopenharmony_ci int nid, verb, param; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (sscanf(buf, "%i %i %i", &nid, &verb, ¶m) != 3) 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci if (!nid || !verb) 24062306a36Sopenharmony_ci return -EINVAL; 24162306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 24262306a36Sopenharmony_ci v = snd_array_new(&codec->init_verbs); 24362306a36Sopenharmony_ci if (!v) { 24462306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 24562306a36Sopenharmony_ci return -ENOMEM; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci v->nid = nid; 24862306a36Sopenharmony_ci v->verb = verb; 24962306a36Sopenharmony_ci v->param = param; 25062306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic ssize_t init_verbs_store(struct device *dev, 25562306a36Sopenharmony_ci struct device_attribute *attr, 25662306a36Sopenharmony_ci const char *buf, size_t count) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 25962306a36Sopenharmony_ci int err = parse_init_verbs(codec, buf); 26062306a36Sopenharmony_ci if (err < 0) 26162306a36Sopenharmony_ci return err; 26262306a36Sopenharmony_ci return count; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic ssize_t hints_show(struct device *dev, 26662306a36Sopenharmony_ci struct device_attribute *attr, 26762306a36Sopenharmony_ci char *buf) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 27062306a36Sopenharmony_ci const struct hda_hint *hint; 27162306a36Sopenharmony_ci int i, len = 0; 27262306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 27362306a36Sopenharmony_ci snd_array_for_each(&codec->hints, i, hint) { 27462306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%s = %s\n", 27562306a36Sopenharmony_ci hint->key, hint->val); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 27862306a36Sopenharmony_ci return len; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic struct hda_hint *get_hint(struct hda_codec *codec, const char *key) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct hda_hint *hint; 28462306a36Sopenharmony_ci int i; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci snd_array_for_each(&codec->hints, i, hint) { 28762306a36Sopenharmony_ci if (!strcmp(hint->key, key)) 28862306a36Sopenharmony_ci return hint; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return NULL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void remove_trail_spaces(char *str) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci char *p; 29662306a36Sopenharmony_ci if (!*str) 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci p = str + strlen(str) - 1; 29962306a36Sopenharmony_ci for (; isspace(*p); p--) { 30062306a36Sopenharmony_ci *p = 0; 30162306a36Sopenharmony_ci if (p == str) 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci#define MAX_HINTS 1024 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int parse_hints(struct hda_codec *codec, const char *buf) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci char *key, *val; 31162306a36Sopenharmony_ci struct hda_hint *hint; 31262306a36Sopenharmony_ci int err = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci buf = skip_spaces(buf); 31562306a36Sopenharmony_ci if (!*buf || *buf == '#' || *buf == '\n') 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci if (*buf == '=') 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci key = kstrndup_noeol(buf, 1024); 32062306a36Sopenharmony_ci if (!key) 32162306a36Sopenharmony_ci return -ENOMEM; 32262306a36Sopenharmony_ci /* extract key and val */ 32362306a36Sopenharmony_ci val = strchr(key, '='); 32462306a36Sopenharmony_ci if (!val) { 32562306a36Sopenharmony_ci kfree(key); 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci *val++ = 0; 32962306a36Sopenharmony_ci val = skip_spaces(val); 33062306a36Sopenharmony_ci remove_trail_spaces(key); 33162306a36Sopenharmony_ci remove_trail_spaces(val); 33262306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 33362306a36Sopenharmony_ci hint = get_hint(codec, key); 33462306a36Sopenharmony_ci if (hint) { 33562306a36Sopenharmony_ci /* replace */ 33662306a36Sopenharmony_ci kfree(hint->key); 33762306a36Sopenharmony_ci hint->key = key; 33862306a36Sopenharmony_ci hint->val = val; 33962306a36Sopenharmony_ci goto unlock; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci /* allocate a new hint entry */ 34262306a36Sopenharmony_ci if (codec->hints.used >= MAX_HINTS) 34362306a36Sopenharmony_ci hint = NULL; 34462306a36Sopenharmony_ci else 34562306a36Sopenharmony_ci hint = snd_array_new(&codec->hints); 34662306a36Sopenharmony_ci if (hint) { 34762306a36Sopenharmony_ci hint->key = key; 34862306a36Sopenharmony_ci hint->val = val; 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci err = -ENOMEM; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci unlock: 35362306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 35462306a36Sopenharmony_ci if (err) 35562306a36Sopenharmony_ci kfree(key); 35662306a36Sopenharmony_ci return err; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic ssize_t hints_store(struct device *dev, 36062306a36Sopenharmony_ci struct device_attribute *attr, 36162306a36Sopenharmony_ci const char *buf, size_t count) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 36462306a36Sopenharmony_ci int err = parse_hints(codec, buf); 36562306a36Sopenharmony_ci if (err < 0) 36662306a36Sopenharmony_ci return err; 36762306a36Sopenharmony_ci return count; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic ssize_t user_pin_configs_show(struct device *dev, 37162306a36Sopenharmony_ci struct device_attribute *attr, 37262306a36Sopenharmony_ci char *buf) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 37562306a36Sopenharmony_ci return pin_configs_show(codec, &codec->user_pins, buf); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int parse_user_pin_configs(struct hda_codec *codec, const char *buf) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci int nid, cfg, err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (sscanf(buf, "%i %i", &nid, &cfg) != 2) 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci if (!nid) 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 38762306a36Sopenharmony_ci err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); 38862306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic ssize_t user_pin_configs_store(struct device *dev, 39362306a36Sopenharmony_ci struct device_attribute *attr, 39462306a36Sopenharmony_ci const char *buf, size_t count) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct hda_codec *codec = dev_get_drvdata(dev); 39762306a36Sopenharmony_ci int err = parse_user_pin_configs(codec, buf); 39862306a36Sopenharmony_ci if (err < 0) 39962306a36Sopenharmony_ci return err; 40062306a36Sopenharmony_ci return count; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */ 40462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(init_verbs); 40562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(hints); 40662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(user_pin_configs); 40762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(reconfig); 40862306a36Sopenharmony_cistatic DEVICE_ATTR_WO(clear); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/** 41162306a36Sopenharmony_ci * snd_hda_get_hint - Look for hint string 41262306a36Sopenharmony_ci * @codec: the HDA codec 41362306a36Sopenharmony_ci * @key: the hint key string 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * Look for a hint key/value pair matching with the given key string 41662306a36Sopenharmony_ci * and returns the value string. If nothing found, returns NULL. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ciconst char *snd_hda_get_hint(struct hda_codec *codec, const char *key) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct hda_hint *hint = get_hint(codec, key); 42162306a36Sopenharmony_ci return hint ? hint->val : NULL; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_hint); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/** 42662306a36Sopenharmony_ci * snd_hda_get_bool_hint - Get a boolean hint value 42762306a36Sopenharmony_ci * @codec: the HDA codec 42862306a36Sopenharmony_ci * @key: the hint key string 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * Look for a hint key/value pair matching with the given key string 43162306a36Sopenharmony_ci * and returns a boolean value parsed from the value. If no matching 43262306a36Sopenharmony_ci * key is found, return a negative value. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ciint snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci const char *p; 43762306a36Sopenharmony_ci int ret; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 44062306a36Sopenharmony_ci p = snd_hda_get_hint(codec, key); 44162306a36Sopenharmony_ci if (!p || !*p) 44262306a36Sopenharmony_ci ret = -ENOENT; 44362306a36Sopenharmony_ci else { 44462306a36Sopenharmony_ci switch (toupper(*p)) { 44562306a36Sopenharmony_ci case 'T': /* true */ 44662306a36Sopenharmony_ci case 'Y': /* yes */ 44762306a36Sopenharmony_ci case '1': 44862306a36Sopenharmony_ci ret = 1; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci ret = 0; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_bool_hint); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/** 46162306a36Sopenharmony_ci * snd_hda_get_int_hint - Get an integer hint value 46262306a36Sopenharmony_ci * @codec: the HDA codec 46362306a36Sopenharmony_ci * @key: the hint key string 46462306a36Sopenharmony_ci * @valp: pointer to store a value 46562306a36Sopenharmony_ci * 46662306a36Sopenharmony_ci * Look for a hint key/value pair matching with the given key string 46762306a36Sopenharmony_ci * and stores the integer value to @valp. If no matching key is found, 46862306a36Sopenharmony_ci * return a negative error code. Otherwise it returns zero. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ciint snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci const char *p; 47362306a36Sopenharmony_ci unsigned long val; 47462306a36Sopenharmony_ci int ret; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci mutex_lock(&codec->user_mutex); 47762306a36Sopenharmony_ci p = snd_hda_get_hint(codec, key); 47862306a36Sopenharmony_ci if (!p) 47962306a36Sopenharmony_ci ret = -ENOENT; 48062306a36Sopenharmony_ci else if (kstrtoul(p, 0, &val)) 48162306a36Sopenharmony_ci ret = -EINVAL; 48262306a36Sopenharmony_ci else { 48362306a36Sopenharmony_ci *valp = val; 48462306a36Sopenharmony_ci ret = 0; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci mutex_unlock(&codec->user_mutex); 48762306a36Sopenharmony_ci return ret; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_int_hint); 49062306a36Sopenharmony_ci#endif /* CONFIG_SND_HDA_RECONFIG */ 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/* 49362306a36Sopenharmony_ci * common sysfs attributes 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 49662306a36Sopenharmony_ci#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name) 49762306a36Sopenharmony_ci#else 49862306a36Sopenharmony_ci#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name) 49962306a36Sopenharmony_ci#endif 50062306a36Sopenharmony_cistatic RECONFIG_DEVICE_ATTR(vendor_id); 50162306a36Sopenharmony_cistatic RECONFIG_DEVICE_ATTR(subsystem_id); 50262306a36Sopenharmony_cistatic RECONFIG_DEVICE_ATTR(revision_id); 50362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(afg); 50462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(mfg); 50562306a36Sopenharmony_cistatic RECONFIG_DEVICE_ATTR(vendor_name); 50662306a36Sopenharmony_cistatic RECONFIG_DEVICE_ATTR(chip_name); 50762306a36Sopenharmony_cistatic RECONFIG_DEVICE_ATTR(modelname); 50862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(init_pin_configs); 50962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(driver_pin_configs); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_PATCH_LOADER 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* parser mode */ 51562306a36Sopenharmony_cienum { 51662306a36Sopenharmony_ci LINE_MODE_NONE, 51762306a36Sopenharmony_ci LINE_MODE_CODEC, 51862306a36Sopenharmony_ci LINE_MODE_MODEL, 51962306a36Sopenharmony_ci LINE_MODE_PINCFG, 52062306a36Sopenharmony_ci LINE_MODE_VERB, 52162306a36Sopenharmony_ci LINE_MODE_HINT, 52262306a36Sopenharmony_ci LINE_MODE_VENDOR_ID, 52362306a36Sopenharmony_ci LINE_MODE_SUBSYSTEM_ID, 52462306a36Sopenharmony_ci LINE_MODE_REVISION_ID, 52562306a36Sopenharmony_ci LINE_MODE_CHIP_NAME, 52662306a36Sopenharmony_ci NUM_LINE_MODES, 52762306a36Sopenharmony_ci}; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic inline int strmatch(const char *a, const char *b) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci return strncasecmp(a, b, strlen(b)) == 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/* parse the contents after the line "[codec]" 53562306a36Sopenharmony_ci * accept only the line with three numbers, and assign the current codec 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_cistatic void parse_codec_mode(char *buf, struct hda_bus *bus, 53862306a36Sopenharmony_ci struct hda_codec **codecp) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci int vendorid, subid, caddr; 54162306a36Sopenharmony_ci struct hda_codec *codec; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci *codecp = NULL; 54462306a36Sopenharmony_ci if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { 54562306a36Sopenharmony_ci list_for_each_codec(codec, bus) { 54662306a36Sopenharmony_ci if ((vendorid <= 0 || codec->core.vendor_id == vendorid) && 54762306a36Sopenharmony_ci (subid <= 0 || codec->core.subsystem_id == subid) && 54862306a36Sopenharmony_ci codec->core.addr == caddr) { 54962306a36Sopenharmony_ci *codecp = codec; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* parse the contents after the other command tags, [pincfg], [verb], 55762306a36Sopenharmony_ci * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model] 55862306a36Sopenharmony_ci * just pass to the sysfs helper (only when any codec was specified) 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic void parse_pincfg_mode(char *buf, struct hda_bus *bus, 56162306a36Sopenharmony_ci struct hda_codec **codecp) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci parse_user_pin_configs(*codecp, buf); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void parse_verb_mode(char *buf, struct hda_bus *bus, 56762306a36Sopenharmony_ci struct hda_codec **codecp) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci parse_init_verbs(*codecp, buf); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void parse_hint_mode(char *buf, struct hda_bus *bus, 57362306a36Sopenharmony_ci struct hda_codec **codecp) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci parse_hints(*codecp, buf); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void parse_model_mode(char *buf, struct hda_bus *bus, 57962306a36Sopenharmony_ci struct hda_codec **codecp) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci kfree((*codecp)->modelname); 58262306a36Sopenharmony_ci (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic void parse_chip_name_mode(char *buf, struct hda_bus *bus, 58662306a36Sopenharmony_ci struct hda_codec **codecp) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci snd_hda_codec_set_name(*codecp, buf); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci#define DEFINE_PARSE_ID_MODE(name) \ 59262306a36Sopenharmony_cistatic void parse_##name##_mode(char *buf, struct hda_bus *bus, \ 59362306a36Sopenharmony_ci struct hda_codec **codecp) \ 59462306a36Sopenharmony_ci{ \ 59562306a36Sopenharmony_ci unsigned long val; \ 59662306a36Sopenharmony_ci if (!kstrtoul(buf, 0, &val)) \ 59762306a36Sopenharmony_ci (*codecp)->core.name = val; \ 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ciDEFINE_PARSE_ID_MODE(vendor_id); 60162306a36Sopenharmony_ciDEFINE_PARSE_ID_MODE(subsystem_id); 60262306a36Sopenharmony_ciDEFINE_PARSE_ID_MODE(revision_id); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistruct hda_patch_item { 60662306a36Sopenharmony_ci const char *tag; 60762306a36Sopenharmony_ci const char *alias; 60862306a36Sopenharmony_ci void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); 60962306a36Sopenharmony_ci}; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic const struct hda_patch_item patch_items[NUM_LINE_MODES] = { 61262306a36Sopenharmony_ci [LINE_MODE_CODEC] = { 61362306a36Sopenharmony_ci .tag = "[codec]", 61462306a36Sopenharmony_ci .parser = parse_codec_mode, 61562306a36Sopenharmony_ci }, 61662306a36Sopenharmony_ci [LINE_MODE_MODEL] = { 61762306a36Sopenharmony_ci .tag = "[model]", 61862306a36Sopenharmony_ci .parser = parse_model_mode, 61962306a36Sopenharmony_ci }, 62062306a36Sopenharmony_ci [LINE_MODE_VERB] = { 62162306a36Sopenharmony_ci .tag = "[verb]", 62262306a36Sopenharmony_ci .alias = "[init_verbs]", 62362306a36Sopenharmony_ci .parser = parse_verb_mode, 62462306a36Sopenharmony_ci }, 62562306a36Sopenharmony_ci [LINE_MODE_PINCFG] = { 62662306a36Sopenharmony_ci .tag = "[pincfg]", 62762306a36Sopenharmony_ci .alias = "[user_pin_configs]", 62862306a36Sopenharmony_ci .parser = parse_pincfg_mode, 62962306a36Sopenharmony_ci }, 63062306a36Sopenharmony_ci [LINE_MODE_HINT] = { 63162306a36Sopenharmony_ci .tag = "[hint]", 63262306a36Sopenharmony_ci .alias = "[hints]", 63362306a36Sopenharmony_ci .parser = parse_hint_mode 63462306a36Sopenharmony_ci }, 63562306a36Sopenharmony_ci [LINE_MODE_VENDOR_ID] = { 63662306a36Sopenharmony_ci .tag = "[vendor_id]", 63762306a36Sopenharmony_ci .parser = parse_vendor_id_mode, 63862306a36Sopenharmony_ci }, 63962306a36Sopenharmony_ci [LINE_MODE_SUBSYSTEM_ID] = { 64062306a36Sopenharmony_ci .tag = "[subsystem_id]", 64162306a36Sopenharmony_ci .parser = parse_subsystem_id_mode, 64262306a36Sopenharmony_ci }, 64362306a36Sopenharmony_ci [LINE_MODE_REVISION_ID] = { 64462306a36Sopenharmony_ci .tag = "[revision_id]", 64562306a36Sopenharmony_ci .parser = parse_revision_id_mode, 64662306a36Sopenharmony_ci }, 64762306a36Sopenharmony_ci [LINE_MODE_CHIP_NAME] = { 64862306a36Sopenharmony_ci .tag = "[chip_name]", 64962306a36Sopenharmony_ci .parser = parse_chip_name_mode, 65062306a36Sopenharmony_ci }, 65162306a36Sopenharmony_ci}; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* check the line starting with '[' -- change the parser mode accodingly */ 65462306a36Sopenharmony_cistatic int parse_line_mode(char *buf, struct hda_bus *bus) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci int i; 65762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(patch_items); i++) { 65862306a36Sopenharmony_ci if (!patch_items[i].tag) 65962306a36Sopenharmony_ci continue; 66062306a36Sopenharmony_ci if (strmatch(buf, patch_items[i].tag)) 66162306a36Sopenharmony_ci return i; 66262306a36Sopenharmony_ci if (patch_items[i].alias && strmatch(buf, patch_items[i].alias)) 66362306a36Sopenharmony_ci return i; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci return LINE_MODE_NONE; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* copy one line from the buffer in fw, and update the fields in fw 66962306a36Sopenharmony_ci * return zero if it reaches to the end of the buffer, or non-zero 67062306a36Sopenharmony_ci * if successfully copied a line 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * the spaces at the beginning and the end of the line are stripped 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_cistatic int get_line_from_fw(char *buf, int size, size_t *fw_size_p, 67562306a36Sopenharmony_ci const void **fw_data_p) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci int len; 67862306a36Sopenharmony_ci size_t fw_size = *fw_size_p; 67962306a36Sopenharmony_ci const char *p = *fw_data_p; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci while (isspace(*p) && fw_size) { 68262306a36Sopenharmony_ci p++; 68362306a36Sopenharmony_ci fw_size--; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci if (!fw_size) 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci for (len = 0; len < fw_size; len++) { 68962306a36Sopenharmony_ci if (!*p) 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci if (*p == '\n') { 69262306a36Sopenharmony_ci p++; 69362306a36Sopenharmony_ci len++; 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci if (len < size) 69762306a36Sopenharmony_ci *buf++ = *p++; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci *buf = 0; 70062306a36Sopenharmony_ci *fw_size_p = fw_size - len; 70162306a36Sopenharmony_ci *fw_data_p = p; 70262306a36Sopenharmony_ci remove_trail_spaces(buf); 70362306a36Sopenharmony_ci return 1; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/** 70762306a36Sopenharmony_ci * snd_hda_load_patch - load a "patch" firmware file and parse it 70862306a36Sopenharmony_ci * @bus: HD-audio bus 70962306a36Sopenharmony_ci * @fw_size: the firmware byte size 71062306a36Sopenharmony_ci * @fw_buf: the firmware data 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ciint snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci char buf[128]; 71562306a36Sopenharmony_ci struct hda_codec *codec; 71662306a36Sopenharmony_ci int line_mode; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci line_mode = LINE_MODE_NONE; 71962306a36Sopenharmony_ci codec = NULL; 72062306a36Sopenharmony_ci while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) { 72162306a36Sopenharmony_ci if (!*buf || *buf == '#' || *buf == '\n') 72262306a36Sopenharmony_ci continue; 72362306a36Sopenharmony_ci if (*buf == '[') 72462306a36Sopenharmony_ci line_mode = parse_line_mode(buf, bus); 72562306a36Sopenharmony_ci else if (patch_items[line_mode].parser && 72662306a36Sopenharmony_ci (codec || line_mode <= LINE_MODE_CODEC)) 72762306a36Sopenharmony_ci patch_items[line_mode].parser(buf, bus, &codec); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_load_patch); 73262306a36Sopenharmony_ci#endif /* CONFIG_SND_HDA_PATCH_LOADER */ 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* 73562306a36Sopenharmony_ci * sysfs entries 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_cistatic struct attribute *hda_dev_attrs[] = { 73862306a36Sopenharmony_ci &dev_attr_vendor_id.attr, 73962306a36Sopenharmony_ci &dev_attr_subsystem_id.attr, 74062306a36Sopenharmony_ci &dev_attr_revision_id.attr, 74162306a36Sopenharmony_ci &dev_attr_afg.attr, 74262306a36Sopenharmony_ci &dev_attr_mfg.attr, 74362306a36Sopenharmony_ci &dev_attr_vendor_name.attr, 74462306a36Sopenharmony_ci &dev_attr_chip_name.attr, 74562306a36Sopenharmony_ci &dev_attr_modelname.attr, 74662306a36Sopenharmony_ci &dev_attr_init_pin_configs.attr, 74762306a36Sopenharmony_ci &dev_attr_driver_pin_configs.attr, 74862306a36Sopenharmony_ci#ifdef CONFIG_PM 74962306a36Sopenharmony_ci &dev_attr_power_on_acct.attr, 75062306a36Sopenharmony_ci &dev_attr_power_off_acct.attr, 75162306a36Sopenharmony_ci#endif 75262306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 75362306a36Sopenharmony_ci &dev_attr_init_verbs.attr, 75462306a36Sopenharmony_ci &dev_attr_hints.attr, 75562306a36Sopenharmony_ci &dev_attr_user_pin_configs.attr, 75662306a36Sopenharmony_ci &dev_attr_reconfig.attr, 75762306a36Sopenharmony_ci &dev_attr_clear.attr, 75862306a36Sopenharmony_ci#endif 75962306a36Sopenharmony_ci NULL 76062306a36Sopenharmony_ci}; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic const struct attribute_group hda_dev_attr_group = { 76362306a36Sopenharmony_ci .attrs = hda_dev_attrs, 76462306a36Sopenharmony_ci}; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciconst struct attribute_group *snd_hda_dev_attr_groups[] = { 76762306a36Sopenharmony_ci &hda_dev_attr_group, 76862306a36Sopenharmony_ci NULL 76962306a36Sopenharmony_ci}; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_civoid snd_hda_sysfs_init(struct hda_codec *codec) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci mutex_init(&codec->user_mutex); 77462306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 77562306a36Sopenharmony_ci snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); 77662306a36Sopenharmony_ci snd_array_init(&codec->hints, sizeof(struct hda_hint), 32); 77762306a36Sopenharmony_ci snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); 77862306a36Sopenharmony_ci#endif 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_civoid snd_hda_sysfs_clear(struct hda_codec *codec) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 78462306a36Sopenharmony_ci struct hda_hint *hint; 78562306a36Sopenharmony_ci int i; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* clear init verbs */ 78862306a36Sopenharmony_ci snd_array_free(&codec->init_verbs); 78962306a36Sopenharmony_ci /* clear hints */ 79062306a36Sopenharmony_ci snd_array_for_each(&codec->hints, i, hint) { 79162306a36Sopenharmony_ci kfree(hint->key); /* we don't need to free hint->val */ 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci snd_array_free(&codec->hints); 79462306a36Sopenharmony_ci snd_array_free(&codec->user_pins); 79562306a36Sopenharmony_ci#endif 79662306a36Sopenharmony_ci} 797