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