162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Author: Dan Scally <djrscally@gmail.com> */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/acpi.h> 562306a36Sopenharmony_ci#include <linux/device.h> 662306a36Sopenharmony_ci#include <linux/i2c.h> 762306a36Sopenharmony_ci#include <linux/mei_cl_bus.h> 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1062306a36Sopenharmony_ci#include <linux/property.h> 1162306a36Sopenharmony_ci#include <linux/string.h> 1262306a36Sopenharmony_ci#include <linux/workqueue.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <media/ipu-bridge.h> 1562306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * 92335fcf-3203-4472-af93-7b4453ac29da 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Used to build MEI CSI device name to lookup MEI CSI device by 2162306a36Sopenharmony_ci * device_find_child_by_name(). 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define MEI_CSI_UUID \ 2462306a36Sopenharmony_ci UUID_LE(0x92335FCF, 0x3203, 0x4472, \ 2562306a36Sopenharmony_ci 0xAF, 0x93, 0x7B, 0x44, 0x53, 0xAC, 0x29, 0xDA) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * IVSC device name 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Used to match IVSC device by ipu_bridge_match_ivsc_dev() 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define IVSC_DEV_NAME "intel_vsc" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Extend this array with ACPI Hardware IDs of devices known to be working 3662306a36Sopenharmony_ci * plus the number of link-frequencies expected by their drivers, along with 3762306a36Sopenharmony_ci * the frequency values in hertz. This is somewhat opportunistic way of adding 3862306a36Sopenharmony_ci * support for this for now in the hopes of a better source for the information 3962306a36Sopenharmony_ci * (possibly some encoded value in the SSDB buffer that we're unaware of) 4062306a36Sopenharmony_ci * becoming apparent in the future. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Do not add an entry for a sensor that is not actually supported. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic const struct ipu_sensor_config ipu_supported_sensors[] = { 4562306a36Sopenharmony_ci /* Omnivision OV5693 */ 4662306a36Sopenharmony_ci IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), 4762306a36Sopenharmony_ci /* Omnivision OV8865 */ 4862306a36Sopenharmony_ci IPU_SENSOR_CONFIG("INT347A", 1, 360000000), 4962306a36Sopenharmony_ci /* Omnivision OV7251 */ 5062306a36Sopenharmony_ci IPU_SENSOR_CONFIG("INT347E", 1, 319200000), 5162306a36Sopenharmony_ci /* Omnivision OV2680 */ 5262306a36Sopenharmony_ci IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), 5362306a36Sopenharmony_ci /* Omnivision ov8856 */ 5462306a36Sopenharmony_ci IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), 5562306a36Sopenharmony_ci /* Omnivision ov2740 */ 5662306a36Sopenharmony_ci IPU_SENSOR_CONFIG("INT3474", 1, 360000000), 5762306a36Sopenharmony_ci /* Hynix hi556 */ 5862306a36Sopenharmony_ci IPU_SENSOR_CONFIG("INT3537", 1, 437000000), 5962306a36Sopenharmony_ci /* Omnivision ov13b10 */ 6062306a36Sopenharmony_ci IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), 6162306a36Sopenharmony_ci /* GalaxyCore GC0310 */ 6262306a36Sopenharmony_ci IPU_SENSOR_CONFIG("INT0310", 0), 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct ipu_property_names prop_names = { 6662306a36Sopenharmony_ci .clock_frequency = "clock-frequency", 6762306a36Sopenharmony_ci .rotation = "rotation", 6862306a36Sopenharmony_ci .orientation = "orientation", 6962306a36Sopenharmony_ci .bus_type = "bus-type", 7062306a36Sopenharmony_ci .data_lanes = "data-lanes", 7162306a36Sopenharmony_ci .remote_endpoint = "remote-endpoint", 7262306a36Sopenharmony_ci .link_frequencies = "link-frequencies", 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const char * const ipu_vcm_types[] = { 7662306a36Sopenharmony_ci "ad5823", 7762306a36Sopenharmony_ci "dw9714", 7862306a36Sopenharmony_ci "ad5816", 7962306a36Sopenharmony_ci "dw9719", 8062306a36Sopenharmony_ci "dw9718", 8162306a36Sopenharmony_ci "dw9806b", 8262306a36Sopenharmony_ci "wv517s", 8362306a36Sopenharmony_ci "lc898122xa", 8462306a36Sopenharmony_ci "lc898212axb", 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev() 8962306a36Sopenharmony_ci * instead of device and driver match to probe IVSC device. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic const struct acpi_device_id ivsc_acpi_ids[] = { 9262306a36Sopenharmony_ci { "INTC1059" }, 9362306a36Sopenharmony_ci { "INTC1095" }, 9462306a36Sopenharmony_ci { "INTC100A" }, 9562306a36Sopenharmony_ci { "INTC10CF" }, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci acpi_handle handle = acpi_device_handle(adev); 10162306a36Sopenharmony_ci struct acpi_device *consumer, *ivsc_adev; 10262306a36Sopenharmony_ci unsigned int i; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { 10562306a36Sopenharmony_ci const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) 10862306a36Sopenharmony_ci /* camera sensor depends on IVSC in DSDT if exist */ 10962306a36Sopenharmony_ci for_each_acpi_consumer_dev(ivsc_adev, consumer) 11062306a36Sopenharmony_ci if (consumer->handle == handle) { 11162306a36Sopenharmony_ci acpi_dev_put(consumer); 11262306a36Sopenharmony_ci return ivsc_adev; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci if (ACPI_COMPANION(dev) != adev) 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!sysfs_streq(dev_name(dev), IVSC_DEV_NAME)) 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 1; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct device *dev, *csi_dev; 13362306a36Sopenharmony_ci uuid_le uuid = MEI_CSI_UUID; 13462306a36Sopenharmony_ci char name[64]; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* IVSC device on platform bus */ 13762306a36Sopenharmony_ci dev = bus_find_device(&platform_bus_type, NULL, adev, 13862306a36Sopenharmony_ci ipu_bridge_match_ivsc_dev); 13962306a36Sopenharmony_ci if (dev) { 14062306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s-%pUl", dev_name(dev), &uuid); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci csi_dev = device_find_child_by_name(dev, name); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci put_device(dev); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return csi_dev; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return NULL; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, 15362306a36Sopenharmony_ci struct acpi_device *sensor_adev) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct acpi_device *adev; 15662306a36Sopenharmony_ci struct device *csi_dev; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); 15962306a36Sopenharmony_ci if (adev) { 16062306a36Sopenharmony_ci csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); 16162306a36Sopenharmony_ci if (!csi_dev) { 16262306a36Sopenharmony_ci acpi_dev_put(adev); 16362306a36Sopenharmony_ci dev_err(&adev->dev, "Failed to find MEI CSI dev\n"); 16462306a36Sopenharmony_ci return -ENODEV; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci sensor->csi_dev = csi_dev; 16862306a36Sopenharmony_ci sensor->ivsc_adev = adev; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, 17562306a36Sopenharmony_ci void *data, u32 size) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 17862306a36Sopenharmony_ci union acpi_object *obj; 17962306a36Sopenharmony_ci acpi_status status; 18062306a36Sopenharmony_ci int ret = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci status = acpi_evaluate_object(adev->handle, id, NULL, &buffer); 18362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 18462306a36Sopenharmony_ci return -ENODEV; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci obj = buffer.pointer; 18762306a36Sopenharmony_ci if (!obj) { 18862306a36Sopenharmony_ci dev_err(&adev->dev, "Couldn't locate ACPI buffer\n"); 18962306a36Sopenharmony_ci return -ENODEV; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_BUFFER) { 19362306a36Sopenharmony_ci dev_err(&adev->dev, "Not an ACPI buffer\n"); 19462306a36Sopenharmony_ci ret = -ENODEV; 19562306a36Sopenharmony_ci goto out_free_buff; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (obj->buffer.length > size) { 19962306a36Sopenharmony_ci dev_err(&adev->dev, "Given buffer is too small\n"); 20062306a36Sopenharmony_ci ret = -EINVAL; 20162306a36Sopenharmony_ci goto out_free_buff; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci memcpy(data, obj->buffer.pointer, obj->buffer.length); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciout_free_buff: 20762306a36Sopenharmony_ci kfree(buffer.pointer); 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic u32 ipu_bridge_parse_rotation(struct acpi_device *adev, 21262306a36Sopenharmony_ci struct ipu_sensor_ssdb *ssdb) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci switch (ssdb->degree) { 21562306a36Sopenharmony_ci case IPU_SENSOR_ROTATION_NORMAL: 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci case IPU_SENSOR_ROTATION_INVERTED: 21862306a36Sopenharmony_ci return 180; 21962306a36Sopenharmony_ci default: 22062306a36Sopenharmony_ci dev_warn(&adev->dev, 22162306a36Sopenharmony_ci "Unknown rotation %d. Assume 0 degree rotation\n", 22262306a36Sopenharmony_ci ssdb->degree); 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci enum v4l2_fwnode_orientation orientation; 23062306a36Sopenharmony_ci struct acpi_pld_info *pld; 23162306a36Sopenharmony_ci acpi_status status; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci status = acpi_get_physical_device_location(adev->handle, &pld); 23462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 23562306a36Sopenharmony_ci dev_warn(&adev->dev, "_PLD call failed, using default orientation\n"); 23662306a36Sopenharmony_ci return V4L2_FWNODE_ORIENTATION_EXTERNAL; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci switch (pld->panel) { 24062306a36Sopenharmony_ci case ACPI_PLD_PANEL_FRONT: 24162306a36Sopenharmony_ci orientation = V4L2_FWNODE_ORIENTATION_FRONT; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case ACPI_PLD_PANEL_BACK: 24462306a36Sopenharmony_ci orientation = V4L2_FWNODE_ORIENTATION_BACK; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case ACPI_PLD_PANEL_TOP: 24762306a36Sopenharmony_ci case ACPI_PLD_PANEL_LEFT: 24862306a36Sopenharmony_ci case ACPI_PLD_PANEL_RIGHT: 24962306a36Sopenharmony_ci case ACPI_PLD_PANEL_UNKNOWN: 25062306a36Sopenharmony_ci orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci default: 25362306a36Sopenharmony_ci dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel); 25462306a36Sopenharmony_ci orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ACPI_FREE(pld); 25962306a36Sopenharmony_ci return orientation; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ciint ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct ipu_sensor_ssdb ssdb = {}; 26562306a36Sopenharmony_ci int ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = ipu_bridge_read_acpi_buffer(adev, "SSDB", &ssdb, sizeof(ssdb)); 26862306a36Sopenharmony_ci if (ret) 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) { 27262306a36Sopenharmony_ci dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype); 27362306a36Sopenharmony_ci ssdb.vcmtype = 0; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (ssdb.lanes > IPU_MAX_LANES) { 27762306a36Sopenharmony_ci dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n"); 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci sensor->link = ssdb.link; 28262306a36Sopenharmony_ci sensor->lanes = ssdb.lanes; 28362306a36Sopenharmony_ci sensor->mclkspeed = ssdb.mclkspeed; 28462306a36Sopenharmony_ci sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); 28562306a36Sopenharmony_ci sensor->orientation = ipu_bridge_parse_orientation(adev); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (ssdb.vcmtype) 28862306a36Sopenharmony_ci sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1]; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, INTEL_IPU_BRIDGE); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void ipu_bridge_create_fwnode_properties( 29562306a36Sopenharmony_ci struct ipu_sensor *sensor, 29662306a36Sopenharmony_ci struct ipu_bridge *bridge, 29762306a36Sopenharmony_ci const struct ipu_sensor_config *cfg) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct ipu_property_names *names = &sensor->prop_names; 30062306a36Sopenharmony_ci struct software_node *nodes = sensor->swnodes; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci sensor->prop_names = prop_names; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (sensor->csi_dev) { 30562306a36Sopenharmony_ci sensor->local_ref[0] = 30662306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_SENSOR_ENDPOINT]); 30762306a36Sopenharmony_ci sensor->remote_ref[0] = 30862306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_IPU_ENDPOINT]); 30962306a36Sopenharmony_ci sensor->ivsc_sensor_ref[0] = 31062306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]); 31162306a36Sopenharmony_ci sensor->ivsc_ipu_ref[0] = 31262306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci sensor->ivsc_sensor_ep_properties[0] = 31562306a36Sopenharmony_ci PROPERTY_ENTRY_U32(names->bus_type, 31662306a36Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); 31762306a36Sopenharmony_ci sensor->ivsc_sensor_ep_properties[1] = 31862306a36Sopenharmony_ci PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, 31962306a36Sopenharmony_ci bridge->data_lanes, 32062306a36Sopenharmony_ci sensor->lanes); 32162306a36Sopenharmony_ci sensor->ivsc_sensor_ep_properties[2] = 32262306a36Sopenharmony_ci PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, 32362306a36Sopenharmony_ci sensor->ivsc_sensor_ref); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci sensor->ivsc_ipu_ep_properties[0] = 32662306a36Sopenharmony_ci PROPERTY_ENTRY_U32(names->bus_type, 32762306a36Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); 32862306a36Sopenharmony_ci sensor->ivsc_ipu_ep_properties[1] = 32962306a36Sopenharmony_ci PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, 33062306a36Sopenharmony_ci bridge->data_lanes, 33162306a36Sopenharmony_ci sensor->lanes); 33262306a36Sopenharmony_ci sensor->ivsc_ipu_ep_properties[2] = 33362306a36Sopenharmony_ci PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, 33462306a36Sopenharmony_ci sensor->ivsc_ipu_ref); 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci sensor->local_ref[0] = 33762306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]); 33862306a36Sopenharmony_ci sensor->remote_ref[0] = 33962306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci sensor->dev_properties[0] = PROPERTY_ENTRY_U32( 34362306a36Sopenharmony_ci sensor->prop_names.clock_frequency, 34462306a36Sopenharmony_ci sensor->mclkspeed); 34562306a36Sopenharmony_ci sensor->dev_properties[1] = PROPERTY_ENTRY_U32( 34662306a36Sopenharmony_ci sensor->prop_names.rotation, 34762306a36Sopenharmony_ci sensor->rotation); 34862306a36Sopenharmony_ci sensor->dev_properties[2] = PROPERTY_ENTRY_U32( 34962306a36Sopenharmony_ci sensor->prop_names.orientation, 35062306a36Sopenharmony_ci sensor->orientation); 35162306a36Sopenharmony_ci if (sensor->vcm_type) { 35262306a36Sopenharmony_ci sensor->vcm_ref[0] = 35362306a36Sopenharmony_ci SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); 35462306a36Sopenharmony_ci sensor->dev_properties[3] = 35562306a36Sopenharmony_ci PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci sensor->ep_properties[0] = PROPERTY_ENTRY_U32( 35962306a36Sopenharmony_ci sensor->prop_names.bus_type, 36062306a36Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); 36162306a36Sopenharmony_ci sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( 36262306a36Sopenharmony_ci sensor->prop_names.data_lanes, 36362306a36Sopenharmony_ci bridge->data_lanes, sensor->lanes); 36462306a36Sopenharmony_ci sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( 36562306a36Sopenharmony_ci sensor->prop_names.remote_endpoint, 36662306a36Sopenharmony_ci sensor->local_ref); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (cfg->nr_link_freqs > 0) 36962306a36Sopenharmony_ci sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( 37062306a36Sopenharmony_ci sensor->prop_names.link_frequencies, 37162306a36Sopenharmony_ci cfg->link_freqs, 37262306a36Sopenharmony_ci cfg->nr_link_freqs); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( 37562306a36Sopenharmony_ci sensor->prop_names.data_lanes, 37662306a36Sopenharmony_ci bridge->data_lanes, sensor->lanes); 37762306a36Sopenharmony_ci sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( 37862306a36Sopenharmony_ci sensor->prop_names.remote_endpoint, 37962306a36Sopenharmony_ci sensor->remote_ref); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci snprintf(sensor->node_names.remote_port, 38562306a36Sopenharmony_ci sizeof(sensor->node_names.remote_port), 38662306a36Sopenharmony_ci SWNODE_GRAPH_PORT_NAME_FMT, sensor->link); 38762306a36Sopenharmony_ci snprintf(sensor->node_names.port, 38862306a36Sopenharmony_ci sizeof(sensor->node_names.port), 38962306a36Sopenharmony_ci SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ 39062306a36Sopenharmony_ci snprintf(sensor->node_names.endpoint, 39162306a36Sopenharmony_ci sizeof(sensor->node_names.endpoint), 39262306a36Sopenharmony_ci SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ 39362306a36Sopenharmony_ci if (sensor->vcm_type) { 39462306a36Sopenharmony_ci /* append link to distinguish nodes with same model VCM */ 39562306a36Sopenharmony_ci snprintf(sensor->node_names.vcm, sizeof(sensor->node_names.vcm), 39662306a36Sopenharmony_ci "%s-%u", sensor->vcm_type, sensor->link); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (sensor->csi_dev) { 40062306a36Sopenharmony_ci snprintf(sensor->node_names.ivsc_sensor_port, 40162306a36Sopenharmony_ci sizeof(sensor->node_names.ivsc_sensor_port), 40262306a36Sopenharmony_ci SWNODE_GRAPH_PORT_NAME_FMT, 0); 40362306a36Sopenharmony_ci snprintf(sensor->node_names.ivsc_ipu_port, 40462306a36Sopenharmony_ci sizeof(sensor->node_names.ivsc_ipu_port), 40562306a36Sopenharmony_ci SWNODE_GRAPH_PORT_NAME_FMT, 1); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct software_node *nodes = sensor->swnodes; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID]; 41462306a36Sopenharmony_ci sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; 41562306a36Sopenharmony_ci sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; 41662306a36Sopenharmony_ci sensor->group[SWNODE_IPU_PORT] = &nodes[SWNODE_IPU_PORT]; 41762306a36Sopenharmony_ci sensor->group[SWNODE_IPU_ENDPOINT] = &nodes[SWNODE_IPU_ENDPOINT]; 41862306a36Sopenharmony_ci if (sensor->vcm_type) 41962306a36Sopenharmony_ci sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (sensor->csi_dev) { 42262306a36Sopenharmony_ci sensor->group[SWNODE_IVSC_HID] = 42362306a36Sopenharmony_ci &nodes[SWNODE_IVSC_HID]; 42462306a36Sopenharmony_ci sensor->group[SWNODE_IVSC_SENSOR_PORT] = 42562306a36Sopenharmony_ci &nodes[SWNODE_IVSC_SENSOR_PORT]; 42662306a36Sopenharmony_ci sensor->group[SWNODE_IVSC_SENSOR_ENDPOINT] = 42762306a36Sopenharmony_ci &nodes[SWNODE_IVSC_SENSOR_ENDPOINT]; 42862306a36Sopenharmony_ci sensor->group[SWNODE_IVSC_IPU_PORT] = 42962306a36Sopenharmony_ci &nodes[SWNODE_IVSC_IPU_PORT]; 43062306a36Sopenharmony_ci sensor->group[SWNODE_IVSC_IPU_ENDPOINT] = 43162306a36Sopenharmony_ci &nodes[SWNODE_IVSC_IPU_ENDPOINT]; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (sensor->vcm_type) 43462306a36Sopenharmony_ci sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; 43562306a36Sopenharmony_ci } else { 43662306a36Sopenharmony_ci if (sensor->vcm_type) 43762306a36Sopenharmony_ci sensor->group[SWNODE_IVSC_HID] = &nodes[SWNODE_VCM]; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge, 44262306a36Sopenharmony_ci struct ipu_sensor *sensor) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct ipu_node_names *names = &sensor->node_names; 44562306a36Sopenharmony_ci struct software_node *nodes = sensor->swnodes; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci ipu_bridge_init_swnode_names(sensor); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name, 45062306a36Sopenharmony_ci sensor->dev_properties); 45162306a36Sopenharmony_ci nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, 45262306a36Sopenharmony_ci &nodes[SWNODE_SENSOR_HID]); 45362306a36Sopenharmony_ci nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( 45462306a36Sopenharmony_ci sensor->node_names.endpoint, 45562306a36Sopenharmony_ci &nodes[SWNODE_SENSOR_PORT], 45662306a36Sopenharmony_ci sensor->ep_properties); 45762306a36Sopenharmony_ci nodes[SWNODE_IPU_PORT] = NODE_PORT(sensor->node_names.remote_port, 45862306a36Sopenharmony_ci &bridge->ipu_hid_node); 45962306a36Sopenharmony_ci nodes[SWNODE_IPU_ENDPOINT] = NODE_ENDPOINT( 46062306a36Sopenharmony_ci sensor->node_names.endpoint, 46162306a36Sopenharmony_ci &nodes[SWNODE_IPU_PORT], 46262306a36Sopenharmony_ci sensor->ipu_properties); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (sensor->csi_dev) { 46562306a36Sopenharmony_ci snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u", 46662306a36Sopenharmony_ci acpi_device_hid(sensor->ivsc_adev), sensor->link); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name, 46962306a36Sopenharmony_ci sensor->ivsc_properties); 47062306a36Sopenharmony_ci nodes[SWNODE_IVSC_SENSOR_PORT] = 47162306a36Sopenharmony_ci NODE_PORT(names->ivsc_sensor_port, 47262306a36Sopenharmony_ci &nodes[SWNODE_IVSC_HID]); 47362306a36Sopenharmony_ci nodes[SWNODE_IVSC_SENSOR_ENDPOINT] = 47462306a36Sopenharmony_ci NODE_ENDPOINT(names->endpoint, 47562306a36Sopenharmony_ci &nodes[SWNODE_IVSC_SENSOR_PORT], 47662306a36Sopenharmony_ci sensor->ivsc_sensor_ep_properties); 47762306a36Sopenharmony_ci nodes[SWNODE_IVSC_IPU_PORT] = 47862306a36Sopenharmony_ci NODE_PORT(names->ivsc_ipu_port, 47962306a36Sopenharmony_ci &nodes[SWNODE_IVSC_HID]); 48062306a36Sopenharmony_ci nodes[SWNODE_IVSC_IPU_ENDPOINT] = 48162306a36Sopenharmony_ci NODE_ENDPOINT(names->endpoint, 48262306a36Sopenharmony_ci &nodes[SWNODE_IVSC_IPU_PORT], 48362306a36Sopenharmony_ci sensor->ivsc_ipu_ep_properties); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci nodes[SWNODE_VCM] = NODE_VCM(sensor->node_names.vcm); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ipu_bridge_init_swnode_group(sensor); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* 49262306a36Sopenharmony_ci * The actual instantiation must be done from a workqueue to avoid 49362306a36Sopenharmony_ci * a deadlock on taking list_lock from v4l2-async twice. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cistruct ipu_bridge_instantiate_vcm_work_data { 49662306a36Sopenharmony_ci struct work_struct work; 49762306a36Sopenharmony_ci struct device *sensor; 49862306a36Sopenharmony_ci char name[16]; 49962306a36Sopenharmony_ci struct i2c_board_info board_info; 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void ipu_bridge_instantiate_vcm_work(struct work_struct *work) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct ipu_bridge_instantiate_vcm_work_data *data = 50562306a36Sopenharmony_ci container_of(work, struct ipu_bridge_instantiate_vcm_work_data, 50662306a36Sopenharmony_ci work); 50762306a36Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(data->sensor); 50862306a36Sopenharmony_ci struct i2c_client *vcm_client; 50962306a36Sopenharmony_ci bool put_fwnode = true; 51062306a36Sopenharmony_ci int ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * The client may get probed before the device_link gets added below 51462306a36Sopenharmony_ci * make sure the sensor is powered-up during probe. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci ret = pm_runtime_get_sync(data->sensor); 51762306a36Sopenharmony_ci if (ret < 0) { 51862306a36Sopenharmony_ci dev_err(data->sensor, "Error %d runtime-resuming sensor, cannot instantiate VCM\n", 51962306a36Sopenharmony_ci ret); 52062306a36Sopenharmony_ci goto out_pm_put; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* 52462306a36Sopenharmony_ci * Note the client is created only once and then kept around 52562306a36Sopenharmony_ci * even after a rmmod, just like the software-nodes. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci vcm_client = i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(adev), 52862306a36Sopenharmony_ci 1, &data->board_info); 52962306a36Sopenharmony_ci if (IS_ERR(vcm_client)) { 53062306a36Sopenharmony_ci dev_err(data->sensor, "Error instantiating VCM client: %ld\n", 53162306a36Sopenharmony_ci PTR_ERR(vcm_client)); 53262306a36Sopenharmony_ci goto out_pm_put; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci device_link_add(&vcm_client->dev, data->sensor, DL_FLAG_PM_RUNTIME); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci dev_info(data->sensor, "Instantiated %s VCM\n", data->board_info.type); 53862306a36Sopenharmony_ci put_fwnode = false; /* Ownership has passed to the i2c-client */ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciout_pm_put: 54162306a36Sopenharmony_ci pm_runtime_put(data->sensor); 54262306a36Sopenharmony_ci put_device(data->sensor); 54362306a36Sopenharmony_ci if (put_fwnode) 54462306a36Sopenharmony_ci fwnode_handle_put(data->board_info.fwnode); 54562306a36Sopenharmony_ci kfree(data); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciint ipu_bridge_instantiate_vcm(struct device *sensor) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct ipu_bridge_instantiate_vcm_work_data *data; 55162306a36Sopenharmony_ci struct fwnode_handle *vcm_fwnode; 55262306a36Sopenharmony_ci struct i2c_client *vcm_client; 55362306a36Sopenharmony_ci struct acpi_device *adev; 55462306a36Sopenharmony_ci char *sep; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci adev = ACPI_COMPANION(sensor); 55762306a36Sopenharmony_ci if (!adev) 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci vcm_fwnode = fwnode_find_reference(dev_fwnode(sensor), "lens-focus", 0); 56162306a36Sopenharmony_ci if (IS_ERR(vcm_fwnode)) 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* When reloading modules the client will already exist */ 56562306a36Sopenharmony_ci vcm_client = i2c_find_device_by_fwnode(vcm_fwnode); 56662306a36Sopenharmony_ci if (vcm_client) { 56762306a36Sopenharmony_ci fwnode_handle_put(vcm_fwnode); 56862306a36Sopenharmony_ci put_device(&vcm_client->dev); 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 57362306a36Sopenharmony_ci if (!data) { 57462306a36Sopenharmony_ci fwnode_handle_put(vcm_fwnode); 57562306a36Sopenharmony_ci return -ENOMEM; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci INIT_WORK(&data->work, ipu_bridge_instantiate_vcm_work); 57962306a36Sopenharmony_ci data->sensor = get_device(sensor); 58062306a36Sopenharmony_ci snprintf(data->name, sizeof(data->name), "%s-VCM", 58162306a36Sopenharmony_ci acpi_dev_name(adev)); 58262306a36Sopenharmony_ci data->board_info.dev_name = data->name; 58362306a36Sopenharmony_ci data->board_info.fwnode = vcm_fwnode; 58462306a36Sopenharmony_ci snprintf(data->board_info.type, sizeof(data->board_info.type), 58562306a36Sopenharmony_ci "%pfwP", vcm_fwnode); 58662306a36Sopenharmony_ci /* Strip "-<link>" postfix */ 58762306a36Sopenharmony_ci sep = strchrnul(data->board_info.type, '-'); 58862306a36Sopenharmony_ci *sep = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci queue_work(system_long_wq, &data->work); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ipu_bridge_instantiate_vcm, INTEL_IPU_BRIDGE); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int ipu_bridge_instantiate_ivsc(struct ipu_sensor *sensor) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct fwnode_handle *fwnode; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (!sensor->csi_dev) 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_IVSC_HID]); 60462306a36Sopenharmony_ci if (!fwnode) 60562306a36Sopenharmony_ci return -ENODEV; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci set_secondary_fwnode(sensor->csi_dev, fwnode); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct ipu_sensor *sensor; 61562306a36Sopenharmony_ci unsigned int i; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci for (i = 0; i < bridge->n_sensors; i++) { 61862306a36Sopenharmony_ci sensor = &bridge->sensors[i]; 61962306a36Sopenharmony_ci software_node_unregister_node_group(sensor->group); 62062306a36Sopenharmony_ci acpi_dev_put(sensor->adev); 62162306a36Sopenharmony_ci put_device(sensor->csi_dev); 62262306a36Sopenharmony_ci acpi_dev_put(sensor->ivsc_adev); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, 62762306a36Sopenharmony_ci struct ipu_bridge *bridge) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct fwnode_handle *fwnode, *primary; 63062306a36Sopenharmony_ci struct ipu_sensor *sensor; 63162306a36Sopenharmony_ci struct acpi_device *adev; 63262306a36Sopenharmony_ci int ret; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { 63562306a36Sopenharmony_ci if (!adev->status.enabled) 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (bridge->n_sensors >= IPU_MAX_PORTS) { 63962306a36Sopenharmony_ci acpi_dev_put(adev); 64062306a36Sopenharmony_ci dev_err(bridge->dev, "Exceeded available IPU ports\n"); 64162306a36Sopenharmony_ci return -EINVAL; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci sensor = &bridge->sensors[bridge->n_sensors]; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ret = bridge->parse_sensor_fwnode(adev, sensor); 64762306a36Sopenharmony_ci if (ret) 64862306a36Sopenharmony_ci goto err_put_adev; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci snprintf(sensor->name, sizeof(sensor->name), "%s-%u", 65162306a36Sopenharmony_ci cfg->hid, sensor->link); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ret = ipu_bridge_check_ivsc_dev(sensor, adev); 65462306a36Sopenharmony_ci if (ret) 65562306a36Sopenharmony_ci goto err_put_adev; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ipu_bridge_create_fwnode_properties(sensor, bridge, cfg); 65862306a36Sopenharmony_ci ipu_bridge_create_connection_swnodes(bridge, sensor); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci ret = software_node_register_node_group(sensor->group); 66162306a36Sopenharmony_ci if (ret) 66262306a36Sopenharmony_ci goto err_put_ivsc; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci fwnode = software_node_fwnode(&sensor->swnodes[ 66562306a36Sopenharmony_ci SWNODE_SENSOR_HID]); 66662306a36Sopenharmony_ci if (!fwnode) { 66762306a36Sopenharmony_ci ret = -ENODEV; 66862306a36Sopenharmony_ci goto err_free_swnodes; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci sensor->adev = acpi_dev_get(adev); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci primary = acpi_fwnode_handle(adev); 67462306a36Sopenharmony_ci primary->secondary = fwnode; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = ipu_bridge_instantiate_ivsc(sensor); 67762306a36Sopenharmony_ci if (ret) 67862306a36Sopenharmony_ci goto err_free_swnodes; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci dev_info(bridge->dev, "Found supported sensor %s\n", 68162306a36Sopenharmony_ci acpi_dev_name(adev)); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci bridge->n_sensors++; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cierr_free_swnodes: 68962306a36Sopenharmony_ci software_node_unregister_node_group(sensor->group); 69062306a36Sopenharmony_cierr_put_ivsc: 69162306a36Sopenharmony_ci put_device(sensor->csi_dev); 69262306a36Sopenharmony_ci acpi_dev_put(sensor->ivsc_adev); 69362306a36Sopenharmony_cierr_put_adev: 69462306a36Sopenharmony_ci acpi_dev_put(adev); 69562306a36Sopenharmony_ci return ret; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci unsigned int i; 70162306a36Sopenharmony_ci int ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { 70462306a36Sopenharmony_ci const struct ipu_sensor_config *cfg = 70562306a36Sopenharmony_ci &ipu_supported_sensors[i]; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ret = ipu_bridge_connect_sensor(cfg, bridge); 70862306a36Sopenharmony_ci if (ret) 70962306a36Sopenharmony_ci goto err_unregister_sensors; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cierr_unregister_sensors: 71562306a36Sopenharmony_ci ipu_bridge_unregister_sensors(bridge); 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int ipu_bridge_ivsc_is_ready(void) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct acpi_device *sensor_adev, *adev; 72262306a36Sopenharmony_ci struct device *csi_dev; 72362306a36Sopenharmony_ci bool ready = true; 72462306a36Sopenharmony_ci unsigned int i; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { 72762306a36Sopenharmony_ci const struct ipu_sensor_config *cfg = 72862306a36Sopenharmony_ci &ipu_supported_sensors[i]; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) { 73162306a36Sopenharmony_ci if (!sensor_adev->status.enabled) 73262306a36Sopenharmony_ci continue; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); 73562306a36Sopenharmony_ci if (!adev) 73662306a36Sopenharmony_ci continue; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); 73962306a36Sopenharmony_ci if (!csi_dev) 74062306a36Sopenharmony_ci ready = false; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci put_device(csi_dev); 74362306a36Sopenharmony_ci acpi_dev_put(adev); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return ready; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ciint ipu_bridge_init(struct device *dev, 75162306a36Sopenharmony_ci ipu_parse_sensor_fwnode_t parse_sensor_fwnode) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct fwnode_handle *fwnode; 75462306a36Sopenharmony_ci struct ipu_bridge *bridge; 75562306a36Sopenharmony_ci unsigned int i; 75662306a36Sopenharmony_ci int ret; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (!ipu_bridge_ivsc_is_ready()) 75962306a36Sopenharmony_ci return -EPROBE_DEFER; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); 76262306a36Sopenharmony_ci if (!bridge) 76362306a36Sopenharmony_ci return -ENOMEM; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci strscpy(bridge->ipu_node_name, IPU_HID, 76662306a36Sopenharmony_ci sizeof(bridge->ipu_node_name)); 76762306a36Sopenharmony_ci bridge->ipu_hid_node.name = bridge->ipu_node_name; 76862306a36Sopenharmony_ci bridge->dev = dev; 76962306a36Sopenharmony_ci bridge->parse_sensor_fwnode = parse_sensor_fwnode; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = software_node_register(&bridge->ipu_hid_node); 77262306a36Sopenharmony_ci if (ret < 0) { 77362306a36Sopenharmony_ci dev_err(dev, "Failed to register the IPU HID node\n"); 77462306a36Sopenharmony_ci goto err_free_bridge; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * Map the lane arrangement, which is fixed for the IPU3 (meaning we 77962306a36Sopenharmony_ci * only need one, rather than one per sensor). We include it as a 78062306a36Sopenharmony_ci * member of the struct ipu_bridge rather than a global variable so 78162306a36Sopenharmony_ci * that it survives if the module is unloaded along with the rest of 78262306a36Sopenharmony_ci * the struct. 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_ci for (i = 0; i < IPU_MAX_LANES; i++) 78562306a36Sopenharmony_ci bridge->data_lanes[i] = i + 1; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci ret = ipu_bridge_connect_sensors(bridge); 78862306a36Sopenharmony_ci if (ret || bridge->n_sensors == 0) 78962306a36Sopenharmony_ci goto err_unregister_ipu; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci dev_info(dev, "Connected %d cameras\n", bridge->n_sensors); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci fwnode = software_node_fwnode(&bridge->ipu_hid_node); 79462306a36Sopenharmony_ci if (!fwnode) { 79562306a36Sopenharmony_ci dev_err(dev, "Error getting fwnode from ipu software_node\n"); 79662306a36Sopenharmony_ci ret = -ENODEV; 79762306a36Sopenharmony_ci goto err_unregister_sensors; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci set_secondary_fwnode(dev, fwnode); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cierr_unregister_sensors: 80562306a36Sopenharmony_ci ipu_bridge_unregister_sensors(bridge); 80662306a36Sopenharmony_cierr_unregister_ipu: 80762306a36Sopenharmony_ci software_node_unregister(&bridge->ipu_hid_node); 80862306a36Sopenharmony_cierr_free_bridge: 80962306a36Sopenharmony_ci kfree(bridge); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci return ret; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ipu_bridge_init, INTEL_IPU_BRIDGE); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 81662306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel IPU Sensors Bridge driver"); 817