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>
This commit is contained in:
Jacopo Mondi 2016-07-15 11:03:45 +02:00 committed by Greg Kroah-Hartman
parent d165a618a1
commit f88b94ec2a

View file

@ -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);
if (resp->num_streams == 0)
goto done;
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;
}
/*
* 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);