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