xref: /openbmc/linux/drivers/staging/greybus/camera.c (revision 3b8ebfeb)
13265edafSLaurent Pinchart /*
23265edafSLaurent Pinchart  * Greybus Camera protocol driver.
33265edafSLaurent Pinchart  *
43265edafSLaurent Pinchart  * Copyright 2015 Google Inc.
53265edafSLaurent Pinchart  * Copyright 2015 Linaro Ltd.
63265edafSLaurent Pinchart  *
73265edafSLaurent Pinchart  * Released under the GPLv2 only.
83265edafSLaurent Pinchart  */
93265edafSLaurent Pinchart 
103265edafSLaurent Pinchart #include <linux/debugfs.h>
113265edafSLaurent Pinchart #include <linux/fs.h>
123265edafSLaurent Pinchart #include <linux/kernel.h>
133265edafSLaurent Pinchart #include <linux/module.h>
143265edafSLaurent Pinchart #include <linux/slab.h>
153265edafSLaurent Pinchart #include <linux/string.h>
163265edafSLaurent Pinchart #include <linux/uaccess.h>
173265edafSLaurent Pinchart #include <linux/vmalloc.h>
183265edafSLaurent Pinchart 
193a8dba4eSGjorgji Rosikopulos #include "gb-camera.h"
203265edafSLaurent Pinchart #include "greybus.h"
213265edafSLaurent Pinchart #include "greybus_protocols.h"
223265edafSLaurent Pinchart 
233265edafSLaurent Pinchart enum gb_camera_debugs_buffer_id {
243265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
253265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
263265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
273265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
283265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_MAX,
293265edafSLaurent Pinchart };
303265edafSLaurent Pinchart 
313265edafSLaurent Pinchart struct gb_camera_debugfs_buffer {
323265edafSLaurent Pinchart 	char data[PAGE_SIZE];
333265edafSLaurent Pinchart 	size_t length;
343265edafSLaurent Pinchart };
353265edafSLaurent Pinchart 
363b8ebfebSLaurent Pinchart enum gb_camera_state {
373b8ebfebSLaurent Pinchart 	GB_CAMERA_STATE_UNCONFIGURED,
383b8ebfebSLaurent Pinchart 	GB_CAMERA_STATE_CONFIGURED,
393b8ebfebSLaurent Pinchart };
403b8ebfebSLaurent Pinchart 
413265edafSLaurent Pinchart /**
423265edafSLaurent Pinchart  * struct gb_camera - A Greybus Camera Device
4317ca6770SEvgeniy Borisov  * @connection: the greybus connection for camera management
4417ca6770SEvgeniy Borisov  * @data_connection: the greybus connection for camera data
453b8ebfebSLaurent Pinchart  * @mutex: protects the connection and state fields
463b8ebfebSLaurent Pinchart  * @state: the current module state
473265edafSLaurent Pinchart  * @debugfs: debugfs entries for camera protocol operations testing
48c3d77f71SGjorgji Rosikopulos  * @module: Greybus camera module registered to HOST processor.
493265edafSLaurent Pinchart  */
503265edafSLaurent Pinchart struct gb_camera {
5168b66c28SLaurent Pinchart 	struct gb_bundle *bundle;
523265edafSLaurent Pinchart 	struct gb_connection *connection;
533ba9fa5cSJohan Hovold 	struct gb_connection *data_connection;
543b8ebfebSLaurent Pinchart 
5517ca6770SEvgeniy Borisov 	struct mutex mutex;
563b8ebfebSLaurent Pinchart 	enum gb_camera_state state;
573265edafSLaurent Pinchart 
583265edafSLaurent Pinchart 	struct {
593265edafSLaurent Pinchart 		struct dentry *root;
603265edafSLaurent Pinchart 		struct gb_camera_debugfs_buffer *buffers;
613265edafSLaurent Pinchart 	} debugfs;
62c3d77f71SGjorgji Rosikopulos 
63c3d77f71SGjorgji Rosikopulos 	struct gb_camera_module module;
643265edafSLaurent Pinchart };
653265edafSLaurent Pinchart 
663265edafSLaurent Pinchart struct gb_camera_stream_config {
673265edafSLaurent Pinchart 	unsigned int width;
683265edafSLaurent Pinchart 	unsigned int height;
693265edafSLaurent Pinchart 	unsigned int format;
703265edafSLaurent Pinchart 	unsigned int vc;
713265edafSLaurent Pinchart 	unsigned int dt[2];
723265edafSLaurent Pinchart 	unsigned int max_size;
733265edafSLaurent Pinchart };
743265edafSLaurent Pinchart 
753a8dba4eSGjorgji Rosikopulos struct gb_camera_fmt_map {
763a8dba4eSGjorgji Rosikopulos 	enum v4l2_mbus_pixelcode mbus_code;
773a8dba4eSGjorgji Rosikopulos 	unsigned int gb_format;
783a8dba4eSGjorgji Rosikopulos };
793a8dba4eSGjorgji Rosikopulos 
803a8dba4eSGjorgji Rosikopulos /* GB format to media code map */
813a8dba4eSGjorgji Rosikopulos static const struct gb_camera_fmt_map mbus_to_gbus_format[] = {
823a8dba4eSGjorgji Rosikopulos 	{
833a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
843a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x01,
853a8dba4eSGjorgji Rosikopulos 	},
863a8dba4eSGjorgji Rosikopulos 	{
877c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV12_1x8,
887c154711SGjorgji Rosikopulos 		.gb_format = 0x12,
897c154711SGjorgji Rosikopulos 	},
907c154711SGjorgji Rosikopulos 	{
917c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV21_1x8,
927c154711SGjorgji Rosikopulos 		.gb_format = 0x13,
937c154711SGjorgji Rosikopulos 	},
947c154711SGjorgji Rosikopulos 	{
957c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YU12_1x8,
963a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x16,
973a8dba4eSGjorgji Rosikopulos 	},
983a8dba4eSGjorgji Rosikopulos 	{
997c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YV12_1x8,
1003a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x17,
1013a8dba4eSGjorgji Rosikopulos 	},
1023a8dba4eSGjorgji Rosikopulos 	{
1033a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
1043a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x40,
105dc5cc72cSGjorgji Rosikopulos 	},
106dc5cc72cSGjorgji Rosikopulos 	{
107dc5cc72cSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8,
108dc5cc72cSGjorgji Rosikopulos 		.gb_format = 0x41,
109cb14e976SGjorgji Rosikopulos 	},
110cb14e976SGjorgji Rosikopulos 	{
111cb14e976SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8,
112cb14e976SGjorgji Rosikopulos 		.gb_format = 0x42,
113cb14e976SGjorgji Rosikopulos 	},
1143a8dba4eSGjorgji Rosikopulos };
1153a8dba4eSGjorgji Rosikopulos 
1163265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT		16
1173265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT		17
1183265edafSLaurent Pinchart 
1193265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
1203265edafSLaurent Pinchart 
12168b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...)	dev_dbg(&gcam->bundle->dev, format)
12268b66c28SLaurent Pinchart #define gcam_info(gcam, format...)	dev_info(&gcam->bundle->dev, format)
12368b66c28SLaurent Pinchart #define gcam_err(gcam, format...)	dev_err(&gcam->bundle->dev, format)
1243265edafSLaurent Pinchart 
1253265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
126f3d5f661SLaurent Pinchart  * Hardware Configuration
1273265edafSLaurent Pinchart  */
1283265edafSLaurent Pinchart 
129c161c0fcSLaurent Pinchart static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id,
130c161c0fcSLaurent Pinchart 					 bool hs)
131c161c0fcSLaurent Pinchart {
132c161c0fcSLaurent Pinchart 	struct gb_svc *svc = gcam->connection->hd->svc;
133c161c0fcSLaurent Pinchart 	int ret;
134c161c0fcSLaurent Pinchart 
135c161c0fcSLaurent Pinchart 	if (hs)
136c161c0fcSLaurent Pinchart 		ret = gb_svc_intf_set_power_mode(svc, intf_id,
137c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_HS_SERIES_A,
138c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
1398c2522d8SEli Sennesh 						 GB_SVC_SMALL_AMPLITUDE,
1408c2522d8SEli Sennesh 						 GB_SVC_NO_DE_EMPHASIS,
141c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
142c161c0fcSLaurent Pinchart 						 GB_SVC_PWRM_RXTERMINATION |
1438c2522d8SEli Sennesh 						 GB_SVC_PWRM_TXTERMINATION, 0,
1448c2522d8SEli Sennesh 						 NULL, NULL);
145c161c0fcSLaurent Pinchart 	else
146c161c0fcSLaurent Pinchart 		ret = gb_svc_intf_set_power_mode(svc, intf_id,
147c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_HS_SERIES_A,
148c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_SLOW_AUTO_MODE,
149ee2f2074SMitchell Tasman 						 2, 1,
1508c2522d8SEli Sennesh 						 GB_SVC_SMALL_AMPLITUDE,
1518c2522d8SEli Sennesh 						 GB_SVC_NO_DE_EMPHASIS,
152c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_SLOW_AUTO_MODE,
153ee2f2074SMitchell Tasman 						 2, 1,
1548c2522d8SEli Sennesh 						 0, 0,
1558c2522d8SEli Sennesh 						 NULL, NULL);
156c161c0fcSLaurent Pinchart 
157c161c0fcSLaurent Pinchart 	return ret;
158c161c0fcSLaurent Pinchart }
159c161c0fcSLaurent Pinchart 
160c161c0fcSLaurent Pinchart static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs)
161c161c0fcSLaurent Pinchart {
162c161c0fcSLaurent Pinchart 	struct gb_interface *intf = gcam->connection->intf;
163c161c0fcSLaurent Pinchart 	struct gb_svc *svc = gcam->connection->hd->svc;
164c161c0fcSLaurent Pinchart 	int ret;
165c161c0fcSLaurent Pinchart 
166c161c0fcSLaurent Pinchart 	ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs);
167c161c0fcSLaurent Pinchart 	if (ret < 0) {
168c161c0fcSLaurent Pinchart 		gcam_err(gcam, "failed to set module interface to %s (%d)\n",
169c161c0fcSLaurent Pinchart 			 hs ? "HS" : "PWM", ret);
170c161c0fcSLaurent Pinchart 		return ret;
171c161c0fcSLaurent Pinchart 	}
172c161c0fcSLaurent Pinchart 
173c161c0fcSLaurent Pinchart 	ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs);
174c161c0fcSLaurent Pinchart 	if (ret < 0) {
175b573b0e6SLaurent Pinchart 		gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs);
176c161c0fcSLaurent Pinchart 		gcam_err(gcam, "failed to set AP interface to %s (%d)\n",
177c161c0fcSLaurent Pinchart 			 hs ? "HS" : "PWM", ret);
178c161c0fcSLaurent Pinchart 		return ret;
179c161c0fcSLaurent Pinchart 	}
180c161c0fcSLaurent Pinchart 
181c161c0fcSLaurent Pinchart 	return 0;
182c161c0fcSLaurent Pinchart }
183c161c0fcSLaurent Pinchart 
184f3d5f661SLaurent Pinchart struct ap_csi_config_request {
185f3d5f661SLaurent Pinchart 	__u8 csi_id;
186f3d5f661SLaurent Pinchart 	__u8 flags;
187f3d5f661SLaurent Pinchart #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
188f3d5f661SLaurent Pinchart 	__u8 num_lanes;
189f3d5f661SLaurent Pinchart 	__u8 padding;
190f3d5f661SLaurent Pinchart 	__le32 bus_freq;
191f3d5f661SLaurent Pinchart 	__le32 lines_per_second;
192f3d5f661SLaurent Pinchart } __packed;
193f3d5f661SLaurent Pinchart 
194f3d5f661SLaurent Pinchart static int gb_camera_setup_data_connection(struct gb_camera *gcam,
195f3d5f661SLaurent Pinchart 		const struct gb_camera_configure_streams_response *resp,
196f3d5f661SLaurent Pinchart 		struct gb_camera_csi_params *csi_params)
197f3d5f661SLaurent Pinchart {
198f3d5f661SLaurent Pinchart 	struct ap_csi_config_request csi_cfg;
199f3d5f661SLaurent Pinchart 	int ret;
200f3d5f661SLaurent Pinchart 
201f3d5f661SLaurent Pinchart 	/* Set the UniPro link to high speed mode. */
202f3d5f661SLaurent Pinchart 	ret = gb_camera_set_power_mode(gcam, true);
203f3d5f661SLaurent Pinchart 	if (ret < 0)
204f3d5f661SLaurent Pinchart 		return ret;
205f3d5f661SLaurent Pinchart 
206f3d5f661SLaurent Pinchart 	/*
207f3d5f661SLaurent Pinchart 	 * Configure the APB1 CSI transmitter using the lines count reported by
208f3d5f661SLaurent Pinchart 	 * the  camera module, but with hard-coded bus frequency and lanes number.
209f3d5f661SLaurent Pinchart 	 *
210f3d5f661SLaurent Pinchart 	 * TODO: use the clocking and size informations reported by camera module
211f3d5f661SLaurent Pinchart 	 * to compute the required CSI bandwidth, and configure the CSI receiver
212f3d5f661SLaurent Pinchart 	 * on AP side, and the CSI transmitter on APB1 side accordingly.
213f3d5f661SLaurent Pinchart 	 */
214f3d5f661SLaurent Pinchart 	memset(&csi_cfg, 0, sizeof(csi_cfg));
215f3d5f661SLaurent Pinchart 	csi_cfg.csi_id = 1;
216f3d5f661SLaurent Pinchart 	csi_cfg.flags = 0;
217f3d5f661SLaurent Pinchart 	csi_cfg.num_lanes = resp->num_lanes;
218f3d5f661SLaurent Pinchart 	csi_cfg.bus_freq = cpu_to_le32(960000000);
219f3d5f661SLaurent Pinchart 	csi_cfg.lines_per_second = resp->lines_per_second;
220f3d5f661SLaurent Pinchart 
221f3d5f661SLaurent Pinchart 	ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
222f3d5f661SLaurent Pinchart 			   sizeof(csi_cfg),
223f3d5f661SLaurent Pinchart 			   GB_APB_REQUEST_CSI_TX_CONTROL, false);
224f3d5f661SLaurent Pinchart 
225f3d5f661SLaurent Pinchart 	if (ret < 0) {
226f3d5f661SLaurent Pinchart 		gcam_err(gcam, "failed to start the CSI transmitter\n");
227f3d5f661SLaurent Pinchart 		gb_camera_set_power_mode(gcam, false);
228f3d5f661SLaurent Pinchart 		return ret;
229f3d5f661SLaurent Pinchart 	}
230f3d5f661SLaurent Pinchart 
231f3d5f661SLaurent Pinchart 	if (csi_params) {
232f3d5f661SLaurent Pinchart 		csi_params->num_lanes = csi_cfg.num_lanes;
233f3d5f661SLaurent Pinchart 		/* Transmitting two bits per cycle. (DDR clock) */
234f3d5f661SLaurent Pinchart 		csi_params->clk_freq = csi_cfg.bus_freq / 2;
235f3d5f661SLaurent Pinchart 		csi_params->lines_per_second = csi_cfg.lines_per_second;
236f3d5f661SLaurent Pinchart 	}
237f3d5f661SLaurent Pinchart 
238f3d5f661SLaurent Pinchart 	return 0;
239f3d5f661SLaurent Pinchart }
240f3d5f661SLaurent Pinchart 
241f3d5f661SLaurent Pinchart static void gb_camera_teardown_data_connection(struct gb_camera *gcam)
242f3d5f661SLaurent Pinchart {
243f3d5f661SLaurent Pinchart 	struct ap_csi_config_request csi_cfg;
244f3d5f661SLaurent Pinchart 	int ret;
245f3d5f661SLaurent Pinchart 
246f3d5f661SLaurent Pinchart 	/* Stop the APB1 CSI transmitter. */
247f3d5f661SLaurent Pinchart 	memset(&csi_cfg, 0, sizeof(csi_cfg));
248f3d5f661SLaurent Pinchart 	csi_cfg.csi_id = 1;
249f3d5f661SLaurent Pinchart 
250f3d5f661SLaurent Pinchart 	ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
251f3d5f661SLaurent Pinchart 			   sizeof(csi_cfg),
252f3d5f661SLaurent Pinchart 			   GB_APB_REQUEST_CSI_TX_CONTROL, false);
253f3d5f661SLaurent Pinchart 
254f3d5f661SLaurent Pinchart 	if (ret < 0)
255f3d5f661SLaurent Pinchart 		gcam_err(gcam, "failed to stop the CSI transmitter\n");
256f3d5f661SLaurent Pinchart 
257f3d5f661SLaurent Pinchart 	/* Set the UniPro link to low speed mode. */
258f3d5f661SLaurent Pinchart 	gb_camera_set_power_mode(gcam, false);
259f3d5f661SLaurent Pinchart }
260f3d5f661SLaurent Pinchart 
261f3d5f661SLaurent Pinchart /* -----------------------------------------------------------------------------
262f3d5f661SLaurent Pinchart  * Camera Protocol Operations
263f3d5f661SLaurent Pinchart  */
264f3d5f661SLaurent Pinchart 
26548b15a9bSLaurent Pinchart static int gb_camera_capabilities(struct gb_camera *gcam,
26648b15a9bSLaurent Pinchart 				  u8 *capabilities, size_t *size)
26748b15a9bSLaurent Pinchart {
26817ca6770SEvgeniy Borisov 	struct gb_operation *op = NULL;
26948b15a9bSLaurent Pinchart 	int ret;
27048b15a9bSLaurent Pinchart 
27117ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
27217ca6770SEvgeniy Borisov 
27317ca6770SEvgeniy Borisov 	if (!gcam->connection) {
27417ca6770SEvgeniy Borisov 		ret = -EINVAL;
27517ca6770SEvgeniy Borisov 		goto done;
27617ca6770SEvgeniy Borisov 	}
27717ca6770SEvgeniy Borisov 
27848b15a9bSLaurent Pinchart 	op = gb_operation_create_flags(gcam->connection,
27948b15a9bSLaurent Pinchart 				       GB_CAMERA_TYPE_CAPABILITIES, 0, *size,
28048b15a9bSLaurent Pinchart 				       GB_OPERATION_FLAG_SHORT_RESPONSE,
28148b15a9bSLaurent Pinchart 				       GFP_KERNEL);
28217ca6770SEvgeniy Borisov 	if (!op) {
28317ca6770SEvgeniy Borisov 		ret = -ENOMEM;
28417ca6770SEvgeniy Borisov 		goto done;
28517ca6770SEvgeniy Borisov 	}
28648b15a9bSLaurent Pinchart 
28748b15a9bSLaurent Pinchart 	ret = gb_operation_request_send_sync(op);
28848b15a9bSLaurent Pinchart 	if (ret) {
28948b15a9bSLaurent Pinchart 		gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret);
29048b15a9bSLaurent Pinchart 		goto done;
29148b15a9bSLaurent Pinchart 	}
29248b15a9bSLaurent Pinchart 
29348b15a9bSLaurent Pinchart 	memcpy(capabilities, op->response->payload, op->response->payload_size);
29448b15a9bSLaurent Pinchart 	*size = op->response->payload_size;
29548b15a9bSLaurent Pinchart 
29648b15a9bSLaurent Pinchart done:
29717ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
29817ca6770SEvgeniy Borisov 	if (op)
29948b15a9bSLaurent Pinchart 		gb_operation_put(op);
30048b15a9bSLaurent Pinchart 	return ret;
30148b15a9bSLaurent Pinchart }
30248b15a9bSLaurent Pinchart 
3033265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam,
3044068487cSLaurent Pinchart 				       unsigned int *num_streams,
3054068487cSLaurent Pinchart 				       unsigned int *flags,
306b4905038SEvgeniy Borisov 				       struct gb_camera_stream_config *streams,
307b4905038SEvgeniy Borisov 				       struct gb_camera_csi_params *csi_params)
3083265edafSLaurent Pinchart {
3093265edafSLaurent Pinchart 	struct gb_camera_configure_streams_request *req;
3103265edafSLaurent Pinchart 	struct gb_camera_configure_streams_response *resp;
3114068487cSLaurent Pinchart 	unsigned int nstreams = *num_streams;
3123265edafSLaurent Pinchart 	unsigned int i;
3133265edafSLaurent Pinchart 	size_t req_size;
3143265edafSLaurent Pinchart 	size_t resp_size;
3153265edafSLaurent Pinchart 	int ret;
3163265edafSLaurent Pinchart 
3173265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
3183265edafSLaurent Pinchart 		return -EINVAL;
3193265edafSLaurent Pinchart 
3203265edafSLaurent Pinchart 	req_size = sizeof(*req) + nstreams * sizeof(req->config[0]);
3213265edafSLaurent Pinchart 	resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]);
3223265edafSLaurent Pinchart 
3233265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
3243265edafSLaurent Pinchart 	resp = kmalloc(resp_size, GFP_KERNEL);
3253265edafSLaurent Pinchart 	if (!req || !resp) {
32617ca6770SEvgeniy Borisov 		kfree(req);
32717ca6770SEvgeniy Borisov 		kfree(resp);
32817ca6770SEvgeniy Borisov 		return -ENOMEM;
3293265edafSLaurent Pinchart 	}
3303265edafSLaurent Pinchart 
331b787d413SJacopo Mondi 	req->num_streams = nstreams;
3324068487cSLaurent Pinchart 	req->flags = *flags;
3333265edafSLaurent Pinchart 	req->padding = 0;
3343265edafSLaurent Pinchart 
3353265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
3363265edafSLaurent Pinchart 		struct gb_camera_stream_config_request *cfg = &req->config[i];
3373265edafSLaurent Pinchart 
338c6622216SLaurent Pinchart 		cfg->width = cpu_to_le16(streams[i].width);
339c6622216SLaurent Pinchart 		cfg->height = cpu_to_le16(streams[i].height);
340c6622216SLaurent Pinchart 		cfg->format = cpu_to_le16(streams[i].format);
3413265edafSLaurent Pinchart 		cfg->padding = 0;
3423265edafSLaurent Pinchart 	}
3433265edafSLaurent Pinchart 
34417ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
34517ca6770SEvgeniy Borisov 
34617ca6770SEvgeniy Borisov 	if (!gcam->connection) {
34717ca6770SEvgeniy Borisov 		ret = -EINVAL;
34817ca6770SEvgeniy Borisov 		goto done;
34917ca6770SEvgeniy Borisov 	}
35017ca6770SEvgeniy Borisov 
3513265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection,
3523265edafSLaurent Pinchart 				GB_CAMERA_TYPE_CONFIGURE_STREAMS,
3533265edafSLaurent Pinchart 				req, req_size, resp, resp_size);
3543265edafSLaurent Pinchart 	if (ret < 0)
35566c36070SLaurent Pinchart 		goto done;
3563265edafSLaurent Pinchart 
357b787d413SJacopo Mondi 	if (resp->num_streams > nstreams) {
3583265edafSLaurent Pinchart 		gcam_dbg(gcam, "got #streams %u > request %u\n",
359b787d413SJacopo Mondi 			 resp->num_streams, nstreams);
3603265edafSLaurent Pinchart 		ret = -EIO;
36166c36070SLaurent Pinchart 		goto done;
3623265edafSLaurent Pinchart 	}
3633265edafSLaurent Pinchart 
3643265edafSLaurent Pinchart 	if (resp->padding != 0) {
3653265edafSLaurent Pinchart 		gcam_dbg(gcam, "response padding != 0");
3663265edafSLaurent Pinchart 		ret = -EIO;
36766c36070SLaurent Pinchart 		goto done;
3683265edafSLaurent Pinchart 	}
3693265edafSLaurent Pinchart 
3703265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
3713265edafSLaurent Pinchart 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
3723265edafSLaurent Pinchart 
373c6622216SLaurent Pinchart 		streams[i].width = le16_to_cpu(cfg->width);
374c6622216SLaurent Pinchart 		streams[i].height = le16_to_cpu(cfg->height);
375c6622216SLaurent Pinchart 		streams[i].format = le16_to_cpu(cfg->format);
3763265edafSLaurent Pinchart 		streams[i].vc = cfg->virtual_channel;
3773265edafSLaurent Pinchart 		streams[i].dt[0] = cfg->data_type[0];
3783265edafSLaurent Pinchart 		streams[i].dt[1] = cfg->data_type[1];
379c6622216SLaurent Pinchart 		streams[i].max_size = le32_to_cpu(cfg->max_size);
3803265edafSLaurent Pinchart 
3813265edafSLaurent Pinchart 		if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) {
3823265edafSLaurent Pinchart 			gcam_dbg(gcam, "stream #%u padding != 0", i);
3833265edafSLaurent Pinchart 			ret = -EIO;
38466c36070SLaurent Pinchart 			goto done;
3853265edafSLaurent Pinchart 		}
3863265edafSLaurent Pinchart 	}
3873265edafSLaurent Pinchart 
388640924d2SLaurent Pinchart 	if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) ||
389640924d2SLaurent Pinchart 	    (*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) {
390f121d79dSJacopo Mondi 		*flags = resp->flags;
391f121d79dSJacopo Mondi 		*num_streams = resp->num_streams;
39266c36070SLaurent Pinchart 		goto done;
39366c36070SLaurent Pinchart 	}
39466c36070SLaurent Pinchart 
3953b8ebfebSLaurent Pinchart 	if (gcam->state == GB_CAMERA_STATE_CONFIGURED) {
3963b8ebfebSLaurent Pinchart 		gb_camera_teardown_data_connection(gcam);
3973b8ebfebSLaurent Pinchart 		gcam->state = GB_CAMERA_STATE_UNCONFIGURED;
3983b8ebfebSLaurent Pinchart 	}
3993b8ebfebSLaurent Pinchart 
400f3d5f661SLaurent Pinchart 	if (resp->num_streams) {
401f3d5f661SLaurent Pinchart 		ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
402f3d5f661SLaurent Pinchart 		if (ret < 0) {
403f3d5f661SLaurent Pinchart 			memset(req, 0, sizeof(*req));
404f3d5f661SLaurent Pinchart 			gb_operation_sync(gcam->connection,
405f3d5f661SLaurent Pinchart 					  GB_CAMERA_TYPE_CONFIGURE_STREAMS,
406f3d5f661SLaurent Pinchart 					  req, req_size, resp, resp_size);
40766c36070SLaurent Pinchart 			goto done;
408b4905038SEvgeniy Borisov 		}
4093b8ebfebSLaurent Pinchart 
4103b8ebfebSLaurent Pinchart 		gcam->state = GB_CAMERA_STATE_CONFIGURED;
411142b21feSLaurent Pinchart 	}
412142b21feSLaurent Pinchart 
413f121d79dSJacopo Mondi 	*flags = resp->flags;
4144068487cSLaurent Pinchart 	*num_streams = resp->num_streams;
4153265edafSLaurent Pinchart 
4163265edafSLaurent Pinchart done:
41717ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
4183265edafSLaurent Pinchart 	kfree(req);
4193265edafSLaurent Pinchart 	kfree(resp);
4203265edafSLaurent Pinchart 	return ret;
4213265edafSLaurent Pinchart }
4223265edafSLaurent Pinchart 
4233265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id,
4243265edafSLaurent Pinchart 			     unsigned int streams, unsigned int num_frames,
4253265edafSLaurent Pinchart 			     size_t settings_size, const void *settings)
4263265edafSLaurent Pinchart {
4273265edafSLaurent Pinchart 	struct gb_camera_capture_request *req;
4283265edafSLaurent Pinchart 	size_t req_size;
429b9f71bc8SJohan Hovold 	int ret;
4303265edafSLaurent Pinchart 
4313265edafSLaurent Pinchart 	if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE)
4323265edafSLaurent Pinchart 		return -EINVAL;
4333265edafSLaurent Pinchart 
4343265edafSLaurent Pinchart 	req_size = sizeof(*req) + settings_size;
4353265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
4363265edafSLaurent Pinchart 	if (!req)
4373265edafSLaurent Pinchart 		return -ENOMEM;
4383265edafSLaurent Pinchart 
439c6622216SLaurent Pinchart 	req->request_id = cpu_to_le32(request_id);
4403265edafSLaurent Pinchart 	req->streams = streams;
4413265edafSLaurent Pinchart 	req->padding = 0;
442c6622216SLaurent Pinchart 	req->num_frames = cpu_to_le16(num_frames);
4433265edafSLaurent Pinchart 	memcpy(req->settings, settings, settings_size);
4443265edafSLaurent Pinchart 
44517ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
44617ca6770SEvgeniy Borisov 
44717ca6770SEvgeniy Borisov 	if (!gcam->connection) {
44817ca6770SEvgeniy Borisov 		ret = -EINVAL;
44917ca6770SEvgeniy Borisov 		goto done;
45017ca6770SEvgeniy Borisov 	}
45117ca6770SEvgeniy Borisov 
452b9f71bc8SJohan Hovold 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE,
4533265edafSLaurent Pinchart 				req, req_size, NULL, 0);
45417ca6770SEvgeniy Borisov done:
45517ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
456b9f71bc8SJohan Hovold 
457b9f71bc8SJohan Hovold 	kfree(req);
458b9f71bc8SJohan Hovold 
459b9f71bc8SJohan Hovold 	return ret;
4603265edafSLaurent Pinchart }
4613265edafSLaurent Pinchart 
4623265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id)
4633265edafSLaurent Pinchart {
4643265edafSLaurent Pinchart 	struct gb_camera_flush_response resp;
4653265edafSLaurent Pinchart 	int ret;
4663265edafSLaurent Pinchart 
46717ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
46817ca6770SEvgeniy Borisov 
46917ca6770SEvgeniy Borisov 	if (!gcam->connection) {
47017ca6770SEvgeniy Borisov 		ret = -EINVAL;
47117ca6770SEvgeniy Borisov 		goto done;
47217ca6770SEvgeniy Borisov 	}
47317ca6770SEvgeniy Borisov 
4743265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0,
4753265edafSLaurent Pinchart 				&resp, sizeof(resp));
47617ca6770SEvgeniy Borisov 
4773265edafSLaurent Pinchart 	if (ret < 0)
47817ca6770SEvgeniy Borisov 		goto done;
4793265edafSLaurent Pinchart 
4803265edafSLaurent Pinchart 	if (request_id)
481c6622216SLaurent Pinchart 		*request_id = le32_to_cpu(resp.request_id);
4823265edafSLaurent Pinchart 
48317ca6770SEvgeniy Borisov done:
48417ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
48517ca6770SEvgeniy Borisov 
48617ca6770SEvgeniy Borisov 	return ret;
4873265edafSLaurent Pinchart }
4883265edafSLaurent Pinchart 
48968b66c28SLaurent Pinchart static int gb_camera_request_handler(struct gb_operation *op)
4903265edafSLaurent Pinchart {
4910ec30632SGreg Kroah-Hartman 	struct gb_camera *gcam = gb_connection_get_data(op->connection);
4923265edafSLaurent Pinchart 	struct gb_camera_metadata_request *payload;
4933265edafSLaurent Pinchart 	struct gb_message *request;
4943265edafSLaurent Pinchart 
49568b66c28SLaurent Pinchart 	if (op->type != GB_CAMERA_TYPE_METADATA) {
49668b66c28SLaurent Pinchart 		gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type);
4973265edafSLaurent Pinchart 		return -EINVAL;
4983265edafSLaurent Pinchart 	}
4993265edafSLaurent Pinchart 
5003265edafSLaurent Pinchart 	request = op->request;
5013265edafSLaurent Pinchart 
5023265edafSLaurent Pinchart 	if (request->payload_size < sizeof(*payload)) {
5033265edafSLaurent Pinchart 		gcam_err(gcam, "Wrong event size received (%zu < %zu)\n",
5043265edafSLaurent Pinchart 			 request->payload_size, sizeof(*payload));
5053265edafSLaurent Pinchart 		return -EINVAL;
5063265edafSLaurent Pinchart 	}
5073265edafSLaurent Pinchart 
5083265edafSLaurent Pinchart 	payload = request->payload;
5093265edafSLaurent Pinchart 
5103265edafSLaurent Pinchart 	gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n",
5113265edafSLaurent Pinchart 		 payload->request_id, payload->frame_number, payload->stream);
5123265edafSLaurent Pinchart 
5133265edafSLaurent Pinchart 	return 0;
5143265edafSLaurent Pinchart }
5153265edafSLaurent Pinchart 
5163265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
5173a8dba4eSGjorgji Rosikopulos  * Interface with HOST ara camera.
5183a8dba4eSGjorgji Rosikopulos  */
5193a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code)
5203a8dba4eSGjorgji Rosikopulos {
5213a8dba4eSGjorgji Rosikopulos 	unsigned int i;
5223a8dba4eSGjorgji Rosikopulos 
5233a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) {
5243a8dba4eSGjorgji Rosikopulos 		if (mbus_to_gbus_format[i].mbus_code == mbus_code)
5253a8dba4eSGjorgji Rosikopulos 			return mbus_to_gbus_format[i].gb_format;
5263a8dba4eSGjorgji Rosikopulos 	}
5273a8dba4eSGjorgji Rosikopulos 	return mbus_to_gbus_format[0].gb_format;
5283a8dba4eSGjorgji Rosikopulos }
5293a8dba4eSGjorgji Rosikopulos 
5303a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt)
5313a8dba4eSGjorgji Rosikopulos {
5323a8dba4eSGjorgji Rosikopulos 	unsigned int i;
5333a8dba4eSGjorgji Rosikopulos 
5343a8dba4eSGjorgji Rosikopulos 	for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) {
5353a8dba4eSGjorgji Rosikopulos 		if (mbus_to_gbus_format[i].gb_format == gb_fmt)
5363a8dba4eSGjorgji Rosikopulos 			return mbus_to_gbus_format[i].mbus_code;
5373a8dba4eSGjorgji Rosikopulos 	}
5383a8dba4eSGjorgji Rosikopulos 	return mbus_to_gbus_format[0].mbus_code;
5393a8dba4eSGjorgji Rosikopulos }
5403a8dba4eSGjorgji Rosikopulos 
541a883b0ebSJacopo Mondi static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len)
542a883b0ebSJacopo Mondi {
543a883b0ebSJacopo Mondi 	struct gb_camera *gcam = priv;
544a883b0ebSJacopo Mondi 	size_t capabilities_len = len;
545a883b0ebSJacopo Mondi 	int ret;
546a883b0ebSJacopo Mondi 
547a883b0ebSJacopo Mondi 	ret = gb_camera_capabilities(gcam, data, &capabilities_len);
548a883b0ebSJacopo Mondi 	if (ret)
549a883b0ebSJacopo Mondi 		return ret;
550a883b0ebSJacopo Mondi 
551a883b0ebSJacopo Mondi 	return capabilities_len;
552a883b0ebSJacopo Mondi }
553a883b0ebSJacopo Mondi 
5545b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams,
555b4905038SEvgeniy Borisov 		unsigned int *flags, struct gb_camera_stream *streams,
556b4905038SEvgeniy Borisov 		struct gb_camera_csi_params *csi_params)
5573a8dba4eSGjorgji Rosikopulos {
5583a8dba4eSGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
5593a8dba4eSGjorgji Rosikopulos 	struct gb_camera_stream_config *gb_streams;
5605b032710SGjorgji Rosikopulos 	unsigned int gb_flags = 0;
5615b032710SGjorgji Rosikopulos 	unsigned int gb_nstreams = *nstreams;
5623a8dba4eSGjorgji Rosikopulos 	unsigned int i;
5633a8dba4eSGjorgji Rosikopulos 	int ret;
5643a8dba4eSGjorgji Rosikopulos 
5655b032710SGjorgji Rosikopulos 	if (gb_nstreams > GB_CAMERA_MAX_STREAMS)
5663a8dba4eSGjorgji Rosikopulos 		return -EINVAL;
5673a8dba4eSGjorgji Rosikopulos 
5685b032710SGjorgji Rosikopulos 	gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL);
5693a8dba4eSGjorgji Rosikopulos 	if (!gb_streams)
5703a8dba4eSGjorgji Rosikopulos 		return -ENOMEM;
5713a8dba4eSGjorgji Rosikopulos 
5725b032710SGjorgji Rosikopulos 	for (i = 0; i < gb_nstreams; i++) {
5733a8dba4eSGjorgji Rosikopulos 		gb_streams[i].width = streams[i].width;
5743a8dba4eSGjorgji Rosikopulos 		gb_streams[i].height = streams[i].height;
5753a8dba4eSGjorgji Rosikopulos 		gb_streams[i].format =
5763a8dba4eSGjorgji Rosikopulos 			gb_camera_mbus_to_gb(streams[i].pixel_code);
5773a8dba4eSGjorgji Rosikopulos 	}
5783a8dba4eSGjorgji Rosikopulos 
5795b032710SGjorgji Rosikopulos 	if (*flags & GB_CAMERA_IN_FLAG_TEST)
5805b032710SGjorgji Rosikopulos 		gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY;
5815b032710SGjorgji Rosikopulos 
5825b032710SGjorgji Rosikopulos 	ret = gb_camera_configure_streams(gcam, &gb_nstreams,
583b4905038SEvgeniy Borisov 					  &gb_flags, gb_streams, csi_params);
5843a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
5853a8dba4eSGjorgji Rosikopulos 		goto done;
5865b032710SGjorgji Rosikopulos 	if (gb_nstreams > *nstreams) {
5875b032710SGjorgji Rosikopulos 		ret = -EINVAL;
5885b032710SGjorgji Rosikopulos 		goto done;
5895b032710SGjorgji Rosikopulos 	}
5903a8dba4eSGjorgji Rosikopulos 
5915b032710SGjorgji Rosikopulos 	*flags = 0;
5925b032710SGjorgji Rosikopulos 	if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)
5935b032710SGjorgji Rosikopulos 		*flags |= GB_CAMERA_OUT_FLAG_ADJUSTED;
5945b032710SGjorgji Rosikopulos 
5955b032710SGjorgji Rosikopulos 	for (i = 0; i < gb_nstreams; i++) {
5963a8dba4eSGjorgji Rosikopulos 		streams[i].width = gb_streams[i].width;
5973a8dba4eSGjorgji Rosikopulos 		streams[i].height = gb_streams[i].height;
5983a8dba4eSGjorgji Rosikopulos 		streams[i].vc = gb_streams[i].vc;
5993a8dba4eSGjorgji Rosikopulos 		streams[i].dt[0] = gb_streams[i].dt[0];
6003a8dba4eSGjorgji Rosikopulos 		streams[i].dt[1] = gb_streams[i].dt[1];
6013a8dba4eSGjorgji Rosikopulos 		streams[i].max_size = gb_streams[i].max_size;
6023a8dba4eSGjorgji Rosikopulos 		streams[i].pixel_code =
6033a8dba4eSGjorgji Rosikopulos 			gb_camera_gb_to_mbus(gb_streams[i].format);
6043a8dba4eSGjorgji Rosikopulos 	}
6055b032710SGjorgji Rosikopulos 	*nstreams = gb_nstreams;
6063a8dba4eSGjorgji Rosikopulos 
6073a8dba4eSGjorgji Rosikopulos done:
6083a8dba4eSGjorgji Rosikopulos 	kfree(gb_streams);
6093a8dba4eSGjorgji Rosikopulos 	return ret;
6103a8dba4eSGjorgji Rosikopulos }
6113a8dba4eSGjorgji Rosikopulos 
6123a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id,
6133a8dba4eSGjorgji Rosikopulos 		unsigned int streams, unsigned int num_frames,
6143a8dba4eSGjorgji Rosikopulos 		size_t settings_size, const void *settings)
6153a8dba4eSGjorgji Rosikopulos {
6161472ec67SGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
6171472ec67SGjorgji Rosikopulos 
6181472ec67SGjorgji Rosikopulos 	return gb_camera_capture(gcam, request_id, streams, num_frames,
6193a8dba4eSGjorgji Rosikopulos 				 settings_size, settings);
6203a8dba4eSGjorgji Rosikopulos }
6213a8dba4eSGjorgji Rosikopulos 
6223a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id)
6233a8dba4eSGjorgji Rosikopulos {
6241472ec67SGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
6251472ec67SGjorgji Rosikopulos 
6261472ec67SGjorgji Rosikopulos 	return gb_camera_flush(gcam, request_id);
6273a8dba4eSGjorgji Rosikopulos }
6283a8dba4eSGjorgji Rosikopulos 
6291472ec67SGjorgji Rosikopulos static const struct gb_camera_ops gb_cam_ops = {
6301472ec67SGjorgji Rosikopulos 	.capabilities = gb_camera_op_capabilities,
6311472ec67SGjorgji Rosikopulos 	.configure_streams = gb_camera_op_configure_streams,
6321472ec67SGjorgji Rosikopulos 	.capture = gb_camera_op_capture,
6331472ec67SGjorgji Rosikopulos 	.flush = gb_camera_op_flush,
6341472ec67SGjorgji Rosikopulos };
6351472ec67SGjorgji Rosikopulos 
6363a8dba4eSGjorgji Rosikopulos /* -----------------------------------------------------------------------------
6373265edafSLaurent Pinchart  * DebugFS
6383265edafSLaurent Pinchart  */
63948b15a9bSLaurent Pinchart 
6403265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam,
6413265edafSLaurent Pinchart 		char *buf, size_t len)
6423265edafSLaurent Pinchart {
64348b15a9bSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
64448b15a9bSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES];
64548b15a9bSLaurent Pinchart 	size_t size = 1024;
64648b15a9bSLaurent Pinchart 	unsigned int i;
64748b15a9bSLaurent Pinchart 	u8 *caps;
64848b15a9bSLaurent Pinchart 	int ret;
64948b15a9bSLaurent Pinchart 
65048b15a9bSLaurent Pinchart 	caps = kmalloc(size, GFP_KERNEL);
65148b15a9bSLaurent Pinchart 	if (!caps)
65248b15a9bSLaurent Pinchart 		return -ENOMEM;
65348b15a9bSLaurent Pinchart 
65448b15a9bSLaurent Pinchart 	ret = gb_camera_capabilities(gcam, caps, &size);
65548b15a9bSLaurent Pinchart 	if (ret < 0)
65648b15a9bSLaurent Pinchart 		goto done;
65748b15a9bSLaurent Pinchart 
65848b15a9bSLaurent Pinchart 	/*
65948b15a9bSLaurent Pinchart 	 * hex_dump_to_buffer() doesn't return the number of bytes dumped prior
66048b15a9bSLaurent Pinchart 	 * to v4.0, we need our own implementation :-(
66148b15a9bSLaurent Pinchart 	 */
66248b15a9bSLaurent Pinchart 	buffer->length = 0;
66348b15a9bSLaurent Pinchart 
66448b15a9bSLaurent Pinchart 	for (i = 0; i < size; i += 16) {
66548b15a9bSLaurent Pinchart 		unsigned int nbytes = min_t(unsigned int, size - i, 16);
66648b15a9bSLaurent Pinchart 
66748b15a9bSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
66848b15a9bSLaurent Pinchart 					  "%*ph\n", nbytes, caps + i);
66948b15a9bSLaurent Pinchart 	}
67048b15a9bSLaurent Pinchart 
67148b15a9bSLaurent Pinchart done:
67248b15a9bSLaurent Pinchart 	kfree(caps);
67348b15a9bSLaurent Pinchart 	return ret;
6743265edafSLaurent Pinchart }
6753265edafSLaurent Pinchart 
6763265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam,
6773265edafSLaurent Pinchart 		char *buf, size_t len)
6783265edafSLaurent Pinchart {
6793265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
6803265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS];
6813265edafSLaurent Pinchart 	struct gb_camera_stream_config *streams;
6823265edafSLaurent Pinchart 	unsigned int nstreams;
683b787d413SJacopo Mondi 	unsigned int flags;
6843265edafSLaurent Pinchart 	unsigned int i;
6853265edafSLaurent Pinchart 	char *token;
6863265edafSLaurent Pinchart 	int ret;
6873265edafSLaurent Pinchart 
6883265edafSLaurent Pinchart 	/* Retrieve number of streams to configure */
689b787d413SJacopo Mondi 	token = strsep(&buf, ";");
6903265edafSLaurent Pinchart 	if (token == NULL)
6913265edafSLaurent Pinchart 		return -EINVAL;
6923265edafSLaurent Pinchart 
6933265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &nstreams);
6943265edafSLaurent Pinchart 	if (ret < 0)
6953265edafSLaurent Pinchart 		return ret;
6963265edafSLaurent Pinchart 
6973265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
6983265edafSLaurent Pinchart 		return -EINVAL;
6993265edafSLaurent Pinchart 
700b787d413SJacopo Mondi 	token = strsep(&buf, ";");
701b787d413SJacopo Mondi 	if (token == NULL)
702b787d413SJacopo Mondi 		return -EINVAL;
703b787d413SJacopo Mondi 
704b787d413SJacopo Mondi 	ret = kstrtouint(token, 10, &flags);
705b787d413SJacopo Mondi 	if (ret < 0)
706b787d413SJacopo Mondi 		return ret;
707b787d413SJacopo Mondi 
7083265edafSLaurent Pinchart 	/* For each stream to configure parse width, height and format */
7093265edafSLaurent Pinchart 	streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL);
7103265edafSLaurent Pinchart 	if (!streams)
7113265edafSLaurent Pinchart 		return -ENOMEM;
7123265edafSLaurent Pinchart 
7133265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
7143265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
7153265edafSLaurent Pinchart 
7163265edafSLaurent Pinchart 		/* width */
7173265edafSLaurent Pinchart 		token = strsep(&buf, ";");
7183265edafSLaurent Pinchart 		if (token == NULL) {
7193265edafSLaurent Pinchart 			ret = -EINVAL;
7203265edafSLaurent Pinchart 			goto done;
7213265edafSLaurent Pinchart 		}
7223265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->width);
7233265edafSLaurent Pinchart 		if (ret < 0)
7243265edafSLaurent Pinchart 			goto done;
7253265edafSLaurent Pinchart 
7263265edafSLaurent Pinchart 		/* height */
7273265edafSLaurent Pinchart 		token = strsep(&buf, ";");
7283265edafSLaurent Pinchart 		if (token == NULL)
7293265edafSLaurent Pinchart 			goto done;
7303265edafSLaurent Pinchart 
7313265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->height);
7323265edafSLaurent Pinchart 		if (ret < 0)
7333265edafSLaurent Pinchart 			goto done;
7343265edafSLaurent Pinchart 
7353265edafSLaurent Pinchart 		/* Image format code */
7363265edafSLaurent Pinchart 		token = strsep(&buf, ";");
7373265edafSLaurent Pinchart 		if (token == NULL)
7383265edafSLaurent Pinchart 			goto done;
7393265edafSLaurent Pinchart 
7403265edafSLaurent Pinchart 		ret = kstrtouint(token, 16, &stream->format);
7413265edafSLaurent Pinchart 		if (ret < 0)
7423265edafSLaurent Pinchart 			goto done;
7433265edafSLaurent Pinchart 	}
7443265edafSLaurent Pinchart 
745b4905038SEvgeniy Borisov 	ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams,
746b4905038SEvgeniy Borisov 					  NULL);
7473265edafSLaurent Pinchart 	if (ret < 0)
7483265edafSLaurent Pinchart 		goto done;
7493265edafSLaurent Pinchart 
7504068487cSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags);
7513265edafSLaurent Pinchart 
7523265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
7533265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
7543265edafSLaurent Pinchart 
7553265edafSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
7563265edafSLaurent Pinchart 					  "%u;%u;%u;%u;%u;%u;%u;",
7573265edafSLaurent Pinchart 					  stream->width, stream->height,
7583265edafSLaurent Pinchart 					  stream->format, stream->vc,
7593265edafSLaurent Pinchart 					  stream->dt[0], stream->dt[1],
7603265edafSLaurent Pinchart 					  stream->max_size);
7613265edafSLaurent Pinchart 	}
7623265edafSLaurent Pinchart 
7633265edafSLaurent Pinchart 	ret = len;
7643265edafSLaurent Pinchart 
7653265edafSLaurent Pinchart done:
7663265edafSLaurent Pinchart 	kfree(streams);
7673265edafSLaurent Pinchart 	return ret;
7683265edafSLaurent Pinchart };
7693265edafSLaurent Pinchart 
7703265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam,
7713265edafSLaurent Pinchart 		char *buf, size_t len)
7723265edafSLaurent Pinchart {
7733265edafSLaurent Pinchart 	unsigned int request_id;
7743265edafSLaurent Pinchart 	unsigned int streams_mask;
7753265edafSLaurent Pinchart 	unsigned int num_frames;
7763265edafSLaurent Pinchart 	char *token;
7773265edafSLaurent Pinchart 	int ret;
7783265edafSLaurent Pinchart 
7793265edafSLaurent Pinchart 	/* Request id */
7803265edafSLaurent Pinchart 	token = strsep(&buf, ";");
7813265edafSLaurent Pinchart 	if (token == NULL)
7823265edafSLaurent Pinchart 		return -EINVAL;
7833265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &request_id);
7843265edafSLaurent Pinchart 	if (ret < 0)
7853265edafSLaurent Pinchart 		return ret;
7863265edafSLaurent Pinchart 
7873265edafSLaurent Pinchart 	/* Stream mask */
7883265edafSLaurent Pinchart 	token = strsep(&buf, ";");
7893265edafSLaurent Pinchart 	if (token == NULL)
7903265edafSLaurent Pinchart 		return -EINVAL;
7913265edafSLaurent Pinchart 	ret = kstrtouint(token, 16, &streams_mask);
7923265edafSLaurent Pinchart 	if (ret < 0)
7933265edafSLaurent Pinchart 		return ret;
7943265edafSLaurent Pinchart 
7953265edafSLaurent Pinchart 	/* number of frames */
7963265edafSLaurent Pinchart 	token = strsep(&buf, ";");
7973265edafSLaurent Pinchart 	if (token == NULL)
7983265edafSLaurent Pinchart 		return -EINVAL;
7993265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &num_frames);
8003265edafSLaurent Pinchart 	if (ret < 0)
8013265edafSLaurent Pinchart 		return ret;
8023265edafSLaurent Pinchart 
8033265edafSLaurent Pinchart 	ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0,
8043265edafSLaurent Pinchart 				NULL);
8053265edafSLaurent Pinchart 	if (ret < 0)
8063265edafSLaurent Pinchart 		return ret;
8073265edafSLaurent Pinchart 
8083265edafSLaurent Pinchart 	return len;
8093265edafSLaurent Pinchart }
8103265edafSLaurent Pinchart 
8113265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam,
8123265edafSLaurent Pinchart 		char *buf, size_t len)
8133265edafSLaurent Pinchart {
8143265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
8153265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH];
8163265edafSLaurent Pinchart 	unsigned int req_id;
8173265edafSLaurent Pinchart 	int ret;
8183265edafSLaurent Pinchart 
8193265edafSLaurent Pinchart 	ret = gb_camera_flush(gcam, &req_id);
8203265edafSLaurent Pinchart 	if (ret < 0)
8213265edafSLaurent Pinchart 		return ret;
8223265edafSLaurent Pinchart 
8233265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u", req_id);
8243265edafSLaurent Pinchart 
8253265edafSLaurent Pinchart 	return len;
8263265edafSLaurent Pinchart }
8273265edafSLaurent Pinchart 
8283265edafSLaurent Pinchart struct gb_camera_debugfs_entry {
8293265edafSLaurent Pinchart 	const char *name;
8303265edafSLaurent Pinchart 	unsigned int mask;
8313265edafSLaurent Pinchart 	unsigned int buffer;
8323265edafSLaurent Pinchart 	ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len);
8333265edafSLaurent Pinchart };
8343265edafSLaurent Pinchart 
8353265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = {
8363265edafSLaurent Pinchart 	{
8373265edafSLaurent Pinchart 		.name = "capabilities",
8383265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO,
8393265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
8403265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capabilities,
8413265edafSLaurent Pinchart 	}, {
8423265edafSLaurent Pinchart 		.name = "configure_streams",
8433265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
8443265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
8453265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_configure_streams,
8463265edafSLaurent Pinchart 	}, {
8473265edafSLaurent Pinchart 		.name = "capture",
8483265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
8493265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
8503265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capture,
8513265edafSLaurent Pinchart 	}, {
8523265edafSLaurent Pinchart 		.name = "flush",
8533265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
8543265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
8553265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_flush,
8563265edafSLaurent Pinchart 	},
8573265edafSLaurent Pinchart };
8583265edafSLaurent Pinchart 
8593265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf,
8603265edafSLaurent Pinchart 				      size_t len, loff_t *offset)
8613265edafSLaurent Pinchart {
8623265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
8633265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
8643265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer;
8653265edafSLaurent Pinchart 	ssize_t ret;
8663265edafSLaurent Pinchart 
8673265edafSLaurent Pinchart 	/* For read-only entries the operation is triggered by a read. */
8683265edafSLaurent Pinchart 	if (!(op->mask & S_IWUGO)) {
8693265edafSLaurent Pinchart 		ret = op->execute(gcam, NULL, 0);
8703265edafSLaurent Pinchart 		if (ret < 0)
8713265edafSLaurent Pinchart 			return ret;
8723265edafSLaurent Pinchart 	}
8733265edafSLaurent Pinchart 
8743265edafSLaurent Pinchart 	buffer = &gcam->debugfs.buffers[op->buffer];
8753265edafSLaurent Pinchart 
8763265edafSLaurent Pinchart 	return simple_read_from_buffer(buf, len, offset, buffer->data,
8773265edafSLaurent Pinchart 				       buffer->length);
8783265edafSLaurent Pinchart }
8793265edafSLaurent Pinchart 
8803265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file,
8813265edafSLaurent Pinchart 				       const char __user *buf, size_t len,
8823265edafSLaurent Pinchart 				       loff_t *offset)
8833265edafSLaurent Pinchart {
8843265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
8853265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
8863265edafSLaurent Pinchart 	ssize_t ret;
8873265edafSLaurent Pinchart 	char *kbuf;
8883265edafSLaurent Pinchart 
8893265edafSLaurent Pinchart 	if (len > 1024)
8903265edafSLaurent Pinchart 	       return -EINVAL;
8913265edafSLaurent Pinchart 
8923265edafSLaurent Pinchart 	kbuf = kmalloc(len + 1, GFP_KERNEL);
8933265edafSLaurent Pinchart 	if (kbuf == NULL)
8943265edafSLaurent Pinchart 		return -ENOMEM;
8953265edafSLaurent Pinchart 
8963265edafSLaurent Pinchart 	if (copy_from_user(kbuf, buf, len)) {
8973265edafSLaurent Pinchart 		ret = -EFAULT;
8983265edafSLaurent Pinchart 		goto done;
8993265edafSLaurent Pinchart 	}
9003265edafSLaurent Pinchart 
9013265edafSLaurent Pinchart 	kbuf[len] = '\0';
9023265edafSLaurent Pinchart 
9033265edafSLaurent Pinchart 	ret = op->execute(gcam, kbuf, len);
9043265edafSLaurent Pinchart 
9053265edafSLaurent Pinchart done:
9063265edafSLaurent Pinchart 	kfree(kbuf);
9073265edafSLaurent Pinchart 	return ret;
9083265edafSLaurent Pinchart }
9093265edafSLaurent Pinchart 
9103265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file)
9113265edafSLaurent Pinchart {
9123265edafSLaurent Pinchart 	unsigned int i;
9133265edafSLaurent Pinchart 
9143265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
9153265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
9163265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
9173265edafSLaurent Pinchart 
9184dda744cSGreg Kroah-Hartman 		if (!strcmp(file->f_path.dentry->d_iname, entry->name)) {
9193265edafSLaurent Pinchart 			file->private_data = (void *)entry;
9203265edafSLaurent Pinchart 			break;
9213265edafSLaurent Pinchart 		}
9223265edafSLaurent Pinchart 	}
9233265edafSLaurent Pinchart 
9243265edafSLaurent Pinchart 	return 0;
9253265edafSLaurent Pinchart }
9263265edafSLaurent Pinchart 
9273265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = {
9283265edafSLaurent Pinchart 	.open = gb_camera_debugfs_open,
9293265edafSLaurent Pinchart 	.read = gb_camera_debugfs_read,
9303265edafSLaurent Pinchart 	.write = gb_camera_debugfs_write,
9313265edafSLaurent Pinchart };
9323265edafSLaurent Pinchart 
9333265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam)
9343265edafSLaurent Pinchart {
9353265edafSLaurent Pinchart 	struct gb_connection *connection = gcam->connection;
9363265edafSLaurent Pinchart 	char dirname[27];
9373265edafSLaurent Pinchart 	unsigned int i;
9383265edafSLaurent Pinchart 
9393265edafSLaurent Pinchart 	/*
9403265edafSLaurent Pinchart 	 * Create root debugfs entry and a file entry for each camera operation.
9413265edafSLaurent Pinchart 	 */
9423265edafSLaurent Pinchart 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
94368b66c28SLaurent Pinchart 		 gcam->bundle->id);
9443265edafSLaurent Pinchart 
9453265edafSLaurent Pinchart 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
9463265edafSLaurent Pinchart 	if (IS_ERR(gcam->debugfs.root)) {
9473265edafSLaurent Pinchart 		gcam_err(gcam, "debugfs root create failed (%ld)\n",
9483265edafSLaurent Pinchart 			 PTR_ERR(gcam->debugfs.root));
9493265edafSLaurent Pinchart 		return PTR_ERR(gcam->debugfs.root);
9503265edafSLaurent Pinchart 	}
9513265edafSLaurent Pinchart 
9523265edafSLaurent Pinchart 	gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) *
9533265edafSLaurent Pinchart 					GB_CAMERA_DEBUGFS_BUFFER_MAX);
9543265edafSLaurent Pinchart 	if (!gcam->debugfs.buffers)
9553265edafSLaurent Pinchart 		return -ENOMEM;
9563265edafSLaurent Pinchart 
9573265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
9583265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
9593265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
9603265edafSLaurent Pinchart 		struct dentry *dentry;
9613265edafSLaurent Pinchart 
9623265edafSLaurent Pinchart 		gcam->debugfs.buffers[i].length = 0;
9633265edafSLaurent Pinchart 
9643265edafSLaurent Pinchart 		dentry = debugfs_create_file(entry->name, entry->mask,
9653265edafSLaurent Pinchart 					     gcam->debugfs.root, gcam,
9663265edafSLaurent Pinchart 					     &gb_camera_debugfs_ops);
9673265edafSLaurent Pinchart 		if (IS_ERR(dentry)) {
9683265edafSLaurent Pinchart 			gcam_err(gcam,
9693265edafSLaurent Pinchart 				 "debugfs operation %s create failed (%ld)\n",
970f9340fc7SAlex Elder 				 entry->name, PTR_ERR(dentry));
9713265edafSLaurent Pinchart 			return PTR_ERR(dentry);
9723265edafSLaurent Pinchart 		}
9733265edafSLaurent Pinchart 	}
9743265edafSLaurent Pinchart 
9753265edafSLaurent Pinchart 	return 0;
9763265edafSLaurent Pinchart }
9773265edafSLaurent Pinchart 
9783265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam)
9793265edafSLaurent Pinchart {
9803265edafSLaurent Pinchart 	debugfs_remove_recursive(gcam->debugfs.root);
9813265edafSLaurent Pinchart 
9823265edafSLaurent Pinchart 	vfree(gcam->debugfs.buffers);
9833265edafSLaurent Pinchart }
9843265edafSLaurent Pinchart 
9853265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
9863265edafSLaurent Pinchart  * Init & Cleanup
9873265edafSLaurent Pinchart  */
9883265edafSLaurent Pinchart 
9893265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam)
9903265edafSLaurent Pinchart {
9913265edafSLaurent Pinchart 	gb_camera_debugfs_cleanup(gcam);
9923265edafSLaurent Pinchart 
9933ba9fa5cSJohan Hovold 	if (gcam->data_connection) {
9943ba9fa5cSJohan Hovold 		gb_connection_disable(gcam->data_connection);
9953ba9fa5cSJohan Hovold 		gb_connection_destroy(gcam->data_connection);
99617ca6770SEvgeniy Borisov 		gcam->data_connection = NULL;
9973265edafSLaurent Pinchart 	}
9983265edafSLaurent Pinchart 
99917ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
100068b66c28SLaurent Pinchart 	if (gcam->connection) {
100168b66c28SLaurent Pinchart 		gb_connection_disable(gcam->connection);
100268b66c28SLaurent Pinchart 		gb_connection_destroy(gcam->connection);
100317ca6770SEvgeniy Borisov 		gcam->connection = NULL;
100417ca6770SEvgeniy Borisov 	}
100517ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
100668b66c28SLaurent Pinchart }
100768b66c28SLaurent Pinchart 
100817ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref)
100917ca6770SEvgeniy Borisov {
101017ca6770SEvgeniy Borisov 	struct gb_camera_module *cam_mod =
101117ca6770SEvgeniy Borisov 		container_of(ref, struct gb_camera_module, refcount);
101217ca6770SEvgeniy Borisov 	kfree(cam_mod->priv);
10133265edafSLaurent Pinchart }
10143265edafSLaurent Pinchart 
101568b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle,
101668b66c28SLaurent Pinchart 			   const struct greybus_bundle_id *id)
10173265edafSLaurent Pinchart {
101868b66c28SLaurent Pinchart 	struct gb_connection *conn;
10193265edafSLaurent Pinchart 	struct gb_camera *gcam;
102068b66c28SLaurent Pinchart 	u16 mgmt_cport_id = 0;
102168b66c28SLaurent Pinchart 	u16 data_cport_id = 0;
102268b66c28SLaurent Pinchart 	unsigned int i;
10233265edafSLaurent Pinchart 	int ret;
10243265edafSLaurent Pinchart 
102568b66c28SLaurent Pinchart 	/*
102668b66c28SLaurent Pinchart 	 * The camera bundle must contain exactly two CPorts, one for the
102768b66c28SLaurent Pinchart 	 * camera management protocol and one for the camera data protocol.
102868b66c28SLaurent Pinchart 	 */
102968b66c28SLaurent Pinchart 	if (bundle->num_cports != 2)
103068b66c28SLaurent Pinchart 		return -ENODEV;
103168b66c28SLaurent Pinchart 
103268b66c28SLaurent Pinchart 	for (i = 0; i < bundle->num_cports; ++i) {
103368b66c28SLaurent Pinchart 		struct greybus_descriptor_cport *desc = &bundle->cport_desc[i];
103468b66c28SLaurent Pinchart 
103568b66c28SLaurent Pinchart 		switch (desc->protocol_id) {
103668b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_MGMT:
103768b66c28SLaurent Pinchart 			mgmt_cport_id = le16_to_cpu(desc->id);
103868b66c28SLaurent Pinchart 			break;
103968b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_DATA:
104068b66c28SLaurent Pinchart 			data_cport_id = le16_to_cpu(desc->id);
104168b66c28SLaurent Pinchart 			break;
104268b66c28SLaurent Pinchart 		default:
104368b66c28SLaurent Pinchart 			return -ENODEV;
104468b66c28SLaurent Pinchart 		}
104568b66c28SLaurent Pinchart 	}
104668b66c28SLaurent Pinchart 
104768b66c28SLaurent Pinchart 	if (!mgmt_cport_id || !data_cport_id)
104868b66c28SLaurent Pinchart 		return -ENODEV;
104968b66c28SLaurent Pinchart 
10503265edafSLaurent Pinchart 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
10513265edafSLaurent Pinchart 	if (!gcam)
10523265edafSLaurent Pinchart 		return -ENOMEM;
10533265edafSLaurent Pinchart 
1054d9e4c4eeSViresh Kumar 	mutex_init(&gcam->mutex);
10553265edafSLaurent Pinchart 
10563b8ebfebSLaurent Pinchart 	gcam->bundle = bundle;
10573b8ebfebSLaurent Pinchart 	gcam->state = GB_CAMERA_STATE_UNCONFIGURED;
10583b8ebfebSLaurent Pinchart 
105968b66c28SLaurent Pinchart 	conn = gb_connection_create(bundle, mgmt_cport_id,
106068b66c28SLaurent Pinchart 				    gb_camera_request_handler);
106168b66c28SLaurent Pinchart 	if (IS_ERR(conn)) {
106268b66c28SLaurent Pinchart 		ret = PTR_ERR(conn);
10633ba9fa5cSJohan Hovold 		goto error;
10643ba9fa5cSJohan Hovold 	}
10653ba9fa5cSJohan Hovold 
106668b66c28SLaurent Pinchart 	gcam->connection = conn;
106768b66c28SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
106868b66c28SLaurent Pinchart 
106968b66c28SLaurent Pinchart 	ret = gb_connection_enable(conn);
107068b66c28SLaurent Pinchart 	if (ret)
107168b66c28SLaurent Pinchart 		goto error;
107268b66c28SLaurent Pinchart 
107368b66c28SLaurent Pinchart 	/*
107468b66c28SLaurent Pinchart 	 * Create the data connection between the camera module data CPort and
107568b66c28SLaurent Pinchart 	 * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge.
107668b66c28SLaurent Pinchart 	 */
107768b66c28SLaurent Pinchart 	conn = gb_connection_create_offloaded(bundle, data_cport_id,
107868b66c28SLaurent Pinchart 					      GB_CONNECTION_FLAG_NO_FLOWCTRL |
107968b66c28SLaurent Pinchart 					      GB_CONNECTION_FLAG_CDSI1);
108068b66c28SLaurent Pinchart 	if (IS_ERR(conn)) {
108168b66c28SLaurent Pinchart 		ret = PTR_ERR(conn);
108268b66c28SLaurent Pinchart 		goto error;
108368b66c28SLaurent Pinchart 	}
108468b66c28SLaurent Pinchart 	gcam->data_connection = conn;
108568b66c28SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
108668b66c28SLaurent Pinchart 
108768b66c28SLaurent Pinchart 	ret = gb_connection_enable(conn);
10883ba9fa5cSJohan Hovold 	if (ret)
10893ba9fa5cSJohan Hovold 		goto error;
109041c23958SJohan Hovold 
10913265edafSLaurent Pinchart 	ret = gb_camera_debugfs_init(gcam);
10923265edafSLaurent Pinchart 	if (ret < 0)
10933265edafSLaurent Pinchart 		goto error;
10943265edafSLaurent Pinchart 
109517ca6770SEvgeniy Borisov 	gcam->module.priv = gcam;
109617ca6770SEvgeniy Borisov 	gcam->module.ops = &gb_cam_ops;
109717ca6770SEvgeniy Borisov 	gcam->module.interface_id = gcam->connection->intf->interface_id;
109817ca6770SEvgeniy Borisov 	gcam->module.release = gb_camera_release_module;
109917ca6770SEvgeniy Borisov 	ret = gb_camera_register(&gcam->module);
11003a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
11013a8dba4eSGjorgji Rosikopulos 		goto error;
11023a8dba4eSGjorgji Rosikopulos 
110368b66c28SLaurent Pinchart 	greybus_set_drvdata(bundle, gcam);
110468b66c28SLaurent Pinchart 
11053265edafSLaurent Pinchart 	return 0;
11063265edafSLaurent Pinchart 
11073265edafSLaurent Pinchart error:
11083265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
110917ca6770SEvgeniy Borisov 	kfree(gcam);
11103265edafSLaurent Pinchart 	return ret;
11113265edafSLaurent Pinchart }
11123265edafSLaurent Pinchart 
111368b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle)
11143265edafSLaurent Pinchart {
111568b66c28SLaurent Pinchart 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
11163265edafSLaurent Pinchart 
11173265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
111817ca6770SEvgeniy Borisov 	gb_camera_unregister(&gcam->module);
11193265edafSLaurent Pinchart }
11203265edafSLaurent Pinchart 
112168b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = {
112268b66c28SLaurent Pinchart 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
112368b66c28SLaurent Pinchart 	{ },
11243265edafSLaurent Pinchart };
11253265edafSLaurent Pinchart 
112668b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = {
112768b66c28SLaurent Pinchart 	.name		= "camera",
112868b66c28SLaurent Pinchart 	.probe		= gb_camera_probe,
112968b66c28SLaurent Pinchart 	.disconnect	= gb_camera_disconnect,
113068b66c28SLaurent Pinchart 	.id_table	= gb_camera_id_table,
113168b66c28SLaurent Pinchart };
113268b66c28SLaurent Pinchart 
113368b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver);
11343265edafSLaurent Pinchart 
11353265edafSLaurent Pinchart MODULE_LICENSE("GPL v2");
1136