1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Author: Dan Scally <djrscally@gmail.com> */ |
3 | |
4 | #include <linux/acpi.h> |
5 | #include <linux/device.h> |
6 | #include <linux/i2c.h> |
7 | #include <linux/mei_cl_bus.h> |
8 | #include <linux/platform_device.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <linux/property.h> |
11 | #include <linux/string.h> |
12 | #include <linux/workqueue.h> |
13 | |
14 | #include <media/ipu-bridge.h> |
15 | #include <media/v4l2-fwnode.h> |
16 | |
17 | /* |
18 | * 92335fcf-3203-4472-af93-7b4453ac29da |
19 | * |
20 | * Used to build MEI CSI device name to lookup MEI CSI device by |
21 | * device_find_child_by_name(). |
22 | */ |
23 | #define MEI_CSI_UUID \ |
24 | UUID_LE(0x92335FCF, 0x3203, 0x4472, \ |
25 | 0xAF, 0x93, 0x7B, 0x44, 0x53, 0xAC, 0x29, 0xDA) |
26 | |
27 | /* |
28 | * IVSC device name |
29 | * |
30 | * Used to match IVSC device by ipu_bridge_match_ivsc_dev() |
31 | */ |
32 | #define IVSC_DEV_NAME "intel_vsc" |
33 | |
34 | /* |
35 | * Extend this array with ACPI Hardware IDs of devices known to be working |
36 | * plus the number of link-frequencies expected by their drivers, along with |
37 | * the frequency values in hertz. This is somewhat opportunistic way of adding |
38 | * support for this for now in the hopes of a better source for the information |
39 | * (possibly some encoded value in the SSDB buffer that we're unaware of) |
40 | * becoming apparent in the future. |
41 | * |
42 | * Do not add an entry for a sensor that is not actually supported. |
43 | */ |
44 | static const struct ipu_sensor_config ipu_supported_sensors[] = { |
45 | /* Omnivision OV5693 */ |
46 | IPU_SENSOR_CONFIG("INT33BE" , 1, 419200000), |
47 | /* Omnivision OV8865 */ |
48 | IPU_SENSOR_CONFIG("INT347A" , 1, 360000000), |
49 | /* Omnivision OV7251 */ |
50 | IPU_SENSOR_CONFIG("INT347E" , 1, 319200000), |
51 | /* Omnivision OV2680 */ |
52 | IPU_SENSOR_CONFIG("OVTI2680" , 1, 331200000), |
53 | /* Omnivision ov8856 */ |
54 | IPU_SENSOR_CONFIG("OVTI8856" , 3, 180000000, 360000000, 720000000), |
55 | /* Omnivision ov2740 */ |
56 | IPU_SENSOR_CONFIG("INT3474" , 1, 360000000), |
57 | /* Hynix hi556 */ |
58 | IPU_SENSOR_CONFIG("INT3537" , 1, 437000000), |
59 | /* Omnivision ov13b10 */ |
60 | IPU_SENSOR_CONFIG("OVTIDB10" , 1, 560000000), |
61 | /* GalaxyCore GC0310 */ |
62 | IPU_SENSOR_CONFIG("INT0310" , 0), |
63 | }; |
64 | |
65 | static const struct ipu_property_names prop_names = { |
66 | .clock_frequency = "clock-frequency" , |
67 | .rotation = "rotation" , |
68 | .orientation = "orientation" , |
69 | .bus_type = "bus-type" , |
70 | .data_lanes = "data-lanes" , |
71 | .remote_endpoint = "remote-endpoint" , |
72 | .link_frequencies = "link-frequencies" , |
73 | }; |
74 | |
75 | static const char * const ipu_vcm_types[] = { |
76 | "ad5823" , |
77 | "dw9714" , |
78 | "ad5816" , |
79 | "dw9719" , |
80 | "dw9718" , |
81 | "dw9806b" , |
82 | "wv517s" , |
83 | "lc898122xa" , |
84 | "lc898212axb" , |
85 | }; |
86 | |
87 | /* |
88 | * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev() |
89 | * instead of device and driver match to probe IVSC device. |
90 | */ |
91 | static const struct acpi_device_id ivsc_acpi_ids[] = { |
92 | { "INTC1059" }, |
93 | { "INTC1095" }, |
94 | { "INTC100A" }, |
95 | { "INTC10CF" }, |
96 | }; |
97 | |
98 | static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) |
99 | { |
100 | acpi_handle handle = acpi_device_handle(adev); |
101 | struct acpi_device *consumer, *ivsc_adev; |
102 | unsigned int i; |
103 | |
104 | for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { |
105 | const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; |
106 | |
107 | for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) |
108 | /* camera sensor depends on IVSC in DSDT if exist */ |
109 | for_each_acpi_consumer_dev(ivsc_adev, consumer) |
110 | if (consumer->handle == handle) { |
111 | acpi_dev_put(adev: consumer); |
112 | return ivsc_adev; |
113 | } |
114 | } |
115 | |
116 | return NULL; |
117 | } |
118 | |
119 | static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev) |
120 | { |
121 | if (ACPI_COMPANION(dev) != adev) |
122 | return 0; |
123 | |
124 | if (!sysfs_streq(s1: dev_name(dev), IVSC_DEV_NAME)) |
125 | return 0; |
126 | |
127 | return 1; |
128 | } |
129 | |
130 | static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) |
131 | { |
132 | struct device *dev, *csi_dev; |
133 | uuid_le uuid = MEI_CSI_UUID; |
134 | char name[64]; |
135 | |
136 | /* IVSC device on platform bus */ |
137 | dev = bus_find_device(bus: &platform_bus_type, NULL, data: adev, |
138 | match: ipu_bridge_match_ivsc_dev); |
139 | if (dev) { |
140 | snprintf(buf: name, size: sizeof(name), fmt: "%s-%pUl" , dev_name(dev), &uuid); |
141 | |
142 | csi_dev = device_find_child_by_name(parent: dev, name); |
143 | |
144 | put_device(dev); |
145 | |
146 | return csi_dev; |
147 | } |
148 | |
149 | return NULL; |
150 | } |
151 | |
152 | static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, |
153 | struct acpi_device *sensor_adev) |
154 | { |
155 | struct acpi_device *adev; |
156 | struct device *csi_dev; |
157 | |
158 | adev = ipu_bridge_get_ivsc_acpi_dev(adev: sensor_adev); |
159 | if (adev) { |
160 | csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); |
161 | if (!csi_dev) { |
162 | acpi_dev_put(adev); |
163 | dev_err(&adev->dev, "Failed to find MEI CSI dev\n" ); |
164 | return -ENODEV; |
165 | } |
166 | |
167 | sensor->csi_dev = csi_dev; |
168 | sensor->ivsc_adev = adev; |
169 | } |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | static int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, |
175 | void *data, u32 size) |
176 | { |
177 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
178 | union acpi_object *obj; |
179 | acpi_status status; |
180 | int ret = 0; |
181 | |
182 | status = acpi_evaluate_object(object: adev->handle, pathname: id, NULL, return_object_buffer: &buffer); |
183 | if (ACPI_FAILURE(status)) |
184 | return -ENODEV; |
185 | |
186 | obj = buffer.pointer; |
187 | if (!obj) { |
188 | dev_err(&adev->dev, "Couldn't locate ACPI buffer\n" ); |
189 | return -ENODEV; |
190 | } |
191 | |
192 | if (obj->type != ACPI_TYPE_BUFFER) { |
193 | dev_err(&adev->dev, "Not an ACPI buffer\n" ); |
194 | ret = -ENODEV; |
195 | goto out_free_buff; |
196 | } |
197 | |
198 | if (obj->buffer.length > size) { |
199 | dev_err(&adev->dev, "Given buffer is too small\n" ); |
200 | ret = -EINVAL; |
201 | goto out_free_buff; |
202 | } |
203 | |
204 | memcpy(data, obj->buffer.pointer, obj->buffer.length); |
205 | |
206 | out_free_buff: |
207 | kfree(objp: buffer.pointer); |
208 | return ret; |
209 | } |
210 | |
211 | static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, |
212 | struct ipu_sensor_ssdb *ssdb) |
213 | { |
214 | switch (ssdb->degree) { |
215 | case IPU_SENSOR_ROTATION_NORMAL: |
216 | return 0; |
217 | case IPU_SENSOR_ROTATION_INVERTED: |
218 | return 180; |
219 | default: |
220 | dev_warn(&adev->dev, |
221 | "Unknown rotation %d. Assume 0 degree rotation\n" , |
222 | ssdb->degree); |
223 | return 0; |
224 | } |
225 | } |
226 | |
227 | static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev) |
228 | { |
229 | enum v4l2_fwnode_orientation orientation; |
230 | struct acpi_pld_info *pld; |
231 | acpi_status status; |
232 | |
233 | status = acpi_get_physical_device_location(handle: adev->handle, pld: &pld); |
234 | if (ACPI_FAILURE(status)) { |
235 | dev_warn(&adev->dev, "_PLD call failed, using default orientation\n" ); |
236 | return V4L2_FWNODE_ORIENTATION_EXTERNAL; |
237 | } |
238 | |
239 | switch (pld->panel) { |
240 | case ACPI_PLD_PANEL_FRONT: |
241 | orientation = V4L2_FWNODE_ORIENTATION_FRONT; |
242 | break; |
243 | case ACPI_PLD_PANEL_BACK: |
244 | orientation = V4L2_FWNODE_ORIENTATION_BACK; |
245 | break; |
246 | case ACPI_PLD_PANEL_TOP: |
247 | case ACPI_PLD_PANEL_LEFT: |
248 | case ACPI_PLD_PANEL_RIGHT: |
249 | case ACPI_PLD_PANEL_UNKNOWN: |
250 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; |
251 | break; |
252 | default: |
253 | dev_warn(&adev->dev, "Unknown _PLD panel val %d\n" , pld->panel); |
254 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; |
255 | break; |
256 | } |
257 | |
258 | ACPI_FREE(pld); |
259 | return orientation; |
260 | } |
261 | |
262 | int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) |
263 | { |
264 | struct ipu_sensor_ssdb ssdb = {}; |
265 | int ret; |
266 | |
267 | ret = ipu_bridge_read_acpi_buffer(adev, id: "SSDB" , data: &ssdb, size: sizeof(ssdb)); |
268 | if (ret) |
269 | return ret; |
270 | |
271 | if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) { |
272 | dev_warn(&adev->dev, "Unknown VCM type %d\n" , ssdb.vcmtype); |
273 | ssdb.vcmtype = 0; |
274 | } |
275 | |
276 | if (ssdb.lanes > IPU_MAX_LANES) { |
277 | dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n" ); |
278 | return -EINVAL; |
279 | } |
280 | |
281 | sensor->link = ssdb.link; |
282 | sensor->lanes = ssdb.lanes; |
283 | sensor->mclkspeed = ssdb.mclkspeed; |
284 | sensor->rotation = ipu_bridge_parse_rotation(adev, ssdb: &ssdb); |
285 | sensor->orientation = ipu_bridge_parse_orientation(adev); |
286 | |
287 | if (ssdb.vcmtype) |
288 | sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1]; |
289 | |
290 | return 0; |
291 | } |
292 | EXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, INTEL_IPU_BRIDGE); |
293 | |
294 | static void ipu_bridge_create_fwnode_properties( |
295 | struct ipu_sensor *sensor, |
296 | struct ipu_bridge *bridge, |
297 | const struct ipu_sensor_config *cfg) |
298 | { |
299 | struct ipu_property_names *names = &sensor->prop_names; |
300 | struct software_node *nodes = sensor->swnodes; |
301 | |
302 | sensor->prop_names = prop_names; |
303 | |
304 | if (sensor->csi_dev) { |
305 | sensor->local_ref[0] = |
306 | SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_SENSOR_ENDPOINT]); |
307 | sensor->remote_ref[0] = |
308 | SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_IPU_ENDPOINT]); |
309 | sensor->ivsc_sensor_ref[0] = |
310 | SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]); |
311 | sensor->ivsc_ipu_ref[0] = |
312 | SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]); |
313 | |
314 | sensor->ivsc_sensor_ep_properties[0] = |
315 | PROPERTY_ENTRY_U32(names->bus_type, |
316 | V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); |
317 | sensor->ivsc_sensor_ep_properties[1] = |
318 | PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, |
319 | bridge->data_lanes, |
320 | sensor->lanes); |
321 | sensor->ivsc_sensor_ep_properties[2] = |
322 | PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, |
323 | sensor->ivsc_sensor_ref); |
324 | |
325 | sensor->ivsc_ipu_ep_properties[0] = |
326 | PROPERTY_ENTRY_U32(names->bus_type, |
327 | V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); |
328 | sensor->ivsc_ipu_ep_properties[1] = |
329 | PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, |
330 | bridge->data_lanes, |
331 | sensor->lanes); |
332 | sensor->ivsc_ipu_ep_properties[2] = |
333 | PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, |
334 | sensor->ivsc_ipu_ref); |
335 | } else { |
336 | sensor->local_ref[0] = |
337 | SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]); |
338 | sensor->remote_ref[0] = |
339 | SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]); |
340 | } |
341 | |
342 | sensor->dev_properties[0] = PROPERTY_ENTRY_U32( |
343 | sensor->prop_names.clock_frequency, |
344 | sensor->mclkspeed); |
345 | sensor->dev_properties[1] = PROPERTY_ENTRY_U32( |
346 | sensor->prop_names.rotation, |
347 | sensor->rotation); |
348 | sensor->dev_properties[2] = PROPERTY_ENTRY_U32( |
349 | sensor->prop_names.orientation, |
350 | sensor->orientation); |
351 | if (sensor->vcm_type) { |
352 | sensor->vcm_ref[0] = |
353 | SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); |
354 | sensor->dev_properties[3] = |
355 | PROPERTY_ENTRY_REF_ARRAY("lens-focus" , sensor->vcm_ref); |
356 | } |
357 | |
358 | sensor->ep_properties[0] = PROPERTY_ENTRY_U32( |
359 | sensor->prop_names.bus_type, |
360 | V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); |
361 | sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( |
362 | sensor->prop_names.data_lanes, |
363 | bridge->data_lanes, sensor->lanes); |
364 | sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( |
365 | sensor->prop_names.remote_endpoint, |
366 | sensor->local_ref); |
367 | |
368 | if (cfg->nr_link_freqs > 0) |
369 | sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( |
370 | sensor->prop_names.link_frequencies, |
371 | cfg->link_freqs, |
372 | cfg->nr_link_freqs); |
373 | |
374 | sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( |
375 | sensor->prop_names.data_lanes, |
376 | bridge->data_lanes, sensor->lanes); |
377 | sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( |
378 | sensor->prop_names.remote_endpoint, |
379 | sensor->remote_ref); |
380 | } |
381 | |
382 | static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) |
383 | { |
384 | snprintf(buf: sensor->node_names.remote_port, |
385 | size: sizeof(sensor->node_names.remote_port), |
386 | SWNODE_GRAPH_PORT_NAME_FMT, sensor->link); |
387 | snprintf(buf: sensor->node_names.port, |
388 | size: sizeof(sensor->node_names.port), |
389 | SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ |
390 | snprintf(buf: sensor->node_names.endpoint, |
391 | size: sizeof(sensor->node_names.endpoint), |
392 | SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ |
393 | if (sensor->vcm_type) { |
394 | /* append link to distinguish nodes with same model VCM */ |
395 | snprintf(buf: sensor->node_names.vcm, size: sizeof(sensor->node_names.vcm), |
396 | fmt: "%s-%u" , sensor->vcm_type, sensor->link); |
397 | } |
398 | |
399 | if (sensor->csi_dev) { |
400 | snprintf(buf: sensor->node_names.ivsc_sensor_port, |
401 | size: sizeof(sensor->node_names.ivsc_sensor_port), |
402 | SWNODE_GRAPH_PORT_NAME_FMT, 0); |
403 | snprintf(buf: sensor->node_names.ivsc_ipu_port, |
404 | size: sizeof(sensor->node_names.ivsc_ipu_port), |
405 | SWNODE_GRAPH_PORT_NAME_FMT, 1); |
406 | } |
407 | } |
408 | |
409 | static void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor) |
410 | { |
411 | struct software_node *nodes = sensor->swnodes; |
412 | |
413 | sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID]; |
414 | sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; |
415 | sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; |
416 | sensor->group[SWNODE_IPU_PORT] = &nodes[SWNODE_IPU_PORT]; |
417 | sensor->group[SWNODE_IPU_ENDPOINT] = &nodes[SWNODE_IPU_ENDPOINT]; |
418 | if (sensor->vcm_type) |
419 | sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; |
420 | |
421 | if (sensor->csi_dev) { |
422 | sensor->group[SWNODE_IVSC_HID] = |
423 | &nodes[SWNODE_IVSC_HID]; |
424 | sensor->group[SWNODE_IVSC_SENSOR_PORT] = |
425 | &nodes[SWNODE_IVSC_SENSOR_PORT]; |
426 | sensor->group[SWNODE_IVSC_SENSOR_ENDPOINT] = |
427 | &nodes[SWNODE_IVSC_SENSOR_ENDPOINT]; |
428 | sensor->group[SWNODE_IVSC_IPU_PORT] = |
429 | &nodes[SWNODE_IVSC_IPU_PORT]; |
430 | sensor->group[SWNODE_IVSC_IPU_ENDPOINT] = |
431 | &nodes[SWNODE_IVSC_IPU_ENDPOINT]; |
432 | |
433 | if (sensor->vcm_type) |
434 | sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; |
435 | } else { |
436 | if (sensor->vcm_type) |
437 | sensor->group[SWNODE_IVSC_HID] = &nodes[SWNODE_VCM]; |
438 | } |
439 | } |
440 | |
441 | static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge, |
442 | struct ipu_sensor *sensor) |
443 | { |
444 | struct ipu_node_names *names = &sensor->node_names; |
445 | struct software_node *nodes = sensor->swnodes; |
446 | |
447 | ipu_bridge_init_swnode_names(sensor); |
448 | |
449 | nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name, |
450 | sensor->dev_properties); |
451 | nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, |
452 | &nodes[SWNODE_SENSOR_HID]); |
453 | nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( |
454 | sensor->node_names.endpoint, |
455 | &nodes[SWNODE_SENSOR_PORT], |
456 | sensor->ep_properties); |
457 | nodes[SWNODE_IPU_PORT] = NODE_PORT(sensor->node_names.remote_port, |
458 | &bridge->ipu_hid_node); |
459 | nodes[SWNODE_IPU_ENDPOINT] = NODE_ENDPOINT( |
460 | sensor->node_names.endpoint, |
461 | &nodes[SWNODE_IPU_PORT], |
462 | sensor->ipu_properties); |
463 | |
464 | if (sensor->csi_dev) { |
465 | snprintf(buf: sensor->ivsc_name, size: sizeof(sensor->ivsc_name), fmt: "%s-%u" , |
466 | acpi_device_hid(device: sensor->ivsc_adev), sensor->link); |
467 | |
468 | nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name, |
469 | sensor->ivsc_properties); |
470 | nodes[SWNODE_IVSC_SENSOR_PORT] = |
471 | NODE_PORT(names->ivsc_sensor_port, |
472 | &nodes[SWNODE_IVSC_HID]); |
473 | nodes[SWNODE_IVSC_SENSOR_ENDPOINT] = |
474 | NODE_ENDPOINT(names->endpoint, |
475 | &nodes[SWNODE_IVSC_SENSOR_PORT], |
476 | sensor->ivsc_sensor_ep_properties); |
477 | nodes[SWNODE_IVSC_IPU_PORT] = |
478 | NODE_PORT(names->ivsc_ipu_port, |
479 | &nodes[SWNODE_IVSC_HID]); |
480 | nodes[SWNODE_IVSC_IPU_ENDPOINT] = |
481 | NODE_ENDPOINT(names->endpoint, |
482 | &nodes[SWNODE_IVSC_IPU_PORT], |
483 | sensor->ivsc_ipu_ep_properties); |
484 | } |
485 | |
486 | nodes[SWNODE_VCM] = NODE_VCM(sensor->node_names.vcm); |
487 | |
488 | ipu_bridge_init_swnode_group(sensor); |
489 | } |
490 | |
491 | /* |
492 | * The actual instantiation must be done from a workqueue to avoid |
493 | * a deadlock on taking list_lock from v4l2-async twice. |
494 | */ |
495 | struct ipu_bridge_instantiate_vcm_work_data { |
496 | struct work_struct work; |
497 | struct device *sensor; |
498 | char name[16]; |
499 | struct i2c_board_info board_info; |
500 | }; |
501 | |
502 | static void ipu_bridge_instantiate_vcm_work(struct work_struct *work) |
503 | { |
504 | struct ipu_bridge_instantiate_vcm_work_data *data = |
505 | container_of(work, struct ipu_bridge_instantiate_vcm_work_data, |
506 | work); |
507 | struct acpi_device *adev = ACPI_COMPANION(data->sensor); |
508 | struct i2c_client *vcm_client; |
509 | bool put_fwnode = true; |
510 | int ret; |
511 | |
512 | /* |
513 | * The client may get probed before the device_link gets added below |
514 | * make sure the sensor is powered-up during probe. |
515 | */ |
516 | ret = pm_runtime_get_sync(dev: data->sensor); |
517 | if (ret < 0) { |
518 | dev_err(data->sensor, "Error %d runtime-resuming sensor, cannot instantiate VCM\n" , |
519 | ret); |
520 | goto out_pm_put; |
521 | } |
522 | |
523 | /* |
524 | * Note the client is created only once and then kept around |
525 | * even after a rmmod, just like the software-nodes. |
526 | */ |
527 | vcm_client = i2c_acpi_new_device_by_fwnode(fwnode: acpi_fwnode_handle(adev), |
528 | index: 1, info: &data->board_info); |
529 | if (IS_ERR(ptr: vcm_client)) { |
530 | dev_err(data->sensor, "Error instantiating VCM client: %ld\n" , |
531 | PTR_ERR(vcm_client)); |
532 | goto out_pm_put; |
533 | } |
534 | |
535 | device_link_add(consumer: &vcm_client->dev, supplier: data->sensor, DL_FLAG_PM_RUNTIME); |
536 | |
537 | dev_info(data->sensor, "Instantiated %s VCM\n" , data->board_info.type); |
538 | put_fwnode = false; /* Ownership has passed to the i2c-client */ |
539 | |
540 | out_pm_put: |
541 | pm_runtime_put(dev: data->sensor); |
542 | put_device(dev: data->sensor); |
543 | if (put_fwnode) |
544 | fwnode_handle_put(fwnode: data->board_info.fwnode); |
545 | kfree(objp: data); |
546 | } |
547 | |
548 | int ipu_bridge_instantiate_vcm(struct device *sensor) |
549 | { |
550 | struct ipu_bridge_instantiate_vcm_work_data *data; |
551 | struct fwnode_handle *vcm_fwnode; |
552 | struct i2c_client *vcm_client; |
553 | struct acpi_device *adev; |
554 | char *sep; |
555 | |
556 | adev = ACPI_COMPANION(sensor); |
557 | if (!adev) |
558 | return 0; |
559 | |
560 | vcm_fwnode = fwnode_find_reference(dev_fwnode(sensor), name: "lens-focus" , index: 0); |
561 | if (IS_ERR(ptr: vcm_fwnode)) |
562 | return 0; |
563 | |
564 | /* When reloading modules the client will already exist */ |
565 | vcm_client = i2c_find_device_by_fwnode(fwnode: vcm_fwnode); |
566 | if (vcm_client) { |
567 | fwnode_handle_put(fwnode: vcm_fwnode); |
568 | put_device(dev: &vcm_client->dev); |
569 | return 0; |
570 | } |
571 | |
572 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
573 | if (!data) { |
574 | fwnode_handle_put(fwnode: vcm_fwnode); |
575 | return -ENOMEM; |
576 | } |
577 | |
578 | INIT_WORK(&data->work, ipu_bridge_instantiate_vcm_work); |
579 | data->sensor = get_device(dev: sensor); |
580 | snprintf(buf: data->name, size: sizeof(data->name), fmt: "%s-VCM" , |
581 | acpi_dev_name(adev)); |
582 | data->board_info.dev_name = data->name; |
583 | data->board_info.fwnode = vcm_fwnode; |
584 | snprintf(buf: data->board_info.type, size: sizeof(data->board_info.type), |
585 | fmt: "%pfwP" , vcm_fwnode); |
586 | /* Strip "-<link>" postfix */ |
587 | sep = strchrnul(data->board_info.type, '-'); |
588 | *sep = 0; |
589 | |
590 | queue_work(wq: system_long_wq, work: &data->work); |
591 | |
592 | return 0; |
593 | } |
594 | EXPORT_SYMBOL_NS_GPL(ipu_bridge_instantiate_vcm, INTEL_IPU_BRIDGE); |
595 | |
596 | static int ipu_bridge_instantiate_ivsc(struct ipu_sensor *sensor) |
597 | { |
598 | struct fwnode_handle *fwnode; |
599 | |
600 | if (!sensor->csi_dev) |
601 | return 0; |
602 | |
603 | fwnode = software_node_fwnode(node: &sensor->swnodes[SWNODE_IVSC_HID]); |
604 | if (!fwnode) |
605 | return -ENODEV; |
606 | |
607 | set_secondary_fwnode(dev: sensor->csi_dev, fwnode); |
608 | |
609 | return 0; |
610 | } |
611 | |
612 | static void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge) |
613 | { |
614 | struct ipu_sensor *sensor; |
615 | unsigned int i; |
616 | |
617 | for (i = 0; i < bridge->n_sensors; i++) { |
618 | sensor = &bridge->sensors[i]; |
619 | software_node_unregister_node_group(node_group: sensor->group); |
620 | acpi_dev_put(adev: sensor->adev); |
621 | put_device(dev: sensor->csi_dev); |
622 | acpi_dev_put(adev: sensor->ivsc_adev); |
623 | } |
624 | } |
625 | |
626 | static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, |
627 | struct ipu_bridge *bridge) |
628 | { |
629 | struct fwnode_handle *fwnode, *primary; |
630 | struct ipu_sensor *sensor; |
631 | struct acpi_device *adev; |
632 | int ret; |
633 | |
634 | for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { |
635 | if (!adev->status.enabled) |
636 | continue; |
637 | |
638 | if (bridge->n_sensors >= IPU_MAX_PORTS) { |
639 | acpi_dev_put(adev); |
640 | dev_err(bridge->dev, "Exceeded available IPU ports\n" ); |
641 | return -EINVAL; |
642 | } |
643 | |
644 | sensor = &bridge->sensors[bridge->n_sensors]; |
645 | |
646 | ret = bridge->parse_sensor_fwnode(adev, sensor); |
647 | if (ret) |
648 | goto err_put_adev; |
649 | |
650 | snprintf(buf: sensor->name, size: sizeof(sensor->name), fmt: "%s-%u" , |
651 | cfg->hid, sensor->link); |
652 | |
653 | ret = ipu_bridge_check_ivsc_dev(sensor, sensor_adev: adev); |
654 | if (ret) |
655 | goto err_put_adev; |
656 | |
657 | ipu_bridge_create_fwnode_properties(sensor, bridge, cfg); |
658 | ipu_bridge_create_connection_swnodes(bridge, sensor); |
659 | |
660 | ret = software_node_register_node_group(node_group: sensor->group); |
661 | if (ret) |
662 | goto err_put_ivsc; |
663 | |
664 | fwnode = software_node_fwnode(node: &sensor->swnodes[ |
665 | SWNODE_SENSOR_HID]); |
666 | if (!fwnode) { |
667 | ret = -ENODEV; |
668 | goto err_free_swnodes; |
669 | } |
670 | |
671 | sensor->adev = acpi_dev_get(adev); |
672 | |
673 | primary = acpi_fwnode_handle(adev); |
674 | primary->secondary = fwnode; |
675 | |
676 | ret = ipu_bridge_instantiate_ivsc(sensor); |
677 | if (ret) |
678 | goto err_free_swnodes; |
679 | |
680 | dev_info(bridge->dev, "Found supported sensor %s\n" , |
681 | acpi_dev_name(adev)); |
682 | |
683 | bridge->n_sensors++; |
684 | } |
685 | |
686 | return 0; |
687 | |
688 | err_free_swnodes: |
689 | software_node_unregister_node_group(node_group: sensor->group); |
690 | err_put_ivsc: |
691 | put_device(dev: sensor->csi_dev); |
692 | acpi_dev_put(adev: sensor->ivsc_adev); |
693 | err_put_adev: |
694 | acpi_dev_put(adev); |
695 | return ret; |
696 | } |
697 | |
698 | static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) |
699 | { |
700 | unsigned int i; |
701 | int ret; |
702 | |
703 | for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { |
704 | const struct ipu_sensor_config *cfg = |
705 | &ipu_supported_sensors[i]; |
706 | |
707 | ret = ipu_bridge_connect_sensor(cfg, bridge); |
708 | if (ret) |
709 | goto err_unregister_sensors; |
710 | } |
711 | |
712 | return 0; |
713 | |
714 | err_unregister_sensors: |
715 | ipu_bridge_unregister_sensors(bridge); |
716 | return ret; |
717 | } |
718 | |
719 | static int ipu_bridge_ivsc_is_ready(void) |
720 | { |
721 | struct acpi_device *sensor_adev, *adev; |
722 | struct device *csi_dev; |
723 | bool ready = true; |
724 | unsigned int i; |
725 | |
726 | for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { |
727 | const struct ipu_sensor_config *cfg = |
728 | &ipu_supported_sensors[i]; |
729 | |
730 | for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) { |
731 | if (!sensor_adev->status.enabled) |
732 | continue; |
733 | |
734 | adev = ipu_bridge_get_ivsc_acpi_dev(adev: sensor_adev); |
735 | if (!adev) |
736 | continue; |
737 | |
738 | csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); |
739 | if (!csi_dev) |
740 | ready = false; |
741 | |
742 | put_device(dev: csi_dev); |
743 | acpi_dev_put(adev); |
744 | } |
745 | } |
746 | |
747 | return ready; |
748 | } |
749 | |
750 | int ipu_bridge_init(struct device *dev, |
751 | ipu_parse_sensor_fwnode_t parse_sensor_fwnode) |
752 | { |
753 | struct fwnode_handle *fwnode; |
754 | struct ipu_bridge *bridge; |
755 | unsigned int i; |
756 | int ret; |
757 | |
758 | if (!ipu_bridge_ivsc_is_ready()) |
759 | return -EPROBE_DEFER; |
760 | |
761 | bridge = kzalloc(size: sizeof(*bridge), GFP_KERNEL); |
762 | if (!bridge) |
763 | return -ENOMEM; |
764 | |
765 | strscpy(p: bridge->ipu_node_name, IPU_HID, |
766 | size: sizeof(bridge->ipu_node_name)); |
767 | bridge->ipu_hid_node.name = bridge->ipu_node_name; |
768 | bridge->dev = dev; |
769 | bridge->parse_sensor_fwnode = parse_sensor_fwnode; |
770 | |
771 | ret = software_node_register(node: &bridge->ipu_hid_node); |
772 | if (ret < 0) { |
773 | dev_err(dev, "Failed to register the IPU HID node\n" ); |
774 | goto err_free_bridge; |
775 | } |
776 | |
777 | /* |
778 | * Map the lane arrangement, which is fixed for the IPU3 (meaning we |
779 | * only need one, rather than one per sensor). We include it as a |
780 | * member of the struct ipu_bridge rather than a global variable so |
781 | * that it survives if the module is unloaded along with the rest of |
782 | * the struct. |
783 | */ |
784 | for (i = 0; i < IPU_MAX_LANES; i++) |
785 | bridge->data_lanes[i] = i + 1; |
786 | |
787 | ret = ipu_bridge_connect_sensors(bridge); |
788 | if (ret || bridge->n_sensors == 0) |
789 | goto err_unregister_ipu; |
790 | |
791 | dev_info(dev, "Connected %d cameras\n" , bridge->n_sensors); |
792 | |
793 | fwnode = software_node_fwnode(node: &bridge->ipu_hid_node); |
794 | if (!fwnode) { |
795 | dev_err(dev, "Error getting fwnode from ipu software_node\n" ); |
796 | ret = -ENODEV; |
797 | goto err_unregister_sensors; |
798 | } |
799 | |
800 | set_secondary_fwnode(dev, fwnode); |
801 | |
802 | return 0; |
803 | |
804 | err_unregister_sensors: |
805 | ipu_bridge_unregister_sensors(bridge); |
806 | err_unregister_ipu: |
807 | software_node_unregister(node: &bridge->ipu_hid_node); |
808 | err_free_bridge: |
809 | kfree(objp: bridge); |
810 | |
811 | return ret; |
812 | } |
813 | EXPORT_SYMBOL_NS_GPL(ipu_bridge_init, INTEL_IPU_BRIDGE); |
814 | |
815 | MODULE_LICENSE("GPL" ); |
816 | MODULE_DESCRIPTION("Intel IPU Sensors Bridge driver" ); |
817 | |