Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Daniel Scally | 1398 | 98.45% | 1 | 25.00% |
Andy Shevchenko | 22 | 1.55% | 3 | 75.00% |
Total | 1420 | 4 |
// SPDX-License-Identifier: GPL-2.0 /* Author: Dan Scally <djrscally@gmail.com> */ #include <linux/acpi.h> #include <linux/device.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", 0), /* Omnivision OV2680 */ CIO2_SENSOR_CONFIG("OVTI2680", 0), }; static const struct cio2_property_names prop_names = { .clock_frequency = "clock-frequency", .rotation = "rotation", .bus_type = "bus-type", .data_lanes = "data-lanes", .remote_endpoint = "remote-endpoint", .link_frequencies = "link-frequencies", }; 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 void cio2_bridge_create_fwnode_properties( struct cio2_sensor *sensor, struct cio2_bridge *bridge, const struct cio2_sensor_config *cfg) { 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_U8( sensor->prop_names.rotation, sensor->ssdb.degree); 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); } 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_dev_put(sensor->adev); } } 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; 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.lanes > CIO2_MAX_LANES) { dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n"); ret = -EINVAL; goto err_put_adev; } 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_put_adev; 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; 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_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; } 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; 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