xref: /openbmc/linux/drivers/staging/greybus/camera.c (revision d9e4c4ee)
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
3817ca6770SEvgeniy Borisov  * @connection: the greybus connection for camera management
3917ca6770SEvgeniy Borisov  * @data_connection: the greybus connection for camera data
4017ca6770SEvgeniy Borisov  * @mutex: protects the connection field
413265edafSLaurent Pinchart  * @debugfs: debugfs entries for camera protocol operations testing
42c3d77f71SGjorgji Rosikopulos  * @module: Greybus camera module registered to HOST processor.
433265edafSLaurent Pinchart  */
443265edafSLaurent Pinchart struct gb_camera {
4568b66c28SLaurent Pinchart 	struct gb_bundle *bundle;
463265edafSLaurent Pinchart 	struct gb_connection *connection;
473ba9fa5cSJohan Hovold 	struct gb_connection *data_connection;
4817ca6770SEvgeniy Borisov 	struct mutex mutex;
493265edafSLaurent Pinchart 
503265edafSLaurent Pinchart 	struct {
513265edafSLaurent Pinchart 		struct dentry *root;
523265edafSLaurent Pinchart 		struct gb_camera_debugfs_buffer *buffers;
533265edafSLaurent Pinchart 	} debugfs;
54c3d77f71SGjorgji Rosikopulos 
55c3d77f71SGjorgji Rosikopulos 	struct gb_camera_module module;
563265edafSLaurent Pinchart };
573265edafSLaurent Pinchart 
583265edafSLaurent Pinchart struct gb_camera_stream_config {
593265edafSLaurent Pinchart 	unsigned int width;
603265edafSLaurent Pinchart 	unsigned int height;
613265edafSLaurent Pinchart 	unsigned int format;
623265edafSLaurent Pinchart 	unsigned int vc;
633265edafSLaurent Pinchart 	unsigned int dt[2];
643265edafSLaurent Pinchart 	unsigned int max_size;
653265edafSLaurent Pinchart };
663265edafSLaurent Pinchart 
673a8dba4eSGjorgji Rosikopulos struct gb_camera_fmt_map {
683a8dba4eSGjorgji Rosikopulos 	enum v4l2_mbus_pixelcode mbus_code;
693a8dba4eSGjorgji Rosikopulos 	unsigned int gb_format;
703a8dba4eSGjorgji Rosikopulos };
713a8dba4eSGjorgji Rosikopulos 
723a8dba4eSGjorgji Rosikopulos /* GB format to media code map */
733a8dba4eSGjorgji Rosikopulos static const struct gb_camera_fmt_map mbus_to_gbus_format[] = {
743a8dba4eSGjorgji Rosikopulos 	{
753a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
763a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x01,
773a8dba4eSGjorgji Rosikopulos 	},
783a8dba4eSGjorgji Rosikopulos 	{
797c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV12_1x8,
807c154711SGjorgji Rosikopulos 		.gb_format = 0x12,
817c154711SGjorgji Rosikopulos 	},
827c154711SGjorgji Rosikopulos 	{
837c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV21_1x8,
847c154711SGjorgji Rosikopulos 		.gb_format = 0x13,
857c154711SGjorgji Rosikopulos 	},
867c154711SGjorgji Rosikopulos 	{
877c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YU12_1x8,
883a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x16,
893a8dba4eSGjorgji Rosikopulos 	},
903a8dba4eSGjorgji Rosikopulos 	{
917c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YV12_1x8,
923a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x17,
933a8dba4eSGjorgji Rosikopulos 	},
943a8dba4eSGjorgji Rosikopulos 	{
953a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
963a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x40,
97dc5cc72cSGjorgji Rosikopulos 	},
98dc5cc72cSGjorgji Rosikopulos 	{
99dc5cc72cSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8,
100dc5cc72cSGjorgji Rosikopulos 		.gb_format = 0x41,
101cb14e976SGjorgji Rosikopulos 	},
102cb14e976SGjorgji Rosikopulos 	{
103cb14e976SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8,
104cb14e976SGjorgji Rosikopulos 		.gb_format = 0x42,
105cb14e976SGjorgji Rosikopulos 	},
1063a8dba4eSGjorgji Rosikopulos };
1073a8dba4eSGjorgji Rosikopulos 
1083265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT		16
1093265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT		17
1103265edafSLaurent Pinchart 
1113265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
1123265edafSLaurent Pinchart 
11368b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...)	dev_dbg(&gcam->bundle->dev, format)
11468b66c28SLaurent Pinchart #define gcam_info(gcam, format...)	dev_info(&gcam->bundle->dev, format)
11568b66c28SLaurent Pinchart #define gcam_err(gcam, format...)	dev_err(&gcam->bundle->dev, format)
1163265edafSLaurent Pinchart 
1173265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
1183265edafSLaurent Pinchart  * Camera Protocol Operations
1193265edafSLaurent Pinchart  */
1203265edafSLaurent Pinchart 
121c161c0fcSLaurent Pinchart static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id,
122c161c0fcSLaurent Pinchart 					 bool hs)
123c161c0fcSLaurent Pinchart {
124c161c0fcSLaurent Pinchart 	struct gb_svc *svc = gcam->connection->hd->svc;
125c161c0fcSLaurent Pinchart 	int ret;
126c161c0fcSLaurent Pinchart 
127c161c0fcSLaurent Pinchart 	if (hs)
128c161c0fcSLaurent Pinchart 		ret = gb_svc_intf_set_power_mode(svc, intf_id,
129c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_HS_SERIES_A,
130c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
1318c2522d8SEli Sennesh 						 GB_SVC_SMALL_AMPLITUDE,
1328c2522d8SEli Sennesh 						 GB_SVC_NO_DE_EMPHASIS,
133c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
134c161c0fcSLaurent Pinchart 						 GB_SVC_PWRM_RXTERMINATION |
1358c2522d8SEli Sennesh 						 GB_SVC_PWRM_TXTERMINATION, 0,
1368c2522d8SEli Sennesh 						 NULL, NULL);
137c161c0fcSLaurent Pinchart 	else
138c161c0fcSLaurent Pinchart 		ret = gb_svc_intf_set_power_mode(svc, intf_id,
139c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_HS_SERIES_A,
140c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_SLOW_AUTO_MODE,
141ee2f2074SMitchell Tasman 						 2, 1,
1428c2522d8SEli Sennesh 						 GB_SVC_SMALL_AMPLITUDE,
1438c2522d8SEli Sennesh 						 GB_SVC_NO_DE_EMPHASIS,
144c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_SLOW_AUTO_MODE,
145ee2f2074SMitchell Tasman 						 2, 1,
1468c2522d8SEli Sennesh 						 0, 0,
1478c2522d8SEli Sennesh 						 NULL, NULL);
148c161c0fcSLaurent Pinchart 
149c161c0fcSLaurent Pinchart 	return ret;
150c161c0fcSLaurent Pinchart }
151c161c0fcSLaurent Pinchart 
152c161c0fcSLaurent Pinchart static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs)
153c161c0fcSLaurent Pinchart {
154c161c0fcSLaurent Pinchart 	struct gb_interface *intf = gcam->connection->intf;
155c161c0fcSLaurent Pinchart 	struct gb_svc *svc = gcam->connection->hd->svc;
156c161c0fcSLaurent Pinchart 	int ret;
157c161c0fcSLaurent Pinchart 
158c161c0fcSLaurent Pinchart 	ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs);
159c161c0fcSLaurent Pinchart 	if (ret < 0) {
160c161c0fcSLaurent Pinchart 		gcam_err(gcam, "failed to set module interface to %s (%d)\n",
161c161c0fcSLaurent Pinchart 			 hs ? "HS" : "PWM", ret);
162c161c0fcSLaurent Pinchart 		return ret;
163c161c0fcSLaurent Pinchart 	}
164c161c0fcSLaurent Pinchart 
165c161c0fcSLaurent Pinchart 	ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs);
166c161c0fcSLaurent Pinchart 	if (ret < 0) {
167b573b0e6SLaurent Pinchart 		gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs);
168c161c0fcSLaurent Pinchart 		gcam_err(gcam, "failed to set AP interface to %s (%d)\n",
169c161c0fcSLaurent Pinchart 			 hs ? "HS" : "PWM", ret);
170c161c0fcSLaurent Pinchart 		return ret;
171c161c0fcSLaurent Pinchart 	}
172c161c0fcSLaurent Pinchart 
173c161c0fcSLaurent Pinchart 	return 0;
174c161c0fcSLaurent Pinchart }
175c161c0fcSLaurent Pinchart 
17648b15a9bSLaurent Pinchart static int gb_camera_capabilities(struct gb_camera *gcam,
17748b15a9bSLaurent Pinchart 				  u8 *capabilities, size_t *size)
17848b15a9bSLaurent Pinchart {
17917ca6770SEvgeniy Borisov 	struct gb_operation *op = NULL;
18048b15a9bSLaurent Pinchart 	int ret;
18148b15a9bSLaurent Pinchart 
18217ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
18317ca6770SEvgeniy Borisov 
18417ca6770SEvgeniy Borisov 	if (!gcam->connection) {
18517ca6770SEvgeniy Borisov 		ret = -EINVAL;
18617ca6770SEvgeniy Borisov 		goto done;
18717ca6770SEvgeniy Borisov 	}
18817ca6770SEvgeniy Borisov 
18948b15a9bSLaurent Pinchart 	op = gb_operation_create_flags(gcam->connection,
19048b15a9bSLaurent Pinchart 				       GB_CAMERA_TYPE_CAPABILITIES, 0, *size,
19148b15a9bSLaurent Pinchart 				       GB_OPERATION_FLAG_SHORT_RESPONSE,
19248b15a9bSLaurent Pinchart 				       GFP_KERNEL);
19317ca6770SEvgeniy Borisov 	if (!op) {
19417ca6770SEvgeniy Borisov 		ret = -ENOMEM;
19517ca6770SEvgeniy Borisov 		goto done;
19617ca6770SEvgeniy Borisov 	}
19748b15a9bSLaurent Pinchart 
19848b15a9bSLaurent Pinchart 	ret = gb_operation_request_send_sync(op);
19948b15a9bSLaurent Pinchart 	if (ret) {
20048b15a9bSLaurent Pinchart 		gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret);
20148b15a9bSLaurent Pinchart 		goto done;
20248b15a9bSLaurent Pinchart 	}
20348b15a9bSLaurent Pinchart 
20448b15a9bSLaurent Pinchart 	memcpy(capabilities, op->response->payload, op->response->payload_size);
20548b15a9bSLaurent Pinchart 	*size = op->response->payload_size;
20648b15a9bSLaurent Pinchart 
20748b15a9bSLaurent Pinchart done:
20817ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
20917ca6770SEvgeniy Borisov 	if (op)
21048b15a9bSLaurent Pinchart 		gb_operation_put(op);
21148b15a9bSLaurent Pinchart 	return ret;
21248b15a9bSLaurent Pinchart }
21348b15a9bSLaurent Pinchart 
214ed4596e9SGreg Kroah-Hartman struct ap_csi_config_request {
215ed4596e9SGreg Kroah-Hartman 	__u8 csi_id;
21627e18d8cSJacopo Mondi 	__u8 flags;
21727e18d8cSJacopo Mondi #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
218ed4596e9SGreg Kroah-Hartman 	__u8 num_lanes;
219ed4596e9SGreg Kroah-Hartman 	__u8 padding;
220ed4596e9SGreg Kroah-Hartman 	__le32 bus_freq;
221446091c9SJacopo Mondi 	__le32 lines_per_second;
222ed4596e9SGreg Kroah-Hartman } __packed;
223ed4596e9SGreg Kroah-Hartman 
2243265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam,
2254068487cSLaurent Pinchart 				       unsigned int *num_streams,
2264068487cSLaurent Pinchart 				       unsigned int *flags,
227b4905038SEvgeniy Borisov 				       struct gb_camera_stream_config *streams,
228b4905038SEvgeniy Borisov 				       struct gb_camera_csi_params *csi_params)
2293265edafSLaurent Pinchart {
2303265edafSLaurent Pinchart 	struct gb_camera_configure_streams_request *req;
2313265edafSLaurent Pinchart 	struct gb_camera_configure_streams_response *resp;
232ed4596e9SGreg Kroah-Hartman 	struct ap_csi_config_request csi_cfg;
2334fbf69c7SJacopo Mondi 
2344068487cSLaurent Pinchart 	unsigned int nstreams = *num_streams;
2353265edafSLaurent Pinchart 	unsigned int i;
2363265edafSLaurent Pinchart 	size_t req_size;
2373265edafSLaurent Pinchart 	size_t resp_size;
2383265edafSLaurent Pinchart 	int ret;
2393265edafSLaurent Pinchart 
2403265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
2413265edafSLaurent Pinchart 		return -EINVAL;
2423265edafSLaurent Pinchart 
2433265edafSLaurent Pinchart 	req_size = sizeof(*req) + nstreams * sizeof(req->config[0]);
2443265edafSLaurent Pinchart 	resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]);
2453265edafSLaurent Pinchart 
2463265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
2473265edafSLaurent Pinchart 	resp = kmalloc(resp_size, GFP_KERNEL);
2483265edafSLaurent Pinchart 	if (!req || !resp) {
24917ca6770SEvgeniy Borisov 		kfree(req);
25017ca6770SEvgeniy Borisov 		kfree(resp);
25117ca6770SEvgeniy Borisov 		return -ENOMEM;
2523265edafSLaurent Pinchart 	}
2533265edafSLaurent Pinchart 
254b787d413SJacopo Mondi 	req->num_streams = nstreams;
2554068487cSLaurent Pinchart 	req->flags = *flags;
2563265edafSLaurent Pinchart 	req->padding = 0;
2573265edafSLaurent Pinchart 
2583265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
2593265edafSLaurent Pinchart 		struct gb_camera_stream_config_request *cfg = &req->config[i];
2603265edafSLaurent Pinchart 
261c6622216SLaurent Pinchart 		cfg->width = cpu_to_le16(streams[i].width);
262c6622216SLaurent Pinchart 		cfg->height = cpu_to_le16(streams[i].height);
263c6622216SLaurent Pinchart 		cfg->format = cpu_to_le16(streams[i].format);
2643265edafSLaurent Pinchart 		cfg->padding = 0;
2653265edafSLaurent Pinchart 	}
2663265edafSLaurent Pinchart 
26717ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
26817ca6770SEvgeniy Borisov 
26917ca6770SEvgeniy Borisov 	if (!gcam->connection) {
27017ca6770SEvgeniy Borisov 		ret = -EINVAL;
27117ca6770SEvgeniy Borisov 		goto done;
27217ca6770SEvgeniy Borisov 	}
27317ca6770SEvgeniy Borisov 
2743265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection,
2753265edafSLaurent Pinchart 				GB_CAMERA_TYPE_CONFIGURE_STREAMS,
2763265edafSLaurent Pinchart 				req, req_size, resp, resp_size);
2773265edafSLaurent Pinchart 	if (ret < 0)
27866c36070SLaurent Pinchart 		goto done;
2793265edafSLaurent Pinchart 
280b787d413SJacopo Mondi 	if (resp->num_streams > nstreams) {
2813265edafSLaurent Pinchart 		gcam_dbg(gcam, "got #streams %u > request %u\n",
282b787d413SJacopo Mondi 			 resp->num_streams, nstreams);
2833265edafSLaurent Pinchart 		ret = -EIO;
28466c36070SLaurent Pinchart 		goto done;
2853265edafSLaurent Pinchart 	}
2863265edafSLaurent Pinchart 
2873265edafSLaurent Pinchart 	if (resp->padding != 0) {
2883265edafSLaurent Pinchart 		gcam_dbg(gcam, "response padding != 0");
2893265edafSLaurent Pinchart 		ret = -EIO;
29066c36070SLaurent Pinchart 		goto done;
2913265edafSLaurent Pinchart 	}
2923265edafSLaurent Pinchart 
2933265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
2943265edafSLaurent Pinchart 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
2953265edafSLaurent Pinchart 
296c6622216SLaurent Pinchart 		streams[i].width = le16_to_cpu(cfg->width);
297c6622216SLaurent Pinchart 		streams[i].height = le16_to_cpu(cfg->height);
298c6622216SLaurent Pinchart 		streams[i].format = le16_to_cpu(cfg->format);
2993265edafSLaurent Pinchart 		streams[i].vc = cfg->virtual_channel;
3003265edafSLaurent Pinchart 		streams[i].dt[0] = cfg->data_type[0];
3013265edafSLaurent Pinchart 		streams[i].dt[1] = cfg->data_type[1];
302c6622216SLaurent Pinchart 		streams[i].max_size = le32_to_cpu(cfg->max_size);
3033265edafSLaurent Pinchart 
3043265edafSLaurent Pinchart 		if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) {
3053265edafSLaurent Pinchart 			gcam_dbg(gcam, "stream #%u padding != 0", i);
3063265edafSLaurent Pinchart 			ret = -EIO;
30766c36070SLaurent Pinchart 			goto done;
3083265edafSLaurent Pinchart 		}
3093265edafSLaurent Pinchart 	}
3103265edafSLaurent Pinchart 
311640924d2SLaurent Pinchart 	if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) ||
312640924d2SLaurent Pinchart 	    (*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) {
313f121d79dSJacopo Mondi 		*flags = resp->flags;
314f121d79dSJacopo Mondi 		*num_streams = resp->num_streams;
31566c36070SLaurent Pinchart 		goto done;
31666c36070SLaurent Pinchart 	}
31766c36070SLaurent Pinchart 
31866c36070SLaurent Pinchart 	/* Setup unipro link speed. */
319640924d2SLaurent Pinchart 	ret = gb_camera_set_power_mode(gcam, nstreams != 0);
32066c36070SLaurent Pinchart 	if (ret < 0)
32166c36070SLaurent Pinchart 		goto done;
322ed4596e9SGreg Kroah-Hartman 
323446091c9SJacopo Mondi 	/*
324446091c9SJacopo Mondi 	 * Configure the APB1 CSI transmitter using the lines count reported by
325446091c9SJacopo Mondi 	 * the  camera module, but with hard-coded bus frequency and lanes number.
326446091c9SJacopo Mondi 	 *
327446091c9SJacopo Mondi 	 * TODO: use the clocking and size informations reported by camera module
328446091c9SJacopo Mondi 	 * to compute the required CSI bandwidth, and configure the CSI receiver
329446091c9SJacopo Mondi 	 * on AP side, and the CSI transmitter on APB1 side accordingly.
330446091c9SJacopo Mondi 	 */
331640924d2SLaurent Pinchart 	memset(&csi_cfg, 0, sizeof(csi_cfg));
332640924d2SLaurent Pinchart 
333f121d79dSJacopo Mondi 	if (nstreams) {
334142b21feSLaurent Pinchart 		csi_cfg.csi_id = 1;
33527e18d8cSJacopo Mondi 		csi_cfg.flags = 0;
336698bdcbfSEvgeniy Borisov 		csi_cfg.num_lanes = resp->num_lanes;
337ed4596e9SGreg Kroah-Hartman 		csi_cfg.bus_freq = cpu_to_le32(960000000);
338446091c9SJacopo Mondi 		csi_cfg.lines_per_second = resp->lines_per_second;
339ed4596e9SGreg Kroah-Hartman 		ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
340e5273381SGreg Kroah-Hartman 				   sizeof(csi_cfg),
341e5273381SGreg Kroah-Hartman 				   GB_APB_REQUEST_CSI_TX_CONTROL, false);
342b4905038SEvgeniy Borisov 		if (csi_params) {
343b4905038SEvgeniy Borisov 			csi_params->num_lanes = csi_cfg.num_lanes;
344b4905038SEvgeniy Borisov 			/* Transmitting two bits per cycle. (DDR clock) */
345b4905038SEvgeniy Borisov 			csi_params->clk_freq = csi_cfg.bus_freq / 2;
346b4905038SEvgeniy Borisov 			csi_params->lines_per_second = csi_cfg.lines_per_second;
347b4905038SEvgeniy Borisov 		}
348f121d79dSJacopo Mondi 	} else {
349142b21feSLaurent Pinchart 		csi_cfg.csi_id = 1;
350ed4596e9SGreg Kroah-Hartman 		ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
351e5273381SGreg Kroah-Hartman 				   sizeof(csi_cfg),
352e5273381SGreg Kroah-Hartman 				   GB_APB_REQUEST_CSI_TX_CONTROL, false);
353142b21feSLaurent Pinchart 	}
354142b21feSLaurent Pinchart 
355142b21feSLaurent Pinchart 	if (ret < 0)
356142b21feSLaurent Pinchart 		gcam_err(gcam, "failed to %s the CSI transmitter\n",
357142b21feSLaurent Pinchart 			 nstreams ? "start" : "stop");
358142b21feSLaurent Pinchart 
359f121d79dSJacopo Mondi 	*flags = resp->flags;
3604068487cSLaurent Pinchart 	*num_streams = resp->num_streams;
3614068487cSLaurent Pinchart 	ret = 0;
3623265edafSLaurent Pinchart 
3633265edafSLaurent Pinchart done:
36417ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
3653265edafSLaurent Pinchart 	kfree(req);
3663265edafSLaurent Pinchart 	kfree(resp);
3673265edafSLaurent Pinchart 	return ret;
3683265edafSLaurent Pinchart }
3693265edafSLaurent Pinchart 
3703265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id,
3713265edafSLaurent Pinchart 			     unsigned int streams, unsigned int num_frames,
3723265edafSLaurent Pinchart 			     size_t settings_size, const void *settings)
3733265edafSLaurent Pinchart {
3743265edafSLaurent Pinchart 	struct gb_camera_capture_request *req;
3753265edafSLaurent Pinchart 	size_t req_size;
376b9f71bc8SJohan Hovold 	int ret;
3773265edafSLaurent Pinchart 
3783265edafSLaurent Pinchart 	if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE)
3793265edafSLaurent Pinchart 		return -EINVAL;
3803265edafSLaurent Pinchart 
3813265edafSLaurent Pinchart 	req_size = sizeof(*req) + settings_size;
3823265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
3833265edafSLaurent Pinchart 	if (!req)
3843265edafSLaurent Pinchart 		return -ENOMEM;
3853265edafSLaurent Pinchart 
386c6622216SLaurent Pinchart 	req->request_id = cpu_to_le32(request_id);
3873265edafSLaurent Pinchart 	req->streams = streams;
3883265edafSLaurent Pinchart 	req->padding = 0;
389c6622216SLaurent Pinchart 	req->num_frames = cpu_to_le16(num_frames);
3903265edafSLaurent Pinchart 	memcpy(req->settings, settings, settings_size);
3913265edafSLaurent Pinchart 
39217ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
39317ca6770SEvgeniy Borisov 
39417ca6770SEvgeniy Borisov 	if (!gcam->connection) {
39517ca6770SEvgeniy Borisov 		ret = -EINVAL;
39617ca6770SEvgeniy Borisov 		goto done;
39717ca6770SEvgeniy Borisov 	}
39817ca6770SEvgeniy Borisov 
399b9f71bc8SJohan Hovold 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE,
4003265edafSLaurent Pinchart 				req, req_size, NULL, 0);
40117ca6770SEvgeniy Borisov done:
40217ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
403b9f71bc8SJohan Hovold 
404b9f71bc8SJohan Hovold 	kfree(req);
405b9f71bc8SJohan Hovold 
406b9f71bc8SJohan Hovold 	return ret;
4073265edafSLaurent Pinchart }
4083265edafSLaurent Pinchart 
4093265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id)
4103265edafSLaurent Pinchart {
4113265edafSLaurent Pinchart 	struct gb_camera_flush_response resp;
4123265edafSLaurent Pinchart 	int ret;
4133265edafSLaurent Pinchart 
41417ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
41517ca6770SEvgeniy Borisov 
41617ca6770SEvgeniy Borisov 	if (!gcam->connection) {
41717ca6770SEvgeniy Borisov 		ret = -EINVAL;
41817ca6770SEvgeniy Borisov 		goto done;
41917ca6770SEvgeniy Borisov 	}
42017ca6770SEvgeniy Borisov 
4213265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0,
4223265edafSLaurent Pinchart 				&resp, sizeof(resp));
42317ca6770SEvgeniy Borisov 
4243265edafSLaurent Pinchart 	if (ret < 0)
42517ca6770SEvgeniy Borisov 		goto done;
4263265edafSLaurent Pinchart 
4273265edafSLaurent Pinchart 	if (request_id)
428c6622216SLaurent Pinchart 		*request_id = le32_to_cpu(resp.request_id);
4293265edafSLaurent Pinchart 
43017ca6770SEvgeniy Borisov done:
43117ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
43217ca6770SEvgeniy Borisov 
43317ca6770SEvgeniy Borisov 	return ret;
4343265edafSLaurent Pinchart }
4353265edafSLaurent Pinchart 
43668b66c28SLaurent Pinchart static int gb_camera_request_handler(struct gb_operation *op)
4373265edafSLaurent Pinchart {
4380ec30632SGreg Kroah-Hartman 	struct gb_camera *gcam = gb_connection_get_data(op->connection);
4393265edafSLaurent Pinchart 	struct gb_camera_metadata_request *payload;
4403265edafSLaurent Pinchart 	struct gb_message *request;
4413265edafSLaurent Pinchart 
44268b66c28SLaurent Pinchart 	if (op->type != GB_CAMERA_TYPE_METADATA) {
44368b66c28SLaurent Pinchart 		gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type);
4443265edafSLaurent Pinchart 		return -EINVAL;
4453265edafSLaurent Pinchart 	}
4463265edafSLaurent Pinchart 
4473265edafSLaurent Pinchart 	request = op->request;
4483265edafSLaurent Pinchart 
4493265edafSLaurent Pinchart 	if (request->payload_size < sizeof(*payload)) {
4503265edafSLaurent Pinchart 		gcam_err(gcam, "Wrong event size received (%zu < %zu)\n",
4513265edafSLaurent Pinchart 			 request->payload_size, sizeof(*payload));
4523265edafSLaurent Pinchart 		return -EINVAL;
4533265edafSLaurent Pinchart 	}
4543265edafSLaurent Pinchart 
4553265edafSLaurent Pinchart 	payload = request->payload;
4563265edafSLaurent Pinchart 
4573265edafSLaurent Pinchart 	gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n",
4583265edafSLaurent Pinchart 		 payload->request_id, payload->frame_number, payload->stream);
4593265edafSLaurent Pinchart 
4603265edafSLaurent Pinchart 	return 0;
4613265edafSLaurent Pinchart }
4623265edafSLaurent Pinchart 
4633265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
4643a8dba4eSGjorgji Rosikopulos  * Interface with HOST ara camera.
4653a8dba4eSGjorgji Rosikopulos  */
4663a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code)
4673a8dba4eSGjorgji Rosikopulos {
4683a8dba4eSGjorgji Rosikopulos 	unsigned int i;
4693a8dba4eSGjorgji Rosikopulos 
4703a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) {
4713a8dba4eSGjorgji Rosikopulos 		if (mbus_to_gbus_format[i].mbus_code == mbus_code)
4723a8dba4eSGjorgji Rosikopulos 			return mbus_to_gbus_format[i].gb_format;
4733a8dba4eSGjorgji Rosikopulos 	}
4743a8dba4eSGjorgji Rosikopulos 	return mbus_to_gbus_format[0].gb_format;
4753a8dba4eSGjorgji Rosikopulos }
4763a8dba4eSGjorgji Rosikopulos 
4773a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt)
4783a8dba4eSGjorgji Rosikopulos {
4793a8dba4eSGjorgji Rosikopulos 	unsigned int i;
4803a8dba4eSGjorgji Rosikopulos 
4813a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) {
4823a8dba4eSGjorgji Rosikopulos 		if (mbus_to_gbus_format[i].gb_format == gb_fmt)
4833a8dba4eSGjorgji Rosikopulos 			return mbus_to_gbus_format[i].mbus_code;
4843a8dba4eSGjorgji Rosikopulos 	}
4853a8dba4eSGjorgji Rosikopulos 	return mbus_to_gbus_format[0].mbus_code;
4863a8dba4eSGjorgji Rosikopulos }
4873a8dba4eSGjorgji Rosikopulos 
488a883b0ebSJacopo Mondi static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len)
489a883b0ebSJacopo Mondi {
490a883b0ebSJacopo Mondi 	struct gb_camera *gcam = priv;
491a883b0ebSJacopo Mondi 	size_t capabilities_len = len;
492a883b0ebSJacopo Mondi 	int ret;
493a883b0ebSJacopo Mondi 
494a883b0ebSJacopo Mondi 	ret = gb_camera_capabilities(gcam, data, &capabilities_len);
495a883b0ebSJacopo Mondi 	if (ret)
496a883b0ebSJacopo Mondi 		return ret;
497a883b0ebSJacopo Mondi 
498a883b0ebSJacopo Mondi 	return capabilities_len;
499a883b0ebSJacopo Mondi }
500a883b0ebSJacopo Mondi 
5015b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams,
502b4905038SEvgeniy Borisov 		unsigned int *flags, struct gb_camera_stream *streams,
503b4905038SEvgeniy Borisov 		struct gb_camera_csi_params *csi_params)
5043a8dba4eSGjorgji Rosikopulos {
5053a8dba4eSGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
5063a8dba4eSGjorgji Rosikopulos 	struct gb_camera_stream_config *gb_streams;
5075b032710SGjorgji Rosikopulos 	unsigned int gb_flags = 0;
5085b032710SGjorgji Rosikopulos 	unsigned int gb_nstreams = *nstreams;
5093a8dba4eSGjorgji Rosikopulos 	unsigned int i;
5103a8dba4eSGjorgji Rosikopulos 	int ret;
5113a8dba4eSGjorgji Rosikopulos 
5125b032710SGjorgji Rosikopulos 	if (gb_nstreams > GB_CAMERA_MAX_STREAMS)
5133a8dba4eSGjorgji Rosikopulos 		return -EINVAL;
5143a8dba4eSGjorgji Rosikopulos 
5155b032710SGjorgji Rosikopulos 	gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL);
5163a8dba4eSGjorgji Rosikopulos 	if (!gb_streams)
5173a8dba4eSGjorgji Rosikopulos 		return -ENOMEM;
5183a8dba4eSGjorgji Rosikopulos 
5195b032710SGjorgji Rosikopulos 	for (i = 0; i < gb_nstreams; i++) {
5203a8dba4eSGjorgji Rosikopulos 		gb_streams[i].width = streams[i].width;
5213a8dba4eSGjorgji Rosikopulos 		gb_streams[i].height = streams[i].height;
5223a8dba4eSGjorgji Rosikopulos 		gb_streams[i].format =
5233a8dba4eSGjorgji Rosikopulos 			gb_camera_mbus_to_gb(streams[i].pixel_code);
5243a8dba4eSGjorgji Rosikopulos 	}
5253a8dba4eSGjorgji Rosikopulos 
5265b032710SGjorgji Rosikopulos 	if (*flags & GB_CAMERA_IN_FLAG_TEST)
5275b032710SGjorgji Rosikopulos 		gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY;
5285b032710SGjorgji Rosikopulos 
5295b032710SGjorgji Rosikopulos 	ret = gb_camera_configure_streams(gcam, &gb_nstreams,
530b4905038SEvgeniy Borisov 					  &gb_flags, gb_streams, csi_params);
5313a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
5323a8dba4eSGjorgji Rosikopulos 		goto done;
5335b032710SGjorgji Rosikopulos 	if (gb_nstreams > *nstreams) {
5345b032710SGjorgji Rosikopulos 		ret = -EINVAL;
5355b032710SGjorgji Rosikopulos 		goto done;
5365b032710SGjorgji Rosikopulos 	}
5373a8dba4eSGjorgji Rosikopulos 
5385b032710SGjorgji Rosikopulos 	*flags = 0;
5395b032710SGjorgji Rosikopulos 	if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)
5405b032710SGjorgji Rosikopulos 		*flags |= GB_CAMERA_OUT_FLAG_ADJUSTED;
5415b032710SGjorgji Rosikopulos 
5425b032710SGjorgji Rosikopulos 	for (i = 0; i < gb_nstreams; i++) {
5433a8dba4eSGjorgji Rosikopulos 		streams[i].width = gb_streams[i].width;
5443a8dba4eSGjorgji Rosikopulos 		streams[i].height = gb_streams[i].height;
5453a8dba4eSGjorgji Rosikopulos 		streams[i].vc = gb_streams[i].vc;
5463a8dba4eSGjorgji Rosikopulos 		streams[i].dt[0] = gb_streams[i].dt[0];
5473a8dba4eSGjorgji Rosikopulos 		streams[i].dt[1] = gb_streams[i].dt[1];
5483a8dba4eSGjorgji Rosikopulos 		streams[i].max_size = gb_streams[i].max_size;
5493a8dba4eSGjorgji Rosikopulos 		streams[i].pixel_code =
5503a8dba4eSGjorgji Rosikopulos 			gb_camera_gb_to_mbus(gb_streams[i].format);
5513a8dba4eSGjorgji Rosikopulos 	}
5525b032710SGjorgji Rosikopulos 	*nstreams = gb_nstreams;
5533a8dba4eSGjorgji Rosikopulos 
5543a8dba4eSGjorgji Rosikopulos done:
5553a8dba4eSGjorgji Rosikopulos 	kfree(gb_streams);
5563a8dba4eSGjorgji Rosikopulos 	return ret;
5573a8dba4eSGjorgji Rosikopulos }
5583a8dba4eSGjorgji Rosikopulos 
5593a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id,
5603a8dba4eSGjorgji Rosikopulos 		unsigned int streams, unsigned int num_frames,
5613a8dba4eSGjorgji Rosikopulos 		size_t settings_size, const void *settings)
5623a8dba4eSGjorgji Rosikopulos {
5631472ec67SGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
5641472ec67SGjorgji Rosikopulos 
5651472ec67SGjorgji Rosikopulos 	return gb_camera_capture(gcam, request_id, streams, num_frames,
5663a8dba4eSGjorgji Rosikopulos 				 settings_size, settings);
5673a8dba4eSGjorgji Rosikopulos }
5683a8dba4eSGjorgji Rosikopulos 
5693a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id)
5703a8dba4eSGjorgji Rosikopulos {
5711472ec67SGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
5721472ec67SGjorgji Rosikopulos 
5731472ec67SGjorgji Rosikopulos 	return gb_camera_flush(gcam, request_id);
5743a8dba4eSGjorgji Rosikopulos }
5753a8dba4eSGjorgji Rosikopulos 
5761472ec67SGjorgji Rosikopulos static const struct gb_camera_ops gb_cam_ops = {
5771472ec67SGjorgji Rosikopulos 	.capabilities = gb_camera_op_capabilities,
5781472ec67SGjorgji Rosikopulos 	.configure_streams = gb_camera_op_configure_streams,
5791472ec67SGjorgji Rosikopulos 	.capture = gb_camera_op_capture,
5801472ec67SGjorgji Rosikopulos 	.flush = gb_camera_op_flush,
5811472ec67SGjorgji Rosikopulos };
5821472ec67SGjorgji Rosikopulos 
5833a8dba4eSGjorgji Rosikopulos /* -----------------------------------------------------------------------------
5843265edafSLaurent Pinchart  * DebugFS
5853265edafSLaurent Pinchart  */
58648b15a9bSLaurent Pinchart 
5873265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam,
5883265edafSLaurent Pinchart 		char *buf, size_t len)
5893265edafSLaurent Pinchart {
59048b15a9bSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
59148b15a9bSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES];
59248b15a9bSLaurent Pinchart 	size_t size = 1024;
59348b15a9bSLaurent Pinchart 	unsigned int i;
59448b15a9bSLaurent Pinchart 	u8 *caps;
59548b15a9bSLaurent Pinchart 	int ret;
59648b15a9bSLaurent Pinchart 
59748b15a9bSLaurent Pinchart 	caps = kmalloc(size, GFP_KERNEL);
59848b15a9bSLaurent Pinchart 	if (!caps)
59948b15a9bSLaurent Pinchart 		return -ENOMEM;
60048b15a9bSLaurent Pinchart 
60148b15a9bSLaurent Pinchart 	ret = gb_camera_capabilities(gcam, caps, &size);
60248b15a9bSLaurent Pinchart 	if (ret < 0)
60348b15a9bSLaurent Pinchart 		goto done;
60448b15a9bSLaurent Pinchart 
60548b15a9bSLaurent Pinchart 	/*
60648b15a9bSLaurent Pinchart 	 * hex_dump_to_buffer() doesn't return the number of bytes dumped prior
60748b15a9bSLaurent Pinchart 	 * to v4.0, we need our own implementation :-(
60848b15a9bSLaurent Pinchart 	 */
60948b15a9bSLaurent Pinchart 	buffer->length = 0;
61048b15a9bSLaurent Pinchart 
61148b15a9bSLaurent Pinchart 	for (i = 0; i < size; i += 16) {
61248b15a9bSLaurent Pinchart 		unsigned int nbytes = min_t(unsigned int, size - i, 16);
61348b15a9bSLaurent Pinchart 
61448b15a9bSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
61548b15a9bSLaurent Pinchart 					  "%*ph\n", nbytes, caps + i);
61648b15a9bSLaurent Pinchart 	}
61748b15a9bSLaurent Pinchart 
61848b15a9bSLaurent Pinchart done:
61948b15a9bSLaurent Pinchart 	kfree(caps);
62048b15a9bSLaurent Pinchart 	return ret;
6213265edafSLaurent Pinchart }
6223265edafSLaurent Pinchart 
6233265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam,
6243265edafSLaurent Pinchart 		char *buf, size_t len)
6253265edafSLaurent Pinchart {
6263265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
6273265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS];
6283265edafSLaurent Pinchart 	struct gb_camera_stream_config *streams;
6293265edafSLaurent Pinchart 	unsigned int nstreams;
630b787d413SJacopo Mondi 	unsigned int flags;
6313265edafSLaurent Pinchart 	unsigned int i;
6323265edafSLaurent Pinchart 	char *token;
6333265edafSLaurent Pinchart 	int ret;
6343265edafSLaurent Pinchart 
6353265edafSLaurent Pinchart 	/* Retrieve number of streams to configure */
636b787d413SJacopo Mondi 	token = strsep(&buf, ";");
6373265edafSLaurent Pinchart 	if (token == NULL)
6383265edafSLaurent Pinchart 		return -EINVAL;
6393265edafSLaurent Pinchart 
6403265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &nstreams);
6413265edafSLaurent Pinchart 	if (ret < 0)
6423265edafSLaurent Pinchart 		return ret;
6433265edafSLaurent Pinchart 
6443265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
6453265edafSLaurent Pinchart 		return -EINVAL;
6463265edafSLaurent Pinchart 
647b787d413SJacopo Mondi 	token = strsep(&buf, ";");
648b787d413SJacopo Mondi 	if (token == NULL)
649b787d413SJacopo Mondi 		return -EINVAL;
650b787d413SJacopo Mondi 
651b787d413SJacopo Mondi 	ret = kstrtouint(token, 10, &flags);
652b787d413SJacopo Mondi 	if (ret < 0)
653b787d413SJacopo Mondi 		return ret;
654b787d413SJacopo Mondi 
6553265edafSLaurent Pinchart 	/* For each stream to configure parse width, height and format */
6563265edafSLaurent Pinchart 	streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL);
6573265edafSLaurent Pinchart 	if (!streams)
6583265edafSLaurent Pinchart 		return -ENOMEM;
6593265edafSLaurent Pinchart 
6603265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
6613265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
6623265edafSLaurent Pinchart 
6633265edafSLaurent Pinchart 		/* width */
6643265edafSLaurent Pinchart 		token = strsep(&buf, ";");
6653265edafSLaurent Pinchart 		if (token == NULL) {
6663265edafSLaurent Pinchart 			ret = -EINVAL;
6673265edafSLaurent Pinchart 			goto done;
6683265edafSLaurent Pinchart 		}
6693265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->width);
6703265edafSLaurent Pinchart 		if (ret < 0)
6713265edafSLaurent Pinchart 			goto done;
6723265edafSLaurent Pinchart 
6733265edafSLaurent Pinchart 		/* height */
6743265edafSLaurent Pinchart 		token = strsep(&buf, ";");
6753265edafSLaurent Pinchart 		if (token == NULL)
6763265edafSLaurent Pinchart 			goto done;
6773265edafSLaurent Pinchart 
6783265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->height);
6793265edafSLaurent Pinchart 		if (ret < 0)
6803265edafSLaurent Pinchart 			goto done;
6813265edafSLaurent Pinchart 
6823265edafSLaurent Pinchart 		/* Image format code */
6833265edafSLaurent Pinchart 		token = strsep(&buf, ";");
6843265edafSLaurent Pinchart 		if (token == NULL)
6853265edafSLaurent Pinchart 			goto done;
6863265edafSLaurent Pinchart 
6873265edafSLaurent Pinchart 		ret = kstrtouint(token, 16, &stream->format);
6883265edafSLaurent Pinchart 		if (ret < 0)
6893265edafSLaurent Pinchart 			goto done;
6903265edafSLaurent Pinchart 	}
6913265edafSLaurent Pinchart 
692b4905038SEvgeniy Borisov 	ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams,
693b4905038SEvgeniy Borisov 					  NULL);
6943265edafSLaurent Pinchart 	if (ret < 0)
6953265edafSLaurent Pinchart 		goto done;
6963265edafSLaurent Pinchart 
6974068487cSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags);
6983265edafSLaurent Pinchart 
6993265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
7003265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
7013265edafSLaurent Pinchart 
7023265edafSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
7033265edafSLaurent Pinchart 					  "%u;%u;%u;%u;%u;%u;%u;",
7043265edafSLaurent Pinchart 					  stream->width, stream->height,
7053265edafSLaurent Pinchart 					  stream->format, stream->vc,
7063265edafSLaurent Pinchart 					  stream->dt[0], stream->dt[1],
7073265edafSLaurent Pinchart 					  stream->max_size);
7083265edafSLaurent Pinchart 	}
7093265edafSLaurent Pinchart 
7103265edafSLaurent Pinchart 	ret = len;
7113265edafSLaurent Pinchart 
7123265edafSLaurent Pinchart done:
7133265edafSLaurent Pinchart 	kfree(streams);
7143265edafSLaurent Pinchart 	return ret;
7153265edafSLaurent Pinchart };
7163265edafSLaurent Pinchart 
7173265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam,
7183265edafSLaurent Pinchart 		char *buf, size_t len)
7193265edafSLaurent Pinchart {
7203265edafSLaurent Pinchart 	unsigned int request_id;
7213265edafSLaurent Pinchart 	unsigned int streams_mask;
7223265edafSLaurent Pinchart 	unsigned int num_frames;
7233265edafSLaurent Pinchart 	char *token;
7243265edafSLaurent Pinchart 	int ret;
7253265edafSLaurent Pinchart 
7263265edafSLaurent Pinchart 	/* Request id */
7273265edafSLaurent Pinchart 	token = strsep(&buf, ";");
7283265edafSLaurent Pinchart 	if (token == NULL)
7293265edafSLaurent Pinchart 		return -EINVAL;
7303265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &request_id);
7313265edafSLaurent Pinchart 	if (ret < 0)
7323265edafSLaurent Pinchart 		return ret;
7333265edafSLaurent Pinchart 
7343265edafSLaurent Pinchart 	/* Stream mask */
7353265edafSLaurent Pinchart 	token = strsep(&buf, ";");
7363265edafSLaurent Pinchart 	if (token == NULL)
7373265edafSLaurent Pinchart 		return -EINVAL;
7383265edafSLaurent Pinchart 	ret = kstrtouint(token, 16, &streams_mask);
7393265edafSLaurent Pinchart 	if (ret < 0)
7403265edafSLaurent Pinchart 		return ret;
7413265edafSLaurent Pinchart 
7423265edafSLaurent Pinchart 	/* number of frames */
7433265edafSLaurent Pinchart 	token = strsep(&buf, ";");
7443265edafSLaurent Pinchart 	if (token == NULL)
7453265edafSLaurent Pinchart 		return -EINVAL;
7463265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &num_frames);
7473265edafSLaurent Pinchart 	if (ret < 0)
7483265edafSLaurent Pinchart 		return ret;
7493265edafSLaurent Pinchart 
7503265edafSLaurent Pinchart 	ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0,
7513265edafSLaurent Pinchart 				NULL);
7523265edafSLaurent Pinchart 	if (ret < 0)
7533265edafSLaurent Pinchart 		return ret;
7543265edafSLaurent Pinchart 
7553265edafSLaurent Pinchart 	return len;
7563265edafSLaurent Pinchart }
7573265edafSLaurent Pinchart 
7583265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam,
7593265edafSLaurent Pinchart 		char *buf, size_t len)
7603265edafSLaurent Pinchart {
7613265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
7623265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH];
7633265edafSLaurent Pinchart 	unsigned int req_id;
7643265edafSLaurent Pinchart 	int ret;
7653265edafSLaurent Pinchart 
7663265edafSLaurent Pinchart 	ret = gb_camera_flush(gcam, &req_id);
7673265edafSLaurent Pinchart 	if (ret < 0)
7683265edafSLaurent Pinchart 		return ret;
7693265edafSLaurent Pinchart 
7703265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u", req_id);
7713265edafSLaurent Pinchart 
7723265edafSLaurent Pinchart 	return len;
7733265edafSLaurent Pinchart }
7743265edafSLaurent Pinchart 
7753265edafSLaurent Pinchart struct gb_camera_debugfs_entry {
7763265edafSLaurent Pinchart 	const char *name;
7773265edafSLaurent Pinchart 	unsigned int mask;
7783265edafSLaurent Pinchart 	unsigned int buffer;
7793265edafSLaurent Pinchart 	ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len);
7803265edafSLaurent Pinchart };
7813265edafSLaurent Pinchart 
7823265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = {
7833265edafSLaurent Pinchart 	{
7843265edafSLaurent Pinchart 		.name = "capabilities",
7853265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO,
7863265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
7873265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capabilities,
7883265edafSLaurent Pinchart 	}, {
7893265edafSLaurent Pinchart 		.name = "configure_streams",
7903265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
7913265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
7923265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_configure_streams,
7933265edafSLaurent Pinchart 	}, {
7943265edafSLaurent Pinchart 		.name = "capture",
7953265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
7963265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
7973265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capture,
7983265edafSLaurent Pinchart 	}, {
7993265edafSLaurent Pinchart 		.name = "flush",
8003265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
8013265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
8023265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_flush,
8033265edafSLaurent Pinchart 	},
8043265edafSLaurent Pinchart };
8053265edafSLaurent Pinchart 
8063265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf,
8073265edafSLaurent Pinchart 				      size_t len, loff_t *offset)
8083265edafSLaurent Pinchart {
8093265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
8103265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
8113265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer;
8123265edafSLaurent Pinchart 	ssize_t ret;
8133265edafSLaurent Pinchart 
8143265edafSLaurent Pinchart 	/* For read-only entries the operation is triggered by a read. */
8153265edafSLaurent Pinchart 	if (!(op->mask & S_IWUGO)) {
8163265edafSLaurent Pinchart 		ret = op->execute(gcam, NULL, 0);
8173265edafSLaurent Pinchart 		if (ret < 0)
8183265edafSLaurent Pinchart 			return ret;
8193265edafSLaurent Pinchart 	}
8203265edafSLaurent Pinchart 
8213265edafSLaurent Pinchart 	buffer = &gcam->debugfs.buffers[op->buffer];
8223265edafSLaurent Pinchart 
8233265edafSLaurent Pinchart 	return simple_read_from_buffer(buf, len, offset, buffer->data,
8243265edafSLaurent Pinchart 				       buffer->length);
8253265edafSLaurent Pinchart }
8263265edafSLaurent Pinchart 
8273265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file,
8283265edafSLaurent Pinchart 				       const char __user *buf, size_t len,
8293265edafSLaurent Pinchart 				       loff_t *offset)
8303265edafSLaurent Pinchart {
8313265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
8323265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
8333265edafSLaurent Pinchart 	ssize_t ret;
8343265edafSLaurent Pinchart 	char *kbuf;
8353265edafSLaurent Pinchart 
8363265edafSLaurent Pinchart 	if (len > 1024)
8373265edafSLaurent Pinchart 	       return -EINVAL;
8383265edafSLaurent Pinchart 
8393265edafSLaurent Pinchart 	kbuf = kmalloc(len + 1, GFP_KERNEL);
8403265edafSLaurent Pinchart 	if (kbuf == NULL)
8413265edafSLaurent Pinchart 		return -ENOMEM;
8423265edafSLaurent Pinchart 
8433265edafSLaurent Pinchart 	if (copy_from_user(kbuf, buf, len)) {
8443265edafSLaurent Pinchart 		ret = -EFAULT;
8453265edafSLaurent Pinchart 		goto done;
8463265edafSLaurent Pinchart 	}
8473265edafSLaurent Pinchart 
8483265edafSLaurent Pinchart 	kbuf[len] = '\0';
8493265edafSLaurent Pinchart 
8503265edafSLaurent Pinchart 	ret = op->execute(gcam, kbuf, len);
8513265edafSLaurent Pinchart 
8523265edafSLaurent Pinchart done:
8533265edafSLaurent Pinchart 	kfree(kbuf);
8543265edafSLaurent Pinchart 	return ret;
8553265edafSLaurent Pinchart }
8563265edafSLaurent Pinchart 
8573265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file)
8583265edafSLaurent Pinchart {
8593265edafSLaurent Pinchart 	unsigned int i;
8603265edafSLaurent Pinchart 
8613265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
8623265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
8633265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
8643265edafSLaurent Pinchart 
8654dda744cSGreg Kroah-Hartman 		if (!strcmp(file->f_path.dentry->d_iname, entry->name)) {
8663265edafSLaurent Pinchart 			file->private_data = (void *)entry;
8673265edafSLaurent Pinchart 			break;
8683265edafSLaurent Pinchart 		}
8693265edafSLaurent Pinchart 	}
8703265edafSLaurent Pinchart 
8713265edafSLaurent Pinchart 	return 0;
8723265edafSLaurent Pinchart }
8733265edafSLaurent Pinchart 
8743265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = {
8753265edafSLaurent Pinchart 	.open = gb_camera_debugfs_open,
8763265edafSLaurent Pinchart 	.read = gb_camera_debugfs_read,
8773265edafSLaurent Pinchart 	.write = gb_camera_debugfs_write,
8783265edafSLaurent Pinchart };
8793265edafSLaurent Pinchart 
8803265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam)
8813265edafSLaurent Pinchart {
8823265edafSLaurent Pinchart 	struct gb_connection *connection = gcam->connection;
8833265edafSLaurent Pinchart 	char dirname[27];
8843265edafSLaurent Pinchart 	unsigned int i;
8853265edafSLaurent Pinchart 
8863265edafSLaurent Pinchart 	/*
8873265edafSLaurent Pinchart 	 * Create root debugfs entry and a file entry for each camera operation.
8883265edafSLaurent Pinchart 	 */
8893265edafSLaurent Pinchart 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
89068b66c28SLaurent Pinchart 		 gcam->bundle->id);
8913265edafSLaurent Pinchart 
8923265edafSLaurent Pinchart 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
8933265edafSLaurent Pinchart 	if (IS_ERR(gcam->debugfs.root)) {
8943265edafSLaurent Pinchart 		gcam_err(gcam, "debugfs root create failed (%ld)\n",
8953265edafSLaurent Pinchart 			 PTR_ERR(gcam->debugfs.root));
8963265edafSLaurent Pinchart 		return PTR_ERR(gcam->debugfs.root);
8973265edafSLaurent Pinchart 	}
8983265edafSLaurent Pinchart 
8993265edafSLaurent Pinchart 	gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) *
9003265edafSLaurent Pinchart 					GB_CAMERA_DEBUGFS_BUFFER_MAX);
9013265edafSLaurent Pinchart 	if (!gcam->debugfs.buffers)
9023265edafSLaurent Pinchart 		return -ENOMEM;
9033265edafSLaurent Pinchart 
9043265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
9053265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
9063265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
9073265edafSLaurent Pinchart 		struct dentry *dentry;
9083265edafSLaurent Pinchart 
9093265edafSLaurent Pinchart 		gcam->debugfs.buffers[i].length = 0;
9103265edafSLaurent Pinchart 
9113265edafSLaurent Pinchart 		dentry = debugfs_create_file(entry->name, entry->mask,
9123265edafSLaurent Pinchart 					     gcam->debugfs.root, gcam,
9133265edafSLaurent Pinchart 					     &gb_camera_debugfs_ops);
9143265edafSLaurent Pinchart 		if (IS_ERR(dentry)) {
9153265edafSLaurent Pinchart 			gcam_err(gcam,
9163265edafSLaurent Pinchart 				 "debugfs operation %s create failed (%ld)\n",
917f9340fc7SAlex Elder 				 entry->name, PTR_ERR(dentry));
9183265edafSLaurent Pinchart 			return PTR_ERR(dentry);
9193265edafSLaurent Pinchart 		}
9203265edafSLaurent Pinchart 	}
9213265edafSLaurent Pinchart 
9223265edafSLaurent Pinchart 	return 0;
9233265edafSLaurent Pinchart }
9243265edafSLaurent Pinchart 
9253265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam)
9263265edafSLaurent Pinchart {
9273265edafSLaurent Pinchart 	debugfs_remove_recursive(gcam->debugfs.root);
9283265edafSLaurent Pinchart 
9293265edafSLaurent Pinchart 	vfree(gcam->debugfs.buffers);
9303265edafSLaurent Pinchart }
9313265edafSLaurent Pinchart 
9323265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
9333265edafSLaurent Pinchart  * Init & Cleanup
9343265edafSLaurent Pinchart  */
9353265edafSLaurent Pinchart 
9363265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam)
9373265edafSLaurent Pinchart {
9383265edafSLaurent Pinchart 	gb_camera_debugfs_cleanup(gcam);
9393265edafSLaurent Pinchart 
9403ba9fa5cSJohan Hovold 	if (gcam->data_connection) {
9413ba9fa5cSJohan Hovold 		gb_connection_disable(gcam->data_connection);
9423ba9fa5cSJohan Hovold 		gb_connection_destroy(gcam->data_connection);
94317ca6770SEvgeniy Borisov 		gcam->data_connection = NULL;
9443265edafSLaurent Pinchart 	}
9453265edafSLaurent Pinchart 
94617ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
94768b66c28SLaurent Pinchart 	if (gcam->connection) {
94868b66c28SLaurent Pinchart 		gb_connection_disable(gcam->connection);
94968b66c28SLaurent Pinchart 		gb_connection_destroy(gcam->connection);
95017ca6770SEvgeniy Borisov 		gcam->connection = NULL;
95117ca6770SEvgeniy Borisov 	}
95217ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
95368b66c28SLaurent Pinchart }
95468b66c28SLaurent Pinchart 
95517ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref)
95617ca6770SEvgeniy Borisov {
95717ca6770SEvgeniy Borisov 	struct gb_camera_module *cam_mod =
95817ca6770SEvgeniy Borisov 		container_of(ref, struct gb_camera_module, refcount);
95917ca6770SEvgeniy Borisov 	kfree(cam_mod->priv);
9603265edafSLaurent Pinchart }
9613265edafSLaurent Pinchart 
96268b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle,
96368b66c28SLaurent Pinchart 			   const struct greybus_bundle_id *id)
9643265edafSLaurent Pinchart {
96568b66c28SLaurent Pinchart 	struct gb_connection *conn;
9663265edafSLaurent Pinchart 	struct gb_camera *gcam;
96768b66c28SLaurent Pinchart 	u16 mgmt_cport_id = 0;
96868b66c28SLaurent Pinchart 	u16 data_cport_id = 0;
96968b66c28SLaurent Pinchart 	unsigned int i;
9703265edafSLaurent Pinchart 	int ret;
9713265edafSLaurent Pinchart 
97268b66c28SLaurent Pinchart 	/*
97368b66c28SLaurent Pinchart 	 * The camera bundle must contain exactly two CPorts, one for the
97468b66c28SLaurent Pinchart 	 * camera management protocol and one for the camera data protocol.
97568b66c28SLaurent Pinchart 	 */
97668b66c28SLaurent Pinchart 	if (bundle->num_cports != 2)
97768b66c28SLaurent Pinchart 		return -ENODEV;
97868b66c28SLaurent Pinchart 
97968b66c28SLaurent Pinchart 	for (i = 0; i < bundle->num_cports; ++i) {
98068b66c28SLaurent Pinchart 		struct greybus_descriptor_cport *desc = &bundle->cport_desc[i];
98168b66c28SLaurent Pinchart 
98268b66c28SLaurent Pinchart 		switch (desc->protocol_id) {
98368b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_MGMT:
98468b66c28SLaurent Pinchart 			mgmt_cport_id = le16_to_cpu(desc->id);
98568b66c28SLaurent Pinchart 			break;
98668b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_DATA:
98768b66c28SLaurent Pinchart 			data_cport_id = le16_to_cpu(desc->id);
98868b66c28SLaurent Pinchart 			break;
98968b66c28SLaurent Pinchart 		default:
99068b66c28SLaurent Pinchart 			return -ENODEV;
99168b66c28SLaurent Pinchart 		}
99268b66c28SLaurent Pinchart 	}
99368b66c28SLaurent Pinchart 
99468b66c28SLaurent Pinchart 	if (!mgmt_cport_id || !data_cport_id)
99568b66c28SLaurent Pinchart 		return -ENODEV;
99668b66c28SLaurent Pinchart 
9973265edafSLaurent Pinchart 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
9983265edafSLaurent Pinchart 	if (!gcam)
9993265edafSLaurent Pinchart 		return -ENOMEM;
10003265edafSLaurent Pinchart 
100168b66c28SLaurent Pinchart 	gcam->bundle = bundle;
1002d9e4c4eeSViresh Kumar 	mutex_init(&gcam->mutex);
10033265edafSLaurent Pinchart 
100468b66c28SLaurent Pinchart 	conn = gb_connection_create(bundle, mgmt_cport_id,
100568b66c28SLaurent Pinchart 				    gb_camera_request_handler);
100668b66c28SLaurent Pinchart 	if (IS_ERR(conn)) {
100768b66c28SLaurent Pinchart 		ret = PTR_ERR(conn);
10083ba9fa5cSJohan Hovold 		goto error;
10093ba9fa5cSJohan Hovold 	}
10103ba9fa5cSJohan Hovold 
101168b66c28SLaurent Pinchart 	gcam->connection = conn;
101268b66c28SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
101368b66c28SLaurent Pinchart 
101468b66c28SLaurent Pinchart 	ret = gb_connection_enable(conn);
101568b66c28SLaurent Pinchart 	if (ret)
101668b66c28SLaurent Pinchart 		goto error;
101768b66c28SLaurent Pinchart 
101868b66c28SLaurent Pinchart 	/*
101968b66c28SLaurent Pinchart 	 * Create the data connection between the camera module data CPort and
102068b66c28SLaurent Pinchart 	 * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge.
102168b66c28SLaurent Pinchart 	 */
102268b66c28SLaurent Pinchart 	conn = gb_connection_create_offloaded(bundle, data_cport_id,
102368b66c28SLaurent Pinchart 					      GB_CONNECTION_FLAG_NO_FLOWCTRL |
102468b66c28SLaurent Pinchart 					      GB_CONNECTION_FLAG_CDSI1);
102568b66c28SLaurent Pinchart 	if (IS_ERR(conn)) {
102668b66c28SLaurent Pinchart 		ret = PTR_ERR(conn);
102768b66c28SLaurent Pinchart 		goto error;
102868b66c28SLaurent Pinchart 	}
102968b66c28SLaurent Pinchart 	gcam->data_connection = conn;
103068b66c28SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
103168b66c28SLaurent Pinchart 
103268b66c28SLaurent Pinchart 	ret = gb_connection_enable(conn);
10333ba9fa5cSJohan Hovold 	if (ret)
10343ba9fa5cSJohan Hovold 		goto error;
103541c23958SJohan Hovold 
10363265edafSLaurent Pinchart 	ret = gb_camera_debugfs_init(gcam);
10373265edafSLaurent Pinchart 	if (ret < 0)
10383265edafSLaurent Pinchart 		goto error;
10393265edafSLaurent Pinchart 
104017ca6770SEvgeniy Borisov 	gcam->module.priv = gcam;
104117ca6770SEvgeniy Borisov 	gcam->module.ops = &gb_cam_ops;
104217ca6770SEvgeniy Borisov 	gcam->module.interface_id = gcam->connection->intf->interface_id;
104317ca6770SEvgeniy Borisov 	gcam->module.release = gb_camera_release_module;
104417ca6770SEvgeniy Borisov 	ret = gb_camera_register(&gcam->module);
10453a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
10463a8dba4eSGjorgji Rosikopulos 		goto error;
10473a8dba4eSGjorgji Rosikopulos 
104868b66c28SLaurent Pinchart 	greybus_set_drvdata(bundle, gcam);
104968b66c28SLaurent Pinchart 
10503265edafSLaurent Pinchart 	return 0;
10513265edafSLaurent Pinchart 
10523265edafSLaurent Pinchart error:
10533265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
105417ca6770SEvgeniy Borisov 	kfree(gcam);
10553265edafSLaurent Pinchart 	return ret;
10563265edafSLaurent Pinchart }
10573265edafSLaurent Pinchart 
105868b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle)
10593265edafSLaurent Pinchart {
106068b66c28SLaurent Pinchart 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
10613265edafSLaurent Pinchart 
10623265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
106317ca6770SEvgeniy Borisov 	gb_camera_unregister(&gcam->module);
10643265edafSLaurent Pinchart }
10653265edafSLaurent Pinchart 
106668b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = {
106768b66c28SLaurent Pinchart 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
106868b66c28SLaurent Pinchart 	{ },
10693265edafSLaurent Pinchart };
10703265edafSLaurent Pinchart 
107168b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = {
107268b66c28SLaurent Pinchart 	.name		= "camera",
107368b66c28SLaurent Pinchart 	.probe		= gb_camera_probe,
107468b66c28SLaurent Pinchart 	.disconnect	= gb_camera_disconnect,
107568b66c28SLaurent Pinchart 	.id_table	= gb_camera_id_table,
107668b66c28SLaurent Pinchart };
107768b66c28SLaurent Pinchart 
107868b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver);
10793265edafSLaurent Pinchart 
10803265edafSLaurent Pinchart MODULE_LICENSE("GPL v2");
1081