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