]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
greybus: camera: Update Configure Streams Response
authorJacopo Mondi <jacopo.mondi@linaro.org>
Fri, 15 Jul 2016 09:03:44 +0000 (11:03 +0200)
committerGreg Kroah-Hartman <gregkh@google.com>
Sun, 17 Jul 2016 01:31:21 +0000 (10:31 +0900)
As camera specification gets updated, and Configure Stream Response
payload modified, define here the new response structure.
In order to not break non up-to-date camera modules, keep the existing
structure and add the _deprecated suffix to it.

Add the size of both new and old structure in order to discriminate
dynamically which version of Camera Specification the camera module
implements and translate deprecated version of configure_streams response
in the new one.

In order not to break camera functionalities, for testing purposes,
hard-code values the APB-A CSI Tx driver still requires for proper
interface configuration (lines_per_second and num_lanes)

Testing Done: Preview, capture and video recording with white camera
module and APB-A with legacy firmware implementations

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>
drivers/staging/greybus/camera.c
drivers/staging/greybus/greybus_protocols.h

index ceffe14201d1a503b937a92d2a9fcdd2fa2037c6..9cf0d4dcc9f2821c62c5e644532c654a18285c21 100644 (file)
@@ -164,6 +164,18 @@ static const struct gb_camera_fmt_info gb_fmt_info[] = {
        },
 };
 
+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
 
@@ -208,6 +220,130 @@ static int gb_camera_operation_sync_flags(struct gb_connection *connection,
        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
  */
@@ -318,9 +454,10 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam,
         * 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;
@@ -425,10 +562,12 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
 {
        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)
@@ -437,11 +576,21 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
        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;
        }
 
@@ -469,24 +618,18 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
                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;
@@ -501,12 +644,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
                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) ||
@@ -557,6 +694,7 @@ done_skip_pm_put:
        mutex_unlock(&gcam->mutex);
        kfree(req);
        kfree(resp);
+       kfree(module_resp);
        return ret;
 }
 
index 21669093daf5cb19e4bbb91f6ec61756058d8778..178ec891e9addee187d77d509dcbd3d9bbeccdd0 100644 (file)
@@ -1706,20 +1706,31 @@ struct gb_camera_stream_config_response {
        __le16 format;
        __u8 virtual_channel;
        __u8 data_type[2];
-       __u8 padding[3];
+       __le16 max_pkt_size;
+       __u8 padding;
        __le32 max_size;
 } __packed;
 
-struct gb_camera_configure_streams_response {
+struct gb_camera_configure_streams_response_deprecated {
        __u8 num_streams;
        __u8 flags;
-#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED   0x01
        __u8 num_lanes;
        __u8 padding;
        __le32 bus_freq;
        __le32 lines_per_second;
        struct gb_camera_stream_config_response config[0];
 } __packed;
+#define GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE    12
+
+struct gb_camera_configure_streams_response {
+       __u8 num_streams;
+#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED   0x01
+       __u8 flags;
+       __u8 padding[2];
+       __le32 data_rate;
+       struct gb_camera_stream_config_response config[0];
+};
+#define GB_CAMERA_CONFIGURE_STREAMS_SIZE               8
 
 /* Greybus Camera Capture request payload - response has no payload */
 struct gb_camera_capture_request {