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) 11812c8b0dcSJohan Hovold goto done; 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; 1551f67ee5cSLaurent Pinchart csi_cfg.num_lanes = 4; 1561f67ee5cSLaurent Pinchart csi_cfg.bus_freq = 960000000; 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; 186b9f71bc8SJohan Hovold int ret; 1873265edafSLaurent Pinchart 1883265edafSLaurent Pinchart if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) 1893265edafSLaurent Pinchart return -EINVAL; 1903265edafSLaurent Pinchart 1913265edafSLaurent Pinchart req_size = sizeof(*req) + settings_size; 1923265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 1933265edafSLaurent Pinchart if (!req) 1943265edafSLaurent Pinchart return -ENOMEM; 1953265edafSLaurent Pinchart 196c6622216SLaurent Pinchart req->request_id = cpu_to_le32(request_id); 1973265edafSLaurent Pinchart req->streams = streams; 1983265edafSLaurent Pinchart req->padding = 0; 199c6622216SLaurent Pinchart req->num_frames = cpu_to_le16(num_frames); 2003265edafSLaurent Pinchart memcpy(req->settings, settings, settings_size); 2013265edafSLaurent Pinchart 202b9f71bc8SJohan Hovold ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, 2033265edafSLaurent Pinchart req, req_size, NULL, 0); 204b9f71bc8SJohan Hovold 205b9f71bc8SJohan Hovold kfree(req); 206b9f71bc8SJohan Hovold 207b9f71bc8SJohan Hovold return ret; 2083265edafSLaurent Pinchart } 2093265edafSLaurent Pinchart 2103265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) 2113265edafSLaurent Pinchart { 2123265edafSLaurent Pinchart struct gb_camera_flush_response resp; 2133265edafSLaurent Pinchart int ret; 2143265edafSLaurent Pinchart 2153265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, 2163265edafSLaurent Pinchart &resp, sizeof(resp)); 2173265edafSLaurent Pinchart if (ret < 0) 2183265edafSLaurent Pinchart return ret; 2193265edafSLaurent Pinchart 2203265edafSLaurent Pinchart if (request_id) 221c6622216SLaurent Pinchart *request_id = le32_to_cpu(resp.request_id); 2223265edafSLaurent Pinchart 2233265edafSLaurent Pinchart return 0; 2243265edafSLaurent Pinchart } 2253265edafSLaurent Pinchart 2263265edafSLaurent Pinchart static int gb_camera_event_recv(u8 type, struct gb_operation *op) 2273265edafSLaurent Pinchart { 2283265edafSLaurent Pinchart struct gb_camera *gcam = op->connection->private; 2293265edafSLaurent Pinchart struct gb_camera_metadata_request *payload; 2303265edafSLaurent Pinchart struct gb_message *request; 2313265edafSLaurent Pinchart 2323265edafSLaurent Pinchart if (type != GB_CAMERA_TYPE_METADATA) { 2333265edafSLaurent Pinchart gcam_err(gcam, "Unsupported unsolicited event: %u\n", type); 2343265edafSLaurent Pinchart return -EINVAL; 2353265edafSLaurent Pinchart } 2363265edafSLaurent Pinchart 2373265edafSLaurent Pinchart request = op->request; 2383265edafSLaurent Pinchart 2393265edafSLaurent Pinchart if (request->payload_size < sizeof(*payload)) { 2403265edafSLaurent Pinchart gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", 2413265edafSLaurent Pinchart request->payload_size, sizeof(*payload)); 2423265edafSLaurent Pinchart return -EINVAL; 2433265edafSLaurent Pinchart } 2443265edafSLaurent Pinchart 2453265edafSLaurent Pinchart payload = request->payload; 2463265edafSLaurent Pinchart 2473265edafSLaurent Pinchart gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", 2483265edafSLaurent Pinchart payload->request_id, payload->frame_number, payload->stream); 2493265edafSLaurent Pinchart 2503265edafSLaurent Pinchart return 0; 2513265edafSLaurent Pinchart } 2523265edafSLaurent Pinchart 2533265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 2543265edafSLaurent Pinchart * DebugFS 2553265edafSLaurent Pinchart */ 2563265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, 2573265edafSLaurent Pinchart char *buf, size_t len) 2583265edafSLaurent Pinchart { 2593265edafSLaurent Pinchart return len; 2603265edafSLaurent Pinchart } 2613265edafSLaurent Pinchart 2623265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, 2633265edafSLaurent Pinchart char *buf, size_t len) 2643265edafSLaurent Pinchart { 2653265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 2663265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; 2673265edafSLaurent Pinchart struct gb_camera_stream_config *streams; 2683265edafSLaurent Pinchart unsigned int nstreams; 2693265edafSLaurent Pinchart const char *sep = ";"; 2703265edafSLaurent Pinchart unsigned int i; 2713265edafSLaurent Pinchart char *token; 2723265edafSLaurent Pinchart int ret; 2733265edafSLaurent Pinchart 2743265edafSLaurent Pinchart /* Retrieve number of streams to configure */ 2753265edafSLaurent Pinchart token = strsep(&buf, sep); 2763265edafSLaurent Pinchart if (token == NULL) 2773265edafSLaurent Pinchart return -EINVAL; 2783265edafSLaurent Pinchart 2793265edafSLaurent Pinchart ret = kstrtouint(token, 10, &nstreams); 2803265edafSLaurent Pinchart if (ret < 0) 2813265edafSLaurent Pinchart return ret; 2823265edafSLaurent Pinchart 2833265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 2843265edafSLaurent Pinchart return -EINVAL; 2853265edafSLaurent Pinchart 2863265edafSLaurent Pinchart /* For each stream to configure parse width, height and format */ 2873265edafSLaurent Pinchart streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); 2883265edafSLaurent Pinchart if (!streams) 2893265edafSLaurent Pinchart return -ENOMEM; 2903265edafSLaurent Pinchart 2913265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 2923265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 2933265edafSLaurent Pinchart 2943265edafSLaurent Pinchart /* width */ 2953265edafSLaurent Pinchart token = strsep(&buf, ";"); 2963265edafSLaurent Pinchart if (token == NULL) { 2973265edafSLaurent Pinchart ret = -EINVAL; 2983265edafSLaurent Pinchart goto done; 2993265edafSLaurent Pinchart } 3003265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->width); 3013265edafSLaurent Pinchart if (ret < 0) 3023265edafSLaurent Pinchart goto done; 3033265edafSLaurent Pinchart 3043265edafSLaurent Pinchart /* height */ 3053265edafSLaurent Pinchart token = strsep(&buf, ";"); 3063265edafSLaurent Pinchart if (token == NULL) 3073265edafSLaurent Pinchart goto done; 3083265edafSLaurent Pinchart 3093265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->height); 3103265edafSLaurent Pinchart if (ret < 0) 3113265edafSLaurent Pinchart goto done; 3123265edafSLaurent Pinchart 3133265edafSLaurent Pinchart /* Image format code */ 3143265edafSLaurent Pinchart token = strsep(&buf, ";"); 3153265edafSLaurent Pinchart if (token == NULL) 3163265edafSLaurent Pinchart goto done; 3173265edafSLaurent Pinchart 3183265edafSLaurent Pinchart ret = kstrtouint(token, 16, &stream->format); 3193265edafSLaurent Pinchart if (ret < 0) 3203265edafSLaurent Pinchart goto done; 3213265edafSLaurent Pinchart } 3223265edafSLaurent Pinchart 3233265edafSLaurent Pinchart ret = gb_camera_configure_streams(gcam, nstreams, streams); 3243265edafSLaurent Pinchart if (ret < 0) 3253265edafSLaurent Pinchart goto done; 3263265edafSLaurent Pinchart 3273265edafSLaurent Pinchart nstreams = ret; 3283265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u;", nstreams); 3293265edafSLaurent Pinchart 3303265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 3313265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 3323265edafSLaurent Pinchart 3333265edafSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 3343265edafSLaurent Pinchart "%u;%u;%u;%u;%u;%u;%u;", 3353265edafSLaurent Pinchart stream->width, stream->height, 3363265edafSLaurent Pinchart stream->format, stream->vc, 3373265edafSLaurent Pinchart stream->dt[0], stream->dt[1], 3383265edafSLaurent Pinchart stream->max_size); 3393265edafSLaurent Pinchart } 3403265edafSLaurent Pinchart 3413265edafSLaurent Pinchart ret = len; 3423265edafSLaurent Pinchart 3433265edafSLaurent Pinchart done: 3443265edafSLaurent Pinchart kfree(streams); 3453265edafSLaurent Pinchart return ret; 3463265edafSLaurent Pinchart }; 3473265edafSLaurent Pinchart 3483265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, 3493265edafSLaurent Pinchart char *buf, size_t len) 3503265edafSLaurent Pinchart { 3513265edafSLaurent Pinchart unsigned int request_id; 3523265edafSLaurent Pinchart unsigned int streams_mask; 3533265edafSLaurent Pinchart unsigned int num_frames; 3543265edafSLaurent Pinchart char *token; 3553265edafSLaurent Pinchart int ret; 3563265edafSLaurent Pinchart 3573265edafSLaurent Pinchart /* Request id */ 3583265edafSLaurent Pinchart token = strsep(&buf, ";"); 3593265edafSLaurent Pinchart if (token == NULL) 3603265edafSLaurent Pinchart return -EINVAL; 3613265edafSLaurent Pinchart ret = kstrtouint(token, 10, &request_id); 3623265edafSLaurent Pinchart if (ret < 0) 3633265edafSLaurent Pinchart return ret; 3643265edafSLaurent Pinchart 3653265edafSLaurent Pinchart /* Stream mask */ 3663265edafSLaurent Pinchart token = strsep(&buf, ";"); 3673265edafSLaurent Pinchart if (token == NULL) 3683265edafSLaurent Pinchart return -EINVAL; 3693265edafSLaurent Pinchart ret = kstrtouint(token, 16, &streams_mask); 3703265edafSLaurent Pinchart if (ret < 0) 3713265edafSLaurent Pinchart return ret; 3723265edafSLaurent Pinchart 3733265edafSLaurent Pinchart /* number of frames */ 3743265edafSLaurent Pinchart token = strsep(&buf, ";"); 3753265edafSLaurent Pinchart if (token == NULL) 3763265edafSLaurent Pinchart return -EINVAL; 3773265edafSLaurent Pinchart ret = kstrtouint(token, 10, &num_frames); 3783265edafSLaurent Pinchart if (ret < 0) 3793265edafSLaurent Pinchart return ret; 3803265edafSLaurent Pinchart 3813265edafSLaurent Pinchart ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, 3823265edafSLaurent Pinchart NULL); 3833265edafSLaurent Pinchart if (ret < 0) 3843265edafSLaurent Pinchart return ret; 3853265edafSLaurent Pinchart 3863265edafSLaurent Pinchart return len; 3873265edafSLaurent Pinchart } 3883265edafSLaurent Pinchart 3893265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, 3903265edafSLaurent Pinchart char *buf, size_t len) 3913265edafSLaurent Pinchart { 3923265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 3933265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; 3943265edafSLaurent Pinchart unsigned int req_id; 3953265edafSLaurent Pinchart int ret; 3963265edafSLaurent Pinchart 3973265edafSLaurent Pinchart ret = gb_camera_flush(gcam, &req_id); 3983265edafSLaurent Pinchart if (ret < 0) 3993265edafSLaurent Pinchart return ret; 4003265edafSLaurent Pinchart 4013265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u", req_id); 4023265edafSLaurent Pinchart 4033265edafSLaurent Pinchart return len; 4043265edafSLaurent Pinchart } 4053265edafSLaurent Pinchart 4063265edafSLaurent Pinchart struct gb_camera_debugfs_entry { 4073265edafSLaurent Pinchart const char *name; 4083265edafSLaurent Pinchart unsigned int mask; 4093265edafSLaurent Pinchart unsigned int buffer; 4103265edafSLaurent Pinchart ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); 4113265edafSLaurent Pinchart }; 4123265edafSLaurent Pinchart 4133265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { 4143265edafSLaurent Pinchart { 4153265edafSLaurent Pinchart .name = "capabilities", 4163265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO, 4173265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 4183265edafSLaurent Pinchart .execute = gb_camera_debugfs_capabilities, 4193265edafSLaurent Pinchart }, { 4203265edafSLaurent Pinchart .name = "configure_streams", 4213265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 4223265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 4233265edafSLaurent Pinchart .execute = gb_camera_debugfs_configure_streams, 4243265edafSLaurent Pinchart }, { 4253265edafSLaurent Pinchart .name = "capture", 4263265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 4273265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 4283265edafSLaurent Pinchart .execute = gb_camera_debugfs_capture, 4293265edafSLaurent Pinchart }, { 4303265edafSLaurent Pinchart .name = "flush", 4313265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 4323265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 4333265edafSLaurent Pinchart .execute = gb_camera_debugfs_flush, 4343265edafSLaurent Pinchart }, 4353265edafSLaurent Pinchart }; 4363265edafSLaurent Pinchart 4373265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, 4383265edafSLaurent Pinchart size_t len, loff_t *offset) 4393265edafSLaurent Pinchart { 4403265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 4413265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 4423265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer; 4433265edafSLaurent Pinchart ssize_t ret; 4443265edafSLaurent Pinchart 4453265edafSLaurent Pinchart /* For read-only entries the operation is triggered by a read. */ 4463265edafSLaurent Pinchart if (!(op->mask & S_IWUGO)) { 4473265edafSLaurent Pinchart ret = op->execute(gcam, NULL, 0); 4483265edafSLaurent Pinchart if (ret < 0) 4493265edafSLaurent Pinchart return ret; 4503265edafSLaurent Pinchart } 4513265edafSLaurent Pinchart 4523265edafSLaurent Pinchart buffer = &gcam->debugfs.buffers[op->buffer]; 4533265edafSLaurent Pinchart 4543265edafSLaurent Pinchart return simple_read_from_buffer(buf, len, offset, buffer->data, 4553265edafSLaurent Pinchart buffer->length); 4563265edafSLaurent Pinchart } 4573265edafSLaurent Pinchart 4583265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file, 4593265edafSLaurent Pinchart const char __user *buf, size_t len, 4603265edafSLaurent Pinchart loff_t *offset) 4613265edafSLaurent Pinchart { 4623265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 4633265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 4643265edafSLaurent Pinchart ssize_t ret; 4653265edafSLaurent Pinchart char *kbuf; 4663265edafSLaurent Pinchart 4673265edafSLaurent Pinchart if (len > 1024) 4683265edafSLaurent Pinchart return -EINVAL; 4693265edafSLaurent Pinchart 4703265edafSLaurent Pinchart kbuf = kmalloc(len + 1, GFP_KERNEL); 4713265edafSLaurent Pinchart if (kbuf == NULL) 4723265edafSLaurent Pinchart return -ENOMEM; 4733265edafSLaurent Pinchart 4743265edafSLaurent Pinchart if (copy_from_user(kbuf, buf, len)) { 4753265edafSLaurent Pinchart ret = -EFAULT; 4763265edafSLaurent Pinchart goto done; 4773265edafSLaurent Pinchart } 4783265edafSLaurent Pinchart 4793265edafSLaurent Pinchart kbuf[len] = '\0'; 4803265edafSLaurent Pinchart 4813265edafSLaurent Pinchart ret = op->execute(gcam, kbuf, len); 4823265edafSLaurent Pinchart 4833265edafSLaurent Pinchart done: 4843265edafSLaurent Pinchart kfree(kbuf); 4853265edafSLaurent Pinchart return ret; 4863265edafSLaurent Pinchart } 4873265edafSLaurent Pinchart 4883265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file) 4893265edafSLaurent Pinchart { 4903265edafSLaurent Pinchart unsigned int i; 4913265edafSLaurent Pinchart 4923265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 4933265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 4943265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 4953265edafSLaurent Pinchart 4964dda744cSGreg Kroah-Hartman if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { 4973265edafSLaurent Pinchart file->private_data = (void *)entry; 4983265edafSLaurent Pinchart break; 4993265edafSLaurent Pinchart } 5003265edafSLaurent Pinchart } 5013265edafSLaurent Pinchart 5023265edafSLaurent Pinchart return 0; 5033265edafSLaurent Pinchart } 5043265edafSLaurent Pinchart 5053265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = { 5063265edafSLaurent Pinchart .open = gb_camera_debugfs_open, 5073265edafSLaurent Pinchart .read = gb_camera_debugfs_read, 5083265edafSLaurent Pinchart .write = gb_camera_debugfs_write, 5093265edafSLaurent Pinchart }; 5103265edafSLaurent Pinchart 5113265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam) 5123265edafSLaurent Pinchart { 5133265edafSLaurent Pinchart struct gb_connection *connection = gcam->connection; 5143265edafSLaurent Pinchart char dirname[27]; 5153265edafSLaurent Pinchart unsigned int i; 5163265edafSLaurent Pinchart 5173265edafSLaurent Pinchart /* 5183265edafSLaurent Pinchart * Create root debugfs entry and a file entry for each camera operation. 5193265edafSLaurent Pinchart */ 5203265edafSLaurent Pinchart snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, 5213265edafSLaurent Pinchart connection->bundle->id); 5223265edafSLaurent Pinchart 5233265edafSLaurent Pinchart gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); 5243265edafSLaurent Pinchart if (IS_ERR(gcam->debugfs.root)) { 5253265edafSLaurent Pinchart gcam_err(gcam, "debugfs root create failed (%ld)\n", 5263265edafSLaurent Pinchart PTR_ERR(gcam->debugfs.root)); 5273265edafSLaurent Pinchart return PTR_ERR(gcam->debugfs.root); 5283265edafSLaurent Pinchart } 5293265edafSLaurent Pinchart 5303265edafSLaurent Pinchart gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * 5313265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX); 5323265edafSLaurent Pinchart if (!gcam->debugfs.buffers) 5333265edafSLaurent Pinchart return -ENOMEM; 5343265edafSLaurent Pinchart 5353265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 5363265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 5373265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 5383265edafSLaurent Pinchart struct dentry *dentry; 5393265edafSLaurent Pinchart 5403265edafSLaurent Pinchart gcam->debugfs.buffers[i].length = 0; 5413265edafSLaurent Pinchart 5423265edafSLaurent Pinchart dentry = debugfs_create_file(entry->name, entry->mask, 5433265edafSLaurent Pinchart gcam->debugfs.root, gcam, 5443265edafSLaurent Pinchart &gb_camera_debugfs_ops); 5453265edafSLaurent Pinchart if (IS_ERR(dentry)) { 5463265edafSLaurent Pinchart gcam_err(gcam, 5473265edafSLaurent Pinchart "debugfs operation %s create failed (%ld)\n", 5483265edafSLaurent Pinchart entry->name, PTR_ERR(gcam->debugfs.root)); 5493265edafSLaurent Pinchart return PTR_ERR(dentry); 5503265edafSLaurent Pinchart } 5513265edafSLaurent Pinchart } 5523265edafSLaurent Pinchart 5533265edafSLaurent Pinchart return 0; 5543265edafSLaurent Pinchart } 5553265edafSLaurent Pinchart 5563265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) 5573265edafSLaurent Pinchart { 5583265edafSLaurent Pinchart if (gcam->debugfs.root) 5593265edafSLaurent Pinchart debugfs_remove_recursive(gcam->debugfs.root); 5603265edafSLaurent Pinchart 5613265edafSLaurent Pinchart vfree(gcam->debugfs.buffers); 5623265edafSLaurent Pinchart } 5633265edafSLaurent Pinchart 5643265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 5653265edafSLaurent Pinchart * Init & Cleanup 5663265edafSLaurent Pinchart */ 5673265edafSLaurent Pinchart 5683265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam) 5693265edafSLaurent Pinchart { 5703265edafSLaurent Pinchart gb_camera_debugfs_cleanup(gcam); 5713265edafSLaurent Pinchart 5723265edafSLaurent Pinchart if (gcam->data_connected) { 5733265edafSLaurent Pinchart struct gb_interface *intf = gcam->connection->intf; 5743265edafSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 5753265edafSLaurent Pinchart 5763265edafSLaurent Pinchart gb_svc_connection_destroy(svc, intf->interface_id, 5773265edafSLaurent Pinchart ES2_APB_CDSI0_CPORT, svc->ap_intf_id, 5783265edafSLaurent Pinchart ES2_APB_CDSI1_CPORT); 5793265edafSLaurent Pinchart } 5803265edafSLaurent Pinchart 5813265edafSLaurent Pinchart kfree(gcam); 5823265edafSLaurent Pinchart } 5833265edafSLaurent Pinchart 5843265edafSLaurent Pinchart static int gb_camera_connection_init(struct gb_connection *connection) 5853265edafSLaurent Pinchart { 5863265edafSLaurent Pinchart struct gb_svc *svc = connection->hd->svc; 5873265edafSLaurent Pinchart struct gb_camera *gcam; 5883265edafSLaurent Pinchart int ret; 5893265edafSLaurent Pinchart 5903265edafSLaurent Pinchart gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); 5913265edafSLaurent Pinchart if (!gcam) 5923265edafSLaurent Pinchart return -ENOMEM; 5933265edafSLaurent Pinchart 5943265edafSLaurent Pinchart gcam->connection = connection; 5953265edafSLaurent Pinchart connection->private = gcam; 5963265edafSLaurent Pinchart 5973265edafSLaurent Pinchart /* 5983265edafSLaurent Pinchart * Create the data connection between camera module CDSI0 and APB CDS1. 5993265edafSLaurent Pinchart * The CPort IDs are hardcoded by the ES2 bridges. 6003265edafSLaurent Pinchart */ 6013265edafSLaurent Pinchart ret = gb_svc_connection_create(svc, connection->intf->interface_id, 6023265edafSLaurent Pinchart ES2_APB_CDSI0_CPORT, svc->ap_intf_id, 6033265edafSLaurent Pinchart ES2_APB_CDSI1_CPORT, false); 6043265edafSLaurent Pinchart if (ret < 0) 6053265edafSLaurent Pinchart goto error; 6063265edafSLaurent Pinchart 60741c23958SJohan Hovold gcam->data_connected = true; 60841c23958SJohan Hovold 609aab4a1a3SLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, connection->intf->interface_id, 610aab4a1a3SLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 611aab4a1a3SLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 612aab4a1a3SLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 613aab4a1a3SLaurent Pinchart GB_SVC_PWRM_RXTERMINATION | 614aab4a1a3SLaurent Pinchart GB_SVC_PWRM_TXTERMINATION, 0); 615bcc050beSLaurent Pinchart if (ret < 0) 616bcc050beSLaurent Pinchart goto error; 617bcc050beSLaurent Pinchart 618aab4a1a3SLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, 619aab4a1a3SLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 620aab4a1a3SLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 621aab4a1a3SLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 622aab4a1a3SLaurent Pinchart GB_SVC_PWRM_RXTERMINATION | 623aab4a1a3SLaurent Pinchart GB_SVC_PWRM_TXTERMINATION, 0); 624bcc050beSLaurent Pinchart if (ret < 0) 625bcc050beSLaurent Pinchart goto error; 626bcc050beSLaurent Pinchart 6273265edafSLaurent Pinchart ret = gb_camera_debugfs_init(gcam); 6283265edafSLaurent Pinchart if (ret < 0) 6293265edafSLaurent Pinchart goto error; 6303265edafSLaurent Pinchart 6313265edafSLaurent Pinchart return 0; 6323265edafSLaurent Pinchart 6333265edafSLaurent Pinchart error: 6343265edafSLaurent Pinchart gb_camera_cleanup(gcam); 6353265edafSLaurent Pinchart return ret; 6363265edafSLaurent Pinchart } 6373265edafSLaurent Pinchart 6383265edafSLaurent Pinchart static void gb_camera_connection_exit(struct gb_connection *connection) 6393265edafSLaurent Pinchart { 6403265edafSLaurent Pinchart struct gb_camera *gcam = connection->private; 6413265edafSLaurent Pinchart 6423265edafSLaurent Pinchart gb_camera_cleanup(gcam); 6433265edafSLaurent Pinchart } 6443265edafSLaurent Pinchart 6453265edafSLaurent Pinchart static struct gb_protocol camera_protocol = { 6463265edafSLaurent Pinchart .name = "camera", 6473265edafSLaurent Pinchart .id = GREYBUS_PROTOCOL_CAMERA_MGMT, 6483265edafSLaurent Pinchart .major = GB_CAMERA_VERSION_MAJOR, 6493265edafSLaurent Pinchart .minor = GB_CAMERA_VERSION_MINOR, 6503265edafSLaurent Pinchart .connection_init = gb_camera_connection_init, 6513265edafSLaurent Pinchart .connection_exit = gb_camera_connection_exit, 6523265edafSLaurent Pinchart .request_recv = gb_camera_event_recv, 6533265edafSLaurent Pinchart }; 6543265edafSLaurent Pinchart 6553265edafSLaurent Pinchart gb_protocol_driver(&camera_protocol); 6563265edafSLaurent Pinchart 6573265edafSLaurent Pinchart MODULE_LICENSE("GPL v2"); 658