From f3d5f6613f0da3ba1135f6e06b9692202fb9920b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 14 Jun 2016 15:59:23 +0300 Subject: [PATCH] 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: Laurent Pinchart Reviewed-by: Gjorgji Rosikopulos Tested-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 141 ++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 0d20c6b8b015..bbf54d7f52a1 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -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 @@ done: 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) - 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; + 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; } } 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); -- 2.39.5