Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Daniel Scally | 1408 | 68.28% | 4 | 30.77% |
Hans de Goede | 358 | 17.36% | 2 | 15.38% |
Fabian Wüthrich | 204 | 9.89% | 1 | 7.69% |
Yong Zhi | 61 | 2.96% | 2 | 15.38% |
Andy Shevchenko | 24 | 1.16% | 3 | 23.08% |
Dan Carpenter | 7 | 0.34% | 1 | 7.69% |
Total | 2062 | 13 |
// SPDX-License-Identifier: GPL-2.0 /* Author: Dan Scally <djrscally@gmail.com> */ #include <linux/acpi.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/pci.h> #include <linux/property.h> #include <media/v4l2-fwnode.h> #include "cio2-bridge.h" /* * Extend this array with ACPI Hardware IDs of devices known to be working * plus the number of link-frequencies expected by their drivers, along with * the frequency values in hertz. This is somewhat opportunistic way of adding * support for this for now in the hopes of a better source for the information * (possibly some encoded value in the SSDB buffer that we're unaware of) * becoming apparent in the future. * * Do not add an entry for a sensor that is not actually supported. */ static const struct cio2_sensor_config cio2_supported_sensors[] = { /* Omnivision OV5693 */ CIO2_SENSOR_CONFIG("INT33BE", 1, 419200000), /* Omnivision OV8865 */ CIO2_SENSOR_CONFIG("INT347A", 1, 360000000), /* Omnivision OV7251 */ CIO2_SENSOR_CONFIG("INT347E", 1, 319200000), /* Omnivision OV2680 */ CIO2_SENSOR_CONFIG("OVTI2680", 0), }; static const struct cio2_property_names prop_names = { .clock_frequency = "clock-frequency", .rotation = "rotation", .orientation = "orientation", .bus_type = "bus-type", .data_lanes = "data-lanes", .remote_endpoint = "remote-endpoint", .link_frequencies = "link-frequencies", }; static const char * const cio2_vcm_types[] = { "ad5823", "dw9714", "ad5816", "dw9719", "dw9718", "dw9806b", "wv517s", "lc898122xa", "lc898212axb", }; static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, void *data, u32 size) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; int ret = 0; status = acpi_evaluate_object(adev->handle, id, NULL, &buffer); if (ACPI_FAILURE(status)) return -ENODEV; obj = buffer.pointer; if (!obj) { dev_err(&adev->dev, "Couldn't locate ACPI buffer\n"); return -ENODEV; } if (obj->type != ACPI_TYPE_BUFFER) { dev_err(&adev->dev, "Not an ACPI buffer\n"); ret = -ENODEV; goto out_free_buff; } if (obj->buffer.length > size) { dev_err(&adev->dev, "Given buffer is too small\n"); ret = -EINVAL; goto out_free_buff; } memcpy(data, obj->buffer.pointer, obj->buffer.length); out_free_buff: kfree(buffer.pointer); return ret; } static u32 cio2_bridge_parse_rotation(struct cio2_sensor *sensor) { switch (sensor->ssdb.degree) { case CIO2_SENSOR_ROTATION_NORMAL: return 0; case CIO2_SENSOR_ROTATION_INVERTED: return 180; default: dev_warn(&sensor->adev->dev, "Unknown rotation %d. Assume 0 degree rotation\n", sensor->ssdb.degree); return 0; } } static enum v4l2_fwnode_orientation cio2_bridge_parse_orientation(struct cio2_sensor *sensor) { switch (sensor->pld->panel) { case ACPI_PLD_PANEL_FRONT: return V4L2_FWNODE_ORIENTATION_FRONT; case ACPI_PLD_PANEL_BACK: return V4L2_FWNODE_ORIENTATION_BACK; case ACPI_PLD_PANEL_TOP: case ACPI_PLD_PANEL_LEFT: case ACPI_PLD_PANEL_RIGHT: case ACPI_PLD_PANEL_UNKNOWN: return V4L2_FWNODE_ORIENTATION_EXTERNAL; default: dev_warn(&sensor->adev->dev, "Unknown _PLD panel value %d\n", sensor->pld->panel); return V4L2_FWNODE_ORIENTATION_EXTERNAL; } } static void cio2_bridge_create_fwnode_properties( struct cio2_sensor *sensor, struct cio2_bridge *bridge, const struct cio2_sensor_config *cfg) { u32 rotation; enum v4l2_fwnode_orientation orientation; rotation = cio2_bridge_parse_rotation(sensor); orientation = cio2_bridge_parse_orientation(sensor); sensor->prop_names = prop_names; sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CIO2_ENDPOINT]); sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]); sensor->dev_properties[0] = PROPERTY_ENTRY_U32( sensor->prop_names.clock_frequency, sensor->ssdb.mclkspeed); sensor->dev_properties[1] = PROPERTY_ENTRY_U32( sensor->prop_names.rotation, rotation); sensor->dev_properties[2] = PROPERTY_ENTRY_U32( sensor->prop_names.orientation, orientation); if (sensor->ssdb.vcmtype) { sensor->vcm_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); sensor->dev_properties[3] = PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); } sensor->ep_properties[0] = PROPERTY_ENTRY_U32( sensor->prop_names.bus_type, V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( sensor->prop_names.data_lanes, bridge->data_lanes, sensor->ssdb.lanes); sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( sensor->prop_names.remote_endpoint, sensor->local_ref); if (cfg->nr_link_freqs > 0) sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( sensor->prop_names.link_frequencies, cfg->link_freqs, cfg->nr_link_freqs); sensor->cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( sensor->prop_names.data_lanes, bridge->data_lanes, sensor->ssdb.lanes); sensor->cio2_properties[1] = PROPERTY_ENTRY_REF_ARRAY( sensor->prop_names.remote_endpoint, sensor->remote_ref); } static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor) { snprintf(sensor->node_names.remote_port, sizeof(sensor->node_names.remote_port), SWNODE_GRAPH_PORT_NAME_FMT, sensor->ssdb.link); snprintf(sensor->node_names.port, sizeof(sensor->node_names.port), SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ snprintf(sensor->node_names.endpoint, sizeof(sensor->node_names.endpoint), SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ } static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge, struct cio2_sensor *sensor) { struct software_node *nodes = sensor->swnodes; cio2_bridge_init_swnode_names(sensor); nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name, sensor->dev_properties); nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, &nodes[SWNODE_SENSOR_HID]); nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( sensor->node_names.endpoint, &nodes[SWNODE_SENSOR_PORT], sensor->ep_properties); nodes[SWNODE_CIO2_PORT] = NODE_PORT(sensor->node_names.remote_port, &bridge->cio2_hid_node); nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT( sensor->node_names.endpoint, &nodes[SWNODE_CIO2_PORT], sensor->cio2_properties); if (sensor->ssdb.vcmtype) nodes[SWNODE_VCM] = NODE_VCM(cio2_vcm_types[sensor->ssdb.vcmtype - 1]); } static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor) { struct i2c_board_info board_info = { }; char name[16]; if (!sensor->ssdb.vcmtype) return; snprintf(name, sizeof(name), "%s-VCM", acpi_dev_name(sensor->adev)); board_info.dev_name = name; strscpy(board_info.type, cio2_vcm_types[sensor->ssdb.vcmtype - 1], ARRAY_SIZE(board_info.type)); board_info.swnode = &sensor->swnodes[SWNODE_VCM]; sensor->vcm_i2c_client = i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(sensor->adev), 1, &board_info); if (IS_ERR(sensor->vcm_i2c_client)) { dev_warn(&sensor->adev->dev, "Error instantiation VCM i2c-client: %ld\n", PTR_ERR(sensor->vcm_i2c_client)); sensor->vcm_i2c_client = NULL; } } static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) { struct cio2_sensor *sensor; unsigned int i; for (i = 0; i < bridge->n_sensors; i++) { sensor = &bridge->sensors[i]; software_node_unregister_nodes(sensor->swnodes); ACPI_FREE(sensor->pld); acpi_dev_put(sensor->adev); i2c_unregister_device(sensor->vcm_i2c_client); } } static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, struct cio2_bridge *bridge, struct pci_dev *cio2) { struct fwnode_handle *fwnode; struct cio2_sensor *sensor; struct acpi_device *adev; acpi_status status; int ret; for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { if (!adev->status.enabled) continue; if (bridge->n_sensors >= CIO2_NUM_PORTS) { acpi_dev_put(adev); dev_err(&cio2->dev, "Exceeded available CIO2 ports\n"); return -EINVAL; } sensor = &bridge->sensors[bridge->n_sensors]; strscpy(sensor->name, cfg->hid, sizeof(sensor->name)); ret = cio2_bridge_read_acpi_buffer(adev, "SSDB", &sensor->ssdb, sizeof(sensor->ssdb)); if (ret) goto err_put_adev; if (sensor->ssdb.vcmtype > ARRAY_SIZE(cio2_vcm_types)) { dev_warn(&adev->dev, "Unknown VCM type %d\n", sensor->ssdb.vcmtype); sensor->ssdb.vcmtype = 0; } status = acpi_get_physical_device_location(adev->handle, &sensor->pld); if (ACPI_FAILURE(status)) { ret = -ENODEV; goto err_put_adev; } if (sensor->ssdb.lanes > CIO2_MAX_LANES) { dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n"); ret = -EINVAL; goto err_free_pld; } cio2_bridge_create_fwnode_properties(sensor, bridge, cfg); cio2_bridge_create_connection_swnodes(bridge, sensor); ret = software_node_register_nodes(sensor->swnodes); if (ret) goto err_free_pld; fwnode = software_node_fwnode(&sensor->swnodes[ SWNODE_SENSOR_HID]); if (!fwnode) { ret = -ENODEV; goto err_free_swnodes; } sensor->adev = acpi_dev_get(adev); adev->fwnode.secondary = fwnode; cio2_bridge_instantiate_vcm_i2c_client(sensor); dev_info(&cio2->dev, "Found supported sensor %s\n", acpi_dev_name(adev)); bridge->n_sensors++; } return 0; err_free_swnodes: software_node_unregister_nodes(sensor->swnodes); err_free_pld: ACPI_FREE(sensor->pld); err_put_adev: acpi_dev_put(adev); return ret; } static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge, struct pci_dev *cio2) { unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(cio2_supported_sensors); i++) { const struct cio2_sensor_config *cfg = &cio2_supported_sensors[i]; ret = cio2_bridge_connect_sensor(cfg, bridge, cio2); if (ret) goto err_unregister_sensors; } return 0; err_unregister_sensors: cio2_bridge_unregister_sensors(bridge); return ret; } /* * The VCM cannot be probed until the PMIC is completely setup. We cannot rely * on -EPROBE_DEFER for this, since the consumer<->supplier relations between * the VCM and regulators/clks are not described in ACPI, instead they are * passed as board-data to the PMIC drivers. Since -PROBE_DEFER does not work * for the clks/regulators the VCM i2c-clients must not be instantiated until * the PMIC is fully setup. * * The sensor/VCM ACPI device has an ACPI _DEP on the PMIC, check this using the * acpi_dev_ready_for_enumeration() helper, like the i2c-core-acpi code does * for the sensors. */ static int cio2_bridge_sensors_are_ready(void) { struct acpi_device *adev; bool ready = true; unsigned int i; for (i = 0; i < ARRAY_SIZE(cio2_supported_sensors); i++) { const struct cio2_sensor_config *cfg = &cio2_supported_sensors[i]; for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { if (!adev->status.enabled) continue; if (!acpi_dev_ready_for_enumeration(adev)) ready = false; } } return ready; } int cio2_bridge_init(struct pci_dev *cio2) { struct device *dev = &cio2->dev; struct fwnode_handle *fwnode; struct cio2_bridge *bridge; unsigned int i; int ret; if (!cio2_bridge_sensors_are_ready()) return -EPROBE_DEFER; bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); if (!bridge) return -ENOMEM; strscpy(bridge->cio2_node_name, CIO2_HID, sizeof(bridge->cio2_node_name)); bridge->cio2_hid_node.name = bridge->cio2_node_name; ret = software_node_register(&bridge->cio2_hid_node); if (ret < 0) { dev_err(dev, "Failed to register the CIO2 HID node\n"); goto err_free_bridge; } /* * Map the lane arrangement, which is fixed for the IPU3 (meaning we * only need one, rather than one per sensor). We include it as a * member of the struct cio2_bridge rather than a global variable so * that it survives if the module is unloaded along with the rest of * the struct. */ for (i = 0; i < CIO2_MAX_LANES; i++) bridge->data_lanes[i] = i + 1; ret = cio2_bridge_connect_sensors(bridge, cio2); if (ret || bridge->n_sensors == 0) goto err_unregister_cio2; dev_info(dev, "Connected %d cameras\n", bridge->n_sensors); fwnode = software_node_fwnode(&bridge->cio2_hid_node); if (!fwnode) { dev_err(dev, "Error getting fwnode from cio2 software_node\n"); ret = -ENODEV; goto err_unregister_sensors; } set_secondary_fwnode(dev, fwnode); return 0; err_unregister_sensors: cio2_bridge_unregister_sensors(bridge); err_unregister_cio2: software_node_unregister(&bridge->cio2_hid_node); err_free_bridge: kfree(bridge); return ret; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1