@@ -10325,6 +10325,15 @@ S: Supported
T: git git://git.code.sf.net/p/intel-sas/isci
F: drivers/scsi/isci/
+INTEL CIO2 ACPI SENSOR BRIDGE
+M: Dan Scally <djrscally@gmail.com>
+M: Hans de Goede <hansg@kernel.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: drivers/media/common/intel-cio2-bridge.c
+F: include/media/intel-cio2-bridge.h
+
INTEL CPU family model numbers
M: Tony Luck <tony.luck@intel.com>
M: x86@kernel.org
@@ -10,6 +10,10 @@ config CYPRESS_FIRMWARE
tristate
depends on USB
+config INTEL_CIO2_BRIDGE
+ tristate
+ depends on ACPI && I2C
+
config TTPCI_EEPROM
tristate
depends on I2C
@@ -4,6 +4,7 @@ obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/
# Please keep it alphabetically sorted by Kconfig name
# (e. g. LC_ALL=C sort Makefile)
obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o
+obj-$(CONFIG_INTEL_CIO2_BRIDGE) += intel-cio2-bridge.o
obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
obj-$(CONFIG_UVC_COMMON) += uvc.o
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
similarity index 66%
copy from drivers/media/pci/intel/ipu3/cio2-bridge.c
copy to drivers/media/common/intel-cio2-bridge.c
@@ -1,45 +1,40 @@
// SPDX-License-Identifier: GPL-2.0
/* Author: Dan Scally <djrscally@gmail.com> */
+/* Author: Hans de Goede <hansg@kernel.org> */
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/property.h>
+
+#include <media/intel-cio2-bridge.h>
#include <media/v4l2-fwnode.h>
-#include "cio2-bridge.h"
+#define NODE_SENSOR(_HID, _PROPS) \
+ (const struct software_node) { \
+ .name = _HID, \
+ .properties = _PROPS, \
+ }
-/*
- * 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),
- /* Omnivision ov8856 */
- CIO2_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
- /* Omnivision ov2740 */
- CIO2_SENSOR_CONFIG("INT3474", 1, 360000000),
- /* Hynix hi556 */
- CIO2_SENSOR_CONFIG("INT3537", 1, 437000000),
- /* Omnivision ov13b10 */
- CIO2_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
- {}
-};
+#define NODE_PORT(_PORT, _SENSOR_NODE) \
+ (const struct software_node) { \
+ .name = _PORT, \
+ .parent = _SENSOR_NODE, \
+ }
-static const struct cio2_property_names prop_names = {
+#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \
+ (const struct software_node) { \
+ .name = _EP, \
+ .parent = _PORT, \
+ .properties = _PROPS, \
+ }
+
+#define NODE_VCM(_TYPE) \
+ (const struct software_node) { \
+ .name = _TYPE, \
+ }
+
+static const struct intel_cio2_property_names prop_names = {
.clock_frequency = "clock-frequency",
.rotation = "rotation",
.orientation = "orientation",
@@ -49,73 +44,8 @@ static const struct cio2_property_names prop_names = {
.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 acpi_device *adev,
- struct cio2_sensor_ssdb *ssdb)
-{
- switch (ssdb->degree) {
- case CIO2_SENSOR_ROTATION_NORMAL:
- return 0;
- case CIO2_SENSOR_ROTATION_INVERTED:
- return 180;
- default:
- dev_warn(&adev->dev,
- "Unknown rotation %d. Assume 0 degree rotation\n",
- ssdb->degree);
- return 0;
- }
-}
-
-static enum v4l2_fwnode_orientation
-cio2_bridge_get_orientation(struct acpi_device *adev)
+enum v4l2_fwnode_orientation
+intel_cio2_bridge_get_orientation(struct acpi_device *adev)
{
enum v4l2_fwnode_orientation orientation;
struct acpi_pld_info *pld;
@@ -149,44 +79,12 @@ cio2_bridge_get_orientation(struct acpi_device *adev)
ACPI_FREE(pld);
return orientation;
}
-
-static int cio2_bridge_parse_sensor_fwnode(struct acpi_device *adev,
- struct cio2_sensor *sensor,
- const struct cio2_sensor_config *cfg)
-{
- struct cio2_sensor_ssdb ssdb = {};
- int ret;
-
- ret = cio2_bridge_read_acpi_buffer(adev, "SSDB", &ssdb, sizeof(ssdb));
- if (ret)
- return ret;
-
- if (ssdb.vcmtype > ARRAY_SIZE(cio2_vcm_types)) {
- dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype);
- ssdb.vcmtype = 0;
- }
-
- if (ssdb.lanes > CIO2_MAX_LANES) {
- dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
- return -EINVAL;
- }
-
- sensor->link = ssdb.link;
- sensor->lanes = ssdb.lanes;
- sensor->mclkspeed = ssdb.mclkspeed;
- sensor->rotation = cio2_bridge_parse_rotation(adev, &ssdb);
- sensor->orientation = cio2_bridge_get_orientation(adev);
-
- if (ssdb.vcmtype)
- sensor->vcm_type = cio2_vcm_types[ssdb.vcmtype - 1];
-
- return 0;
-}
+EXPORT_SYMBOL(intel_cio2_bridge_get_orientation);
static void cio2_bridge_create_fwnode_properties(
- struct cio2_sensor *sensor,
- struct cio2_bridge *bridge,
- const struct cio2_sensor_config *cfg)
+ struct intel_cio2_sensor *sensor,
+ struct intel_cio2_bridge *bridge,
+ const struct intel_cio2_sensor_config *cfg)
{
sensor->prop_names = prop_names;
@@ -233,7 +131,7 @@ static void cio2_bridge_create_fwnode_properties(
sensor->remote_ref);
}
-static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor)
+static void cio2_bridge_init_swnode_names(struct intel_cio2_sensor *sensor)
{
snprintf(sensor->node_names.remote_port,
sizeof(sensor->node_names.remote_port),
@@ -251,7 +149,7 @@ static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor)
}
}
-static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor)
+static void cio2_bridge_init_swnode_group(struct intel_cio2_sensor *sensor)
{
struct software_node *nodes = sensor->swnodes;
@@ -264,8 +162,9 @@ static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor)
sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM];
}
-static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge,
- struct cio2_sensor *sensor)
+static void cio2_bridge_create_connection_swnodes(
+ struct intel_cio2_bridge *bridge,
+ struct intel_cio2_sensor *sensor)
{
struct software_node *nodes = sensor->swnodes;
@@ -290,7 +189,8 @@ static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge,
cio2_bridge_init_swnode_group(sensor);
}
-static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor)
+static void cio2_bridge_instantiate_vcm_i2c_client(
+ struct intel_cio2_sensor *sensor)
{
struct i2c_board_info board_info = { };
char name[16];
@@ -313,9 +213,9 @@ static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor)
}
}
-static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge)
+static void cio2_bridge_unregister_sensors(struct intel_cio2_bridge *bridge)
{
- struct cio2_sensor *sensor;
+ struct intel_cio2_sensor *sensor;
unsigned int i;
for (i = 0; i < bridge->n_sensors; i++) {
@@ -326,11 +226,12 @@ static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge)
}
}
-static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
- struct cio2_bridge *bridge)
+static int cio2_bridge_connect_sensor(
+ const struct intel_cio2_sensor_config *cfg,
+ struct intel_cio2_bridge *bridge)
{
struct fwnode_handle *fwnode, *primary;
- struct cio2_sensor *sensor;
+ struct intel_cio2_sensor *sensor;
struct acpi_device *adev;
int ret;
@@ -338,7 +239,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
if (!adev->status.enabled)
continue;
- if (bridge->n_sensors >= CIO2_NUM_PORTS) {
+ if (bridge->n_sensors >= INTEL_CIO2_NUM_PORTS) {
acpi_dev_put(adev);
dev_err(bridge->dev, "Exceeded available CIO2 ports\n");
return -EINVAL;
@@ -389,13 +290,13 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
return ret;
}
-static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge)
+static int cio2_bridge_connect_sensors(struct intel_cio2_bridge *bridge)
{
unsigned int i;
int ret;
for (i = 0; bridge->supported_sensors[i].hid; i++) {
- const struct cio2_sensor_config *cfg =
+ const struct intel_cio2_sensor_config *cfg =
&bridge->supported_sensors[i];
ret = cio2_bridge_connect_sensor(cfg, bridge);
@@ -423,14 +324,15 @@ static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge)
* for the sensors.
*/
static int cio2_bridge_sensors_are_ready(
- const struct cio2_sensor_config *supported_sensors)
+ const struct intel_cio2_sensor_config *supported_sensors)
{
struct acpi_device *adev;
bool ready = true;
unsigned int i;
for (i = 0; supported_sensors[i].hid; i++) {
- const struct cio2_sensor_config *cfg = &supported_sensors[i];
+ const struct intel_cio2_sensor_config *cfg =
+ &supported_sensors[i];
for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
if (!adev->status.enabled)
@@ -444,15 +346,15 @@ static int cio2_bridge_sensors_are_ready(
return ready;
}
-static int __cio2_bridge_init(
+int intel_cio2_bridge_init(
struct device *dev,
int (*parse_sensor_fwnode)(struct acpi_device *adev,
- struct cio2_sensor *sensor,
- const struct cio2_sensor_config *cfg),
- const struct cio2_sensor_config *supported_sensors)
+ struct intel_cio2_sensor *sensor,
+ const struct intel_cio2_sensor_config *cfg),
+ const struct intel_cio2_sensor_config *supported_sensors)
{
struct fwnode_handle *fwnode;
- struct cio2_bridge *bridge;
+ struct intel_cio2_bridge *bridge;
unsigned int i;
int ret;
@@ -463,7 +365,7 @@ static int __cio2_bridge_init(
if (!bridge)
return -ENOMEM;
- strscpy(bridge->cio2_node_name, CIO2_HID,
+ strscpy(bridge->cio2_node_name, INTEL_CIO2_HID,
sizeof(bridge->cio2_node_name));
bridge->cio2_hid_node.name = bridge->cio2_node_name;
bridge->dev = dev;
@@ -478,12 +380,12 @@ static int __cio2_bridge_init(
/*
* 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
+ * only need one, rather than one per sensor). We include it as a member
+ * of the struct intel_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++)
+ for (i = 0; i < INTEL_CIO2_MAX_LANES; i++)
bridge->data_lanes[i] = i + 1;
ret = cio2_bridge_connect_sensors(bridge);
@@ -512,9 +414,9 @@ static int __cio2_bridge_init(
return ret;
}
+EXPORT_SYMBOL(intel_cio2_bridge_init);
-int cio2_bridge_init(struct device *dev)
-{
- return __cio2_bridge_init(dev, cio2_bridge_parse_sensor_fwnode,
- cio2_supported_sensors);
-}
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Scally <djrscally@gmail.com>");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_DESCRIPTION("Intel CIO2 ACPI Sensors Bridge");
@@ -8,6 +8,7 @@ config VIDEO_IPU3_CIO2
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
select VIDEOBUF2_DMA_SG
+ select INTEL_CIO2_BRIDGE if CIO2_BRIDGE
help
This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel
@@ -5,9 +5,52 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/property.h>
+
+#include <media/intel-cio2-bridge.h>
#include <media/v4l2-fwnode.h>
-#include "cio2-bridge.h"
+#include "ipu3-cio2.h"
+
+/* Data representation as it is in ACPI SSDB buffer */
+struct cio2_sensor_ssdb {
+ u8 version;
+ u8 sku;
+ u8 guid_csi2[16];
+ u8 devfunction;
+ u8 bus;
+ u32 dphylinkenfuses;
+ u32 clockdiv;
+ u8 link;
+ u8 lanes;
+ u32 csiparams[10];
+ u32 maxlanespeed;
+ u8 sensorcalibfileidx;
+ u8 sensorcalibfileidxInMBZ[3];
+ u8 romtype;
+ u8 vcmtype;
+ u8 platforminfo;
+ u8 platformsubinfo;
+ u8 flash;
+ u8 privacyled;
+ u8 degree;
+ u8 mipilinkdefined;
+ u32 mclkspeed;
+ u8 controllogicid;
+ u8 reserved1[3];
+ u8 mclkport;
+ u8 reserved2[13];
+} __packed;
+
+/* Values are educated guesses as we don't have a spec */
+#define CIO2_SENSOR_ROTATION_NORMAL 0
+#define CIO2_SENSOR_ROTATION_INVERTED 1
+
+#define CIO2_SENSOR_CONFIG(_HID, _NR, ...) \
+ (const struct intel_cio2_sensor_config) { \
+ .hid = _HID, \
+ .nr_link_freqs = _NR, \
+ .link_freqs = { __VA_ARGS__ } \
+ }
/*
* Extend this array with ACPI Hardware IDs of devices known to be working
@@ -19,7 +62,7 @@
*
* Do not add an entry for a sensor that is not actually supported.
*/
-static const struct cio2_sensor_config cio2_supported_sensors[] = {
+static const struct intel_cio2_sensor_config cio2_supported_sensors[] = {
/* Omnivision OV5693 */
CIO2_SENSOR_CONFIG("INT33BE", 1, 419200000),
/* Omnivision OV8865 */
@@ -39,16 +82,6 @@ static const struct cio2_sensor_config cio2_supported_sensors[] = {
{}
};
-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",
@@ -114,45 +147,10 @@ static u32 cio2_bridge_parse_rotation(struct acpi_device *adev,
}
}
-static enum v4l2_fwnode_orientation
-cio2_bridge_get_orientation(struct acpi_device *adev)
-{
- enum v4l2_fwnode_orientation orientation;
- struct acpi_pld_info *pld;
- acpi_status status;
-
- status = acpi_get_physical_device_location(adev->handle, &pld);
- if (ACPI_FAILURE(status)) {
- dev_warn(&adev->dev, "_PLD call failed using unknown orientation\n");
- return V4L2_FWNODE_ORIENTATION_EXTERNAL;
- }
-
- switch (pld->panel) {
- case ACPI_PLD_PANEL_FRONT:
- orientation = V4L2_FWNODE_ORIENTATION_FRONT;
- break;
- case ACPI_PLD_PANEL_BACK:
- orientation = V4L2_FWNODE_ORIENTATION_BACK;
- break;
- case ACPI_PLD_PANEL_TOP:
- case ACPI_PLD_PANEL_LEFT:
- case ACPI_PLD_PANEL_RIGHT:
- case ACPI_PLD_PANEL_UNKNOWN:
- orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
- break;
- default:
- dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel);
- orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
- break;
- }
-
- ACPI_FREE(pld);
- return orientation;
-}
-
-static int cio2_bridge_parse_sensor_fwnode(struct acpi_device *adev,
- struct cio2_sensor *sensor,
- const struct cio2_sensor_config *cfg)
+static int cio2_bridge_parse_sensor_fwnode(
+ struct acpi_device *adev,
+ struct intel_cio2_sensor *sensor,
+ const struct intel_cio2_sensor_config *cfg)
{
struct cio2_sensor_ssdb ssdb = {};
int ret;
@@ -166,7 +164,7 @@ static int cio2_bridge_parse_sensor_fwnode(struct acpi_device *adev,
ssdb.vcmtype = 0;
}
- if (ssdb.lanes > CIO2_MAX_LANES) {
+ if (ssdb.lanes > INTEL_CIO2_MAX_LANES) {
dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
return -EINVAL;
}
@@ -175,7 +173,7 @@ static int cio2_bridge_parse_sensor_fwnode(struct acpi_device *adev,
sensor->lanes = ssdb.lanes;
sensor->mclkspeed = ssdb.mclkspeed;
sensor->rotation = cio2_bridge_parse_rotation(adev, &ssdb);
- sensor->orientation = cio2_bridge_get_orientation(adev);
+ sensor->orientation = intel_cio2_bridge_get_orientation(adev);
if (ssdb.vcmtype)
sensor->vcm_type = cio2_vcm_types[ssdb.vcmtype - 1];
@@ -183,338 +181,8 @@ static int cio2_bridge_parse_sensor_fwnode(struct acpi_device *adev,
return 0;
}
-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->mclkspeed);
- sensor->dev_properties[1] = PROPERTY_ENTRY_U32(
- sensor->prop_names.rotation,
- sensor->rotation);
- sensor->dev_properties[2] = PROPERTY_ENTRY_U32(
- sensor->prop_names.orientation,
- sensor->orientation);
- if (sensor->vcm_type) {
- 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->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->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->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 */
- if (sensor->vcm_type) {
- /* append link to distinguish nodes with same model VCM */
- snprintf(sensor->node_names.vcm, sizeof(sensor->node_names.vcm),
- "%s-%u", sensor->vcm_type, sensor->link);
- }
-}
-
-static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor)
-{
- struct software_node *nodes = sensor->swnodes;
-
- sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID];
- sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT];
- sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT];
- sensor->group[SWNODE_CIO2_PORT] = &nodes[SWNODE_CIO2_PORT];
- sensor->group[SWNODE_CIO2_ENDPOINT] = &nodes[SWNODE_CIO2_ENDPOINT];
- if (sensor->vcm_type)
- sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM];
-}
-
-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);
- nodes[SWNODE_VCM] = NODE_VCM(sensor->node_names.vcm);
-
- cio2_bridge_init_swnode_group(sensor);
-}
-
-static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor)
-{
- struct i2c_board_info board_info = { };
- char name[16];
-
- if (!sensor->vcm_type)
- return;
-
- snprintf(name, sizeof(name), "%s-VCM", acpi_dev_name(sensor->adev));
- board_info.dev_name = name;
- strscpy(board_info.type, sensor->vcm_type, 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_node_group(sensor->group);
- 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 fwnode_handle *fwnode, *primary;
- 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(bridge->dev, "Exceeded available CIO2 ports\n");
- return -EINVAL;
- }
-
- sensor = &bridge->sensors[bridge->n_sensors];
-
- ret = bridge->parse_sensor_fwnode(adev, sensor, cfg);
- if (ret)
- goto err_put_adev;
-
- snprintf(sensor->name, sizeof(sensor->name), "%s-%u",
- cfg->hid, sensor->link);
-
- cio2_bridge_create_fwnode_properties(sensor, bridge, cfg);
- cio2_bridge_create_connection_swnodes(bridge, sensor);
-
- ret = software_node_register_node_group(sensor->group);
- 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);
-
- primary = acpi_fwnode_handle(adev);
- primary->secondary = fwnode;
-
- cio2_bridge_instantiate_vcm_i2c_client(sensor);
-
- dev_info(bridge->dev, "Found supported sensor %s\n",
- acpi_dev_name(adev));
-
- bridge->n_sensors++;
- }
-
- return 0;
-
-err_free_swnodes:
- software_node_unregister_node_group(sensor->group);
-err_put_adev:
- acpi_dev_put(adev);
- return ret;
-}
-
-static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; bridge->supported_sensors[i].hid; i++) {
- const struct cio2_sensor_config *cfg =
- &bridge->supported_sensors[i];
-
- ret = cio2_bridge_connect_sensor(cfg, bridge);
- 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(
- const struct cio2_sensor_config *supported_sensors)
-{
- struct acpi_device *adev;
- bool ready = true;
- unsigned int i;
-
- for (i = 0; supported_sensors[i].hid; i++) {
- const struct cio2_sensor_config *cfg = &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;
-}
-
-static int __cio2_bridge_init(
- struct device *dev,
- int (*parse_sensor_fwnode)(struct acpi_device *adev,
- struct cio2_sensor *sensor,
- const struct cio2_sensor_config *cfg),
- const struct cio2_sensor_config *supported_sensors)
-{
- struct fwnode_handle *fwnode;
- struct cio2_bridge *bridge;
- unsigned int i;
- int ret;
-
- if (!cio2_bridge_sensors_are_ready(supported_sensors))
- 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;
- bridge->dev = dev;
- bridge->parse_sensor_fwnode = parse_sensor_fwnode;
- bridge->supported_sensors = supported_sensors;
-
- 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);
- 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;
-}
-
int cio2_bridge_init(struct device *dev)
{
- return __cio2_bridge_init(dev, cio2_bridge_parse_sensor_fwnode,
- cio2_supported_sensors);
+ return intel_cio2_bridge_init(dev, cio2_bridge_parse_sensor_fwnode,
+ cio2_supported_sensors);
}
deleted file mode 100644
@@ -1,156 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Author: Dan Scally <djrscally@gmail.com> */
-#ifndef __CIO2_BRIDGE_H
-#define __CIO2_BRIDGE_H
-
-#include <linux/property.h>
-#include <linux/types.h>
-
-#include "ipu3-cio2.h"
-
-struct i2c_client;
-
-#define CIO2_HID "INT343E"
-#define CIO2_MAX_LANES 4
-#define MAX_NUM_LINK_FREQS 3
-
-/* Values are educated guesses as we don't have a spec */
-#define CIO2_SENSOR_ROTATION_NORMAL 0
-#define CIO2_SENSOR_ROTATION_INVERTED 1
-
-#define CIO2_SENSOR_CONFIG(_HID, _NR, ...) \
- (const struct cio2_sensor_config) { \
- .hid = _HID, \
- .nr_link_freqs = _NR, \
- .link_freqs = { __VA_ARGS__ } \
- }
-
-#define NODE_SENSOR(_HID, _PROPS) \
- (const struct software_node) { \
- .name = _HID, \
- .properties = _PROPS, \
- }
-
-#define NODE_PORT(_PORT, _SENSOR_NODE) \
- (const struct software_node) { \
- .name = _PORT, \
- .parent = _SENSOR_NODE, \
- }
-
-#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \
- (const struct software_node) { \
- .name = _EP, \
- .parent = _PORT, \
- .properties = _PROPS, \
- }
-
-#define NODE_VCM(_TYPE) \
- (const struct software_node) { \
- .name = _TYPE, \
- }
-
-enum cio2_sensor_swnodes {
- SWNODE_SENSOR_HID,
- SWNODE_SENSOR_PORT,
- SWNODE_SENSOR_ENDPOINT,
- SWNODE_CIO2_PORT,
- SWNODE_CIO2_ENDPOINT,
- /* Must be last because it is optional / maybe empty */
- SWNODE_VCM,
- SWNODE_COUNT
-};
-
-/* Data representation as it is in ACPI SSDB buffer */
-struct cio2_sensor_ssdb {
- u8 version;
- u8 sku;
- u8 guid_csi2[16];
- u8 devfunction;
- u8 bus;
- u32 dphylinkenfuses;
- u32 clockdiv;
- u8 link;
- u8 lanes;
- u32 csiparams[10];
- u32 maxlanespeed;
- u8 sensorcalibfileidx;
- u8 sensorcalibfileidxInMBZ[3];
- u8 romtype;
- u8 vcmtype;
- u8 platforminfo;
- u8 platformsubinfo;
- u8 flash;
- u8 privacyled;
- u8 degree;
- u8 mipilinkdefined;
- u32 mclkspeed;
- u8 controllogicid;
- u8 reserved1[3];
- u8 mclkport;
- u8 reserved2[13];
-} __packed;
-
-struct cio2_property_names {
- char clock_frequency[16];
- char rotation[9];
- char orientation[12];
- char bus_type[9];
- char data_lanes[11];
- char remote_endpoint[16];
- char link_frequencies[17];
-};
-
-struct cio2_node_names {
- char port[7];
- char endpoint[11];
- char remote_port[7];
- char vcm[16];
-};
-
-struct cio2_sensor_config {
- const char *hid;
- const u8 nr_link_freqs;
- const u64 link_freqs[MAX_NUM_LINK_FREQS];
-};
-
-struct cio2_sensor {
- /* append ssdb.link(u8) in "-%u" format as suffix of HID */
- char name[ACPI_ID_LEN + 4];
- struct acpi_device *adev;
- struct i2c_client *vcm_i2c_client;
-
- /* SWNODE_COUNT + 1 for terminating NULL */
- const struct software_node *group[SWNODE_COUNT + 1];
- struct software_node swnodes[SWNODE_COUNT];
- struct cio2_node_names node_names;
-
- u8 link;
- u8 lanes;
- u32 mclkspeed;
- u32 rotation;
- enum v4l2_fwnode_orientation orientation;
- const char *vcm_type;
-
- struct cio2_property_names prop_names;
- struct property_entry ep_properties[5];
- struct property_entry dev_properties[5];
- struct property_entry cio2_properties[3];
- struct software_node_ref_args local_ref[1];
- struct software_node_ref_args remote_ref[1];
- struct software_node_ref_args vcm_ref[1];
-};
-
-struct cio2_bridge {
- struct device *dev;
- int (*parse_sensor_fwnode)(struct acpi_device *adev,
- struct cio2_sensor *sensor,
- const struct cio2_sensor_config *cfg);
- const struct cio2_sensor_config *supported_sensors;
- char cio2_node_name[ACPI_ID_LEN];
- struct software_node cio2_hid_node;
- u32 data_lanes[4];
- unsigned int n_sensors;
- struct cio2_sensor sensors[CIO2_NUM_PORTS];
-};
-
-#endif
new file mode 100644
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+/* Author: Hans de Goede <hansg@kernel.org> */
+#ifndef _INTEL_CIO2_BRIDGE_H
+#define _INTEL_CIO2_BRIDGE_H
+
+#include <linux/property.h>
+#include <linux/types.h>
+
+#include <media/v4l2-fwnode.h>
+
+struct acpi_device;
+struct i2c_client;
+
+#define INTEL_CIO2_HID "INT343E"
+#define INTEL_CIO2_MAX_LANES 4
+#define INTEL_CIO2_NUM_PORTS 4U /* DPHYs */
+#define INTEL_CIO2_MAX_NUM_LINK_FREQS 3
+
+enum intel_cio2_sensor_swnodes {
+ SWNODE_SENSOR_HID,
+ SWNODE_SENSOR_PORT,
+ SWNODE_SENSOR_ENDPOINT,
+ SWNODE_CIO2_PORT,
+ SWNODE_CIO2_ENDPOINT,
+ /* Must be last because it is optional / maybe empty */
+ SWNODE_VCM,
+ SWNODE_COUNT
+};
+
+struct intel_cio2_property_names {
+ char clock_frequency[16];
+ char rotation[9];
+ char orientation[12];
+ char bus_type[9];
+ char data_lanes[11];
+ char remote_endpoint[16];
+ char link_frequencies[17];
+};
+
+struct intel_cio2_node_names {
+ char port[7];
+ char endpoint[11];
+ char remote_port[7];
+ char vcm[16];
+};
+
+struct intel_cio2_sensor_config {
+ const char *hid;
+ const u8 nr_link_freqs;
+ const u64 link_freqs[INTEL_CIO2_MAX_NUM_LINK_FREQS];
+};
+
+struct intel_cio2_sensor {
+ /* append link(u8) in "-%u" format as suffix of HID */
+ char name[ACPI_ID_LEN + 4];
+ struct acpi_device *adev;
+ struct i2c_client *vcm_i2c_client;
+
+ /* SWNODE_COUNT + 1 for terminating NULL */
+ const struct software_node *group[SWNODE_COUNT + 1];
+ struct software_node swnodes[SWNODE_COUNT];
+ struct intel_cio2_node_names node_names;
+
+ u8 link;
+ u8 lanes;
+ u32 mclkspeed;
+ u32 rotation;
+ enum v4l2_fwnode_orientation orientation;
+ const char *vcm_type;
+
+ struct intel_cio2_property_names prop_names;
+ struct property_entry ep_properties[5];
+ struct property_entry dev_properties[5];
+ struct property_entry cio2_properties[3];
+ struct software_node_ref_args local_ref[1];
+ struct software_node_ref_args remote_ref[1];
+ struct software_node_ref_args vcm_ref[1];
+};
+
+struct intel_cio2_bridge {
+ struct device *dev;
+ int (*parse_sensor_fwnode)(struct acpi_device *adev,
+ struct intel_cio2_sensor *sensor,
+ const struct intel_cio2_sensor_config *cfg);
+ const struct intel_cio2_sensor_config *supported_sensors;
+ char cio2_node_name[ACPI_ID_LEN];
+ struct software_node cio2_hid_node;
+ u32 data_lanes[INTEL_CIO2_MAX_LANES];
+ unsigned int n_sensors;
+ struct intel_cio2_sensor sensors[INTEL_CIO2_NUM_PORTS];
+};
+
+int intel_cio2_bridge_init(
+ struct device *dev,
+ int (*parse_sensor_fwnode)(struct acpi_device *adev,
+ struct intel_cio2_sensor *sensor,
+ const struct intel_cio2_sensor_config *cfg),
+ const struct intel_cio2_sensor_config *supported_sensors);
+
+enum v4l2_fwnode_orientation
+intel_cio2_bridge_get_orientation(struct acpi_device *adev);
+
+#endif
Move all the code implementing cio2_bridge_init() into a new shared intel-cio2-bridge.ko, so that it can be reused by the atomisp driver (and maybe also by the future IPU6 CSI2 driver). Note this just moves a bunch of stuff around prefixes some symbols / defines with intel_ / INTEL_ no functional changes. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- MAINTAINERS | 9 + drivers/media/common/Kconfig | 4 + drivers/media/common/Makefile | 1 + .../intel-cio2-bridge.c} | 228 +++------ drivers/media/pci/intel/ipu3/Kconfig | 1 + drivers/media/pci/intel/ipu3/cio2-bridge.c | 438 +++--------------- drivers/media/pci/intel/ipu3/cio2-bridge.h | 156 ------- include/media/intel-cio2-bridge.h | 104 +++++ 8 files changed, 237 insertions(+), 704 deletions(-) copy drivers/media/{pci/intel/ipu3/cio2-bridge.c => common/intel-cio2-bridge.c} (66%) delete mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h create mode 100644 include/media/intel-cio2-bridge.h