Commit f88b94ec authored by Jacopo Mondi's avatar Jacopo Mondi Committed by Greg Kroah-Hartman
Browse files

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: default avatarJacopo Mondi <jacopo.mondi@linaro.org>
Reviewed-by: default avatarLaurent Pinchart <laurent.pinchart@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent d165a618
Loading
Loading
Loading
Loading
+91 −35
Original line number Original line Diff line number Diff line
@@ -220,6 +220,50 @@ static int gb_camera_operation_sync_flags(struct gb_connection *connection,
	return ret;
	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
 * Temporary support for camera modules implementing legacy version
 * of camera specifications
 * of camera specifications
@@ -409,8 +453,8 @@ struct ap_csi_config_request {
#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
	__u8 num_lanes;
	__u8 num_lanes;
	__u8 padding;
	__u8 padding;
	__le32 bus_freq;
	__le32 csi_clk_freq;
	__le32 lines_per_second;
	__le32 max_pkt_size;
} __packed;
} __packed;


/*
/*
@@ -418,14 +462,18 @@ struct ap_csi_config_request {
 * requirements.
 * requirements.
 */
 */
#define GB_CAMERA_CSI_NUM_DATA_LANES		4
#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,
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 gb_camera_csi_params *csi_params)
{
{
	struct ap_csi_config_request csi_cfg;
	struct ap_csi_config_request csi_cfg;
	struct gb_connection *conn;
	struct gb_connection *conn;
	unsigned int clk_freq;
	int ret;
	int ret;


	/*
	/*
@@ -451,34 +499,40 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam,
		goto error_conn_disable;
		goto error_conn_disable;


	/*
	/*
	 * Configure the APB1 CSI transmitter with hard-coded bus frequency,
	 * Configure the APB-A CSI-2 transmitter.
	 * lanes number and lines per second.
	 *
	 *
	 * TODO: Use the data rate and size information reported by camera
	 * Hardcode the number of lanes to 4 and compute the bus clock frequency
	 * module to compute the required CSI bandwidth, and configure the
	 * based on the module bandwidth requirements with a safety margin.
	 * CSI receiver on AP side, and the CSI transmitter on APB1 side
	 * accordingly.
	 */
	 */
	memset(&csi_cfg, 0, sizeof(csi_cfg));
	memset(&csi_cfg, 0, sizeof(csi_cfg));
	csi_cfg.csi_id = 1;
	csi_cfg.csi_id = 1;
	csi_cfg.flags = 0;
	csi_cfg.flags = 0;
	csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES;
	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,
	ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
			   sizeof(csi_cfg),
			   sizeof(csi_cfg),
			   GB_APB_REQUEST_CSI_TX_CONTROL, false);
			   GB_APB_REQUEST_CSI_TX_CONTROL, false);

	if (ret < 0) {
	if (ret < 0) {
		gcam_err(gcam, "failed to start the CSI transmitter\n");
		gcam_err(gcam, "failed to start the CSI transmitter\n");
		goto error_power;
		goto error_power;
	}
	}


	if (csi_params) {
	if (csi_params) {
		csi_params->clk_freq = csi_cfg.csi_clk_freq;
		csi_params->num_lanes = csi_cfg.num_lanes;
		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;
	return 0;
@@ -664,13 +718,16 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
		gb_pm_runtime_put_noidle(gcam->bundle);
		gb_pm_runtime_put_noidle(gcam->bundle);
	}
	}


	if (resp->num_streams) {
	if (resp->num_streams == 0)
		goto done;

	/*
	/*
	 * Make sure the bundle won't be suspended until streams get
	 * Make sure the bundle won't be suspended until streams get
	 * unconfigured after the stream is configured successfully
	 * unconfigured after the stream is configured successfully
	 */
	 */
	gb_pm_runtime_get_noresume(gcam->bundle);
	gb_pm_runtime_get_noresume(gcam->bundle);


	/* Setup CSI-2 connection from APB-A to AP */
	ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
	ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
	if (ret < 0) {
	if (ret < 0) {
		memset(req, 0, sizeof(*req));
		memset(req, 0, sizeof(*req));
@@ -685,7 +742,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
	}
	}


	gcam->state = GB_CAMERA_STATE_CONFIGURED;
	gcam->state = GB_CAMERA_STATE_CONFIGURED;
	}


done:
done:
	gb_pm_runtime_put_autosuspend(gcam->bundle);
	gb_pm_runtime_put_autosuspend(gcam->bundle);