},
};
+static const struct gb_camera_fmt_info *gb_camera_get_format_info(u16 gb_fmt)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) {
+ if (gb_fmt_info[i].gb_format == gb_fmt)
+ return &gb_fmt_info[i];
+ }
+
+ return NULL;
+}
+
#define ES2_APB_CDSI0_CPORT 16
#define ES2_APB_CDSI1_CPORT 17
return ret;
}
+/*
+ * Temporary support for camera modules implementing legacy version
+ * of camera specifications
+ */
+static int gb_camera_configure_stream_translate_deprecated(
+ struct gb_camera *gcam,
+ void *module_resp,
+ struct gb_camera_configure_streams_response *resp)
+{
+ unsigned int i;
+ struct gb_camera_configure_streams_response_deprecated *dresp =
+ (struct gb_camera_configure_streams_response_deprecated *)
+ module_resp;
+
+ if (dresp->padding != 0) {
+ gcam_err(gcam, "legacy response padding != 0\n");
+ return -EIO;
+ }
+
+ resp->num_streams = dresp->num_streams;
+ resp->flags = dresp->flags;
+ resp->data_rate = dresp->bus_freq;
+
+ for (i = 0; i < dresp->num_streams; i++) {
+ const struct gb_camera_fmt_info *fmt_info;
+ struct gb_camera_stream_config_response *cfg;
+
+ if (dresp->config[i].padding ||
+ dresp->config[i].max_pkt_size) {
+ gcam_err(gcam, "legacy stream #%u padding != 0\n", i);
+ return -EIO;
+ }
+
+ resp->config[i] = dresp->config[i];
+ cfg = &resp->config[i];
+
+ /*
+ * As implementations of legacy version of camera protocol do
+ * not provide the max_pkt_size attribute, re-calculate it on
+ * AP side.
+ */
+ 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) {
+ cfg->max_pkt_size = cpu_to_le16(4096);
+ } else if (fmt_info->bpp > 0) {
+ unsigned int width = le16_to_cpu(cfg->width);
+
+ cfg->max_pkt_size = cpu_to_le32(width * fmt_info->bpp / 8);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Validate the stream configuration response verifying padding is correctly
+ * set and the returned number of streams is supported
+ *
+ * FIXME: The function also checks which protocol version the camera module
+ * implements and if it supports or not the new bandwidth requirements
+ * definition parameters.
+ * In case the camera implements the legacy version of protocol
+ * specifications, it gets translated to the new one.
+ */
+static int gb_camera_configure_streams_validate_response(
+ struct gb_camera *gcam,
+ void *module_resp,
+ struct gb_camera_configure_streams_response *ap_resp,
+ unsigned int resp_size,
+ unsigned int nstreams)
+{
+ struct gb_camera_configure_streams_response *resp;
+ unsigned int i;
+ unsigned int module_resp_size =
+ resp_size -
+ sizeof(struct gb_camera_stream_config_response) *
+ nstreams;
+
+ /* TODO: remove support for legacy camera modules */
+ if (module_resp_size == GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE)
+ return gb_camera_configure_stream_translate_deprecated(gcam,
+ module_resp, ap_resp);
+
+ if (module_resp_size != GB_CAMERA_CONFIGURE_STREAMS_SIZE) {
+ gcam_err(gcam, "unrecognized protocol version %u\n",
+ module_resp_size);
+ return -EIO;
+ }
+
+ resp = (struct gb_camera_configure_streams_response *) module_resp;
+ *ap_resp = *resp;
+
+ /* Validate the returned response structure */
+ if (ap_resp->padding[0] || ap_resp->padding[1]) {
+ gcam_err(gcam, "response padding != 0\n");
+ return -EIO;
+ }
+
+ if (ap_resp->num_streams > nstreams) {
+ gcam_err(gcam, "got #streams %u > request %u\n",
+ resp->num_streams, nstreams);
+ return -EIO;
+ }
+
+ for (i = 0; i < ap_resp->num_streams; i++) {
+ struct gb_camera_stream_config_response *cfg = &resp->config[i];
+
+ if (cfg->padding) {
+ gcam_err(gcam, "stream #%u padding != 0\n", i);
+ return -EIO;
+ }
+
+ ap_resp->config[i] = *cfg;
+ }
+
+ return 0;
+}
+
/* -----------------------------------------------------------------------------
* Hardware Configuration
*/
* Configure the APB1 CSI transmitter with hard-coded bus frequency,
* lanes number and lines per second.
*
- * 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.
+ * 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.
*/
memset(&csi_cfg, 0, sizeof(csi_cfg));
csi_cfg.csi_id = 1;
{
struct gb_camera_configure_streams_request *req;
struct gb_camera_configure_streams_response *resp;
+ void *module_resp;
unsigned int nstreams = *num_streams;
unsigned int i;
size_t req_size;
size_t resp_size;
+ size_t module_resp_size;
int ret;
if (nstreams > GB_CAMERA_MAX_STREAMS)
req_size = sizeof(*req) + nstreams * sizeof(req->config[0]);
resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]);
+ /*
+ * FIXME: Reserve enough space for the deprecated version of the
+ * configure_stream response, as it is bigger than the
+ * newly defined one
+ */
+ module_resp_size = GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE +
+ nstreams * sizeof(resp->config[0]);
+
req = kmalloc(req_size, GFP_KERNEL);
resp = kmalloc(resp_size, GFP_KERNEL);
- if (!req || !resp) {
+ module_resp = kmalloc(module_resp_size, GFP_KERNEL);
+ if (!req || !resp || !module_resp) {
kfree(req);
kfree(resp);
+ kfree(module_resp);
return -ENOMEM;
}
goto done;
}
- ret = gb_operation_sync(gcam->connection,
- GB_CAMERA_TYPE_CONFIGURE_STREAMS,
- req, req_size, resp, resp_size);
+ ret = gb_camera_operation_sync_flags(gcam->connection,
+ GB_CAMERA_TYPE_CONFIGURE_STREAMS,
+ GB_OPERATION_FLAG_SHORT_RESPONSE,
+ req, req_size,
+ module_resp, &module_resp_size);
if (ret < 0)
goto done;
- if (resp->num_streams > nstreams) {
- gcam_dbg(gcam, "got #streams %u > request %u\n",
- resp->num_streams, nstreams);
- ret = -EIO;
- goto done;
- }
-
- if (resp->padding != 0) {
- gcam_dbg(gcam, "response padding != 0");
- ret = -EIO;
+ ret = gb_camera_configure_streams_validate_response(gcam,
+ module_resp, resp, module_resp_size, nstreams);
+ if (ret < 0)
goto done;
- }
*flags = resp->flags;
*num_streams = resp->num_streams;
streams[i].dt[0] = cfg->data_type[0];
streams[i].dt[1] = cfg->data_type[1];
streams[i].max_size = le32_to_cpu(cfg->max_size);
-
- if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) {
- gcam_dbg(gcam, "stream #%u padding != 0", i);
- ret = -EIO;
- goto done;
- }
}
if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) ||
mutex_unlock(&gcam->mutex);
kfree(req);
kfree(resp);
+ kfree(module_resp);
return ret;
}