summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/camera.c
diff options
context:
space:
mode:
authorJacopo Mondi <jacopo.mondi@linaro.org>2016-07-15 11:03:45 +0200
committerGreg Kroah-Hartman <gregkh@google.com>2016-07-17 10:31:21 +0900
commitf88b94ec2a3b37e8f9fd20f8fb17d9e0a104fe1f (patch)
tree7e2d96dd17f25d8591a5668335a9e4447fe714a4 /drivers/staging/greybus/camera.c
parentd165a618a14c8871f8e14090791298e1b2ef8231 (diff)
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>
Diffstat (limited to 'drivers/staging/greybus/camera.c')
-rw-r--r--drivers/staging/greybus/camera.c126
1 files changed, 91 insertions, 35 deletions
diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c
index 9cf0d4dcc9f2..e0719e2db4e6 100644
--- a/drivers/staging/greybus/camera.c
+++ b/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);