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