From 68b66c28010ebc2ebe21d6bad2de2eda1bed01fc Mon Sep 17 00:00:00 2001
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Fri, 20 May 2016 14:33:35 +0300
Subject: [PATCH] greybus: camera: Convert to bundle driver

Convert the legacy camera protocol driver to a bundle driver.

Modules now can (and must) declare the camera data cport in their
manifest as the data connection isn't hardcoded anymore.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Johan Hovold <johan@hovoldconsulting.com>
Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
---
 drivers/staging/greybus/camera.c | 123 +++++++++++++++++++++----------
 drivers/staging/greybus/legacy.c |   1 -
 2 files changed, 84 insertions(+), 40 deletions(-)

diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c
index 4831ad652c04a..dd482bd946371 100644
--- a/drivers/staging/greybus/camera.c
+++ b/drivers/staging/greybus/camera.c
@@ -36,11 +36,11 @@ struct gb_camera_debugfs_buffer {
 /**
  * struct gb_camera - A Greybus Camera Device
  * @connection: the greybus connection for camera control
- * @data_connected: whether the data connection has been established
  * @debugfs: debugfs entries for camera protocol operations testing
  * @module: Greybus camera module registered to HOST processor.
  */
 struct gb_camera {
+	struct gb_bundle *bundle;
 	struct gb_connection *connection;
 	struct gb_connection *data_connection;
 
@@ -103,12 +103,9 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = {
 
 #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
 
-#define gcam_dbg(gcam, format...) \
-	dev_dbg(&gcam->connection->bundle->dev, format)
-#define gcam_info(gcam, format...) \
-	dev_info(&gcam->connection->bundle->dev, format)
-#define gcam_err(gcam, format...) \
-	dev_err(&gcam->connection->bundle->dev, format)
+#define gcam_dbg(gcam, format...)	dev_dbg(&gcam->bundle->dev, format)
+#define gcam_info(gcam, format...)	dev_info(&gcam->bundle->dev, format)
+#define gcam_err(gcam, format...)	dev_err(&gcam->bundle->dev, format)
 
 /* -----------------------------------------------------------------------------
  * Camera Protocol Operations
@@ -383,14 +380,14 @@ static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id)
 	return 0;
 }
 
-static int gb_camera_event_recv(u8 type, struct gb_operation *op)
+static int gb_camera_request_handler(struct gb_operation *op)
 {
 	struct gb_camera *gcam = gb_connection_get_data(op->connection);
 	struct gb_camera_metadata_request *payload;
 	struct gb_message *request;
 
-	if (type != GB_CAMERA_TYPE_METADATA) {
-		gcam_err(gcam, "Unsupported unsolicited event: %u\n", type);
+	if (op->type != GB_CAMERA_TYPE_METADATA) {
+		gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type);
 		return -EINVAL;
 	}
 
@@ -849,7 +846,7 @@ static int gb_camera_debugfs_init(struct gb_camera *gcam)
 	 * Create root debugfs entry and a file entry for each camera operation.
 	 */
 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
-		 connection->bundle->id);
+		 gcam->bundle->id);
 
 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
 	if (IS_ERR(gcam->debugfs.root)) {
@@ -905,39 +902,84 @@ static void gb_camera_cleanup(struct gb_camera *gcam)
 		gb_connection_destroy(gcam->data_connection);
 	}
 
+	if (gcam->connection) {
+		gb_connection_disable(gcam->connection);
+		gb_connection_destroy(gcam->connection);
+	}
+
 	kfree(gcam);
 }
 
-static int gb_camera_connection_init(struct gb_connection *connection)
+static int gb_camera_probe(struct gb_bundle *bundle,
+			   const struct greybus_bundle_id *id)
 {
-	struct gb_connection *data;
+	struct gb_connection *conn;
 	struct gb_camera *gcam;
-	unsigned long data_flags;
+	u16 mgmt_cport_id = 0;
+	u16 data_cport_id = 0;
+	unsigned int i;
 	int ret;
 
+	/*
+	 * The camera bundle must contain exactly two CPorts, one for the
+	 * camera management protocol and one for the camera data protocol.
+	 */
+	if (bundle->num_cports != 2)
+		return -ENODEV;
+
+	for (i = 0; i < bundle->num_cports; ++i) {
+		struct greybus_descriptor_cport *desc = &bundle->cport_desc[i];
+
+		switch (desc->protocol_id) {
+		case GREYBUS_PROTOCOL_CAMERA_MGMT:
+			mgmt_cport_id = le16_to_cpu(desc->id);
+			break;
+		case GREYBUS_PROTOCOL_CAMERA_DATA:
+			data_cport_id = le16_to_cpu(desc->id);
+			break;
+		default:
+			return -ENODEV;
+		}
+	}
+
+	if (!mgmt_cport_id || !data_cport_id)
+		return -ENODEV;
+
 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
 	if (!gcam)
 		return -ENOMEM;
 
-	gcam->connection = connection;
-	gb_connection_set_data(connection, gcam);
+	gcam->bundle = bundle;
+
+	conn = gb_connection_create(bundle, mgmt_cport_id,
+				    gb_camera_request_handler);
+	if (IS_ERR(conn)) {
+		ret = PTR_ERR(conn);
+		goto error;
+	}
+
+	gcam->connection = conn;
+	gb_connection_set_data(conn, gcam);
+
+	ret = gb_connection_enable(conn);
+	if (ret)
+		goto error;
 
 	/*
-	 * Create the data connection between camera module CDSI0 and APB CDS1.
-	 * The CPort IDs are hardcoded by the ES2 bridges.
+	 * Create the data connection between the camera module data CPort and
+	 * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge.
 	 */
-	data_flags = GB_CONNECTION_FLAG_NO_FLOWCTRL | GB_CONNECTION_FLAG_CDSI1;
-
-	data = gb_connection_create_offloaded(connection->bundle,
-					      ES2_APB_CDSI0_CPORT,
-					      data_flags);
-	if (IS_ERR(data)) {
-		ret = PTR_ERR(data);
+	conn = gb_connection_create_offloaded(bundle, data_cport_id,
+					      GB_CONNECTION_FLAG_NO_FLOWCTRL |
+					      GB_CONNECTION_FLAG_CDSI1);
+	if (IS_ERR(conn)) {
+		ret = PTR_ERR(conn);
 		goto error;
 	}
-	gcam->data_connection = data;
+	gcam->data_connection = conn;
+	gb_connection_set_data(conn, gcam);
 
-	ret = gb_connection_enable(data);
+	ret = gb_connection_enable(conn);
 	if (ret)
 		goto error;
 
@@ -949,6 +991,8 @@ static int gb_camera_connection_init(struct gb_connection *connection)
 	if (ret < 0)
 		goto error;
 
+	greybus_set_drvdata(bundle, gcam);
+
 	return 0;
 
 error:
@@ -956,25 +1000,26 @@ error:
 	return ret;
 }
 
-static void gb_camera_connection_exit(struct gb_connection *connection)
+static void gb_camera_disconnect(struct gb_bundle *bundle)
 {
-	struct gb_camera *gcam = gb_connection_get_data(connection);
+	struct gb_camera *gcam = greybus_get_drvdata(bundle);
 
 	gb_camera_unregister_intf_ops(gcam);
-
 	gb_camera_cleanup(gcam);
 }
 
-static struct gb_protocol camera_protocol = {
-	.name			= "camera",
-	.id			= GREYBUS_PROTOCOL_CAMERA_MGMT,
-	.major			= GB_CAMERA_VERSION_MAJOR,
-	.minor			= GB_CAMERA_VERSION_MINOR,
-	.connection_init	= gb_camera_connection_init,
-	.connection_exit	= gb_camera_connection_exit,
-	.request_recv		= gb_camera_event_recv,
+static const struct greybus_bundle_id gb_camera_id_table[] = {
+	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
+	{ },
+};
+
+static struct greybus_driver gb_camera_driver = {
+	.name		= "camera",
+	.probe		= gb_camera_probe,
+	.disconnect	= gb_camera_disconnect,
+	.id_table	= gb_camera_id_table,
 };
 
-gb_protocol_driver(&camera_protocol);
+module_greybus_driver(gb_camera_driver);
 
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c
index 06bd85cc4459b..19f3d93abfed2 100644
--- a/drivers/staging/greybus/legacy.c
+++ b/drivers/staging/greybus/legacy.c
@@ -236,7 +236,6 @@ static void legacy_disconnect(struct gb_bundle *bundle)
 }
 
 static const struct greybus_bundle_id legacy_id_table[] = {
-	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
 	{ }
 };
 MODULE_DEVICE_TABLE(greybus, legacy_id_table);
-- 
2.30.2