brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · a3fb380 Raw
305 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright(c) 2015-2020 Intel Corporation.3 4#include <linux/device.h>5#include <linux/mod_devicetable.h>6#include <linux/slab.h>7#include <linux/sysfs.h>8#include <linux/soundwire/sdw.h>9#include <linux/soundwire/sdw_type.h>10#include "bus.h"11#include "sysfs_local.h"12 13struct dpn_attribute {14	struct device_attribute	dev_attr;15	int N;16	int dir;17	const char *format_string;18};19 20/*21 * Since we can't use ARRAY_SIZE, hard-code number of dpN attributes.22 * This needs to be updated when adding new attributes - an error will be23 * flagged on a mismatch.24 */25#define SDW_DPN_ATTRIBUTES 1526 27#define sdw_dpn_attribute_alloc(field)					\28static int field##_attribute_alloc(struct device *dev,			\29				struct attribute **res,			\30				int N, int dir,				\31				const char *format_string)		\32{									\33	struct dpn_attribute *dpn_attr;					\34									\35	dpn_attr = devm_kzalloc(dev, sizeof(*dpn_attr), GFP_KERNEL);	\36	if (!dpn_attr)							\37		return -ENOMEM;						\38	dpn_attr->N = N;						\39	dpn_attr->dir = dir;						\40	sysfs_attr_init(&dpn_attr->dev_attr.attr);			\41	dpn_attr->format_string = format_string;			\42	dpn_attr->dev_attr.attr.name = __stringify(field);		\43	dpn_attr->dev_attr.attr.mode = 0444;				\44	dpn_attr->dev_attr.show = field##_show;				\45									\46	*res = &dpn_attr->dev_attr.attr;				\47									\48	return 0;							\49}50 51#define sdw_dpn_attr(field)						\52									\53static ssize_t field##_dpn_show(struct sdw_slave *slave,		\54				int N,					\55				int dir,				\56				const char *format_string,		\57				char *buf)				\58{									\59	struct sdw_dpn_prop *dpn;					\60	unsigned long mask;						\61	int bit;							\62	int i;								\63									\64	if (dir) {							\65		dpn = slave->prop.src_dpn_prop;				\66		mask = slave->prop.source_ports;			\67	} else {							\68		dpn = slave->prop.sink_dpn_prop;			\69		mask = slave->prop.sink_ports;				\70	}								\71									\72	i = 0;								\73	for_each_set_bit(bit, &mask, 32) {				\74		if (bit == N) {						\75			return sprintf(buf, format_string,		\76				       dpn[i].field);			\77		}							\78		i++;							\79	}								\80	return -EINVAL;							\81}									\82									\83static ssize_t field##_show(struct device *dev,				\84			    struct device_attribute *attr,		\85			    char *buf)					\86{									\87	struct sdw_slave *slave = dev_to_sdw_dev(dev);			\88	struct dpn_attribute *dpn_attr =				\89		container_of(attr, struct dpn_attribute, dev_attr);	\90									\91	return field##_dpn_show(slave,					\92				dpn_attr->N, dpn_attr->dir,		\93				dpn_attr->format_string,		\94				buf);					\95}									\96sdw_dpn_attribute_alloc(field)97 98sdw_dpn_attr(imp_def_interrupts);99sdw_dpn_attr(max_word);100sdw_dpn_attr(min_word);101sdw_dpn_attr(type);102sdw_dpn_attr(max_grouping);103sdw_dpn_attr(simple_ch_prep_sm);104sdw_dpn_attr(ch_prep_timeout);105sdw_dpn_attr(max_ch);106sdw_dpn_attr(min_ch);107sdw_dpn_attr(max_async_buffer);108sdw_dpn_attr(block_pack_mode);109sdw_dpn_attr(port_encoding);110 111#define sdw_dpn_array_attr(field)					\112									\113static ssize_t field##_dpn_show(struct sdw_slave *slave,		\114				int N,					\115				int dir,				\116				const char *format_string,		\117				char *buf)				\118{									\119	struct sdw_dpn_prop *dpn;					\120	unsigned long mask;						\121	ssize_t size = 0;						\122	int bit;							\123	int i;								\124	int j;								\125									\126	if (dir) {							\127		dpn = slave->prop.src_dpn_prop;				\128		mask = slave->prop.source_ports;			\129	} else {							\130		dpn = slave->prop.sink_dpn_prop;			\131		mask = slave->prop.sink_ports;				\132	}								\133									\134	i = 0;								\135	for_each_set_bit(bit, &mask, 32) {				\136		if (bit == N) {						\137			for (j = 0; j < dpn[i].num_##field; j++)	\138				size += sprintf(buf + size,		\139						format_string,		\140						dpn[i].field[j]);	\141			size += sprintf(buf + size, "\n");		\142			return size;					\143		}							\144		i++;							\145	}								\146	return -EINVAL;							\147}									\148static ssize_t field##_show(struct device *dev,				\149			    struct device_attribute *attr,		\150			    char *buf)					\151{									\152	struct sdw_slave *slave = dev_to_sdw_dev(dev);			\153	struct dpn_attribute *dpn_attr =				\154		container_of(attr, struct dpn_attribute, dev_attr);	\155									\156	return field##_dpn_show(slave,					\157				dpn_attr->N, dpn_attr->dir,		\158				dpn_attr->format_string,		\159				buf);					\160}									\161sdw_dpn_attribute_alloc(field)162 163sdw_dpn_array_attr(words);164sdw_dpn_array_attr(ch_combinations);165sdw_dpn_array_attr(channels);166 167static int add_all_attributes(struct device *dev, int N, int dir)168{169	struct attribute **dpn_attrs;170	struct attribute_group *dpn_group;171	int i = 0;172	int ret;173 174	/* allocate attributes, last one is NULL */175	dpn_attrs = devm_kcalloc(dev, SDW_DPN_ATTRIBUTES + 1,176				 sizeof(struct attribute *),177				 GFP_KERNEL);178	if (!dpn_attrs)179		return -ENOMEM;180 181	ret = max_word_attribute_alloc(dev, &dpn_attrs[i++],182				       N, dir, "%d\n");183	if (ret < 0)184		return ret;185 186	ret = min_word_attribute_alloc(dev, &dpn_attrs[i++],187				       N, dir, "%d\n");188	if (ret < 0)189		return ret;190 191	ret = words_attribute_alloc(dev, &dpn_attrs[i++],192				    N, dir, "%d\n");193	if (ret < 0)194		return ret;195 196	ret = type_attribute_alloc(dev, &dpn_attrs[i++],197				   N, dir, "%d\n");198	if (ret < 0)199		return ret;200 201	ret = max_grouping_attribute_alloc(dev, &dpn_attrs[i++],202					   N, dir, "%d\n");203	if (ret < 0)204		return ret;205 206	ret = simple_ch_prep_sm_attribute_alloc(dev, &dpn_attrs[i++],207						N, dir, "%d\n");208	if (ret < 0)209		return ret;210 211	ret = ch_prep_timeout_attribute_alloc(dev, &dpn_attrs[i++],212					      N, dir, "%d\n");213	if (ret < 0)214		return ret;215 216	ret = imp_def_interrupts_attribute_alloc(dev, &dpn_attrs[i++],217						 N, dir, "0x%x\n");218	if (ret < 0)219		return ret;220 221	ret = min_ch_attribute_alloc(dev, &dpn_attrs[i++],222				     N, dir, "%d\n");223	if (ret < 0)224		return ret;225 226	ret = max_ch_attribute_alloc(dev, &dpn_attrs[i++],227				     N, dir, "%d\n");228	if (ret < 0)229		return ret;230 231	ret = channels_attribute_alloc(dev, &dpn_attrs[i++],232				       N, dir, "%d\n");233	if (ret < 0)234		return ret;235 236	ret = ch_combinations_attribute_alloc(dev, &dpn_attrs[i++],237					      N, dir, "%d\n");238	if (ret < 0)239		return ret;240 241	ret = max_async_buffer_attribute_alloc(dev, &dpn_attrs[i++],242					       N, dir, "%d\n");243	if (ret < 0)244		return ret;245 246	ret = block_pack_mode_attribute_alloc(dev, &dpn_attrs[i++],247					      N, dir, "%d\n");248	if (ret < 0)249		return ret;250 251	ret = port_encoding_attribute_alloc(dev, &dpn_attrs[i++],252					    N, dir, "%d\n");253	if (ret < 0)254		return ret;255 256	/* paranoia check for editing mistakes */257	if (i != SDW_DPN_ATTRIBUTES) {258		dev_err(dev, "mismatch in attributes, allocated %d got %d\n",259			SDW_DPN_ATTRIBUTES, i);260		return -EINVAL;261	}262 263	dpn_group = devm_kzalloc(dev, sizeof(*dpn_group), GFP_KERNEL);264	if (!dpn_group)265		return -ENOMEM;266 267	dpn_group->attrs = dpn_attrs;268	dpn_group->name = devm_kasprintf(dev, GFP_KERNEL, "dp%d_%s",269					 N, dir ? "src" : "sink");270	if (!dpn_group->name)271		return -ENOMEM;272 273	ret = devm_device_add_group(dev, dpn_group);274	if (ret < 0)275		return ret;276 277	return 0;278}279 280int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave)281{282	unsigned long mask;283	int ret;284	int i;285 286	if (!slave->prop.source_ports && !slave->prop.sink_ports)287		return 0;288 289	mask = slave->prop.source_ports;290	for_each_set_bit(i, &mask, 32) {291		ret = add_all_attributes(&slave->dev, i, 1);292		if (ret < 0)293			return ret;294	}295 296	mask = slave->prop.sink_ports;297	for_each_set_bit(i, &mask, 32) {298		ret = add_all_attributes(&slave->dev, i, 0);299		if (ret < 0)300			return ret;301	}302 303	return 0;304}305