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 459120b906SLaurent Pinchart * @data_cport_id: the data CPort ID on the module side 463b8ebfebSLaurent Pinchart * @mutex: protects the connection and state fields 473b8ebfebSLaurent Pinchart * @state: the current module state 483265edafSLaurent Pinchart * @debugfs: debugfs entries for camera protocol operations testing 49c3d77f71SGjorgji Rosikopulos * @module: Greybus camera module registered to HOST processor. 503265edafSLaurent Pinchart */ 513265edafSLaurent Pinchart struct gb_camera { 5268b66c28SLaurent Pinchart struct gb_bundle *bundle; 533265edafSLaurent Pinchart struct gb_connection *connection; 543ba9fa5cSJohan Hovold struct gb_connection *data_connection; 559120b906SLaurent Pinchart u16 data_cport_id; 563b8ebfebSLaurent Pinchart 5717ca6770SEvgeniy Borisov struct mutex mutex; 583b8ebfebSLaurent Pinchart enum gb_camera_state state; 593265edafSLaurent Pinchart 603265edafSLaurent Pinchart struct { 613265edafSLaurent Pinchart struct dentry *root; 623265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffers; 633265edafSLaurent Pinchart } debugfs; 64c3d77f71SGjorgji Rosikopulos 65c3d77f71SGjorgji Rosikopulos struct gb_camera_module module; 663265edafSLaurent Pinchart }; 673265edafSLaurent Pinchart 683265edafSLaurent Pinchart struct gb_camera_stream_config { 693265edafSLaurent Pinchart unsigned int width; 703265edafSLaurent Pinchart unsigned int height; 713265edafSLaurent Pinchart unsigned int format; 723265edafSLaurent Pinchart unsigned int vc; 733265edafSLaurent Pinchart unsigned int dt[2]; 743265edafSLaurent Pinchart unsigned int max_size; 753265edafSLaurent Pinchart }; 763265edafSLaurent Pinchart 776cc27048SJacopo Mondi struct gb_camera_fmt_info { 783a8dba4eSGjorgji Rosikopulos enum v4l2_mbus_pixelcode mbus_code; 793a8dba4eSGjorgji Rosikopulos unsigned int gb_format; 806cc27048SJacopo Mondi unsigned int bpp; 813a8dba4eSGjorgji Rosikopulos }; 823a8dba4eSGjorgji Rosikopulos 833a8dba4eSGjorgji Rosikopulos /* GB format to media code map */ 846cc27048SJacopo Mondi static const struct gb_camera_fmt_info gb_fmt_info[] = { 853a8dba4eSGjorgji Rosikopulos { 863a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, 873a8dba4eSGjorgji Rosikopulos .gb_format = 0x01, 886cc27048SJacopo Mondi .bpp = 16, 893a8dba4eSGjorgji Rosikopulos }, 903a8dba4eSGjorgji Rosikopulos { 917c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV12_1x8, 927c154711SGjorgji Rosikopulos .gb_format = 0x12, 936cc27048SJacopo Mondi .bpp = 12, 947c154711SGjorgji Rosikopulos }, 957c154711SGjorgji Rosikopulos { 967c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_NV21_1x8, 977c154711SGjorgji Rosikopulos .gb_format = 0x13, 986cc27048SJacopo Mondi .bpp = 12, 997c154711SGjorgji Rosikopulos }, 1007c154711SGjorgji Rosikopulos { 1017c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YU12_1x8, 1023a8dba4eSGjorgji Rosikopulos .gb_format = 0x16, 1036cc27048SJacopo Mondi .bpp = 12, 1043a8dba4eSGjorgji Rosikopulos }, 1053a8dba4eSGjorgji Rosikopulos { 1067c154711SGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_YV12_1x8, 1073a8dba4eSGjorgji Rosikopulos .gb_format = 0x17, 1086cc27048SJacopo Mondi .bpp = 12, 1093a8dba4eSGjorgji Rosikopulos }, 1103a8dba4eSGjorgji Rosikopulos { 1113a8dba4eSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, 1123a8dba4eSGjorgji Rosikopulos .gb_format = 0x40, 1136cc27048SJacopo Mondi .bpp = 0, 114dc5cc72cSGjorgji Rosikopulos }, 115dc5cc72cSGjorgji Rosikopulos { 116563c742aSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_GB_CAM_METADATA_1X8, 117dc5cc72cSGjorgji Rosikopulos .gb_format = 0x41, 1186cc27048SJacopo Mondi .bpp = 0, 119cb14e976SGjorgji Rosikopulos }, 120cb14e976SGjorgji Rosikopulos { 121563c742aSGjorgji Rosikopulos .mbus_code = V4L2_MBUS_FMT_GB_CAM_DEBUG_DATA_1X8, 122cb14e976SGjorgji Rosikopulos .gb_format = 0x42, 1236cc27048SJacopo Mondi .bpp = 0, 124cb14e976SGjorgji Rosikopulos }, 125caad3090SEvgeniy Borisov { 126caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10, 127caad3090SEvgeniy Borisov .gb_format = 0x80, 1286cc27048SJacopo Mondi .bpp = 10, 129caad3090SEvgeniy Borisov }, 130caad3090SEvgeniy Borisov { 131caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10, 132caad3090SEvgeniy Borisov .gb_format = 0x81, 1336cc27048SJacopo Mondi .bpp = 10, 134caad3090SEvgeniy Borisov }, 135caad3090SEvgeniy Borisov { 136caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, 137caad3090SEvgeniy Borisov .gb_format = 0x82, 1386cc27048SJacopo Mondi .bpp = 10, 139caad3090SEvgeniy Borisov }, 140caad3090SEvgeniy Borisov { 141caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10, 142caad3090SEvgeniy Borisov .gb_format = 0x83, 1436cc27048SJacopo Mondi .bpp = 10, 144caad3090SEvgeniy Borisov }, 145caad3090SEvgeniy Borisov { 146caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12, 147caad3090SEvgeniy Borisov .gb_format = 0x84, 1486cc27048SJacopo Mondi .bpp = 12, 149caad3090SEvgeniy Borisov }, 150caad3090SEvgeniy Borisov { 151caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12, 152caad3090SEvgeniy Borisov .gb_format = 0x85, 1536cc27048SJacopo Mondi .bpp = 12, 154caad3090SEvgeniy Borisov }, 155caad3090SEvgeniy Borisov { 156caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, 157caad3090SEvgeniy Borisov .gb_format = 0x86, 1586cc27048SJacopo Mondi .bpp = 12, 159caad3090SEvgeniy Borisov }, 160caad3090SEvgeniy Borisov { 161caad3090SEvgeniy Borisov .mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12, 162caad3090SEvgeniy Borisov .gb_format = 0x87, 1636cc27048SJacopo Mondi .bpp = 12, 164caad3090SEvgeniy Borisov }, 1653a8dba4eSGjorgji Rosikopulos }; 1663a8dba4eSGjorgji Rosikopulos 167d165a618SJacopo Mondi static const struct gb_camera_fmt_info *gb_camera_get_format_info(u16 gb_fmt) 168d165a618SJacopo Mondi { 169d165a618SJacopo Mondi unsigned int i; 170d165a618SJacopo Mondi 171d165a618SJacopo Mondi for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { 172d165a618SJacopo Mondi if (gb_fmt_info[i].gb_format == gb_fmt) 173d165a618SJacopo Mondi return &gb_fmt_info[i]; 174d165a618SJacopo Mondi } 175d165a618SJacopo Mondi 176d165a618SJacopo Mondi return NULL; 177d165a618SJacopo Mondi } 178d165a618SJacopo Mondi 1793265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT 16 1803265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT 17 1813265edafSLaurent Pinchart 1823265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE 8192 1833265edafSLaurent Pinchart 18468b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...) dev_dbg(&gcam->bundle->dev, format) 18568b66c28SLaurent Pinchart #define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) 18668b66c28SLaurent Pinchart #define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) 1873265edafSLaurent Pinchart 188fdf73c00SJacopo Mondi static int gb_camera_operation_sync_flags(struct gb_connection *connection, 189fdf73c00SJacopo Mondi int type, unsigned int flags, 190fdf73c00SJacopo Mondi void *request, size_t request_size, 191fdf73c00SJacopo Mondi void *response, size_t *response_size) 192fdf73c00SJacopo Mondi { 193fdf73c00SJacopo Mondi struct gb_operation *operation; 194fdf73c00SJacopo Mondi int ret; 195fdf73c00SJacopo Mondi 196fdf73c00SJacopo Mondi operation = gb_operation_create_flags(connection, type, request_size, 197fdf73c00SJacopo Mondi *response_size, flags, 198fdf73c00SJacopo Mondi GFP_KERNEL); 199fdf73c00SJacopo Mondi if (!operation) 200fdf73c00SJacopo Mondi return -ENOMEM; 201fdf73c00SJacopo Mondi 202fdf73c00SJacopo Mondi if (request_size) 203fdf73c00SJacopo Mondi memcpy(operation->request->payload, request, request_size); 204fdf73c00SJacopo Mondi 205fdf73c00SJacopo Mondi ret = gb_operation_request_send_sync(operation); 206fdf73c00SJacopo Mondi if (ret) { 207fdf73c00SJacopo Mondi dev_err(&connection->hd->dev, 208fdf73c00SJacopo Mondi "%s: synchronous operation of type 0x%02x failed: %d\n", 209fdf73c00SJacopo Mondi connection->name, type, ret); 210fdf73c00SJacopo Mondi } else { 211fdf73c00SJacopo Mondi *response_size = operation->response->payload_size; 212fdf73c00SJacopo Mondi 213fdf73c00SJacopo Mondi if (operation->response->payload_size) 214fdf73c00SJacopo Mondi memcpy(response, operation->response->payload, 215fdf73c00SJacopo Mondi operation->response->payload_size); 216fdf73c00SJacopo Mondi } 217fdf73c00SJacopo Mondi 218fdf73c00SJacopo Mondi gb_operation_put(operation); 219fdf73c00SJacopo Mondi 220fdf73c00SJacopo Mondi return ret; 221fdf73c00SJacopo Mondi } 222fdf73c00SJacopo Mondi 223f88b94ecSJacopo Mondi static int gb_camera_get_max_pkt_size(struct gb_camera *gcam, 224f88b94ecSJacopo Mondi struct gb_camera_configure_streams_response *resp) 225f88b94ecSJacopo Mondi { 226f88b94ecSJacopo Mondi unsigned int max_pkt_size = 0; 227f88b94ecSJacopo Mondi unsigned int i; 228f88b94ecSJacopo Mondi 229f88b94ecSJacopo Mondi for (i = 0; i < resp->num_streams; i++) { 230f88b94ecSJacopo Mondi struct gb_camera_stream_config_response *cfg = &resp->config[i]; 231f88b94ecSJacopo Mondi const struct gb_camera_fmt_info *fmt_info; 232f88b94ecSJacopo Mondi unsigned int pkt_size; 233f88b94ecSJacopo Mondi 234f88b94ecSJacopo Mondi fmt_info = gb_camera_get_format_info(cfg->format); 235f88b94ecSJacopo Mondi if (!fmt_info) { 236f88b94ecSJacopo Mondi gcam_err(gcam, "unsupported greybus image format: %d\n", 237f88b94ecSJacopo Mondi cfg->format); 238f88b94ecSJacopo Mondi return -EIO; 239f88b94ecSJacopo Mondi } 240f88b94ecSJacopo Mondi 241f88b94ecSJacopo Mondi if (fmt_info->bpp == 0) { 242f88b94ecSJacopo Mondi pkt_size = le32_to_cpu(cfg->max_pkt_size); 243f88b94ecSJacopo Mondi 244f88b94ecSJacopo Mondi if (pkt_size == 0) { 245f88b94ecSJacopo Mondi gcam_err(gcam, 246f88b94ecSJacopo Mondi "Stream %u: invalid zero maximum packet size\n", 247f88b94ecSJacopo Mondi i); 248f88b94ecSJacopo Mondi return -EIO; 249f88b94ecSJacopo Mondi } 250f88b94ecSJacopo Mondi } else { 251f88b94ecSJacopo Mondi pkt_size = le16_to_cpu(cfg->width) * fmt_info->bpp / 8; 252f88b94ecSJacopo Mondi 253f88b94ecSJacopo Mondi if (pkt_size != le32_to_cpu(cfg->max_pkt_size)) { 254f88b94ecSJacopo Mondi gcam_err(gcam, 255f88b94ecSJacopo Mondi "Stream %u: maximum packet size mismatch (%u/%u)\n", 256f88b94ecSJacopo Mondi i, pkt_size, cfg->max_pkt_size); 257f88b94ecSJacopo Mondi return -EIO; 258f88b94ecSJacopo Mondi } 259f88b94ecSJacopo Mondi } 260f88b94ecSJacopo Mondi 261f88b94ecSJacopo Mondi max_pkt_size = max(pkt_size, max_pkt_size); 262f88b94ecSJacopo Mondi } 263f88b94ecSJacopo Mondi 264f88b94ecSJacopo Mondi return max_pkt_size; 265f88b94ecSJacopo Mondi } 266f88b94ecSJacopo Mondi 267d165a618SJacopo Mondi /* 268d165a618SJacopo Mondi * Validate the stream configuration response verifying padding is correctly 269d165a618SJacopo Mondi * set and the returned number of streams is supported 270d165a618SJacopo Mondi */ 271c9161d72SJacopo Mondi static const int gb_camera_configure_streams_validate_response( 272d165a618SJacopo Mondi struct gb_camera *gcam, 273c9161d72SJacopo Mondi struct gb_camera_configure_streams_response *resp, 274d165a618SJacopo Mondi unsigned int nstreams) 275d165a618SJacopo Mondi { 276d165a618SJacopo Mondi unsigned int i; 277d165a618SJacopo Mondi 278d165a618SJacopo Mondi /* Validate the returned response structure */ 279c9161d72SJacopo Mondi if (resp->padding[0] || resp->padding[1]) { 280d165a618SJacopo Mondi gcam_err(gcam, "response padding != 0\n"); 281d165a618SJacopo Mondi return -EIO; 282d165a618SJacopo Mondi } 283d165a618SJacopo Mondi 284c9161d72SJacopo Mondi if (resp->num_streams > nstreams) { 285d165a618SJacopo Mondi gcam_err(gcam, "got #streams %u > request %u\n", 286d165a618SJacopo Mondi resp->num_streams, nstreams); 287d165a618SJacopo Mondi return -EIO; 288d165a618SJacopo Mondi } 289d165a618SJacopo Mondi 290c9161d72SJacopo Mondi for (i = 0; i < resp->num_streams; i++) { 291d165a618SJacopo Mondi struct gb_camera_stream_config_response *cfg = &resp->config[i]; 292d165a618SJacopo Mondi if (cfg->padding) { 293d165a618SJacopo Mondi gcam_err(gcam, "stream #%u padding != 0\n", i); 294d165a618SJacopo Mondi return -EIO; 295d165a618SJacopo Mondi } 296d165a618SJacopo Mondi } 297d165a618SJacopo Mondi 298d165a618SJacopo Mondi return 0; 299d165a618SJacopo Mondi } 300d165a618SJacopo Mondi 3013265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 302f3d5f661SLaurent Pinchart * Hardware Configuration 3033265edafSLaurent Pinchart */ 3043265edafSLaurent Pinchart 305c161c0fcSLaurent Pinchart static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, 306c161c0fcSLaurent Pinchart bool hs) 307c161c0fcSLaurent Pinchart { 308c161c0fcSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 309c161c0fcSLaurent Pinchart int ret; 310c161c0fcSLaurent Pinchart 311c161c0fcSLaurent Pinchart if (hs) 312c161c0fcSLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, intf_id, 313c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 314c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 3158c2522d8SEli Sennesh GB_SVC_SMALL_AMPLITUDE, 3168c2522d8SEli Sennesh GB_SVC_NO_DE_EMPHASIS, 317c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_FAST_MODE, 2, 2, 318c161c0fcSLaurent Pinchart GB_SVC_PWRM_RXTERMINATION | 3198c2522d8SEli Sennesh GB_SVC_PWRM_TXTERMINATION, 0, 3208c2522d8SEli Sennesh NULL, NULL); 321c161c0fcSLaurent Pinchart else 322c161c0fcSLaurent Pinchart ret = gb_svc_intf_set_power_mode(svc, intf_id, 323c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_HS_SERIES_A, 324c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_SLOW_AUTO_MODE, 325ee2f2074SMitchell Tasman 2, 1, 3268c2522d8SEli Sennesh GB_SVC_SMALL_AMPLITUDE, 3278c2522d8SEli Sennesh GB_SVC_NO_DE_EMPHASIS, 328c161c0fcSLaurent Pinchart GB_SVC_UNIPRO_SLOW_AUTO_MODE, 329ee2f2074SMitchell Tasman 2, 1, 3308c2522d8SEli Sennesh 0, 0, 3318c2522d8SEli Sennesh NULL, NULL); 332c161c0fcSLaurent Pinchart 333c161c0fcSLaurent Pinchart return ret; 334c161c0fcSLaurent Pinchart } 335c161c0fcSLaurent Pinchart 336c161c0fcSLaurent Pinchart static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) 337c161c0fcSLaurent Pinchart { 338c161c0fcSLaurent Pinchart struct gb_interface *intf = gcam->connection->intf; 339c161c0fcSLaurent Pinchart struct gb_svc *svc = gcam->connection->hd->svc; 340c161c0fcSLaurent Pinchart int ret; 341c161c0fcSLaurent Pinchart 342c161c0fcSLaurent Pinchart ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs); 343c161c0fcSLaurent Pinchart if (ret < 0) { 344c161c0fcSLaurent Pinchart gcam_err(gcam, "failed to set module interface to %s (%d)\n", 345c161c0fcSLaurent Pinchart hs ? "HS" : "PWM", ret); 346c161c0fcSLaurent Pinchart return ret; 347c161c0fcSLaurent Pinchart } 348c161c0fcSLaurent Pinchart 349c161c0fcSLaurent Pinchart ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); 350c161c0fcSLaurent Pinchart if (ret < 0) { 351b573b0e6SLaurent Pinchart gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs); 352c161c0fcSLaurent Pinchart gcam_err(gcam, "failed to set AP interface to %s (%d)\n", 353c161c0fcSLaurent Pinchart hs ? "HS" : "PWM", ret); 354c161c0fcSLaurent Pinchart return ret; 355c161c0fcSLaurent Pinchart } 356c161c0fcSLaurent Pinchart 357c161c0fcSLaurent Pinchart return 0; 358c161c0fcSLaurent Pinchart } 359c161c0fcSLaurent Pinchart 360f3d5f661SLaurent Pinchart struct ap_csi_config_request { 361f3d5f661SLaurent Pinchart __u8 csi_id; 362f3d5f661SLaurent Pinchart __u8 flags; 363f3d5f661SLaurent Pinchart #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 364f3d5f661SLaurent Pinchart __u8 num_lanes; 365f3d5f661SLaurent Pinchart __u8 padding; 366f88b94ecSJacopo Mondi __le32 csi_clk_freq; 367f88b94ecSJacopo Mondi __le32 max_pkt_size; 368f3d5f661SLaurent Pinchart } __packed; 369f3d5f661SLaurent Pinchart 37024f9a6e4SLaurent Pinchart /* 37124f9a6e4SLaurent Pinchart * TODO: Compute the number of lanes dynamically based on bandwidth 37224f9a6e4SLaurent Pinchart * requirements. 37324f9a6e4SLaurent Pinchart */ 37424f9a6e4SLaurent Pinchart #define GB_CAMERA_CSI_NUM_DATA_LANES 4 375f88b94ecSJacopo Mondi 376f88b94ecSJacopo Mondi #define GB_CAMERA_CSI_CLK_FREQ_MAX 999000000U 377f88b94ecSJacopo Mondi #define GB_CAMERA_CSI_CLK_FREQ_MIN 100000000U 378f88b94ecSJacopo Mondi #define GB_CAMERA_CSI_CLK_FREQ_MARGIN 150000000U 37924f9a6e4SLaurent Pinchart 380f3d5f661SLaurent Pinchart static int gb_camera_setup_data_connection(struct gb_camera *gcam, 381f88b94ecSJacopo Mondi struct gb_camera_configure_streams_response *resp, 382f3d5f661SLaurent Pinchart struct gb_camera_csi_params *csi_params) 383f3d5f661SLaurent Pinchart { 384f3d5f661SLaurent Pinchart struct ap_csi_config_request csi_cfg; 3859120b906SLaurent Pinchart struct gb_connection *conn; 386f88b94ecSJacopo Mondi unsigned int clk_freq; 387f3d5f661SLaurent Pinchart int ret; 388f3d5f661SLaurent Pinchart 3899120b906SLaurent Pinchart /* 3909120b906SLaurent Pinchart * Create the data connection between the camera module data CPort and 3919120b906SLaurent Pinchart * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. 3929120b906SLaurent Pinchart */ 3939120b906SLaurent Pinchart conn = gb_connection_create_offloaded(gcam->bundle, gcam->data_cport_id, 3949120b906SLaurent Pinchart GB_CONNECTION_FLAG_NO_FLOWCTRL | 3959120b906SLaurent Pinchart GB_CONNECTION_FLAG_CDSI1); 3969120b906SLaurent Pinchart if (IS_ERR(conn)) 3979120b906SLaurent Pinchart return PTR_ERR(conn); 3989120b906SLaurent Pinchart 3999120b906SLaurent Pinchart gcam->data_connection = conn; 4009120b906SLaurent Pinchart gb_connection_set_data(conn, gcam); 4019120b906SLaurent Pinchart 4029120b906SLaurent Pinchart ret = gb_connection_enable(conn); 4039120b906SLaurent Pinchart if (ret) 4049120b906SLaurent Pinchart goto error_conn_destroy; 4059120b906SLaurent Pinchart 406f3d5f661SLaurent Pinchart /* Set the UniPro link to high speed mode. */ 407f3d5f661SLaurent Pinchart ret = gb_camera_set_power_mode(gcam, true); 408f3d5f661SLaurent Pinchart if (ret < 0) 4099120b906SLaurent Pinchart goto error_conn_disable; 410f3d5f661SLaurent Pinchart 411f3d5f661SLaurent Pinchart /* 412f88b94ecSJacopo Mondi * Configure the APB-A CSI-2 transmitter. 413f3d5f661SLaurent Pinchart * 414f88b94ecSJacopo Mondi * Hardcode the number of lanes to 4 and compute the bus clock frequency 415f88b94ecSJacopo Mondi * based on the module bandwidth requirements with a safety margin. 416f3d5f661SLaurent Pinchart */ 417f3d5f661SLaurent Pinchart memset(&csi_cfg, 0, sizeof(csi_cfg)); 418f3d5f661SLaurent Pinchart csi_cfg.csi_id = 1; 419f3d5f661SLaurent Pinchart csi_cfg.flags = 0; 42024f9a6e4SLaurent Pinchart csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES; 421f88b94ecSJacopo Mondi 422f88b94ecSJacopo Mondi clk_freq = resp->data_rate / 2 / GB_CAMERA_CSI_NUM_DATA_LANES; 423f88b94ecSJacopo Mondi clk_freq = clamp(clk_freq + GB_CAMERA_CSI_CLK_FREQ_MARGIN, 424f88b94ecSJacopo Mondi GB_CAMERA_CSI_CLK_FREQ_MIN, 425f88b94ecSJacopo Mondi GB_CAMERA_CSI_CLK_FREQ_MAX); 426f88b94ecSJacopo Mondi csi_cfg.csi_clk_freq = clk_freq; 427f88b94ecSJacopo Mondi 428f88b94ecSJacopo Mondi ret = gb_camera_get_max_pkt_size(gcam, resp); 429f88b94ecSJacopo Mondi if (ret < 0) { 430f88b94ecSJacopo Mondi ret = -EIO; 431f88b94ecSJacopo Mondi goto error_power; 432f88b94ecSJacopo Mondi } 433f88b94ecSJacopo Mondi csi_cfg.max_pkt_size = ret; 434f3d5f661SLaurent Pinchart 435f3d5f661SLaurent Pinchart ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 436f3d5f661SLaurent Pinchart sizeof(csi_cfg), 437f3d5f661SLaurent Pinchart GB_APB_REQUEST_CSI_TX_CONTROL, false); 438f3d5f661SLaurent Pinchart if (ret < 0) { 439f3d5f661SLaurent Pinchart gcam_err(gcam, "failed to start the CSI transmitter\n"); 4409120b906SLaurent Pinchart goto error_power; 441f3d5f661SLaurent Pinchart } 442f3d5f661SLaurent Pinchart 443f3d5f661SLaurent Pinchart if (csi_params) { 444f88b94ecSJacopo Mondi csi_params->clk_freq = csi_cfg.csi_clk_freq; 445f3d5f661SLaurent Pinchart csi_params->num_lanes = csi_cfg.num_lanes; 446f3d5f661SLaurent Pinchart } 447f3d5f661SLaurent Pinchart 448f3d5f661SLaurent Pinchart return 0; 4499120b906SLaurent Pinchart 4509120b906SLaurent Pinchart error_power: 4519120b906SLaurent Pinchart gb_camera_set_power_mode(gcam, false); 4529120b906SLaurent Pinchart error_conn_disable: 4539120b906SLaurent Pinchart gb_connection_disable(gcam->data_connection); 4549120b906SLaurent Pinchart error_conn_destroy: 4559120b906SLaurent Pinchart gb_connection_destroy(gcam->data_connection); 4569120b906SLaurent Pinchart gcam->data_connection = NULL; 4579120b906SLaurent Pinchart return ret; 458f3d5f661SLaurent Pinchart } 459f3d5f661SLaurent Pinchart 460f3d5f661SLaurent Pinchart static void gb_camera_teardown_data_connection(struct gb_camera *gcam) 461f3d5f661SLaurent Pinchart { 462f3d5f661SLaurent Pinchart struct ap_csi_config_request csi_cfg; 463f3d5f661SLaurent Pinchart int ret; 464f3d5f661SLaurent Pinchart 465f3d5f661SLaurent Pinchart /* Stop the APB1 CSI transmitter. */ 466f3d5f661SLaurent Pinchart memset(&csi_cfg, 0, sizeof(csi_cfg)); 467f3d5f661SLaurent Pinchart csi_cfg.csi_id = 1; 468f3d5f661SLaurent Pinchart 469f3d5f661SLaurent Pinchart ret = gb_hd_output(gcam->connection->hd, &csi_cfg, 470f3d5f661SLaurent Pinchart sizeof(csi_cfg), 471f3d5f661SLaurent Pinchart GB_APB_REQUEST_CSI_TX_CONTROL, false); 472f3d5f661SLaurent Pinchart 473f3d5f661SLaurent Pinchart if (ret < 0) 474f3d5f661SLaurent Pinchart gcam_err(gcam, "failed to stop the CSI transmitter\n"); 475f3d5f661SLaurent Pinchart 476f3d5f661SLaurent Pinchart /* Set the UniPro link to low speed mode. */ 477f3d5f661SLaurent Pinchart gb_camera_set_power_mode(gcam, false); 4789120b906SLaurent Pinchart 4799120b906SLaurent Pinchart /* Destroy the data connection. */ 4809120b906SLaurent Pinchart gb_connection_disable(gcam->data_connection); 4819120b906SLaurent Pinchart gb_connection_destroy(gcam->data_connection); 4829120b906SLaurent Pinchart gcam->data_connection = NULL; 483f3d5f661SLaurent Pinchart } 484f3d5f661SLaurent Pinchart 485f3d5f661SLaurent Pinchart /* ----------------------------------------------------------------------------- 486f3d5f661SLaurent Pinchart * Camera Protocol Operations 487f3d5f661SLaurent Pinchart */ 488f3d5f661SLaurent Pinchart 48948b15a9bSLaurent Pinchart static int gb_camera_capabilities(struct gb_camera *gcam, 49048b15a9bSLaurent Pinchart u8 *capabilities, size_t *size) 49148b15a9bSLaurent Pinchart { 49248b15a9bSLaurent Pinchart int ret; 49348b15a9bSLaurent Pinchart 494211634f2SDavid Lin ret = gb_pm_runtime_get_sync(gcam->bundle); 495211634f2SDavid Lin if (ret) 496211634f2SDavid Lin return ret; 497211634f2SDavid Lin 49817ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 49917ca6770SEvgeniy Borisov 50017ca6770SEvgeniy Borisov if (!gcam->connection) { 50117ca6770SEvgeniy Borisov ret = -EINVAL; 50217ca6770SEvgeniy Borisov goto done; 50317ca6770SEvgeniy Borisov } 50417ca6770SEvgeniy Borisov 505fdf73c00SJacopo Mondi ret = gb_camera_operation_sync_flags(gcam->connection, 506fdf73c00SJacopo Mondi GB_CAMERA_TYPE_CAPABILITIES, 50748b15a9bSLaurent Pinchart GB_OPERATION_FLAG_SHORT_RESPONSE, 508fdf73c00SJacopo Mondi NULL, 0, 509fdf73c00SJacopo Mondi (void *)capabilities, size); 510fdf73c00SJacopo Mondi if (ret) 51148b15a9bSLaurent Pinchart gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); 51248b15a9bSLaurent Pinchart 51348b15a9bSLaurent Pinchart done: 51417ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 515211634f2SDavid Lin 516211634f2SDavid Lin gb_pm_runtime_put_autosuspend(gcam->bundle); 517211634f2SDavid Lin 51848b15a9bSLaurent Pinchart return ret; 51948b15a9bSLaurent Pinchart } 52048b15a9bSLaurent Pinchart 5213265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam, 5224068487cSLaurent Pinchart unsigned int *num_streams, 5234068487cSLaurent Pinchart unsigned int *flags, 524b4905038SEvgeniy Borisov struct gb_camera_stream_config *streams, 525b4905038SEvgeniy Borisov struct gb_camera_csi_params *csi_params) 5263265edafSLaurent Pinchart { 5273265edafSLaurent Pinchart struct gb_camera_configure_streams_request *req; 5283265edafSLaurent Pinchart struct gb_camera_configure_streams_response *resp; 5294068487cSLaurent Pinchart unsigned int nstreams = *num_streams; 5303265edafSLaurent Pinchart unsigned int i; 5313265edafSLaurent Pinchart size_t req_size; 5323265edafSLaurent Pinchart size_t resp_size; 5333265edafSLaurent Pinchart int ret; 5343265edafSLaurent Pinchart 5353265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 5363265edafSLaurent Pinchart return -EINVAL; 5373265edafSLaurent Pinchart 5383265edafSLaurent Pinchart req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); 5393265edafSLaurent Pinchart resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); 5403265edafSLaurent Pinchart 5413265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 5423265edafSLaurent Pinchart resp = kmalloc(resp_size, GFP_KERNEL); 543c9161d72SJacopo Mondi if (!req || !resp) { 54417ca6770SEvgeniy Borisov kfree(req); 54517ca6770SEvgeniy Borisov kfree(resp); 54617ca6770SEvgeniy Borisov return -ENOMEM; 5473265edafSLaurent Pinchart } 5483265edafSLaurent Pinchart 549b787d413SJacopo Mondi req->num_streams = nstreams; 5504068487cSLaurent Pinchart req->flags = *flags; 5513265edafSLaurent Pinchart req->padding = 0; 5523265edafSLaurent Pinchart 5533265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 5543265edafSLaurent Pinchart struct gb_camera_stream_config_request *cfg = &req->config[i]; 5553265edafSLaurent Pinchart 556c6622216SLaurent Pinchart cfg->width = cpu_to_le16(streams[i].width); 557c6622216SLaurent Pinchart cfg->height = cpu_to_le16(streams[i].height); 558c6622216SLaurent Pinchart cfg->format = cpu_to_le16(streams[i].format); 5593265edafSLaurent Pinchart cfg->padding = 0; 5603265edafSLaurent Pinchart } 5613265edafSLaurent Pinchart 56217ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 56317ca6770SEvgeniy Borisov 564211634f2SDavid Lin ret = gb_pm_runtime_get_sync(gcam->bundle); 565211634f2SDavid Lin if (ret) 566211634f2SDavid Lin goto done_skip_pm_put; 567211634f2SDavid Lin 56817ca6770SEvgeniy Borisov if (!gcam->connection) { 56917ca6770SEvgeniy Borisov ret = -EINVAL; 57017ca6770SEvgeniy Borisov goto done; 57117ca6770SEvgeniy Borisov } 57217ca6770SEvgeniy Borisov 573d165a618SJacopo Mondi ret = gb_camera_operation_sync_flags(gcam->connection, 5743265edafSLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 575d165a618SJacopo Mondi GB_OPERATION_FLAG_SHORT_RESPONSE, 576d165a618SJacopo Mondi req, req_size, 577c9161d72SJacopo Mondi resp, &resp_size); 5783265edafSLaurent Pinchart if (ret < 0) 57966c36070SLaurent Pinchart goto done; 5803265edafSLaurent Pinchart 581c9161d72SJacopo Mondi ret = gb_camera_configure_streams_validate_response(gcam, resp, 582c9161d72SJacopo Mondi nstreams); 583d165a618SJacopo Mondi if (ret < 0) 58466c36070SLaurent Pinchart goto done; 5853265edafSLaurent Pinchart 5867f93eab7SJacopo Mondi *flags = resp->flags; 5877f93eab7SJacopo Mondi *num_streams = resp->num_streams; 5887f93eab7SJacopo Mondi 58936ab1108SJacopo Mondi for (i = 0; i < resp->num_streams; ++i) { 5903265edafSLaurent Pinchart struct gb_camera_stream_config_response *cfg = &resp->config[i]; 5913265edafSLaurent Pinchart 592c6622216SLaurent Pinchart streams[i].width = le16_to_cpu(cfg->width); 593c6622216SLaurent Pinchart streams[i].height = le16_to_cpu(cfg->height); 594c6622216SLaurent Pinchart streams[i].format = le16_to_cpu(cfg->format); 5953265edafSLaurent Pinchart streams[i].vc = cfg->virtual_channel; 5963265edafSLaurent Pinchart streams[i].dt[0] = cfg->data_type[0]; 5973265edafSLaurent Pinchart streams[i].dt[1] = cfg->data_type[1]; 598c6622216SLaurent Pinchart streams[i].max_size = le32_to_cpu(cfg->max_size); 5993265edafSLaurent Pinchart } 6003265edafSLaurent Pinchart 601640924d2SLaurent Pinchart if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || 6027f93eab7SJacopo Mondi (req->flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) 60366c36070SLaurent Pinchart goto done; 60466c36070SLaurent Pinchart 6053b8ebfebSLaurent Pinchart if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { 6063b8ebfebSLaurent Pinchart gb_camera_teardown_data_connection(gcam); 6073b8ebfebSLaurent Pinchart gcam->state = GB_CAMERA_STATE_UNCONFIGURED; 608211634f2SDavid Lin 609211634f2SDavid Lin /* 610211634f2SDavid Lin * When unconfiguring streams release the PM runtime reference 611211634f2SDavid Lin * that was acquired when streams were configured. The bundle 612211634f2SDavid Lin * won't be suspended until the PM runtime reference acquired at 613211634f2SDavid Lin * the beginning of this function gets released right before 614211634f2SDavid Lin * returning. 615211634f2SDavid Lin */ 616211634f2SDavid Lin gb_pm_runtime_put_noidle(gcam->bundle); 6173b8ebfebSLaurent Pinchart } 6183b8ebfebSLaurent Pinchart 619f88b94ecSJacopo Mondi if (resp->num_streams == 0) 620f88b94ecSJacopo Mondi goto done; 621f88b94ecSJacopo Mondi 622211634f2SDavid Lin /* 623211634f2SDavid Lin * Make sure the bundle won't be suspended until streams get 624211634f2SDavid Lin * unconfigured after the stream is configured successfully 625211634f2SDavid Lin */ 626211634f2SDavid Lin gb_pm_runtime_get_noresume(gcam->bundle); 627211634f2SDavid Lin 628f88b94ecSJacopo Mondi /* Setup CSI-2 connection from APB-A to AP */ 629f3d5f661SLaurent Pinchart ret = gb_camera_setup_data_connection(gcam, resp, csi_params); 630f3d5f661SLaurent Pinchart if (ret < 0) { 631f3d5f661SLaurent Pinchart memset(req, 0, sizeof(*req)); 632f3d5f661SLaurent Pinchart gb_operation_sync(gcam->connection, 633f3d5f661SLaurent Pinchart GB_CAMERA_TYPE_CONFIGURE_STREAMS, 63480b3982bSJacopo Mondi req, sizeof(*req), 63580b3982bSJacopo Mondi resp, sizeof(*resp)); 6367f93eab7SJacopo Mondi *flags = 0; 6377f93eab7SJacopo Mondi *num_streams = 0; 638211634f2SDavid Lin gb_pm_runtime_put_noidle(gcam->bundle); 63966c36070SLaurent Pinchart goto done; 640b4905038SEvgeniy Borisov } 6413b8ebfebSLaurent Pinchart 6423b8ebfebSLaurent Pinchart gcam->state = GB_CAMERA_STATE_CONFIGURED; 643142b21feSLaurent Pinchart 6443265edafSLaurent Pinchart done: 645211634f2SDavid Lin gb_pm_runtime_put_autosuspend(gcam->bundle); 646211634f2SDavid Lin 647211634f2SDavid Lin done_skip_pm_put: 64817ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 6493265edafSLaurent Pinchart kfree(req); 6503265edafSLaurent Pinchart kfree(resp); 6513265edafSLaurent Pinchart return ret; 6523265edafSLaurent Pinchart } 6533265edafSLaurent Pinchart 6543265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, 6553265edafSLaurent Pinchart unsigned int streams, unsigned int num_frames, 6563265edafSLaurent Pinchart size_t settings_size, const void *settings) 6573265edafSLaurent Pinchart { 6583265edafSLaurent Pinchart struct gb_camera_capture_request *req; 6593265edafSLaurent Pinchart size_t req_size; 660b9f71bc8SJohan Hovold int ret; 6613265edafSLaurent Pinchart 6623265edafSLaurent Pinchart if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) 6633265edafSLaurent Pinchart return -EINVAL; 6643265edafSLaurent Pinchart 6653265edafSLaurent Pinchart req_size = sizeof(*req) + settings_size; 6663265edafSLaurent Pinchart req = kmalloc(req_size, GFP_KERNEL); 6673265edafSLaurent Pinchart if (!req) 6683265edafSLaurent Pinchart return -ENOMEM; 6693265edafSLaurent Pinchart 670c6622216SLaurent Pinchart req->request_id = cpu_to_le32(request_id); 6713265edafSLaurent Pinchart req->streams = streams; 6723265edafSLaurent Pinchart req->padding = 0; 673c6622216SLaurent Pinchart req->num_frames = cpu_to_le16(num_frames); 6743265edafSLaurent Pinchart memcpy(req->settings, settings, settings_size); 6753265edafSLaurent Pinchart 67617ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 67717ca6770SEvgeniy Borisov 67817ca6770SEvgeniy Borisov if (!gcam->connection) { 67917ca6770SEvgeniy Borisov ret = -EINVAL; 68017ca6770SEvgeniy Borisov goto done; 68117ca6770SEvgeniy Borisov } 68217ca6770SEvgeniy Borisov 683b9f71bc8SJohan Hovold ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, 6843265edafSLaurent Pinchart req, req_size, NULL, 0); 68517ca6770SEvgeniy Borisov done: 68617ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 687b9f71bc8SJohan Hovold 688b9f71bc8SJohan Hovold kfree(req); 689b9f71bc8SJohan Hovold 690b9f71bc8SJohan Hovold return ret; 6913265edafSLaurent Pinchart } 6923265edafSLaurent Pinchart 6933265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) 6943265edafSLaurent Pinchart { 6953265edafSLaurent Pinchart struct gb_camera_flush_response resp; 6963265edafSLaurent Pinchart int ret; 6973265edafSLaurent Pinchart 69817ca6770SEvgeniy Borisov mutex_lock(&gcam->mutex); 69917ca6770SEvgeniy Borisov 70017ca6770SEvgeniy Borisov if (!gcam->connection) { 70117ca6770SEvgeniy Borisov ret = -EINVAL; 70217ca6770SEvgeniy Borisov goto done; 70317ca6770SEvgeniy Borisov } 70417ca6770SEvgeniy Borisov 7053265edafSLaurent Pinchart ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, 7063265edafSLaurent Pinchart &resp, sizeof(resp)); 70717ca6770SEvgeniy Borisov 7083265edafSLaurent Pinchart if (ret < 0) 70917ca6770SEvgeniy Borisov goto done; 7103265edafSLaurent Pinchart 7113265edafSLaurent Pinchart if (request_id) 712c6622216SLaurent Pinchart *request_id = le32_to_cpu(resp.request_id); 7133265edafSLaurent Pinchart 71417ca6770SEvgeniy Borisov done: 71517ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 71617ca6770SEvgeniy Borisov 71717ca6770SEvgeniy Borisov return ret; 7183265edafSLaurent Pinchart } 7193265edafSLaurent Pinchart 72068b66c28SLaurent Pinchart static int gb_camera_request_handler(struct gb_operation *op) 7213265edafSLaurent Pinchart { 7220ec30632SGreg Kroah-Hartman struct gb_camera *gcam = gb_connection_get_data(op->connection); 7233265edafSLaurent Pinchart struct gb_camera_metadata_request *payload; 7243265edafSLaurent Pinchart struct gb_message *request; 7253265edafSLaurent Pinchart 72668b66c28SLaurent Pinchart if (op->type != GB_CAMERA_TYPE_METADATA) { 72768b66c28SLaurent Pinchart gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type); 7283265edafSLaurent Pinchart return -EINVAL; 7293265edafSLaurent Pinchart } 7303265edafSLaurent Pinchart 7313265edafSLaurent Pinchart request = op->request; 7323265edafSLaurent Pinchart 7333265edafSLaurent Pinchart if (request->payload_size < sizeof(*payload)) { 7343265edafSLaurent Pinchart gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", 7353265edafSLaurent Pinchart request->payload_size, sizeof(*payload)); 7363265edafSLaurent Pinchart return -EINVAL; 7373265edafSLaurent Pinchart } 7383265edafSLaurent Pinchart 7393265edafSLaurent Pinchart payload = request->payload; 7403265edafSLaurent Pinchart 7413265edafSLaurent Pinchart gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", 7423265edafSLaurent Pinchart payload->request_id, payload->frame_number, payload->stream); 7433265edafSLaurent Pinchart 7443265edafSLaurent Pinchart return 0; 7453265edafSLaurent Pinchart } 7463265edafSLaurent Pinchart 7473265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 748127bada1SJacopo Mondi * Interface with HOST gmp camera. 7493a8dba4eSGjorgji Rosikopulos */ 7503a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) 7513a8dba4eSGjorgji Rosikopulos { 7523a8dba4eSGjorgji Rosikopulos unsigned int i; 7533a8dba4eSGjorgji Rosikopulos 7546cc27048SJacopo Mondi for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { 7556cc27048SJacopo Mondi if (gb_fmt_info[i].mbus_code == mbus_code) 7566cc27048SJacopo Mondi return gb_fmt_info[i].gb_format; 7573a8dba4eSGjorgji Rosikopulos } 7586cc27048SJacopo Mondi return gb_fmt_info[0].gb_format; 7593a8dba4eSGjorgji Rosikopulos } 7603a8dba4eSGjorgji Rosikopulos 7613a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) 7623a8dba4eSGjorgji Rosikopulos { 7633a8dba4eSGjorgji Rosikopulos unsigned int i; 7643a8dba4eSGjorgji Rosikopulos 7656cc27048SJacopo Mondi for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { 7666cc27048SJacopo Mondi if (gb_fmt_info[i].gb_format == gb_fmt) 7676cc27048SJacopo Mondi return gb_fmt_info[i].mbus_code; 7683a8dba4eSGjorgji Rosikopulos } 7696cc27048SJacopo Mondi return gb_fmt_info[0].mbus_code; 7703a8dba4eSGjorgji Rosikopulos } 7713a8dba4eSGjorgji Rosikopulos 772a883b0ebSJacopo Mondi static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) 773a883b0ebSJacopo Mondi { 774a883b0ebSJacopo Mondi struct gb_camera *gcam = priv; 775a883b0ebSJacopo Mondi size_t capabilities_len = len; 776a883b0ebSJacopo Mondi int ret; 777a883b0ebSJacopo Mondi 778a883b0ebSJacopo Mondi ret = gb_camera_capabilities(gcam, data, &capabilities_len); 779a883b0ebSJacopo Mondi if (ret) 780a883b0ebSJacopo Mondi return ret; 781a883b0ebSJacopo Mondi 782a883b0ebSJacopo Mondi return capabilities_len; 783a883b0ebSJacopo Mondi } 784a883b0ebSJacopo Mondi 7855b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, 786b4905038SEvgeniy Borisov unsigned int *flags, struct gb_camera_stream *streams, 787b4905038SEvgeniy Borisov struct gb_camera_csi_params *csi_params) 7883a8dba4eSGjorgji Rosikopulos { 7893a8dba4eSGjorgji Rosikopulos struct gb_camera *gcam = priv; 7903a8dba4eSGjorgji Rosikopulos struct gb_camera_stream_config *gb_streams; 7915b032710SGjorgji Rosikopulos unsigned int gb_flags = 0; 7925b032710SGjorgji Rosikopulos unsigned int gb_nstreams = *nstreams; 7933a8dba4eSGjorgji Rosikopulos unsigned int i; 7943a8dba4eSGjorgji Rosikopulos int ret; 7953a8dba4eSGjorgji Rosikopulos 7965b032710SGjorgji Rosikopulos if (gb_nstreams > GB_CAMERA_MAX_STREAMS) 7973a8dba4eSGjorgji Rosikopulos return -EINVAL; 7983a8dba4eSGjorgji Rosikopulos 7995b032710SGjorgji Rosikopulos gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); 8003a8dba4eSGjorgji Rosikopulos if (!gb_streams) 8013a8dba4eSGjorgji Rosikopulos return -ENOMEM; 8023a8dba4eSGjorgji Rosikopulos 8035b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 8043a8dba4eSGjorgji Rosikopulos gb_streams[i].width = streams[i].width; 8053a8dba4eSGjorgji Rosikopulos gb_streams[i].height = streams[i].height; 8063a8dba4eSGjorgji Rosikopulos gb_streams[i].format = 8073a8dba4eSGjorgji Rosikopulos gb_camera_mbus_to_gb(streams[i].pixel_code); 8083a8dba4eSGjorgji Rosikopulos } 8093a8dba4eSGjorgji Rosikopulos 8105b032710SGjorgji Rosikopulos if (*flags & GB_CAMERA_IN_FLAG_TEST) 8115b032710SGjorgji Rosikopulos gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; 8125b032710SGjorgji Rosikopulos 8135b032710SGjorgji Rosikopulos ret = gb_camera_configure_streams(gcam, &gb_nstreams, 814b4905038SEvgeniy Borisov &gb_flags, gb_streams, csi_params); 8153a8dba4eSGjorgji Rosikopulos if (ret < 0) 8163a8dba4eSGjorgji Rosikopulos goto done; 8175b032710SGjorgji Rosikopulos if (gb_nstreams > *nstreams) { 8185b032710SGjorgji Rosikopulos ret = -EINVAL; 8195b032710SGjorgji Rosikopulos goto done; 8205b032710SGjorgji Rosikopulos } 8213a8dba4eSGjorgji Rosikopulos 8225b032710SGjorgji Rosikopulos *flags = 0; 8235b032710SGjorgji Rosikopulos if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) 8245b032710SGjorgji Rosikopulos *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; 8255b032710SGjorgji Rosikopulos 8265b032710SGjorgji Rosikopulos for (i = 0; i < gb_nstreams; i++) { 8273a8dba4eSGjorgji Rosikopulos streams[i].width = gb_streams[i].width; 8283a8dba4eSGjorgji Rosikopulos streams[i].height = gb_streams[i].height; 8293a8dba4eSGjorgji Rosikopulos streams[i].vc = gb_streams[i].vc; 8303a8dba4eSGjorgji Rosikopulos streams[i].dt[0] = gb_streams[i].dt[0]; 8313a8dba4eSGjorgji Rosikopulos streams[i].dt[1] = gb_streams[i].dt[1]; 8323a8dba4eSGjorgji Rosikopulos streams[i].max_size = gb_streams[i].max_size; 8333a8dba4eSGjorgji Rosikopulos streams[i].pixel_code = 8343a8dba4eSGjorgji Rosikopulos gb_camera_gb_to_mbus(gb_streams[i].format); 8353a8dba4eSGjorgji Rosikopulos } 8365b032710SGjorgji Rosikopulos *nstreams = gb_nstreams; 8373a8dba4eSGjorgji Rosikopulos 8383a8dba4eSGjorgji Rosikopulos done: 8393a8dba4eSGjorgji Rosikopulos kfree(gb_streams); 8403a8dba4eSGjorgji Rosikopulos return ret; 8413a8dba4eSGjorgji Rosikopulos } 8423a8dba4eSGjorgji Rosikopulos 8433a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id, 8443a8dba4eSGjorgji Rosikopulos unsigned int streams, unsigned int num_frames, 8453a8dba4eSGjorgji Rosikopulos size_t settings_size, const void *settings) 8463a8dba4eSGjorgji Rosikopulos { 8471472ec67SGjorgji Rosikopulos struct gb_camera *gcam = priv; 8481472ec67SGjorgji Rosikopulos 8491472ec67SGjorgji Rosikopulos return gb_camera_capture(gcam, request_id, streams, num_frames, 8503a8dba4eSGjorgji Rosikopulos settings_size, settings); 8513a8dba4eSGjorgji Rosikopulos } 8523a8dba4eSGjorgji Rosikopulos 8533a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id) 8543a8dba4eSGjorgji Rosikopulos { 8551472ec67SGjorgji Rosikopulos struct gb_camera *gcam = priv; 8561472ec67SGjorgji Rosikopulos 8571472ec67SGjorgji Rosikopulos return gb_camera_flush(gcam, request_id); 8583a8dba4eSGjorgji Rosikopulos } 8593a8dba4eSGjorgji Rosikopulos 8601472ec67SGjorgji Rosikopulos static const struct gb_camera_ops gb_cam_ops = { 8611472ec67SGjorgji Rosikopulos .capabilities = gb_camera_op_capabilities, 8621472ec67SGjorgji Rosikopulos .configure_streams = gb_camera_op_configure_streams, 8631472ec67SGjorgji Rosikopulos .capture = gb_camera_op_capture, 8641472ec67SGjorgji Rosikopulos .flush = gb_camera_op_flush, 8651472ec67SGjorgji Rosikopulos }; 8661472ec67SGjorgji Rosikopulos 8673a8dba4eSGjorgji Rosikopulos /* ----------------------------------------------------------------------------- 8683265edafSLaurent Pinchart * DebugFS 8693265edafSLaurent Pinchart */ 87048b15a9bSLaurent Pinchart 8713265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, 8723265edafSLaurent Pinchart char *buf, size_t len) 8733265edafSLaurent Pinchart { 87448b15a9bSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 87548b15a9bSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES]; 87648b15a9bSLaurent Pinchart size_t size = 1024; 87748b15a9bSLaurent Pinchart unsigned int i; 87848b15a9bSLaurent Pinchart u8 *caps; 87948b15a9bSLaurent Pinchart int ret; 88048b15a9bSLaurent Pinchart 88148b15a9bSLaurent Pinchart caps = kmalloc(size, GFP_KERNEL); 88248b15a9bSLaurent Pinchart if (!caps) 88348b15a9bSLaurent Pinchart return -ENOMEM; 88448b15a9bSLaurent Pinchart 88548b15a9bSLaurent Pinchart ret = gb_camera_capabilities(gcam, caps, &size); 88648b15a9bSLaurent Pinchart if (ret < 0) 88748b15a9bSLaurent Pinchart goto done; 88848b15a9bSLaurent Pinchart 88948b15a9bSLaurent Pinchart /* 89048b15a9bSLaurent Pinchart * hex_dump_to_buffer() doesn't return the number of bytes dumped prior 89148b15a9bSLaurent Pinchart * to v4.0, we need our own implementation :-( 89248b15a9bSLaurent Pinchart */ 89348b15a9bSLaurent Pinchart buffer->length = 0; 89448b15a9bSLaurent Pinchart 89548b15a9bSLaurent Pinchart for (i = 0; i < size; i += 16) { 89648b15a9bSLaurent Pinchart unsigned int nbytes = min_t(unsigned int, size - i, 16); 89748b15a9bSLaurent Pinchart 89848b15a9bSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 89948b15a9bSLaurent Pinchart "%*ph\n", nbytes, caps + i); 90048b15a9bSLaurent Pinchart } 90148b15a9bSLaurent Pinchart 90248b15a9bSLaurent Pinchart done: 90348b15a9bSLaurent Pinchart kfree(caps); 90448b15a9bSLaurent Pinchart return ret; 9053265edafSLaurent Pinchart } 9063265edafSLaurent Pinchart 9073265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, 9083265edafSLaurent Pinchart char *buf, size_t len) 9093265edafSLaurent Pinchart { 9103265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 9113265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; 9123265edafSLaurent Pinchart struct gb_camera_stream_config *streams; 9133265edafSLaurent Pinchart unsigned int nstreams; 914b787d413SJacopo Mondi unsigned int flags; 9153265edafSLaurent Pinchart unsigned int i; 9163265edafSLaurent Pinchart char *token; 9173265edafSLaurent Pinchart int ret; 9183265edafSLaurent Pinchart 9193265edafSLaurent Pinchart /* Retrieve number of streams to configure */ 920b787d413SJacopo Mondi token = strsep(&buf, ";"); 9213265edafSLaurent Pinchart if (token == NULL) 9223265edafSLaurent Pinchart return -EINVAL; 9233265edafSLaurent Pinchart 9243265edafSLaurent Pinchart ret = kstrtouint(token, 10, &nstreams); 9253265edafSLaurent Pinchart if (ret < 0) 9263265edafSLaurent Pinchart return ret; 9273265edafSLaurent Pinchart 9283265edafSLaurent Pinchart if (nstreams > GB_CAMERA_MAX_STREAMS) 9293265edafSLaurent Pinchart return -EINVAL; 9303265edafSLaurent Pinchart 931b787d413SJacopo Mondi token = strsep(&buf, ";"); 932b787d413SJacopo Mondi if (token == NULL) 933b787d413SJacopo Mondi return -EINVAL; 934b787d413SJacopo Mondi 935b787d413SJacopo Mondi ret = kstrtouint(token, 10, &flags); 936b787d413SJacopo Mondi if (ret < 0) 937b787d413SJacopo Mondi return ret; 938b787d413SJacopo Mondi 9393265edafSLaurent Pinchart /* For each stream to configure parse width, height and format */ 9403265edafSLaurent Pinchart streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); 9413265edafSLaurent Pinchart if (!streams) 9423265edafSLaurent Pinchart return -ENOMEM; 9433265edafSLaurent Pinchart 9443265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 9453265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 9463265edafSLaurent Pinchart 9473265edafSLaurent Pinchart /* width */ 9483265edafSLaurent Pinchart token = strsep(&buf, ";"); 9493265edafSLaurent Pinchart if (token == NULL) { 9503265edafSLaurent Pinchart ret = -EINVAL; 9513265edafSLaurent Pinchart goto done; 9523265edafSLaurent Pinchart } 9533265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->width); 9543265edafSLaurent Pinchart if (ret < 0) 9553265edafSLaurent Pinchart goto done; 9563265edafSLaurent Pinchart 9573265edafSLaurent Pinchart /* height */ 9583265edafSLaurent Pinchart token = strsep(&buf, ";"); 9593265edafSLaurent Pinchart if (token == NULL) 9603265edafSLaurent Pinchart goto done; 9613265edafSLaurent Pinchart 9623265edafSLaurent Pinchart ret = kstrtouint(token, 10, &stream->height); 9633265edafSLaurent Pinchart if (ret < 0) 9643265edafSLaurent Pinchart goto done; 9653265edafSLaurent Pinchart 9663265edafSLaurent Pinchart /* Image format code */ 9673265edafSLaurent Pinchart token = strsep(&buf, ";"); 9683265edafSLaurent Pinchart if (token == NULL) 9693265edafSLaurent Pinchart goto done; 9703265edafSLaurent Pinchart 9713265edafSLaurent Pinchart ret = kstrtouint(token, 16, &stream->format); 9723265edafSLaurent Pinchart if (ret < 0) 9733265edafSLaurent Pinchart goto done; 9743265edafSLaurent Pinchart } 9753265edafSLaurent Pinchart 976b4905038SEvgeniy Borisov ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams, 977b4905038SEvgeniy Borisov NULL); 9783265edafSLaurent Pinchart if (ret < 0) 9793265edafSLaurent Pinchart goto done; 9803265edafSLaurent Pinchart 9814068487cSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); 9823265edafSLaurent Pinchart 9833265edafSLaurent Pinchart for (i = 0; i < nstreams; ++i) { 9843265edafSLaurent Pinchart struct gb_camera_stream_config *stream = &streams[i]; 9853265edafSLaurent Pinchart 9863265edafSLaurent Pinchart buffer->length += sprintf(buffer->data + buffer->length, 9873265edafSLaurent Pinchart "%u;%u;%u;%u;%u;%u;%u;", 9883265edafSLaurent Pinchart stream->width, stream->height, 9893265edafSLaurent Pinchart stream->format, stream->vc, 9903265edafSLaurent Pinchart stream->dt[0], stream->dt[1], 9913265edafSLaurent Pinchart stream->max_size); 9923265edafSLaurent Pinchart } 9933265edafSLaurent Pinchart 9943265edafSLaurent Pinchart ret = len; 9953265edafSLaurent Pinchart 9963265edafSLaurent Pinchart done: 9973265edafSLaurent Pinchart kfree(streams); 9983265edafSLaurent Pinchart return ret; 9993265edafSLaurent Pinchart }; 10003265edafSLaurent Pinchart 10013265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, 10023265edafSLaurent Pinchart char *buf, size_t len) 10033265edafSLaurent Pinchart { 10043265edafSLaurent Pinchart unsigned int request_id; 10053265edafSLaurent Pinchart unsigned int streams_mask; 10063265edafSLaurent Pinchart unsigned int num_frames; 10073265edafSLaurent Pinchart char *token; 10083265edafSLaurent Pinchart int ret; 10093265edafSLaurent Pinchart 10103265edafSLaurent Pinchart /* Request id */ 10113265edafSLaurent Pinchart token = strsep(&buf, ";"); 10123265edafSLaurent Pinchart if (token == NULL) 10133265edafSLaurent Pinchart return -EINVAL; 10143265edafSLaurent Pinchart ret = kstrtouint(token, 10, &request_id); 10153265edafSLaurent Pinchart if (ret < 0) 10163265edafSLaurent Pinchart return ret; 10173265edafSLaurent Pinchart 10183265edafSLaurent Pinchart /* Stream mask */ 10193265edafSLaurent Pinchart token = strsep(&buf, ";"); 10203265edafSLaurent Pinchart if (token == NULL) 10213265edafSLaurent Pinchart return -EINVAL; 10223265edafSLaurent Pinchart ret = kstrtouint(token, 16, &streams_mask); 10233265edafSLaurent Pinchart if (ret < 0) 10243265edafSLaurent Pinchart return ret; 10253265edafSLaurent Pinchart 10263265edafSLaurent Pinchart /* number of frames */ 10273265edafSLaurent Pinchart token = strsep(&buf, ";"); 10283265edafSLaurent Pinchart if (token == NULL) 10293265edafSLaurent Pinchart return -EINVAL; 10303265edafSLaurent Pinchart ret = kstrtouint(token, 10, &num_frames); 10313265edafSLaurent Pinchart if (ret < 0) 10323265edafSLaurent Pinchart return ret; 10333265edafSLaurent Pinchart 10343265edafSLaurent Pinchart ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, 10353265edafSLaurent Pinchart NULL); 10363265edafSLaurent Pinchart if (ret < 0) 10373265edafSLaurent Pinchart return ret; 10383265edafSLaurent Pinchart 10393265edafSLaurent Pinchart return len; 10403265edafSLaurent Pinchart } 10413265edafSLaurent Pinchart 10423265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, 10433265edafSLaurent Pinchart char *buf, size_t len) 10443265edafSLaurent Pinchart { 10453265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer = 10463265edafSLaurent Pinchart &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; 10473265edafSLaurent Pinchart unsigned int req_id; 10483265edafSLaurent Pinchart int ret; 10493265edafSLaurent Pinchart 10503265edafSLaurent Pinchart ret = gb_camera_flush(gcam, &req_id); 10513265edafSLaurent Pinchart if (ret < 0) 10523265edafSLaurent Pinchart return ret; 10533265edafSLaurent Pinchart 10543265edafSLaurent Pinchart buffer->length = sprintf(buffer->data, "%u", req_id); 10553265edafSLaurent Pinchart 10563265edafSLaurent Pinchart return len; 10573265edafSLaurent Pinchart } 10583265edafSLaurent Pinchart 10593265edafSLaurent Pinchart struct gb_camera_debugfs_entry { 10603265edafSLaurent Pinchart const char *name; 10613265edafSLaurent Pinchart unsigned int mask; 10623265edafSLaurent Pinchart unsigned int buffer; 10633265edafSLaurent Pinchart ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); 10643265edafSLaurent Pinchart }; 10653265edafSLaurent Pinchart 10663265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { 10673265edafSLaurent Pinchart { 10683265edafSLaurent Pinchart .name = "capabilities", 10693265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO, 10703265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, 10713265edafSLaurent Pinchart .execute = gb_camera_debugfs_capabilities, 10723265edafSLaurent Pinchart }, { 10733265edafSLaurent Pinchart .name = "configure_streams", 10743265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 10753265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, 10763265edafSLaurent Pinchart .execute = gb_camera_debugfs_configure_streams, 10773265edafSLaurent Pinchart }, { 10783265edafSLaurent Pinchart .name = "capture", 10793265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 10803265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, 10813265edafSLaurent Pinchart .execute = gb_camera_debugfs_capture, 10823265edafSLaurent Pinchart }, { 10833265edafSLaurent Pinchart .name = "flush", 10843265edafSLaurent Pinchart .mask = S_IFREG | S_IRUGO | S_IWUGO, 10853265edafSLaurent Pinchart .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, 10863265edafSLaurent Pinchart .execute = gb_camera_debugfs_flush, 10873265edafSLaurent Pinchart }, 10883265edafSLaurent Pinchart }; 10893265edafSLaurent Pinchart 10903265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, 10913265edafSLaurent Pinchart size_t len, loff_t *offset) 10923265edafSLaurent Pinchart { 10933265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 10943265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 10953265edafSLaurent Pinchart struct gb_camera_debugfs_buffer *buffer; 10963265edafSLaurent Pinchart ssize_t ret; 10973265edafSLaurent Pinchart 10983265edafSLaurent Pinchart /* For read-only entries the operation is triggered by a read. */ 10993265edafSLaurent Pinchart if (!(op->mask & S_IWUGO)) { 11003265edafSLaurent Pinchart ret = op->execute(gcam, NULL, 0); 11013265edafSLaurent Pinchart if (ret < 0) 11023265edafSLaurent Pinchart return ret; 11033265edafSLaurent Pinchart } 11043265edafSLaurent Pinchart 11053265edafSLaurent Pinchart buffer = &gcam->debugfs.buffers[op->buffer]; 11063265edafSLaurent Pinchart 11073265edafSLaurent Pinchart return simple_read_from_buffer(buf, len, offset, buffer->data, 11083265edafSLaurent Pinchart buffer->length); 11093265edafSLaurent Pinchart } 11103265edafSLaurent Pinchart 11113265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file, 11123265edafSLaurent Pinchart const char __user *buf, size_t len, 11133265edafSLaurent Pinchart loff_t *offset) 11143265edafSLaurent Pinchart { 11153265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *op = file->private_data; 11163265edafSLaurent Pinchart struct gb_camera *gcam = file->f_inode->i_private; 11173265edafSLaurent Pinchart ssize_t ret; 11183265edafSLaurent Pinchart char *kbuf; 11193265edafSLaurent Pinchart 11203265edafSLaurent Pinchart if (len > 1024) 11213265edafSLaurent Pinchart return -EINVAL; 11223265edafSLaurent Pinchart 11233265edafSLaurent Pinchart kbuf = kmalloc(len + 1, GFP_KERNEL); 1124ce35e9beSEva Rachel Retuya if (!kbuf) 11253265edafSLaurent Pinchart return -ENOMEM; 11263265edafSLaurent Pinchart 11273265edafSLaurent Pinchart if (copy_from_user(kbuf, buf, len)) { 11283265edafSLaurent Pinchart ret = -EFAULT; 11293265edafSLaurent Pinchart goto done; 11303265edafSLaurent Pinchart } 11313265edafSLaurent Pinchart 11323265edafSLaurent Pinchart kbuf[len] = '\0'; 11333265edafSLaurent Pinchart 11343265edafSLaurent Pinchart ret = op->execute(gcam, kbuf, len); 11353265edafSLaurent Pinchart 11363265edafSLaurent Pinchart done: 11373265edafSLaurent Pinchart kfree(kbuf); 11383265edafSLaurent Pinchart return ret; 11393265edafSLaurent Pinchart } 11403265edafSLaurent Pinchart 11413265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file) 11423265edafSLaurent Pinchart { 11433265edafSLaurent Pinchart unsigned int i; 11443265edafSLaurent Pinchart 11453265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 11463265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 11473265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 11483265edafSLaurent Pinchart 11494dda744cSGreg Kroah-Hartman if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { 11503265edafSLaurent Pinchart file->private_data = (void *)entry; 11513265edafSLaurent Pinchart break; 11523265edafSLaurent Pinchart } 11533265edafSLaurent Pinchart } 11543265edafSLaurent Pinchart 11553265edafSLaurent Pinchart return 0; 11563265edafSLaurent Pinchart } 11573265edafSLaurent Pinchart 11583265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = { 11593265edafSLaurent Pinchart .open = gb_camera_debugfs_open, 11603265edafSLaurent Pinchart .read = gb_camera_debugfs_read, 11613265edafSLaurent Pinchart .write = gb_camera_debugfs_write, 11623265edafSLaurent Pinchart }; 11633265edafSLaurent Pinchart 11643265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam) 11653265edafSLaurent Pinchart { 11663265edafSLaurent Pinchart struct gb_connection *connection = gcam->connection; 11673265edafSLaurent Pinchart char dirname[27]; 11683265edafSLaurent Pinchart unsigned int i; 11693265edafSLaurent Pinchart 11703265edafSLaurent Pinchart /* 11713265edafSLaurent Pinchart * Create root debugfs entry and a file entry for each camera operation. 11723265edafSLaurent Pinchart */ 11733265edafSLaurent Pinchart snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, 117468b66c28SLaurent Pinchart gcam->bundle->id); 11753265edafSLaurent Pinchart 11763265edafSLaurent Pinchart gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); 11773265edafSLaurent Pinchart if (IS_ERR(gcam->debugfs.root)) { 11783265edafSLaurent Pinchart gcam_err(gcam, "debugfs root create failed (%ld)\n", 11793265edafSLaurent Pinchart PTR_ERR(gcam->debugfs.root)); 11803265edafSLaurent Pinchart return PTR_ERR(gcam->debugfs.root); 11813265edafSLaurent Pinchart } 11823265edafSLaurent Pinchart 11833265edafSLaurent Pinchart gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * 11843265edafSLaurent Pinchart GB_CAMERA_DEBUGFS_BUFFER_MAX); 11853265edafSLaurent Pinchart if (!gcam->debugfs.buffers) 11863265edafSLaurent Pinchart return -ENOMEM; 11873265edafSLaurent Pinchart 11883265edafSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { 11893265edafSLaurent Pinchart const struct gb_camera_debugfs_entry *entry = 11903265edafSLaurent Pinchart &gb_camera_debugfs_entries[i]; 11913265edafSLaurent Pinchart struct dentry *dentry; 11923265edafSLaurent Pinchart 11933265edafSLaurent Pinchart gcam->debugfs.buffers[i].length = 0; 11943265edafSLaurent Pinchart 11953265edafSLaurent Pinchart dentry = debugfs_create_file(entry->name, entry->mask, 11963265edafSLaurent Pinchart gcam->debugfs.root, gcam, 11973265edafSLaurent Pinchart &gb_camera_debugfs_ops); 11983265edafSLaurent Pinchart if (IS_ERR(dentry)) { 11993265edafSLaurent Pinchart gcam_err(gcam, 12003265edafSLaurent Pinchart "debugfs operation %s create failed (%ld)\n", 1201f9340fc7SAlex Elder entry->name, PTR_ERR(dentry)); 12023265edafSLaurent Pinchart return PTR_ERR(dentry); 12033265edafSLaurent Pinchart } 12043265edafSLaurent Pinchart } 12053265edafSLaurent Pinchart 12063265edafSLaurent Pinchart return 0; 12073265edafSLaurent Pinchart } 12083265edafSLaurent Pinchart 12093265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) 12103265edafSLaurent Pinchart { 12113265edafSLaurent Pinchart debugfs_remove_recursive(gcam->debugfs.root); 12123265edafSLaurent Pinchart 12133265edafSLaurent Pinchart vfree(gcam->debugfs.buffers); 12143265edafSLaurent Pinchart } 12153265edafSLaurent Pinchart 12163265edafSLaurent Pinchart /* ----------------------------------------------------------------------------- 12173265edafSLaurent Pinchart * Init & Cleanup 12183265edafSLaurent Pinchart */ 12193265edafSLaurent Pinchart 12203265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam) 12213265edafSLaurent Pinchart { 12223265edafSLaurent Pinchart gb_camera_debugfs_cleanup(gcam); 12233265edafSLaurent Pinchart 12249120b906SLaurent Pinchart mutex_lock(&gcam->mutex); 12253ba9fa5cSJohan Hovold if (gcam->data_connection) { 12263ba9fa5cSJohan Hovold gb_connection_disable(gcam->data_connection); 12273ba9fa5cSJohan Hovold gb_connection_destroy(gcam->data_connection); 122817ca6770SEvgeniy Borisov gcam->data_connection = NULL; 12293265edafSLaurent Pinchart } 12303265edafSLaurent Pinchart 123168b66c28SLaurent Pinchart if (gcam->connection) { 123268b66c28SLaurent Pinchart gb_connection_disable(gcam->connection); 123368b66c28SLaurent Pinchart gb_connection_destroy(gcam->connection); 123417ca6770SEvgeniy Borisov gcam->connection = NULL; 123517ca6770SEvgeniy Borisov } 123617ca6770SEvgeniy Borisov mutex_unlock(&gcam->mutex); 123768b66c28SLaurent Pinchart } 123868b66c28SLaurent Pinchart 123917ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref) 124017ca6770SEvgeniy Borisov { 124117ca6770SEvgeniy Borisov struct gb_camera_module *cam_mod = 124217ca6770SEvgeniy Borisov container_of(ref, struct gb_camera_module, refcount); 124317ca6770SEvgeniy Borisov kfree(cam_mod->priv); 12443265edafSLaurent Pinchart } 12453265edafSLaurent Pinchart 124668b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle, 124768b66c28SLaurent Pinchart const struct greybus_bundle_id *id) 12483265edafSLaurent Pinchart { 124968b66c28SLaurent Pinchart struct gb_connection *conn; 12503265edafSLaurent Pinchart struct gb_camera *gcam; 125168b66c28SLaurent Pinchart u16 mgmt_cport_id = 0; 125268b66c28SLaurent Pinchart u16 data_cport_id = 0; 125368b66c28SLaurent Pinchart unsigned int i; 12543265edafSLaurent Pinchart int ret; 12553265edafSLaurent Pinchart 125668b66c28SLaurent Pinchart /* 125768b66c28SLaurent Pinchart * The camera bundle must contain exactly two CPorts, one for the 125868b66c28SLaurent Pinchart * camera management protocol and one for the camera data protocol. 125968b66c28SLaurent Pinchart */ 126068b66c28SLaurent Pinchart if (bundle->num_cports != 2) 126168b66c28SLaurent Pinchart return -ENODEV; 126268b66c28SLaurent Pinchart 126368b66c28SLaurent Pinchart for (i = 0; i < bundle->num_cports; ++i) { 126468b66c28SLaurent Pinchart struct greybus_descriptor_cport *desc = &bundle->cport_desc[i]; 126568b66c28SLaurent Pinchart 126668b66c28SLaurent Pinchart switch (desc->protocol_id) { 126768b66c28SLaurent Pinchart case GREYBUS_PROTOCOL_CAMERA_MGMT: 126868b66c28SLaurent Pinchart mgmt_cport_id = le16_to_cpu(desc->id); 126968b66c28SLaurent Pinchart break; 127068b66c28SLaurent Pinchart case GREYBUS_PROTOCOL_CAMERA_DATA: 127168b66c28SLaurent Pinchart data_cport_id = le16_to_cpu(desc->id); 127268b66c28SLaurent Pinchart break; 127368b66c28SLaurent Pinchart default: 127468b66c28SLaurent Pinchart return -ENODEV; 127568b66c28SLaurent Pinchart } 127668b66c28SLaurent Pinchart } 127768b66c28SLaurent Pinchart 127868b66c28SLaurent Pinchart if (!mgmt_cport_id || !data_cport_id) 127968b66c28SLaurent Pinchart return -ENODEV; 128068b66c28SLaurent Pinchart 12813265edafSLaurent Pinchart gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); 12823265edafSLaurent Pinchart if (!gcam) 12833265edafSLaurent Pinchart return -ENOMEM; 12843265edafSLaurent Pinchart 1285d9e4c4eeSViresh Kumar mutex_init(&gcam->mutex); 12863265edafSLaurent Pinchart 12873b8ebfebSLaurent Pinchart gcam->bundle = bundle; 12883b8ebfebSLaurent Pinchart gcam->state = GB_CAMERA_STATE_UNCONFIGURED; 12899120b906SLaurent Pinchart gcam->data_cport_id = data_cport_id; 12903b8ebfebSLaurent Pinchart 129168b66c28SLaurent Pinchart conn = gb_connection_create(bundle, mgmt_cport_id, 129268b66c28SLaurent Pinchart gb_camera_request_handler); 129368b66c28SLaurent Pinchart if (IS_ERR(conn)) { 129468b66c28SLaurent Pinchart ret = PTR_ERR(conn); 12953ba9fa5cSJohan Hovold goto error; 12963ba9fa5cSJohan Hovold } 12973ba9fa5cSJohan Hovold 129868b66c28SLaurent Pinchart gcam->connection = conn; 129968b66c28SLaurent Pinchart gb_connection_set_data(conn, gcam); 130068b66c28SLaurent Pinchart 130168b66c28SLaurent Pinchart ret = gb_connection_enable(conn); 130268b66c28SLaurent Pinchart if (ret) 130368b66c28SLaurent Pinchart goto error; 130468b66c28SLaurent Pinchart 13053265edafSLaurent Pinchart ret = gb_camera_debugfs_init(gcam); 13063265edafSLaurent Pinchart if (ret < 0) 13073265edafSLaurent Pinchart goto error; 13083265edafSLaurent Pinchart 130917ca6770SEvgeniy Borisov gcam->module.priv = gcam; 131017ca6770SEvgeniy Borisov gcam->module.ops = &gb_cam_ops; 131117ca6770SEvgeniy Borisov gcam->module.interface_id = gcam->connection->intf->interface_id; 131217ca6770SEvgeniy Borisov gcam->module.release = gb_camera_release_module; 131317ca6770SEvgeniy Borisov ret = gb_camera_register(&gcam->module); 13143a8dba4eSGjorgji Rosikopulos if (ret < 0) 13153a8dba4eSGjorgji Rosikopulos goto error; 13163a8dba4eSGjorgji Rosikopulos 131768b66c28SLaurent Pinchart greybus_set_drvdata(bundle, gcam); 131868b66c28SLaurent Pinchart 1319211634f2SDavid Lin gb_pm_runtime_put_autosuspend(gcam->bundle); 1320211634f2SDavid Lin 13213265edafSLaurent Pinchart return 0; 13223265edafSLaurent Pinchart 13233265edafSLaurent Pinchart error: 13243265edafSLaurent Pinchart gb_camera_cleanup(gcam); 132517ca6770SEvgeniy Borisov kfree(gcam); 13263265edafSLaurent Pinchart return ret; 13273265edafSLaurent Pinchart } 13283265edafSLaurent Pinchart 132968b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle) 13303265edafSLaurent Pinchart { 133168b66c28SLaurent Pinchart struct gb_camera *gcam = greybus_get_drvdata(bundle); 1332211634f2SDavid Lin int ret; 1333211634f2SDavid Lin 1334211634f2SDavid Lin ret = gb_pm_runtime_get_sync(bundle); 1335211634f2SDavid Lin if (ret) 1336211634f2SDavid Lin gb_pm_runtime_get_noresume(bundle); 13373265edafSLaurent Pinchart 13383265edafSLaurent Pinchart gb_camera_cleanup(gcam); 133917ca6770SEvgeniy Borisov gb_camera_unregister(&gcam->module); 13403265edafSLaurent Pinchart } 13413265edafSLaurent Pinchart 134268b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = { 134368b66c28SLaurent Pinchart { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, 134468b66c28SLaurent Pinchart { }, 13453265edafSLaurent Pinchart }; 13463265edafSLaurent Pinchart 1347948c6227SGreg Kroah-Hartman #ifdef CONFIG_PM 1348211634f2SDavid Lin static int gb_camera_suspend(struct device *dev) 1349211634f2SDavid Lin { 1350211634f2SDavid Lin struct gb_bundle *bundle = to_gb_bundle(dev); 1351211634f2SDavid Lin struct gb_camera *gcam = greybus_get_drvdata(bundle); 1352211634f2SDavid Lin 1353211634f2SDavid Lin if (gcam->data_connection) 1354211634f2SDavid Lin gb_connection_disable(gcam->data_connection); 1355211634f2SDavid Lin 1356211634f2SDavid Lin gb_connection_disable(gcam->connection); 1357211634f2SDavid Lin 1358211634f2SDavid Lin return 0; 1359211634f2SDavid Lin } 1360211634f2SDavid Lin 1361211634f2SDavid Lin static int gb_camera_resume(struct device *dev) 1362211634f2SDavid Lin { 1363211634f2SDavid Lin struct gb_bundle *bundle = to_gb_bundle(dev); 1364211634f2SDavid Lin struct gb_camera *gcam = greybus_get_drvdata(bundle); 1365211634f2SDavid Lin int ret; 1366211634f2SDavid Lin 1367211634f2SDavid Lin ret = gb_connection_enable(gcam->connection); 1368211634f2SDavid Lin if (ret) { 1369211634f2SDavid Lin gcam_err(gcam, "failed to enable connection: %d\n", ret); 1370211634f2SDavid Lin return ret; 1371211634f2SDavid Lin } 1372211634f2SDavid Lin 1373211634f2SDavid Lin if (gcam->data_connection) { 1374211634f2SDavid Lin ret = gb_connection_enable(gcam->data_connection); 1375211634f2SDavid Lin if (ret) { 1376211634f2SDavid Lin gcam_err(gcam, 1377211634f2SDavid Lin "failed to enable data connection: %d\n", ret); 1378211634f2SDavid Lin return ret; 1379211634f2SDavid Lin } 1380211634f2SDavid Lin } 1381211634f2SDavid Lin 1382211634f2SDavid Lin return 0; 1383211634f2SDavid Lin } 1384211634f2SDavid Lin #endif 1385211634f2SDavid Lin 1386211634f2SDavid Lin static const struct dev_pm_ops gb_camera_pm_ops = { 1387211634f2SDavid Lin SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL) 1388211634f2SDavid Lin }; 1389211634f2SDavid Lin 139068b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = { 139168b66c28SLaurent Pinchart .name = "camera", 139268b66c28SLaurent Pinchart .probe = gb_camera_probe, 139368b66c28SLaurent Pinchart .disconnect = gb_camera_disconnect, 139468b66c28SLaurent Pinchart .id_table = gb_camera_id_table, 1395211634f2SDavid Lin .driver.pm = &gb_camera_pm_ops, 139668b66c28SLaurent Pinchart }; 139768b66c28SLaurent Pinchart 139868b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver); 13993265edafSLaurent Pinchart 14003265edafSLaurent Pinchart MODULE_LICENSE("GPL v2"); 1401