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