Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3d5f661 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Greg Kroah-Hartman
Browse files

greybus: camera: Clean up on stream configuration failure



When the camera pipeline can't be configured due to a failure of one of
the components (failure to start the CSI transmitter for instance),
components that have already been setup for video streaming need to be
set back to a quiescient state. This is especially important to ensure
that a stream configuration failure won't keep the UniPro links in high
speed mode forever.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: default avatarGjorgji Rosikopulos <grosikopulos@mm-sol.com>
Tested-by: default avatarGjorgji Rosikopulos <grosikopulos@mm-sol.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent d1a8c36e
Loading
Loading
Loading
Loading
+91 −50
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = {
#define gcam_err(gcam, format...)	dev_err(&gcam->bundle->dev, format)

/* -----------------------------------------------------------------------------
 * Camera Protocol Operations
 * Hardware Configuration
 */

static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id,
@@ -173,6 +173,87 @@ static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs)
	return 0;
}

struct ap_csi_config_request {
	__u8 csi_id;
	__u8 flags;
#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
	__u8 num_lanes;
	__u8 padding;
	__le32 bus_freq;
	__le32 lines_per_second;
} __packed;

static int gb_camera_setup_data_connection(struct gb_camera *gcam,
		const struct gb_camera_configure_streams_response *resp,
		struct gb_camera_csi_params *csi_params)
{
	struct ap_csi_config_request csi_cfg;
	int ret;

	/* Set the UniPro link to high speed mode. */
	ret = gb_camera_set_power_mode(gcam, true);
	if (ret < 0)
		return ret;

	/*
	 * Configure the APB1 CSI transmitter using the lines count reported by
	 * the  camera module, but with hard-coded bus frequency and lanes number.
	 *
	 * TODO: use the clocking and size informations 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.
	 */
	memset(&csi_cfg, 0, sizeof(csi_cfg));
	csi_cfg.csi_id = 1;
	csi_cfg.flags = 0;
	csi_cfg.num_lanes = resp->num_lanes;
	csi_cfg.bus_freq = cpu_to_le32(960000000);
	csi_cfg.lines_per_second = resp->lines_per_second;

	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");
		gb_camera_set_power_mode(gcam, false);
		return ret;
	}

	if (csi_params) {
		csi_params->num_lanes = csi_cfg.num_lanes;
		/* Transmitting two bits per cycle. (DDR clock) */
		csi_params->clk_freq = csi_cfg.bus_freq / 2;
		csi_params->lines_per_second = csi_cfg.lines_per_second;
	}

	return 0;
}

static void gb_camera_teardown_data_connection(struct gb_camera *gcam)
{
	struct ap_csi_config_request csi_cfg;
	int ret;

	/* Stop the APB1 CSI transmitter. */
	memset(&csi_cfg, 0, sizeof(csi_cfg));
	csi_cfg.csi_id = 1;

	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 stop the CSI transmitter\n");

	/* Set the UniPro link to low speed mode. */
	gb_camera_set_power_mode(gcam, false);
}

/* -----------------------------------------------------------------------------
 * Camera Protocol Operations
 */

static int gb_camera_capabilities(struct gb_camera *gcam,
				  u8 *capabilities, size_t *size)
{
@@ -211,16 +292,6 @@ static int gb_camera_capabilities(struct gb_camera *gcam,
	return ret;
}

struct ap_csi_config_request {
	__u8 csi_id;
	__u8 flags;
#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
	__u8 num_lanes;
	__u8 padding;
	__le32 bus_freq;
	__le32 lines_per_second;
} __packed;

static int gb_camera_configure_streams(struct gb_camera *gcam,
				       unsigned int *num_streams,
				       unsigned int *flags,
@@ -229,7 +300,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
{
	struct gb_camera_configure_streams_request *req;
	struct gb_camera_configure_streams_response *resp;
	struct ap_csi_config_request csi_cfg;

	unsigned int nstreams = *num_streams;
	unsigned int i;
@@ -315,50 +385,21 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
		goto done;
	}

	/* Setup unipro link speed. */
	ret = gb_camera_set_power_mode(gcam, nstreams != 0);
	if (ret < 0)
	if (resp->num_streams) {
		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, req_size, resp, resp_size);
			goto done;

	/*
	 * Configure the APB1 CSI transmitter using the lines count reported by
	 * the  camera module, but with hard-coded bus frequency and lanes number.
	 *
	 * TODO: use the clocking and size informations 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.
	 */
	memset(&csi_cfg, 0, sizeof(csi_cfg));

	if (nstreams) {
		csi_cfg.csi_id = 1;
		csi_cfg.flags = 0;
		csi_cfg.num_lanes = resp->num_lanes;
		csi_cfg.bus_freq = cpu_to_le32(960000000);
		csi_cfg.lines_per_second = resp->lines_per_second;
		ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
				   sizeof(csi_cfg),
				   GB_APB_REQUEST_CSI_TX_CONTROL, false);
		if (csi_params) {
			csi_params->num_lanes = csi_cfg.num_lanes;
			/* Transmitting two bits per cycle. (DDR clock) */
			csi_params->clk_freq = csi_cfg.bus_freq / 2;
			csi_params->lines_per_second = csi_cfg.lines_per_second;
		}
	} else {
		csi_cfg.csi_id = 1;
		ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
				   sizeof(csi_cfg),
				   GB_APB_REQUEST_CSI_TX_CONTROL, false);
		gb_camera_teardown_data_connection(gcam);
	}

	if (ret < 0)
		gcam_err(gcam, "failed to %s the CSI transmitter\n",
			 nstreams ? "start" : "stop");

	*flags = resp->flags;
	*num_streams = resp->num_streams;
	ret = 0;

done:
	mutex_unlock(&gcam->mutex);