13265edafSLaurent Pinchart /* 23265edafSLaurent Pinchart * Greybus Camera protocol driver. 33265edafSLaurent Pinchart * 43265edafSLaurent Pinchart * Copyright 2015 Google Inc. 53265edafSLaurent Pinchart * Copyright 2015 Linaro Ltd. 63265edafSLaurent Pinchart * 73265edafSLaurent Pinchart * Released under the GPLv2 only. 83265edafSLaurent Pinchart */ 93265edafSLaurent Pinchart 103265edafSLaurent Pinchart #include <linux/debugfs.h> 113265edafSLaurent Pinchart #include <linux/fs.h> 123265edafSLaurent Pinchart #include <linux/kernel.h> 133265edafSLaurent Pinchart #include <linux/module.h> 143265edafSLaurent Pinchart #include <linux/slab.h> 153265edafSLaurent Pinchart #include <linux/string.h> 163265edafSLaurent Pinchart #include <linux/uaccess.h> 173265edafSLaurent Pinchart #include <linux/vmalloc.h> 183265edafSLaurent Pinchart 19142b21feSLaurent Pinchart #include "es2.h" 203265edafSLaurent Pinchart #include "greybus.h" 213265edafSLaurent Pinchart #include "greybus_protocols.h" 223265edafSLaurent Pinchart 233265edafSLaurent Pinchart enum gb_camera_debugs_buffer_id { 243265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 253265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 263265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 273265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 283265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX, 293265edafSLaurent Pinchart }; 303265edafSLaurent Pinchart 313265edafSLaurent Pinchart struct gb_camera_debugfs_buffer { 323265edafSLaurent Pinchart char data[PAGE_SIZE]; 333265edafSLaurent Pinchart size_t length; 343265edafSLaurent Pinchart }; 353265edafSLaurent Pinchart 363265edafSLaurent Pinchart /** 373265edafSLaurent Pinchart * struct gb_camera - A Greybus Camera Device 383265edafSLaurent Pinchart * @connection: the greybus connection for camera control 393265edafSLaurent Pinchart * @data_connected: whether the data connection has been established 403265edafSLaurent Pinchart * @debugfs: debugfs entries for camera protocol operations testing 413265edafSLaurent Pinchart */ 423265edafSLaurent Pinchart struct gb_camera { 433265edafSLaurent Pinchart struct gb_connection *connection; 443265edafSLaurent Pinchart bool data_connected; 453265edafSLaurent Pinchart 463265edafSLaurent Pinchart struct { 473265edafSLaurent Pinchart struct dentry *root; 483265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffers; 493265edafSLaurent Pinchart } debugfs; 503265edafSLaurent Pinchart }; 513265edafSLaurent Pinchart 523265edafSLaurent Pinchart struct gb_camera_stream_config { 533265edafSLaurent Pinchart unsigned int width; 543265edafSLaurent Pinchart unsigned int height; 553265edafSLaurent Pinchart unsigned int format; 563265edafSLaurent Pinchart unsigned int vc; 573265edafSLaurent Pinchart unsigned int dt[2]; 583265edafSLaurent Pinchart unsigned int max_size; 593265edafSLaurent Pinchart }; 603265edafSLaurent Pinchart 613265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT 16 623265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT 17 633265edafSLaurent Pinchart 643265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE 8192 653265edafSLaurent Pinchart 663265edafSLaurent Pinchart #define gcam_dbg(gcam, format...) \ 673265edafSLaurent Pinchart dev_dbg(&gcam->connection->bundle->dev, format) 683265edafSLaurent Pinchart #define gcam_info(gcam, format...) \ 693265edafSLaurent Pinchart dev_info(&gcam->connection->bundle->dev, format) 703265edafSLaurent Pinchart #define gcam_err(gcam, format...) \ 713265edafSLaurent Pinchart dev_err(&gcam->connection->bundle->dev, format) 723265edafSLaurent Pinchart 733265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 743265edafSLaurent Pinchart * Camera Protocol Operations 753265edafSLaurent Pinchart */ 763265edafSLaurent Pinchart 773265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam, 783265edafSLaurent Pinchart unsigned int nstreams, 793265edafSLaurent Pinchart struct gb_camera_stream_config *streams) 803265edafSLaurent Pinchart { 813265edafSLaurent Pinchart struct gb_camera_configure_streams_request *req; 823265edafSLaurent Pinchart struct gb_camera_configure_streams_response *resp; 83142b21feSLaurent Pinchart struct es2_ap_csi_config csi_cfg; 843265edafSLaurent Pinchart unsigned int i; 853265edafSLaurent Pinchart size_t req_size; 863265edafSLaurent Pinchart size_t resp_size; 873265edafSLaurent Pinchart int ret; 883265edafSLaurent Pinchart 893265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 903265edafSLaurent Pinchart return -EINVAL; 913265edafSLaurent Pinchart 923265edafSLaurent Pinchart req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); 933265edafSLaurent Pinchart resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); 943265edafSLaurent Pinchart 953265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 963265edafSLaurent Pinchart resp = kmalloc(resp_size, GFP_KERNEL); 973265edafSLaurent Pinchart if (!req || !resp) { 983265edafSLaurent Pinchart ret = -ENOMEM; 993265edafSLaurent Pinchart goto done; 1003265edafSLaurent Pinchart } 1013265edafSLaurent Pinchart 10298ce3b0aSLaurent Pinchart req->num_streams = cpu_to_le16(nstreams); 1033265edafSLaurent Pinchart req->padding = 0; 1043265edafSLaurent Pinchart 1053265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 1063265edafSLaurent Pinchart struct gb_camera_stream_config_request *cfg = &req->config[i]; 1073265edafSLaurent Pinchart 108c6622216SLaurent Pinchart cfg->width = cpu_to_le16(streams[i].width); 109c6622216SLaurent Pinchart cfg->height = cpu_to_le16(streams[i].height); 110c6622216SLaurent Pinchart cfg->format = cpu_to_le16(streams[i].format); 1113265edafSLaurent Pinchart cfg->padding = 0; 1123265edafSLaurent Pinchart } 1133265edafSLaurent Pinchart 1143265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, 1153265edafSLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 1163265edafSLaurent Pinchart req, req_size, resp, resp_size); 1173265edafSLaurent Pinchart if (ret < 0) 1183265edafSLaurent Pinchart return ret; 1193265edafSLaurent Pinchart 12098ce3b0aSLaurent Pinchart if (le16_to_cpu(resp->num_streams) > nstreams) { 1213265edafSLaurent Pinchart gcam_dbg(gcam, "got #streams %u > request %u\n", 12298ce3b0aSLaurent Pinchart le16_to_cpu(resp->num_streams), nstreams); 1233265edafSLaurent Pinchart ret = -EIO; 1243265edafSLaurent Pinchart goto done; 1253265edafSLaurent Pinchart } 1263265edafSLaurent Pinchart 1273265edafSLaurent Pinchart if (resp->padding != 0) { 1283265edafSLaurent Pinchart gcam_dbg(gcam, "response padding != 0"); 1293265edafSLaurent Pinchart ret = -EIO; 1303265edafSLaurent Pinchart goto done; 1313265edafSLaurent Pinchart } 1323265edafSLaurent Pinchart 1333265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 1343265edafSLaurent Pinchart struct gb_camera_stream_config_response *cfg = &resp->config[i]; 1353265edafSLaurent Pinchart 136c6622216SLaurent Pinchart streams[i].width = le16_to_cpu(cfg->width); 137c6622216SLaurent Pinchart streams[i].height = le16_to_cpu(cfg->height); 138c6622216SLaurent Pinchart streams[i].format = le16_to_cpu(cfg->format); 1393265edafSLaurent Pinchart streams[i].vc = cfg->virtual_channel; 1403265edafSLaurent Pinchart streams[i].dt[0] = cfg->data_type[0]; 1413265edafSLaurent Pinchart streams[i].dt[1] = cfg->data_type[1]; 142c6622216SLaurent Pinchart streams[i].max_size = le32_to_cpu(cfg->max_size); 1433265edafSLaurent Pinchart 1443265edafSLaurent Pinchart if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { 1453265edafSLaurent Pinchart gcam_dbg(gcam, "stream #%u padding != 0", i); 1463265edafSLaurent Pinchart ret = -EIO; 1473265edafSLaurent Pinchart goto done; 1483265edafSLaurent Pinchart } 1493265edafSLaurent Pinchart } 1503265edafSLaurent Pinchart 151142b21feSLaurent Pinchart /* Configure the CSI transmitter. Hardcode the parameters for now. */ 152142b21feSLaurent Pinchart if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) { 153142b21feSLaurent Pinchart csi_cfg.csi_id = 1; 154142b21feSLaurent Pinchart csi_cfg.clock_mode = 0; 155142b21feSLaurent Pinchart csi_cfg.num_lanes = 2; 156142b21feSLaurent Pinchart csi_cfg.bus_freq = 250000000; 157142b21feSLaurent Pinchart 158142b21feSLaurent Pinchart ret = es2_ap_csi_setup(gcam->connection->hd, true, &csi_cfg); 159142b21feSLaurent Pinchart } else if (nstreams == 0) { 160142b21feSLaurent Pinchart csi_cfg.csi_id = 1; 161142b21feSLaurent Pinchart csi_cfg.clock_mode = 0; 162142b21feSLaurent Pinchart csi_cfg.num_lanes = 0; 163142b21feSLaurent Pinchart csi_cfg.bus_freq = 0; 164142b21feSLaurent Pinchart 165142b21feSLaurent Pinchart ret = es2_ap_csi_setup(gcam->connection->hd, false, &csi_cfg); 166142b21feSLaurent Pinchart } 167142b21feSLaurent Pinchart 168142b21feSLaurent Pinchart if (ret < 0) 169142b21feSLaurent Pinchart gcam_err(gcam, "failed to %s the CSI transmitter\n", 170142b21feSLaurent Pinchart nstreams ? "start" : "stop"); 171142b21feSLaurent Pinchart 17298ce3b0aSLaurent Pinchart ret = le16_to_cpu(resp->num_streams); 1733265edafSLaurent Pinchart 1743265edafSLaurent Pinchart done: 1753265edafSLaurent Pinchart kfree(req); 1763265edafSLaurent Pinchart kfree(resp); 1773265edafSLaurent Pinchart return ret; 1783265edafSLaurent Pinchart } 1793265edafSLaurent Pinchart 1803265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, 1813265edafSLaurent Pinchart unsigned int streams, unsigned int num_frames, 1823265edafSLaurent Pinchart size_t settings_size, const void *settings) 1833265edafSLaurent Pinchart { 1843265edafSLaurent Pinchart struct gb_camera_capture_request *req; 1853265edafSLaurent Pinchart size_t req_size; 1863265edafSLaurent Pinchart 1873265edafSLaurent Pinchart if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) 1883265edafSLaurent Pinchart return -EINVAL; 1893265edafSLaurent Pinchart 1903265edafSLaurent Pinchart req_size = sizeof(*req) + settings_size; 1913265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 1923265edafSLaurent Pinchart if (!req) 1933265edafSLaurent Pinchart return -ENOMEM; 1943265edafSLaurent Pinchart 195c6622216SLaurent Pinchart req->request_id = cpu_to_le32(request_id); 1963265edafSLaurent Pinchart req->streams = streams; 1973265edafSLaurent Pinchart req->padding = 0; 198c6622216SLaurent Pinchart req->num_frames = cpu_to_le16(num_frames); 1993265edafSLaurent Pinchart memcpy(req->settings, settings, settings_size); 2003265edafSLaurent Pinchart 2013265edafSLaurent Pinchart return gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, 2023265edafSLaurent Pinchart req, req_size, NULL, 0); 2033265edafSLaurent Pinchart } 2043265edafSLaurent Pinchart 2053265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) 2063265edafSLaurent Pinchart { 2073265edafSLaurent Pinchart struct gb_camera_flush_response resp; 2083265edafSLaurent Pinchart int ret; 2093265edafSLaurent Pinchart 2103265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, 2113265edafSLaurent Pinchart &resp, sizeof(resp)); 2123265edafSLaurent Pinchart if (ret < 0) 2133265edafSLaurent Pinchart return ret; 2143265edafSLaurent Pinchart 2153265edafSLaurent Pinchart if (request_id) 216c6622216SLaurent Pinchart *request_id = le32_to_cpu(resp.request_id); 2173265edafSLaurent Pinchart 2183265edafSLaurent Pinchart return 0; 2193265edafSLaurent Pinchart } 2203265edafSLaurent Pinchart 2213265edafSLaurent Pinchart static int gb_camera_event_recv(u8 type, struct gb_operation *op) 2223265edafSLaurent Pinchart { 2233265edafSLaurent Pinchart struct gb_camera *gcam = op->connection->private; 2243265edafSLaurent Pinchart struct gb_camera_metadata_request *payload; 2253265edafSLaurent Pinchart struct gb_message *request; 2263265edafSLaurent Pinchart 2273265edafSLaurent Pinchart if (type != GB_CAMERA_TYPE_METADATA) { 2283265edafSLaurent Pinchart gcam_err(gcam, "Unsupported unsolicited event: %u\n", type); 2293265edafSLaurent Pinchart return -EINVAL; 2303265edafSLaurent Pinchart } 2313265edafSLaurent Pinchart 2323265edafSLaurent Pinchart request = op->request; 2333265edafSLaurent Pinchart 2343265edafSLaurent Pinchart if (request->payload_size < sizeof(*payload)) { 2353265edafSLaurent Pinchart gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", 2363265edafSLaurent Pinchart request->payload_size, sizeof(*payload)); 2373265edafSLaurent Pinchart return -EINVAL; 2383265edafSLaurent Pinchart } 2393265edafSLaurent Pinchart 2403265edafSLaurent Pinchart payload = request->payload; 2413265edafSLaurent Pinchart 2423265edafSLaurent Pinchart gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", 2433265edafSLaurent Pinchart payload->request_id, payload->frame_number, payload->stream); 2443265edafSLaurent Pinchart 2453265edafSLaurent Pinchart return 0; 2463265edafSLaurent Pinchart } 2473265edafSLaurent Pinchart 2483265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 2493265edafSLaurent Pinchart * DebugFS 2503265edafSLaurent Pinchart */ 2513265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, 2523265edafSLaurent Pinchart char *buf, size_t len) 2533265edafSLaurent Pinchart { 2543265edafSLaurent Pinchart return len; 2553265edafSLaurent Pinchart } 2563265edafSLaurent Pinchart 2573265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, 2583265edafSLaurent Pinchart char *buf, size_t len) 2593265edafSLaurent Pinchart { 2603265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 2613265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; 2623265edafSLaurent Pinchart struct gb_camera_stream_config *streams; 2633265edafSLaurent Pinchart unsigned int nstreams; 2643265edafSLaurent Pinchart const char *sep = ";"; 2653265edafSLaurent Pinchart unsigned int i; 2663265edafSLaurent Pinchart char *token; 2673265edafSLaurent Pinchart int ret; 2683265edafSLaurent Pinchart 2693265edafSLaurent Pinchart /* Retrieve number of streams to configure */ 2703265edafSLaurent Pinchart token = strsep(&buf, sep); 2713265edafSLaurent Pinchart if (token == NULL) 2723265edafSLaurent Pinchart return -EINVAL; 2733265edafSLaurent Pinchart 2743265edafSLaurent Pinchart ret = kstrtouint(token, 10, &nstreams); 2753265edafSLaurent Pinchart if (ret < 0) 2763265edafSLaurent Pinchart return ret; 2773265edafSLaurent Pinchart 2783265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 2793265edafSLaurent Pinchart return -EINVAL; 2803265edafSLaurent Pinchart 2813265edafSLaurent Pinchart /* For each stream to configure parse width, height and format */ 2823265edafSLaurent Pinchart streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); 2833265edafSLaurent Pinchart if (!streams) 2843265edafSLaurent Pinchart return -ENOMEM; 2853265edafSLaurent Pinchart 2863265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 2873265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 2883265edafSLaurent Pinchart 2893265edafSLaurent Pinchart /* width */ 2903265edafSLaurent Pinchart token = strsep(&buf, ";"); 2913265edafSLaurent Pinchart if (token == NULL) { 2923265edafSLaurent Pinchart ret = -EINVAL; 2933265edafSLaurent Pinchart goto done; 2943265edafSLaurent Pinchart } 2953265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->width); 2963265edafSLaurent Pinchart if (ret < 0) 2973265edafSLaurent Pinchart goto done; 2983265edafSLaurent Pinchart 2993265edafSLaurent Pinchart /* height */ 3003265edafSLaurent Pinchart token = strsep(&buf, ";"); 3013265edafSLaurent Pinchart if (token == NULL) 3023265edafSLaurent Pinchart goto done; 3033265edafSLaurent Pinchart 3043265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->height); 3053265edafSLaurent Pinchart if (ret < 0) 3063265edafSLaurent Pinchart goto done; 3073265edafSLaurent Pinchart 3083265edafSLaurent Pinchart /* Image format code */ 3093265edafSLaurent Pinchart token = strsep(&buf, ";"); 3103265edafSLaurent Pinchart if (token == NULL) 3113265edafSLaurent Pinchart goto done; 3123265edafSLaurent Pinchart 3133265edafSLaurent Pinchart ret = kstrtouint(token, 16, &stream->format); 3143265edafSLaurent Pinchart if (ret < 0) 3153265edafSLaurent Pinchart goto done; 3163265edafSLaurent Pinchart } 3173265edafSLaurent Pinchart 3183265edafSLaurent Pinchart ret = gb_camera_configure_streams(gcam, nstreams, streams); 3193265edafSLaurent Pinchart if (ret < 0) 3203265edafSLaurent Pinchart goto done; 3213265edafSLaurent Pinchart 3223265edafSLaurent Pinchart nstreams = ret; 3233265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u;", nstreams); 3243265edafSLaurent Pinchart 3253265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 3263265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 3273265edafSLaurent Pinchart 3283265edafSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 3293265edafSLaurent Pinchart "%u;%u;%u;%u;%u;%u;%u;", 3303265edafSLaurent Pinchart stream->width, stream->height, 3313265edafSLaurent Pinchart stream->format, stream->vc, 3323265edafSLaurent Pinchart stream->dt[0], stream->dt[1], 3333265edafSLaurent Pinchart stream->max_size); 3343265edafSLaurent Pinchart } 3353265edafSLaurent Pinchart 3363265edafSLaurent Pinchart ret = len; 3373265edafSLaurent Pinchart 3383265edafSLaurent Pinchart done: 3393265edafSLaurent Pinchart kfree(streams); 3403265edafSLaurent Pinchart return ret; 3413265edafSLaurent Pinchart }; 3423265edafSLaurent Pinchart 3433265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, 3443265edafSLaurent Pinchart char *buf, size_t len) 3453265edafSLaurent Pinchart { 3463265edafSLaurent Pinchart unsigned int request_id; 3473265edafSLaurent Pinchart unsigned int streams_mask; 3483265edafSLaurent Pinchart unsigned int num_frames; 3493265edafSLaurent Pinchart char *token; 3503265edafSLaurent Pinchart int ret; 3513265edafSLaurent Pinchart 3523265edafSLaurent Pinchart /* Request id */ 3533265edafSLaurent Pinchart token = strsep(&buf, ";"); 3543265edafSLaurent Pinchart if (token == NULL) 3553265edafSLaurent Pinchart return -EINVAL; 3563265edafSLaurent Pinchart ret = kstrtouint(token, 10, &request_id); 3573265edafSLaurent Pinchart if (ret < 0) 3583265edafSLaurent Pinchart return ret; 3593265edafSLaurent Pinchart 3603265edafSLaurent Pinchart /* Stream mask */ 3613265edafSLaurent Pinchart token = strsep(&buf, ";"); 3623265edafSLaurent Pinchart if (token == NULL) 3633265edafSLaurent Pinchart return -EINVAL; 3643265edafSLaurent Pinchart ret = kstrtouint(token, 16, &streams_mask); 3653265edafSLaurent Pinchart if (ret < 0) 3663265edafSLaurent Pinchart return ret; 3673265edafSLaurent Pinchart 3683265edafSLaurent Pinchart /* number of frames */ 3693265edafSLaurent Pinchart token = strsep(&buf, ";"); 3703265edafSLaurent Pinchart if (token == NULL) 3713265edafSLaurent Pinchart return -EINVAL; 3723265edafSLaurent Pinchart ret = kstrtouint(token, 10, &num_frames); 3733265edafSLaurent Pinchart if (ret < 0) 3743265edafSLaurent Pinchart return ret; 3753265edafSLaurent Pinchart 3763265edafSLaurent Pinchart ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, 3773265edafSLaurent Pinchart NULL); 3783265edafSLaurent Pinchart if (ret < 0) 3793265edafSLaurent Pinchart return ret; 3803265edafSLaurent Pinchart 3813265edafSLaurent Pinchart return len; 3823265edafSLaurent Pinchart } 3833265edafSLaurent Pinchart 3843265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, 3853265edafSLaurent Pinchart char *buf, size_t len) 3863265edafSLaurent Pinchart { 3873265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 3883265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; 3893265edafSLaurent Pinchart unsigned int req_id; 3903265edafSLaurent Pinchart int ret; 3913265edafSLaurent Pinchart 3923265edafSLaurent Pinchart ret = gb_camera_flush(gcam, &req_id); 3933265edafSLaurent Pinchart if (ret < 0) 3943265edafSLaurent Pinchart return ret; 3953265edafSLaurent Pinchart 3963265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u", req_id); 3973265edafSLaurent Pinchart 3983265edafSLaurent Pinchart return len; 3993265edafSLaurent Pinchart } 4003265edafSLaurent Pinchart 4013265edafSLaurent Pinchart struct gb_camera_debugfs_entry { 4023265edafSLaurent Pinchart const char *name; 4033265edafSLaurent Pinchart unsigned int mask; 4043265edafSLaurent Pinchart unsigned int buffer; 4053265edafSLaurent Pinchart ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); 4063265edafSLaurent Pinchart }; 4073265edafSLaurent Pinchart 4083265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { 4093265edafSLaurent Pinchart { 4103265edafSLaurent Pinchart .name = "capabilities", 4113265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO, 4123265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 4133265edafSLaurent Pinchart .execute = gb_camera_debugfs_capabilities, 4143265edafSLaurent Pinchart }, { 4153265edafSLaurent Pinchart .name = "configure_streams", 4163265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 4173265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 4183265edafSLaurent Pinchart .execute = gb_camera_debugfs_configure_streams, 4193265edafSLaurent Pinchart }, { 4203265edafSLaurent Pinchart .name = "capture", 4213265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 4223265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 4233265edafSLaurent Pinchart .execute = gb_camera_debugfs_capture, 4243265edafSLaurent Pinchart }, { 4253265edafSLaurent Pinchart .name = "flush", 4263265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 4273265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 4283265edafSLaurent Pinchart .execute = gb_camera_debugfs_flush, 4293265edafSLaurent Pinchart }, 4303265edafSLaurent Pinchart }; 4313265edafSLaurent Pinchart 4323265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, 4333265edafSLaurent Pinchart size_t len, loff_t *offset) 4343265edafSLaurent Pinchart { 4353265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 4363265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 4373265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer; 4383265edafSLaurent Pinchart ssize_t ret; 4393265edafSLaurent Pinchart 4403265edafSLaurent Pinchart /* For read-only entries the operation is triggered by a read. */ 4413265edafSLaurent Pinchart if (!(op->mask & S_IWUGO)) { 4423265edafSLaurent Pinchart ret = op->execute(gcam, NULL, 0); 4433265edafSLaurent Pinchart if (ret < 0) 4443265edafSLaurent Pinchart return ret; 4453265edafSLaurent Pinchart } 4463265edafSLaurent Pinchart 4473265edafSLaurent Pinchart buffer = &gcam->debugfs.buffers[op->buffer]; 4483265edafSLaurent Pinchart 4493265edafSLaurent Pinchart return simple_read_from_buffer(buf, len, offset, buffer->data, 4503265edafSLaurent Pinchart buffer->length); 4513265edafSLaurent Pinchart } 4523265edafSLaurent Pinchart 4533265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file, 4543265edafSLaurent Pinchart const char __user *buf, size_t len, 4553265edafSLaurent Pinchart loff_t *offset) 4563265edafSLaurent Pinchart { 4573265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 4583265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 4593265edafSLaurent Pinchart ssize_t ret; 4603265edafSLaurent Pinchart char *kbuf; 4613265edafSLaurent Pinchart 4623265edafSLaurent Pinchart if (len > 1024) 4633265edafSLaurent Pinchart return -EINVAL; 4643265edafSLaurent Pinchart 4653265edafSLaurent Pinchart kbuf = kmalloc(len + 1, GFP_KERNEL); 4663265edafSLaurent Pinchart if (kbuf == NULL) 4673265edafSLaurent Pinchart return -ENOMEM; 4683265edafSLaurent Pinchart 4693265edafSLaurent Pinchart if (copy_from_user(kbuf, buf, len)) { 4703265edafSLaurent Pinchart ret = -EFAULT; 4713265edafSLaurent Pinchart goto done; 4723265edafSLaurent Pinchart } 4733265edafSLaurent Pinchart 4743265edafSLaurent Pinchart kbuf[len] = '\0'; 4753265edafSLaurent Pinchart 4763265edafSLaurent Pinchart ret = op->execute(gcam, kbuf, len); 4773265edafSLaurent Pinchart 4783265edafSLaurent Pinchart done: 4793265edafSLaurent Pinchart kfree(kbuf); 4803265edafSLaurent Pinchart return ret; 4813265edafSLaurent Pinchart } 4823265edafSLaurent Pinchart 4833265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file) 4843265edafSLaurent Pinchart { 4853265edafSLaurent Pinchart unsigned int i; 4863265edafSLaurent Pinchart 4873265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 4883265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 4893265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 4903265edafSLaurent Pinchart 4914dda744cSGreg Kroah-Hartman if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { 4923265edafSLaurent Pinchart file->private_data = (void *)entry; 4933265edafSLaurent Pinchart break; 4943265edafSLaurent Pinchart } 4953265edafSLaurent Pinchart } 4963265edafSLaurent Pinchart 4973265edafSLaurent Pinchart return 0; 4983265edafSLaurent Pinchart } 4993265edafSLaurent Pinchart 5003265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = { 5013265edafSLaurent Pinchart .open = gb_camera_debugfs_open, 5023265edafSLaurent Pinchart .read = gb_camera_debugfs_read, 5033265edafSLaurent Pinchart .write = gb_camera_debugfs_write, 5043265edafSLaurent Pinchart }; 5053265edafSLaurent Pinchart 5063265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam) 5073265edafSLaurent Pinchart { 5083265edafSLaurent Pinchart struct gb_connection *connection = gcam->connection; 5093265edafSLaurent Pinchart char dirname[27]; 5103265edafSLaurent Pinchart unsigned int i; 5113265edafSLaurent Pinchart 5123265edafSLaurent Pinchart /* 5133265edafSLaurent Pinchart * Create root debugfs entry and a file entry for each camera operation. 5143265edafSLaurent Pinchart */ 5153265edafSLaurent Pinchart snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, 5163265edafSLaurent Pinchart connection->bundle->id); 5173265edafSLaurent Pinchart 5183265edafSLaurent Pinchart gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); 5193265edafSLaurent Pinchart if (IS_ERR(gcam->debugfs.root)) { 5203265edafSLaurent Pinchart gcam_err(gcam, "debugfs root create failed (%ld)\n", 5213265edafSLaurent Pinchart PTR_ERR(gcam->debugfs.root)); 5223265edafSLaurent Pinchart return PTR_ERR(gcam->debugfs.root); 5233265edafSLaurent Pinchart } 5243265edafSLaurent Pinchart 5253265edafSLaurent Pinchart gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * 5263265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX); 5273265edafSLaurent Pinchart if (!gcam->debugfs.buffers) 5283265edafSLaurent Pinchart return -ENOMEM; 5293265edafSLaurent Pinchart 5303265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 5313265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 5323265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 5333265edafSLaurent Pinchart struct dentry *dentry; 5343265edafSLaurent Pinchart 5353265edafSLaurent Pinchart gcam->debugfs.buffers[i].length = 0; 5363265edafSLaurent Pinchart 5373265edafSLaurent Pinchart dentry = debugfs_create_file(entry->name, entry->mask, 5383265edafSLaurent Pinchart gcam->debugfs.root, gcam, 5393265edafSLaurent Pinchart &gb_camera_debugfs_ops); 5403265edafSLaurent Pinchart if (IS_ERR(dentry)) { 5413265edafSLaurent Pinchart gcam_err(gcam, 5423265edafSLaurent Pinchart "debugfs operation %s create failed (%ld)\n", 5433265edafSLaurent Pinchart entry->name, PTR_ERR(gcam->debugfs.root)); 5443265edafSLaurent Pinchart return PTR_ERR(dentry); 5453265edafSLaurent Pinchart } 5463265edafSLaurent Pinchart } 5473265edafSLaurent Pinchart 5483265edafSLaurent Pinchart return 0; 5493265edafSLaurent Pinchart } 5503265edafSLaurent Pinchart 5513265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) 5523265edafSLaurent Pinchart { 5533265edafSLaurent Pinchart if (gcam->debugfs.root) 5543265edafSLaurent Pinchart debugfs_remove_recursive(gcam->debugfs.root); 5553265edafSLaurent Pinchart 5563265edafSLaurent Pinchart vfree(gcam->debugfs.buffers); 5573265edafSLaurent Pinchart } 5583265edafSLaurent Pinchart 5593265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 5603265edafSLaurent Pinchart * Init & Cleanup 5613265edafSLaurent Pinchart */ 5623265edafSLaurent Pinchart 5633265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam) 5643265edafSLaurent Pinchart { 5653265edafSLaurent Pinchart gb_camera_debugfs_cleanup(gcam); 5663265edafSLaurent Pinchart 5673265edafSLaurent Pinchart if (gcam->data_connected) { 5683265edafSLaurent Pinchart struct gb_interface *intf = gcam->connection->intf; 5693265edafSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 5703265edafSLaurent Pinchart 5713265edafSLaurent Pinchart gb_svc_connection_destroy(svc, intf->interface_id, 5723265edafSLaurent Pinchart ES2_APB_CDSI0_CPORT, svc->ap_intf_id, 5733265edafSLaurent Pinchart ES2_APB_CDSI1_CPORT); 5743265edafSLaurent Pinchart } 5753265edafSLaurent Pinchart 5763265edafSLaurent Pinchart kfree(gcam); 5773265edafSLaurent Pinchart } 5783265edafSLaurent Pinchart 5793265edafSLaurent Pinchart static int gb_camera_connection_init(struct gb_connection *connection) 5803265edafSLaurent Pinchart { 5813265edafSLaurent Pinchart struct gb_svc *svc = connection->hd->svc; 5823265edafSLaurent Pinchart struct gb_camera *gcam; 5833265edafSLaurent Pinchart int ret; 5843265edafSLaurent Pinchart 5853265edafSLaurent Pinchart gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); 5863265edafSLaurent Pinchart if (!gcam) 5873265edafSLaurent Pinchart return -ENOMEM; 5883265edafSLaurent Pinchart 5893265edafSLaurent Pinchart gcam->connection = connection; 5903265edafSLaurent Pinchart connection->private = gcam; 5913265edafSLaurent Pinchart 5923265edafSLaurent Pinchart /* 5933265edafSLaurent Pinchart * Create the data connection between camera module CDSI0 and APB CDS1. 5943265edafSLaurent Pinchart * The CPort IDs are hardcoded by the ES2 bridges. 5953265edafSLaurent Pinchart */ 5963265edafSLaurent Pinchart ret = gb_svc_connection_create(svc, connection->intf->interface_id, 5973265edafSLaurent Pinchart ES2_APB_CDSI0_CPORT, svc->ap_intf_id, 5983265edafSLaurent Pinchart ES2_APB_CDSI1_CPORT, false); 5993265edafSLaurent Pinchart if (ret < 0) 6003265edafSLaurent Pinchart goto error; 6013265edafSLaurent Pinchart 602bcc050beSLaurent Pinchart ret = gb_svc_link_config(svc, connection->intf->interface_id, 603b1c7d67eSLaurent Pinchart GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); 604bcc050beSLaurent Pinchart if (ret < 0) 605bcc050beSLaurent Pinchart goto error; 606bcc050beSLaurent Pinchart 607bcc050beSLaurent Pinchart ret = gb_svc_link_config(svc, svc->ap_intf_id, 608b1c7d67eSLaurent Pinchart GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); 609bcc050beSLaurent Pinchart if (ret < 0) 610bcc050beSLaurent Pinchart goto error; 611bcc050beSLaurent Pinchart 6123265edafSLaurent Pinchart gcam->data_connected = true; 6133265edafSLaurent Pinchart 6143265edafSLaurent Pinchart ret = gb_camera_debugfs_init(gcam); 6153265edafSLaurent Pinchart if (ret < 0) 6163265edafSLaurent Pinchart goto error; 6173265edafSLaurent Pinchart 6183265edafSLaurent Pinchart return 0; 6193265edafSLaurent Pinchart 6203265edafSLaurent Pinchart error: 6213265edafSLaurent Pinchart gb_camera_cleanup(gcam); 6223265edafSLaurent Pinchart return ret; 6233265edafSLaurent Pinchart } 6243265edafSLaurent Pinchart 6253265edafSLaurent Pinchart static void gb_camera_connection_exit(struct gb_connection *connection) 6263265edafSLaurent Pinchart { 6273265edafSLaurent Pinchart struct gb_camera *gcam = connection->private; 6283265edafSLaurent Pinchart 6293265edafSLaurent Pinchart gb_camera_cleanup(gcam); 6303265edafSLaurent Pinchart } 6313265edafSLaurent Pinchart 6323265edafSLaurent Pinchart static struct gb_protocol camera_protocol = { 6333265edafSLaurent Pinchart .name = "camera", 6343265edafSLaurent Pinchart .id = GREYBUS_PROTOCOL_CAMERA_MGMT, 6353265edafSLaurent Pinchart .major = GB_CAMERA_VERSION_MAJOR, 6363265edafSLaurent Pinchart .minor = GB_CAMERA_VERSION_MINOR, 6373265edafSLaurent Pinchart .connection_init = gb_camera_connection_init, 6383265edafSLaurent Pinchart .connection_exit = gb_camera_connection_exit, 6393265edafSLaurent Pinchart .request_recv = gb_camera_event_recv, 6403265edafSLaurent Pinchart }; 6413265edafSLaurent Pinchart 6423265edafSLaurent Pinchart gb_protocol_driver(&camera_protocol); 6433265edafSLaurent Pinchart 6443265edafSLaurent Pinchart MODULE_LICENSE("GPL v2"); 645