diff options
Diffstat (limited to 'greybus_camera.patch')
| -rw-r--r-- | greybus_camera.patch | 1538 |
1 files changed, 1538 insertions, 0 deletions
diff --git a/greybus_camera.patch b/greybus_camera.patch new file mode 100644 index 00000000000000..0900dd1bf85672 --- /dev/null +++ b/greybus_camera.patch @@ -0,0 +1,1538 @@ +--- + drivers/greybus/camera.c | 1400 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/gb-camera.h | 127 +++ + 2 files changed, 1527 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/camera.c +@@ -0,0 +1,1400 @@ ++/* ++ * Greybus Camera protocol driver. ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/debugfs.h> ++#include <linux/fs.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/string.h> ++#include <linux/uaccess.h> ++#include <linux/vmalloc.h> ++ ++#include "gb-camera.h" ++#include "greybus.h" ++#include "greybus_protocols.h" ++ ++enum gb_camera_debugs_buffer_id { ++ GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, ++ GB_CAMERA_DEBUGFS_BUFFER_STREAMS, ++ GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, ++ GB_CAMERA_DEBUGFS_BUFFER_FLUSH, ++ GB_CAMERA_DEBUGFS_BUFFER_MAX, ++}; ++ ++struct gb_camera_debugfs_buffer { ++ char data[PAGE_SIZE]; ++ size_t length; ++}; ++ ++enum gb_camera_state { ++ GB_CAMERA_STATE_UNCONFIGURED, ++ GB_CAMERA_STATE_CONFIGURED, ++}; ++ ++/** ++ * struct gb_camera - A Greybus Camera Device ++ * @connection: the greybus connection for camera management ++ * @data_connection: the greybus connection for camera data ++ * @data_cport_id: the data CPort ID on the module side ++ * @mutex: protects the connection and state fields ++ * @state: the current module state ++ * @debugfs: debugfs entries for camera protocol operations testing ++ * @module: Greybus camera module registered to HOST processor. ++ */ ++struct gb_camera { ++ struct gb_bundle *bundle; ++ struct gb_connection *connection; ++ struct gb_connection *data_connection; ++ u16 data_cport_id; ++ ++ struct mutex mutex; ++ enum gb_camera_state state; ++ ++ struct { ++ struct dentry *root; ++ struct gb_camera_debugfs_buffer *buffers; ++ } debugfs; ++ ++ struct gb_camera_module module; ++}; ++ ++struct gb_camera_stream_config { ++ unsigned int width; ++ unsigned int height; ++ unsigned int format; ++ unsigned int vc; ++ unsigned int dt[2]; ++ unsigned int max_size; ++}; ++ ++struct gb_camera_fmt_info { ++ enum v4l2_mbus_pixelcode mbus_code; ++ unsigned int gb_format; ++ unsigned int bpp; ++}; ++ ++/* GB format to media code map */ ++static const struct gb_camera_fmt_info gb_fmt_info[] = { ++ { ++ .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, ++ .gb_format = 0x01, ++ .bpp = 16, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_NV12_1x8, ++ .gb_format = 0x12, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_NV21_1x8, ++ .gb_format = 0x13, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_YU12_1x8, ++ .gb_format = 0x16, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_YV12_1x8, ++ .gb_format = 0x17, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, ++ .gb_format = 0x40, ++ .bpp = 0, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_GB_CAM_METADATA_1X8, ++ .gb_format = 0x41, ++ .bpp = 0, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_GB_CAM_DEBUG_DATA_1X8, ++ .gb_format = 0x42, ++ .bpp = 0, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10, ++ .gb_format = 0x80, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10, ++ .gb_format = 0x81, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, ++ .gb_format = 0x82, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10, ++ .gb_format = 0x83, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12, ++ .gb_format = 0x84, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12, ++ .gb_format = 0x85, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, ++ .gb_format = 0x86, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12, ++ .gb_format = 0x87, ++ .bpp = 12, ++ }, ++}; ++ ++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 ++ ++#define GB_CAMERA_MAX_SETTINGS_SIZE 8192 ++ ++#define gcam_dbg(gcam, format...) dev_dbg(&gcam->bundle->dev, format) ++#define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) ++#define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) ++ ++static int gb_camera_operation_sync_flags(struct gb_connection *connection, ++ int type, unsigned int flags, ++ void *request, size_t request_size, ++ void *response, size_t *response_size) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ operation = gb_operation_create_flags(connection, type, request_size, ++ *response_size, flags, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: synchronous operation of type 0x%02x failed: %d\n", ++ connection->name, type, ret); ++ } else { ++ *response_size = operation->response->payload_size; ++ ++ if (operation->response->payload_size) ++ memcpy(response, operation->response->payload, ++ operation->response->payload_size); ++ } ++ ++ gb_operation_put(operation); ++ ++ 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; ++} ++ ++/* ++ * Validate the stream configuration response verifying padding is correctly ++ * set and the returned number of streams is supported ++ */ ++static const int gb_camera_configure_streams_validate_response( ++ struct gb_camera *gcam, ++ struct gb_camera_configure_streams_response *resp, ++ unsigned int nstreams) ++{ ++ unsigned int i; ++ ++ /* Validate the returned response structure */ ++ if (resp->padding[0] || resp->padding[1]) { ++ gcam_err(gcam, "response padding != 0\n"); ++ return -EIO; ++ } ++ ++ if (resp->num_streams > nstreams) { ++ gcam_err(gcam, "got #streams %u > request %u\n", ++ resp->num_streams, nstreams); ++ return -EIO; ++ } ++ ++ for (i = 0; i < 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; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Hardware Configuration ++ */ ++ ++static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, ++ bool hs) ++{ ++ struct gb_svc *svc = gcam->connection->hd->svc; ++ int ret; ++ ++ if (hs) ++ ret = gb_svc_intf_set_power_mode(svc, intf_id, ++ GB_SVC_UNIPRO_HS_SERIES_A, ++ GB_SVC_UNIPRO_FAST_MODE, 2, 2, ++ GB_SVC_SMALL_AMPLITUDE, ++ GB_SVC_NO_DE_EMPHASIS, ++ GB_SVC_UNIPRO_FAST_MODE, 2, 2, ++ GB_SVC_PWRM_RXTERMINATION | ++ GB_SVC_PWRM_TXTERMINATION, 0, ++ NULL, NULL); ++ else ++ ret = gb_svc_intf_set_power_mode(svc, intf_id, ++ GB_SVC_UNIPRO_HS_SERIES_A, ++ GB_SVC_UNIPRO_SLOW_AUTO_MODE, ++ 2, 1, ++ GB_SVC_SMALL_AMPLITUDE, ++ GB_SVC_NO_DE_EMPHASIS, ++ GB_SVC_UNIPRO_SLOW_AUTO_MODE, ++ 2, 1, ++ 0, 0, ++ NULL, NULL); ++ ++ return ret; ++} ++ ++static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) ++{ ++ struct gb_interface *intf = gcam->connection->intf; ++ struct gb_svc *svc = gcam->connection->hd->svc; ++ int ret; ++ ++ ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs); ++ if (ret < 0) { ++ gcam_err(gcam, "failed to set module interface to %s (%d)\n", ++ hs ? "HS" : "PWM", ret); ++ return ret; ++ } ++ ++ ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); ++ if (ret < 0) { ++ gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs); ++ gcam_err(gcam, "failed to set AP interface to %s (%d)\n", ++ hs ? "HS" : "PWM", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++struct ap_csi_config_request { ++ __u8 csi_id; ++ __u8 flags; ++#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 ++ __u8 num_lanes; ++ __u8 padding; ++ __le32 csi_clk_freq; ++ __le32 max_pkt_size; ++} __packed; ++ ++/* ++ * TODO: Compute the number of lanes dynamically based on bandwidth ++ * requirements. ++ */ ++#define GB_CAMERA_CSI_NUM_DATA_LANES 4 ++ ++#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, ++ 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; ++ ++ /* ++ * Create the data connection between the camera module data CPort and ++ * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. ++ */ ++ conn = gb_connection_create_offloaded(gcam->bundle, gcam->data_cport_id, ++ GB_CONNECTION_FLAG_NO_FLOWCTRL | ++ GB_CONNECTION_FLAG_CDSI1); ++ if (IS_ERR(conn)) ++ return PTR_ERR(conn); ++ ++ gcam->data_connection = conn; ++ gb_connection_set_data(conn, gcam); ++ ++ ret = gb_connection_enable(conn); ++ if (ret) ++ goto error_conn_destroy; ++ ++ /* Set the UniPro link to high speed mode. */ ++ ret = gb_camera_set_power_mode(gcam, true); ++ if (ret < 0) ++ goto error_conn_disable; ++ ++ /* ++ * Configure the APB-A CSI-2 transmitter. ++ * ++ * 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; ++ ++ 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; ++ } ++ ++ return 0; ++ ++error_power: ++ gb_camera_set_power_mode(gcam, false); ++error_conn_disable: ++ gb_connection_disable(gcam->data_connection); ++error_conn_destroy: ++ gb_connection_destroy(gcam->data_connection); ++ gcam->data_connection = NULL; ++ return ret; ++} ++ ++static void gb_camera_teardown_data_connection(struct gb_camera *gcam) ++{ ++ struct ap_csi_config_request csi_cfg; ++ int ret; ++ ++ /* Stop the APB1 CSI transmitter. */ ++ memset(&csi_cfg, 0, sizeof(csi_cfg)); ++ csi_cfg.csi_id = 1; ++ ++ 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 stop the CSI transmitter\n"); ++ ++ /* Set the UniPro link to low speed mode. */ ++ gb_camera_set_power_mode(gcam, false); ++ ++ /* Destroy the data connection. */ ++ gb_connection_disable(gcam->data_connection); ++ gb_connection_destroy(gcam->data_connection); ++ gcam->data_connection = NULL; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Camera Protocol Operations ++ */ ++ ++static int gb_camera_capabilities(struct gb_camera *gcam, ++ u8 *capabilities, size_t *size) ++{ ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(gcam->bundle); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&gcam->mutex); ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_camera_operation_sync_flags(gcam->connection, ++ GB_CAMERA_TYPE_CAPABILITIES, ++ GB_OPERATION_FLAG_SHORT_RESPONSE, ++ NULL, 0, ++ (void *)capabilities, size); ++ if (ret) ++ gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); ++ ++done: ++ mutex_unlock(&gcam->mutex); ++ ++ gb_pm_runtime_put_autosuspend(gcam->bundle); ++ ++ return ret; ++} ++ ++static int gb_camera_configure_streams(struct gb_camera *gcam, ++ unsigned int *num_streams, ++ unsigned int *flags, ++ struct gb_camera_stream_config *streams, ++ struct gb_camera_csi_params *csi_params) ++{ ++ struct gb_camera_configure_streams_request *req; ++ struct gb_camera_configure_streams_response *resp; ++ unsigned int nstreams = *num_streams; ++ unsigned int i; ++ size_t req_size; ++ size_t resp_size; ++ int ret; ++ ++ if (nstreams > GB_CAMERA_MAX_STREAMS) ++ return -EINVAL; ++ ++ req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); ++ resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); ++ ++ req = kmalloc(req_size, GFP_KERNEL); ++ resp = kmalloc(resp_size, GFP_KERNEL); ++ if (!req || !resp) { ++ kfree(req); ++ kfree(resp); ++ return -ENOMEM; ++ } ++ ++ req->num_streams = nstreams; ++ req->flags = *flags; ++ req->padding = 0; ++ ++ for (i = 0; i < nstreams; ++i) { ++ struct gb_camera_stream_config_request *cfg = &req->config[i]; ++ ++ cfg->width = cpu_to_le16(streams[i].width); ++ cfg->height = cpu_to_le16(streams[i].height); ++ cfg->format = cpu_to_le16(streams[i].format); ++ cfg->padding = 0; ++ } ++ ++ mutex_lock(&gcam->mutex); ++ ++ ret = gb_pm_runtime_get_sync(gcam->bundle); ++ if (ret) ++ goto done_skip_pm_put; ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_camera_operation_sync_flags(gcam->connection, ++ GB_CAMERA_TYPE_CONFIGURE_STREAMS, ++ GB_OPERATION_FLAG_SHORT_RESPONSE, ++ req, req_size, ++ resp, &resp_size); ++ if (ret < 0) ++ goto done; ++ ++ ret = gb_camera_configure_streams_validate_response(gcam, resp, ++ nstreams); ++ if (ret < 0) ++ goto done; ++ ++ *flags = resp->flags; ++ *num_streams = resp->num_streams; ++ ++ for (i = 0; i < resp->num_streams; ++i) { ++ struct gb_camera_stream_config_response *cfg = &resp->config[i]; ++ ++ streams[i].width = le16_to_cpu(cfg->width); ++ streams[i].height = le16_to_cpu(cfg->height); ++ streams[i].format = le16_to_cpu(cfg->format); ++ streams[i].vc = cfg->virtual_channel; ++ 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 ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || ++ (req->flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) ++ goto done; ++ ++ if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { ++ gb_camera_teardown_data_connection(gcam); ++ gcam->state = GB_CAMERA_STATE_UNCONFIGURED; ++ ++ /* ++ * When unconfiguring streams release the PM runtime reference ++ * that was acquired when streams were configured. The bundle ++ * won't be suspended until the PM runtime reference acquired at ++ * the beginning of this function gets released right before ++ * returning. ++ */ ++ gb_pm_runtime_put_noidle(gcam->bundle); ++ } ++ ++ 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); ++ ++ /* 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); ++ ++done_skip_pm_put: ++ mutex_unlock(&gcam->mutex); ++ kfree(req); ++ kfree(resp); ++ return ret; ++} ++ ++static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, ++ unsigned int streams, unsigned int num_frames, ++ size_t settings_size, const void *settings) ++{ ++ struct gb_camera_capture_request *req; ++ size_t req_size; ++ int ret; ++ ++ if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) ++ return -EINVAL; ++ ++ req_size = sizeof(*req) + settings_size; ++ req = kmalloc(req_size, GFP_KERNEL); ++ if (!req) ++ return -ENOMEM; ++ ++ req->request_id = cpu_to_le32(request_id); ++ req->streams = streams; ++ req->padding = 0; ++ req->num_frames = cpu_to_le16(num_frames); ++ memcpy(req->settings, settings, settings_size); ++ ++ mutex_lock(&gcam->mutex); ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, ++ req, req_size, NULL, 0); ++done: ++ mutex_unlock(&gcam->mutex); ++ ++ kfree(req); ++ ++ return ret; ++} ++ ++static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) ++{ ++ struct gb_camera_flush_response resp; ++ int ret; ++ ++ mutex_lock(&gcam->mutex); ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, ++ &resp, sizeof(resp)); ++ ++ if (ret < 0) ++ goto done; ++ ++ if (request_id) ++ *request_id = le32_to_cpu(resp.request_id); ++ ++done: ++ mutex_unlock(&gcam->mutex); ++ ++ return ret; ++} ++ ++static int gb_camera_request_handler(struct gb_operation *op) ++{ ++ struct gb_camera *gcam = gb_connection_get_data(op->connection); ++ struct gb_camera_metadata_request *payload; ++ struct gb_message *request; ++ ++ if (op->type != GB_CAMERA_TYPE_METADATA) { ++ gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type); ++ return -EINVAL; ++ } ++ ++ request = op->request; ++ ++ if (request->payload_size < sizeof(*payload)) { ++ gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", ++ request->payload_size, sizeof(*payload)); ++ return -EINVAL; ++ } ++ ++ payload = request->payload; ++ ++ gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", ++ payload->request_id, payload->frame_number, payload->stream); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interface with HOST gmp camera. ++ */ ++static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { ++ if (gb_fmt_info[i].mbus_code == mbus_code) ++ return gb_fmt_info[i].gb_format; ++ } ++ return gb_fmt_info[0].gb_format; ++} ++ ++static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(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].mbus_code; ++ } ++ return gb_fmt_info[0].mbus_code; ++} ++ ++static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) ++{ ++ struct gb_camera *gcam = priv; ++ size_t capabilities_len = len; ++ int ret; ++ ++ ret = gb_camera_capabilities(gcam, data, &capabilities_len); ++ if (ret) ++ return ret; ++ ++ return capabilities_len; ++} ++ ++static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, ++ unsigned int *flags, struct gb_camera_stream *streams, ++ struct gb_camera_csi_params *csi_params) ++{ ++ struct gb_camera *gcam = priv; ++ struct gb_camera_stream_config *gb_streams; ++ unsigned int gb_flags = 0; ++ unsigned int gb_nstreams = *nstreams; ++ unsigned int i; ++ int ret; ++ ++ if (gb_nstreams > GB_CAMERA_MAX_STREAMS) ++ return -EINVAL; ++ ++ gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); ++ if (!gb_streams) ++ return -ENOMEM; ++ ++ for (i = 0; i < gb_nstreams; i++) { ++ gb_streams[i].width = streams[i].width; ++ gb_streams[i].height = streams[i].height; ++ gb_streams[i].format = ++ gb_camera_mbus_to_gb(streams[i].pixel_code); ++ } ++ ++ if (*flags & GB_CAMERA_IN_FLAG_TEST) ++ gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; ++ ++ ret = gb_camera_configure_streams(gcam, &gb_nstreams, ++ &gb_flags, gb_streams, csi_params); ++ if (ret < 0) ++ goto done; ++ if (gb_nstreams > *nstreams) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ *flags = 0; ++ if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) ++ *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; ++ ++ for (i = 0; i < gb_nstreams; i++) { ++ streams[i].width = gb_streams[i].width; ++ streams[i].height = gb_streams[i].height; ++ streams[i].vc = gb_streams[i].vc; ++ streams[i].dt[0] = gb_streams[i].dt[0]; ++ streams[i].dt[1] = gb_streams[i].dt[1]; ++ streams[i].max_size = gb_streams[i].max_size; ++ streams[i].pixel_code = ++ gb_camera_gb_to_mbus(gb_streams[i].format); ++ } ++ *nstreams = gb_nstreams; ++ ++done: ++ kfree(gb_streams); ++ return ret; ++} ++ ++static int gb_camera_op_capture(void *priv, u32 request_id, ++ unsigned int streams, unsigned int num_frames, ++ size_t settings_size, const void *settings) ++{ ++ struct gb_camera *gcam = priv; ++ ++ return gb_camera_capture(gcam, request_id, streams, num_frames, ++ settings_size, settings); ++} ++ ++static int gb_camera_op_flush(void *priv, u32 *request_id) ++{ ++ struct gb_camera *gcam = priv; ++ ++ return gb_camera_flush(gcam, request_id); ++} ++ ++static const struct gb_camera_ops gb_cam_ops = { ++ .capabilities = gb_camera_op_capabilities, ++ .configure_streams = gb_camera_op_configure_streams, ++ .capture = gb_camera_op_capture, ++ .flush = gb_camera_op_flush, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * DebugFS ++ */ ++ ++static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ struct gb_camera_debugfs_buffer *buffer = ++ &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES]; ++ size_t size = 1024; ++ unsigned int i; ++ u8 *caps; ++ int ret; ++ ++ caps = kmalloc(size, GFP_KERNEL); ++ if (!caps) ++ return -ENOMEM; ++ ++ ret = gb_camera_capabilities(gcam, caps, &size); ++ if (ret < 0) ++ goto done; ++ ++ /* ++ * hex_dump_to_buffer() doesn't return the number of bytes dumped prior ++ * to v4.0, we need our own implementation :-( ++ */ ++ buffer->length = 0; ++ ++ for (i = 0; i < size; i += 16) { ++ unsigned int nbytes = min_t(unsigned int, size - i, 16); ++ ++ buffer->length += sprintf(buffer->data + buffer->length, ++ "%*ph\n", nbytes, caps + i); ++ } ++ ++done: ++ kfree(caps); ++ return ret; ++} ++ ++static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ struct gb_camera_debugfs_buffer *buffer = ++ &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; ++ struct gb_camera_stream_config *streams; ++ unsigned int nstreams; ++ unsigned int flags; ++ unsigned int i; ++ char *token; ++ int ret; ++ ++ /* Retrieve number of streams to configure */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ++ ret = kstrtouint(token, 10, &nstreams); ++ if (ret < 0) ++ return ret; ++ ++ if (nstreams > GB_CAMERA_MAX_STREAMS) ++ return -EINVAL; ++ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ++ ret = kstrtouint(token, 10, &flags); ++ if (ret < 0) ++ return ret; ++ ++ /* For each stream to configure parse width, height and format */ ++ streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); ++ if (!streams) ++ return -ENOMEM; ++ ++ for (i = 0; i < nstreams; ++i) { ++ struct gb_camera_stream_config *stream = &streams[i]; ++ ++ /* width */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ret = kstrtouint(token, 10, &stream->width); ++ if (ret < 0) ++ goto done; ++ ++ /* height */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ goto done; ++ ++ ret = kstrtouint(token, 10, &stream->height); ++ if (ret < 0) ++ goto done; ++ ++ /* Image format code */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ goto done; ++ ++ ret = kstrtouint(token, 16, &stream->format); ++ if (ret < 0) ++ goto done; ++ } ++ ++ ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams, ++ NULL); ++ if (ret < 0) ++ goto done; ++ ++ buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); ++ ++ for (i = 0; i < nstreams; ++i) { ++ struct gb_camera_stream_config *stream = &streams[i]; ++ ++ buffer->length += sprintf(buffer->data + buffer->length, ++ "%u;%u;%u;%u;%u;%u;%u;", ++ stream->width, stream->height, ++ stream->format, stream->vc, ++ stream->dt[0], stream->dt[1], ++ stream->max_size); ++ } ++ ++ ret = len; ++ ++done: ++ kfree(streams); ++ return ret; ++}; ++ ++static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ unsigned int request_id; ++ unsigned int streams_mask; ++ unsigned int num_frames; ++ char *token; ++ int ret; ++ ++ /* Request id */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ret = kstrtouint(token, 10, &request_id); ++ if (ret < 0) ++ return ret; ++ ++ /* Stream mask */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ret = kstrtouint(token, 16, &streams_mask); ++ if (ret < 0) ++ return ret; ++ ++ /* number of frames */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ret = kstrtouint(token, 10, &num_frames); ++ if (ret < 0) ++ return ret; ++ ++ ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, ++ NULL); ++ if (ret < 0) ++ return ret; ++ ++ return len; ++} ++ ++static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ struct gb_camera_debugfs_buffer *buffer = ++ &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; ++ unsigned int req_id; ++ int ret; ++ ++ ret = gb_camera_flush(gcam, &req_id); ++ if (ret < 0) ++ return ret; ++ ++ buffer->length = sprintf(buffer->data, "%u", req_id); ++ ++ return len; ++} ++ ++struct gb_camera_debugfs_entry { ++ const char *name; ++ unsigned int mask; ++ unsigned int buffer; ++ ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); ++}; ++ ++static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { ++ { ++ .name = "capabilities", ++ .mask = S_IFREG | S_IRUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, ++ .execute = gb_camera_debugfs_capabilities, ++ }, { ++ .name = "configure_streams", ++ .mask = S_IFREG | S_IRUGO | S_IWUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, ++ .execute = gb_camera_debugfs_configure_streams, ++ }, { ++ .name = "capture", ++ .mask = S_IFREG | S_IRUGO | S_IWUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, ++ .execute = gb_camera_debugfs_capture, ++ }, { ++ .name = "flush", ++ .mask = S_IFREG | S_IRUGO | S_IWUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, ++ .execute = gb_camera_debugfs_flush, ++ }, ++}; ++ ++static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ const struct gb_camera_debugfs_entry *op = file->private_data; ++ struct gb_camera *gcam = file->f_inode->i_private; ++ struct gb_camera_debugfs_buffer *buffer; ++ ssize_t ret; ++ ++ /* For read-only entries the operation is triggered by a read. */ ++ if (!(op->mask & S_IWUGO)) { ++ ret = op->execute(gcam, NULL, 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ buffer = &gcam->debugfs.buffers[op->buffer]; ++ ++ return simple_read_from_buffer(buf, len, offset, buffer->data, ++ buffer->length); ++} ++ ++static ssize_t gb_camera_debugfs_write(struct file *file, ++ const char __user *buf, size_t len, ++ loff_t *offset) ++{ ++ const struct gb_camera_debugfs_entry *op = file->private_data; ++ struct gb_camera *gcam = file->f_inode->i_private; ++ ssize_t ret; ++ char *kbuf; ++ ++ if (len > 1024) ++ return -EINVAL; ++ ++ kbuf = kmalloc(len + 1, GFP_KERNEL); ++ if (kbuf == NULL) ++ return -ENOMEM; ++ ++ if (copy_from_user(kbuf, buf, len)) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ kbuf[len] = '\0'; ++ ++ ret = op->execute(gcam, kbuf, len); ++ ++done: ++ kfree(kbuf); ++ return ret; ++} ++ ++static int gb_camera_debugfs_open(struct inode *inode, struct file *file) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { ++ const struct gb_camera_debugfs_entry *entry = ++ &gb_camera_debugfs_entries[i]; ++ ++ if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { ++ file->private_data = (void *)entry; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct file_operations gb_camera_debugfs_ops = { ++ .open = gb_camera_debugfs_open, ++ .read = gb_camera_debugfs_read, ++ .write = gb_camera_debugfs_write, ++}; ++ ++static int gb_camera_debugfs_init(struct gb_camera *gcam) ++{ ++ struct gb_connection *connection = gcam->connection; ++ char dirname[27]; ++ unsigned int i; ++ ++ /* ++ * Create root debugfs entry and a file entry for each camera operation. ++ */ ++ snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, ++ gcam->bundle->id); ++ ++ gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); ++ if (IS_ERR(gcam->debugfs.root)) { ++ gcam_err(gcam, "debugfs root create failed (%ld)\n", ++ PTR_ERR(gcam->debugfs.root)); ++ return PTR_ERR(gcam->debugfs.root); ++ } ++ ++ gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * ++ GB_CAMERA_DEBUGFS_BUFFER_MAX); ++ if (!gcam->debugfs.buffers) ++ return -ENOMEM; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { ++ const struct gb_camera_debugfs_entry *entry = ++ &gb_camera_debugfs_entries[i]; ++ struct dentry *dentry; ++ ++ gcam->debugfs.buffers[i].length = 0; ++ ++ dentry = debugfs_create_file(entry->name, entry->mask, ++ gcam->debugfs.root, gcam, ++ &gb_camera_debugfs_ops); ++ if (IS_ERR(dentry)) { ++ gcam_err(gcam, ++ "debugfs operation %s create failed (%ld)\n", ++ entry->name, PTR_ERR(dentry)); ++ return PTR_ERR(dentry); ++ } ++ } ++ ++ return 0; ++} ++ ++static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) ++{ ++ debugfs_remove_recursive(gcam->debugfs.root); ++ ++ vfree(gcam->debugfs.buffers); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Init & Cleanup ++ */ ++ ++static void gb_camera_cleanup(struct gb_camera *gcam) ++{ ++ gb_camera_debugfs_cleanup(gcam); ++ ++ mutex_lock(&gcam->mutex); ++ if (gcam->data_connection) { ++ gb_connection_disable(gcam->data_connection); ++ gb_connection_destroy(gcam->data_connection); ++ gcam->data_connection = NULL; ++ } ++ ++ if (gcam->connection) { ++ gb_connection_disable(gcam->connection); ++ gb_connection_destroy(gcam->connection); ++ gcam->connection = NULL; ++ } ++ mutex_unlock(&gcam->mutex); ++} ++ ++static void gb_camera_release_module(struct kref *ref) ++{ ++ struct gb_camera_module *cam_mod = ++ container_of(ref, struct gb_camera_module, refcount); ++ kfree(cam_mod->priv); ++} ++ ++static int gb_camera_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct gb_connection *conn; ++ struct gb_camera *gcam; ++ u16 mgmt_cport_id = 0; ++ u16 data_cport_id = 0; ++ unsigned int i; ++ int ret; ++ ++ /* ++ * The camera bundle must contain exactly two CPorts, one for the ++ * camera management protocol and one for the camera data protocol. ++ */ ++ if (bundle->num_cports != 2) ++ return -ENODEV; ++ ++ for (i = 0; i < bundle->num_cports; ++i) { ++ struct greybus_descriptor_cport *desc = &bundle->cport_desc[i]; ++ ++ switch (desc->protocol_id) { ++ case GREYBUS_PROTOCOL_CAMERA_MGMT: ++ mgmt_cport_id = le16_to_cpu(desc->id); ++ break; ++ case GREYBUS_PROTOCOL_CAMERA_DATA: ++ data_cport_id = le16_to_cpu(desc->id); ++ break; ++ default: ++ return -ENODEV; ++ } ++ } ++ ++ if (!mgmt_cport_id || !data_cport_id) ++ return -ENODEV; ++ ++ gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); ++ if (!gcam) ++ return -ENOMEM; ++ ++ mutex_init(&gcam->mutex); ++ ++ gcam->bundle = bundle; ++ gcam->state = GB_CAMERA_STATE_UNCONFIGURED; ++ gcam->data_cport_id = data_cport_id; ++ ++ conn = gb_connection_create(bundle, mgmt_cport_id, ++ gb_camera_request_handler); ++ if (IS_ERR(conn)) { ++ ret = PTR_ERR(conn); ++ goto error; ++ } ++ ++ gcam->connection = conn; ++ gb_connection_set_data(conn, gcam); ++ ++ ret = gb_connection_enable(conn); ++ if (ret) ++ goto error; ++ ++ ret = gb_camera_debugfs_init(gcam); ++ if (ret < 0) ++ goto error; ++ ++ gcam->module.priv = gcam; ++ gcam->module.ops = &gb_cam_ops; ++ gcam->module.interface_id = gcam->connection->intf->interface_id; ++ gcam->module.release = gb_camera_release_module; ++ ret = gb_camera_register(&gcam->module); ++ if (ret < 0) ++ goto error; ++ ++ greybus_set_drvdata(bundle, gcam); ++ ++ gb_pm_runtime_put_autosuspend(gcam->bundle); ++ ++ return 0; ++ ++error: ++ gb_camera_cleanup(gcam); ++ kfree(gcam); ++ return ret; ++} ++ ++static void gb_camera_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_camera *gcam = greybus_get_drvdata(bundle); ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ gb_camera_cleanup(gcam); ++ gb_camera_unregister(&gcam->module); ++} ++ ++static const struct greybus_bundle_id gb_camera_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, ++ { }, ++}; ++ ++#ifdef CONFIG_PM ++static int gb_camera_suspend(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gb_camera *gcam = greybus_get_drvdata(bundle); ++ ++ if (gcam->data_connection) ++ gb_connection_disable(gcam->data_connection); ++ ++ gb_connection_disable(gcam->connection); ++ ++ return 0; ++} ++ ++static int gb_camera_resume(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gb_camera *gcam = greybus_get_drvdata(bundle); ++ int ret; ++ ++ ret = gb_connection_enable(gcam->connection); ++ if (ret) { ++ gcam_err(gcam, "failed to enable connection: %d\n", ret); ++ return ret; ++ } ++ ++ if (gcam->data_connection) { ++ ret = gb_connection_enable(gcam->data_connection); ++ if (ret) { ++ gcam_err(gcam, ++ "failed to enable data connection: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops gb_camera_pm_ops = { ++ SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL) ++}; ++ ++static struct greybus_driver gb_camera_driver = { ++ .name = "camera", ++ .probe = gb_camera_probe, ++ .disconnect = gb_camera_disconnect, ++ .id_table = gb_camera_id_table, ++ .driver.pm = &gb_camera_pm_ops, ++}; ++ ++module_greybus_driver(gb_camera_driver); ++ ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/greybus/gb-camera.h +@@ -0,0 +1,127 @@ ++/* ++ * Greybus Camera protocol driver. ++ * ++ * Copyright 2015 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++#ifndef __GB_CAMERA_H ++#define __GB_CAMERA_H ++ ++#include <linux/v4l2-mediabus.h> ++ ++/* Input flags need to be set from the caller */ ++#define GB_CAMERA_IN_FLAG_TEST (1 << 0) ++/* Output flags returned */ ++#define GB_CAMERA_OUT_FLAG_ADJUSTED (1 << 0) ++ ++/** ++ * struct gb_camera_stream - Represents greybus camera stream. ++ * @width: Stream width in pixels. ++ * @height: Stream height in pixels. ++ * @pixel_code: Media bus pixel code. ++ * @vc: MIPI CSI virtual channel. ++ * @dt: MIPI CSI data types. Most formats use a single data type, in which case ++ * the second element will be ignored. ++ * @max_size: Maximum size of a frame in bytes. The camera module guarantees ++ * that all data between the Frame Start and Frame End packet for ++ * the associated virtual channel and data type(s) will not exceed ++ * this size. ++ */ ++struct gb_camera_stream { ++ unsigned int width; ++ unsigned int height; ++ enum v4l2_mbus_pixelcode pixel_code; ++ unsigned int vc; ++ unsigned int dt[2]; ++ unsigned int max_size; ++}; ++ ++/** ++ * struct gb_camera_csi_params - CSI configuration parameters ++ * @num_lanes: number of CSI data lanes ++ * @clk_freq: CSI clock frequency in Hz ++ */ ++struct gb_camera_csi_params { ++ unsigned int num_lanes; ++ unsigned int clk_freq; ++}; ++ ++/** ++ * struct gb_camera_ops - Greybus camera operations, used by the Greybus camera ++ * driver to expose operations to the host camera driver. ++ * @capabilities: Retrieve camera capabilities and store them in the buffer ++ * 'buf' capabilities. The buffer maximum size is specified by ++ * the caller in the 'size' parameter, and the effective ++ * capabilities size is returned from the function. If the buffer ++ * size is too small to hold the capabilities an error is ++ * returned and the buffer is left untouched. ++ * ++ * @configure_streams: Negotiate configuration and prepare the module for video ++ * capture. The caller specifies the number of streams it ++ * requests in the 'nstreams' argument and the associated ++ * streams configurations in the 'streams' argument. The ++ * GB_CAMERA_IN_FLAG_TEST 'flag' can be set to test a ++ * configuration without applying it, otherwise the ++ * configuration is applied by the module. The module can ++ * decide to modify the requested configuration, including ++ * using a different number of streams. In that case the ++ * modified configuration won't be applied, the ++ * GB_CAMERA_OUT_FLAG_ADJUSTED 'flag' will be set upon ++ * return, and the modified configuration and number of ++ * streams stored in 'streams' and 'array'. The module ++ * returns its CSI-2 bus parameters in the 'csi_params' ++ * structure in all cases. ++ * ++ * @capture: Submit a capture request. The supplied 'request_id' must be unique ++ * and higher than the IDs of all the previously submitted requests. ++ * The 'streams' argument specifies which streams are affected by the ++ * request in the form of a bitmask, with bits corresponding to the ++ * configured streams indexes. If the request contains settings, the ++ * 'settings' argument points to the settings buffer and its size is ++ * specified by the 'settings_size' argument. Otherwise the 'settings' ++ * argument should be set to NULL and 'settings_size' to 0. ++ * ++ * @flush: Flush the capture requests queue. Return the ID of the last request ++ * that will processed by the device before it stops transmitting video ++ * frames. All queued capture requests with IDs higher than the returned ++ * ID will be dropped without being processed. ++ */ ++struct gb_camera_ops { ++ ssize_t (*capabilities)(void *priv, char *buf, size_t len); ++ int (*configure_streams)(void *priv, unsigned int *nstreams, ++ unsigned int *flags, struct gb_camera_stream *streams, ++ struct gb_camera_csi_params *csi_params); ++ int (*capture)(void *priv, u32 request_id, ++ unsigned int streams, unsigned int num_frames, ++ size_t settings_size, const void *settings); ++ int (*flush)(void *priv, u32 *request_id); ++}; ++ ++/** ++ * struct gb_camera_module - Represents greybus camera module. ++ * @priv: Module private data, passed to all camera operations. ++ * @ops: Greybus camera operation callbacks. ++ * @interface_id: Interface id of the module. ++ * @refcount: Reference counting object. ++ * @release: Module release function. ++ * @list: List entry in the camera modules list. ++ */ ++struct gb_camera_module { ++ void *priv; ++ const struct gb_camera_ops *ops; ++ ++ unsigned int interface_id; ++ struct kref refcount; ++ void (*release)(struct kref *kref); ++ struct list_head list; /* Global list */ ++}; ++ ++#define gb_camera_call(f, op, args...) \ ++ (!(f) ? -ENODEV : (((f)->ops->op) ? \ ++ (f)->ops->op((f)->priv, ##args) : -ENOIOCTLCMD)) ++ ++int gb_camera_register(struct gb_camera_module *module); ++int gb_camera_unregister(struct gb_camera_module *module); ++ ++#endif /* __GB_CAMERA_H */ |
