xref: /openbmc/linux/drivers/staging/greybus/camera.c (revision c6622216)
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 
193265edafSLaurent Pinchart #include "greybus.h"
203265edafSLaurent Pinchart #include "greybus_protocols.h"
213265edafSLaurent Pinchart 
223265edafSLaurent Pinchart enum gb_camera_debugs_buffer_id {
233265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
243265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
253265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
263265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
273265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_MAX,
283265edafSLaurent Pinchart };
293265edafSLaurent Pinchart 
303265edafSLaurent Pinchart struct gb_camera_debugfs_buffer {
313265edafSLaurent Pinchart 	char data[PAGE_SIZE];
323265edafSLaurent Pinchart 	size_t length;
333265edafSLaurent Pinchart };
343265edafSLaurent Pinchart 
353265edafSLaurent Pinchart /**
363265edafSLaurent Pinchart  * struct gb_camera - A Greybus Camera Device
373265edafSLaurent Pinchart  * @connection: the greybus connection for camera control
383265edafSLaurent Pinchart  * @data_connected: whether the data connection has been established
393265edafSLaurent Pinchart  * @debugfs: debugfs entries for camera protocol operations testing
403265edafSLaurent Pinchart  */
413265edafSLaurent Pinchart struct gb_camera {
423265edafSLaurent Pinchart 	struct gb_connection *connection;
433265edafSLaurent Pinchart 	bool data_connected;
443265edafSLaurent Pinchart 
453265edafSLaurent Pinchart 	struct {
463265edafSLaurent Pinchart 		struct dentry *root;
473265edafSLaurent Pinchart 		struct gb_camera_debugfs_buffer *buffers;
483265edafSLaurent Pinchart 	} debugfs;
493265edafSLaurent Pinchart };
503265edafSLaurent Pinchart 
513265edafSLaurent Pinchart struct gb_camera_stream_config {
523265edafSLaurent Pinchart 	unsigned int width;
533265edafSLaurent Pinchart 	unsigned int height;
543265edafSLaurent Pinchart 	unsigned int format;
553265edafSLaurent Pinchart 	unsigned int vc;
563265edafSLaurent Pinchart 	unsigned int dt[2];
573265edafSLaurent Pinchart 	unsigned int max_size;
583265edafSLaurent Pinchart };
593265edafSLaurent Pinchart 
603265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT		16
613265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT		17
623265edafSLaurent Pinchart 
633265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
643265edafSLaurent Pinchart 
653265edafSLaurent Pinchart #define gcam_dbg(gcam, format...) \
663265edafSLaurent Pinchart 	dev_dbg(&gcam->connection->bundle->dev, format)
673265edafSLaurent Pinchart #define gcam_info(gcam, format...) \
683265edafSLaurent Pinchart 	dev_info(&gcam->connection->bundle->dev, format)
693265edafSLaurent Pinchart #define gcam_err(gcam, format...) \
703265edafSLaurent Pinchart 	dev_err(&gcam->connection->bundle->dev, format)
713265edafSLaurent Pinchart 
723265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
733265edafSLaurent Pinchart  * Camera Protocol Operations
743265edafSLaurent Pinchart  */
753265edafSLaurent Pinchart 
763265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam,
773265edafSLaurent Pinchart 				       unsigned int nstreams,
783265edafSLaurent Pinchart 				       struct gb_camera_stream_config *streams)
793265edafSLaurent Pinchart {
803265edafSLaurent Pinchart 	struct gb_camera_configure_streams_request *req;
813265edafSLaurent Pinchart 	struct gb_camera_configure_streams_response *resp;
823265edafSLaurent Pinchart 	unsigned int i;
833265edafSLaurent Pinchart 	size_t req_size;
843265edafSLaurent Pinchart 	size_t resp_size;
853265edafSLaurent Pinchart 	int ret;
863265edafSLaurent Pinchart 
873265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
883265edafSLaurent Pinchart 		return -EINVAL;
893265edafSLaurent Pinchart 
903265edafSLaurent Pinchart 	req_size = sizeof(*req) + nstreams * sizeof(req->config[0]);
913265edafSLaurent Pinchart 	resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]);
923265edafSLaurent Pinchart 
933265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
943265edafSLaurent Pinchart 	resp = kmalloc(resp_size, GFP_KERNEL);
953265edafSLaurent Pinchart 	if (!req || !resp) {
963265edafSLaurent Pinchart 		ret = -ENOMEM;
973265edafSLaurent Pinchart 		goto done;
983265edafSLaurent Pinchart 	}
993265edafSLaurent Pinchart 
1003265edafSLaurent Pinchart 	req->num_streams = nstreams;
1013265edafSLaurent Pinchart 	req->padding = 0;
1023265edafSLaurent Pinchart 
1033265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
1043265edafSLaurent Pinchart 		struct gb_camera_stream_config_request *cfg = &req->config[i];
1053265edafSLaurent Pinchart 
106c6622216SLaurent Pinchart 		cfg->width = cpu_to_le16(streams[i].width);
107c6622216SLaurent Pinchart 		cfg->height = cpu_to_le16(streams[i].height);
108c6622216SLaurent Pinchart 		cfg->format = cpu_to_le16(streams[i].format);
1093265edafSLaurent Pinchart 		cfg->padding = 0;
1103265edafSLaurent Pinchart 	}
1113265edafSLaurent Pinchart 
1123265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection,
1133265edafSLaurent Pinchart 				GB_CAMERA_TYPE_CONFIGURE_STREAMS,
1143265edafSLaurent Pinchart 				req, req_size, resp, resp_size);
1153265edafSLaurent Pinchart 	if (ret < 0)
1163265edafSLaurent Pinchart 		return ret;
1173265edafSLaurent Pinchart 
1183265edafSLaurent Pinchart 	if (resp->num_streams > nstreams) {
1193265edafSLaurent Pinchart 		gcam_dbg(gcam, "got #streams %u > request %u\n",
1203265edafSLaurent Pinchart 			 resp->num_streams, nstreams);
1213265edafSLaurent Pinchart 		ret = -EIO;
1223265edafSLaurent Pinchart 		goto done;
1233265edafSLaurent Pinchart 	}
1243265edafSLaurent Pinchart 
1253265edafSLaurent Pinchart 	if (resp->padding != 0) {
1263265edafSLaurent Pinchart 		gcam_dbg(gcam, "response padding != 0");
1273265edafSLaurent Pinchart 		ret = -EIO;
1283265edafSLaurent Pinchart 		goto done;
1293265edafSLaurent Pinchart 	}
1303265edafSLaurent Pinchart 
1313265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
1323265edafSLaurent Pinchart 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
1333265edafSLaurent Pinchart 
134c6622216SLaurent Pinchart 		streams[i].width = le16_to_cpu(cfg->width);
135c6622216SLaurent Pinchart 		streams[i].height = le16_to_cpu(cfg->height);
136c6622216SLaurent Pinchart 		streams[i].format = le16_to_cpu(cfg->format);
1373265edafSLaurent Pinchart 		streams[i].vc = cfg->virtual_channel;
1383265edafSLaurent Pinchart 		streams[i].dt[0] = cfg->data_type[0];
1393265edafSLaurent Pinchart 		streams[i].dt[1] = cfg->data_type[1];
140c6622216SLaurent Pinchart 		streams[i].max_size = le32_to_cpu(cfg->max_size);
1413265edafSLaurent Pinchart 
1423265edafSLaurent Pinchart 		if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) {
1433265edafSLaurent Pinchart 			gcam_dbg(gcam, "stream #%u padding != 0", i);
1443265edafSLaurent Pinchart 			ret = -EIO;
1453265edafSLaurent Pinchart 			goto done;
1463265edafSLaurent Pinchart 		}
1473265edafSLaurent Pinchart 	}
1483265edafSLaurent Pinchart 
1493265edafSLaurent Pinchart 	ret = resp->num_streams;
1503265edafSLaurent Pinchart 
1513265edafSLaurent Pinchart done:
1523265edafSLaurent Pinchart 	kfree(req);
1533265edafSLaurent Pinchart 	kfree(resp);
1543265edafSLaurent Pinchart 	return ret;
1553265edafSLaurent Pinchart }
1563265edafSLaurent Pinchart 
1573265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id,
1583265edafSLaurent Pinchart 			     unsigned int streams, unsigned int num_frames,
1593265edafSLaurent Pinchart 			     size_t settings_size, const void *settings)
1603265edafSLaurent Pinchart {
1613265edafSLaurent Pinchart 	struct gb_camera_capture_request *req;
1623265edafSLaurent Pinchart 	size_t req_size;
1633265edafSLaurent Pinchart 
1643265edafSLaurent Pinchart 	if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE)
1653265edafSLaurent Pinchart 		return -EINVAL;
1663265edafSLaurent Pinchart 
1673265edafSLaurent Pinchart 	req_size = sizeof(*req) + settings_size;
1683265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
1693265edafSLaurent Pinchart 	if (!req)
1703265edafSLaurent Pinchart 		return -ENOMEM;
1713265edafSLaurent Pinchart 
172c6622216SLaurent Pinchart 	req->request_id = cpu_to_le32(request_id);
1733265edafSLaurent Pinchart 	req->streams = streams;
1743265edafSLaurent Pinchart 	req->padding = 0;
175c6622216SLaurent Pinchart 	req->num_frames = cpu_to_le16(num_frames);
1763265edafSLaurent Pinchart 	memcpy(req->settings, settings, settings_size);
1773265edafSLaurent Pinchart 
1783265edafSLaurent Pinchart 	return gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE,
1793265edafSLaurent Pinchart 				 req, req_size, NULL, 0);
1803265edafSLaurent Pinchart }
1813265edafSLaurent Pinchart 
1823265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id)
1833265edafSLaurent Pinchart {
1843265edafSLaurent Pinchart 	struct gb_camera_flush_response resp;
1853265edafSLaurent Pinchart 	int ret;
1863265edafSLaurent Pinchart 
1873265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0,
1883265edafSLaurent Pinchart 				&resp, sizeof(resp));
1893265edafSLaurent Pinchart 	if (ret < 0)
1903265edafSLaurent Pinchart 		return ret;
1913265edafSLaurent Pinchart 
1923265edafSLaurent Pinchart 	if (request_id)
193c6622216SLaurent Pinchart 		*request_id = le32_to_cpu(resp.request_id);
1943265edafSLaurent Pinchart 
1953265edafSLaurent Pinchart 	return 0;
1963265edafSLaurent Pinchart }
1973265edafSLaurent Pinchart 
1983265edafSLaurent Pinchart static int gb_camera_event_recv(u8 type, struct gb_operation *op)
1993265edafSLaurent Pinchart {
2003265edafSLaurent Pinchart 	struct gb_camera *gcam = op->connection->private;
2013265edafSLaurent Pinchart 	struct gb_camera_metadata_request *payload;
2023265edafSLaurent Pinchart 	struct gb_message *request;
2033265edafSLaurent Pinchart 
2043265edafSLaurent Pinchart 	if (type != GB_CAMERA_TYPE_METADATA) {
2053265edafSLaurent Pinchart 		gcam_err(gcam, "Unsupported unsolicited event: %u\n", type);
2063265edafSLaurent Pinchart 		return -EINVAL;
2073265edafSLaurent Pinchart 	}
2083265edafSLaurent Pinchart 
2093265edafSLaurent Pinchart 	request = op->request;
2103265edafSLaurent Pinchart 
2113265edafSLaurent Pinchart 	if (request->payload_size < sizeof(*payload)) {
2123265edafSLaurent Pinchart 		gcam_err(gcam, "Wrong event size received (%zu < %zu)\n",
2133265edafSLaurent Pinchart 			 request->payload_size, sizeof(*payload));
2143265edafSLaurent Pinchart 		return -EINVAL;
2153265edafSLaurent Pinchart 	}
2163265edafSLaurent Pinchart 
2173265edafSLaurent Pinchart 	payload = request->payload;
2183265edafSLaurent Pinchart 
2193265edafSLaurent Pinchart 	gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n",
2203265edafSLaurent Pinchart 		 payload->request_id, payload->frame_number, payload->stream);
2213265edafSLaurent Pinchart 
2223265edafSLaurent Pinchart 	return 0;
2233265edafSLaurent Pinchart }
2243265edafSLaurent Pinchart 
2253265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
2263265edafSLaurent Pinchart  * DebugFS
2273265edafSLaurent Pinchart  */
2283265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam,
2293265edafSLaurent Pinchart 		char *buf, size_t len)
2303265edafSLaurent Pinchart {
2313265edafSLaurent Pinchart 	return len;
2323265edafSLaurent Pinchart }
2333265edafSLaurent Pinchart 
2343265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam,
2353265edafSLaurent Pinchart 		char *buf, size_t len)
2363265edafSLaurent Pinchart {
2373265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
2383265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS];
2393265edafSLaurent Pinchart 	struct gb_camera_stream_config *streams;
2403265edafSLaurent Pinchart 	unsigned int nstreams;
2413265edafSLaurent Pinchart 	const char *sep = ";";
2423265edafSLaurent Pinchart 	unsigned int i;
2433265edafSLaurent Pinchart 	char *token;
2443265edafSLaurent Pinchart 	int ret;
2453265edafSLaurent Pinchart 
2463265edafSLaurent Pinchart 	/* Retrieve number of streams to configure */
2473265edafSLaurent Pinchart 	token = strsep(&buf, sep);
2483265edafSLaurent Pinchart 	if (token == NULL)
2493265edafSLaurent Pinchart 		return -EINVAL;
2503265edafSLaurent Pinchart 
2513265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &nstreams);
2523265edafSLaurent Pinchart 	if (ret < 0)
2533265edafSLaurent Pinchart 		return ret;
2543265edafSLaurent Pinchart 
2553265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
2563265edafSLaurent Pinchart 		return -EINVAL;
2573265edafSLaurent Pinchart 
2583265edafSLaurent Pinchart 	/* For each stream to configure parse width, height and format */
2593265edafSLaurent Pinchart 	streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL);
2603265edafSLaurent Pinchart 	if (!streams)
2613265edafSLaurent Pinchart 		return -ENOMEM;
2623265edafSLaurent Pinchart 
2633265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
2643265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
2653265edafSLaurent Pinchart 
2663265edafSLaurent Pinchart 		/* width */
2673265edafSLaurent Pinchart 		token = strsep(&buf, ";");
2683265edafSLaurent Pinchart 		if (token == NULL) {
2693265edafSLaurent Pinchart 			ret = -EINVAL;
2703265edafSLaurent Pinchart 			goto done;
2713265edafSLaurent Pinchart 		}
2723265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->width);
2733265edafSLaurent Pinchart 		if (ret < 0)
2743265edafSLaurent Pinchart 			goto done;
2753265edafSLaurent Pinchart 
2763265edafSLaurent Pinchart 		/* height */
2773265edafSLaurent Pinchart 		token = strsep(&buf, ";");
2783265edafSLaurent Pinchart 		if (token == NULL)
2793265edafSLaurent Pinchart 			goto done;
2803265edafSLaurent Pinchart 
2813265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->height);
2823265edafSLaurent Pinchart 		if (ret < 0)
2833265edafSLaurent Pinchart 			goto done;
2843265edafSLaurent Pinchart 
2853265edafSLaurent Pinchart 		/* Image format code */
2863265edafSLaurent Pinchart 		token = strsep(&buf, ";");
2873265edafSLaurent Pinchart 		if (token == NULL)
2883265edafSLaurent Pinchart 			goto done;
2893265edafSLaurent Pinchart 
2903265edafSLaurent Pinchart 		ret = kstrtouint(token, 16, &stream->format);
2913265edafSLaurent Pinchart 		if (ret < 0)
2923265edafSLaurent Pinchart 			goto done;
2933265edafSLaurent Pinchart 	}
2943265edafSLaurent Pinchart 
2953265edafSLaurent Pinchart 	ret = gb_camera_configure_streams(gcam, nstreams, streams);
2963265edafSLaurent Pinchart 	if (ret < 0)
2973265edafSLaurent Pinchart 		goto done;
2983265edafSLaurent Pinchart 
2993265edafSLaurent Pinchart 	nstreams = ret;
3003265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u;", nstreams);
3013265edafSLaurent Pinchart 
3023265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
3033265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
3043265edafSLaurent Pinchart 
3053265edafSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
3063265edafSLaurent Pinchart 					  "%u;%u;%u;%u;%u;%u;%u;",
3073265edafSLaurent Pinchart 					  stream->width, stream->height,
3083265edafSLaurent Pinchart 					  stream->format, stream->vc,
3093265edafSLaurent Pinchart 					  stream->dt[0], stream->dt[1],
3103265edafSLaurent Pinchart 					  stream->max_size);
3113265edafSLaurent Pinchart 	}
3123265edafSLaurent Pinchart 
3133265edafSLaurent Pinchart 	ret = len;
3143265edafSLaurent Pinchart 
3153265edafSLaurent Pinchart done:
3163265edafSLaurent Pinchart 	kfree(streams);
3173265edafSLaurent Pinchart 	return ret;
3183265edafSLaurent Pinchart };
3193265edafSLaurent Pinchart 
3203265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam,
3213265edafSLaurent Pinchart 		char *buf, size_t len)
3223265edafSLaurent Pinchart {
3233265edafSLaurent Pinchart 	unsigned int request_id;
3243265edafSLaurent Pinchart 	unsigned int streams_mask;
3253265edafSLaurent Pinchart 	unsigned int num_frames;
3263265edafSLaurent Pinchart 	char *token;
3273265edafSLaurent Pinchart 	int ret;
3283265edafSLaurent Pinchart 
3293265edafSLaurent Pinchart 	/* Request id */
3303265edafSLaurent Pinchart 	token = strsep(&buf, ";");
3313265edafSLaurent Pinchart 	if (token == NULL)
3323265edafSLaurent Pinchart 		return -EINVAL;
3333265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &request_id);
3343265edafSLaurent Pinchart 	if (ret < 0)
3353265edafSLaurent Pinchart 		return ret;
3363265edafSLaurent Pinchart 
3373265edafSLaurent Pinchart 	/* Stream mask */
3383265edafSLaurent Pinchart 	token = strsep(&buf, ";");
3393265edafSLaurent Pinchart 	if (token == NULL)
3403265edafSLaurent Pinchart 		return -EINVAL;
3413265edafSLaurent Pinchart 	ret = kstrtouint(token, 16, &streams_mask);
3423265edafSLaurent Pinchart 	if (ret < 0)
3433265edafSLaurent Pinchart 		return ret;
3443265edafSLaurent Pinchart 
3453265edafSLaurent Pinchart 	/* number of frames */
3463265edafSLaurent Pinchart 	token = strsep(&buf, ";");
3473265edafSLaurent Pinchart 	if (token == NULL)
3483265edafSLaurent Pinchart 		return -EINVAL;
3493265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &num_frames);
3503265edafSLaurent Pinchart 	if (ret < 0)
3513265edafSLaurent Pinchart 		return ret;
3523265edafSLaurent Pinchart 
3533265edafSLaurent Pinchart 	ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0,
3543265edafSLaurent Pinchart 				NULL);
3553265edafSLaurent Pinchart 	if (ret < 0)
3563265edafSLaurent Pinchart 		return ret;
3573265edafSLaurent Pinchart 
3583265edafSLaurent Pinchart 	return len;
3593265edafSLaurent Pinchart }
3603265edafSLaurent Pinchart 
3613265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam,
3623265edafSLaurent Pinchart 		char *buf, size_t len)
3633265edafSLaurent Pinchart {
3643265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
3653265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH];
3663265edafSLaurent Pinchart 	unsigned int req_id;
3673265edafSLaurent Pinchart 	int ret;
3683265edafSLaurent Pinchart 
3693265edafSLaurent Pinchart 	ret = gb_camera_flush(gcam, &req_id);
3703265edafSLaurent Pinchart 	if (ret < 0)
3713265edafSLaurent Pinchart 		return ret;
3723265edafSLaurent Pinchart 
3733265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u", req_id);
3743265edafSLaurent Pinchart 
3753265edafSLaurent Pinchart 	return len;
3763265edafSLaurent Pinchart }
3773265edafSLaurent Pinchart 
3783265edafSLaurent Pinchart struct gb_camera_debugfs_entry {
3793265edafSLaurent Pinchart 	const char *name;
3803265edafSLaurent Pinchart 	unsigned int mask;
3813265edafSLaurent Pinchart 	unsigned int buffer;
3823265edafSLaurent Pinchart 	ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len);
3833265edafSLaurent Pinchart };
3843265edafSLaurent Pinchart 
3853265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = {
3863265edafSLaurent Pinchart 	{
3873265edafSLaurent Pinchart 		.name = "capabilities",
3883265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO,
3893265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
3903265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capabilities,
3913265edafSLaurent Pinchart 	}, {
3923265edafSLaurent Pinchart 		.name = "configure_streams",
3933265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
3943265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
3953265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_configure_streams,
3963265edafSLaurent Pinchart 	}, {
3973265edafSLaurent Pinchart 		.name = "capture",
3983265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
3993265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
4003265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capture,
4013265edafSLaurent Pinchart 	}, {
4023265edafSLaurent Pinchart 		.name = "flush",
4033265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
4043265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
4053265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_flush,
4063265edafSLaurent Pinchart 	},
4073265edafSLaurent Pinchart };
4083265edafSLaurent Pinchart 
4093265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf,
4103265edafSLaurent Pinchart 				      size_t len, loff_t *offset)
4113265edafSLaurent Pinchart {
4123265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
4133265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
4143265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer;
4153265edafSLaurent Pinchart 	ssize_t ret;
4163265edafSLaurent Pinchart 
4173265edafSLaurent Pinchart 	/* For read-only entries the operation is triggered by a read. */
4183265edafSLaurent Pinchart 	if (!(op->mask & S_IWUGO)) {
4193265edafSLaurent Pinchart 		ret = op->execute(gcam, NULL, 0);
4203265edafSLaurent Pinchart 		if (ret < 0)
4213265edafSLaurent Pinchart 			return ret;
4223265edafSLaurent Pinchart 	}
4233265edafSLaurent Pinchart 
4243265edafSLaurent Pinchart 	buffer = &gcam->debugfs.buffers[op->buffer];
4253265edafSLaurent Pinchart 
4263265edafSLaurent Pinchart 	return simple_read_from_buffer(buf, len, offset, buffer->data,
4273265edafSLaurent Pinchart 				       buffer->length);
4283265edafSLaurent Pinchart }
4293265edafSLaurent Pinchart 
4303265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file,
4313265edafSLaurent Pinchart 				       const char __user *buf, size_t len,
4323265edafSLaurent Pinchart 				       loff_t *offset)
4333265edafSLaurent Pinchart {
4343265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
4353265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
4363265edafSLaurent Pinchart 	ssize_t ret;
4373265edafSLaurent Pinchart 	char *kbuf;
4383265edafSLaurent Pinchart 
4393265edafSLaurent Pinchart 	if (len > 1024)
4403265edafSLaurent Pinchart 	       return -EINVAL;
4413265edafSLaurent Pinchart 
4423265edafSLaurent Pinchart 	kbuf = kmalloc(len + 1, GFP_KERNEL);
4433265edafSLaurent Pinchart 	if (kbuf == NULL)
4443265edafSLaurent Pinchart 		return -ENOMEM;
4453265edafSLaurent Pinchart 
4463265edafSLaurent Pinchart 	if (copy_from_user(kbuf, buf, len)) {
4473265edafSLaurent Pinchart 		ret = -EFAULT;
4483265edafSLaurent Pinchart 		goto done;
4493265edafSLaurent Pinchart 	}
4503265edafSLaurent Pinchart 
4513265edafSLaurent Pinchart 	kbuf[len] = '\0';
4523265edafSLaurent Pinchart 
4533265edafSLaurent Pinchart 	ret = op->execute(gcam, kbuf, len);
4543265edafSLaurent Pinchart 
4553265edafSLaurent Pinchart done:
4563265edafSLaurent Pinchart 	kfree(kbuf);
4573265edafSLaurent Pinchart 	return ret;
4583265edafSLaurent Pinchart }
4593265edafSLaurent Pinchart 
4603265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file)
4613265edafSLaurent Pinchart {
4623265edafSLaurent Pinchart 	unsigned int i;
4633265edafSLaurent Pinchart 
4643265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
4653265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
4663265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
4673265edafSLaurent Pinchart 
4684dda744cSGreg Kroah-Hartman 		if (!strcmp(file->f_path.dentry->d_iname, entry->name)) {
4693265edafSLaurent Pinchart 			file->private_data = (void *)entry;
4703265edafSLaurent Pinchart 			break;
4713265edafSLaurent Pinchart 		}
4723265edafSLaurent Pinchart 	}
4733265edafSLaurent Pinchart 
4743265edafSLaurent Pinchart 	return 0;
4753265edafSLaurent Pinchart }
4763265edafSLaurent Pinchart 
4773265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = {
4783265edafSLaurent Pinchart 	.open = gb_camera_debugfs_open,
4793265edafSLaurent Pinchart 	.read = gb_camera_debugfs_read,
4803265edafSLaurent Pinchart 	.write = gb_camera_debugfs_write,
4813265edafSLaurent Pinchart };
4823265edafSLaurent Pinchart 
4833265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam)
4843265edafSLaurent Pinchart {
4853265edafSLaurent Pinchart 	struct gb_connection *connection = gcam->connection;
4863265edafSLaurent Pinchart 	char dirname[27];
4873265edafSLaurent Pinchart 	unsigned int i;
4883265edafSLaurent Pinchart 
4893265edafSLaurent Pinchart 	/*
4903265edafSLaurent Pinchart 	 * Create root debugfs entry and a file entry for each camera operation.
4913265edafSLaurent Pinchart 	 */
4923265edafSLaurent Pinchart 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
4933265edafSLaurent Pinchart 		 connection->bundle->id);
4943265edafSLaurent Pinchart 
4953265edafSLaurent Pinchart 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
4963265edafSLaurent Pinchart 	if (IS_ERR(gcam->debugfs.root)) {
4973265edafSLaurent Pinchart 		gcam_err(gcam, "debugfs root create failed (%ld)\n",
4983265edafSLaurent Pinchart 			 PTR_ERR(gcam->debugfs.root));
4993265edafSLaurent Pinchart 		return PTR_ERR(gcam->debugfs.root);
5003265edafSLaurent Pinchart 	}
5013265edafSLaurent Pinchart 
5023265edafSLaurent Pinchart 	gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) *
5033265edafSLaurent Pinchart 					GB_CAMERA_DEBUGFS_BUFFER_MAX);
5043265edafSLaurent Pinchart 	if (!gcam->debugfs.buffers)
5053265edafSLaurent Pinchart 		return -ENOMEM;
5063265edafSLaurent Pinchart 
5073265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
5083265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
5093265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
5103265edafSLaurent Pinchart 		struct dentry *dentry;
5113265edafSLaurent Pinchart 
5123265edafSLaurent Pinchart 		gcam->debugfs.buffers[i].length = 0;
5133265edafSLaurent Pinchart 
5143265edafSLaurent Pinchart 		dentry = debugfs_create_file(entry->name, entry->mask,
5153265edafSLaurent Pinchart 					     gcam->debugfs.root, gcam,
5163265edafSLaurent Pinchart 					     &gb_camera_debugfs_ops);
5173265edafSLaurent Pinchart 		if (IS_ERR(dentry)) {
5183265edafSLaurent Pinchart 			gcam_err(gcam,
5193265edafSLaurent Pinchart 				 "debugfs operation %s create failed (%ld)\n",
5203265edafSLaurent Pinchart 				 entry->name, PTR_ERR(gcam->debugfs.root));
5213265edafSLaurent Pinchart 			return PTR_ERR(dentry);
5223265edafSLaurent Pinchart 		}
5233265edafSLaurent Pinchart 	}
5243265edafSLaurent Pinchart 
5253265edafSLaurent Pinchart 	return 0;
5263265edafSLaurent Pinchart }
5273265edafSLaurent Pinchart 
5283265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam)
5293265edafSLaurent Pinchart {
5303265edafSLaurent Pinchart 	if (gcam->debugfs.root)
5313265edafSLaurent Pinchart 		debugfs_remove_recursive(gcam->debugfs.root);
5323265edafSLaurent Pinchart 
5333265edafSLaurent Pinchart 	vfree(gcam->debugfs.buffers);
5343265edafSLaurent Pinchart }
5353265edafSLaurent Pinchart 
5363265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
5373265edafSLaurent Pinchart  * Init & Cleanup
5383265edafSLaurent Pinchart  */
5393265edafSLaurent Pinchart 
5403265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam)
5413265edafSLaurent Pinchart {
5423265edafSLaurent Pinchart 	gb_camera_debugfs_cleanup(gcam);
5433265edafSLaurent Pinchart 
5443265edafSLaurent Pinchart 	if (gcam->data_connected) {
5453265edafSLaurent Pinchart 		struct gb_interface *intf = gcam->connection->intf;
5463265edafSLaurent Pinchart 		struct gb_svc *svc = gcam->connection->hd->svc;
5473265edafSLaurent Pinchart 
5483265edafSLaurent Pinchart 		gb_svc_connection_destroy(svc, intf->interface_id,
5493265edafSLaurent Pinchart 					  ES2_APB_CDSI0_CPORT, svc->ap_intf_id,
5503265edafSLaurent Pinchart 					  ES2_APB_CDSI1_CPORT);
5513265edafSLaurent Pinchart 	}
5523265edafSLaurent Pinchart 
5533265edafSLaurent Pinchart 	kfree(gcam);
5543265edafSLaurent Pinchart }
5553265edafSLaurent Pinchart 
5563265edafSLaurent Pinchart static int gb_camera_connection_init(struct gb_connection *connection)
5573265edafSLaurent Pinchart {
5583265edafSLaurent Pinchart 	struct gb_svc *svc = connection->hd->svc;
5593265edafSLaurent Pinchart 	struct gb_camera *gcam;
5603265edafSLaurent Pinchart 	int ret;
5613265edafSLaurent Pinchart 
5623265edafSLaurent Pinchart 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
5633265edafSLaurent Pinchart 	if (!gcam)
5643265edafSLaurent Pinchart 		return -ENOMEM;
5653265edafSLaurent Pinchart 
5663265edafSLaurent Pinchart 	gcam->connection = connection;
5673265edafSLaurent Pinchart 	connection->private = gcam;
5683265edafSLaurent Pinchart 
5693265edafSLaurent Pinchart 	/*
5703265edafSLaurent Pinchart 	 * Create the data connection between camera module CDSI0 and APB CDS1.
5713265edafSLaurent Pinchart 	 * The CPort IDs are hardcoded by the ES2 bridges.
5723265edafSLaurent Pinchart 	 */
5733265edafSLaurent Pinchart 	ret = gb_svc_connection_create(svc, connection->intf->interface_id,
5743265edafSLaurent Pinchart 				       ES2_APB_CDSI0_CPORT, svc->ap_intf_id,
5753265edafSLaurent Pinchart 				       ES2_APB_CDSI1_CPORT, false);
5763265edafSLaurent Pinchart 	if (ret < 0)
5773265edafSLaurent Pinchart 		goto error;
5783265edafSLaurent Pinchart 
5793265edafSLaurent Pinchart 	gcam->data_connected = true;
5803265edafSLaurent Pinchart 
5813265edafSLaurent Pinchart 	ret = gb_camera_debugfs_init(gcam);
5823265edafSLaurent Pinchart 	if (ret < 0)
5833265edafSLaurent Pinchart 		goto error;
5843265edafSLaurent Pinchart 
5853265edafSLaurent Pinchart 	return 0;
5863265edafSLaurent Pinchart 
5873265edafSLaurent Pinchart error:
5883265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
5893265edafSLaurent Pinchart 	return ret;
5903265edafSLaurent Pinchart }
5913265edafSLaurent Pinchart 
5923265edafSLaurent Pinchart static void gb_camera_connection_exit(struct gb_connection *connection)
5933265edafSLaurent Pinchart {
5943265edafSLaurent Pinchart 	struct gb_camera *gcam = connection->private;
5953265edafSLaurent Pinchart 
5963265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
5973265edafSLaurent Pinchart }
5983265edafSLaurent Pinchart 
5993265edafSLaurent Pinchart static struct gb_protocol camera_protocol = {
6003265edafSLaurent Pinchart 	.name			= "camera",
6013265edafSLaurent Pinchart 	.id			= GREYBUS_PROTOCOL_CAMERA_MGMT,
6023265edafSLaurent Pinchart 	.major			= GB_CAMERA_VERSION_MAJOR,
6033265edafSLaurent Pinchart 	.minor			= GB_CAMERA_VERSION_MINOR,
6043265edafSLaurent Pinchart 	.connection_init	= gb_camera_connection_init,
6053265edafSLaurent Pinchart 	.connection_exit	= gb_camera_connection_exit,
6063265edafSLaurent Pinchart 	.request_recv		= gb_camera_event_recv,
6073265edafSLaurent Pinchart };
6083265edafSLaurent Pinchart 
6093265edafSLaurent Pinchart gb_protocol_driver(&camera_protocol);
6103265edafSLaurent Pinchart 
6113265edafSLaurent Pinchart MODULE_LICENSE("GPL v2");
612