Browse Source

greybus: camera: Configure APB-A with new params

Update the configuration supplied to APB-A to use new parameters, while
keeping compatibility with legacy camera modules.
Substitute hard-coded clock frequency with information provided by camera
module, and retrieve the maximum CSI Long Packet length from the
returned stream configuration.

This patch requires APB-A csi-tx driver to be updated to comply with
newly defined bandwidth requirements.

Testing Done: preview, capture and video recording with updated csi-tx
driver on APB-A side, and legacy white camera module firmware

Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Jacopo Mondi 9 years ago
parent
commit
f88b94ec2a
1 changed files with 91 additions and 35 deletions
  1. 91 35
      drivers/staging/greybus/camera.c

+ 91 - 35
drivers/staging/greybus/camera.c

@@ -220,6 +220,50 @@ static int gb_camera_operation_sync_flags(struct gb_connection *connection,
 	return ret;
 }
 
+static int gb_camera_get_max_pkt_size(struct gb_camera *gcam,
+		struct gb_camera_configure_streams_response *resp)
+{
+	unsigned int max_pkt_size = 0;
+	unsigned int i;
+
+	for (i = 0; i < resp->num_streams; i++) {
+		struct gb_camera_stream_config_response *cfg = &resp->config[i];
+		const struct gb_camera_fmt_info *fmt_info;
+		unsigned int pkt_size;
+
+		fmt_info = gb_camera_get_format_info(cfg->format);
+		if (!fmt_info) {
+			gcam_err(gcam, "unsupported greybus image format: %d\n",
+				 cfg->format);
+			return -EIO;
+		}
+
+		if (fmt_info->bpp == 0) {
+			pkt_size = le32_to_cpu(cfg->max_pkt_size);
+
+			if (pkt_size == 0) {
+				gcam_err(gcam,
+					 "Stream %u: invalid zero maximum packet size\n",
+					 i);
+				return -EIO;
+			}
+		} else {
+			pkt_size = le16_to_cpu(cfg->width) * fmt_info->bpp / 8;
+
+			if (pkt_size != le32_to_cpu(cfg->max_pkt_size)) {
+				gcam_err(gcam,
+					 "Stream %u: maximum packet size mismatch (%u/%u)\n",
+					 i, pkt_size, cfg->max_pkt_size);
+				return -EIO;
+			}
+		}
+
+		max_pkt_size = max(pkt_size, max_pkt_size);
+	}
+
+	return max_pkt_size;
+}
+
 /*
  * Temporary support for camera modules implementing legacy version
  * of camera specifications
@@ -409,8 +453,8 @@ struct ap_csi_config_request {
 #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
 	__u8 num_lanes;
 	__u8 padding;
-	__le32 bus_freq;
-	__le32 lines_per_second;
+	__le32 csi_clk_freq;
+	__le32 max_pkt_size;
 } __packed;
 
 /*
@@ -418,14 +462,18 @@ struct ap_csi_config_request {
  * requirements.
  */
 #define GB_CAMERA_CSI_NUM_DATA_LANES		4
-#define GB_CAMERA_LINES_PER_SECOND		(1280 * 30)
+
+#define GB_CAMERA_CSI_CLK_FREQ_MAX		999000000U
+#define GB_CAMERA_CSI_CLK_FREQ_MIN		100000000U
+#define GB_CAMERA_CSI_CLK_FREQ_MARGIN		150000000U
 
 static int gb_camera_setup_data_connection(struct gb_camera *gcam,
-		const struct gb_camera_configure_streams_response *resp,
+		struct gb_camera_configure_streams_response *resp,
 		struct gb_camera_csi_params *csi_params)
 {
 	struct ap_csi_config_request csi_cfg;
 	struct gb_connection *conn;
+	unsigned int clk_freq;
 	int ret;
 
 	/*
@@ -451,34 +499,40 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam,
 		goto error_conn_disable;
 
 	/*
-	 * Configure the APB1 CSI transmitter with hard-coded bus frequency,
-	 * lanes number and lines per second.
+	 * Configure the APB-A CSI-2 transmitter.
 	 *
-	 * TODO: Use the data rate and size information reported by camera
-	 * module to compute the required CSI bandwidth, and configure the
-	 * CSI receiver on AP side, and the CSI transmitter on APB1 side
-	 * accordingly.
+	 * Hardcode the number of lanes to 4 and compute the bus clock frequency
+	 * based on the module bandwidth requirements with a safety margin.
 	 */
 	memset(&csi_cfg, 0, sizeof(csi_cfg));
 	csi_cfg.csi_id = 1;
 	csi_cfg.flags = 0;
 	csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES;
-	csi_cfg.bus_freq = cpu_to_le32(960000000);
-	csi_cfg.lines_per_second = GB_CAMERA_LINES_PER_SECOND;
+
+	clk_freq = resp->data_rate / 2 / GB_CAMERA_CSI_NUM_DATA_LANES;
+	clk_freq = clamp(clk_freq + GB_CAMERA_CSI_CLK_FREQ_MARGIN,
+			 GB_CAMERA_CSI_CLK_FREQ_MIN,
+			 GB_CAMERA_CSI_CLK_FREQ_MAX);
+	csi_cfg.csi_clk_freq = clk_freq;
+
+	ret = gb_camera_get_max_pkt_size(gcam, resp);
+	if (ret < 0) {
+		ret = -EIO;
+		goto error_power;
+	}
+	csi_cfg.max_pkt_size = ret;
 
 	ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
 			   sizeof(csi_cfg),
 			   GB_APB_REQUEST_CSI_TX_CONTROL, false);
-
 	if (ret < 0) {
 		gcam_err(gcam, "failed to start the CSI transmitter\n");
 		goto error_power;
 	}
 
 	if (csi_params) {
+		csi_params->clk_freq = csi_cfg.csi_clk_freq;
 		csi_params->num_lanes = csi_cfg.num_lanes;
-		/* Transmitting two bits per cycle. (DDR clock) */
-		csi_params->clk_freq = csi_cfg.bus_freq / 2;
 	}
 
 	return 0;
@@ -664,29 +718,31 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
 		gb_pm_runtime_put_noidle(gcam->bundle);
 	}
 
-	if (resp->num_streams) {
-		/*
-		 * Make sure the bundle won't be suspended until streams get
-		 * unconfigured after the stream is configured successfully
-		 */
-		gb_pm_runtime_get_noresume(gcam->bundle);
-
-		ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
-		if (ret < 0) {
-			memset(req, 0, sizeof(*req));
-			gb_operation_sync(gcam->connection,
-					  GB_CAMERA_TYPE_CONFIGURE_STREAMS,
-					  req, sizeof(*req),
-					  resp, sizeof(*resp));
-			*flags = 0;
-			*num_streams = 0;
-			gb_pm_runtime_put_noidle(gcam->bundle);
-			goto done;
-		}
+	if (resp->num_streams == 0)
+		goto done;
+
+	/*
+	 * Make sure the bundle won't be suspended until streams get
+	 * unconfigured after the stream is configured successfully
+	 */
+	gb_pm_runtime_get_noresume(gcam->bundle);
 
-		gcam->state = GB_CAMERA_STATE_CONFIGURED;
+	/* Setup CSI-2 connection from APB-A to AP */
+	ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
+	if (ret < 0) {
+		memset(req, 0, sizeof(*req));
+		gb_operation_sync(gcam->connection,
+				  GB_CAMERA_TYPE_CONFIGURE_STREAMS,
+				  req, sizeof(*req),
+				  resp, sizeof(*resp));
+		*flags = 0;
+		*num_streams = 0;
+		gb_pm_runtime_put_noidle(gcam->bundle);
+		goto done;
 	}
 
+	gcam->state = GB_CAMERA_STATE_CONFIGURED;
+
 done:
 	gb_pm_runtime_put_autosuspend(gcam->bundle);