Commit | Line | Data |
---|---|---|
56d4fe31 VK |
1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | // Copyright(c) 2015-17 Intel Corporation. | |
3 | ||
4 | /* | |
5 | * MIPI Discovery And Configuration (DisCo) Specification for SoundWire | |
6 | * specifies properties to be implemented for SoundWire Masters and Slaves. | |
7 | * The DisCo spec doesn't mandate these properties. However, SDW bus cannot | |
8 | * work without knowing these values. | |
9 | * | |
10 | * The helper functions read the Master and Slave properties. Implementers | |
11 | * of Master or Slave drivers can use any of the below three mechanisms: | |
12 | * a) Use these APIs here as .read_prop() callback for Master and Slave | |
13 | * b) Implement own methods and set those as .read_prop(), but invoke | |
14 | * APIs in this file for generic read and override the values with | |
15 | * platform specific data | |
16 | * c) Implement ones own methods which do not use anything provided | |
17 | * here | |
18 | */ | |
19 | ||
20 | #include <linux/device.h> | |
21 | #include <linux/property.h> | |
22 | #include <linux/mod_devicetable.h> | |
23 | #include <linux/soundwire/sdw.h> | |
24 | #include "bus.h" | |
25 | ||
26 | /** | |
27 | * sdw_master_read_prop() - Read Master properties | |
28 | * @bus: SDW bus instance | |
29 | */ | |
30 | int sdw_master_read_prop(struct sdw_bus *bus) | |
31 | { | |
32 | struct sdw_master_prop *prop = &bus->prop; | |
33 | struct fwnode_handle *link; | |
34 | char name[32]; | |
35 | int nval, i; | |
36 | ||
37 | device_property_read_u32(bus->dev, | |
31dba312 PLB |
38 | "mipi-sdw-sw-interface-revision", |
39 | &prop->revision); | |
56d4fe31 VK |
40 | |
41 | /* Find master handle */ | |
42 | snprintf(name, sizeof(name), | |
31dba312 | 43 | "mipi-sdw-master-%d-subproperties", bus->link_id); |
56d4fe31 VK |
44 | |
45 | link = device_get_named_child_node(bus->dev, name); | |
46 | if (!link) { | |
47 | dev_err(bus->dev, "Master node %s not found\n", name); | |
48 | return -EIO; | |
49 | } | |
50 | ||
51 | if (fwnode_property_read_bool(link, | |
00910f3c | 52 | "mipi-sdw-clock-stop-mode0-supported")) |
56d4fe31 VK |
53 | prop->clk_stop_mode = SDW_CLK_STOP_MODE0; |
54 | ||
55 | if (fwnode_property_read_bool(link, | |
00910f3c | 56 | "mipi-sdw-clock-stop-mode1-supported")) |
56d4fe31 VK |
57 | prop->clk_stop_mode |= SDW_CLK_STOP_MODE1; |
58 | ||
59 | fwnode_property_read_u32(link, | |
31dba312 PLB |
60 | "mipi-sdw-max-clock-frequency", |
61 | &prop->max_freq); | |
56d4fe31 VK |
62 | |
63 | nval = fwnode_property_read_u32_array(link, | |
64 | "mipi-sdw-clock-frequencies-supported", NULL, 0); | |
65 | if (nval > 0) { | |
56d4fe31 VK |
66 | prop->num_freq = nval; |
67 | prop->freq = devm_kcalloc(bus->dev, prop->num_freq, | |
31dba312 | 68 | sizeof(*prop->freq), GFP_KERNEL); |
56d4fe31 VK |
69 | if (!prop->freq) |
70 | return -ENOMEM; | |
71 | ||
72 | fwnode_property_read_u32_array(link, | |
73 | "mipi-sdw-clock-frequencies-supported", | |
74 | prop->freq, prop->num_freq); | |
75 | } | |
76 | ||
77 | /* | |
78 | * Check the frequencies supported. If FW doesn't provide max | |
79 | * freq, then populate here by checking values. | |
80 | */ | |
81 | if (!prop->max_freq && prop->freq) { | |
82 | prop->max_freq = prop->freq[0]; | |
83 | for (i = 1; i < prop->num_freq; i++) { | |
84 | if (prop->freq[i] > prop->max_freq) | |
85 | prop->max_freq = prop->freq[i]; | |
86 | } | |
87 | } | |
88 | ||
89 | nval = fwnode_property_read_u32_array(link, | |
90 | "mipi-sdw-supported-clock-gears", NULL, 0); | |
91 | if (nval > 0) { | |
56d4fe31 VK |
92 | prop->num_clk_gears = nval; |
93 | prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears, | |
31dba312 PLB |
94 | sizeof(*prop->clk_gears), |
95 | GFP_KERNEL); | |
56d4fe31 VK |
96 | if (!prop->clk_gears) |
97 | return -ENOMEM; | |
98 | ||
99 | fwnode_property_read_u32_array(link, | |
31dba312 PLB |
100 | "mipi-sdw-supported-clock-gears", |
101 | prop->clk_gears, | |
102 | prop->num_clk_gears); | |
56d4fe31 VK |
103 | } |
104 | ||
105 | fwnode_property_read_u32(link, "mipi-sdw-default-frame-rate", | |
31dba312 | 106 | &prop->default_frame_rate); |
56d4fe31 VK |
107 | |
108 | fwnode_property_read_u32(link, "mipi-sdw-default-frame-row-size", | |
31dba312 | 109 | &prop->default_row); |
56d4fe31 VK |
110 | |
111 | fwnode_property_read_u32(link, "mipi-sdw-default-frame-col-size", | |
31dba312 | 112 | &prop->default_col); |
56d4fe31 VK |
113 | |
114 | prop->dynamic_frame = fwnode_property_read_bool(link, | |
115 | "mipi-sdw-dynamic-frame-shape"); | |
116 | ||
117 | fwnode_property_read_u32(link, "mipi-sdw-command-error-threshold", | |
31dba312 | 118 | &prop->err_threshold); |
56d4fe31 VK |
119 | |
120 | return 0; | |
121 | } | |
122 | EXPORT_SYMBOL(sdw_master_read_prop); | |
123 | ||
124 | static int sdw_slave_read_dp0(struct sdw_slave *slave, | |
31dba312 PLB |
125 | struct fwnode_handle *port, |
126 | struct sdw_dp0_prop *dp0) | |
56d4fe31 VK |
127 | { |
128 | int nval; | |
129 | ||
130 | fwnode_property_read_u32(port, "mipi-sdw-port-max-wordlength", | |
31dba312 | 131 | &dp0->max_word); |
56d4fe31 VK |
132 | |
133 | fwnode_property_read_u32(port, "mipi-sdw-port-min-wordlength", | |
31dba312 | 134 | &dp0->min_word); |
56d4fe31 VK |
135 | |
136 | nval = fwnode_property_read_u32_array(port, | |
137 | "mipi-sdw-port-wordlength-configs", NULL, 0); | |
138 | if (nval > 0) { | |
139 | ||
140 | dp0->num_words = nval; | |
141 | dp0->words = devm_kcalloc(&slave->dev, | |
31dba312 PLB |
142 | dp0->num_words, sizeof(*dp0->words), |
143 | GFP_KERNEL); | |
56d4fe31 VK |
144 | if (!dp0->words) |
145 | return -ENOMEM; | |
146 | ||
147 | fwnode_property_read_u32_array(port, | |
148 | "mipi-sdw-port-wordlength-configs", | |
149 | dp0->words, dp0->num_words); | |
150 | } | |
151 | ||
31dba312 PLB |
152 | dp0->flow_controlled = fwnode_property_read_bool(port, |
153 | "mipi-sdw-bra-flow-controlled"); | |
56d4fe31 | 154 | |
31dba312 PLB |
155 | dp0->simple_ch_prep_sm = fwnode_property_read_bool(port, |
156 | "mipi-sdw-simplified-channel-prepare-sm"); | |
56d4fe31 | 157 | |
31dba312 PLB |
158 | dp0->device_interrupts = fwnode_property_read_bool(port, |
159 | "mipi-sdw-imp-def-dp0-interrupts-supported"); | |
56d4fe31 VK |
160 | |
161 | return 0; | |
162 | } | |
163 | ||
164 | static int sdw_slave_read_dpn(struct sdw_slave *slave, | |
31dba312 PLB |
165 | struct sdw_dpn_prop *dpn, int count, int ports, |
166 | char *type) | |
56d4fe31 VK |
167 | { |
168 | struct fwnode_handle *node; | |
169 | u32 bit, i = 0; | |
170 | int nval; | |
171 | unsigned long addr; | |
172 | char name[40]; | |
173 | ||
174 | addr = ports; | |
175 | /* valid ports are 1 to 14 so apply mask */ | |
176 | addr &= GENMASK(14, 1); | |
177 | ||
178 | for_each_set_bit(bit, &addr, 32) { | |
179 | snprintf(name, sizeof(name), | |
31dba312 | 180 | "mipi-sdw-dp-%d-%s-subproperties", bit, type); |
56d4fe31 VK |
181 | |
182 | dpn[i].num = bit; | |
183 | ||
184 | node = device_get_named_child_node(&slave->dev, name); | |
185 | if (!node) { | |
186 | dev_err(&slave->dev, "%s dpN not found\n", name); | |
187 | return -EIO; | |
188 | } | |
189 | ||
190 | fwnode_property_read_u32(node, "mipi-sdw-port-max-wordlength", | |
31dba312 | 191 | &dpn[i].max_word); |
56d4fe31 | 192 | fwnode_property_read_u32(node, "mipi-sdw-port-min-wordlength", |
31dba312 | 193 | &dpn[i].min_word); |
56d4fe31 VK |
194 | |
195 | nval = fwnode_property_read_u32_array(node, | |
196 | "mipi-sdw-port-wordlength-configs", NULL, 0); | |
197 | if (nval > 0) { | |
56d4fe31 VK |
198 | dpn[i].num_words = nval; |
199 | dpn[i].words = devm_kcalloc(&slave->dev, | |
31dba312 PLB |
200 | dpn[i].num_words, |
201 | sizeof(*dpn[i].words), | |
202 | GFP_KERNEL); | |
56d4fe31 VK |
203 | if (!dpn[i].words) |
204 | return -ENOMEM; | |
205 | ||
206 | fwnode_property_read_u32_array(node, | |
207 | "mipi-sdw-port-wordlength-configs", | |
208 | dpn[i].words, dpn[i].num_words); | |
209 | } | |
210 | ||
211 | fwnode_property_read_u32(node, "mipi-sdw-data-port-type", | |
31dba312 | 212 | &dpn[i].type); |
56d4fe31 VK |
213 | |
214 | fwnode_property_read_u32(node, | |
31dba312 PLB |
215 | "mipi-sdw-max-grouping-supported", |
216 | &dpn[i].max_grouping); | |
56d4fe31 VK |
217 | |
218 | dpn[i].simple_ch_prep_sm = fwnode_property_read_bool(node, | |
219 | "mipi-sdw-simplified-channelprepare-sm"); | |
220 | ||
221 | fwnode_property_read_u32(node, | |
31dba312 PLB |
222 | "mipi-sdw-port-channelprepare-timeout", |
223 | &dpn[i].ch_prep_timeout); | |
56d4fe31 VK |
224 | |
225 | fwnode_property_read_u32(node, | |
226 | "mipi-sdw-imp-def-dpn-interrupts-supported", | |
227 | &dpn[i].device_interrupts); | |
228 | ||
229 | fwnode_property_read_u32(node, "mipi-sdw-min-channel-number", | |
31dba312 | 230 | &dpn[i].min_ch); |
56d4fe31 VK |
231 | |
232 | fwnode_property_read_u32(node, "mipi-sdw-max-channel-number", | |
31dba312 | 233 | &dpn[i].max_ch); |
56d4fe31 VK |
234 | |
235 | nval = fwnode_property_read_u32_array(node, | |
236 | "mipi-sdw-channel-number-list", NULL, 0); | |
237 | if (nval > 0) { | |
56d4fe31 VK |
238 | dpn[i].num_ch = nval; |
239 | dpn[i].ch = devm_kcalloc(&slave->dev, dpn[i].num_ch, | |
31dba312 PLB |
240 | sizeof(*dpn[i].ch), |
241 | GFP_KERNEL); | |
56d4fe31 VK |
242 | if (!dpn[i].ch) |
243 | return -ENOMEM; | |
244 | ||
245 | fwnode_property_read_u32_array(node, | |
246 | "mipi-sdw-channel-number-list", | |
247 | dpn[i].ch, dpn[i].num_ch); | |
248 | } | |
249 | ||
250 | nval = fwnode_property_read_u32_array(node, | |
251 | "mipi-sdw-channel-combination-list", NULL, 0); | |
252 | if (nval > 0) { | |
56d4fe31 VK |
253 | dpn[i].num_ch_combinations = nval; |
254 | dpn[i].ch_combinations = devm_kcalloc(&slave->dev, | |
255 | dpn[i].num_ch_combinations, | |
256 | sizeof(*dpn[i].ch_combinations), | |
257 | GFP_KERNEL); | |
258 | if (!dpn[i].ch_combinations) | |
259 | return -ENOMEM; | |
260 | ||
261 | fwnode_property_read_u32_array(node, | |
262 | "mipi-sdw-channel-combination-list", | |
263 | dpn[i].ch_combinations, | |
264 | dpn[i].num_ch_combinations); | |
265 | } | |
266 | ||
267 | fwnode_property_read_u32(node, | |
268 | "mipi-sdw-modes-supported", &dpn[i].modes); | |
269 | ||
270 | fwnode_property_read_u32(node, "mipi-sdw-max-async-buffer", | |
31dba312 | 271 | &dpn[i].max_async_buffer); |
56d4fe31 VK |
272 | |
273 | dpn[i].block_pack_mode = fwnode_property_read_bool(node, | |
274 | "mipi-sdw-block-packing-mode"); | |
275 | ||
276 | fwnode_property_read_u32(node, "mipi-sdw-port-encoding-type", | |
31dba312 | 277 | &dpn[i].port_encoding); |
56d4fe31 VK |
278 | |
279 | /* TODO: Read audio mode */ | |
280 | ||
281 | i++; | |
282 | } | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | /** | |
288 | * sdw_slave_read_prop() - Read Slave properties | |
289 | * @slave: SDW Slave | |
290 | */ | |
291 | int sdw_slave_read_prop(struct sdw_slave *slave) | |
292 | { | |
293 | struct sdw_slave_prop *prop = &slave->prop; | |
294 | struct device *dev = &slave->dev; | |
295 | struct fwnode_handle *port; | |
296 | int num_of_ports, nval, i, dp0 = 0; | |
297 | ||
298 | device_property_read_u32(dev, "mipi-sdw-sw-interface-revision", | |
31dba312 | 299 | &prop->mipi_revision); |
56d4fe31 VK |
300 | |
301 | prop->wake_capable = device_property_read_bool(dev, | |
302 | "mipi-sdw-wake-up-unavailable"); | |
303 | prop->wake_capable = !prop->wake_capable; | |
304 | ||
305 | prop->test_mode_capable = device_property_read_bool(dev, | |
306 | "mipi-sdw-test-mode-supported"); | |
307 | ||
308 | prop->clk_stop_mode1 = false; | |
309 | if (device_property_read_bool(dev, | |
310 | "mipi-sdw-clock-stop-mode1-supported")) | |
311 | prop->clk_stop_mode1 = true; | |
312 | ||
313 | prop->simple_clk_stop_capable = device_property_read_bool(dev, | |
314 | "mipi-sdw-simplified-clockstopprepare-sm-supported"); | |
315 | ||
316 | device_property_read_u32(dev, "mipi-sdw-clockstopprepare-timeout", | |
31dba312 | 317 | &prop->clk_stop_timeout); |
56d4fe31 VK |
318 | |
319 | device_property_read_u32(dev, "mipi-sdw-slave-channelprepare-timeout", | |
31dba312 | 320 | &prop->ch_prep_timeout); |
56d4fe31 VK |
321 | |
322 | device_property_read_u32(dev, | |
323 | "mipi-sdw-clockstopprepare-hard-reset-behavior", | |
324 | &prop->reset_behave); | |
325 | ||
326 | prop->high_PHY_capable = device_property_read_bool(dev, | |
327 | "mipi-sdw-highPHY-capable"); | |
328 | ||
329 | prop->paging_support = device_property_read_bool(dev, | |
330 | "mipi-sdw-paging-support"); | |
331 | ||
332 | prop->bank_delay_support = device_property_read_bool(dev, | |
333 | "mipi-sdw-bank-delay-support"); | |
334 | ||
335 | device_property_read_u32(dev, | |
336 | "mipi-sdw-port15-read-behavior", &prop->p15_behave); | |
337 | ||
338 | device_property_read_u32(dev, "mipi-sdw-master-count", | |
31dba312 | 339 | &prop->master_count); |
56d4fe31 VK |
340 | |
341 | device_property_read_u32(dev, "mipi-sdw-source-port-list", | |
31dba312 | 342 | &prop->source_ports); |
56d4fe31 VK |
343 | |
344 | device_property_read_u32(dev, "mipi-sdw-sink-port-list", | |
31dba312 | 345 | &prop->sink_ports); |
56d4fe31 VK |
346 | |
347 | /* Read dp0 properties */ | |
348 | port = device_get_named_child_node(dev, "mipi-sdw-dp-0-subproperties"); | |
349 | if (!port) { | |
350 | dev_dbg(dev, "DP0 node not found!!\n"); | |
351 | } else { | |
56d4fe31 | 352 | prop->dp0_prop = devm_kzalloc(&slave->dev, |
31dba312 PLB |
353 | sizeof(*prop->dp0_prop), |
354 | GFP_KERNEL); | |
56d4fe31 VK |
355 | if (!prop->dp0_prop) |
356 | return -ENOMEM; | |
357 | ||
358 | sdw_slave_read_dp0(slave, port, prop->dp0_prop); | |
359 | dp0 = 1; | |
360 | } | |
361 | ||
362 | /* | |
363 | * Based on each DPn port, get source and sink dpn properties. | |
364 | * Also, some ports can operate as both source or sink. | |
365 | */ | |
366 | ||
367 | /* Allocate memory for set bits in port lists */ | |
368 | nval = hweight32(prop->source_ports); | |
369 | prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, | |
31dba312 PLB |
370 | sizeof(*prop->src_dpn_prop), |
371 | GFP_KERNEL); | |
56d4fe31 VK |
372 | if (!prop->src_dpn_prop) |
373 | return -ENOMEM; | |
374 | ||
375 | /* Read dpn properties for source port(s) */ | |
376 | sdw_slave_read_dpn(slave, prop->src_dpn_prop, nval, | |
31dba312 | 377 | prop->source_ports, "source"); |
56d4fe31 VK |
378 | |
379 | nval = hweight32(prop->sink_ports); | |
380 | prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, | |
31dba312 PLB |
381 | sizeof(*prop->sink_dpn_prop), |
382 | GFP_KERNEL); | |
56d4fe31 VK |
383 | if (!prop->sink_dpn_prop) |
384 | return -ENOMEM; | |
385 | ||
386 | /* Read dpn properties for sink port(s) */ | |
387 | sdw_slave_read_dpn(slave, prop->sink_dpn_prop, nval, | |
31dba312 | 388 | prop->sink_ports, "sink"); |
56d4fe31 VK |
389 | |
390 | /* some ports are bidirectional so check total ports by ORing */ | |
391 | nval = prop->source_ports | prop->sink_ports; | |
392 | num_of_ports = hweight32(nval) + dp0; /* add DP0 */ | |
393 | ||
394 | /* Allocate port_ready based on num_of_ports */ | |
395 | slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, | |
31dba312 PLB |
396 | sizeof(*slave->port_ready), |
397 | GFP_KERNEL); | |
56d4fe31 VK |
398 | if (!slave->port_ready) |
399 | return -ENOMEM; | |
400 | ||
401 | /* Initialize completion */ | |
402 | for (i = 0; i < num_of_ports; i++) | |
403 | init_completion(&slave->port_ready[i]); | |
404 | ||
405 | return 0; | |
406 | } | |
407 | EXPORT_SYMBOL(sdw_slave_read_prop); |