xref: /openbmc/linux/drivers/staging/greybus/camera.c (revision 3a8dba4e)
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 	{
733a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YUYV8_1_5X8,
743a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x16,
753a8dba4eSGjorgji Rosikopulos 	},
763a8dba4eSGjorgji Rosikopulos 	{
773a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YVYU8_1_5X8,
783a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x17,
793a8dba4eSGjorgji Rosikopulos 	},
803a8dba4eSGjorgji Rosikopulos 	{
813a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
823a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x40,
833a8dba4eSGjorgji Rosikopulos 	}
843a8dba4eSGjorgji Rosikopulos };
853a8dba4eSGjorgji Rosikopulos 
863265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT		16
873265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT		17
883265edafSLaurent Pinchart 
893265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
903265edafSLaurent Pinchart 
913265edafSLaurent Pinchart #define gcam_dbg(gcam, format...) \
923265edafSLaurent Pinchart 	dev_dbg(&gcam->connection->bundle->dev, format)
933265edafSLaurent Pinchart #define gcam_info(gcam, format...) \
943265edafSLaurent Pinchart 	dev_info(&gcam->connection->bundle->dev, format)
953265edafSLaurent Pinchart #define gcam_err(gcam, format...) \
963265edafSLaurent Pinchart 	dev_err(&gcam->connection->bundle->dev, format)
973265edafSLaurent Pinchart 
983265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
993265edafSLaurent Pinchart  * Camera Protocol Operations
1003265edafSLaurent Pinchart  */
1013265edafSLaurent Pinchart 
102ed4596e9SGreg Kroah-Hartman struct ap_csi_config_request {
103ed4596e9SGreg Kroah-Hartman 	__u8 csi_id;
104ed4596e9SGreg Kroah-Hartman 	__u8 clock_mode;
105ed4596e9SGreg Kroah-Hartman 	__u8 num_lanes;
106ed4596e9SGreg Kroah-Hartman 	__u8 padding;
107ed4596e9SGreg Kroah-Hartman 	__le32 bus_freq;
108ed4596e9SGreg Kroah-Hartman } __packed;
109ed4596e9SGreg Kroah-Hartman 
1103265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam,
1113265edafSLaurent Pinchart 				       unsigned int nstreams,
112b787d413SJacopo Mondi 				       unsigned int flags,
1133265edafSLaurent Pinchart 				       struct gb_camera_stream_config *streams)
1143265edafSLaurent Pinchart {
1153265edafSLaurent Pinchart 	struct gb_camera_configure_streams_request *req;
1163265edafSLaurent Pinchart 	struct gb_camera_configure_streams_response *resp;
117ed4596e9SGreg Kroah-Hartman 	struct ap_csi_config_request csi_cfg;
1183265edafSLaurent Pinchart 	unsigned int i;
1193265edafSLaurent Pinchart 	size_t req_size;
1203265edafSLaurent Pinchart 	size_t resp_size;
1213265edafSLaurent Pinchart 	int ret;
1223265edafSLaurent Pinchart 
1233265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
1243265edafSLaurent Pinchart 		return -EINVAL;
1253265edafSLaurent Pinchart 
1263265edafSLaurent Pinchart 	req_size = sizeof(*req) + nstreams * sizeof(req->config[0]);
1273265edafSLaurent Pinchart 	resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]);
1283265edafSLaurent Pinchart 
1293265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
1303265edafSLaurent Pinchart 	resp = kmalloc(resp_size, GFP_KERNEL);
1313265edafSLaurent Pinchart 	if (!req || !resp) {
1323265edafSLaurent Pinchart 		ret = -ENOMEM;
1333265edafSLaurent Pinchart 		goto done;
1343265edafSLaurent Pinchart 	}
1353265edafSLaurent Pinchart 
136b787d413SJacopo Mondi 	req->num_streams = nstreams;
137b787d413SJacopo Mondi 	req->flags = flags;
1383265edafSLaurent Pinchart 	req->padding = 0;
1393265edafSLaurent Pinchart 
1403265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
1413265edafSLaurent Pinchart 		struct gb_camera_stream_config_request *cfg = &req->config[i];
1423265edafSLaurent Pinchart 
143c6622216SLaurent Pinchart 		cfg->width = cpu_to_le16(streams[i].width);
144c6622216SLaurent Pinchart 		cfg->height = cpu_to_le16(streams[i].height);
145c6622216SLaurent Pinchart 		cfg->format = cpu_to_le16(streams[i].format);
1463265edafSLaurent Pinchart 		cfg->padding = 0;
1473265edafSLaurent Pinchart 	}
1483265edafSLaurent Pinchart 
1493265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection,
1503265edafSLaurent Pinchart 				GB_CAMERA_TYPE_CONFIGURE_STREAMS,
1513265edafSLaurent Pinchart 				req, req_size, resp, resp_size);
1523265edafSLaurent Pinchart 	if (ret < 0)
15312c8b0dcSJohan Hovold 		goto done;
1543265edafSLaurent Pinchart 
155b787d413SJacopo Mondi 	if (resp->num_streams > nstreams) {
1563265edafSLaurent Pinchart 		gcam_dbg(gcam, "got #streams %u > request %u\n",
157b787d413SJacopo Mondi 			 resp->num_streams, nstreams);
1583265edafSLaurent Pinchart 		ret = -EIO;
1593265edafSLaurent Pinchart 		goto done;
1603265edafSLaurent Pinchart 	}
1613265edafSLaurent Pinchart 
1623265edafSLaurent Pinchart 	if (resp->padding != 0) {
1633265edafSLaurent Pinchart 		gcam_dbg(gcam, "response padding != 0");
1643265edafSLaurent Pinchart 		ret = -EIO;
1653265edafSLaurent Pinchart 		goto done;
1663265edafSLaurent Pinchart 	}
1673265edafSLaurent Pinchart 
1683265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
1693265edafSLaurent Pinchart 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
1703265edafSLaurent Pinchart 
171c6622216SLaurent Pinchart 		streams[i].width = le16_to_cpu(cfg->width);
172c6622216SLaurent Pinchart 		streams[i].height = le16_to_cpu(cfg->height);
173c6622216SLaurent Pinchart 		streams[i].format = le16_to_cpu(cfg->format);
1743265edafSLaurent Pinchart 		streams[i].vc = cfg->virtual_channel;
1753265edafSLaurent Pinchart 		streams[i].dt[0] = cfg->data_type[0];
1763265edafSLaurent Pinchart 		streams[i].dt[1] = cfg->data_type[1];
177c6622216SLaurent Pinchart 		streams[i].max_size = le32_to_cpu(cfg->max_size);
1783265edafSLaurent Pinchart 
1793265edafSLaurent Pinchart 		if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) {
1803265edafSLaurent Pinchart 			gcam_dbg(gcam, "stream #%u padding != 0", i);
1813265edafSLaurent Pinchart 			ret = -EIO;
1823265edafSLaurent Pinchart 			goto done;
1833265edafSLaurent Pinchart 		}
1843265edafSLaurent Pinchart 	}
1853265edafSLaurent Pinchart 
186ed4596e9SGreg Kroah-Hartman 	memset(&csi_cfg, 0, sizeof(csi_cfg));
187ed4596e9SGreg Kroah-Hartman 
188142b21feSLaurent Pinchart 	/* Configure the CSI transmitter. Hardcode the parameters for now. */
189142b21feSLaurent Pinchart 	if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) {
190142b21feSLaurent Pinchart 		csi_cfg.csi_id = 1;
191142b21feSLaurent Pinchart 		csi_cfg.clock_mode = 0;
1921f67ee5cSLaurent Pinchart 		csi_cfg.num_lanes = 4;
193ed4596e9SGreg Kroah-Hartman 		csi_cfg.bus_freq = cpu_to_le32(960000000);
194ed4596e9SGreg Kroah-Hartman 		ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
195e5273381SGreg Kroah-Hartman 				   sizeof(csi_cfg),
196e5273381SGreg Kroah-Hartman 				   GB_APB_REQUEST_CSI_TX_CONTROL, false);
197142b21feSLaurent Pinchart 	} else if (nstreams == 0) {
198142b21feSLaurent Pinchart 		csi_cfg.csi_id = 1;
199ed4596e9SGreg Kroah-Hartman 		ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
200e5273381SGreg Kroah-Hartman 				   sizeof(csi_cfg),
201e5273381SGreg Kroah-Hartman 				   GB_APB_REQUEST_CSI_TX_CONTROL, false);
202142b21feSLaurent Pinchart 	}
203142b21feSLaurent Pinchart 
204142b21feSLaurent Pinchart 	if (ret < 0)
205142b21feSLaurent Pinchart 		gcam_err(gcam, "failed to %s the CSI transmitter\n",
206142b21feSLaurent Pinchart 			 nstreams ? "start" : "stop");
207142b21feSLaurent Pinchart 
208b787d413SJacopo Mondi 	ret = resp->num_streams;
2093265edafSLaurent Pinchart 
2103265edafSLaurent Pinchart done:
2113265edafSLaurent Pinchart 	kfree(req);
2123265edafSLaurent Pinchart 	kfree(resp);
2133265edafSLaurent Pinchart 	return ret;
2143265edafSLaurent Pinchart }
2153265edafSLaurent Pinchart 
2163265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id,
2173265edafSLaurent Pinchart 			     unsigned int streams, unsigned int num_frames,
2183265edafSLaurent Pinchart 			     size_t settings_size, const void *settings)
2193265edafSLaurent Pinchart {
2203265edafSLaurent Pinchart 	struct gb_camera_capture_request *req;
2213265edafSLaurent Pinchart 	size_t req_size;
222b9f71bc8SJohan Hovold 	int ret;
2233265edafSLaurent Pinchart 
2243265edafSLaurent Pinchart 	if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE)
2253265edafSLaurent Pinchart 		return -EINVAL;
2263265edafSLaurent Pinchart 
2273265edafSLaurent Pinchart 	req_size = sizeof(*req) + settings_size;
2283265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
2293265edafSLaurent Pinchart 	if (!req)
2303265edafSLaurent Pinchart 		return -ENOMEM;
2313265edafSLaurent Pinchart 
232c6622216SLaurent Pinchart 	req->request_id = cpu_to_le32(request_id);
2333265edafSLaurent Pinchart 	req->streams = streams;
2343265edafSLaurent Pinchart 	req->padding = 0;
235c6622216SLaurent Pinchart 	req->num_frames = cpu_to_le16(num_frames);
2363265edafSLaurent Pinchart 	memcpy(req->settings, settings, settings_size);
2373265edafSLaurent Pinchart 
238b9f71bc8SJohan Hovold 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE,
2393265edafSLaurent Pinchart 				 req, req_size, NULL, 0);
240b9f71bc8SJohan Hovold 
241b9f71bc8SJohan Hovold 	kfree(req);
242b9f71bc8SJohan Hovold 
243b9f71bc8SJohan Hovold 	return ret;
2443265edafSLaurent Pinchart }
2453265edafSLaurent Pinchart 
2463265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id)
2473265edafSLaurent Pinchart {
2483265edafSLaurent Pinchart 	struct gb_camera_flush_response resp;
2493265edafSLaurent Pinchart 	int ret;
2503265edafSLaurent Pinchart 
2513265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0,
2523265edafSLaurent Pinchart 				&resp, sizeof(resp));
2533265edafSLaurent Pinchart 	if (ret < 0)
2543265edafSLaurent Pinchart 		return ret;
2553265edafSLaurent Pinchart 
2563265edafSLaurent Pinchart 	if (request_id)
257c6622216SLaurent Pinchart 		*request_id = le32_to_cpu(resp.request_id);
2583265edafSLaurent Pinchart 
2593265edafSLaurent Pinchart 	return 0;
2603265edafSLaurent Pinchart }
2613265edafSLaurent Pinchart 
2623265edafSLaurent Pinchart static int gb_camera_event_recv(u8 type, struct gb_operation *op)
2633265edafSLaurent Pinchart {
2643265edafSLaurent Pinchart 	struct gb_camera *gcam = op->connection->private;
2653265edafSLaurent Pinchart 	struct gb_camera_metadata_request *payload;
2663265edafSLaurent Pinchart 	struct gb_message *request;
2673265edafSLaurent Pinchart 
2683265edafSLaurent Pinchart 	if (type != GB_CAMERA_TYPE_METADATA) {
2693265edafSLaurent Pinchart 		gcam_err(gcam, "Unsupported unsolicited event: %u\n", type);
2703265edafSLaurent Pinchart 		return -EINVAL;
2713265edafSLaurent Pinchart 	}
2723265edafSLaurent Pinchart 
2733265edafSLaurent Pinchart 	request = op->request;
2743265edafSLaurent Pinchart 
2753265edafSLaurent Pinchart 	if (request->payload_size < sizeof(*payload)) {
2763265edafSLaurent Pinchart 		gcam_err(gcam, "Wrong event size received (%zu < %zu)\n",
2773265edafSLaurent Pinchart 			 request->payload_size, sizeof(*payload));
2783265edafSLaurent Pinchart 		return -EINVAL;
2793265edafSLaurent Pinchart 	}
2803265edafSLaurent Pinchart 
2813265edafSLaurent Pinchart 	payload = request->payload;
2823265edafSLaurent Pinchart 
2833265edafSLaurent Pinchart 	gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n",
2843265edafSLaurent Pinchart 		 payload->request_id, payload->frame_number, payload->stream);
2853265edafSLaurent Pinchart 
2863265edafSLaurent Pinchart 	return 0;
2873265edafSLaurent Pinchart }
2883265edafSLaurent Pinchart 
2893265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
2903a8dba4eSGjorgji Rosikopulos  * Interface with HOST ara camera.
2913a8dba4eSGjorgji Rosikopulos  */
2923a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code)
2933a8dba4eSGjorgji Rosikopulos {
2943a8dba4eSGjorgji Rosikopulos 	unsigned int i;
2953a8dba4eSGjorgji Rosikopulos 
2963a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) {
2973a8dba4eSGjorgji Rosikopulos 		if (mbus_to_gbus_format[i].mbus_code == mbus_code)
2983a8dba4eSGjorgji Rosikopulos 			return mbus_to_gbus_format[i].gb_format;
2993a8dba4eSGjorgji Rosikopulos 	}
3003a8dba4eSGjorgji Rosikopulos 	return mbus_to_gbus_format[0].gb_format;
3013a8dba4eSGjorgji Rosikopulos }
3023a8dba4eSGjorgji Rosikopulos 
3033a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt)
3043a8dba4eSGjorgji Rosikopulos {
3053a8dba4eSGjorgji Rosikopulos 	unsigned int i;
3063a8dba4eSGjorgji Rosikopulos 
3073a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) {
3083a8dba4eSGjorgji Rosikopulos 		if (mbus_to_gbus_format[i].gb_format == gb_fmt)
3093a8dba4eSGjorgji Rosikopulos 			return mbus_to_gbus_format[i].mbus_code;
3103a8dba4eSGjorgji Rosikopulos 	}
3113a8dba4eSGjorgji Rosikopulos 	return mbus_to_gbus_format[0].mbus_code;
3123a8dba4eSGjorgji Rosikopulos }
3133a8dba4eSGjorgji Rosikopulos 
3143a8dba4eSGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams,
3153a8dba4eSGjorgji Rosikopulos 			struct gb_camera_stream *streams)
3163a8dba4eSGjorgji Rosikopulos {
3173a8dba4eSGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
3183a8dba4eSGjorgji Rosikopulos 	struct gb_camera_stream_config *gb_streams;
3193a8dba4eSGjorgji Rosikopulos 	unsigned int i;
3203a8dba4eSGjorgji Rosikopulos 	int ret;
3213a8dba4eSGjorgji Rosikopulos 
3223a8dba4eSGjorgji Rosikopulos 	if (nstreams > GB_CAMERA_MAX_STREAMS)
3233a8dba4eSGjorgji Rosikopulos 		return -EINVAL;
3243a8dba4eSGjorgji Rosikopulos 
3253a8dba4eSGjorgji Rosikopulos 	gb_streams = kzalloc(nstreams * sizeof(*gb_streams), GFP_KERNEL);
3263a8dba4eSGjorgji Rosikopulos 	if (!gb_streams)
3273a8dba4eSGjorgji Rosikopulos 		return -ENOMEM;
3283a8dba4eSGjorgji Rosikopulos 
3293a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < nstreams; i++) {
3303a8dba4eSGjorgji Rosikopulos 		gb_streams[i].width = streams[i].width;
3313a8dba4eSGjorgji Rosikopulos 		gb_streams[i].height = streams[i].height;
3323a8dba4eSGjorgji Rosikopulos 		gb_streams[i].format =
3333a8dba4eSGjorgji Rosikopulos 			gb_camera_mbus_to_gb(streams[i].pixel_code);
3343a8dba4eSGjorgji Rosikopulos 	}
3353a8dba4eSGjorgji Rosikopulos 
3363a8dba4eSGjorgji Rosikopulos 	ret = gb_camera_configure_streams(gcam, nstreams, 0, gb_streams);
3373a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
3383a8dba4eSGjorgji Rosikopulos 		goto done;
3393a8dba4eSGjorgji Rosikopulos 
3403a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < nstreams; i++) {
3413a8dba4eSGjorgji Rosikopulos 		streams[i].width = gb_streams[i].width;
3423a8dba4eSGjorgji Rosikopulos 		streams[i].height = gb_streams[i].height;
3433a8dba4eSGjorgji Rosikopulos 		streams[i].vc = gb_streams[i].vc;
3443a8dba4eSGjorgji Rosikopulos 		streams[i].dt[0] = gb_streams[i].dt[0];
3453a8dba4eSGjorgji Rosikopulos 		streams[i].dt[1] = gb_streams[i].dt[1];
3463a8dba4eSGjorgji Rosikopulos 		streams[i].max_size = gb_streams[i].max_size;
3473a8dba4eSGjorgji Rosikopulos 		streams[i].pixel_code =
3483a8dba4eSGjorgji Rosikopulos 			gb_camera_gb_to_mbus(gb_streams[i].format);
3493a8dba4eSGjorgji Rosikopulos 	}
3503a8dba4eSGjorgji Rosikopulos 
3513a8dba4eSGjorgji Rosikopulos done:
3523a8dba4eSGjorgji Rosikopulos 	kfree(gb_streams);
3533a8dba4eSGjorgji Rosikopulos 	return ret;
3543a8dba4eSGjorgji Rosikopulos }
3553a8dba4eSGjorgji Rosikopulos 
3563a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id,
3573a8dba4eSGjorgji Rosikopulos 		unsigned int streams, unsigned int num_frames,
3583a8dba4eSGjorgji Rosikopulos 		size_t settings_size, const void *settings)
3593a8dba4eSGjorgji Rosikopulos {
3603a8dba4eSGjorgji Rosikopulos 	return gb_camera_capture(priv, request_id, streams, num_frames,
3613a8dba4eSGjorgji Rosikopulos 				 settings_size, settings);
3623a8dba4eSGjorgji Rosikopulos }
3633a8dba4eSGjorgji Rosikopulos 
3643a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id)
3653a8dba4eSGjorgji Rosikopulos {
3663a8dba4eSGjorgji Rosikopulos 	return gb_camera_flush(priv, request_id);
3673a8dba4eSGjorgji Rosikopulos }
3683a8dba4eSGjorgji Rosikopulos 
3693a8dba4eSGjorgji Rosikopulos struct gb_camera_ops gb_cam_ops = {
3703a8dba4eSGjorgji Rosikopulos 	.configure_streams = gb_camera_op_configure_streams,
3713a8dba4eSGjorgji Rosikopulos 	.capture = gb_camera_op_capture,
3723a8dba4eSGjorgji Rosikopulos 	.flush = gb_camera_op_flush,
3733a8dba4eSGjorgji Rosikopulos };
3743a8dba4eSGjorgji Rosikopulos 
3753a8dba4eSGjorgji Rosikopulos static int gb_camera_register_intf_ops(struct gb_camera *gcam)
3763a8dba4eSGjorgji Rosikopulos {
3773a8dba4eSGjorgji Rosikopulos 	return gb_camera_register(&gb_cam_ops, gcam);
3783a8dba4eSGjorgji Rosikopulos }
3793a8dba4eSGjorgji Rosikopulos 
3803a8dba4eSGjorgji Rosikopulos /* -----------------------------------------------------------------------------
3813265edafSLaurent Pinchart  * DebugFS
3823265edafSLaurent Pinchart  */
3833265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam,
3843265edafSLaurent Pinchart 		char *buf, size_t len)
3853265edafSLaurent Pinchart {
3863265edafSLaurent Pinchart 	return len;
3873265edafSLaurent Pinchart }
3883265edafSLaurent Pinchart 
3893265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam,
3903265edafSLaurent Pinchart 		char *buf, size_t len)
3913265edafSLaurent Pinchart {
3923265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
3933265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS];
3943265edafSLaurent Pinchart 	struct gb_camera_stream_config *streams;
3953265edafSLaurent Pinchart 	unsigned int nstreams;
396b787d413SJacopo Mondi 	unsigned int flags;
3973265edafSLaurent Pinchart 	unsigned int i;
3983265edafSLaurent Pinchart 	char *token;
3993265edafSLaurent Pinchart 	int ret;
4003265edafSLaurent Pinchart 
4013265edafSLaurent Pinchart 	/* Retrieve number of streams to configure */
402b787d413SJacopo Mondi 	token = strsep(&buf, ";");
4033265edafSLaurent Pinchart 	if (token == NULL)
4043265edafSLaurent Pinchart 		return -EINVAL;
4053265edafSLaurent Pinchart 
4063265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &nstreams);
4073265edafSLaurent Pinchart 	if (ret < 0)
4083265edafSLaurent Pinchart 		return ret;
4093265edafSLaurent Pinchart 
4103265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
4113265edafSLaurent Pinchart 		return -EINVAL;
4123265edafSLaurent Pinchart 
413b787d413SJacopo Mondi 	token = strsep(&buf, ";");
414b787d413SJacopo Mondi 	if (token == NULL)
415b787d413SJacopo Mondi 		return -EINVAL;
416b787d413SJacopo Mondi 
417b787d413SJacopo Mondi 	ret = kstrtouint(token, 10, &flags);
418b787d413SJacopo Mondi 	if (ret < 0)
419b787d413SJacopo Mondi 		return ret;
420b787d413SJacopo Mondi 
4213265edafSLaurent Pinchart 	/* For each stream to configure parse width, height and format */
4223265edafSLaurent Pinchart 	streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL);
4233265edafSLaurent Pinchart 	if (!streams)
4243265edafSLaurent Pinchart 		return -ENOMEM;
4253265edafSLaurent Pinchart 
4263265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
4273265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
4283265edafSLaurent Pinchart 
4293265edafSLaurent Pinchart 		/* width */
4303265edafSLaurent Pinchart 		token = strsep(&buf, ";");
4313265edafSLaurent Pinchart 		if (token == NULL) {
4323265edafSLaurent Pinchart 			ret = -EINVAL;
4333265edafSLaurent Pinchart 			goto done;
4343265edafSLaurent Pinchart 		}
4353265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->width);
4363265edafSLaurent Pinchart 		if (ret < 0)
4373265edafSLaurent Pinchart 			goto done;
4383265edafSLaurent Pinchart 
4393265edafSLaurent Pinchart 		/* height */
4403265edafSLaurent Pinchart 		token = strsep(&buf, ";");
4413265edafSLaurent Pinchart 		if (token == NULL)
4423265edafSLaurent Pinchart 			goto done;
4433265edafSLaurent Pinchart 
4443265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->height);
4453265edafSLaurent Pinchart 		if (ret < 0)
4463265edafSLaurent Pinchart 			goto done;
4473265edafSLaurent Pinchart 
4483265edafSLaurent Pinchart 		/* Image format code */
4493265edafSLaurent Pinchart 		token = strsep(&buf, ";");
4503265edafSLaurent Pinchart 		if (token == NULL)
4513265edafSLaurent Pinchart 			goto done;
4523265edafSLaurent Pinchart 
4533265edafSLaurent Pinchart 		ret = kstrtouint(token, 16, &stream->format);
4543265edafSLaurent Pinchart 		if (ret < 0)
4553265edafSLaurent Pinchart 			goto done;
4563265edafSLaurent Pinchart 	}
4573265edafSLaurent Pinchart 
458b787d413SJacopo Mondi 	ret = gb_camera_configure_streams(gcam, nstreams, flags, streams);
4593265edafSLaurent Pinchart 	if (ret < 0)
4603265edafSLaurent Pinchart 		goto done;
4613265edafSLaurent Pinchart 
4623265edafSLaurent Pinchart 	nstreams = ret;
4633265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u;", nstreams);
4643265edafSLaurent Pinchart 
4653265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
4663265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
4673265edafSLaurent Pinchart 
4683265edafSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
4693265edafSLaurent Pinchart 					  "%u;%u;%u;%u;%u;%u;%u;",
4703265edafSLaurent Pinchart 					  stream->width, stream->height,
4713265edafSLaurent Pinchart 					  stream->format, stream->vc,
4723265edafSLaurent Pinchart 					  stream->dt[0], stream->dt[1],
4733265edafSLaurent Pinchart 					  stream->max_size);
4743265edafSLaurent Pinchart 	}
4753265edafSLaurent Pinchart 
4763265edafSLaurent Pinchart 	ret = len;
4773265edafSLaurent Pinchart 
4783265edafSLaurent Pinchart done:
4793265edafSLaurent Pinchart 	kfree(streams);
4803265edafSLaurent Pinchart 	return ret;
4813265edafSLaurent Pinchart };
4823265edafSLaurent Pinchart 
4833265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam,
4843265edafSLaurent Pinchart 		char *buf, size_t len)
4853265edafSLaurent Pinchart {
4863265edafSLaurent Pinchart 	unsigned int request_id;
4873265edafSLaurent Pinchart 	unsigned int streams_mask;
4883265edafSLaurent Pinchart 	unsigned int num_frames;
4893265edafSLaurent Pinchart 	char *token;
4903265edafSLaurent Pinchart 	int ret;
4913265edafSLaurent Pinchart 
4923265edafSLaurent Pinchart 	/* Request id */
4933265edafSLaurent Pinchart 	token = strsep(&buf, ";");
4943265edafSLaurent Pinchart 	if (token == NULL)
4953265edafSLaurent Pinchart 		return -EINVAL;
4963265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &request_id);
4973265edafSLaurent Pinchart 	if (ret < 0)
4983265edafSLaurent Pinchart 		return ret;
4993265edafSLaurent Pinchart 
5003265edafSLaurent Pinchart 	/* Stream mask */
5013265edafSLaurent Pinchart 	token = strsep(&buf, ";");
5023265edafSLaurent Pinchart 	if (token == NULL)
5033265edafSLaurent Pinchart 		return -EINVAL;
5043265edafSLaurent Pinchart 	ret = kstrtouint(token, 16, &streams_mask);
5053265edafSLaurent Pinchart 	if (ret < 0)
5063265edafSLaurent Pinchart 		return ret;
5073265edafSLaurent Pinchart 
5083265edafSLaurent Pinchart 	/* number of frames */
5093265edafSLaurent Pinchart 	token = strsep(&buf, ";");
5103265edafSLaurent Pinchart 	if (token == NULL)
5113265edafSLaurent Pinchart 		return -EINVAL;
5123265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &num_frames);
5133265edafSLaurent Pinchart 	if (ret < 0)
5143265edafSLaurent Pinchart 		return ret;
5153265edafSLaurent Pinchart 
5163265edafSLaurent Pinchart 	ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0,
5173265edafSLaurent Pinchart 				NULL);
5183265edafSLaurent Pinchart 	if (ret < 0)
5193265edafSLaurent Pinchart 		return ret;
5203265edafSLaurent Pinchart 
5213265edafSLaurent Pinchart 	return len;
5223265edafSLaurent Pinchart }
5233265edafSLaurent Pinchart 
5243265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam,
5253265edafSLaurent Pinchart 		char *buf, size_t len)
5263265edafSLaurent Pinchart {
5273265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
5283265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH];
5293265edafSLaurent Pinchart 	unsigned int req_id;
5303265edafSLaurent Pinchart 	int ret;
5313265edafSLaurent Pinchart 
5323265edafSLaurent Pinchart 	ret = gb_camera_flush(gcam, &req_id);
5333265edafSLaurent Pinchart 	if (ret < 0)
5343265edafSLaurent Pinchart 		return ret;
5353265edafSLaurent Pinchart 
5363265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u", req_id);
5373265edafSLaurent Pinchart 
5383265edafSLaurent Pinchart 	return len;
5393265edafSLaurent Pinchart }
5403265edafSLaurent Pinchart 
5413265edafSLaurent Pinchart struct gb_camera_debugfs_entry {
5423265edafSLaurent Pinchart 	const char *name;
5433265edafSLaurent Pinchart 	unsigned int mask;
5443265edafSLaurent Pinchart 	unsigned int buffer;
5453265edafSLaurent Pinchart 	ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len);
5463265edafSLaurent Pinchart };
5473265edafSLaurent Pinchart 
5483265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = {
5493265edafSLaurent Pinchart 	{
5503265edafSLaurent Pinchart 		.name = "capabilities",
5513265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO,
5523265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
5533265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capabilities,
5543265edafSLaurent Pinchart 	}, {
5553265edafSLaurent Pinchart 		.name = "configure_streams",
5563265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
5573265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
5583265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_configure_streams,
5593265edafSLaurent Pinchart 	}, {
5603265edafSLaurent Pinchart 		.name = "capture",
5613265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
5623265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
5633265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capture,
5643265edafSLaurent Pinchart 	}, {
5653265edafSLaurent Pinchart 		.name = "flush",
5663265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
5673265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
5683265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_flush,
5693265edafSLaurent Pinchart 	},
5703265edafSLaurent Pinchart };
5713265edafSLaurent Pinchart 
5723265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf,
5733265edafSLaurent Pinchart 				      size_t len, loff_t *offset)
5743265edafSLaurent Pinchart {
5753265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
5763265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
5773265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer;
5783265edafSLaurent Pinchart 	ssize_t ret;
5793265edafSLaurent Pinchart 
5803265edafSLaurent Pinchart 	/* For read-only entries the operation is triggered by a read. */
5813265edafSLaurent Pinchart 	if (!(op->mask & S_IWUGO)) {
5823265edafSLaurent Pinchart 		ret = op->execute(gcam, NULL, 0);
5833265edafSLaurent Pinchart 		if (ret < 0)
5843265edafSLaurent Pinchart 			return ret;
5853265edafSLaurent Pinchart 	}
5863265edafSLaurent Pinchart 
5873265edafSLaurent Pinchart 	buffer = &gcam->debugfs.buffers[op->buffer];
5883265edafSLaurent Pinchart 
5893265edafSLaurent Pinchart 	return simple_read_from_buffer(buf, len, offset, buffer->data,
5903265edafSLaurent Pinchart 				       buffer->length);
5913265edafSLaurent Pinchart }
5923265edafSLaurent Pinchart 
5933265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file,
5943265edafSLaurent Pinchart 				       const char __user *buf, size_t len,
5953265edafSLaurent Pinchart 				       loff_t *offset)
5963265edafSLaurent Pinchart {
5973265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
5983265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
5993265edafSLaurent Pinchart 	ssize_t ret;
6003265edafSLaurent Pinchart 	char *kbuf;
6013265edafSLaurent Pinchart 
6023265edafSLaurent Pinchart 	if (len > 1024)
6033265edafSLaurent Pinchart 	       return -EINVAL;
6043265edafSLaurent Pinchart 
6053265edafSLaurent Pinchart 	kbuf = kmalloc(len + 1, GFP_KERNEL);
6063265edafSLaurent Pinchart 	if (kbuf == NULL)
6073265edafSLaurent Pinchart 		return -ENOMEM;
6083265edafSLaurent Pinchart 
6093265edafSLaurent Pinchart 	if (copy_from_user(kbuf, buf, len)) {
6103265edafSLaurent Pinchart 		ret = -EFAULT;
6113265edafSLaurent Pinchart 		goto done;
6123265edafSLaurent Pinchart 	}
6133265edafSLaurent Pinchart 
6143265edafSLaurent Pinchart 	kbuf[len] = '\0';
6153265edafSLaurent Pinchart 
6163265edafSLaurent Pinchart 	ret = op->execute(gcam, kbuf, len);
6173265edafSLaurent Pinchart 
6183265edafSLaurent Pinchart done:
6193265edafSLaurent Pinchart 	kfree(kbuf);
6203265edafSLaurent Pinchart 	return ret;
6213265edafSLaurent Pinchart }
6223265edafSLaurent Pinchart 
6233265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file)
6243265edafSLaurent Pinchart {
6253265edafSLaurent Pinchart 	unsigned int i;
6263265edafSLaurent Pinchart 
6273265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
6283265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
6293265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
6303265edafSLaurent Pinchart 
6314dda744cSGreg Kroah-Hartman 		if (!strcmp(file->f_path.dentry->d_iname, entry->name)) {
6323265edafSLaurent Pinchart 			file->private_data = (void *)entry;
6333265edafSLaurent Pinchart 			break;
6343265edafSLaurent Pinchart 		}
6353265edafSLaurent Pinchart 	}
6363265edafSLaurent Pinchart 
6373265edafSLaurent Pinchart 	return 0;
6383265edafSLaurent Pinchart }
6393265edafSLaurent Pinchart 
6403265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = {
6413265edafSLaurent Pinchart 	.open = gb_camera_debugfs_open,
6423265edafSLaurent Pinchart 	.read = gb_camera_debugfs_read,
6433265edafSLaurent Pinchart 	.write = gb_camera_debugfs_write,
6443265edafSLaurent Pinchart };
6453265edafSLaurent Pinchart 
6463265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam)
6473265edafSLaurent Pinchart {
6483265edafSLaurent Pinchart 	struct gb_connection *connection = gcam->connection;
6493265edafSLaurent Pinchart 	char dirname[27];
6503265edafSLaurent Pinchart 	unsigned int i;
6513265edafSLaurent Pinchart 
6523265edafSLaurent Pinchart 	/*
6533265edafSLaurent Pinchart 	 * Create root debugfs entry and a file entry for each camera operation.
6543265edafSLaurent Pinchart 	 */
6553265edafSLaurent Pinchart 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
6563265edafSLaurent Pinchart 		 connection->bundle->id);
6573265edafSLaurent Pinchart 
6583265edafSLaurent Pinchart 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
6593265edafSLaurent Pinchart 	if (IS_ERR(gcam->debugfs.root)) {
6603265edafSLaurent Pinchart 		gcam_err(gcam, "debugfs root create failed (%ld)\n",
6613265edafSLaurent Pinchart 			 PTR_ERR(gcam->debugfs.root));
6623265edafSLaurent Pinchart 		return PTR_ERR(gcam->debugfs.root);
6633265edafSLaurent Pinchart 	}
6643265edafSLaurent Pinchart 
6653265edafSLaurent Pinchart 	gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) *
6663265edafSLaurent Pinchart 					GB_CAMERA_DEBUGFS_BUFFER_MAX);
6673265edafSLaurent Pinchart 	if (!gcam->debugfs.buffers)
6683265edafSLaurent Pinchart 		return -ENOMEM;
6693265edafSLaurent Pinchart 
6703265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
6713265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
6723265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
6733265edafSLaurent Pinchart 		struct dentry *dentry;
6743265edafSLaurent Pinchart 
6753265edafSLaurent Pinchart 		gcam->debugfs.buffers[i].length = 0;
6763265edafSLaurent Pinchart 
6773265edafSLaurent Pinchart 		dentry = debugfs_create_file(entry->name, entry->mask,
6783265edafSLaurent Pinchart 					     gcam->debugfs.root, gcam,
6793265edafSLaurent Pinchart 					     &gb_camera_debugfs_ops);
6803265edafSLaurent Pinchart 		if (IS_ERR(dentry)) {
6813265edafSLaurent Pinchart 			gcam_err(gcam,
6823265edafSLaurent Pinchart 				 "debugfs operation %s create failed (%ld)\n",
6833265edafSLaurent Pinchart 				 entry->name, PTR_ERR(gcam->debugfs.root));
6843265edafSLaurent Pinchart 			return PTR_ERR(dentry);
6853265edafSLaurent Pinchart 		}
6863265edafSLaurent Pinchart 	}
6873265edafSLaurent Pinchart 
6883265edafSLaurent Pinchart 	return 0;
6893265edafSLaurent Pinchart }
6903265edafSLaurent Pinchart 
6913265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam)
6923265edafSLaurent Pinchart {
6933265edafSLaurent Pinchart 	if (gcam->debugfs.root)
6943265edafSLaurent Pinchart 		debugfs_remove_recursive(gcam->debugfs.root);
6953265edafSLaurent Pinchart 
6963265edafSLaurent Pinchart 	vfree(gcam->debugfs.buffers);
6973265edafSLaurent Pinchart }
6983265edafSLaurent Pinchart 
6993265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
7003265edafSLaurent Pinchart  * Init & Cleanup
7013265edafSLaurent Pinchart  */
7023265edafSLaurent Pinchart 
7033265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam)
7043265edafSLaurent Pinchart {
7053265edafSLaurent Pinchart 	gb_camera_debugfs_cleanup(gcam);
7063265edafSLaurent Pinchart 
7073265edafSLaurent Pinchart 	if (gcam->data_connected) {
7083265edafSLaurent Pinchart 		struct gb_interface *intf = gcam->connection->intf;
7093265edafSLaurent Pinchart 		struct gb_svc *svc = gcam->connection->hd->svc;
7103265edafSLaurent Pinchart 
7113265edafSLaurent Pinchart 		gb_svc_connection_destroy(svc, intf->interface_id,
7123265edafSLaurent Pinchart 					  ES2_APB_CDSI0_CPORT, svc->ap_intf_id,
7133265edafSLaurent Pinchart 					  ES2_APB_CDSI1_CPORT);
7143265edafSLaurent Pinchart 	}
7153265edafSLaurent Pinchart 
7163265edafSLaurent Pinchart 	kfree(gcam);
7173265edafSLaurent Pinchart }
7183265edafSLaurent Pinchart 
7193265edafSLaurent Pinchart static int gb_camera_connection_init(struct gb_connection *connection)
7203265edafSLaurent Pinchart {
7213265edafSLaurent Pinchart 	struct gb_svc *svc = connection->hd->svc;
7223265edafSLaurent Pinchart 	struct gb_camera *gcam;
7233265edafSLaurent Pinchart 	int ret;
7243265edafSLaurent Pinchart 
7253265edafSLaurent Pinchart 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
7263265edafSLaurent Pinchart 	if (!gcam)
7273265edafSLaurent Pinchart 		return -ENOMEM;
7283265edafSLaurent Pinchart 
7293265edafSLaurent Pinchart 	gcam->connection = connection;
7303265edafSLaurent Pinchart 	connection->private = gcam;
7313265edafSLaurent Pinchart 
7323265edafSLaurent Pinchart 	/*
7333265edafSLaurent Pinchart 	 * Create the data connection between camera module CDSI0 and APB CDS1.
7343265edafSLaurent Pinchart 	 * The CPort IDs are hardcoded by the ES2 bridges.
7353265edafSLaurent Pinchart 	 */
7363265edafSLaurent Pinchart 	ret = gb_svc_connection_create(svc, connection->intf->interface_id,
7373265edafSLaurent Pinchart 				       ES2_APB_CDSI0_CPORT, svc->ap_intf_id,
7383265edafSLaurent Pinchart 				       ES2_APB_CDSI1_CPORT, false);
7393265edafSLaurent Pinchart 	if (ret < 0)
7403265edafSLaurent Pinchart 		goto error;
7413265edafSLaurent Pinchart 
74241c23958SJohan Hovold 	gcam->data_connected = true;
74341c23958SJohan Hovold 
744aab4a1a3SLaurent Pinchart 	ret = gb_svc_intf_set_power_mode(svc, connection->intf->interface_id,
745aab4a1a3SLaurent Pinchart 					 GB_SVC_UNIPRO_HS_SERIES_A,
746aab4a1a3SLaurent Pinchart 					 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
747aab4a1a3SLaurent Pinchart 					 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
748aab4a1a3SLaurent Pinchart 					 GB_SVC_PWRM_RXTERMINATION |
749aab4a1a3SLaurent Pinchart 					 GB_SVC_PWRM_TXTERMINATION, 0);
750bcc050beSLaurent Pinchart 	if (ret < 0)
751bcc050beSLaurent Pinchart 		goto error;
752bcc050beSLaurent Pinchart 
753aab4a1a3SLaurent Pinchart 	ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id,
754aab4a1a3SLaurent Pinchart 					 GB_SVC_UNIPRO_HS_SERIES_A,
755aab4a1a3SLaurent Pinchart 					 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
756aab4a1a3SLaurent Pinchart 					 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
757aab4a1a3SLaurent Pinchart 					 GB_SVC_PWRM_RXTERMINATION |
758aab4a1a3SLaurent Pinchart 					 GB_SVC_PWRM_TXTERMINATION, 0);
759bcc050beSLaurent Pinchart 	if (ret < 0)
760bcc050beSLaurent Pinchart 		goto error;
761bcc050beSLaurent Pinchart 
7623265edafSLaurent Pinchart 	ret = gb_camera_debugfs_init(gcam);
7633265edafSLaurent Pinchart 	if (ret < 0)
7643265edafSLaurent Pinchart 		goto error;
7653265edafSLaurent Pinchart 
7663a8dba4eSGjorgji Rosikopulos 	ret = gb_camera_register_intf_ops(gcam);
7673a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
7683a8dba4eSGjorgji Rosikopulos 		goto error;
7693a8dba4eSGjorgji Rosikopulos 
7703265edafSLaurent Pinchart 	return 0;
7713265edafSLaurent Pinchart 
7723265edafSLaurent Pinchart error:
7733265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
7743265edafSLaurent Pinchart 	return ret;
7753265edafSLaurent Pinchart }
7763265edafSLaurent Pinchart 
7773265edafSLaurent Pinchart static void gb_camera_connection_exit(struct gb_connection *connection)
7783265edafSLaurent Pinchart {
7793265edafSLaurent Pinchart 	struct gb_camera *gcam = connection->private;
7803265edafSLaurent Pinchart 
7813265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
7823265edafSLaurent Pinchart }
7833265edafSLaurent Pinchart 
7843265edafSLaurent Pinchart static struct gb_protocol camera_protocol = {
7853265edafSLaurent Pinchart 	.name			= "camera",
7863265edafSLaurent Pinchart 	.id			= GREYBUS_PROTOCOL_CAMERA_MGMT,
7873265edafSLaurent Pinchart 	.major			= GB_CAMERA_VERSION_MAJOR,
7883265edafSLaurent Pinchart 	.minor			= GB_CAMERA_VERSION_MINOR,
7893265edafSLaurent Pinchart 	.connection_init	= gb_camera_connection_init,
7903265edafSLaurent Pinchart 	.connection_exit	= gb_camera_connection_exit,
7913265edafSLaurent Pinchart 	.request_recv		= gb_camera_event_recv,
7923265edafSLaurent Pinchart };
7933265edafSLaurent Pinchart 
7943265edafSLaurent Pinchart gb_protocol_driver(&camera_protocol);
7953265edafSLaurent Pinchart 
7963265edafSLaurent Pinchart MODULE_LICENSE("GPL v2");
797