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 363b8ebfebSLaurent Pinchart enum gb_camera_state { 373b8ebfebSLaurent Pinchart GB_CAMERA_STATE_UNCONFIGURED, 383b8ebfebSLaurent Pinchart GB_CAMERA_STATE_CONFIGURED, 393b8ebfebSLaurent Pinchart }; 403b8ebfebSLaurent Pinchart 413265edafSLaurent Pinchart /** 423265edafSLaurent Pinchart * struct gb_camera - A Greybus Camera Device 4317ca6770SEvgeniy Borisov * @connection: the greybus connection for camera management 4417ca6770SEvgeniy Borisov * @data_connection: the greybus connection for camera data 453b8ebfebSLaurent Pinchart * @mutex: protects the connection and state fields 463b8ebfebSLaurent Pinchart * @state: the current module state 473265edafSLaurent Pinchart * @debugfs: debugfs entries for camera protocol operations testing 48c3d77f71SGjorgji Rosikopulos * @module: Greybus camera module registered to HOST processor. 493265edafSLaurent Pinchart */ 503265edafSLaurent Pinchart struct gb_camera { 5168b66c28SLaurent Pinchart struct gb_bundle *bundle; 523265edafSLaurent Pinchart struct gb_connection *connection; 533ba9fa5cSJohan Hovold struct gb_connection *data_connection; 543b8ebfebSLaurent Pinchart 5517ca6770SEvgeniy Borisov struct mutex mutex; 563b8ebfebSLaurent Pinchart enum gb_camera_state state; 573265edafSLaurent Pinchart 583265edafSLaurent Pinchart struct { 593265edafSLaurent Pinchart struct dentry *root; 603265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffers; 613265edafSLaurent Pinchart } debugfs; 62c3d77f71SGjorgji Rosikopulos 63c3d77f71SGjorgji Rosikopulos struct gb_camera_module module; 643265edafSLaurent Pinchart }; 653265edafSLaurent Pinchart 663265edafSLaurent Pinchart struct gb_camera_stream_config { 673265edafSLaurent Pinchart unsigned int width; 683265edafSLaurent Pinchart unsigned int height; 693265edafSLaurent Pinchart unsigned int format; 703265edafSLaurent Pinchart unsigned int vc; 713265edafSLaurent Pinchart unsigned int dt[2]; 723265edafSLaurent Pinchart unsigned int max_size; 733265edafSLaurent Pinchart }; 743265edafSLaurent Pinchart 753a8dba4eSGjorgji Rosikopulos struct gb_camera_fmt_map { 763a8dba4eSGjorgji Rosikopulos enum v4l2_mbus_pixelcode mbus_code; 773a8dba4eSGjorgji Rosikopulos unsigned int gb_format; 783a8dba4eSGjorgji Rosikopulos }; 793a8dba4eSGjorgji Rosikopulos 803a8dba4eSGjorgji Rosikopulos /* GB format to media code map */ 813a8dba4eSGjorgji Rosikopulos static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { 823a8dba4eSGjorgji Rosikopulos { 833a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, 843a8dba4eSGjorgji Rosikopulos .gb_format = 0x01, 853a8dba4eSGjorgji Rosikopulos }, 863a8dba4eSGjorgji Rosikopulos { 877c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV12_1x8, 887c154711SGjorgji Rosikopulos .gb_format = 0x12, 897c154711SGjorgji Rosikopulos }, 907c154711SGjorgji Rosikopulos { 917c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV21_1x8, 927c154711SGjorgji Rosikopulos .gb_format = 0x13, 937c154711SGjorgji Rosikopulos }, 947c154711SGjorgji Rosikopulos { 957c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YU12_1x8, 963a8dba4eSGjorgji Rosikopulos .gb_format = 0x16, 973a8dba4eSGjorgji Rosikopulos }, 983a8dba4eSGjorgji Rosikopulos { 997c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YV12_1x8, 1003a8dba4eSGjorgji Rosikopulos .gb_format = 0x17, 1013a8dba4eSGjorgji Rosikopulos }, 1023a8dba4eSGjorgji Rosikopulos { 1033a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, 1043a8dba4eSGjorgji Rosikopulos .gb_format = 0x40, 105dc5cc72cSGjorgji Rosikopulos }, 106dc5cc72cSGjorgji Rosikopulos { 107dc5cc72cSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8, 108dc5cc72cSGjorgji Rosikopulos .gb_format = 0x41, 109cb14e976SGjorgji Rosikopulos }, 110cb14e976SGjorgji Rosikopulos { 111cb14e976SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8, 112cb14e976SGjorgji Rosikopulos .gb_format = 0x42, 113cb14e976SGjorgji Rosikopulos }, 1143a8dba4eSGjorgji Rosikopulos }; 1153a8dba4eSGjorgji Rosikopulos 1163265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT 16 1173265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT 17 1183265edafSLaurent Pinchart 1193265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE 8192 1203265edafSLaurent Pinchart 12168b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...) dev_dbg(&gcam->bundle->dev, format) 12268b66c28SLaurent Pinchart #define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) 12368b66c28SLaurent Pinchart #define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) 1243265edafSLaurent Pinchart 1253265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 126f3d5f661SLaurent Pinchart * Hardware Configuration 1273265edafSLaurent Pinchart */ 1283265edafSLaurent Pinchart 129c161c0fcSLaurent Pinchart static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, 130c161c0fcSLaurent Pinchart bool hs) 131c161c0fcSLaurent Pinchart { 132c161c0fcSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 133c161c0fcSLaurent Pinchart int ret; 134c161c0fcSLaurent Pinchart 135c161c0fcSLaurent Pinchart if (hs) 136c161c0fcSLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, intf_id, 137c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 138c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 1398c2522d8SEli Sennesh GB_SVC_SMALL_AMPLITUDE, 1408c2522d8SEli Sennesh GB_SVC_NO_DE_EMPHASIS, 141c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 142c161c0fcSLaurent Pinchart GB_SVC_PWRM_RXTERMINATION | 1438c2522d8SEli Sennesh GB_SVC_PWRM_TXTERMINATION, 0, 1448c2522d8SEli Sennesh NULL, NULL); 145c161c0fcSLaurent Pinchart else 146c161c0fcSLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, intf_id, 147c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 148c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_SLOW_AUTO_MODE, 149ee2f2074SMitchell Tasman 2, 1, 1508c2522d8SEli Sennesh GB_SVC_SMALL_AMPLITUDE, 1518c2522d8SEli Sennesh GB_SVC_NO_DE_EMPHASIS, 152c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_SLOW_AUTO_MODE, 153ee2f2074SMitchell Tasman 2, 1, 1548c2522d8SEli Sennesh 0, 0, 1558c2522d8SEli Sennesh NULL, NULL); 156c161c0fcSLaurent Pinchart 157c161c0fcSLaurent Pinchart return ret; 158c161c0fcSLaurent Pinchart } 159c161c0fcSLaurent Pinchart 160c161c0fcSLaurent Pinchart static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) 161c161c0fcSLaurent Pinchart { 162c161c0fcSLaurent Pinchart struct gb_interface *intf = gcam->connection->intf; 163c161c0fcSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 164c161c0fcSLaurent Pinchart int ret; 165c161c0fcSLaurent Pinchart 166c161c0fcSLaurent Pinchart ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs); 167c161c0fcSLaurent Pinchart if (ret < 0) { 168c161c0fcSLaurent Pinchart gcam_err(gcam, "failed to set module interface to %s (%d)\n", 169c161c0fcSLaurent Pinchart hs ? "HS" : "PWM", ret); 170c161c0fcSLaurent Pinchart return ret; 171c161c0fcSLaurent Pinchart } 172c161c0fcSLaurent Pinchart 173c161c0fcSLaurent Pinchart ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); 174c161c0fcSLaurent Pinchart if (ret < 0) { 175b573b0e6SLaurent Pinchart gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs); 176c161c0fcSLaurent Pinchart gcam_err(gcam, "failed to set AP interface to %s (%d)\n", 177c161c0fcSLaurent Pinchart hs ? "HS" : "PWM", ret); 178c161c0fcSLaurent Pinchart return ret; 179c161c0fcSLaurent Pinchart } 180c161c0fcSLaurent Pinchart 181c161c0fcSLaurent Pinchart return 0; 182c161c0fcSLaurent Pinchart } 183c161c0fcSLaurent Pinchart 184f3d5f661SLaurent Pinchart struct ap_csi_config_request { 185f3d5f661SLaurent Pinchart __u8 csi_id; 186f3d5f661SLaurent Pinchart __u8 flags; 187f3d5f661SLaurent Pinchart #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 188f3d5f661SLaurent Pinchart __u8 num_lanes; 189f3d5f661SLaurent Pinchart __u8 padding; 190f3d5f661SLaurent Pinchart __le32 bus_freq; 191f3d5f661SLaurent Pinchart __le32 lines_per_second; 192f3d5f661SLaurent Pinchart } __packed; 193f3d5f661SLaurent Pinchart 194f3d5f661SLaurent Pinchart static int gb_camera_setup_data_connection(struct gb_camera *gcam, 195f3d5f661SLaurent Pinchart const struct gb_camera_configure_streams_response *resp, 196f3d5f661SLaurent Pinchart struct gb_camera_csi_params *csi_params) 197f3d5f661SLaurent Pinchart { 198f3d5f661SLaurent Pinchart struct ap_csi_config_request csi_cfg; 199f3d5f661SLaurent Pinchart int ret; 200f3d5f661SLaurent Pinchart 201f3d5f661SLaurent Pinchart /* Set the UniPro link to high speed mode. */ 202f3d5f661SLaurent Pinchart ret = gb_camera_set_power_mode(gcam, true); 203f3d5f661SLaurent Pinchart if (ret < 0) 204f3d5f661SLaurent Pinchart return ret; 205f3d5f661SLaurent Pinchart 206f3d5f661SLaurent Pinchart /* 207f3d5f661SLaurent Pinchart * Configure the APB1 CSI transmitter using the lines count reported by 208f3d5f661SLaurent Pinchart * the camera module, but with hard-coded bus frequency and lanes number. 209f3d5f661SLaurent Pinchart * 210f3d5f661SLaurent Pinchart * TODO: use the clocking and size informations reported by camera module 211f3d5f661SLaurent Pinchart * to compute the required CSI bandwidth, and configure the CSI receiver 212f3d5f661SLaurent Pinchart * on AP side, and the CSI transmitter on APB1 side accordingly. 213f3d5f661SLaurent Pinchart */ 214f3d5f661SLaurent Pinchart memset(&csi_cfg, 0, sizeof(csi_cfg)); 215f3d5f661SLaurent Pinchart csi_cfg.csi_id = 1; 216f3d5f661SLaurent Pinchart csi_cfg.flags = 0; 217f3d5f661SLaurent Pinchart csi_cfg.num_lanes = resp->num_lanes; 218f3d5f661SLaurent Pinchart csi_cfg.bus_freq = cpu_to_le32(960000000); 219f3d5f661SLaurent Pinchart csi_cfg.lines_per_second = resp->lines_per_second; 220f3d5f661SLaurent Pinchart 221f3d5f661SLaurent Pinchart ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 222f3d5f661SLaurent Pinchart sizeof(csi_cfg), 223f3d5f661SLaurent Pinchart GB_APB_REQUEST_CSI_TX_CONTROL, false); 224f3d5f661SLaurent Pinchart 225f3d5f661SLaurent Pinchart if (ret < 0) { 226f3d5f661SLaurent Pinchart gcam_err(gcam, "failed to start the CSI transmitter\n"); 227f3d5f661SLaurent Pinchart gb_camera_set_power_mode(gcam, false); 228f3d5f661SLaurent Pinchart return ret; 229f3d5f661SLaurent Pinchart } 230f3d5f661SLaurent Pinchart 231f3d5f661SLaurent Pinchart if (csi_params) { 232f3d5f661SLaurent Pinchart csi_params->num_lanes = csi_cfg.num_lanes; 233f3d5f661SLaurent Pinchart /* Transmitting two bits per cycle. (DDR clock) */ 234f3d5f661SLaurent Pinchart csi_params->clk_freq = csi_cfg.bus_freq / 2; 235f3d5f661SLaurent Pinchart csi_params->lines_per_second = csi_cfg.lines_per_second; 236f3d5f661SLaurent Pinchart } 237f3d5f661SLaurent Pinchart 238f3d5f661SLaurent Pinchart return 0; 239f3d5f661SLaurent Pinchart } 240f3d5f661SLaurent Pinchart 241f3d5f661SLaurent Pinchart static void gb_camera_teardown_data_connection(struct gb_camera *gcam) 242f3d5f661SLaurent Pinchart { 243f3d5f661SLaurent Pinchart struct ap_csi_config_request csi_cfg; 244f3d5f661SLaurent Pinchart int ret; 245f3d5f661SLaurent Pinchart 246f3d5f661SLaurent Pinchart /* Stop the APB1 CSI transmitter. */ 247f3d5f661SLaurent Pinchart memset(&csi_cfg, 0, sizeof(csi_cfg)); 248f3d5f661SLaurent Pinchart csi_cfg.csi_id = 1; 249f3d5f661SLaurent Pinchart 250f3d5f661SLaurent Pinchart ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 251f3d5f661SLaurent Pinchart sizeof(csi_cfg), 252f3d5f661SLaurent Pinchart GB_APB_REQUEST_CSI_TX_CONTROL, false); 253f3d5f661SLaurent Pinchart 254f3d5f661SLaurent Pinchart if (ret < 0) 255f3d5f661SLaurent Pinchart gcam_err(gcam, "failed to stop the CSI transmitter\n"); 256f3d5f661SLaurent Pinchart 257f3d5f661SLaurent Pinchart /* Set the UniPro link to low speed mode. */ 258f3d5f661SLaurent Pinchart gb_camera_set_power_mode(gcam, false); 259f3d5f661SLaurent Pinchart } 260f3d5f661SLaurent Pinchart 261f3d5f661SLaurent Pinchart /* ----------------------------------------------------------------------------- 262f3d5f661SLaurent Pinchart * Camera Protocol Operations 263f3d5f661SLaurent Pinchart */ 264f3d5f661SLaurent Pinchart 26548b15a9bSLaurent Pinchart static int gb_camera_capabilities(struct gb_camera *gcam, 26648b15a9bSLaurent Pinchart u8 *capabilities, size_t *size) 26748b15a9bSLaurent Pinchart { 26817ca6770SEvgeniy Borisov struct gb_operation *op = NULL; 26948b15a9bSLaurent Pinchart int ret; 27048b15a9bSLaurent Pinchart 27117ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 27217ca6770SEvgeniy Borisov 27317ca6770SEvgeniy Borisov if (!gcam->connection) { 27417ca6770SEvgeniy Borisov ret = -EINVAL; 27517ca6770SEvgeniy Borisov goto done; 27617ca6770SEvgeniy Borisov } 27717ca6770SEvgeniy Borisov 27848b15a9bSLaurent Pinchart op = gb_operation_create_flags(gcam->connection, 27948b15a9bSLaurent Pinchart GB_CAMERA_TYPE_CAPABILITIES, 0, *size, 28048b15a9bSLaurent Pinchart GB_OPERATION_FLAG_SHORT_RESPONSE, 28148b15a9bSLaurent Pinchart GFP_KERNEL); 28217ca6770SEvgeniy Borisov if (!op) { 28317ca6770SEvgeniy Borisov ret = -ENOMEM; 28417ca6770SEvgeniy Borisov goto done; 28517ca6770SEvgeniy Borisov } 28648b15a9bSLaurent Pinchart 28748b15a9bSLaurent Pinchart ret = gb_operation_request_send_sync(op); 28848b15a9bSLaurent Pinchart if (ret) { 28948b15a9bSLaurent Pinchart gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); 29048b15a9bSLaurent Pinchart goto done; 29148b15a9bSLaurent Pinchart } 29248b15a9bSLaurent Pinchart 29348b15a9bSLaurent Pinchart memcpy(capabilities, op->response->payload, op->response->payload_size); 29448b15a9bSLaurent Pinchart *size = op->response->payload_size; 29548b15a9bSLaurent Pinchart 29648b15a9bSLaurent Pinchart done: 29717ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 29817ca6770SEvgeniy Borisov if (op) 29948b15a9bSLaurent Pinchart gb_operation_put(op); 30048b15a9bSLaurent Pinchart return ret; 30148b15a9bSLaurent Pinchart } 30248b15a9bSLaurent Pinchart 3033265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam, 3044068487cSLaurent Pinchart unsigned int *num_streams, 3054068487cSLaurent Pinchart unsigned int *flags, 306b4905038SEvgeniy Borisov struct gb_camera_stream_config *streams, 307b4905038SEvgeniy Borisov struct gb_camera_csi_params *csi_params) 3083265edafSLaurent Pinchart { 3093265edafSLaurent Pinchart struct gb_camera_configure_streams_request *req; 3103265edafSLaurent Pinchart struct gb_camera_configure_streams_response *resp; 3114068487cSLaurent Pinchart unsigned int nstreams = *num_streams; 3123265edafSLaurent Pinchart unsigned int i; 3133265edafSLaurent Pinchart size_t req_size; 3143265edafSLaurent Pinchart size_t resp_size; 3153265edafSLaurent Pinchart int ret; 3163265edafSLaurent Pinchart 3173265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 3183265edafSLaurent Pinchart return -EINVAL; 3193265edafSLaurent Pinchart 3203265edafSLaurent Pinchart req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); 3213265edafSLaurent Pinchart resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); 3223265edafSLaurent Pinchart 3233265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 3243265edafSLaurent Pinchart resp = kmalloc(resp_size, GFP_KERNEL); 3253265edafSLaurent Pinchart if (!req || !resp) { 32617ca6770SEvgeniy Borisov kfree(req); 32717ca6770SEvgeniy Borisov kfree(resp); 32817ca6770SEvgeniy Borisov return -ENOMEM; 3293265edafSLaurent Pinchart } 3303265edafSLaurent Pinchart 331b787d413SJacopo Mondi req->num_streams = nstreams; 3324068487cSLaurent Pinchart req->flags = *flags; 3333265edafSLaurent Pinchart req->padding = 0; 3343265edafSLaurent Pinchart 3353265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 3363265edafSLaurent Pinchart struct gb_camera_stream_config_request *cfg = &req->config[i]; 3373265edafSLaurent Pinchart 338c6622216SLaurent Pinchart cfg->width = cpu_to_le16(streams[i].width); 339c6622216SLaurent Pinchart cfg->height = cpu_to_le16(streams[i].height); 340c6622216SLaurent Pinchart cfg->format = cpu_to_le16(streams[i].format); 3413265edafSLaurent Pinchart cfg->padding = 0; 3423265edafSLaurent Pinchart } 3433265edafSLaurent Pinchart 34417ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 34517ca6770SEvgeniy Borisov 34617ca6770SEvgeniy Borisov if (!gcam->connection) { 34717ca6770SEvgeniy Borisov ret = -EINVAL; 34817ca6770SEvgeniy Borisov goto done; 34917ca6770SEvgeniy Borisov } 35017ca6770SEvgeniy Borisov 3513265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, 3523265edafSLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 3533265edafSLaurent Pinchart req, req_size, resp, resp_size); 3543265edafSLaurent Pinchart if (ret < 0) 35566c36070SLaurent Pinchart goto done; 3563265edafSLaurent Pinchart 357b787d413SJacopo Mondi if (resp->num_streams > nstreams) { 3583265edafSLaurent Pinchart gcam_dbg(gcam, "got #streams %u > request %u\n", 359b787d413SJacopo Mondi resp->num_streams, nstreams); 3603265edafSLaurent Pinchart ret = -EIO; 36166c36070SLaurent Pinchart goto done; 3623265edafSLaurent Pinchart } 3633265edafSLaurent Pinchart 3643265edafSLaurent Pinchart if (resp->padding != 0) { 3653265edafSLaurent Pinchart gcam_dbg(gcam, "response padding != 0"); 3663265edafSLaurent Pinchart ret = -EIO; 36766c36070SLaurent Pinchart goto done; 3683265edafSLaurent Pinchart } 3693265edafSLaurent Pinchart 3703265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 3713265edafSLaurent Pinchart struct gb_camera_stream_config_response *cfg = &resp->config[i]; 3723265edafSLaurent Pinchart 373c6622216SLaurent Pinchart streams[i].width = le16_to_cpu(cfg->width); 374c6622216SLaurent Pinchart streams[i].height = le16_to_cpu(cfg->height); 375c6622216SLaurent Pinchart streams[i].format = le16_to_cpu(cfg->format); 3763265edafSLaurent Pinchart streams[i].vc = cfg->virtual_channel; 3773265edafSLaurent Pinchart streams[i].dt[0] = cfg->data_type[0]; 3783265edafSLaurent Pinchart streams[i].dt[1] = cfg->data_type[1]; 379c6622216SLaurent Pinchart streams[i].max_size = le32_to_cpu(cfg->max_size); 3803265edafSLaurent Pinchart 3813265edafSLaurent Pinchart if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { 3823265edafSLaurent Pinchart gcam_dbg(gcam, "stream #%u padding != 0", i); 3833265edafSLaurent Pinchart ret = -EIO; 38466c36070SLaurent Pinchart goto done; 3853265edafSLaurent Pinchart } 3863265edafSLaurent Pinchart } 3873265edafSLaurent Pinchart 388640924d2SLaurent Pinchart if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || 389640924d2SLaurent Pinchart (*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { 390f121d79dSJacopo Mondi *flags = resp->flags; 391f121d79dSJacopo Mondi *num_streams = resp->num_streams; 39266c36070SLaurent Pinchart goto done; 39366c36070SLaurent Pinchart } 39466c36070SLaurent Pinchart 3953b8ebfebSLaurent Pinchart if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { 3963b8ebfebSLaurent Pinchart gb_camera_teardown_data_connection(gcam); 3973b8ebfebSLaurent Pinchart gcam->state = GB_CAMERA_STATE_UNCONFIGURED; 3983b8ebfebSLaurent Pinchart } 3993b8ebfebSLaurent Pinchart 400f3d5f661SLaurent Pinchart if (resp->num_streams) { 401f3d5f661SLaurent Pinchart ret = gb_camera_setup_data_connection(gcam, resp, csi_params); 402f3d5f661SLaurent Pinchart if (ret < 0) { 403f3d5f661SLaurent Pinchart memset(req, 0, sizeof(*req)); 404f3d5f661SLaurent Pinchart gb_operation_sync(gcam->connection, 405f3d5f661SLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 406f3d5f661SLaurent Pinchart req, req_size, resp, resp_size); 40766c36070SLaurent Pinchart goto done; 408b4905038SEvgeniy Borisov } 4093b8ebfebSLaurent Pinchart 4103b8ebfebSLaurent Pinchart gcam->state = GB_CAMERA_STATE_CONFIGURED; 411142b21feSLaurent Pinchart } 412142b21feSLaurent Pinchart 413f121d79dSJacopo Mondi *flags = resp->flags; 4144068487cSLaurent Pinchart *num_streams = resp->num_streams; 4153265edafSLaurent Pinchart 4163265edafSLaurent Pinchart done: 41717ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 4183265edafSLaurent Pinchart kfree(req); 4193265edafSLaurent Pinchart kfree(resp); 4203265edafSLaurent Pinchart return ret; 4213265edafSLaurent Pinchart } 4223265edafSLaurent Pinchart 4233265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, 4243265edafSLaurent Pinchart unsigned int streams, unsigned int num_frames, 4253265edafSLaurent Pinchart size_t settings_size, const void *settings) 4263265edafSLaurent Pinchart { 4273265edafSLaurent Pinchart struct gb_camera_capture_request *req; 4283265edafSLaurent Pinchart size_t req_size; 429b9f71bc8SJohan Hovold int ret; 4303265edafSLaurent Pinchart 4313265edafSLaurent Pinchart if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) 4323265edafSLaurent Pinchart return -EINVAL; 4333265edafSLaurent Pinchart 4343265edafSLaurent Pinchart req_size = sizeof(*req) + settings_size; 4353265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 4363265edafSLaurent Pinchart if (!req) 4373265edafSLaurent Pinchart return -ENOMEM; 4383265edafSLaurent Pinchart 439c6622216SLaurent Pinchart req->request_id = cpu_to_le32(request_id); 4403265edafSLaurent Pinchart req->streams = streams; 4413265edafSLaurent Pinchart req->padding = 0; 442c6622216SLaurent Pinchart req->num_frames = cpu_to_le16(num_frames); 4433265edafSLaurent Pinchart memcpy(req->settings, settings, settings_size); 4443265edafSLaurent Pinchart 44517ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 44617ca6770SEvgeniy Borisov 44717ca6770SEvgeniy Borisov if (!gcam->connection) { 44817ca6770SEvgeniy Borisov ret = -EINVAL; 44917ca6770SEvgeniy Borisov goto done; 45017ca6770SEvgeniy Borisov } 45117ca6770SEvgeniy Borisov 452b9f71bc8SJohan Hovold ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, 4533265edafSLaurent Pinchart req, req_size, NULL, 0); 45417ca6770SEvgeniy Borisov done: 45517ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 456b9f71bc8SJohan Hovold 457b9f71bc8SJohan Hovold kfree(req); 458b9f71bc8SJohan Hovold 459b9f71bc8SJohan Hovold return ret; 4603265edafSLaurent Pinchart } 4613265edafSLaurent Pinchart 4623265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) 4633265edafSLaurent Pinchart { 4643265edafSLaurent Pinchart struct gb_camera_flush_response resp; 4653265edafSLaurent Pinchart int ret; 4663265edafSLaurent Pinchart 46717ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 46817ca6770SEvgeniy Borisov 46917ca6770SEvgeniy Borisov if (!gcam->connection) { 47017ca6770SEvgeniy Borisov ret = -EINVAL; 47117ca6770SEvgeniy Borisov goto done; 47217ca6770SEvgeniy Borisov } 47317ca6770SEvgeniy Borisov 4743265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, 4753265edafSLaurent Pinchart &resp, sizeof(resp)); 47617ca6770SEvgeniy Borisov 4773265edafSLaurent Pinchart if (ret < 0) 47817ca6770SEvgeniy Borisov goto done; 4793265edafSLaurent Pinchart 4803265edafSLaurent Pinchart if (request_id) 481c6622216SLaurent Pinchart *request_id = le32_to_cpu(resp.request_id); 4823265edafSLaurent Pinchart 48317ca6770SEvgeniy Borisov done: 48417ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 48517ca6770SEvgeniy Borisov 48617ca6770SEvgeniy Borisov return ret; 4873265edafSLaurent Pinchart } 4883265edafSLaurent Pinchart 48968b66c28SLaurent Pinchart static int gb_camera_request_handler(struct gb_operation *op) 4903265edafSLaurent Pinchart { 4910ec30632SGreg Kroah-Hartman struct gb_camera *gcam = gb_connection_get_data(op->connection); 4923265edafSLaurent Pinchart struct gb_camera_metadata_request *payload; 4933265edafSLaurent Pinchart struct gb_message *request; 4943265edafSLaurent Pinchart 49568b66c28SLaurent Pinchart if (op->type != GB_CAMERA_TYPE_METADATA) { 49668b66c28SLaurent Pinchart gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type); 4973265edafSLaurent Pinchart return -EINVAL; 4983265edafSLaurent Pinchart } 4993265edafSLaurent Pinchart 5003265edafSLaurent Pinchart request = op->request; 5013265edafSLaurent Pinchart 5023265edafSLaurent Pinchart if (request->payload_size < sizeof(*payload)) { 5033265edafSLaurent Pinchart gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", 5043265edafSLaurent Pinchart request->payload_size, sizeof(*payload)); 5053265edafSLaurent Pinchart return -EINVAL; 5063265edafSLaurent Pinchart } 5073265edafSLaurent Pinchart 5083265edafSLaurent Pinchart payload = request->payload; 5093265edafSLaurent Pinchart 5103265edafSLaurent Pinchart gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", 5113265edafSLaurent Pinchart payload->request_id, payload->frame_number, payload->stream); 5123265edafSLaurent Pinchart 5133265edafSLaurent Pinchart return 0; 5143265edafSLaurent Pinchart } 5153265edafSLaurent Pinchart 5163265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 5173a8dba4eSGjorgji Rosikopulos * Interface with HOST ara camera. 5183a8dba4eSGjorgji Rosikopulos */ 5193a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) 5203a8dba4eSGjorgji Rosikopulos { 5213a8dba4eSGjorgji Rosikopulos unsigned int i; 5223a8dba4eSGjorgji Rosikopulos 5233a8dba4eSGjorgji Rosikopulos for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { 5243a8dba4eSGjorgji Rosikopulos if (mbus_to_gbus_format[i].mbus_code == mbus_code) 5253a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[i].gb_format; 5263a8dba4eSGjorgji Rosikopulos } 5273a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[0].gb_format; 5283a8dba4eSGjorgji Rosikopulos } 5293a8dba4eSGjorgji Rosikopulos 5303a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) 5313a8dba4eSGjorgji Rosikopulos { 5323a8dba4eSGjorgji Rosikopulos unsigned int i; 5333a8dba4eSGjorgji Rosikopulos 5343a8dba4eSGjorgji Rosikopulos for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { 5353a8dba4eSGjorgji Rosikopulos if (mbus_to_gbus_format[i].gb_format == gb_fmt) 5363a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[i].mbus_code; 5373a8dba4eSGjorgji Rosikopulos } 5383a8dba4eSGjorgji Rosikopulos return mbus_to_gbus_format[0].mbus_code; 5393a8dba4eSGjorgji Rosikopulos } 5403a8dba4eSGjorgji Rosikopulos 541a883b0ebSJacopo Mondi static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) 542a883b0ebSJacopo Mondi { 543a883b0ebSJacopo Mondi struct gb_camera *gcam = priv; 544a883b0ebSJacopo Mondi size_t capabilities_len = len; 545a883b0ebSJacopo Mondi int ret; 546a883b0ebSJacopo Mondi 547a883b0ebSJacopo Mondi ret = gb_camera_capabilities(gcam, data, &capabilities_len); 548a883b0ebSJacopo Mondi if (ret) 549a883b0ebSJacopo Mondi return ret; 550a883b0ebSJacopo Mondi 551a883b0ebSJacopo Mondi return capabilities_len; 552a883b0ebSJacopo Mondi } 553a883b0ebSJacopo Mondi 5545b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, 555b4905038SEvgeniy Borisov unsigned int *flags, struct gb_camera_stream *streams, 556b4905038SEvgeniy Borisov struct gb_camera_csi_params *csi_params) 5573a8dba4eSGjorgji Rosikopulos { 5583a8dba4eSGjorgji Rosikopulos struct gb_camera *gcam = priv; 5593a8dba4eSGjorgji Rosikopulos struct gb_camera_stream_config *gb_streams; 5605b032710SGjorgji Rosikopulos unsigned int gb_flags = 0; 5615b032710SGjorgji Rosikopulos unsigned int gb_nstreams = *nstreams; 5623a8dba4eSGjorgji Rosikopulos unsigned int i; 5633a8dba4eSGjorgji Rosikopulos int ret; 5643a8dba4eSGjorgji Rosikopulos 5655b032710SGjorgji Rosikopulos if (gb_nstreams > GB_CAMERA_MAX_STREAMS) 5663a8dba4eSGjorgji Rosikopulos return -EINVAL; 5673a8dba4eSGjorgji Rosikopulos 5685b032710SGjorgji Rosikopulos gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); 5693a8dba4eSGjorgji Rosikopulos if (!gb_streams) 5703a8dba4eSGjorgji Rosikopulos return -ENOMEM; 5713a8dba4eSGjorgji Rosikopulos 5725b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 5733a8dba4eSGjorgji Rosikopulos gb_streams[i].width = streams[i].width; 5743a8dba4eSGjorgji Rosikopulos gb_streams[i].height = streams[i].height; 5753a8dba4eSGjorgji Rosikopulos gb_streams[i].format = 5763a8dba4eSGjorgji Rosikopulos gb_camera_mbus_to_gb(streams[i].pixel_code); 5773a8dba4eSGjorgji Rosikopulos } 5783a8dba4eSGjorgji Rosikopulos 5795b032710SGjorgji Rosikopulos if (*flags & GB_CAMERA_IN_FLAG_TEST) 5805b032710SGjorgji Rosikopulos gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; 5815b032710SGjorgji Rosikopulos 5825b032710SGjorgji Rosikopulos ret = gb_camera_configure_streams(gcam, &gb_nstreams, 583b4905038SEvgeniy Borisov &gb_flags, gb_streams, csi_params); 5843a8dba4eSGjorgji Rosikopulos if (ret < 0) 5853a8dba4eSGjorgji Rosikopulos goto done; 5865b032710SGjorgji Rosikopulos if (gb_nstreams > *nstreams) { 5875b032710SGjorgji Rosikopulos ret = -EINVAL; 5885b032710SGjorgji Rosikopulos goto done; 5895b032710SGjorgji Rosikopulos } 5903a8dba4eSGjorgji Rosikopulos 5915b032710SGjorgji Rosikopulos *flags = 0; 5925b032710SGjorgji Rosikopulos if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) 5935b032710SGjorgji Rosikopulos *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; 5945b032710SGjorgji Rosikopulos 5955b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 5963a8dba4eSGjorgji Rosikopulos streams[i].width = gb_streams[i].width; 5973a8dba4eSGjorgji Rosikopulos streams[i].height = gb_streams[i].height; 5983a8dba4eSGjorgji Rosikopulos streams[i].vc = gb_streams[i].vc; 5993a8dba4eSGjorgji Rosikopulos streams[i].dt[0] = gb_streams[i].dt[0]; 6003a8dba4eSGjorgji Rosikopulos streams[i].dt[1] = gb_streams[i].dt[1]; 6013a8dba4eSGjorgji Rosikopulos streams[i].max_size = gb_streams[i].max_size; 6023a8dba4eSGjorgji Rosikopulos streams[i].pixel_code = 6033a8dba4eSGjorgji Rosikopulos gb_camera_gb_to_mbus(gb_streams[i].format); 6043a8dba4eSGjorgji Rosikopulos } 6055b032710SGjorgji Rosikopulos *nstreams = gb_nstreams; 6063a8dba4eSGjorgji Rosikopulos 6073a8dba4eSGjorgji Rosikopulos done: 6083a8dba4eSGjorgji Rosikopulos kfree(gb_streams); 6093a8dba4eSGjorgji Rosikopulos return ret; 6103a8dba4eSGjorgji Rosikopulos } 6113a8dba4eSGjorgji Rosikopulos 6123a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id, 6133a8dba4eSGjorgji Rosikopulos unsigned int streams, unsigned int num_frames, 6143a8dba4eSGjorgji Rosikopulos size_t settings_size, const void *settings) 6153a8dba4eSGjorgji Rosikopulos { 6161472ec67SGjorgji Rosikopulos struct gb_camera *gcam = priv; 6171472ec67SGjorgji Rosikopulos 6181472ec67SGjorgji Rosikopulos return gb_camera_capture(gcam, request_id, streams, num_frames, 6193a8dba4eSGjorgji Rosikopulos settings_size, settings); 6203a8dba4eSGjorgji Rosikopulos } 6213a8dba4eSGjorgji Rosikopulos 6223a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id) 6233a8dba4eSGjorgji Rosikopulos { 6241472ec67SGjorgji Rosikopulos struct gb_camera *gcam = priv; 6251472ec67SGjorgji Rosikopulos 6261472ec67SGjorgji Rosikopulos return gb_camera_flush(gcam, request_id); 6273a8dba4eSGjorgji Rosikopulos } 6283a8dba4eSGjorgji Rosikopulos 6291472ec67SGjorgji Rosikopulos static const struct gb_camera_ops gb_cam_ops = { 6301472ec67SGjorgji Rosikopulos .capabilities = gb_camera_op_capabilities, 6311472ec67SGjorgji Rosikopulos .configure_streams = gb_camera_op_configure_streams, 6321472ec67SGjorgji Rosikopulos .capture = gb_camera_op_capture, 6331472ec67SGjorgji Rosikopulos .flush = gb_camera_op_flush, 6341472ec67SGjorgji Rosikopulos }; 6351472ec67SGjorgji Rosikopulos 6363a8dba4eSGjorgji Rosikopulos /* ----------------------------------------------------------------------------- 6373265edafSLaurent Pinchart * DebugFS 6383265edafSLaurent Pinchart */ 63948b15a9bSLaurent Pinchart 6403265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, 6413265edafSLaurent Pinchart char *buf, size_t len) 6423265edafSLaurent Pinchart { 64348b15a9bSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 64448b15a9bSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES]; 64548b15a9bSLaurent Pinchart size_t size = 1024; 64648b15a9bSLaurent Pinchart unsigned int i; 64748b15a9bSLaurent Pinchart u8 *caps; 64848b15a9bSLaurent Pinchart int ret; 64948b15a9bSLaurent Pinchart 65048b15a9bSLaurent Pinchart caps = kmalloc(size, GFP_KERNEL); 65148b15a9bSLaurent Pinchart if (!caps) 65248b15a9bSLaurent Pinchart return -ENOMEM; 65348b15a9bSLaurent Pinchart 65448b15a9bSLaurent Pinchart ret = gb_camera_capabilities(gcam, caps, &size); 65548b15a9bSLaurent Pinchart if (ret < 0) 65648b15a9bSLaurent Pinchart goto done; 65748b15a9bSLaurent Pinchart 65848b15a9bSLaurent Pinchart /* 65948b15a9bSLaurent Pinchart * hex_dump_to_buffer() doesn't return the number of bytes dumped prior 66048b15a9bSLaurent Pinchart * to v4.0, we need our own implementation :-( 66148b15a9bSLaurent Pinchart */ 66248b15a9bSLaurent Pinchart buffer->length = 0; 66348b15a9bSLaurent Pinchart 66448b15a9bSLaurent Pinchart for (i = 0; i < size; i += 16) { 66548b15a9bSLaurent Pinchart unsigned int nbytes = min_t(unsigned int, size - i, 16); 66648b15a9bSLaurent Pinchart 66748b15a9bSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 66848b15a9bSLaurent Pinchart "%*ph\n", nbytes, caps + i); 66948b15a9bSLaurent Pinchart } 67048b15a9bSLaurent Pinchart 67148b15a9bSLaurent Pinchart done: 67248b15a9bSLaurent Pinchart kfree(caps); 67348b15a9bSLaurent Pinchart return ret; 6743265edafSLaurent Pinchart } 6753265edafSLaurent Pinchart 6763265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, 6773265edafSLaurent Pinchart char *buf, size_t len) 6783265edafSLaurent Pinchart { 6793265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 6803265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; 6813265edafSLaurent Pinchart struct gb_camera_stream_config *streams; 6823265edafSLaurent Pinchart unsigned int nstreams; 683b787d413SJacopo Mondi unsigned int flags; 6843265edafSLaurent Pinchart unsigned int i; 6853265edafSLaurent Pinchart char *token; 6863265edafSLaurent Pinchart int ret; 6873265edafSLaurent Pinchart 6883265edafSLaurent Pinchart /* Retrieve number of streams to configure */ 689b787d413SJacopo Mondi token = strsep(&buf, ";"); 6903265edafSLaurent Pinchart if (token == NULL) 6913265edafSLaurent Pinchart return -EINVAL; 6923265edafSLaurent Pinchart 6933265edafSLaurent Pinchart ret = kstrtouint(token, 10, &nstreams); 6943265edafSLaurent Pinchart if (ret < 0) 6953265edafSLaurent Pinchart return ret; 6963265edafSLaurent Pinchart 6973265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 6983265edafSLaurent Pinchart return -EINVAL; 6993265edafSLaurent Pinchart 700b787d413SJacopo Mondi token = strsep(&buf, ";"); 701b787d413SJacopo Mondi if (token == NULL) 702b787d413SJacopo Mondi return -EINVAL; 703b787d413SJacopo Mondi 704b787d413SJacopo Mondi ret = kstrtouint(token, 10, &flags); 705b787d413SJacopo Mondi if (ret < 0) 706b787d413SJacopo Mondi return ret; 707b787d413SJacopo Mondi 7083265edafSLaurent Pinchart /* For each stream to configure parse width, height and format */ 7093265edafSLaurent Pinchart streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); 7103265edafSLaurent Pinchart if (!streams) 7113265edafSLaurent Pinchart return -ENOMEM; 7123265edafSLaurent Pinchart 7133265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 7143265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 7153265edafSLaurent Pinchart 7163265edafSLaurent Pinchart /* width */ 7173265edafSLaurent Pinchart token = strsep(&buf, ";"); 7183265edafSLaurent Pinchart if (token == NULL) { 7193265edafSLaurent Pinchart ret = -EINVAL; 7203265edafSLaurent Pinchart goto done; 7213265edafSLaurent Pinchart } 7223265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->width); 7233265edafSLaurent Pinchart if (ret < 0) 7243265edafSLaurent Pinchart goto done; 7253265edafSLaurent Pinchart 7263265edafSLaurent Pinchart /* height */ 7273265edafSLaurent Pinchart token = strsep(&buf, ";"); 7283265edafSLaurent Pinchart if (token == NULL) 7293265edafSLaurent Pinchart goto done; 7303265edafSLaurent Pinchart 7313265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->height); 7323265edafSLaurent Pinchart if (ret < 0) 7333265edafSLaurent Pinchart goto done; 7343265edafSLaurent Pinchart 7353265edafSLaurent Pinchart /* Image format code */ 7363265edafSLaurent Pinchart token = strsep(&buf, ";"); 7373265edafSLaurent Pinchart if (token == NULL) 7383265edafSLaurent Pinchart goto done; 7393265edafSLaurent Pinchart 7403265edafSLaurent Pinchart ret = kstrtouint(token, 16, &stream->format); 7413265edafSLaurent Pinchart if (ret < 0) 7423265edafSLaurent Pinchart goto done; 7433265edafSLaurent Pinchart } 7443265edafSLaurent Pinchart 745b4905038SEvgeniy Borisov ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams, 746b4905038SEvgeniy Borisov NULL); 7473265edafSLaurent Pinchart if (ret < 0) 7483265edafSLaurent Pinchart goto done; 7493265edafSLaurent Pinchart 7504068487cSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); 7513265edafSLaurent Pinchart 7523265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 7533265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 7543265edafSLaurent Pinchart 7553265edafSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 7563265edafSLaurent Pinchart "%u;%u;%u;%u;%u;%u;%u;", 7573265edafSLaurent Pinchart stream->width, stream->height, 7583265edafSLaurent Pinchart stream->format, stream->vc, 7593265edafSLaurent Pinchart stream->dt[0], stream->dt[1], 7603265edafSLaurent Pinchart stream->max_size); 7613265edafSLaurent Pinchart } 7623265edafSLaurent Pinchart 7633265edafSLaurent Pinchart ret = len; 7643265edafSLaurent Pinchart 7653265edafSLaurent Pinchart done: 7663265edafSLaurent Pinchart kfree(streams); 7673265edafSLaurent Pinchart return ret; 7683265edafSLaurent Pinchart }; 7693265edafSLaurent Pinchart 7703265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, 7713265edafSLaurent Pinchart char *buf, size_t len) 7723265edafSLaurent Pinchart { 7733265edafSLaurent Pinchart unsigned int request_id; 7743265edafSLaurent Pinchart unsigned int streams_mask; 7753265edafSLaurent Pinchart unsigned int num_frames; 7763265edafSLaurent Pinchart char *token; 7773265edafSLaurent Pinchart int ret; 7783265edafSLaurent Pinchart 7793265edafSLaurent Pinchart /* Request id */ 7803265edafSLaurent Pinchart token = strsep(&buf, ";"); 7813265edafSLaurent Pinchart if (token == NULL) 7823265edafSLaurent Pinchart return -EINVAL; 7833265edafSLaurent Pinchart ret = kstrtouint(token, 10, &request_id); 7843265edafSLaurent Pinchart if (ret < 0) 7853265edafSLaurent Pinchart return ret; 7863265edafSLaurent Pinchart 7873265edafSLaurent Pinchart /* Stream mask */ 7883265edafSLaurent Pinchart token = strsep(&buf, ";"); 7893265edafSLaurent Pinchart if (token == NULL) 7903265edafSLaurent Pinchart return -EINVAL; 7913265edafSLaurent Pinchart ret = kstrtouint(token, 16, &streams_mask); 7923265edafSLaurent Pinchart if (ret < 0) 7933265edafSLaurent Pinchart return ret; 7943265edafSLaurent Pinchart 7953265edafSLaurent Pinchart /* number of frames */ 7963265edafSLaurent Pinchart token = strsep(&buf, ";"); 7973265edafSLaurent Pinchart if (token == NULL) 7983265edafSLaurent Pinchart return -EINVAL; 7993265edafSLaurent Pinchart ret = kstrtouint(token, 10, &num_frames); 8003265edafSLaurent Pinchart if (ret < 0) 8013265edafSLaurent Pinchart return ret; 8023265edafSLaurent Pinchart 8033265edafSLaurent Pinchart ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, 8043265edafSLaurent Pinchart NULL); 8053265edafSLaurent Pinchart if (ret < 0) 8063265edafSLaurent Pinchart return ret; 8073265edafSLaurent Pinchart 8083265edafSLaurent Pinchart return len; 8093265edafSLaurent Pinchart } 8103265edafSLaurent Pinchart 8113265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, 8123265edafSLaurent Pinchart char *buf, size_t len) 8133265edafSLaurent Pinchart { 8143265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 8153265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; 8163265edafSLaurent Pinchart unsigned int req_id; 8173265edafSLaurent Pinchart int ret; 8183265edafSLaurent Pinchart 8193265edafSLaurent Pinchart ret = gb_camera_flush(gcam, &req_id); 8203265edafSLaurent Pinchart if (ret < 0) 8213265edafSLaurent Pinchart return ret; 8223265edafSLaurent Pinchart 8233265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u", req_id); 8243265edafSLaurent Pinchart 8253265edafSLaurent Pinchart return len; 8263265edafSLaurent Pinchart } 8273265edafSLaurent Pinchart 8283265edafSLaurent Pinchart struct gb_camera_debugfs_entry { 8293265edafSLaurent Pinchart const char *name; 8303265edafSLaurent Pinchart unsigned int mask; 8313265edafSLaurent Pinchart unsigned int buffer; 8323265edafSLaurent Pinchart ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); 8333265edafSLaurent Pinchart }; 8343265edafSLaurent Pinchart 8353265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { 8363265edafSLaurent Pinchart { 8373265edafSLaurent Pinchart .name = "capabilities", 8383265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO, 8393265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 8403265edafSLaurent Pinchart .execute = gb_camera_debugfs_capabilities, 8413265edafSLaurent Pinchart }, { 8423265edafSLaurent Pinchart .name = "configure_streams", 8433265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 8443265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 8453265edafSLaurent Pinchart .execute = gb_camera_debugfs_configure_streams, 8463265edafSLaurent Pinchart }, { 8473265edafSLaurent Pinchart .name = "capture", 8483265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 8493265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 8503265edafSLaurent Pinchart .execute = gb_camera_debugfs_capture, 8513265edafSLaurent Pinchart }, { 8523265edafSLaurent Pinchart .name = "flush", 8533265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 8543265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 8553265edafSLaurent Pinchart .execute = gb_camera_debugfs_flush, 8563265edafSLaurent Pinchart }, 8573265edafSLaurent Pinchart }; 8583265edafSLaurent Pinchart 8593265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, 8603265edafSLaurent Pinchart size_t len, loff_t *offset) 8613265edafSLaurent Pinchart { 8623265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 8633265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 8643265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer; 8653265edafSLaurent Pinchart ssize_t ret; 8663265edafSLaurent Pinchart 8673265edafSLaurent Pinchart /* For read-only entries the operation is triggered by a read. */ 8683265edafSLaurent Pinchart if (!(op->mask & S_IWUGO)) { 8693265edafSLaurent Pinchart ret = op->execute(gcam, NULL, 0); 8703265edafSLaurent Pinchart if (ret < 0) 8713265edafSLaurent Pinchart return ret; 8723265edafSLaurent Pinchart } 8733265edafSLaurent Pinchart 8743265edafSLaurent Pinchart buffer = &gcam->debugfs.buffers[op->buffer]; 8753265edafSLaurent Pinchart 8763265edafSLaurent Pinchart return simple_read_from_buffer(buf, len, offset, buffer->data, 8773265edafSLaurent Pinchart buffer->length); 8783265edafSLaurent Pinchart } 8793265edafSLaurent Pinchart 8803265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file, 8813265edafSLaurent Pinchart const char __user *buf, size_t len, 8823265edafSLaurent Pinchart loff_t *offset) 8833265edafSLaurent Pinchart { 8843265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 8853265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 8863265edafSLaurent Pinchart ssize_t ret; 8873265edafSLaurent Pinchart char *kbuf; 8883265edafSLaurent Pinchart 8893265edafSLaurent Pinchart if (len > 1024) 8903265edafSLaurent Pinchart return -EINVAL; 8913265edafSLaurent Pinchart 8923265edafSLaurent Pinchart kbuf = kmalloc(len + 1, GFP_KERNEL); 8933265edafSLaurent Pinchart if (kbuf == NULL) 8943265edafSLaurent Pinchart return -ENOMEM; 8953265edafSLaurent Pinchart 8963265edafSLaurent Pinchart if (copy_from_user(kbuf, buf, len)) { 8973265edafSLaurent Pinchart ret = -EFAULT; 8983265edafSLaurent Pinchart goto done; 8993265edafSLaurent Pinchart } 9003265edafSLaurent Pinchart 9013265edafSLaurent Pinchart kbuf[len] = '\0'; 9023265edafSLaurent Pinchart 9033265edafSLaurent Pinchart ret = op->execute(gcam, kbuf, len); 9043265edafSLaurent Pinchart 9053265edafSLaurent Pinchart done: 9063265edafSLaurent Pinchart kfree(kbuf); 9073265edafSLaurent Pinchart return ret; 9083265edafSLaurent Pinchart } 9093265edafSLaurent Pinchart 9103265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file) 9113265edafSLaurent Pinchart { 9123265edafSLaurent Pinchart unsigned int i; 9133265edafSLaurent Pinchart 9143265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 9153265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 9163265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 9173265edafSLaurent Pinchart 9184dda744cSGreg Kroah-Hartman if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { 9193265edafSLaurent Pinchart file->private_data = (void *)entry; 9203265edafSLaurent Pinchart break; 9213265edafSLaurent Pinchart } 9223265edafSLaurent Pinchart } 9233265edafSLaurent Pinchart 9243265edafSLaurent Pinchart return 0; 9253265edafSLaurent Pinchart } 9263265edafSLaurent Pinchart 9273265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = { 9283265edafSLaurent Pinchart .open = gb_camera_debugfs_open, 9293265edafSLaurent Pinchart .read = gb_camera_debugfs_read, 9303265edafSLaurent Pinchart .write = gb_camera_debugfs_write, 9313265edafSLaurent Pinchart }; 9323265edafSLaurent Pinchart 9333265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam) 9343265edafSLaurent Pinchart { 9353265edafSLaurent Pinchart struct gb_connection *connection = gcam->connection; 9363265edafSLaurent Pinchart char dirname[27]; 9373265edafSLaurent Pinchart unsigned int i; 9383265edafSLaurent Pinchart 9393265edafSLaurent Pinchart /* 9403265edafSLaurent Pinchart * Create root debugfs entry and a file entry for each camera operation. 9413265edafSLaurent Pinchart */ 9423265edafSLaurent Pinchart snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, 94368b66c28SLaurent Pinchart gcam->bundle->id); 9443265edafSLaurent Pinchart 9453265edafSLaurent Pinchart gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); 9463265edafSLaurent Pinchart if (IS_ERR(gcam->debugfs.root)) { 9473265edafSLaurent Pinchart gcam_err(gcam, "debugfs root create failed (%ld)\n", 9483265edafSLaurent Pinchart PTR_ERR(gcam->debugfs.root)); 9493265edafSLaurent Pinchart return PTR_ERR(gcam->debugfs.root); 9503265edafSLaurent Pinchart } 9513265edafSLaurent Pinchart 9523265edafSLaurent Pinchart gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * 9533265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX); 9543265edafSLaurent Pinchart if (!gcam->debugfs.buffers) 9553265edafSLaurent Pinchart return -ENOMEM; 9563265edafSLaurent Pinchart 9573265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 9583265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 9593265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 9603265edafSLaurent Pinchart struct dentry *dentry; 9613265edafSLaurent Pinchart 9623265edafSLaurent Pinchart gcam->debugfs.buffers[i].length = 0; 9633265edafSLaurent Pinchart 9643265edafSLaurent Pinchart dentry = debugfs_create_file(entry->name, entry->mask, 9653265edafSLaurent Pinchart gcam->debugfs.root, gcam, 9663265edafSLaurent Pinchart &gb_camera_debugfs_ops); 9673265edafSLaurent Pinchart if (IS_ERR(dentry)) { 9683265edafSLaurent Pinchart gcam_err(gcam, 9693265edafSLaurent Pinchart "debugfs operation %s create failed (%ld)\n", 970f9340fc7SAlex Elder entry->name, PTR_ERR(dentry)); 9713265edafSLaurent Pinchart return PTR_ERR(dentry); 9723265edafSLaurent Pinchart } 9733265edafSLaurent Pinchart } 9743265edafSLaurent Pinchart 9753265edafSLaurent Pinchart return 0; 9763265edafSLaurent Pinchart } 9773265edafSLaurent Pinchart 9783265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) 9793265edafSLaurent Pinchart { 9803265edafSLaurent Pinchart debugfs_remove_recursive(gcam->debugfs.root); 9813265edafSLaurent Pinchart 9823265edafSLaurent Pinchart vfree(gcam->debugfs.buffers); 9833265edafSLaurent Pinchart } 9843265edafSLaurent Pinchart 9853265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 9863265edafSLaurent Pinchart * Init & Cleanup 9873265edafSLaurent Pinchart */ 9883265edafSLaurent Pinchart 9893265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam) 9903265edafSLaurent Pinchart { 9913265edafSLaurent Pinchart gb_camera_debugfs_cleanup(gcam); 9923265edafSLaurent Pinchart 9933ba9fa5cSJohan Hovold if (gcam->data_connection) { 9943ba9fa5cSJohan Hovold gb_connection_disable(gcam->data_connection); 9953ba9fa5cSJohan Hovold gb_connection_destroy(gcam->data_connection); 99617ca6770SEvgeniy Borisov gcam->data_connection = NULL; 9973265edafSLaurent Pinchart } 9983265edafSLaurent Pinchart 99917ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 100068b66c28SLaurent Pinchart if (gcam->connection) { 100168b66c28SLaurent Pinchart gb_connection_disable(gcam->connection); 100268b66c28SLaurent Pinchart gb_connection_destroy(gcam->connection); 100317ca6770SEvgeniy Borisov gcam->connection = NULL; 100417ca6770SEvgeniy Borisov } 100517ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 100668b66c28SLaurent Pinchart } 100768b66c28SLaurent Pinchart 100817ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref) 100917ca6770SEvgeniy Borisov { 101017ca6770SEvgeniy Borisov struct gb_camera_module *cam_mod = 101117ca6770SEvgeniy Borisov container_of(ref, struct gb_camera_module, refcount); 101217ca6770SEvgeniy Borisov kfree(cam_mod->priv); 10133265edafSLaurent Pinchart } 10143265edafSLaurent Pinchart 101568b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle, 101668b66c28SLaurent Pinchart const struct greybus_bundle_id *id) 10173265edafSLaurent Pinchart { 101868b66c28SLaurent Pinchart struct gb_connection *conn; 10193265edafSLaurent Pinchart struct gb_camera *gcam; 102068b66c28SLaurent Pinchart u16 mgmt_cport_id = 0; 102168b66c28SLaurent Pinchart u16 data_cport_id = 0; 102268b66c28SLaurent Pinchart unsigned int i; 10233265edafSLaurent Pinchart int ret; 10243265edafSLaurent Pinchart 102568b66c28SLaurent Pinchart /* 102668b66c28SLaurent Pinchart * The camera bundle must contain exactly two CPorts, one for the 102768b66c28SLaurent Pinchart * camera management protocol and one for the camera data protocol. 102868b66c28SLaurent Pinchart */ 102968b66c28SLaurent Pinchart if (bundle->num_cports != 2) 103068b66c28SLaurent Pinchart return -ENODEV; 103168b66c28SLaurent Pinchart 103268b66c28SLaurent Pinchart for (i = 0; i < bundle->num_cports; ++i) { 103368b66c28SLaurent Pinchart struct greybus_descriptor_cport *desc = &bundle->cport_desc[i]; 103468b66c28SLaurent Pinchart 103568b66c28SLaurent Pinchart switch (desc->protocol_id) { 103668b66c28SLaurent Pinchart case GREYBUS_PROTOCOL_CAMERA_MGMT: 103768b66c28SLaurent Pinchart mgmt_cport_id = le16_to_cpu(desc->id); 103868b66c28SLaurent Pinchart break; 103968b66c28SLaurent Pinchart case GREYBUS_PROTOCOL_CAMERA_DATA: 104068b66c28SLaurent Pinchart data_cport_id = le16_to_cpu(desc->id); 104168b66c28SLaurent Pinchart break; 104268b66c28SLaurent Pinchart default: 104368b66c28SLaurent Pinchart return -ENODEV; 104468b66c28SLaurent Pinchart } 104568b66c28SLaurent Pinchart } 104668b66c28SLaurent Pinchart 104768b66c28SLaurent Pinchart if (!mgmt_cport_id || !data_cport_id) 104868b66c28SLaurent Pinchart return -ENODEV; 104968b66c28SLaurent Pinchart 10503265edafSLaurent Pinchart gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); 10513265edafSLaurent Pinchart if (!gcam) 10523265edafSLaurent Pinchart return -ENOMEM; 10533265edafSLaurent Pinchart 1054d9e4c4eeSViresh Kumar mutex_init(&gcam->mutex); 10553265edafSLaurent Pinchart 10563b8ebfebSLaurent Pinchart gcam->bundle = bundle; 10573b8ebfebSLaurent Pinchart gcam->state = GB_CAMERA_STATE_UNCONFIGURED; 10583b8ebfebSLaurent Pinchart 105968b66c28SLaurent Pinchart conn = gb_connection_create(bundle, mgmt_cport_id, 106068b66c28SLaurent Pinchart gb_camera_request_handler); 106168b66c28SLaurent Pinchart if (IS_ERR(conn)) { 106268b66c28SLaurent Pinchart ret = PTR_ERR(conn); 10633ba9fa5cSJohan Hovold goto error; 10643ba9fa5cSJohan Hovold } 10653ba9fa5cSJohan Hovold 106668b66c28SLaurent Pinchart gcam->connection = conn; 106768b66c28SLaurent Pinchart gb_connection_set_data(conn, gcam); 106868b66c28SLaurent Pinchart 106968b66c28SLaurent Pinchart ret = gb_connection_enable(conn); 107068b66c28SLaurent Pinchart if (ret) 107168b66c28SLaurent Pinchart goto error; 107268b66c28SLaurent Pinchart 107368b66c28SLaurent Pinchart /* 107468b66c28SLaurent Pinchart * Create the data connection between the camera module data CPort and 107568b66c28SLaurent Pinchart * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. 107668b66c28SLaurent Pinchart */ 107768b66c28SLaurent Pinchart conn = gb_connection_create_offloaded(bundle, data_cport_id, 107868b66c28SLaurent Pinchart GB_CONNECTION_FLAG_NO_FLOWCTRL | 107968b66c28SLaurent Pinchart GB_CONNECTION_FLAG_CDSI1); 108068b66c28SLaurent Pinchart if (IS_ERR(conn)) { 108168b66c28SLaurent Pinchart ret = PTR_ERR(conn); 108268b66c28SLaurent Pinchart goto error; 108368b66c28SLaurent Pinchart } 108468b66c28SLaurent Pinchart gcam->data_connection = conn; 108568b66c28SLaurent Pinchart gb_connection_set_data(conn, gcam); 108668b66c28SLaurent Pinchart 108768b66c28SLaurent Pinchart ret = gb_connection_enable(conn); 10883ba9fa5cSJohan Hovold if (ret) 10893ba9fa5cSJohan Hovold goto error; 109041c23958SJohan Hovold 10913265edafSLaurent Pinchart ret = gb_camera_debugfs_init(gcam); 10923265edafSLaurent Pinchart if (ret < 0) 10933265edafSLaurent Pinchart goto error; 10943265edafSLaurent Pinchart 109517ca6770SEvgeniy Borisov gcam->module.priv = gcam; 109617ca6770SEvgeniy Borisov gcam->module.ops = &gb_cam_ops; 109717ca6770SEvgeniy Borisov gcam->module.interface_id = gcam->connection->intf->interface_id; 109817ca6770SEvgeniy Borisov gcam->module.release = gb_camera_release_module; 109917ca6770SEvgeniy Borisov ret = gb_camera_register(&gcam->module); 11003a8dba4eSGjorgji Rosikopulos if (ret < 0) 11013a8dba4eSGjorgji Rosikopulos goto error; 11023a8dba4eSGjorgji Rosikopulos 110368b66c28SLaurent Pinchart greybus_set_drvdata(bundle, gcam); 110468b66c28SLaurent Pinchart 11053265edafSLaurent Pinchart return 0; 11063265edafSLaurent Pinchart 11073265edafSLaurent Pinchart error: 11083265edafSLaurent Pinchart gb_camera_cleanup(gcam); 110917ca6770SEvgeniy Borisov kfree(gcam); 11103265edafSLaurent Pinchart return ret; 11113265edafSLaurent Pinchart } 11123265edafSLaurent Pinchart 111368b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle) 11143265edafSLaurent Pinchart { 111568b66c28SLaurent Pinchart struct gb_camera *gcam = greybus_get_drvdata(bundle); 11163265edafSLaurent Pinchart 11173265edafSLaurent Pinchart gb_camera_cleanup(gcam); 111817ca6770SEvgeniy Borisov gb_camera_unregister(&gcam->module); 11193265edafSLaurent Pinchart } 11203265edafSLaurent Pinchart 112168b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = { 112268b66c28SLaurent Pinchart { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, 112368b66c28SLaurent Pinchart { }, 11243265edafSLaurent Pinchart }; 11253265edafSLaurent Pinchart 112668b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = { 112768b66c28SLaurent Pinchart .name = "camera", 112868b66c28SLaurent Pinchart .probe = gb_camera_probe, 112968b66c28SLaurent Pinchart .disconnect = gb_camera_disconnect, 113068b66c28SLaurent Pinchart .id_table = gb_camera_id_table, 113168b66c28SLaurent Pinchart }; 113268b66c28SLaurent Pinchart 113368b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver); 11343265edafSLaurent Pinchart 11353265edafSLaurent Pinchart MODULE_LICENSE("GPL v2"); 1136