Merge tag 'acpi-6.6-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[platform/kernel/linux-rpi.git] / drivers / soundwire / sysfs_slave_dpn.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 // 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
13 struct 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 be
23  * flagged on a mismatch.
24  */
25 #define SDW_DPN_ATTRIBUTES 15
26
27 #define sdw_dpn_attribute_alloc(field)                                  \
28 static 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                                                                         \
53 static 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                                                                         \
83 static 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 }                                                                       \
96 sdw_dpn_attribute_alloc(field)
97
98 sdw_dpn_attr(imp_def_interrupts);
99 sdw_dpn_attr(max_word);
100 sdw_dpn_attr(min_word);
101 sdw_dpn_attr(type);
102 sdw_dpn_attr(max_grouping);
103 sdw_dpn_attr(simple_ch_prep_sm);
104 sdw_dpn_attr(ch_prep_timeout);
105 sdw_dpn_attr(max_ch);
106 sdw_dpn_attr(min_ch);
107 sdw_dpn_attr(max_async_buffer);
108 sdw_dpn_attr(block_pack_mode);
109 sdw_dpn_attr(port_encoding);
110
111 #define sdw_dpn_array_attr(field)                                       \
112                                                                         \
113 static 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 }                                                                       \
148 static 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 }                                                                       \
161 sdw_dpn_attribute_alloc(field)
162
163 sdw_dpn_array_attr(words);
164 sdw_dpn_array_attr(ch_combinations);
165 sdw_dpn_array_attr(channels);
166
167 static 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
280 int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave)
281 {
282         unsigned long mask;
283         int ret;
284         int i;
285
286         mask = slave->prop.source_ports;
287         for_each_set_bit(i, &mask, 32) {
288                 ret = add_all_attributes(&slave->dev, i, 1);
289                 if (ret < 0)
290                         return ret;
291         }
292
293         mask = slave->prop.sink_ports;
294         for_each_set_bit(i, &mask, 32) {
295                 ret = add_all_attributes(&slave->dev, i, 0);
296                 if (ret < 0)
297                         return ret;
298         }
299
300         return 0;
301 }