diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ceffe14201d1..9cf0d4dcc9f2 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -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; + ret = gb_camera_configure_streams_validate_response(gcam, + module_resp, resp, module_resp_size, nstreams); + if (ret < 0) goto done; - } - - if (resp->padding != 0) { - gcam_dbg(gcam, "response padding != 0"); - ret = -EIO; - 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 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, mutex_unlock(&gcam->mutex); kfree(req); kfree(resp); + kfree(module_resp); return ret; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 21669093daf5..178ec891e9ad 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -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 {