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