Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: ipu3-cio2: Add support for instantiating i2c-clients for VCMs

Some sensors come with a variable-focus lens where the lens focus is
controller by a VCM (Voice Coil Motor). If there is a VCM for the
lens-focus, and if so which one, is described on the vcm_type field
of the ACPI SSDB table.

These VCMs are a second I2C device listed as an extra I2cSerialBusV2
resource in the same ACPI device as the sensor. The i2c-core-acpi.c
code only instantiates an i2c-client for the first I2cSerialBusV2
resource.

Add support for instantiating an i2c-client for the VCM with
the type of the i2c-client set based on the SSDB vcm_type field.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>

authored by

Hans de Goede and committed by
Mauro Carvalho Chehab
68b9bcc8 fc2c2045

+69 -2
+55
drivers/media/pci/intel/ipu3/cio2-bridge.c
··· 3 3 4 4 #include <linux/acpi.h> 5 5 #include <linux/device.h> 6 + #include <linux/i2c.h> 6 7 #include <linux/pci.h> 7 8 #include <linux/property.h> 8 9 #include <media/v4l2-fwnode.h> ··· 37 36 .data_lanes = "data-lanes", 38 37 .remote_endpoint = "remote-endpoint", 39 38 .link_frequencies = "link-frequencies", 39 + }; 40 + 41 + static const char * const cio2_vcm_types[] = { 42 + "ad5823", 43 + "dw9714", 44 + "ad5816", 45 + "dw9719", 46 + "dw9718", 47 + "dw9806b", 48 + "wv517s", 49 + "lc898122xa", 50 + "lc898212axb", 40 51 }; 41 52 42 53 static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, ··· 147 134 sensor->dev_properties[2] = PROPERTY_ENTRY_U32( 148 135 sensor->prop_names.orientation, 149 136 orientation); 137 + if (sensor->ssdb.vcmtype) { 138 + sensor->vcm_ref[0] = 139 + SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); 140 + sensor->dev_properties[3] = 141 + PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); 142 + } 150 143 151 144 sensor->ep_properties[0] = PROPERTY_ENTRY_U32( 152 145 sensor->prop_names.bus_type, ··· 214 195 sensor->node_names.endpoint, 215 196 &nodes[SWNODE_CIO2_PORT], 216 197 sensor->cio2_properties); 198 + if (sensor->ssdb.vcmtype) 199 + nodes[SWNODE_VCM] = 200 + NODE_VCM(cio2_vcm_types[sensor->ssdb.vcmtype - 1]); 201 + } 202 + 203 + static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor) 204 + { 205 + struct i2c_board_info board_info = { }; 206 + char name[16]; 207 + 208 + if (!sensor->ssdb.vcmtype) 209 + return; 210 + 211 + snprintf(name, sizeof(name), "%s-VCM", acpi_dev_name(sensor->adev)); 212 + board_info.dev_name = name; 213 + strscpy(board_info.type, cio2_vcm_types[sensor->ssdb.vcmtype - 1], 214 + ARRAY_SIZE(board_info.type)); 215 + board_info.swnode = &sensor->swnodes[SWNODE_VCM]; 216 + 217 + sensor->vcm_i2c_client = 218 + i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(sensor->adev), 219 + 1, &board_info); 220 + if (IS_ERR(sensor->vcm_i2c_client)) { 221 + dev_warn(&sensor->adev->dev, "Error instantiation VCM i2c-client: %ld\n", 222 + PTR_ERR(sensor->vcm_i2c_client)); 223 + sensor->vcm_i2c_client = NULL; 224 + } 217 225 } 218 226 219 227 static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) ··· 253 207 software_node_unregister_nodes(sensor->swnodes); 254 208 ACPI_FREE(sensor->pld); 255 209 acpi_dev_put(sensor->adev); 210 + i2c_unregister_device(sensor->vcm_i2c_client); 256 211 } 257 212 } 258 213 ··· 286 239 if (ret) 287 240 goto err_put_adev; 288 241 242 + if (sensor->ssdb.vcmtype > ARRAY_SIZE(cio2_vcm_types)) { 243 + dev_warn(&adev->dev, "Unknown VCM type %d\n", 244 + sensor->ssdb.vcmtype); 245 + sensor->ssdb.vcmtype = 0; 246 + } 247 + 289 248 status = acpi_get_physical_device_location(adev->handle, &sensor->pld); 290 249 if (ACPI_FAILURE(status)) { 291 250 ret = -ENODEV; ··· 321 268 322 269 sensor->adev = acpi_dev_get(adev); 323 270 adev->fwnode.secondary = fwnode; 271 + 272 + cio2_bridge_instantiate_vcm_i2c_client(sensor); 324 273 325 274 dev_info(&cio2->dev, "Found supported sensor %s\n", 326 275 acpi_dev_name(adev));
+14 -2
drivers/media/pci/intel/ipu3/cio2-bridge.h
··· 8 8 9 9 #include "ipu3-cio2.h" 10 10 11 + struct i2c_client; 12 + 11 13 #define CIO2_HID "INT343E" 12 14 #define CIO2_MAX_LANES 4 13 15 #define MAX_NUM_LINK_FREQS 3 ··· 44 42 .properties = _PROPS, \ 45 43 } 46 44 45 + #define NODE_VCM(_TYPE) \ 46 + (const struct software_node) { \ 47 + .name = _TYPE, \ 48 + } 49 + 47 50 enum cio2_sensor_swnodes { 48 51 SWNODE_SENSOR_HID, 49 52 SWNODE_SENSOR_PORT, 50 53 SWNODE_SENSOR_ENDPOINT, 51 54 SWNODE_CIO2_PORT, 52 55 SWNODE_CIO2_ENDPOINT, 56 + /* Must be last because it is optional / maybe empty */ 57 + SWNODE_VCM, 53 58 SWNODE_COUNT 54 59 }; 55 60 ··· 115 106 struct cio2_sensor { 116 107 char name[ACPI_ID_LEN]; 117 108 struct acpi_device *adev; 109 + struct i2c_client *vcm_i2c_client; 118 110 119 - struct software_node swnodes[6]; 111 + /* SWNODE_COUNT + 1 for terminating empty node */ 112 + struct software_node swnodes[SWNODE_COUNT + 1]; 120 113 struct cio2_node_names node_names; 121 114 122 115 struct cio2_sensor_ssdb ssdb; ··· 126 115 127 116 struct cio2_property_names prop_names; 128 117 struct property_entry ep_properties[5]; 129 - struct property_entry dev_properties[4]; 118 + struct property_entry dev_properties[5]; 130 119 struct property_entry cio2_properties[3]; 131 120 struct software_node_ref_args local_ref[1]; 132 121 struct software_node_ref_args remote_ref[1]; 122 + struct software_node_ref_args vcm_ref[1]; 133 123 }; 134 124 135 125 struct cio2_bridge {