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 193a8dba4eSGjorgji Rosikopulos #include "gb-camera.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 613a8dba4eSGjorgji Rosikopulos struct gb_camera_fmt_map { 623a8dba4eSGjorgji Rosikopulos enum v4l2_mbus_pixelcode mbus_code; 633a8dba4eSGjorgji Rosikopulos unsigned int gb_format; 643a8dba4eSGjorgji Rosikopulos }; 653a8dba4eSGjorgji Rosikopulos 663a8dba4eSGjorgji Rosikopulos /* GB format to media code map */ 673a8dba4eSGjorgji Rosikopulos static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { 683a8dba4eSGjorgji Rosikopulos { 693a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, 703a8dba4eSGjorgji Rosikopulos .gb_format = 0x01, 713a8dba4eSGjorgji Rosikopulos }, 723a8dba4eSGjorgji Rosikopulos { 737c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV12_1x8, 747c154711SGjorgji Rosikopulos .gb_format = 0x12, 757c154711SGjorgji Rosikopulos }, 767c154711SGjorgji Rosikopulos { 777c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV21_1x8, 787c154711SGjorgji Rosikopulos .gb_format = 0x13, 797c154711SGjorgji Rosikopulos }, 807c154711SGjorgji Rosikopulos { 817c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YU12_1x8, 823a8dba4eSGjorgji Rosikopulos .gb_format = 0x16, 833a8dba4eSGjorgji Rosikopulos }, 843a8dba4eSGjorgji Rosikopulos { 857c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YV12_1x8, 863a8dba4eSGjorgji Rosikopulos .gb_format = 0x17, 873a8dba4eSGjorgji Rosikopulos }, 883a8dba4eSGjorgji Rosikopulos { 893a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, 903a8dba4eSGjorgji Rosikopulos .gb_format = 0x40, 913a8dba4eSGjorgji Rosikopulos } 923a8dba4eSGjorgji Rosikopulos }; 933a8dba4eSGjorgji Rosikopulos 943265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT 16 953265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT 17 963265edafSLaurent Pinchart 973265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE 8192 983265edafSLaurent Pinchart 993265edafSLaurent Pinchart #define gcam_dbg(gcam, format...) \ 1003265edafSLaurent Pinchart dev_dbg(&gcam->connection->bundle->dev, format) 1013265edafSLaurent Pinchart #define gcam_info(gcam, format...) \ 1023265edafSLaurent Pinchart dev_info(&gcam->connection->bundle->dev, format) 1033265edafSLaurent Pinchart #define gcam_err(gcam, format...) \ 1043265edafSLaurent Pinchart dev_err(&gcam->connection->bundle->dev, format) 1053265edafSLaurent Pinchart 1063265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 1073265edafSLaurent Pinchart * Camera Protocol Operations 1083265edafSLaurent Pinchart */ 1093265edafSLaurent Pinchart 110ed4596e9SGreg Kroah-Hartman struct ap_csi_config_request { 111ed4596e9SGreg Kroah-Hartman __u8 csi_id; 112ed4596e9SGreg Kroah-Hartman __u8 clock_mode; 113ed4596e9SGreg Kroah-Hartman __u8 num_lanes; 114ed4596e9SGreg Kroah-Hartman __u8 padding; 115ed4596e9SGreg Kroah-Hartman __le32 bus_freq; 116ed4596e9SGreg Kroah-Hartman } __packed; 117ed4596e9SGreg Kroah-Hartman 1183265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam, 1194068487cSLaurent Pinchart unsigned int *num_streams, 1204068487cSLaurent Pinchart unsigned int *flags, 1213265edafSLaurent Pinchart struct gb_camera_stream_config *streams) 1223265edafSLaurent Pinchart { 1234fbf69c7SJacopo Mondi struct gb_interface *intf = gcam->connection->intf; 1244fbf69c7SJacopo Mondi struct gb_svc *svc = gcam->connection->hd->svc; 1253265edafSLaurent Pinchart struct gb_camera_configure_streams_request *req; 1263265edafSLaurent Pinchart struct gb_camera_configure_streams_response *resp; 127ed4596e9SGreg Kroah-Hartman struct ap_csi_config_request csi_cfg; 1284fbf69c7SJacopo Mondi 1294068487cSLaurent Pinchart unsigned int nstreams = *num_streams; 1303265edafSLaurent Pinchart unsigned int i; 1313265edafSLaurent Pinchart size_t req_size; 1323265edafSLaurent Pinchart size_t resp_size; 1333265edafSLaurent Pinchart int ret; 1343265edafSLaurent Pinchart 1353265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 1363265edafSLaurent Pinchart return -EINVAL; 1373265edafSLaurent Pinchart 1383265edafSLaurent Pinchart req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); 1393265edafSLaurent Pinchart resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); 1403265edafSLaurent Pinchart 1413265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 1423265edafSLaurent Pinchart resp = kmalloc(resp_size, GFP_KERNEL); 1433265edafSLaurent Pinchart if (!req || !resp) { 1443265edafSLaurent Pinchart ret = -ENOMEM; 1453265edafSLaurent Pinchart goto done; 1463265edafSLaurent Pinchart } 1473265edafSLaurent Pinchart 1484fbf69c7SJacopo Mondi /* 1494fbf69c7SJacopo Mondi * Setup unipro link speed before actually issuing configuration 1504fbf69c7SJacopo Mondi * to the camera module, to assure unipro network speed is set 1514fbf69c7SJacopo Mondi * before CSI interfaces gets configured 1524fbf69c7SJacopo Mondi */ 153f121d79dSJacopo Mondi if (nstreams && !(*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { 1544fbf69c7SJacopo Mondi ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, 1554fbf69c7SJacopo Mondi GB_SVC_UNIPRO_HS_SERIES_A, 1564fbf69c7SJacopo Mondi GB_SVC_UNIPRO_FAST_MODE, 2, 2, 1574fbf69c7SJacopo Mondi GB_SVC_UNIPRO_FAST_MODE, 2, 2, 1584fbf69c7SJacopo Mondi GB_SVC_PWRM_RXTERMINATION | 1594fbf69c7SJacopo Mondi GB_SVC_PWRM_TXTERMINATION, 0); 1604fbf69c7SJacopo Mondi if (ret < 0) 1614fbf69c7SJacopo Mondi goto done; 1624fbf69c7SJacopo Mondi 1634fbf69c7SJacopo Mondi ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, 1644fbf69c7SJacopo Mondi GB_SVC_UNIPRO_HS_SERIES_A, 1654fbf69c7SJacopo Mondi GB_SVC_UNIPRO_FAST_MODE, 2, 2, 1664fbf69c7SJacopo Mondi GB_SVC_UNIPRO_FAST_MODE, 2, 2, 1674fbf69c7SJacopo Mondi GB_SVC_PWRM_RXTERMINATION | 1684fbf69c7SJacopo Mondi GB_SVC_PWRM_TXTERMINATION, 0); 1694fbf69c7SJacopo Mondi if (ret < 0) 1704fbf69c7SJacopo Mondi goto done; 171f121d79dSJacopo Mondi } else if (nstreams == 0) { 1724fbf69c7SJacopo Mondi ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, 1734fbf69c7SJacopo Mondi GB_SVC_UNIPRO_HS_SERIES_A, 1744fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 1754fbf69c7SJacopo Mondi 1, 2, 1764fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 1774fbf69c7SJacopo Mondi 1, 2, 1784fbf69c7SJacopo Mondi 0, 0); 1794fbf69c7SJacopo Mondi if (ret < 0) { 1804fbf69c7SJacopo Mondi gcam_err(gcam, "can't take camera link to PWM-G1 auto: %d\n", 1814fbf69c7SJacopo Mondi ret); 1824fbf69c7SJacopo Mondi goto done; 1834fbf69c7SJacopo Mondi } 1844fbf69c7SJacopo Mondi 1854fbf69c7SJacopo Mondi ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, 1864fbf69c7SJacopo Mondi GB_SVC_UNIPRO_HS_SERIES_A, 1874fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 1884fbf69c7SJacopo Mondi 1, 2, 1894fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 1904fbf69c7SJacopo Mondi 1, 2, 1914fbf69c7SJacopo Mondi 0, 0); 1924fbf69c7SJacopo Mondi if (ret < 0) { 1934fbf69c7SJacopo Mondi gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", 1944fbf69c7SJacopo Mondi ret); 1954fbf69c7SJacopo Mondi goto done; 1964fbf69c7SJacopo Mondi } 1974fbf69c7SJacopo Mondi } 1984fbf69c7SJacopo Mondi 199b787d413SJacopo Mondi req->num_streams = nstreams; 2004068487cSLaurent Pinchart req->flags = *flags; 2013265edafSLaurent Pinchart req->padding = 0; 2023265edafSLaurent Pinchart 2033265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 2043265edafSLaurent Pinchart struct gb_camera_stream_config_request *cfg = &req->config[i]; 2053265edafSLaurent Pinchart 206c6622216SLaurent Pinchart cfg->width = cpu_to_le16(streams[i].width); 207c6622216SLaurent Pinchart cfg->height = cpu_to_le16(streams[i].height); 208c6622216SLaurent Pinchart cfg->format = cpu_to_le16(streams[i].format); 2093265edafSLaurent Pinchart cfg->padding = 0; 2103265edafSLaurent Pinchart } 2113265edafSLaurent Pinchart 2123265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, 2133265edafSLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 2143265edafSLaurent Pinchart req, req_size, resp, resp_size); 2153265edafSLaurent Pinchart if (ret < 0) 2164fbf69c7SJacopo Mondi goto set_unipro_slow_mode; 2173265edafSLaurent Pinchart 218b787d413SJacopo Mondi if (resp->num_streams > nstreams) { 2193265edafSLaurent Pinchart gcam_dbg(gcam, "got #streams %u > request %u\n", 220b787d413SJacopo Mondi resp->num_streams, nstreams); 2213265edafSLaurent Pinchart ret = -EIO; 2224fbf69c7SJacopo Mondi goto set_unipro_slow_mode; 2233265edafSLaurent Pinchart } 2243265edafSLaurent Pinchart 2253265edafSLaurent Pinchart if (resp->padding != 0) { 2263265edafSLaurent Pinchart gcam_dbg(gcam, "response padding != 0"); 2273265edafSLaurent Pinchart ret = -EIO; 2284fbf69c7SJacopo Mondi goto set_unipro_slow_mode; 2293265edafSLaurent Pinchart } 2303265edafSLaurent Pinchart 2313265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 2323265edafSLaurent Pinchart struct gb_camera_stream_config_response *cfg = &resp->config[i]; 2333265edafSLaurent Pinchart 234c6622216SLaurent Pinchart streams[i].width = le16_to_cpu(cfg->width); 235c6622216SLaurent Pinchart streams[i].height = le16_to_cpu(cfg->height); 236c6622216SLaurent Pinchart streams[i].format = le16_to_cpu(cfg->format); 2373265edafSLaurent Pinchart streams[i].vc = cfg->virtual_channel; 2383265edafSLaurent Pinchart streams[i].dt[0] = cfg->data_type[0]; 2393265edafSLaurent Pinchart streams[i].dt[1] = cfg->data_type[1]; 240c6622216SLaurent Pinchart streams[i].max_size = le32_to_cpu(cfg->max_size); 2413265edafSLaurent Pinchart 2423265edafSLaurent Pinchart if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { 2433265edafSLaurent Pinchart gcam_dbg(gcam, "stream #%u padding != 0", i); 2443265edafSLaurent Pinchart ret = -EIO; 2454fbf69c7SJacopo Mondi goto set_unipro_slow_mode; 2463265edafSLaurent Pinchart } 2473265edafSLaurent Pinchart } 2483265edafSLaurent Pinchart 249f121d79dSJacopo Mondi if (nstreams && resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) { 250f121d79dSJacopo Mondi *flags = resp->flags; 251f121d79dSJacopo Mondi *num_streams = resp->num_streams; 252f121d79dSJacopo Mondi goto set_unipro_slow_mode; 253f121d79dSJacopo Mondi } 254f121d79dSJacopo Mondi 255ed4596e9SGreg Kroah-Hartman memset(&csi_cfg, 0, sizeof(csi_cfg)); 256ed4596e9SGreg Kroah-Hartman 257142b21feSLaurent Pinchart /* Configure the CSI transmitter. Hardcode the parameters for now. */ 258f121d79dSJacopo Mondi if (nstreams) { 259142b21feSLaurent Pinchart csi_cfg.csi_id = 1; 260142b21feSLaurent Pinchart csi_cfg.clock_mode = 0; 2611f67ee5cSLaurent Pinchart csi_cfg.num_lanes = 4; 262ed4596e9SGreg Kroah-Hartman csi_cfg.bus_freq = cpu_to_le32(960000000); 263ed4596e9SGreg Kroah-Hartman ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 264e5273381SGreg Kroah-Hartman sizeof(csi_cfg), 265e5273381SGreg Kroah-Hartman GB_APB_REQUEST_CSI_TX_CONTROL, false); 266f121d79dSJacopo Mondi } else { 267142b21feSLaurent Pinchart csi_cfg.csi_id = 1; 268ed4596e9SGreg Kroah-Hartman ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 269e5273381SGreg Kroah-Hartman sizeof(csi_cfg), 270e5273381SGreg Kroah-Hartman GB_APB_REQUEST_CSI_TX_CONTROL, false); 271142b21feSLaurent Pinchart } 272142b21feSLaurent Pinchart 273142b21feSLaurent Pinchart if (ret < 0) 274142b21feSLaurent Pinchart gcam_err(gcam, "failed to %s the CSI transmitter\n", 275142b21feSLaurent Pinchart nstreams ? "start" : "stop"); 276142b21feSLaurent Pinchart 277f121d79dSJacopo Mondi *flags = resp->flags; 2784068487cSLaurent Pinchart *num_streams = resp->num_streams; 2794068487cSLaurent Pinchart ret = 0; 2803265edafSLaurent Pinchart 2814fbf69c7SJacopo Mondi kfree(req); 2824fbf69c7SJacopo Mondi kfree(resp); 2834fbf69c7SJacopo Mondi return ret; 2844fbf69c7SJacopo Mondi 2854fbf69c7SJacopo Mondi set_unipro_slow_mode: 2864fbf69c7SJacopo Mondi ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, 2874fbf69c7SJacopo Mondi GB_SVC_UNIPRO_HS_SERIES_A, 2884fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2894fbf69c7SJacopo Mondi 1, 2, 2904fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2914fbf69c7SJacopo Mondi 1, 2, 2924fbf69c7SJacopo Mondi 0, 0); 2934fbf69c7SJacopo Mondi if (ret < 0) { 2944fbf69c7SJacopo Mondi gcam_err(gcam, "can't take camera link to PWM-G1 auto: %d\n", 2954fbf69c7SJacopo Mondi ret); 2964fbf69c7SJacopo Mondi goto done; 2974fbf69c7SJacopo Mondi } 2984fbf69c7SJacopo Mondi 2994fbf69c7SJacopo Mondi gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, 3004fbf69c7SJacopo Mondi GB_SVC_UNIPRO_HS_SERIES_A, 3014fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 3024fbf69c7SJacopo Mondi 1, 2, 3034fbf69c7SJacopo Mondi GB_SVC_UNIPRO_SLOW_AUTO_MODE, 3044fbf69c7SJacopo Mondi 1, 2, 3054fbf69c7SJacopo Mondi 0, 0); 3064fbf69c7SJacopo Mondi if (ret < 0) 3074fbf69c7SJacopo Mondi gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", 3084fbf69c7SJacopo Mondi ret); 3094fbf69c7SJacopo Mondi 3104fbf69c7SJacopo Mondi 3113265edafSLaurent Pinchart done: 3123265edafSLaurent Pinchart kfree(req); 3133265edafSLaurent Pinchart kfree(resp); 3143265edafSLaurent Pinchart return ret; 3153265edafSLaurent Pinchart } 3163265edafSLaurent Pinchart 3173265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, 3183265edafSLaurent Pinchart unsigned int streams, unsigned int num_frames, 3193265edafSLaurent Pinchart size_t settings_size, const void *settings) 3203265edafSLaurent Pinchart { 3213265edafSLaurent Pinchart struct gb_camera_capture_request *req; 3223265edafSLaurent Pinchart size_t req_size; 323b9f71bc8SJohan Hovold int ret; 3243265edafSLaurent Pinchart 3253265edafSLaurent Pinchart if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) 3263265edafSLaurent Pinchart return -EINVAL; 3273265edafSLaurent Pinchart 3283265edafSLaurent Pinchart req_size = sizeof(*req) + settings_size; 3293265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 3303265edafSLaurent Pinchart if (!req) 3313265edafSLaurent Pinchart return -ENOMEM; 3323265edafSLaurent Pinchart 333c6622216SLaurent Pinchart req->request_id = cpu_to_le32(request_id); 3343265edafSLaurent Pinchart req->streams = streams; 3353265edafSLaurent Pinchart req->padding = 0; 336c6622216SLaurent Pinchart req->num_frames = cpu_to_le16(num_frames); 3373265edafSLaurent Pinchart memcpy(req->settings, settings, settings_size); 3383265edafSLaurent Pinchart 339b9f71bc8SJohan Hovold ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, 3403265edafSLaurent Pinchart req, req_size, NULL, 0); 341b9f71bc8SJohan Hovold 342b9f71bc8SJohan Hovold kfree(req); 343b9f71bc8SJohan Hovold 344b9f71bc8SJohan Hovold return ret; 3453265edafSLaurent Pinchart } 3463265edafSLaurent Pinchart 3473265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) 3483265edafSLaurent Pinchart { 3493265edafSLaurent Pinchart struct gb_camera_flush_response resp; 3503265edafSLaurent Pinchart int ret; 3513265edafSLaurent Pinchart 3523265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, 3533265edafSLaurent Pinchart &resp, sizeof(resp)); 3543265edafSLaurent Pinchart if (ret < 0) 3553265edafSLaurent Pinchart return ret; 3563265edafSLaurent Pinchart 3573265edafSLaurent Pinchart if (request_id) 358c6622216SLaurent Pinchart *request_id = le32_to_cpu(resp.request_id); 3593265edafSLaurent Pinchart 3603265edafSLaurent Pinchart return 0; 3613265edafSLaurent Pinchart } 3623265edafSLaurent Pinchart 3633265edafSLaurent Pinchart static int gb_camera_event_recv(u8 type, struct gb_operation *op) 3643265edafSLaurent Pinchart { 3653265edafSLaurent Pinchart struct gb_camera *gcam = op->connection->private; 3663265edafSLaurent Pinchart struct gb_camera_metadata_request *payload; 3673265edafSLaurent Pinchart struct gb_message *request; 3683265edafSLaurent Pinchart 3693265edafSLaurent Pinchart if (type != GB_CAMERA_TYPE_METADATA) { 3703265edafSLaurent Pinchart gcam_err(gcam, "Unsupported unsolicited event: %u\n", type); 3713265edafSLaurent Pinchart return -EINVAL; 3723265edafSLaurent Pinchart } 3733265edafSLaurent Pinchart 3743265edafSLaurent Pinchart request = op->request; 3753265edafSLaurent Pinchart 3763265edafSLaurent Pinchart if (request->payload_size < sizeof(*payload)) { 3773265edafSLaurent Pinchart gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", 3783265edafSLaurent Pinchart request->payload_size, sizeof(*payload)); 3793265edafSLaurent Pinchart return -EINVAL; 3803265edafSLaurent Pinchart } 3813265edafSLaurent Pinchart 3823265edafSLaurent Pinchart payload = request->payload; 3833265edafSLaurent Pinchart 3843265edafSLaurent Pinchart gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", 3853265edafSLaurent Pinchart payload->request_id, payload->frame_number, payload->stream); 3863265edafSLaurent Pinchart 3873265edafSLaurent Pinchart return 0; 3883265edafSLaurent Pinchart } 3893265edafSLaurent Pinchart 3903265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 3913a8dba4eSGjorgji Rosikopulos * Interface with HOST ara camera. 3923a8dba4eSGjorgji Rosikopulos */ 3933a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) 3943a8dba4eSGjorgji Rosikopulos { 3953a8dba4eSGjorgji Rosikopulos unsigned int i; 3963a8dba4eSGjorgji Rosikopulos 3973a8dba4eSGjorgji Rosikopulos for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { 3983a8dba4eSGjorgji Rosikopulos if (mbus_to_gbus_format[i].mbus_code == mbus_code) 3993a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[i].gb_format; 4003a8dba4eSGjorgji Rosikopulos } 4013a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[0].gb_format; 4023a8dba4eSGjorgji Rosikopulos } 4033a8dba4eSGjorgji Rosikopulos 4043a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) 4053a8dba4eSGjorgji Rosikopulos { 4063a8dba4eSGjorgji Rosikopulos unsigned int i; 4073a8dba4eSGjorgji Rosikopulos 4083a8dba4eSGjorgji Rosikopulos for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { 4093a8dba4eSGjorgji Rosikopulos if (mbus_to_gbus_format[i].gb_format == gb_fmt) 4103a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[i].mbus_code; 4113a8dba4eSGjorgji Rosikopulos } 4123a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[0].mbus_code; 4133a8dba4eSGjorgji Rosikopulos } 4143a8dba4eSGjorgji Rosikopulos 4155b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, 4165b032710SGjorgji Rosikopulos unsigned int *flags, struct gb_camera_stream *streams) 4173a8dba4eSGjorgji Rosikopulos { 4183a8dba4eSGjorgji Rosikopulos struct gb_camera *gcam = priv; 4193a8dba4eSGjorgji Rosikopulos struct gb_camera_stream_config *gb_streams; 4205b032710SGjorgji Rosikopulos unsigned int gb_flags = 0; 4215b032710SGjorgji Rosikopulos unsigned int gb_nstreams = *nstreams; 4223a8dba4eSGjorgji Rosikopulos unsigned int i; 4233a8dba4eSGjorgji Rosikopulos int ret; 4243a8dba4eSGjorgji Rosikopulos 4255b032710SGjorgji Rosikopulos if (gb_nstreams > GB_CAMERA_MAX_STREAMS) 4263a8dba4eSGjorgji Rosikopulos return -EINVAL; 4273a8dba4eSGjorgji Rosikopulos 4285b032710SGjorgji Rosikopulos gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); 4293a8dba4eSGjorgji Rosikopulos if (!gb_streams) 4303a8dba4eSGjorgji Rosikopulos return -ENOMEM; 4313a8dba4eSGjorgji Rosikopulos 4325b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 4333a8dba4eSGjorgji Rosikopulos gb_streams[i].width = streams[i].width; 4343a8dba4eSGjorgji Rosikopulos gb_streams[i].height = streams[i].height; 4353a8dba4eSGjorgji Rosikopulos gb_streams[i].format = 4363a8dba4eSGjorgji Rosikopulos gb_camera_mbus_to_gb(streams[i].pixel_code); 4373a8dba4eSGjorgji Rosikopulos } 4383a8dba4eSGjorgji Rosikopulos 4395b032710SGjorgji Rosikopulos if (*flags & GB_CAMERA_IN_FLAG_TEST) 4405b032710SGjorgji Rosikopulos gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; 4415b032710SGjorgji Rosikopulos 4425b032710SGjorgji Rosikopulos ret = gb_camera_configure_streams(gcam, &gb_nstreams, 4435b032710SGjorgji Rosikopulos &gb_flags, gb_streams); 4443a8dba4eSGjorgji Rosikopulos if (ret < 0) 4453a8dba4eSGjorgji Rosikopulos goto done; 4465b032710SGjorgji Rosikopulos if (gb_nstreams > *nstreams) { 4475b032710SGjorgji Rosikopulos ret = -EINVAL; 4485b032710SGjorgji Rosikopulos goto done; 4495b032710SGjorgji Rosikopulos } 4503a8dba4eSGjorgji Rosikopulos 4515b032710SGjorgji Rosikopulos *flags = 0; 4525b032710SGjorgji Rosikopulos if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) 4535b032710SGjorgji Rosikopulos *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; 4545b032710SGjorgji Rosikopulos 4555b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 4563a8dba4eSGjorgji Rosikopulos streams[i].width = gb_streams[i].width; 4573a8dba4eSGjorgji Rosikopulos streams[i].height = gb_streams[i].height; 4583a8dba4eSGjorgji Rosikopulos streams[i].vc = gb_streams[i].vc; 4593a8dba4eSGjorgji Rosikopulos streams[i].dt[0] = gb_streams[i].dt[0]; 4603a8dba4eSGjorgji Rosikopulos streams[i].dt[1] = gb_streams[i].dt[1]; 4613a8dba4eSGjorgji Rosikopulos streams[i].max_size = gb_streams[i].max_size; 4623a8dba4eSGjorgji Rosikopulos streams[i].pixel_code = 4633a8dba4eSGjorgji Rosikopulos gb_camera_gb_to_mbus(gb_streams[i].format); 4643a8dba4eSGjorgji Rosikopulos } 4655b032710SGjorgji Rosikopulos *nstreams = gb_nstreams; 4663a8dba4eSGjorgji Rosikopulos 4673a8dba4eSGjorgji Rosikopulos done: 4683a8dba4eSGjorgji Rosikopulos kfree(gb_streams); 4693a8dba4eSGjorgji Rosikopulos return ret; 4703a8dba4eSGjorgji Rosikopulos } 4713a8dba4eSGjorgji Rosikopulos 4723a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id, 4733a8dba4eSGjorgji Rosikopulos unsigned int streams, unsigned int num_frames, 4743a8dba4eSGjorgji Rosikopulos size_t settings_size, const void *settings) 4753a8dba4eSGjorgji Rosikopulos { 4763a8dba4eSGjorgji Rosikopulos return gb_camera_capture(priv, request_id, streams, num_frames, 4773a8dba4eSGjorgji Rosikopulos settings_size, settings); 4783a8dba4eSGjorgji Rosikopulos } 4793a8dba4eSGjorgji Rosikopulos 4803a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id) 4813a8dba4eSGjorgji Rosikopulos { 4823a8dba4eSGjorgji Rosikopulos return gb_camera_flush(priv, request_id); 4833a8dba4eSGjorgji Rosikopulos } 4843a8dba4eSGjorgji Rosikopulos 4853a8dba4eSGjorgji Rosikopulos struct gb_camera_ops gb_cam_ops = { 4863a8dba4eSGjorgji Rosikopulos .configure_streams = gb_camera_op_configure_streams, 4873a8dba4eSGjorgji Rosikopulos .capture = gb_camera_op_capture, 4883a8dba4eSGjorgji Rosikopulos .flush = gb_camera_op_flush, 4893a8dba4eSGjorgji Rosikopulos }; 4903a8dba4eSGjorgji Rosikopulos 4913a8dba4eSGjorgji Rosikopulos static int gb_camera_register_intf_ops(struct gb_camera *gcam) 4923a8dba4eSGjorgji Rosikopulos { 4933a8dba4eSGjorgji Rosikopulos return gb_camera_register(&gb_cam_ops, gcam); 4943a8dba4eSGjorgji Rosikopulos } 4953a8dba4eSGjorgji Rosikopulos 4963a8dba4eSGjorgji Rosikopulos /* ----------------------------------------------------------------------------- 4973265edafSLaurent Pinchart * DebugFS 4983265edafSLaurent Pinchart */ 4993265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, 5003265edafSLaurent Pinchart char *buf, size_t len) 5013265edafSLaurent Pinchart { 5023265edafSLaurent Pinchart return len; 5033265edafSLaurent Pinchart } 5043265edafSLaurent Pinchart 5053265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, 5063265edafSLaurent Pinchart char *buf, size_t len) 5073265edafSLaurent Pinchart { 5083265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 5093265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; 5103265edafSLaurent Pinchart struct gb_camera_stream_config *streams; 5113265edafSLaurent Pinchart unsigned int nstreams; 512b787d413SJacopo Mondi unsigned int flags; 5133265edafSLaurent Pinchart unsigned int i; 5143265edafSLaurent Pinchart char *token; 5153265edafSLaurent Pinchart int ret; 5163265edafSLaurent Pinchart 5173265edafSLaurent Pinchart /* Retrieve number of streams to configure */ 518b787d413SJacopo Mondi token = strsep(&buf, ";"); 5193265edafSLaurent Pinchart if (token == NULL) 5203265edafSLaurent Pinchart return -EINVAL; 5213265edafSLaurent Pinchart 5223265edafSLaurent Pinchart ret = kstrtouint(token, 10, &nstreams); 5233265edafSLaurent Pinchart if (ret < 0) 5243265edafSLaurent Pinchart return ret; 5253265edafSLaurent Pinchart 5263265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 5273265edafSLaurent Pinchart return -EINVAL; 5283265edafSLaurent Pinchart 529b787d413SJacopo Mondi token = strsep(&buf, ";"); 530b787d413SJacopo Mondi if (token == NULL) 531b787d413SJacopo Mondi return -EINVAL; 532b787d413SJacopo Mondi 533b787d413SJacopo Mondi ret = kstrtouint(token, 10, &flags); 534b787d413SJacopo Mondi if (ret < 0) 535b787d413SJacopo Mondi return ret; 536b787d413SJacopo Mondi 5373265edafSLaurent Pinchart /* For each stream to configure parse width, height and format */ 5383265edafSLaurent Pinchart streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); 5393265edafSLaurent Pinchart if (!streams) 5403265edafSLaurent Pinchart return -ENOMEM; 5413265edafSLaurent Pinchart 5423265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 5433265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 5443265edafSLaurent Pinchart 5453265edafSLaurent Pinchart /* width */ 5463265edafSLaurent Pinchart token = strsep(&buf, ";"); 5473265edafSLaurent Pinchart if (token == NULL) { 5483265edafSLaurent Pinchart ret = -EINVAL; 5493265edafSLaurent Pinchart goto done; 5503265edafSLaurent Pinchart } 5513265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->width); 5523265edafSLaurent Pinchart if (ret < 0) 5533265edafSLaurent Pinchart goto done; 5543265edafSLaurent Pinchart 5553265edafSLaurent Pinchart /* height */ 5563265edafSLaurent Pinchart token = strsep(&buf, ";"); 5573265edafSLaurent Pinchart if (token == NULL) 5583265edafSLaurent Pinchart goto done; 5593265edafSLaurent Pinchart 5603265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->height); 5613265edafSLaurent Pinchart if (ret < 0) 5623265edafSLaurent Pinchart goto done; 5633265edafSLaurent Pinchart 5643265edafSLaurent Pinchart /* Image format code */ 5653265edafSLaurent Pinchart token = strsep(&buf, ";"); 5663265edafSLaurent Pinchart if (token == NULL) 5673265edafSLaurent Pinchart goto done; 5683265edafSLaurent Pinchart 5693265edafSLaurent Pinchart ret = kstrtouint(token, 16, &stream->format); 5703265edafSLaurent Pinchart if (ret < 0) 5713265edafSLaurent Pinchart goto done; 5723265edafSLaurent Pinchart } 5733265edafSLaurent Pinchart 5744068487cSLaurent Pinchart ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams); 5753265edafSLaurent Pinchart if (ret < 0) 5763265edafSLaurent Pinchart goto done; 5773265edafSLaurent Pinchart 5784068487cSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); 5793265edafSLaurent Pinchart 5803265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 5813265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 5823265edafSLaurent Pinchart 5833265edafSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 5843265edafSLaurent Pinchart "%u;%u;%u;%u;%u;%u;%u;", 5853265edafSLaurent Pinchart stream->width, stream->height, 5863265edafSLaurent Pinchart stream->format, stream->vc, 5873265edafSLaurent Pinchart stream->dt[0], stream->dt[1], 5883265edafSLaurent Pinchart stream->max_size); 5893265edafSLaurent Pinchart } 5903265edafSLaurent Pinchart 5913265edafSLaurent Pinchart ret = len; 5923265edafSLaurent Pinchart 5933265edafSLaurent Pinchart done: 5943265edafSLaurent Pinchart kfree(streams); 5953265edafSLaurent Pinchart return ret; 5963265edafSLaurent Pinchart }; 5973265edafSLaurent Pinchart 5983265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, 5993265edafSLaurent Pinchart char *buf, size_t len) 6003265edafSLaurent Pinchart { 6013265edafSLaurent Pinchart unsigned int request_id; 6023265edafSLaurent Pinchart unsigned int streams_mask; 6033265edafSLaurent Pinchart unsigned int num_frames; 6043265edafSLaurent Pinchart char *token; 6053265edafSLaurent Pinchart int ret; 6063265edafSLaurent Pinchart 6073265edafSLaurent Pinchart /* Request id */ 6083265edafSLaurent Pinchart token = strsep(&buf, ";"); 6093265edafSLaurent Pinchart if (token == NULL) 6103265edafSLaurent Pinchart return -EINVAL; 6113265edafSLaurent Pinchart ret = kstrtouint(token, 10, &request_id); 6123265edafSLaurent Pinchart if (ret < 0) 6133265edafSLaurent Pinchart return ret; 6143265edafSLaurent Pinchart 6153265edafSLaurent Pinchart /* Stream mask */ 6163265edafSLaurent Pinchart token = strsep(&buf, ";"); 6173265edafSLaurent Pinchart if (token == NULL) 6183265edafSLaurent Pinchart return -EINVAL; 6193265edafSLaurent Pinchart ret = kstrtouint(token, 16, &streams_mask); 6203265edafSLaurent Pinchart if (ret < 0) 6213265edafSLaurent Pinchart return ret; 6223265edafSLaurent Pinchart 6233265edafSLaurent Pinchart /* number of frames */ 6243265edafSLaurent Pinchart token = strsep(&buf, ";"); 6253265edafSLaurent Pinchart if (token == NULL) 6263265edafSLaurent Pinchart return -EINVAL; 6273265edafSLaurent Pinchart ret = kstrtouint(token, 10, &num_frames); 6283265edafSLaurent Pinchart if (ret < 0) 6293265edafSLaurent Pinchart return ret; 6303265edafSLaurent Pinchart 6313265edafSLaurent Pinchart ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, 6323265edafSLaurent Pinchart NULL); 6333265edafSLaurent Pinchart if (ret < 0) 6343265edafSLaurent Pinchart return ret; 6353265edafSLaurent Pinchart 6363265edafSLaurent Pinchart return len; 6373265edafSLaurent Pinchart } 6383265edafSLaurent Pinchart 6393265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, 6403265edafSLaurent Pinchart char *buf, size_t len) 6413265edafSLaurent Pinchart { 6423265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 6433265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; 6443265edafSLaurent Pinchart unsigned int req_id; 6453265edafSLaurent Pinchart int ret; 6463265edafSLaurent Pinchart 6473265edafSLaurent Pinchart ret = gb_camera_flush(gcam, &req_id); 6483265edafSLaurent Pinchart if (ret < 0) 6493265edafSLaurent Pinchart return ret; 6503265edafSLaurent Pinchart 6513265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u", req_id); 6523265edafSLaurent Pinchart 6533265edafSLaurent Pinchart return len; 6543265edafSLaurent Pinchart } 6553265edafSLaurent Pinchart 6563265edafSLaurent Pinchart struct gb_camera_debugfs_entry { 6573265edafSLaurent Pinchart const char *name; 6583265edafSLaurent Pinchart unsigned int mask; 6593265edafSLaurent Pinchart unsigned int buffer; 6603265edafSLaurent Pinchart ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); 6613265edafSLaurent Pinchart }; 6623265edafSLaurent Pinchart 6633265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { 6643265edafSLaurent Pinchart { 6653265edafSLaurent Pinchart .name = "capabilities", 6663265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO, 6673265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 6683265edafSLaurent Pinchart .execute = gb_camera_debugfs_capabilities, 6693265edafSLaurent Pinchart }, { 6703265edafSLaurent Pinchart .name = "configure_streams", 6713265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 6723265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 6733265edafSLaurent Pinchart .execute = gb_camera_debugfs_configure_streams, 6743265edafSLaurent Pinchart }, { 6753265edafSLaurent Pinchart .name = "capture", 6763265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 6773265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 6783265edafSLaurent Pinchart .execute = gb_camera_debugfs_capture, 6793265edafSLaurent Pinchart }, { 6803265edafSLaurent Pinchart .name = "flush", 6813265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 6823265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 6833265edafSLaurent Pinchart .execute = gb_camera_debugfs_flush, 6843265edafSLaurent Pinchart }, 6853265edafSLaurent Pinchart }; 6863265edafSLaurent Pinchart 6873265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, 6883265edafSLaurent Pinchart size_t len, loff_t *offset) 6893265edafSLaurent Pinchart { 6903265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 6913265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 6923265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer; 6933265edafSLaurent Pinchart ssize_t ret; 6943265edafSLaurent Pinchart 6953265edafSLaurent Pinchart /* For read-only entries the operation is triggered by a read. */ 6963265edafSLaurent Pinchart if (!(op->mask & S_IWUGO)) { 6973265edafSLaurent Pinchart ret = op->execute(gcam, NULL, 0); 6983265edafSLaurent Pinchart if (ret < 0) 6993265edafSLaurent Pinchart return ret; 7003265edafSLaurent Pinchart } 7013265edafSLaurent Pinchart 7023265edafSLaurent Pinchart buffer = &gcam->debugfs.buffers[op->buffer]; 7033265edafSLaurent Pinchart 7043265edafSLaurent Pinchart return simple_read_from_buffer(buf, len, offset, buffer->data, 7053265edafSLaurent Pinchart buffer->length); 7063265edafSLaurent Pinchart } 7073265edafSLaurent Pinchart 7083265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file, 7093265edafSLaurent Pinchart const char __user *buf, size_t len, 7103265edafSLaurent Pinchart loff_t *offset) 7113265edafSLaurent Pinchart { 7123265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 7133265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 7143265edafSLaurent Pinchart ssize_t ret; 7153265edafSLaurent Pinchart char *kbuf; 7163265edafSLaurent Pinchart 7173265edafSLaurent Pinchart if (len > 1024) 7183265edafSLaurent Pinchart return -EINVAL; 7193265edafSLaurent Pinchart 7203265edafSLaurent Pinchart kbuf = kmalloc(len + 1, GFP_KERNEL); 7213265edafSLaurent Pinchart if (kbuf == NULL) 7223265edafSLaurent Pinchart return -ENOMEM; 7233265edafSLaurent Pinchart 7243265edafSLaurent Pinchart if (copy_from_user(kbuf, buf, len)) { 7253265edafSLaurent Pinchart ret = -EFAULT; 7263265edafSLaurent Pinchart goto done; 7273265edafSLaurent Pinchart } 7283265edafSLaurent Pinchart 7293265edafSLaurent Pinchart kbuf[len] = '\0'; 7303265edafSLaurent Pinchart 7313265edafSLaurent Pinchart ret = op->execute(gcam, kbuf, len); 7323265edafSLaurent Pinchart 7333265edafSLaurent Pinchart done: 7343265edafSLaurent Pinchart kfree(kbuf); 7353265edafSLaurent Pinchart return ret; 7363265edafSLaurent Pinchart } 7373265edafSLaurent Pinchart 7383265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file) 7393265edafSLaurent Pinchart { 7403265edafSLaurent Pinchart unsigned int i; 7413265edafSLaurent Pinchart 7423265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 7433265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 7443265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 7453265edafSLaurent Pinchart 7464dda744cSGreg Kroah-Hartman if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { 7473265edafSLaurent Pinchart file->private_data = (void *)entry; 7483265edafSLaurent Pinchart break; 7493265edafSLaurent Pinchart } 7503265edafSLaurent Pinchart } 7513265edafSLaurent Pinchart 7523265edafSLaurent Pinchart return 0; 7533265edafSLaurent Pinchart } 7543265edafSLaurent Pinchart 7553265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = { 7563265edafSLaurent Pinchart .open = gb_camera_debugfs_open, 7573265edafSLaurent Pinchart .read = gb_camera_debugfs_read, 7583265edafSLaurent Pinchart .write = gb_camera_debugfs_write, 7593265edafSLaurent Pinchart }; 7603265edafSLaurent Pinchart 7613265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam) 7623265edafSLaurent Pinchart { 7633265edafSLaurent Pinchart struct gb_connection *connection = gcam->connection; 7643265edafSLaurent Pinchart char dirname[27]; 7653265edafSLaurent Pinchart unsigned int i; 7663265edafSLaurent Pinchart 7673265edafSLaurent Pinchart /* 7683265edafSLaurent Pinchart * Create root debugfs entry and a file entry for each camera operation. 7693265edafSLaurent Pinchart */ 7703265edafSLaurent Pinchart snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, 7713265edafSLaurent Pinchart connection->bundle->id); 7723265edafSLaurent Pinchart 7733265edafSLaurent Pinchart gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); 7743265edafSLaurent Pinchart if (IS_ERR(gcam->debugfs.root)) { 7753265edafSLaurent Pinchart gcam_err(gcam, "debugfs root create failed (%ld)\n", 7763265edafSLaurent Pinchart PTR_ERR(gcam->debugfs.root)); 7773265edafSLaurent Pinchart return PTR_ERR(gcam->debugfs.root); 7783265edafSLaurent Pinchart } 7793265edafSLaurent Pinchart 7803265edafSLaurent Pinchart gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * 7813265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX); 7823265edafSLaurent Pinchart if (!gcam->debugfs.buffers) 7833265edafSLaurent Pinchart return -ENOMEM; 7843265edafSLaurent Pinchart 7853265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 7863265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 7873265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 7883265edafSLaurent Pinchart struct dentry *dentry; 7893265edafSLaurent Pinchart 7903265edafSLaurent Pinchart gcam->debugfs.buffers[i].length = 0; 7913265edafSLaurent Pinchart 7923265edafSLaurent Pinchart dentry = debugfs_create_file(entry->name, entry->mask, 7933265edafSLaurent Pinchart gcam->debugfs.root, gcam, 7943265edafSLaurent Pinchart &gb_camera_debugfs_ops); 7953265edafSLaurent Pinchart if (IS_ERR(dentry)) { 7963265edafSLaurent Pinchart gcam_err(gcam, 7973265edafSLaurent Pinchart "debugfs operation %s create failed (%ld)\n", 7983265edafSLaurent Pinchart entry->name, PTR_ERR(gcam->debugfs.root)); 7993265edafSLaurent Pinchart return PTR_ERR(dentry); 8003265edafSLaurent Pinchart } 8013265edafSLaurent Pinchart } 8023265edafSLaurent Pinchart 8033265edafSLaurent Pinchart return 0; 8043265edafSLaurent Pinchart } 8053265edafSLaurent Pinchart 8063265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) 8073265edafSLaurent Pinchart { 8083265edafSLaurent Pinchart if (gcam->debugfs.root) 8093265edafSLaurent Pinchart debugfs_remove_recursive(gcam->debugfs.root); 8103265edafSLaurent Pinchart 8113265edafSLaurent Pinchart vfree(gcam->debugfs.buffers); 8123265edafSLaurent Pinchart } 8133265edafSLaurent Pinchart 8143265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 8153265edafSLaurent Pinchart * Init & Cleanup 8163265edafSLaurent Pinchart */ 8173265edafSLaurent Pinchart 8183265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam) 8193265edafSLaurent Pinchart { 8203265edafSLaurent Pinchart gb_camera_debugfs_cleanup(gcam); 8213265edafSLaurent Pinchart 8223265edafSLaurent Pinchart if (gcam->data_connected) { 8233265edafSLaurent Pinchart struct gb_interface *intf = gcam->connection->intf; 8243265edafSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 8253265edafSLaurent Pinchart 8263265edafSLaurent Pinchart gb_svc_connection_destroy(svc, intf->interface_id, 8273265edafSLaurent Pinchart ES2_APB_CDSI0_CPORT, svc->ap_intf_id, 8283265edafSLaurent Pinchart ES2_APB_CDSI1_CPORT); 8293265edafSLaurent Pinchart } 8303265edafSLaurent Pinchart 8313265edafSLaurent Pinchart kfree(gcam); 8323265edafSLaurent Pinchart } 8333265edafSLaurent Pinchart 8343265edafSLaurent Pinchart static int gb_camera_connection_init(struct gb_connection *connection) 8353265edafSLaurent Pinchart { 8363265edafSLaurent Pinchart struct gb_svc *svc = connection->hd->svc; 8373265edafSLaurent Pinchart struct gb_camera *gcam; 8383265edafSLaurent Pinchart int ret; 8393265edafSLaurent Pinchart 8403265edafSLaurent Pinchart gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); 8413265edafSLaurent Pinchart if (!gcam) 8423265edafSLaurent Pinchart return -ENOMEM; 8433265edafSLaurent Pinchart 8443265edafSLaurent Pinchart gcam->connection = connection; 8453265edafSLaurent Pinchart connection->private = gcam; 8463265edafSLaurent Pinchart 8473265edafSLaurent Pinchart /* 8483265edafSLaurent Pinchart * Create the data connection between camera module CDSI0 and APB CDS1. 8493265edafSLaurent Pinchart * The CPort IDs are hardcoded by the ES2 bridges. 8503265edafSLaurent Pinchart */ 8513265edafSLaurent Pinchart ret = gb_svc_connection_create(svc, connection->intf->interface_id, 8523265edafSLaurent Pinchart ES2_APB_CDSI0_CPORT, svc->ap_intf_id, 8533265edafSLaurent Pinchart ES2_APB_CDSI1_CPORT, false); 8543265edafSLaurent Pinchart if (ret < 0) 8553265edafSLaurent Pinchart goto error; 8563265edafSLaurent Pinchart 85741c23958SJohan Hovold gcam->data_connected = true; 85841c23958SJohan Hovold 8593265edafSLaurent Pinchart ret = gb_camera_debugfs_init(gcam); 8603265edafSLaurent Pinchart if (ret < 0) 8613265edafSLaurent Pinchart goto error; 8623265edafSLaurent Pinchart 8633a8dba4eSGjorgji Rosikopulos ret = gb_camera_register_intf_ops(gcam); 8643a8dba4eSGjorgji Rosikopulos if (ret < 0) 8653a8dba4eSGjorgji Rosikopulos goto error; 8663a8dba4eSGjorgji Rosikopulos 8673265edafSLaurent Pinchart return 0; 8683265edafSLaurent Pinchart 8693265edafSLaurent Pinchart error: 8703265edafSLaurent Pinchart gb_camera_cleanup(gcam); 8713265edafSLaurent Pinchart return ret; 8723265edafSLaurent Pinchart } 8733265edafSLaurent Pinchart 8743265edafSLaurent Pinchart static void gb_camera_connection_exit(struct gb_connection *connection) 8753265edafSLaurent Pinchart { 8763265edafSLaurent Pinchart struct gb_camera *gcam = connection->private; 8773265edafSLaurent Pinchart 8783265edafSLaurent Pinchart gb_camera_cleanup(gcam); 8793265edafSLaurent Pinchart } 8803265edafSLaurent Pinchart 8813265edafSLaurent Pinchart static struct gb_protocol camera_protocol = { 8823265edafSLaurent Pinchart .name = "camera", 8833265edafSLaurent Pinchart .id = GREYBUS_PROTOCOL_CAMERA_MGMT, 8843265edafSLaurent Pinchart .major = GB_CAMERA_VERSION_MAJOR, 8853265edafSLaurent Pinchart .minor = GB_CAMERA_VERSION_MINOR, 8863265edafSLaurent Pinchart .connection_init = gb_camera_connection_init, 8873265edafSLaurent Pinchart .connection_exit = gb_camera_connection_exit, 8883265edafSLaurent Pinchart .request_recv = gb_camera_event_recv, 8893265edafSLaurent Pinchart }; 8903265edafSLaurent Pinchart 8913265edafSLaurent Pinchart gb_protocol_driver(&camera_protocol); 8923265edafSLaurent Pinchart 8933265edafSLaurent Pinchart MODULE_LICENSE("GPL v2"); 894