brintos

brintos / linux-shallow public Read only

0
0
Text · 18.6 KiB · 265fd47 Raw
793 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sysfs interface for HD-audio codec4 *5 * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>6 *7 * split from hda_hwdep.c8 */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	mutex_lock(&codec->user_mutex);85	snd_array_for_each(list, i, pin) {86		len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n",87				     pin->nid, pin->cfg);88	}89	mutex_unlock(&codec->user_mutex);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_RECONFIG110 111/*112 * sysfs interface113 */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	snd_hda_power_up(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		goto error;139	}140	err = device_reprobe(hda_codec_dev(codec));141	if (err < 0)142		goto error;143	err = snd_card_register(codec->card);144 error:145	snd_hda_power_down(codec);146	return err;147}148 149/*150 * allocate a string at most len chars, and remove the trailing EOL151 */152static char *kstrndup_noeol(const char *src, size_t len)153{154	char *s = kstrndup(src, len, GFP_KERNEL);155	char *p;156	if (!s)157		return NULL;158	p = strchr(s, '\n');159	if (p)160		*p = 0;161	return s;162}163 164#define CODEC_INFO_STORE(type, field)				\165static ssize_t type##_store(struct device *dev,			\166			    struct device_attribute *attr,	\167			    const char *buf, size_t count)	\168{								\169	struct hda_codec *codec = dev_get_drvdata(dev);		\170	unsigned long val;					\171	int err = kstrtoul(buf, 0, &val);			\172	if (err < 0)						\173		return err;					\174	codec->field = val;					\175	return count;						\176}177 178#define CODEC_INFO_STR_STORE(type, field)			\179static ssize_t type##_store(struct device *dev,			\180			    struct device_attribute *attr,	\181			    const char *buf, size_t count)	\182{								\183	struct hda_codec *codec = dev_get_drvdata(dev);		\184	char *s = kstrndup_noeol(buf, 64);			\185	if (!s)							\186		return -ENOMEM;					\187	kfree(codec->field);					\188	codec->field = s;					\189	return count;						\190}191 192CODEC_INFO_STORE(vendor_id, core.vendor_id);193CODEC_INFO_STORE(subsystem_id, core.subsystem_id);194CODEC_INFO_STORE(revision_id, core.revision_id);195CODEC_INFO_STR_STORE(vendor_name, core.vendor_name);196CODEC_INFO_STR_STORE(chip_name, core.chip_name);197CODEC_INFO_STR_STORE(modelname, modelname);198 199#define CODEC_ACTION_STORE(type)				\200static ssize_t type##_store(struct device *dev,			\201			    struct device_attribute *attr,	\202			    const char *buf, size_t count)	\203{								\204	struct hda_codec *codec = dev_get_drvdata(dev);		\205	int err = 0;						\206	if (*buf)						\207		err = type##_codec(codec);			\208	return err < 0 ? err : count;				\209}210 211CODEC_ACTION_STORE(reconfig);212CODEC_ACTION_STORE(clear);213 214static ssize_t init_verbs_show(struct device *dev,215			       struct device_attribute *attr,216			       char *buf)217{218	struct hda_codec *codec = dev_get_drvdata(dev);219	const struct hda_verb *v;220	int i, len = 0;221	mutex_lock(&codec->user_mutex);222	snd_array_for_each(&codec->init_verbs, i, v) {223		len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n",224				     v->nid, v->verb, v->param);225	}226	mutex_unlock(&codec->user_mutex);227	return len;228}229 230static int parse_init_verbs(struct hda_codec *codec, const char *buf)231{232	struct hda_verb *v;233	int nid, verb, param;234 235	if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)236		return -EINVAL;237	if (!nid || !verb)238		return -EINVAL;239	mutex_lock(&codec->user_mutex);240	v = snd_array_new(&codec->init_verbs);241	if (!v) {242		mutex_unlock(&codec->user_mutex);243		return -ENOMEM;244	}245	v->nid = nid;246	v->verb = verb;247	v->param = param;248	mutex_unlock(&codec->user_mutex);249	return 0;250}251 252static ssize_t init_verbs_store(struct device *dev,253				struct device_attribute *attr,254				const char *buf, size_t count)255{256	struct hda_codec *codec = dev_get_drvdata(dev);257	int err = parse_init_verbs(codec, buf);258	if (err < 0)259		return err;260	return count;261}262 263static ssize_t hints_show(struct device *dev,264			  struct device_attribute *attr,265			  char *buf)266{267	struct hda_codec *codec = dev_get_drvdata(dev);268	const struct hda_hint *hint;269	int i, len = 0;270	mutex_lock(&codec->user_mutex);271	snd_array_for_each(&codec->hints, i, hint) {272		len += sysfs_emit_at(buf, len, "%s = %s\n",273				     hint->key, hint->val);274	}275	mutex_unlock(&codec->user_mutex);276	return len;277}278 279static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)280{281	struct hda_hint *hint;282	int i;283 284	snd_array_for_each(&codec->hints, i, hint) {285		if (!strcmp(hint->key, key))286			return hint;287	}288	return NULL;289}290 291static void remove_trail_spaces(char *str)292{293	char *p;294	if (!*str)295		return;296	p = str + strlen(str) - 1;297	for (; isspace(*p); p--) {298		*p = 0;299		if (p == str)300			return;301	}302}303 304#define MAX_HINTS	1024305 306static int parse_hints(struct hda_codec *codec, const char *buf)307{308	char *key, *val;309	struct hda_hint *hint;310	int err = 0;311 312	buf = skip_spaces(buf);313	if (!*buf || *buf == '#' || *buf == '\n')314		return 0;315	if (*buf == '=')316		return -EINVAL;317	key = kstrndup_noeol(buf, 1024);318	if (!key)319		return -ENOMEM;320	/* extract key and val */321	val = strchr(key, '=');322	if (!val) {323		kfree(key);324		return -EINVAL;325	}326	*val++ = 0;327	val = skip_spaces(val);328	remove_trail_spaces(key);329	remove_trail_spaces(val);330	mutex_lock(&codec->user_mutex);331	hint = get_hint(codec, key);332	if (hint) {333		/* replace */334		kfree(hint->key);335		hint->key = key;336		hint->val = val;337		goto unlock;338	}339	/* allocate a new hint entry */340	if (codec->hints.used >= MAX_HINTS)341		hint = NULL;342	else343		hint = snd_array_new(&codec->hints);344	if (hint) {345		hint->key = key;346		hint->val = val;347	} else {348		err = -ENOMEM;349	}350 unlock:351	mutex_unlock(&codec->user_mutex);352	if (err)353		kfree(key);354	return err;355}356 357static ssize_t hints_store(struct device *dev,358			   struct device_attribute *attr,359			   const char *buf, size_t count)360{361	struct hda_codec *codec = dev_get_drvdata(dev);362	int err = parse_hints(codec, buf);363	if (err < 0)364		return err;365	return count;366}367 368static ssize_t user_pin_configs_show(struct device *dev,369				     struct device_attribute *attr,370				     char *buf)371{372	struct hda_codec *codec = dev_get_drvdata(dev);373	return pin_configs_show(codec, &codec->user_pins, buf);374}375 376static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)377{378	int nid, cfg, err;379 380	if (sscanf(buf, "%i %i", &nid, &cfg) != 2)381		return -EINVAL;382	if (!nid)383		return -EINVAL;384	mutex_lock(&codec->user_mutex);385	err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);386	mutex_unlock(&codec->user_mutex);387	return err;388}389 390static ssize_t user_pin_configs_store(struct device *dev,391				      struct device_attribute *attr,392				      const char *buf, size_t count)393{394	struct hda_codec *codec = dev_get_drvdata(dev);395	int err = parse_user_pin_configs(codec, buf);396	if (err < 0)397		return err;398	return count;399}400 401/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */402static DEVICE_ATTR_RW(init_verbs);403static DEVICE_ATTR_RW(hints);404static DEVICE_ATTR_RW(user_pin_configs);405static DEVICE_ATTR_WO(reconfig);406static DEVICE_ATTR_WO(clear);407 408/**409 * snd_hda_get_hint - Look for hint string410 * @codec: the HDA codec411 * @key: the hint key string412 *413 * Look for a hint key/value pair matching with the given key string414 * and returns the value string.  If nothing found, returns NULL.415 */416const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)417{418	struct hda_hint *hint = get_hint(codec, key);419	return hint ? hint->val : NULL;420}421EXPORT_SYMBOL_GPL(snd_hda_get_hint);422 423/**424 * snd_hda_get_bool_hint - Get a boolean hint value425 * @codec: the HDA codec426 * @key: the hint key string427 *428 * Look for a hint key/value pair matching with the given key string429 * and returns a boolean value parsed from the value.  If no matching430 * key is found, return a negative value.431 */432int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)433{434	const char *p;435	int ret;436 437	mutex_lock(&codec->user_mutex);438	p = snd_hda_get_hint(codec, key);439	if (!p || !*p)440		ret = -ENOENT;441	else {442		switch (toupper(*p)) {443		case 'T': /* true */444		case 'Y': /* yes */445		case '1':446			ret = 1;447			break;448		default:449			ret = 0;450			break;451		}452	}453	mutex_unlock(&codec->user_mutex);454	return ret;455}456EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);457 458/**459 * snd_hda_get_int_hint - Get an integer hint value460 * @codec: the HDA codec461 * @key: the hint key string462 * @valp: pointer to store a value463 *464 * Look for a hint key/value pair matching with the given key string465 * and stores the integer value to @valp.  If no matching key is found,466 * return a negative error code.  Otherwise it returns zero.467 */468int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)469{470	const char *p;471	unsigned long val;472	int ret;473 474	mutex_lock(&codec->user_mutex);475	p = snd_hda_get_hint(codec, key);476	if (!p)477		ret = -ENOENT;478	else if (kstrtoul(p, 0, &val))479		ret = -EINVAL;480	else {481		*valp = val;482		ret = 0;483	}484	mutex_unlock(&codec->user_mutex);485	return ret;486}487EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);488#endif /* CONFIG_SND_HDA_RECONFIG */489 490/*491 * common sysfs attributes492 */493#ifdef CONFIG_SND_HDA_RECONFIG494#define RECONFIG_DEVICE_ATTR(name)	DEVICE_ATTR_RW(name)495#else496#define RECONFIG_DEVICE_ATTR(name)	DEVICE_ATTR_RO(name)497#endif498static RECONFIG_DEVICE_ATTR(vendor_id);499static RECONFIG_DEVICE_ATTR(subsystem_id);500static RECONFIG_DEVICE_ATTR(revision_id);501static DEVICE_ATTR_RO(afg);502static DEVICE_ATTR_RO(mfg);503static RECONFIG_DEVICE_ATTR(vendor_name);504static RECONFIG_DEVICE_ATTR(chip_name);505static RECONFIG_DEVICE_ATTR(modelname);506static DEVICE_ATTR_RO(init_pin_configs);507static DEVICE_ATTR_RO(driver_pin_configs);508 509 510#ifdef CONFIG_SND_HDA_PATCH_LOADER511 512/* parser mode */513enum {514	LINE_MODE_NONE,515	LINE_MODE_CODEC,516	LINE_MODE_MODEL,517	LINE_MODE_PINCFG,518	LINE_MODE_VERB,519	LINE_MODE_HINT,520	LINE_MODE_VENDOR_ID,521	LINE_MODE_SUBSYSTEM_ID,522	LINE_MODE_REVISION_ID,523	LINE_MODE_CHIP_NAME,524	NUM_LINE_MODES,525};526 527static inline int strmatch(const char *a, const char *b)528{529	return strncasecmp(a, b, strlen(b)) == 0;530}531 532/* parse the contents after the line "[codec]"533 * accept only the line with three numbers, and assign the current codec534 */535static void parse_codec_mode(char *buf, struct hda_bus *bus,536			     struct hda_codec **codecp)537{538	int vendorid, subid, caddr;539	struct hda_codec *codec;540 541	*codecp = NULL;542	if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {543		list_for_each_codec(codec, bus) {544			if ((vendorid <= 0 || codec->core.vendor_id == vendorid) &&545			    (subid <= 0 || codec->core.subsystem_id == subid) &&546			    codec->core.addr == caddr) {547				*codecp = codec;548				break;549			}550		}551	}552}553 554/* parse the contents after the other command tags, [pincfg], [verb],555 * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]556 * just pass to the sysfs helper (only when any codec was specified)557 */558static void parse_pincfg_mode(char *buf, struct hda_bus *bus,559			      struct hda_codec **codecp)560{561	parse_user_pin_configs(*codecp, buf);562}563 564static void parse_verb_mode(char *buf, struct hda_bus *bus,565			    struct hda_codec **codecp)566{567	parse_init_verbs(*codecp, buf);568}569 570static void parse_hint_mode(char *buf, struct hda_bus *bus,571			    struct hda_codec **codecp)572{573	parse_hints(*codecp, buf);574}575 576static void parse_model_mode(char *buf, struct hda_bus *bus,577			     struct hda_codec **codecp)578{579	kfree((*codecp)->modelname);580	(*codecp)->modelname = kstrdup(buf, GFP_KERNEL);581}582 583static void parse_chip_name_mode(char *buf, struct hda_bus *bus,584				 struct hda_codec **codecp)585{586	snd_hda_codec_set_name(*codecp, buf);587}588 589#define DEFINE_PARSE_ID_MODE(name) \590static void parse_##name##_mode(char *buf, struct hda_bus *bus, \591				 struct hda_codec **codecp) \592{ \593	unsigned long val; \594	if (!kstrtoul(buf, 0, &val)) \595		(*codecp)->core.name = val; \596}597 598DEFINE_PARSE_ID_MODE(vendor_id);599DEFINE_PARSE_ID_MODE(subsystem_id);600DEFINE_PARSE_ID_MODE(revision_id);601 602 603struct hda_patch_item {604	const char *tag;605	const char *alias;606	void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);607};608 609static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {610	[LINE_MODE_CODEC] = {611		.tag = "[codec]",612		.parser = parse_codec_mode,613	},614	[LINE_MODE_MODEL] = {615		.tag = "[model]",616		.parser = parse_model_mode,617	},618	[LINE_MODE_VERB] = {619		.tag = "[verb]",620		.alias = "[init_verbs]",621		.parser = parse_verb_mode,622	},623	[LINE_MODE_PINCFG] = {624		.tag = "[pincfg]",625		.alias = "[user_pin_configs]",626		.parser = parse_pincfg_mode,627	},628	[LINE_MODE_HINT] = {629		.tag = "[hint]",630		.alias = "[hints]",631		.parser = parse_hint_mode632	},633	[LINE_MODE_VENDOR_ID] = {634		.tag = "[vendor_id]",635		.parser = parse_vendor_id_mode,636	},637	[LINE_MODE_SUBSYSTEM_ID] = {638		.tag = "[subsystem_id]",639		.parser = parse_subsystem_id_mode,640	},641	[LINE_MODE_REVISION_ID] = {642		.tag = "[revision_id]",643		.parser = parse_revision_id_mode,644	},645	[LINE_MODE_CHIP_NAME] = {646		.tag = "[chip_name]",647		.parser = parse_chip_name_mode,648	},649};650 651/* check the line starting with '[' -- change the parser mode accodingly */652static int parse_line_mode(char *buf, struct hda_bus *bus)653{654	int i;655	for (i = 0; i < ARRAY_SIZE(patch_items); i++) {656		if (!patch_items[i].tag)657			continue;658		if (strmatch(buf, patch_items[i].tag))659			return i;660		if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))661			return i;662	}663	return LINE_MODE_NONE;664}665 666/* copy one line from the buffer in fw, and update the fields in fw667 * return zero if it reaches to the end of the buffer, or non-zero668 * if successfully copied a line669 *670 * the spaces at the beginning and the end of the line are stripped671 */672static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,673			    const void **fw_data_p)674{675	int len;676	size_t fw_size = *fw_size_p;677	const char *p = *fw_data_p;678 679	while (isspace(*p) && fw_size) {680		p++;681		fw_size--;682	}683	if (!fw_size)684		return 0;685 686	for (len = 0; len < fw_size; len++) {687		if (!*p)688			break;689		if (*p == '\n') {690			p++;691			len++;692			break;693		}694		if (len < size)695			*buf++ = *p++;696	}697	*buf = 0;698	*fw_size_p = fw_size - len;699	*fw_data_p = p;700	remove_trail_spaces(buf);701	return 1;702}703 704/**705 * snd_hda_load_patch - load a "patch" firmware file and parse it706 * @bus: HD-audio bus707 * @fw_size: the firmware byte size708 * @fw_buf: the firmware data709 */710int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)711{712	char buf[128];713	struct hda_codec *codec;714	int line_mode;715 716	line_mode = LINE_MODE_NONE;717	codec = NULL;718	while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {719		if (!*buf || *buf == '#' || *buf == '\n')720			continue;721		if (*buf == '[')722			line_mode = parse_line_mode(buf, bus);723		else if (patch_items[line_mode].parser &&724			 (codec || line_mode <= LINE_MODE_CODEC))725			patch_items[line_mode].parser(buf, bus, &codec);726	}727	return 0;728}729EXPORT_SYMBOL_GPL(snd_hda_load_patch);730#endif /* CONFIG_SND_HDA_PATCH_LOADER */731 732/*733 * sysfs entries734 */735static struct attribute *hda_dev_attrs[] = {736	&dev_attr_vendor_id.attr,737	&dev_attr_subsystem_id.attr,738	&dev_attr_revision_id.attr,739	&dev_attr_afg.attr,740	&dev_attr_mfg.attr,741	&dev_attr_vendor_name.attr,742	&dev_attr_chip_name.attr,743	&dev_attr_modelname.attr,744	&dev_attr_init_pin_configs.attr,745	&dev_attr_driver_pin_configs.attr,746	&dev_attr_power_on_acct.attr,747	&dev_attr_power_off_acct.attr,748#ifdef CONFIG_SND_HDA_RECONFIG749	&dev_attr_init_verbs.attr,750	&dev_attr_hints.attr,751	&dev_attr_user_pin_configs.attr,752	&dev_attr_reconfig.attr,753	&dev_attr_clear.attr,754#endif755	NULL756};757 758static const struct attribute_group hda_dev_attr_group = {759	.attrs	= hda_dev_attrs,760};761 762const struct attribute_group *snd_hda_dev_attr_groups[] = {763	&hda_dev_attr_group,764	NULL765};766 767void snd_hda_sysfs_init(struct hda_codec *codec)768{769	mutex_init(&codec->user_mutex);770#ifdef CONFIG_SND_HDA_RECONFIG771	snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);772	snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);773	snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);774#endif775}776 777void snd_hda_sysfs_clear(struct hda_codec *codec)778{779#ifdef CONFIG_SND_HDA_RECONFIG780	struct hda_hint *hint;781	int i;782 783	/* clear init verbs */784	snd_array_free(&codec->init_verbs);785	/* clear hints */786	snd_array_for_each(&codec->hints, i, hint) {787		kfree(hint->key); /* we don't need to free hint->val */788	}789	snd_array_free(&codec->hints);790	snd_array_free(&codec->user_pins);791#endif792}793