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 3817ca6770SEvgeniy Borisov * @connection: the greybus connection for camera management 3917ca6770SEvgeniy Borisov * @data_connection: the greybus connection for camera data 4017ca6770SEvgeniy Borisov * @mutex: protects the connection field 413265edafSLaurent Pinchart * @debugfs: debugfs entries for camera protocol operations testing 42c3d77f71SGjorgji Rosikopulos * @module: Greybus camera module registered to HOST processor. 433265edafSLaurent Pinchart */ 443265edafSLaurent Pinchart struct gb_camera { 4568b66c28SLaurent Pinchart struct gb_bundle *bundle; 463265edafSLaurent Pinchart struct gb_connection *connection; 473ba9fa5cSJohan Hovold struct gb_connection *data_connection; 4817ca6770SEvgeniy Borisov struct mutex mutex; 493265edafSLaurent Pinchart 503265edafSLaurent Pinchart struct { 513265edafSLaurent Pinchart struct dentry *root; 523265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffers; 533265edafSLaurent Pinchart } debugfs; 54c3d77f71SGjorgji Rosikopulos 55c3d77f71SGjorgji Rosikopulos struct gb_camera_module module; 563265edafSLaurent Pinchart }; 573265edafSLaurent Pinchart 583265edafSLaurent Pinchart struct gb_camera_stream_config { 593265edafSLaurent Pinchart unsigned int width; 603265edafSLaurent Pinchart unsigned int height; 613265edafSLaurent Pinchart unsigned int format; 623265edafSLaurent Pinchart unsigned int vc; 633265edafSLaurent Pinchart unsigned int dt[2]; 643265edafSLaurent Pinchart unsigned int max_size; 653265edafSLaurent Pinchart }; 663265edafSLaurent Pinchart 673a8dba4eSGjorgji Rosikopulos struct gb_camera_fmt_map { 683a8dba4eSGjorgji Rosikopulos enum v4l2_mbus_pixelcode mbus_code; 693a8dba4eSGjorgji Rosikopulos unsigned int gb_format; 703a8dba4eSGjorgji Rosikopulos }; 713a8dba4eSGjorgji Rosikopulos 723a8dba4eSGjorgji Rosikopulos /* GB format to media code map */ 733a8dba4eSGjorgji Rosikopulos static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { 743a8dba4eSGjorgji Rosikopulos { 753a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, 763a8dba4eSGjorgji Rosikopulos .gb_format = 0x01, 773a8dba4eSGjorgji Rosikopulos }, 783a8dba4eSGjorgji Rosikopulos { 797c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV12_1x8, 807c154711SGjorgji Rosikopulos .gb_format = 0x12, 817c154711SGjorgji Rosikopulos }, 827c154711SGjorgji Rosikopulos { 837c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV21_1x8, 847c154711SGjorgji Rosikopulos .gb_format = 0x13, 857c154711SGjorgji Rosikopulos }, 867c154711SGjorgji Rosikopulos { 877c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YU12_1x8, 883a8dba4eSGjorgji Rosikopulos .gb_format = 0x16, 893a8dba4eSGjorgji Rosikopulos }, 903a8dba4eSGjorgji Rosikopulos { 917c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YV12_1x8, 923a8dba4eSGjorgji Rosikopulos .gb_format = 0x17, 933a8dba4eSGjorgji Rosikopulos }, 943a8dba4eSGjorgji Rosikopulos { 953a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, 963a8dba4eSGjorgji Rosikopulos .gb_format = 0x40, 97dc5cc72cSGjorgji Rosikopulos }, 98dc5cc72cSGjorgji Rosikopulos { 99dc5cc72cSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8, 100dc5cc72cSGjorgji Rosikopulos .gb_format = 0x41, 101cb14e976SGjorgji Rosikopulos }, 102cb14e976SGjorgji Rosikopulos { 103cb14e976SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8, 104cb14e976SGjorgji Rosikopulos .gb_format = 0x42, 105cb14e976SGjorgji Rosikopulos }, 1063a8dba4eSGjorgji Rosikopulos }; 1073a8dba4eSGjorgji Rosikopulos 1083265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT 16 1093265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT 17 1103265edafSLaurent Pinchart 1113265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE 8192 1123265edafSLaurent Pinchart 11368b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...) dev_dbg(&gcam->bundle->dev, format) 11468b66c28SLaurent Pinchart #define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) 11568b66c28SLaurent Pinchart #define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) 1163265edafSLaurent Pinchart 1173265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 1183265edafSLaurent Pinchart * Camera Protocol Operations 1193265edafSLaurent Pinchart */ 1203265edafSLaurent Pinchart 121c161c0fcSLaurent Pinchart static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, 122c161c0fcSLaurent Pinchart bool hs) 123c161c0fcSLaurent Pinchart { 124c161c0fcSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 125c161c0fcSLaurent Pinchart int ret; 126c161c0fcSLaurent Pinchart 127c161c0fcSLaurent Pinchart if (hs) 128c161c0fcSLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, intf_id, 129c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 130c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 1318c2522d8SEli Sennesh GB_SVC_SMALL_AMPLITUDE, 1328c2522d8SEli Sennesh GB_SVC_NO_DE_EMPHASIS, 133c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 134c161c0fcSLaurent Pinchart GB_SVC_PWRM_RXTERMINATION | 1358c2522d8SEli Sennesh GB_SVC_PWRM_TXTERMINATION, 0, 1368c2522d8SEli Sennesh NULL, NULL); 137c161c0fcSLaurent Pinchart else 138c161c0fcSLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, intf_id, 139c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 140c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_SLOW_AUTO_MODE, 141ee2f2074SMitchell Tasman 2, 1, 1428c2522d8SEli Sennesh GB_SVC_SMALL_AMPLITUDE, 1438c2522d8SEli Sennesh GB_SVC_NO_DE_EMPHASIS, 144c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_SLOW_AUTO_MODE, 145ee2f2074SMitchell Tasman 2, 1, 1468c2522d8SEli Sennesh 0, 0, 1478c2522d8SEli Sennesh NULL, NULL); 148c161c0fcSLaurent Pinchart 149c161c0fcSLaurent Pinchart return ret; 150c161c0fcSLaurent Pinchart } 151c161c0fcSLaurent Pinchart 152c161c0fcSLaurent Pinchart static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) 153c161c0fcSLaurent Pinchart { 154c161c0fcSLaurent Pinchart struct gb_interface *intf = gcam->connection->intf; 155c161c0fcSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 156c161c0fcSLaurent Pinchart int ret; 157c161c0fcSLaurent Pinchart 158c161c0fcSLaurent Pinchart ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs); 159c161c0fcSLaurent Pinchart if (ret < 0) { 160c161c0fcSLaurent Pinchart gcam_err(gcam, "failed to set module interface to %s (%d)\n", 161c161c0fcSLaurent Pinchart hs ? "HS" : "PWM", ret); 162c161c0fcSLaurent Pinchart return ret; 163c161c0fcSLaurent Pinchart } 164c161c0fcSLaurent Pinchart 165c161c0fcSLaurent Pinchart ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); 166c161c0fcSLaurent Pinchart if (ret < 0) { 167b573b0e6SLaurent Pinchart gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs); 168c161c0fcSLaurent Pinchart gcam_err(gcam, "failed to set AP interface to %s (%d)\n", 169c161c0fcSLaurent Pinchart hs ? "HS" : "PWM", ret); 170c161c0fcSLaurent Pinchart return ret; 171c161c0fcSLaurent Pinchart } 172c161c0fcSLaurent Pinchart 173c161c0fcSLaurent Pinchart return 0; 174c161c0fcSLaurent Pinchart } 175c161c0fcSLaurent Pinchart 17648b15a9bSLaurent Pinchart static int gb_camera_capabilities(struct gb_camera *gcam, 17748b15a9bSLaurent Pinchart u8 *capabilities, size_t *size) 17848b15a9bSLaurent Pinchart { 17917ca6770SEvgeniy Borisov struct gb_operation *op = NULL; 18048b15a9bSLaurent Pinchart int ret; 18148b15a9bSLaurent Pinchart 18217ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 18317ca6770SEvgeniy Borisov 18417ca6770SEvgeniy Borisov if (!gcam->connection) { 18517ca6770SEvgeniy Borisov ret = -EINVAL; 18617ca6770SEvgeniy Borisov goto done; 18717ca6770SEvgeniy Borisov } 18817ca6770SEvgeniy Borisov 18948b15a9bSLaurent Pinchart op = gb_operation_create_flags(gcam->connection, 19048b15a9bSLaurent Pinchart GB_CAMERA_TYPE_CAPABILITIES, 0, *size, 19148b15a9bSLaurent Pinchart GB_OPERATION_FLAG_SHORT_RESPONSE, 19248b15a9bSLaurent Pinchart GFP_KERNEL); 19317ca6770SEvgeniy Borisov if (!op) { 19417ca6770SEvgeniy Borisov ret = -ENOMEM; 19517ca6770SEvgeniy Borisov goto done; 19617ca6770SEvgeniy Borisov } 19748b15a9bSLaurent Pinchart 19848b15a9bSLaurent Pinchart ret = gb_operation_request_send_sync(op); 19948b15a9bSLaurent Pinchart if (ret) { 20048b15a9bSLaurent Pinchart gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); 20148b15a9bSLaurent Pinchart goto done; 20248b15a9bSLaurent Pinchart } 20348b15a9bSLaurent Pinchart 20448b15a9bSLaurent Pinchart memcpy(capabilities, op->response->payload, op->response->payload_size); 20548b15a9bSLaurent Pinchart *size = op->response->payload_size; 20648b15a9bSLaurent Pinchart 20748b15a9bSLaurent Pinchart done: 20817ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 20917ca6770SEvgeniy Borisov if (op) 21048b15a9bSLaurent Pinchart gb_operation_put(op); 21148b15a9bSLaurent Pinchart return ret; 21248b15a9bSLaurent Pinchart } 21348b15a9bSLaurent Pinchart 214ed4596e9SGreg Kroah-Hartman struct ap_csi_config_request { 215ed4596e9SGreg Kroah-Hartman __u8 csi_id; 21627e18d8cSJacopo Mondi __u8 flags; 21727e18d8cSJacopo Mondi #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 218ed4596e9SGreg Kroah-Hartman __u8 num_lanes; 219ed4596e9SGreg Kroah-Hartman __u8 padding; 220ed4596e9SGreg Kroah-Hartman __le32 bus_freq; 221446091c9SJacopo Mondi __le32 lines_per_second; 222ed4596e9SGreg Kroah-Hartman } __packed; 223ed4596e9SGreg Kroah-Hartman 2243265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam, 2254068487cSLaurent Pinchart unsigned int *num_streams, 2264068487cSLaurent Pinchart unsigned int *flags, 227b4905038SEvgeniy Borisov struct gb_camera_stream_config *streams, 228b4905038SEvgeniy Borisov struct gb_camera_csi_params *csi_params) 2293265edafSLaurent Pinchart { 2303265edafSLaurent Pinchart struct gb_camera_configure_streams_request *req; 2313265edafSLaurent Pinchart struct gb_camera_configure_streams_response *resp; 232ed4596e9SGreg Kroah-Hartman struct ap_csi_config_request csi_cfg; 2334fbf69c7SJacopo Mondi 2344068487cSLaurent Pinchart unsigned int nstreams = *num_streams; 2353265edafSLaurent Pinchart unsigned int i; 2363265edafSLaurent Pinchart size_t req_size; 2373265edafSLaurent Pinchart size_t resp_size; 2383265edafSLaurent Pinchart int ret; 2393265edafSLaurent Pinchart 2403265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 2413265edafSLaurent Pinchart return -EINVAL; 2423265edafSLaurent Pinchart 2433265edafSLaurent Pinchart req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); 2443265edafSLaurent Pinchart resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); 2453265edafSLaurent Pinchart 2463265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 2473265edafSLaurent Pinchart resp = kmalloc(resp_size, GFP_KERNEL); 2483265edafSLaurent Pinchart if (!req || !resp) { 24917ca6770SEvgeniy Borisov kfree(req); 25017ca6770SEvgeniy Borisov kfree(resp); 25117ca6770SEvgeniy Borisov return -ENOMEM; 2523265edafSLaurent Pinchart } 2533265edafSLaurent Pinchart 254b787d413SJacopo Mondi req->num_streams = nstreams; 2554068487cSLaurent Pinchart req->flags = *flags; 2563265edafSLaurent Pinchart req->padding = 0; 2573265edafSLaurent Pinchart 2583265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 2593265edafSLaurent Pinchart struct gb_camera_stream_config_request *cfg = &req->config[i]; 2603265edafSLaurent Pinchart 261c6622216SLaurent Pinchart cfg->width = cpu_to_le16(streams[i].width); 262c6622216SLaurent Pinchart cfg->height = cpu_to_le16(streams[i].height); 263c6622216SLaurent Pinchart cfg->format = cpu_to_le16(streams[i].format); 2643265edafSLaurent Pinchart cfg->padding = 0; 2653265edafSLaurent Pinchart } 2663265edafSLaurent Pinchart 26717ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 26817ca6770SEvgeniy Borisov 26917ca6770SEvgeniy Borisov if (!gcam->connection) { 27017ca6770SEvgeniy Borisov ret = -EINVAL; 27117ca6770SEvgeniy Borisov goto done; 27217ca6770SEvgeniy Borisov } 27317ca6770SEvgeniy Borisov 2743265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, 2753265edafSLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 2763265edafSLaurent Pinchart req, req_size, resp, resp_size); 2773265edafSLaurent Pinchart if (ret < 0) 27866c36070SLaurent Pinchart goto done; 2793265edafSLaurent Pinchart 280b787d413SJacopo Mondi if (resp->num_streams > nstreams) { 2813265edafSLaurent Pinchart gcam_dbg(gcam, "got #streams %u > request %u\n", 282b787d413SJacopo Mondi resp->num_streams, nstreams); 2833265edafSLaurent Pinchart ret = -EIO; 28466c36070SLaurent Pinchart goto done; 2853265edafSLaurent Pinchart } 2863265edafSLaurent Pinchart 2873265edafSLaurent Pinchart if (resp->padding != 0) { 2883265edafSLaurent Pinchart gcam_dbg(gcam, "response padding != 0"); 2893265edafSLaurent Pinchart ret = -EIO; 29066c36070SLaurent Pinchart goto done; 2913265edafSLaurent Pinchart } 2923265edafSLaurent Pinchart 2933265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 2943265edafSLaurent Pinchart struct gb_camera_stream_config_response *cfg = &resp->config[i]; 2953265edafSLaurent Pinchart 296c6622216SLaurent Pinchart streams[i].width = le16_to_cpu(cfg->width); 297c6622216SLaurent Pinchart streams[i].height = le16_to_cpu(cfg->height); 298c6622216SLaurent Pinchart streams[i].format = le16_to_cpu(cfg->format); 2993265edafSLaurent Pinchart streams[i].vc = cfg->virtual_channel; 3003265edafSLaurent Pinchart streams[i].dt[0] = cfg->data_type[0]; 3013265edafSLaurent Pinchart streams[i].dt[1] = cfg->data_type[1]; 302c6622216SLaurent Pinchart streams[i].max_size = le32_to_cpu(cfg->max_size); 3033265edafSLaurent Pinchart 3043265edafSLaurent Pinchart if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { 3053265edafSLaurent Pinchart gcam_dbg(gcam, "stream #%u padding != 0", i); 3063265edafSLaurent Pinchart ret = -EIO; 30766c36070SLaurent Pinchart goto done; 3083265edafSLaurent Pinchart } 3093265edafSLaurent Pinchart } 3103265edafSLaurent Pinchart 311640924d2SLaurent Pinchart if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || 312640924d2SLaurent Pinchart (*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { 313f121d79dSJacopo Mondi *flags = resp->flags; 314f121d79dSJacopo Mondi *num_streams = resp->num_streams; 31566c36070SLaurent Pinchart goto done; 31666c36070SLaurent Pinchart } 31766c36070SLaurent Pinchart 31866c36070SLaurent Pinchart /* Setup unipro link speed. */ 319640924d2SLaurent Pinchart ret = gb_camera_set_power_mode(gcam, nstreams != 0); 32066c36070SLaurent Pinchart if (ret < 0) 32166c36070SLaurent Pinchart goto done; 322ed4596e9SGreg Kroah-Hartman 323446091c9SJacopo Mondi /* 324446091c9SJacopo Mondi * Configure the APB1 CSI transmitter using the lines count reported by 325446091c9SJacopo Mondi * the camera module, but with hard-coded bus frequency and lanes number. 326446091c9SJacopo Mondi * 327446091c9SJacopo Mondi * TODO: use the clocking and size informations reported by camera module 328446091c9SJacopo Mondi * to compute the required CSI bandwidth, and configure the CSI receiver 329446091c9SJacopo Mondi * on AP side, and the CSI transmitter on APB1 side accordingly. 330446091c9SJacopo Mondi */ 331640924d2SLaurent Pinchart memset(&csi_cfg, 0, sizeof(csi_cfg)); 332640924d2SLaurent Pinchart 333f121d79dSJacopo Mondi if (nstreams) { 334142b21feSLaurent Pinchart csi_cfg.csi_id = 1; 33527e18d8cSJacopo Mondi csi_cfg.flags = 0; 336698bdcbfSEvgeniy Borisov csi_cfg.num_lanes = resp->num_lanes; 337ed4596e9SGreg Kroah-Hartman csi_cfg.bus_freq = cpu_to_le32(960000000); 338446091c9SJacopo Mondi csi_cfg.lines_per_second = resp->lines_per_second; 339ed4596e9SGreg Kroah-Hartman ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 340e5273381SGreg Kroah-Hartman sizeof(csi_cfg), 341e5273381SGreg Kroah-Hartman GB_APB_REQUEST_CSI_TX_CONTROL, false); 342b4905038SEvgeniy Borisov if (csi_params) { 343b4905038SEvgeniy Borisov csi_params->num_lanes = csi_cfg.num_lanes; 344b4905038SEvgeniy Borisov /* Transmitting two bits per cycle. (DDR clock) */ 345b4905038SEvgeniy Borisov csi_params->clk_freq = csi_cfg.bus_freq / 2; 346b4905038SEvgeniy Borisov csi_params->lines_per_second = csi_cfg.lines_per_second; 347b4905038SEvgeniy Borisov } 348f121d79dSJacopo Mondi } else { 349142b21feSLaurent Pinchart csi_cfg.csi_id = 1; 350ed4596e9SGreg Kroah-Hartman ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 351e5273381SGreg Kroah-Hartman sizeof(csi_cfg), 352e5273381SGreg Kroah-Hartman GB_APB_REQUEST_CSI_TX_CONTROL, false); 353142b21feSLaurent Pinchart } 354142b21feSLaurent Pinchart 355142b21feSLaurent Pinchart if (ret < 0) 356142b21feSLaurent Pinchart gcam_err(gcam, "failed to %s the CSI transmitter\n", 357142b21feSLaurent Pinchart nstreams ? "start" : "stop"); 358142b21feSLaurent Pinchart 359f121d79dSJacopo Mondi *flags = resp->flags; 3604068487cSLaurent Pinchart *num_streams = resp->num_streams; 3614068487cSLaurent Pinchart ret = 0; 3623265edafSLaurent Pinchart 3633265edafSLaurent Pinchart done: 36417ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 3653265edafSLaurent Pinchart kfree(req); 3663265edafSLaurent Pinchart kfree(resp); 3673265edafSLaurent Pinchart return ret; 3683265edafSLaurent Pinchart } 3693265edafSLaurent Pinchart 3703265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, 3713265edafSLaurent Pinchart unsigned int streams, unsigned int num_frames, 3723265edafSLaurent Pinchart size_t settings_size, const void *settings) 3733265edafSLaurent Pinchart { 3743265edafSLaurent Pinchart struct gb_camera_capture_request *req; 3753265edafSLaurent Pinchart size_t req_size; 376b9f71bc8SJohan Hovold int ret; 3773265edafSLaurent Pinchart 3783265edafSLaurent Pinchart if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) 3793265edafSLaurent Pinchart return -EINVAL; 3803265edafSLaurent Pinchart 3813265edafSLaurent Pinchart req_size = sizeof(*req) + settings_size; 3823265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 3833265edafSLaurent Pinchart if (!req) 3843265edafSLaurent Pinchart return -ENOMEM; 3853265edafSLaurent Pinchart 386c6622216SLaurent Pinchart req->request_id = cpu_to_le32(request_id); 3873265edafSLaurent Pinchart req->streams = streams; 3883265edafSLaurent Pinchart req->padding = 0; 389c6622216SLaurent Pinchart req->num_frames = cpu_to_le16(num_frames); 3903265edafSLaurent Pinchart memcpy(req->settings, settings, settings_size); 3913265edafSLaurent Pinchart 39217ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 39317ca6770SEvgeniy Borisov 39417ca6770SEvgeniy Borisov if (!gcam->connection) { 39517ca6770SEvgeniy Borisov ret = -EINVAL; 39617ca6770SEvgeniy Borisov goto done; 39717ca6770SEvgeniy Borisov } 39817ca6770SEvgeniy Borisov 399b9f71bc8SJohan Hovold ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, 4003265edafSLaurent Pinchart req, req_size, NULL, 0); 40117ca6770SEvgeniy Borisov done: 40217ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 403b9f71bc8SJohan Hovold 404b9f71bc8SJohan Hovold kfree(req); 405b9f71bc8SJohan Hovold 406b9f71bc8SJohan Hovold return ret; 4073265edafSLaurent Pinchart } 4083265edafSLaurent Pinchart 4093265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) 4103265edafSLaurent Pinchart { 4113265edafSLaurent Pinchart struct gb_camera_flush_response resp; 4123265edafSLaurent Pinchart int ret; 4133265edafSLaurent Pinchart 41417ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 41517ca6770SEvgeniy Borisov 41617ca6770SEvgeniy Borisov if (!gcam->connection) { 41717ca6770SEvgeniy Borisov ret = -EINVAL; 41817ca6770SEvgeniy Borisov goto done; 41917ca6770SEvgeniy Borisov } 42017ca6770SEvgeniy Borisov 4213265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, 4223265edafSLaurent Pinchart &resp, sizeof(resp)); 42317ca6770SEvgeniy Borisov 4243265edafSLaurent Pinchart if (ret < 0) 42517ca6770SEvgeniy Borisov goto done; 4263265edafSLaurent Pinchart 4273265edafSLaurent Pinchart if (request_id) 428c6622216SLaurent Pinchart *request_id = le32_to_cpu(resp.request_id); 4293265edafSLaurent Pinchart 43017ca6770SEvgeniy Borisov done: 43117ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 43217ca6770SEvgeniy Borisov 43317ca6770SEvgeniy Borisov return ret; 4343265edafSLaurent Pinchart } 4353265edafSLaurent Pinchart 43668b66c28SLaurent Pinchart static int gb_camera_request_handler(struct gb_operation *op) 4373265edafSLaurent Pinchart { 4380ec30632SGreg Kroah-Hartman struct gb_camera *gcam = gb_connection_get_data(op->connection); 4393265edafSLaurent Pinchart struct gb_camera_metadata_request *payload; 4403265edafSLaurent Pinchart struct gb_message *request; 4413265edafSLaurent Pinchart 44268b66c28SLaurent Pinchart if (op->type != GB_CAMERA_TYPE_METADATA) { 44368b66c28SLaurent Pinchart gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type); 4443265edafSLaurent Pinchart return -EINVAL; 4453265edafSLaurent Pinchart } 4463265edafSLaurent Pinchart 4473265edafSLaurent Pinchart request = op->request; 4483265edafSLaurent Pinchart 4493265edafSLaurent Pinchart if (request->payload_size < sizeof(*payload)) { 4503265edafSLaurent Pinchart gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", 4513265edafSLaurent Pinchart request->payload_size, sizeof(*payload)); 4523265edafSLaurent Pinchart return -EINVAL; 4533265edafSLaurent Pinchart } 4543265edafSLaurent Pinchart 4553265edafSLaurent Pinchart payload = request->payload; 4563265edafSLaurent Pinchart 4573265edafSLaurent Pinchart gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", 4583265edafSLaurent Pinchart payload->request_id, payload->frame_number, payload->stream); 4593265edafSLaurent Pinchart 4603265edafSLaurent Pinchart return 0; 4613265edafSLaurent Pinchart } 4623265edafSLaurent Pinchart 4633265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 4643a8dba4eSGjorgji Rosikopulos * Interface with HOST ara camera. 4653a8dba4eSGjorgji Rosikopulos */ 4663a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) 4673a8dba4eSGjorgji Rosikopulos { 4683a8dba4eSGjorgji Rosikopulos unsigned int i; 4693a8dba4eSGjorgji Rosikopulos 4703a8dba4eSGjorgji Rosikopulos for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { 4713a8dba4eSGjorgji Rosikopulos if (mbus_to_gbus_format[i].mbus_code == mbus_code) 4723a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[i].gb_format; 4733a8dba4eSGjorgji Rosikopulos } 4743a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[0].gb_format; 4753a8dba4eSGjorgji Rosikopulos } 4763a8dba4eSGjorgji Rosikopulos 4773a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) 4783a8dba4eSGjorgji Rosikopulos { 4793a8dba4eSGjorgji Rosikopulos unsigned int i; 4803a8dba4eSGjorgji Rosikopulos 4813a8dba4eSGjorgji Rosikopulos for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { 4823a8dba4eSGjorgji Rosikopulos if (mbus_to_gbus_format[i].gb_format == gb_fmt) 4833a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[i].mbus_code; 4843a8dba4eSGjorgji Rosikopulos } 4853a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[0].mbus_code; 4863a8dba4eSGjorgji Rosikopulos } 4873a8dba4eSGjorgji Rosikopulos 488a883b0ebSJacopo Mondi static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) 489a883b0ebSJacopo Mondi { 490a883b0ebSJacopo Mondi struct gb_camera *gcam = priv; 491a883b0ebSJacopo Mondi size_t capabilities_len = len; 492a883b0ebSJacopo Mondi int ret; 493a883b0ebSJacopo Mondi 494a883b0ebSJacopo Mondi ret = gb_camera_capabilities(gcam, data, &capabilities_len); 495a883b0ebSJacopo Mondi if (ret) 496a883b0ebSJacopo Mondi return ret; 497a883b0ebSJacopo Mondi 498a883b0ebSJacopo Mondi return capabilities_len; 499a883b0ebSJacopo Mondi } 500a883b0ebSJacopo Mondi 5015b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, 502b4905038SEvgeniy Borisov unsigned int *flags, struct gb_camera_stream *streams, 503b4905038SEvgeniy Borisov struct gb_camera_csi_params *csi_params) 5043a8dba4eSGjorgji Rosikopulos { 5053a8dba4eSGjorgji Rosikopulos struct gb_camera *gcam = priv; 5063a8dba4eSGjorgji Rosikopulos struct gb_camera_stream_config *gb_streams; 5075b032710SGjorgji Rosikopulos unsigned int gb_flags = 0; 5085b032710SGjorgji Rosikopulos unsigned int gb_nstreams = *nstreams; 5093a8dba4eSGjorgji Rosikopulos unsigned int i; 5103a8dba4eSGjorgji Rosikopulos int ret; 5113a8dba4eSGjorgji Rosikopulos 5125b032710SGjorgji Rosikopulos if (gb_nstreams > GB_CAMERA_MAX_STREAMS) 5133a8dba4eSGjorgji Rosikopulos return -EINVAL; 5143a8dba4eSGjorgji Rosikopulos 5155b032710SGjorgji Rosikopulos gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); 5163a8dba4eSGjorgji Rosikopulos if (!gb_streams) 5173a8dba4eSGjorgji Rosikopulos return -ENOMEM; 5183a8dba4eSGjorgji Rosikopulos 5195b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 5203a8dba4eSGjorgji Rosikopulos gb_streams[i].width = streams[i].width; 5213a8dba4eSGjorgji Rosikopulos gb_streams[i].height = streams[i].height; 5223a8dba4eSGjorgji Rosikopulos gb_streams[i].format = 5233a8dba4eSGjorgji Rosikopulos gb_camera_mbus_to_gb(streams[i].pixel_code); 5243a8dba4eSGjorgji Rosikopulos } 5253a8dba4eSGjorgji Rosikopulos 5265b032710SGjorgji Rosikopulos if (*flags & GB_CAMERA_IN_FLAG_TEST) 5275b032710SGjorgji Rosikopulos gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; 5285b032710SGjorgji Rosikopulos 5295b032710SGjorgji Rosikopulos ret = gb_camera_configure_streams(gcam, &gb_nstreams, 530b4905038SEvgeniy Borisov &gb_flags, gb_streams, csi_params); 5313a8dba4eSGjorgji Rosikopulos if (ret < 0) 5323a8dba4eSGjorgji Rosikopulos goto done; 5335b032710SGjorgji Rosikopulos if (gb_nstreams > *nstreams) { 5345b032710SGjorgji Rosikopulos ret = -EINVAL; 5355b032710SGjorgji Rosikopulos goto done; 5365b032710SGjorgji Rosikopulos } 5373a8dba4eSGjorgji Rosikopulos 5385b032710SGjorgji Rosikopulos *flags = 0; 5395b032710SGjorgji Rosikopulos if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) 5405b032710SGjorgji Rosikopulos *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; 5415b032710SGjorgji Rosikopulos 5425b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 5433a8dba4eSGjorgji Rosikopulos streams[i].width = gb_streams[i].width; 5443a8dba4eSGjorgji Rosikopulos streams[i].height = gb_streams[i].height; 5453a8dba4eSGjorgji Rosikopulos streams[i].vc = gb_streams[i].vc; 5463a8dba4eSGjorgji Rosikopulos streams[i].dt[0] = gb_streams[i].dt[0]; 5473a8dba4eSGjorgji Rosikopulos streams[i].dt[1] = gb_streams[i].dt[1]; 5483a8dba4eSGjorgji Rosikopulos streams[i].max_size = gb_streams[i].max_size; 5493a8dba4eSGjorgji Rosikopulos streams[i].pixel_code = 5503a8dba4eSGjorgji Rosikopulos gb_camera_gb_to_mbus(gb_streams[i].format); 5513a8dba4eSGjorgji Rosikopulos } 5525b032710SGjorgji Rosikopulos *nstreams = gb_nstreams; 5533a8dba4eSGjorgji Rosikopulos 5543a8dba4eSGjorgji Rosikopulos done: 5553a8dba4eSGjorgji Rosikopulos kfree(gb_streams); 5563a8dba4eSGjorgji Rosikopulos return ret; 5573a8dba4eSGjorgji Rosikopulos } 5583a8dba4eSGjorgji Rosikopulos 5593a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id, 5603a8dba4eSGjorgji Rosikopulos unsigned int streams, unsigned int num_frames, 5613a8dba4eSGjorgji Rosikopulos size_t settings_size, const void *settings) 5623a8dba4eSGjorgji Rosikopulos { 5631472ec67SGjorgji Rosikopulos struct gb_camera *gcam = priv; 5641472ec67SGjorgji Rosikopulos 5651472ec67SGjorgji Rosikopulos return gb_camera_capture(gcam, request_id, streams, num_frames, 5663a8dba4eSGjorgji Rosikopulos settings_size, settings); 5673a8dba4eSGjorgji Rosikopulos } 5683a8dba4eSGjorgji Rosikopulos 5693a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id) 5703a8dba4eSGjorgji Rosikopulos { 5711472ec67SGjorgji Rosikopulos struct gb_camera *gcam = priv; 5721472ec67SGjorgji Rosikopulos 5731472ec67SGjorgji Rosikopulos return gb_camera_flush(gcam, request_id); 5743a8dba4eSGjorgji Rosikopulos } 5753a8dba4eSGjorgji Rosikopulos 5761472ec67SGjorgji Rosikopulos static const struct gb_camera_ops gb_cam_ops = { 5771472ec67SGjorgji Rosikopulos .capabilities = gb_camera_op_capabilities, 5781472ec67SGjorgji Rosikopulos .configure_streams = gb_camera_op_configure_streams, 5791472ec67SGjorgji Rosikopulos .capture = gb_camera_op_capture, 5801472ec67SGjorgji Rosikopulos .flush = gb_camera_op_flush, 5811472ec67SGjorgji Rosikopulos }; 5821472ec67SGjorgji Rosikopulos 5833a8dba4eSGjorgji Rosikopulos /* ----------------------------------------------------------------------------- 5843265edafSLaurent Pinchart * DebugFS 5853265edafSLaurent Pinchart */ 58648b15a9bSLaurent Pinchart 5873265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, 5883265edafSLaurent Pinchart char *buf, size_t len) 5893265edafSLaurent Pinchart { 59048b15a9bSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 59148b15a9bSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES]; 59248b15a9bSLaurent Pinchart size_t size = 1024; 59348b15a9bSLaurent Pinchart unsigned int i; 59448b15a9bSLaurent Pinchart u8 *caps; 59548b15a9bSLaurent Pinchart int ret; 59648b15a9bSLaurent Pinchart 59748b15a9bSLaurent Pinchart caps = kmalloc(size, GFP_KERNEL); 59848b15a9bSLaurent Pinchart if (!caps) 59948b15a9bSLaurent Pinchart return -ENOMEM; 60048b15a9bSLaurent Pinchart 60148b15a9bSLaurent Pinchart ret = gb_camera_capabilities(gcam, caps, &size); 60248b15a9bSLaurent Pinchart if (ret < 0) 60348b15a9bSLaurent Pinchart goto done; 60448b15a9bSLaurent Pinchart 60548b15a9bSLaurent Pinchart /* 60648b15a9bSLaurent Pinchart * hex_dump_to_buffer() doesn't return the number of bytes dumped prior 60748b15a9bSLaurent Pinchart * to v4.0, we need our own implementation :-( 60848b15a9bSLaurent Pinchart */ 60948b15a9bSLaurent Pinchart buffer->length = 0; 61048b15a9bSLaurent Pinchart 61148b15a9bSLaurent Pinchart for (i = 0; i < size; i += 16) { 61248b15a9bSLaurent Pinchart unsigned int nbytes = min_t(unsigned int, size - i, 16); 61348b15a9bSLaurent Pinchart 61448b15a9bSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 61548b15a9bSLaurent Pinchart "%*ph\n", nbytes, caps + i); 61648b15a9bSLaurent Pinchart } 61748b15a9bSLaurent Pinchart 61848b15a9bSLaurent Pinchart done: 61948b15a9bSLaurent Pinchart kfree(caps); 62048b15a9bSLaurent Pinchart return ret; 6213265edafSLaurent Pinchart } 6223265edafSLaurent Pinchart 6233265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, 6243265edafSLaurent Pinchart char *buf, size_t len) 6253265edafSLaurent Pinchart { 6263265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 6273265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; 6283265edafSLaurent Pinchart struct gb_camera_stream_config *streams; 6293265edafSLaurent Pinchart unsigned int nstreams; 630b787d413SJacopo Mondi unsigned int flags; 6313265edafSLaurent Pinchart unsigned int i; 6323265edafSLaurent Pinchart char *token; 6333265edafSLaurent Pinchart int ret; 6343265edafSLaurent Pinchart 6353265edafSLaurent Pinchart /* Retrieve number of streams to configure */ 636b787d413SJacopo Mondi token = strsep(&buf, ";"); 6373265edafSLaurent Pinchart if (token == NULL) 6383265edafSLaurent Pinchart return -EINVAL; 6393265edafSLaurent Pinchart 6403265edafSLaurent Pinchart ret = kstrtouint(token, 10, &nstreams); 6413265edafSLaurent Pinchart if (ret < 0) 6423265edafSLaurent Pinchart return ret; 6433265edafSLaurent Pinchart 6443265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 6453265edafSLaurent Pinchart return -EINVAL; 6463265edafSLaurent Pinchart 647b787d413SJacopo Mondi token = strsep(&buf, ";"); 648b787d413SJacopo Mondi if (token == NULL) 649b787d413SJacopo Mondi return -EINVAL; 650b787d413SJacopo Mondi 651b787d413SJacopo Mondi ret = kstrtouint(token, 10, &flags); 652b787d413SJacopo Mondi if (ret < 0) 653b787d413SJacopo Mondi return ret; 654b787d413SJacopo Mondi 6553265edafSLaurent Pinchart /* For each stream to configure parse width, height and format */ 6563265edafSLaurent Pinchart streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); 6573265edafSLaurent Pinchart if (!streams) 6583265edafSLaurent Pinchart return -ENOMEM; 6593265edafSLaurent Pinchart 6603265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 6613265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 6623265edafSLaurent Pinchart 6633265edafSLaurent Pinchart /* width */ 6643265edafSLaurent Pinchart token = strsep(&buf, ";"); 6653265edafSLaurent Pinchart if (token == NULL) { 6663265edafSLaurent Pinchart ret = -EINVAL; 6673265edafSLaurent Pinchart goto done; 6683265edafSLaurent Pinchart } 6693265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->width); 6703265edafSLaurent Pinchart if (ret < 0) 6713265edafSLaurent Pinchart goto done; 6723265edafSLaurent Pinchart 6733265edafSLaurent Pinchart /* height */ 6743265edafSLaurent Pinchart token = strsep(&buf, ";"); 6753265edafSLaurent Pinchart if (token == NULL) 6763265edafSLaurent Pinchart goto done; 6773265edafSLaurent Pinchart 6783265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->height); 6793265edafSLaurent Pinchart if (ret < 0) 6803265edafSLaurent Pinchart goto done; 6813265edafSLaurent Pinchart 6823265edafSLaurent Pinchart /* Image format code */ 6833265edafSLaurent Pinchart token = strsep(&buf, ";"); 6843265edafSLaurent Pinchart if (token == NULL) 6853265edafSLaurent Pinchart goto done; 6863265edafSLaurent Pinchart 6873265edafSLaurent Pinchart ret = kstrtouint(token, 16, &stream->format); 6883265edafSLaurent Pinchart if (ret < 0) 6893265edafSLaurent Pinchart goto done; 6903265edafSLaurent Pinchart } 6913265edafSLaurent Pinchart 692b4905038SEvgeniy Borisov ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams, 693b4905038SEvgeniy Borisov NULL); 6943265edafSLaurent Pinchart if (ret < 0) 6953265edafSLaurent Pinchart goto done; 6963265edafSLaurent Pinchart 6974068487cSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); 6983265edafSLaurent Pinchart 6993265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 7003265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 7013265edafSLaurent Pinchart 7023265edafSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 7033265edafSLaurent Pinchart "%u;%u;%u;%u;%u;%u;%u;", 7043265edafSLaurent Pinchart stream->width, stream->height, 7053265edafSLaurent Pinchart stream->format, stream->vc, 7063265edafSLaurent Pinchart stream->dt[0], stream->dt[1], 7073265edafSLaurent Pinchart stream->max_size); 7083265edafSLaurent Pinchart } 7093265edafSLaurent Pinchart 7103265edafSLaurent Pinchart ret = len; 7113265edafSLaurent Pinchart 7123265edafSLaurent Pinchart done: 7133265edafSLaurent Pinchart kfree(streams); 7143265edafSLaurent Pinchart return ret; 7153265edafSLaurent Pinchart }; 7163265edafSLaurent Pinchart 7173265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, 7183265edafSLaurent Pinchart char *buf, size_t len) 7193265edafSLaurent Pinchart { 7203265edafSLaurent Pinchart unsigned int request_id; 7213265edafSLaurent Pinchart unsigned int streams_mask; 7223265edafSLaurent Pinchart unsigned int num_frames; 7233265edafSLaurent Pinchart char *token; 7243265edafSLaurent Pinchart int ret; 7253265edafSLaurent Pinchart 7263265edafSLaurent Pinchart /* Request id */ 7273265edafSLaurent Pinchart token = strsep(&buf, ";"); 7283265edafSLaurent Pinchart if (token == NULL) 7293265edafSLaurent Pinchart return -EINVAL; 7303265edafSLaurent Pinchart ret = kstrtouint(token, 10, &request_id); 7313265edafSLaurent Pinchart if (ret < 0) 7323265edafSLaurent Pinchart return ret; 7333265edafSLaurent Pinchart 7343265edafSLaurent Pinchart /* Stream mask */ 7353265edafSLaurent Pinchart token = strsep(&buf, ";"); 7363265edafSLaurent Pinchart if (token == NULL) 7373265edafSLaurent Pinchart return -EINVAL; 7383265edafSLaurent Pinchart ret = kstrtouint(token, 16, &streams_mask); 7393265edafSLaurent Pinchart if (ret < 0) 7403265edafSLaurent Pinchart return ret; 7413265edafSLaurent Pinchart 7423265edafSLaurent Pinchart /* number of frames */ 7433265edafSLaurent Pinchart token = strsep(&buf, ";"); 7443265edafSLaurent Pinchart if (token == NULL) 7453265edafSLaurent Pinchart return -EINVAL; 7463265edafSLaurent Pinchart ret = kstrtouint(token, 10, &num_frames); 7473265edafSLaurent Pinchart if (ret < 0) 7483265edafSLaurent Pinchart return ret; 7493265edafSLaurent Pinchart 7503265edafSLaurent Pinchart ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, 7513265edafSLaurent Pinchart NULL); 7523265edafSLaurent Pinchart if (ret < 0) 7533265edafSLaurent Pinchart return ret; 7543265edafSLaurent Pinchart 7553265edafSLaurent Pinchart return len; 7563265edafSLaurent Pinchart } 7573265edafSLaurent Pinchart 7583265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, 7593265edafSLaurent Pinchart char *buf, size_t len) 7603265edafSLaurent Pinchart { 7613265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 7623265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; 7633265edafSLaurent Pinchart unsigned int req_id; 7643265edafSLaurent Pinchart int ret; 7653265edafSLaurent Pinchart 7663265edafSLaurent Pinchart ret = gb_camera_flush(gcam, &req_id); 7673265edafSLaurent Pinchart if (ret < 0) 7683265edafSLaurent Pinchart return ret; 7693265edafSLaurent Pinchart 7703265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u", req_id); 7713265edafSLaurent Pinchart 7723265edafSLaurent Pinchart return len; 7733265edafSLaurent Pinchart } 7743265edafSLaurent Pinchart 7753265edafSLaurent Pinchart struct gb_camera_debugfs_entry { 7763265edafSLaurent Pinchart const char *name; 7773265edafSLaurent Pinchart unsigned int mask; 7783265edafSLaurent Pinchart unsigned int buffer; 7793265edafSLaurent Pinchart ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); 7803265edafSLaurent Pinchart }; 7813265edafSLaurent Pinchart 7823265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { 7833265edafSLaurent Pinchart { 7843265edafSLaurent Pinchart .name = "capabilities", 7853265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO, 7863265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 7873265edafSLaurent Pinchart .execute = gb_camera_debugfs_capabilities, 7883265edafSLaurent Pinchart }, { 7893265edafSLaurent Pinchart .name = "configure_streams", 7903265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 7913265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 7923265edafSLaurent Pinchart .execute = gb_camera_debugfs_configure_streams, 7933265edafSLaurent Pinchart }, { 7943265edafSLaurent Pinchart .name = "capture", 7953265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 7963265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 7973265edafSLaurent Pinchart .execute = gb_camera_debugfs_capture, 7983265edafSLaurent Pinchart }, { 7993265edafSLaurent Pinchart .name = "flush", 8003265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 8013265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 8023265edafSLaurent Pinchart .execute = gb_camera_debugfs_flush, 8033265edafSLaurent Pinchart }, 8043265edafSLaurent Pinchart }; 8053265edafSLaurent Pinchart 8063265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, 8073265edafSLaurent Pinchart size_t len, loff_t *offset) 8083265edafSLaurent Pinchart { 8093265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 8103265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 8113265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer; 8123265edafSLaurent Pinchart ssize_t ret; 8133265edafSLaurent Pinchart 8143265edafSLaurent Pinchart /* For read-only entries the operation is triggered by a read. */ 8153265edafSLaurent Pinchart if (!(op->mask & S_IWUGO)) { 8163265edafSLaurent Pinchart ret = op->execute(gcam, NULL, 0); 8173265edafSLaurent Pinchart if (ret < 0) 8183265edafSLaurent Pinchart return ret; 8193265edafSLaurent Pinchart } 8203265edafSLaurent Pinchart 8213265edafSLaurent Pinchart buffer = &gcam->debugfs.buffers[op->buffer]; 8223265edafSLaurent Pinchart 8233265edafSLaurent Pinchart return simple_read_from_buffer(buf, len, offset, buffer->data, 8243265edafSLaurent Pinchart buffer->length); 8253265edafSLaurent Pinchart } 8263265edafSLaurent Pinchart 8273265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file, 8283265edafSLaurent Pinchart const char __user *buf, size_t len, 8293265edafSLaurent Pinchart loff_t *offset) 8303265edafSLaurent Pinchart { 8313265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 8323265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 8333265edafSLaurent Pinchart ssize_t ret; 8343265edafSLaurent Pinchart char *kbuf; 8353265edafSLaurent Pinchart 8363265edafSLaurent Pinchart if (len > 1024) 8373265edafSLaurent Pinchart return -EINVAL; 8383265edafSLaurent Pinchart 8393265edafSLaurent Pinchart kbuf = kmalloc(len + 1, GFP_KERNEL); 8403265edafSLaurent Pinchart if (kbuf == NULL) 8413265edafSLaurent Pinchart return -ENOMEM; 8423265edafSLaurent Pinchart 8433265edafSLaurent Pinchart if (copy_from_user(kbuf, buf, len)) { 8443265edafSLaurent Pinchart ret = -EFAULT; 8453265edafSLaurent Pinchart goto done; 8463265edafSLaurent Pinchart } 8473265edafSLaurent Pinchart 8483265edafSLaurent Pinchart kbuf[len] = '\0'; 8493265edafSLaurent Pinchart 8503265edafSLaurent Pinchart ret = op->execute(gcam, kbuf, len); 8513265edafSLaurent Pinchart 8523265edafSLaurent Pinchart done: 8533265edafSLaurent Pinchart kfree(kbuf); 8543265edafSLaurent Pinchart return ret; 8553265edafSLaurent Pinchart } 8563265edafSLaurent Pinchart 8573265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file) 8583265edafSLaurent Pinchart { 8593265edafSLaurent Pinchart unsigned int i; 8603265edafSLaurent Pinchart 8613265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 8623265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 8633265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 8643265edafSLaurent Pinchart 8654dda744cSGreg Kroah-Hartman if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { 8663265edafSLaurent Pinchart file->private_data = (void *)entry; 8673265edafSLaurent Pinchart break; 8683265edafSLaurent Pinchart } 8693265edafSLaurent Pinchart } 8703265edafSLaurent Pinchart 8713265edafSLaurent Pinchart return 0; 8723265edafSLaurent Pinchart } 8733265edafSLaurent Pinchart 8743265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = { 8753265edafSLaurent Pinchart .open = gb_camera_debugfs_open, 8763265edafSLaurent Pinchart .read = gb_camera_debugfs_read, 8773265edafSLaurent Pinchart .write = gb_camera_debugfs_write, 8783265edafSLaurent Pinchart }; 8793265edafSLaurent Pinchart 8803265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam) 8813265edafSLaurent Pinchart { 8823265edafSLaurent Pinchart struct gb_connection *connection = gcam->connection; 8833265edafSLaurent Pinchart char dirname[27]; 8843265edafSLaurent Pinchart unsigned int i; 8853265edafSLaurent Pinchart 8863265edafSLaurent Pinchart /* 8873265edafSLaurent Pinchart * Create root debugfs entry and a file entry for each camera operation. 8883265edafSLaurent Pinchart */ 8893265edafSLaurent Pinchart snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, 89068b66c28SLaurent Pinchart gcam->bundle->id); 8913265edafSLaurent Pinchart 8923265edafSLaurent Pinchart gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); 8933265edafSLaurent Pinchart if (IS_ERR(gcam->debugfs.root)) { 8943265edafSLaurent Pinchart gcam_err(gcam, "debugfs root create failed (%ld)\n", 8953265edafSLaurent Pinchart PTR_ERR(gcam->debugfs.root)); 8963265edafSLaurent Pinchart return PTR_ERR(gcam->debugfs.root); 8973265edafSLaurent Pinchart } 8983265edafSLaurent Pinchart 8993265edafSLaurent Pinchart gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * 9003265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX); 9013265edafSLaurent Pinchart if (!gcam->debugfs.buffers) 9023265edafSLaurent Pinchart return -ENOMEM; 9033265edafSLaurent Pinchart 9043265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 9053265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 9063265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 9073265edafSLaurent Pinchart struct dentry *dentry; 9083265edafSLaurent Pinchart 9093265edafSLaurent Pinchart gcam->debugfs.buffers[i].length = 0; 9103265edafSLaurent Pinchart 9113265edafSLaurent Pinchart dentry = debugfs_create_file(entry->name, entry->mask, 9123265edafSLaurent Pinchart gcam->debugfs.root, gcam, 9133265edafSLaurent Pinchart &gb_camera_debugfs_ops); 9143265edafSLaurent Pinchart if (IS_ERR(dentry)) { 9153265edafSLaurent Pinchart gcam_err(gcam, 9163265edafSLaurent Pinchart "debugfs operation %s create failed (%ld)\n", 917f9340fc7SAlex Elder entry->name, PTR_ERR(dentry)); 9183265edafSLaurent Pinchart return PTR_ERR(dentry); 9193265edafSLaurent Pinchart } 9203265edafSLaurent Pinchart } 9213265edafSLaurent Pinchart 9223265edafSLaurent Pinchart return 0; 9233265edafSLaurent Pinchart } 9243265edafSLaurent Pinchart 9253265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) 9263265edafSLaurent Pinchart { 9273265edafSLaurent Pinchart debugfs_remove_recursive(gcam->debugfs.root); 9283265edafSLaurent Pinchart 9293265edafSLaurent Pinchart vfree(gcam->debugfs.buffers); 9303265edafSLaurent Pinchart } 9313265edafSLaurent Pinchart 9323265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 9333265edafSLaurent Pinchart * Init & Cleanup 9343265edafSLaurent Pinchart */ 9353265edafSLaurent Pinchart 9363265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam) 9373265edafSLaurent Pinchart { 9383265edafSLaurent Pinchart gb_camera_debugfs_cleanup(gcam); 9393265edafSLaurent Pinchart 9403ba9fa5cSJohan Hovold if (gcam->data_connection) { 9413ba9fa5cSJohan Hovold gb_connection_disable(gcam->data_connection); 9423ba9fa5cSJohan Hovold gb_connection_destroy(gcam->data_connection); 94317ca6770SEvgeniy Borisov gcam->data_connection = NULL; 9443265edafSLaurent Pinchart } 9453265edafSLaurent Pinchart 94617ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 94768b66c28SLaurent Pinchart if (gcam->connection) { 94868b66c28SLaurent Pinchart gb_connection_disable(gcam->connection); 94968b66c28SLaurent Pinchart gb_connection_destroy(gcam->connection); 95017ca6770SEvgeniy Borisov gcam->connection = NULL; 95117ca6770SEvgeniy Borisov } 95217ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 95368b66c28SLaurent Pinchart } 95468b66c28SLaurent Pinchart 95517ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref) 95617ca6770SEvgeniy Borisov { 95717ca6770SEvgeniy Borisov struct gb_camera_module *cam_mod = 95817ca6770SEvgeniy Borisov container_of(ref, struct gb_camera_module, refcount); 95917ca6770SEvgeniy Borisov kfree(cam_mod->priv); 9603265edafSLaurent Pinchart } 9613265edafSLaurent Pinchart 96268b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle, 96368b66c28SLaurent Pinchart const struct greybus_bundle_id *id) 9643265edafSLaurent Pinchart { 96568b66c28SLaurent Pinchart struct gb_connection *conn; 9663265edafSLaurent Pinchart struct gb_camera *gcam; 96768b66c28SLaurent Pinchart u16 mgmt_cport_id = 0; 96868b66c28SLaurent Pinchart u16 data_cport_id = 0; 96968b66c28SLaurent Pinchart unsigned int i; 9703265edafSLaurent Pinchart int ret; 9713265edafSLaurent Pinchart 97268b66c28SLaurent Pinchart /* 97368b66c28SLaurent Pinchart * The camera bundle must contain exactly two CPorts, one for the 97468b66c28SLaurent Pinchart * camera management protocol and one for the camera data protocol. 97568b66c28SLaurent Pinchart */ 97668b66c28SLaurent Pinchart if (bundle->num_cports != 2) 97768b66c28SLaurent Pinchart return -ENODEV; 97868b66c28SLaurent Pinchart 97968b66c28SLaurent Pinchart for (i = 0; i < bundle->num_cports; ++i) { 98068b66c28SLaurent Pinchart struct greybus_descriptor_cport *desc = &bundle->cport_desc[i]; 98168b66c28SLaurent Pinchart 98268b66c28SLaurent Pinchart switch (desc->protocol_id) { 98368b66c28SLaurent Pinchart case GREYBUS_PROTOCOL_CAMERA_MGMT: 98468b66c28SLaurent Pinchart mgmt_cport_id = le16_to_cpu(desc->id); 98568b66c28SLaurent Pinchart break; 98668b66c28SLaurent Pinchart case GREYBUS_PROTOCOL_CAMERA_DATA: 98768b66c28SLaurent Pinchart data_cport_id = le16_to_cpu(desc->id); 98868b66c28SLaurent Pinchart break; 98968b66c28SLaurent Pinchart default: 99068b66c28SLaurent Pinchart return -ENODEV; 99168b66c28SLaurent Pinchart } 99268b66c28SLaurent Pinchart } 99368b66c28SLaurent Pinchart 99468b66c28SLaurent Pinchart if (!mgmt_cport_id || !data_cport_id) 99568b66c28SLaurent Pinchart return -ENODEV; 99668b66c28SLaurent Pinchart 9973265edafSLaurent Pinchart gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); 9983265edafSLaurent Pinchart if (!gcam) 9993265edafSLaurent Pinchart return -ENOMEM; 10003265edafSLaurent Pinchart 100168b66c28SLaurent Pinchart gcam->bundle = bundle; 1002d9e4c4eeSViresh Kumar mutex_init(&gcam->mutex); 10033265edafSLaurent Pinchart 100468b66c28SLaurent Pinchart conn = gb_connection_create(bundle, mgmt_cport_id, 100568b66c28SLaurent Pinchart gb_camera_request_handler); 100668b66c28SLaurent Pinchart if (IS_ERR(conn)) { 100768b66c28SLaurent Pinchart ret = PTR_ERR(conn); 10083ba9fa5cSJohan Hovold goto error; 10093ba9fa5cSJohan Hovold } 10103ba9fa5cSJohan Hovold 101168b66c28SLaurent Pinchart gcam->connection = conn; 101268b66c28SLaurent Pinchart gb_connection_set_data(conn, gcam); 101368b66c28SLaurent Pinchart 101468b66c28SLaurent Pinchart ret = gb_connection_enable(conn); 101568b66c28SLaurent Pinchart if (ret) 101668b66c28SLaurent Pinchart goto error; 101768b66c28SLaurent Pinchart 101868b66c28SLaurent Pinchart /* 101968b66c28SLaurent Pinchart * Create the data connection between the camera module data CPort and 102068b66c28SLaurent Pinchart * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. 102168b66c28SLaurent Pinchart */ 102268b66c28SLaurent Pinchart conn = gb_connection_create_offloaded(bundle, data_cport_id, 102368b66c28SLaurent Pinchart GB_CONNECTION_FLAG_NO_FLOWCTRL | 102468b66c28SLaurent Pinchart GB_CONNECTION_FLAG_CDSI1); 102568b66c28SLaurent Pinchart if (IS_ERR(conn)) { 102668b66c28SLaurent Pinchart ret = PTR_ERR(conn); 102768b66c28SLaurent Pinchart goto error; 102868b66c28SLaurent Pinchart } 102968b66c28SLaurent Pinchart gcam->data_connection = conn; 103068b66c28SLaurent Pinchart gb_connection_set_data(conn, gcam); 103168b66c28SLaurent Pinchart 103268b66c28SLaurent Pinchart ret = gb_connection_enable(conn); 10333ba9fa5cSJohan Hovold if (ret) 10343ba9fa5cSJohan Hovold goto error; 103541c23958SJohan Hovold 10363265edafSLaurent Pinchart ret = gb_camera_debugfs_init(gcam); 10373265edafSLaurent Pinchart if (ret < 0) 10383265edafSLaurent Pinchart goto error; 10393265edafSLaurent Pinchart 104017ca6770SEvgeniy Borisov gcam->module.priv = gcam; 104117ca6770SEvgeniy Borisov gcam->module.ops = &gb_cam_ops; 104217ca6770SEvgeniy Borisov gcam->module.interface_id = gcam->connection->intf->interface_id; 104317ca6770SEvgeniy Borisov gcam->module.release = gb_camera_release_module; 104417ca6770SEvgeniy Borisov ret = gb_camera_register(&gcam->module); 10453a8dba4eSGjorgji Rosikopulos if (ret < 0) 10463a8dba4eSGjorgji Rosikopulos goto error; 10473a8dba4eSGjorgji Rosikopulos 104868b66c28SLaurent Pinchart greybus_set_drvdata(bundle, gcam); 104968b66c28SLaurent Pinchart 10503265edafSLaurent Pinchart return 0; 10513265edafSLaurent Pinchart 10523265edafSLaurent Pinchart error: 10533265edafSLaurent Pinchart gb_camera_cleanup(gcam); 105417ca6770SEvgeniy Borisov kfree(gcam); 10553265edafSLaurent Pinchart return ret; 10563265edafSLaurent Pinchart } 10573265edafSLaurent Pinchart 105868b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle) 10593265edafSLaurent Pinchart { 106068b66c28SLaurent Pinchart struct gb_camera *gcam = greybus_get_drvdata(bundle); 10613265edafSLaurent Pinchart 10623265edafSLaurent Pinchart gb_camera_cleanup(gcam); 106317ca6770SEvgeniy Borisov gb_camera_unregister(&gcam->module); 10643265edafSLaurent Pinchart } 10653265edafSLaurent Pinchart 106668b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = { 106768b66c28SLaurent Pinchart { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, 106868b66c28SLaurent Pinchart { }, 10693265edafSLaurent Pinchart }; 10703265edafSLaurent Pinchart 107168b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = { 107268b66c28SLaurent Pinchart .name = "camera", 107368b66c28SLaurent Pinchart .probe = gb_camera_probe, 107468b66c28SLaurent Pinchart .disconnect = gb_camera_disconnect, 107568b66c28SLaurent Pinchart .id_table = gb_camera_id_table, 107668b66c28SLaurent Pinchart }; 107768b66c28SLaurent Pinchart 107868b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver); 10793265edafSLaurent Pinchart 10803265edafSLaurent Pinchart MODULE_LICENSE("GPL v2"); 1081