xref: /openbmc/linux/drivers/staging/greybus/camera.c (revision f032e2cd)
1eb50fd3aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
23265edafSLaurent Pinchart /*
33265edafSLaurent Pinchart  * Greybus Camera protocol driver.
43265edafSLaurent Pinchart  *
53265edafSLaurent Pinchart  * Copyright 2015 Google Inc.
63265edafSLaurent Pinchart  * Copyright 2015 Linaro Ltd.
73265edafSLaurent Pinchart  */
83265edafSLaurent Pinchart 
93265edafSLaurent Pinchart #include <linux/debugfs.h>
103265edafSLaurent Pinchart #include <linux/fs.h>
113265edafSLaurent Pinchart #include <linux/kernel.h>
123265edafSLaurent Pinchart #include <linux/module.h>
133265edafSLaurent Pinchart #include <linux/slab.h>
143265edafSLaurent Pinchart #include <linux/string.h>
153265edafSLaurent Pinchart #include <linux/uaccess.h>
163265edafSLaurent Pinchart #include <linux/vmalloc.h>
17ec0ad868SGreg Kroah-Hartman #include <linux/greybus.h>
183265edafSLaurent Pinchart 
193a8dba4eSGjorgji Rosikopulos #include "gb-camera.h"
203265edafSLaurent Pinchart #include "greybus_protocols.h"
213265edafSLaurent Pinchart 
223265edafSLaurent Pinchart enum gb_camera_debugs_buffer_id {
233265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
243265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
253265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
263265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
273265edafSLaurent Pinchart 	GB_CAMERA_DEBUGFS_BUFFER_MAX,
283265edafSLaurent Pinchart };
293265edafSLaurent Pinchart 
303265edafSLaurent Pinchart struct gb_camera_debugfs_buffer {
313265edafSLaurent Pinchart 	char data[PAGE_SIZE];
323265edafSLaurent Pinchart 	size_t length;
333265edafSLaurent Pinchart };
343265edafSLaurent Pinchart 
353b8ebfebSLaurent Pinchart enum gb_camera_state {
363b8ebfebSLaurent Pinchart 	GB_CAMERA_STATE_UNCONFIGURED,
373b8ebfebSLaurent Pinchart 	GB_CAMERA_STATE_CONFIGURED,
383b8ebfebSLaurent Pinchart };
393b8ebfebSLaurent Pinchart 
403265edafSLaurent Pinchart /**
413265edafSLaurent Pinchart  * struct gb_camera - A Greybus Camera Device
4217ca6770SEvgeniy Borisov  * @connection: the greybus connection for camera management
4317ca6770SEvgeniy Borisov  * @data_connection: the greybus connection for camera data
449120b906SLaurent Pinchart  * @data_cport_id: the data CPort ID on the module side
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;
549120b906SLaurent Pinchart 	u16 data_cport_id;
553b8ebfebSLaurent Pinchart 
5617ca6770SEvgeniy Borisov 	struct mutex mutex;
573b8ebfebSLaurent Pinchart 	enum gb_camera_state state;
583265edafSLaurent Pinchart 
593265edafSLaurent Pinchart 	struct {
603265edafSLaurent Pinchart 		struct dentry *root;
613265edafSLaurent Pinchart 		struct gb_camera_debugfs_buffer *buffers;
623265edafSLaurent Pinchart 	} debugfs;
63c3d77f71SGjorgji Rosikopulos 
64c3d77f71SGjorgji Rosikopulos 	struct gb_camera_module module;
653265edafSLaurent Pinchart };
663265edafSLaurent Pinchart 
673265edafSLaurent Pinchart struct gb_camera_stream_config {
683265edafSLaurent Pinchart 	unsigned int width;
693265edafSLaurent Pinchart 	unsigned int height;
703265edafSLaurent Pinchart 	unsigned int format;
713265edafSLaurent Pinchart 	unsigned int vc;
723265edafSLaurent Pinchart 	unsigned int dt[2];
733265edafSLaurent Pinchart 	unsigned int max_size;
743265edafSLaurent Pinchart };
753265edafSLaurent Pinchart 
766cc27048SJacopo Mondi struct gb_camera_fmt_info {
773a8dba4eSGjorgji Rosikopulos 	enum v4l2_mbus_pixelcode mbus_code;
783a8dba4eSGjorgji Rosikopulos 	unsigned int gb_format;
796cc27048SJacopo Mondi 	unsigned int bpp;
803a8dba4eSGjorgji Rosikopulos };
813a8dba4eSGjorgji Rosikopulos 
823a8dba4eSGjorgji Rosikopulos /* GB format to media code map */
836cc27048SJacopo Mondi static const struct gb_camera_fmt_info gb_fmt_info[] = {
843a8dba4eSGjorgji Rosikopulos 	{
853a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
863a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x01,
876cc27048SJacopo Mondi 		.bpp	   = 16,
883a8dba4eSGjorgji Rosikopulos 	},
893a8dba4eSGjorgji Rosikopulos 	{
907c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV12_1x8,
917c154711SGjorgji Rosikopulos 		.gb_format = 0x12,
926cc27048SJacopo Mondi 		.bpp	   = 12,
937c154711SGjorgji Rosikopulos 	},
947c154711SGjorgji Rosikopulos 	{
957c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV21_1x8,
967c154711SGjorgji Rosikopulos 		.gb_format = 0x13,
976cc27048SJacopo Mondi 		.bpp	   = 12,
987c154711SGjorgji Rosikopulos 	},
997c154711SGjorgji Rosikopulos 	{
1007c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YU12_1x8,
1013a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x16,
1026cc27048SJacopo Mondi 		.bpp	   = 12,
1033a8dba4eSGjorgji Rosikopulos 	},
1043a8dba4eSGjorgji Rosikopulos 	{
1057c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YV12_1x8,
1063a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x17,
1076cc27048SJacopo Mondi 		.bpp	   = 12,
1083a8dba4eSGjorgji Rosikopulos 	},
1093a8dba4eSGjorgji Rosikopulos 	{
1103a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
1113a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x40,
1126cc27048SJacopo Mondi 		.bpp	   = 0,
113dc5cc72cSGjorgji Rosikopulos 	},
114dc5cc72cSGjorgji Rosikopulos 	{
115563c742aSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_GB_CAM_METADATA_1X8,
116dc5cc72cSGjorgji Rosikopulos 		.gb_format = 0x41,
1176cc27048SJacopo Mondi 		.bpp	   = 0,
118cb14e976SGjorgji Rosikopulos 	},
119cb14e976SGjorgji Rosikopulos 	{
120563c742aSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_GB_CAM_DEBUG_DATA_1X8,
121cb14e976SGjorgji Rosikopulos 		.gb_format = 0x42,
1226cc27048SJacopo Mondi 		.bpp	   = 0,
123cb14e976SGjorgji Rosikopulos 	},
124caad3090SEvgeniy Borisov 	{
125caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10,
126caad3090SEvgeniy Borisov 		.gb_format = 0x80,
1276cc27048SJacopo Mondi 		.bpp	   = 10,
128caad3090SEvgeniy Borisov 	},
129caad3090SEvgeniy Borisov 	{
130caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10,
131caad3090SEvgeniy Borisov 		.gb_format = 0x81,
1326cc27048SJacopo Mondi 		.bpp	   = 10,
133caad3090SEvgeniy Borisov 	},
134caad3090SEvgeniy Borisov 	{
135caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
136caad3090SEvgeniy Borisov 		.gb_format = 0x82,
1376cc27048SJacopo Mondi 		.bpp	   = 10,
138caad3090SEvgeniy Borisov 	},
139caad3090SEvgeniy Borisov 	{
140caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10,
141caad3090SEvgeniy Borisov 		.gb_format = 0x83,
1426cc27048SJacopo Mondi 		.bpp	   = 10,
143caad3090SEvgeniy Borisov 	},
144caad3090SEvgeniy Borisov 	{
145caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12,
146caad3090SEvgeniy Borisov 		.gb_format = 0x84,
1476cc27048SJacopo Mondi 		.bpp	   = 12,
148caad3090SEvgeniy Borisov 	},
149caad3090SEvgeniy Borisov 	{
150caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12,
151caad3090SEvgeniy Borisov 		.gb_format = 0x85,
1526cc27048SJacopo Mondi 		.bpp	   = 12,
153caad3090SEvgeniy Borisov 	},
154caad3090SEvgeniy Borisov 	{
155caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
156caad3090SEvgeniy Borisov 		.gb_format = 0x86,
1576cc27048SJacopo Mondi 		.bpp	   = 12,
158caad3090SEvgeniy Borisov 	},
159caad3090SEvgeniy Borisov 	{
160caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12,
161caad3090SEvgeniy Borisov 		.gb_format = 0x87,
1626cc27048SJacopo Mondi 		.bpp	   = 12,
163caad3090SEvgeniy Borisov 	},
1643a8dba4eSGjorgji Rosikopulos };
1653a8dba4eSGjorgji Rosikopulos 
gb_camera_get_format_info(u16 gb_fmt)166d165a618SJacopo Mondi static const struct gb_camera_fmt_info *gb_camera_get_format_info(u16 gb_fmt)
167d165a618SJacopo Mondi {
168d165a618SJacopo Mondi 	unsigned int i;
169d165a618SJacopo Mondi 
170d165a618SJacopo Mondi 	for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) {
171d165a618SJacopo Mondi 		if (gb_fmt_info[i].gb_format == gb_fmt)
172d165a618SJacopo Mondi 			return &gb_fmt_info[i];
173d165a618SJacopo Mondi 	}
174d165a618SJacopo Mondi 
175d165a618SJacopo Mondi 	return NULL;
176d165a618SJacopo Mondi }
177d165a618SJacopo Mondi 
1783265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT		16
1793265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT		17
1803265edafSLaurent Pinchart 
1813265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
1823265edafSLaurent Pinchart 
18368b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...)	dev_dbg(&gcam->bundle->dev, format)
18468b66c28SLaurent Pinchart #define gcam_info(gcam, format...)	dev_info(&gcam->bundle->dev, format)
18568b66c28SLaurent Pinchart #define gcam_err(gcam, format...)	dev_err(&gcam->bundle->dev, format)
1863265edafSLaurent Pinchart 
gb_camera_operation_sync_flags(struct gb_connection * connection,int type,unsigned int flags,void * request,size_t request_size,void * response,size_t * response_size)187fdf73c00SJacopo Mondi static int gb_camera_operation_sync_flags(struct gb_connection *connection,
188fdf73c00SJacopo Mondi 					  int type, unsigned int flags,
189fdf73c00SJacopo Mondi 					  void *request, size_t request_size,
190fdf73c00SJacopo Mondi 					  void *response, size_t *response_size)
191fdf73c00SJacopo Mondi {
192fdf73c00SJacopo Mondi 	struct gb_operation *operation;
193fdf73c00SJacopo Mondi 	int ret;
194fdf73c00SJacopo Mondi 
195fdf73c00SJacopo Mondi 	operation = gb_operation_create_flags(connection, type, request_size,
196fdf73c00SJacopo Mondi 					      *response_size, flags,
197fdf73c00SJacopo Mondi 					      GFP_KERNEL);
198fdf73c00SJacopo Mondi 	if (!operation)
199fdf73c00SJacopo Mondi 		return  -ENOMEM;
200fdf73c00SJacopo Mondi 
201fdf73c00SJacopo Mondi 	if (request_size)
202fdf73c00SJacopo Mondi 		memcpy(operation->request->payload, request, request_size);
203fdf73c00SJacopo Mondi 
204fdf73c00SJacopo Mondi 	ret = gb_operation_request_send_sync(operation);
205fdf73c00SJacopo Mondi 	if (ret) {
206fdf73c00SJacopo Mondi 		dev_err(&connection->hd->dev,
207fdf73c00SJacopo Mondi 			"%s: synchronous operation of type 0x%02x failed: %d\n",
208fdf73c00SJacopo Mondi 			connection->name, type, ret);
209fdf73c00SJacopo Mondi 	} else {
210fdf73c00SJacopo Mondi 		*response_size = operation->response->payload_size;
211fdf73c00SJacopo Mondi 
212fdf73c00SJacopo Mondi 		if (operation->response->payload_size)
213fdf73c00SJacopo Mondi 			memcpy(response, operation->response->payload,
214fdf73c00SJacopo Mondi 			       operation->response->payload_size);
215fdf73c00SJacopo Mondi 	}
216fdf73c00SJacopo Mondi 
217fdf73c00SJacopo Mondi 	gb_operation_put(operation);
218fdf73c00SJacopo Mondi 
219fdf73c00SJacopo Mondi 	return ret;
220fdf73c00SJacopo Mondi }
221fdf73c00SJacopo Mondi 
gb_camera_get_max_pkt_size(struct gb_camera * gcam,struct gb_camera_configure_streams_response * resp)222f88b94ecSJacopo Mondi static int gb_camera_get_max_pkt_size(struct gb_camera *gcam,
223f88b94ecSJacopo Mondi 		struct gb_camera_configure_streams_response *resp)
224f88b94ecSJacopo Mondi {
225f88b94ecSJacopo Mondi 	unsigned int max_pkt_size = 0;
226f88b94ecSJacopo Mondi 	unsigned int i;
227f88b94ecSJacopo Mondi 
228f88b94ecSJacopo Mondi 	for (i = 0; i < resp->num_streams; i++) {
229f88b94ecSJacopo Mondi 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
230f88b94ecSJacopo Mondi 		const struct gb_camera_fmt_info *fmt_info;
231f88b94ecSJacopo Mondi 		unsigned int pkt_size;
232f88b94ecSJacopo Mondi 
233f88b94ecSJacopo Mondi 		fmt_info = gb_camera_get_format_info(cfg->format);
234f88b94ecSJacopo Mondi 		if (!fmt_info) {
235f88b94ecSJacopo Mondi 			gcam_err(gcam, "unsupported greybus image format: %d\n",
236f88b94ecSJacopo Mondi 				 cfg->format);
237f88b94ecSJacopo Mondi 			return -EIO;
238f88b94ecSJacopo Mondi 		}
239f88b94ecSJacopo Mondi 
240f88b94ecSJacopo Mondi 		if (fmt_info->bpp == 0) {
241f88b94ecSJacopo Mondi 			pkt_size = le32_to_cpu(cfg->max_pkt_size);
242f88b94ecSJacopo Mondi 
243f88b94ecSJacopo Mondi 			if (pkt_size == 0) {
244f88b94ecSJacopo Mondi 				gcam_err(gcam,
245f88b94ecSJacopo Mondi 					 "Stream %u: invalid zero maximum packet size\n",
246f88b94ecSJacopo Mondi 					 i);
247f88b94ecSJacopo Mondi 				return -EIO;
248f88b94ecSJacopo Mondi 			}
249f88b94ecSJacopo Mondi 		} else {
250f88b94ecSJacopo Mondi 			pkt_size = le16_to_cpu(cfg->width) * fmt_info->bpp / 8;
251f88b94ecSJacopo Mondi 
252f88b94ecSJacopo Mondi 			if (pkt_size != le32_to_cpu(cfg->max_pkt_size)) {
253f88b94ecSJacopo Mondi 				gcam_err(gcam,
254f88b94ecSJacopo Mondi 					 "Stream %u: maximum packet size mismatch (%u/%u)\n",
255f88b94ecSJacopo Mondi 					 i, pkt_size, cfg->max_pkt_size);
256f88b94ecSJacopo Mondi 				return -EIO;
257f88b94ecSJacopo Mondi 			}
258f88b94ecSJacopo Mondi 		}
259f88b94ecSJacopo Mondi 
260f88b94ecSJacopo Mondi 		max_pkt_size = max(pkt_size, max_pkt_size);
261f88b94ecSJacopo Mondi 	}
262f88b94ecSJacopo Mondi 
263f88b94ecSJacopo Mondi 	return max_pkt_size;
264f88b94ecSJacopo Mondi }
265f88b94ecSJacopo Mondi 
266d165a618SJacopo Mondi /*
267d165a618SJacopo Mondi  * Validate the stream configuration response verifying padding is correctly
268d165a618SJacopo Mondi  * set and the returned number of streams is supported
269d165a618SJacopo Mondi  */
gb_camera_configure_streams_validate_response(struct gb_camera * gcam,struct gb_camera_configure_streams_response * resp,unsigned int nstreams)270c9161d72SJacopo Mondi static const int gb_camera_configure_streams_validate_response(
271d165a618SJacopo Mondi 		struct gb_camera *gcam,
272c9161d72SJacopo Mondi 		struct gb_camera_configure_streams_response *resp,
273d165a618SJacopo Mondi 		unsigned int nstreams)
274d165a618SJacopo Mondi {
275d165a618SJacopo Mondi 	unsigned int i;
276d165a618SJacopo Mondi 
277d165a618SJacopo Mondi 	/* Validate the returned response structure */
278c9161d72SJacopo Mondi 	if (resp->padding[0] || resp->padding[1]) {
279d165a618SJacopo Mondi 		gcam_err(gcam, "response padding != 0\n");
280d165a618SJacopo Mondi 		return -EIO;
281d165a618SJacopo Mondi 	}
282d165a618SJacopo Mondi 
283c9161d72SJacopo Mondi 	if (resp->num_streams > nstreams) {
284d165a618SJacopo Mondi 		gcam_err(gcam, "got #streams %u > request %u\n",
285d165a618SJacopo Mondi 			 resp->num_streams, nstreams);
286d165a618SJacopo Mondi 		return -EIO;
287d165a618SJacopo Mondi 	}
288d165a618SJacopo Mondi 
289c9161d72SJacopo Mondi 	for (i = 0; i < resp->num_streams; i++) {
290d165a618SJacopo Mondi 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
291cd7b701fSElise Lennion 
292d165a618SJacopo Mondi 		if (cfg->padding) {
293d165a618SJacopo Mondi 			gcam_err(gcam, "stream #%u padding != 0\n", i);
294d165a618SJacopo Mondi 			return -EIO;
295d165a618SJacopo Mondi 		}
296d165a618SJacopo Mondi 	}
297d165a618SJacopo Mondi 
298d165a618SJacopo Mondi 	return 0;
299d165a618SJacopo Mondi }
300d165a618SJacopo Mondi 
3013265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
302f3d5f661SLaurent Pinchart  * Hardware Configuration
3033265edafSLaurent Pinchart  */
3043265edafSLaurent Pinchart 
gb_camera_set_intf_power_mode(struct gb_camera * gcam,u8 intf_id,bool hs)305c161c0fcSLaurent Pinchart static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id,
306c161c0fcSLaurent Pinchart 					 bool hs)
307c161c0fcSLaurent Pinchart {
308c161c0fcSLaurent Pinchart 	struct gb_svc *svc = gcam->connection->hd->svc;
309c161c0fcSLaurent Pinchart 	int ret;
310c161c0fcSLaurent Pinchart 
311c161c0fcSLaurent Pinchart 	if (hs)
312c161c0fcSLaurent Pinchart 		ret = gb_svc_intf_set_power_mode(svc, intf_id,
313c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_HS_SERIES_A,
314c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
3158c2522d8SEli Sennesh 						 GB_SVC_SMALL_AMPLITUDE,
3168c2522d8SEli Sennesh 						 GB_SVC_NO_DE_EMPHASIS,
317c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_FAST_MODE, 2, 2,
318c161c0fcSLaurent Pinchart 						 GB_SVC_PWRM_RXTERMINATION |
3198c2522d8SEli Sennesh 						 GB_SVC_PWRM_TXTERMINATION, 0,
3208c2522d8SEli Sennesh 						 NULL, NULL);
321c161c0fcSLaurent Pinchart 	else
322c161c0fcSLaurent Pinchart 		ret = gb_svc_intf_set_power_mode(svc, intf_id,
323c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_HS_SERIES_A,
324c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_SLOW_AUTO_MODE,
325ee2f2074SMitchell Tasman 						 2, 1,
3268c2522d8SEli Sennesh 						 GB_SVC_SMALL_AMPLITUDE,
3278c2522d8SEli Sennesh 						 GB_SVC_NO_DE_EMPHASIS,
328c161c0fcSLaurent Pinchart 						 GB_SVC_UNIPRO_SLOW_AUTO_MODE,
329ee2f2074SMitchell Tasman 						 2, 1,
3308c2522d8SEli Sennesh 						 0, 0,
3318c2522d8SEli Sennesh 						 NULL, NULL);
332c161c0fcSLaurent Pinchart 
333c161c0fcSLaurent Pinchart 	return ret;
334c161c0fcSLaurent Pinchart }
335c161c0fcSLaurent Pinchart 
gb_camera_set_power_mode(struct gb_camera * gcam,bool hs)336c161c0fcSLaurent Pinchart static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs)
337c161c0fcSLaurent Pinchart {
338c161c0fcSLaurent Pinchart 	struct gb_interface *intf = gcam->connection->intf;
339c161c0fcSLaurent Pinchart 	struct gb_svc *svc = gcam->connection->hd->svc;
340c161c0fcSLaurent Pinchart 	int ret;
341c161c0fcSLaurent Pinchart 
342c161c0fcSLaurent Pinchart 	ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs);
343c161c0fcSLaurent Pinchart 	if (ret < 0) {
344c161c0fcSLaurent Pinchart 		gcam_err(gcam, "failed to set module interface to %s (%d)\n",
345c161c0fcSLaurent Pinchart 			 hs ? "HS" : "PWM", ret);
346c161c0fcSLaurent Pinchart 		return ret;
347c161c0fcSLaurent Pinchart 	}
348c161c0fcSLaurent Pinchart 
349c161c0fcSLaurent Pinchart 	ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs);
350c161c0fcSLaurent Pinchart 	if (ret < 0) {
351b573b0e6SLaurent Pinchart 		gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs);
352c161c0fcSLaurent Pinchart 		gcam_err(gcam, "failed to set AP interface to %s (%d)\n",
353c161c0fcSLaurent Pinchart 			 hs ? "HS" : "PWM", ret);
354c161c0fcSLaurent Pinchart 		return ret;
355c161c0fcSLaurent Pinchart 	}
356c161c0fcSLaurent Pinchart 
357c161c0fcSLaurent Pinchart 	return 0;
358c161c0fcSLaurent Pinchart }
359c161c0fcSLaurent Pinchart 
360f3d5f661SLaurent Pinchart struct ap_csi_config_request {
361f3d5f661SLaurent Pinchart 	__u8 csi_id;
362f3d5f661SLaurent Pinchart 	__u8 flags;
363f3d5f661SLaurent Pinchart #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01
364f3d5f661SLaurent Pinchart 	__u8 num_lanes;
365f3d5f661SLaurent Pinchart 	__u8 padding;
366f88b94ecSJacopo Mondi 	__le32 csi_clk_freq;
367f88b94ecSJacopo Mondi 	__le32 max_pkt_size;
368f3d5f661SLaurent Pinchart } __packed;
369f3d5f661SLaurent Pinchart 
37024f9a6e4SLaurent Pinchart /*
37124f9a6e4SLaurent Pinchart  * TODO: Compute the number of lanes dynamically based on bandwidth
37224f9a6e4SLaurent Pinchart  * requirements.
37324f9a6e4SLaurent Pinchart  */
37424f9a6e4SLaurent Pinchart #define GB_CAMERA_CSI_NUM_DATA_LANES		4
375f88b94ecSJacopo Mondi 
376f88b94ecSJacopo Mondi #define GB_CAMERA_CSI_CLK_FREQ_MAX		999000000U
377f88b94ecSJacopo Mondi #define GB_CAMERA_CSI_CLK_FREQ_MIN		100000000U
378f88b94ecSJacopo Mondi #define GB_CAMERA_CSI_CLK_FREQ_MARGIN		150000000U
37924f9a6e4SLaurent Pinchart 
gb_camera_setup_data_connection(struct gb_camera * gcam,struct gb_camera_configure_streams_response * resp,struct gb_camera_csi_params * csi_params)380f3d5f661SLaurent Pinchart static int gb_camera_setup_data_connection(struct gb_camera *gcam,
381f88b94ecSJacopo Mondi 		struct gb_camera_configure_streams_response *resp,
382f3d5f661SLaurent Pinchart 		struct gb_camera_csi_params *csi_params)
383f3d5f661SLaurent Pinchart {
384f3d5f661SLaurent Pinchart 	struct ap_csi_config_request csi_cfg;
3859120b906SLaurent Pinchart 	struct gb_connection *conn;
386f88b94ecSJacopo Mondi 	unsigned int clk_freq;
387f3d5f661SLaurent Pinchart 	int ret;
388f3d5f661SLaurent Pinchart 
3899120b906SLaurent Pinchart 	/*
3909120b906SLaurent Pinchart 	 * Create the data connection between the camera module data CPort and
3919120b906SLaurent Pinchart 	 * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge.
3929120b906SLaurent Pinchart 	 */
3939120b906SLaurent Pinchart 	conn = gb_connection_create_offloaded(gcam->bundle, gcam->data_cport_id,
3949120b906SLaurent Pinchart 					      GB_CONNECTION_FLAG_NO_FLOWCTRL |
3959120b906SLaurent Pinchart 					      GB_CONNECTION_FLAG_CDSI1);
3969120b906SLaurent Pinchart 	if (IS_ERR(conn))
3979120b906SLaurent Pinchart 		return PTR_ERR(conn);
3989120b906SLaurent Pinchart 
3999120b906SLaurent Pinchart 	gcam->data_connection = conn;
4009120b906SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
4019120b906SLaurent Pinchart 
4029120b906SLaurent Pinchart 	ret = gb_connection_enable(conn);
4039120b906SLaurent Pinchart 	if (ret)
4049120b906SLaurent Pinchart 		goto error_conn_destroy;
4059120b906SLaurent Pinchart 
406f3d5f661SLaurent Pinchart 	/* Set the UniPro link to high speed mode. */
407f3d5f661SLaurent Pinchart 	ret = gb_camera_set_power_mode(gcam, true);
408f3d5f661SLaurent Pinchart 	if (ret < 0)
4099120b906SLaurent Pinchart 		goto error_conn_disable;
410f3d5f661SLaurent Pinchart 
411f3d5f661SLaurent Pinchart 	/*
412f88b94ecSJacopo Mondi 	 * Configure the APB-A CSI-2 transmitter.
413f3d5f661SLaurent Pinchart 	 *
414f88b94ecSJacopo Mondi 	 * Hardcode the number of lanes to 4 and compute the bus clock frequency
415f88b94ecSJacopo Mondi 	 * based on the module bandwidth requirements with a safety margin.
416f3d5f661SLaurent Pinchart 	 */
417f3d5f661SLaurent Pinchart 	memset(&csi_cfg, 0, sizeof(csi_cfg));
418f3d5f661SLaurent Pinchart 	csi_cfg.csi_id = 1;
419f3d5f661SLaurent Pinchart 	csi_cfg.flags = 0;
42024f9a6e4SLaurent Pinchart 	csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES;
421f88b94ecSJacopo Mondi 
422f88b94ecSJacopo Mondi 	clk_freq = resp->data_rate / 2 / GB_CAMERA_CSI_NUM_DATA_LANES;
423f88b94ecSJacopo Mondi 	clk_freq = clamp(clk_freq + GB_CAMERA_CSI_CLK_FREQ_MARGIN,
424f88b94ecSJacopo Mondi 			 GB_CAMERA_CSI_CLK_FREQ_MIN,
425f88b94ecSJacopo Mondi 			 GB_CAMERA_CSI_CLK_FREQ_MAX);
426f88b94ecSJacopo Mondi 	csi_cfg.csi_clk_freq = clk_freq;
427f88b94ecSJacopo Mondi 
428f88b94ecSJacopo Mondi 	ret = gb_camera_get_max_pkt_size(gcam, resp);
429f88b94ecSJacopo Mondi 	if (ret < 0) {
430f88b94ecSJacopo Mondi 		ret = -EIO;
431f88b94ecSJacopo Mondi 		goto error_power;
432f88b94ecSJacopo Mondi 	}
433f88b94ecSJacopo Mondi 	csi_cfg.max_pkt_size = ret;
434f3d5f661SLaurent Pinchart 
435f3d5f661SLaurent Pinchart 	ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
436f3d5f661SLaurent Pinchart 			   sizeof(csi_cfg),
437f3d5f661SLaurent Pinchart 			   GB_APB_REQUEST_CSI_TX_CONTROL, false);
438f3d5f661SLaurent Pinchart 	if (ret < 0) {
439f3d5f661SLaurent Pinchart 		gcam_err(gcam, "failed to start the CSI transmitter\n");
4409120b906SLaurent Pinchart 		goto error_power;
441f3d5f661SLaurent Pinchart 	}
442f3d5f661SLaurent Pinchart 
443f3d5f661SLaurent Pinchart 	if (csi_params) {
444f88b94ecSJacopo Mondi 		csi_params->clk_freq = csi_cfg.csi_clk_freq;
445f3d5f661SLaurent Pinchart 		csi_params->num_lanes = csi_cfg.num_lanes;
446f3d5f661SLaurent Pinchart 	}
447f3d5f661SLaurent Pinchart 
448f3d5f661SLaurent Pinchart 	return 0;
4499120b906SLaurent Pinchart 
4509120b906SLaurent Pinchart error_power:
4519120b906SLaurent Pinchart 	gb_camera_set_power_mode(gcam, false);
4529120b906SLaurent Pinchart error_conn_disable:
4539120b906SLaurent Pinchart 	gb_connection_disable(gcam->data_connection);
4549120b906SLaurent Pinchart error_conn_destroy:
4559120b906SLaurent Pinchart 	gb_connection_destroy(gcam->data_connection);
4569120b906SLaurent Pinchart 	gcam->data_connection = NULL;
4579120b906SLaurent Pinchart 	return ret;
458f3d5f661SLaurent Pinchart }
459f3d5f661SLaurent Pinchart 
gb_camera_teardown_data_connection(struct gb_camera * gcam)460f3d5f661SLaurent Pinchart static void gb_camera_teardown_data_connection(struct gb_camera *gcam)
461f3d5f661SLaurent Pinchart {
462f3d5f661SLaurent Pinchart 	struct ap_csi_config_request csi_cfg;
463f3d5f661SLaurent Pinchart 	int ret;
464f3d5f661SLaurent Pinchart 
465f3d5f661SLaurent Pinchart 	/* Stop the APB1 CSI transmitter. */
466f3d5f661SLaurent Pinchart 	memset(&csi_cfg, 0, sizeof(csi_cfg));
467f3d5f661SLaurent Pinchart 	csi_cfg.csi_id = 1;
468f3d5f661SLaurent Pinchart 
469f3d5f661SLaurent Pinchart 	ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
470f3d5f661SLaurent Pinchart 			   sizeof(csi_cfg),
471f3d5f661SLaurent Pinchart 			   GB_APB_REQUEST_CSI_TX_CONTROL, false);
472f3d5f661SLaurent Pinchart 
473f3d5f661SLaurent Pinchart 	if (ret < 0)
474f3d5f661SLaurent Pinchart 		gcam_err(gcam, "failed to stop the CSI transmitter\n");
475f3d5f661SLaurent Pinchart 
476f3d5f661SLaurent Pinchart 	/* Set the UniPro link to low speed mode. */
477f3d5f661SLaurent Pinchart 	gb_camera_set_power_mode(gcam, false);
4789120b906SLaurent Pinchart 
4799120b906SLaurent Pinchart 	/* Destroy the data connection. */
4809120b906SLaurent Pinchart 	gb_connection_disable(gcam->data_connection);
4819120b906SLaurent Pinchart 	gb_connection_destroy(gcam->data_connection);
4829120b906SLaurent Pinchart 	gcam->data_connection = NULL;
483f3d5f661SLaurent Pinchart }
484f3d5f661SLaurent Pinchart 
485f3d5f661SLaurent Pinchart /* -----------------------------------------------------------------------------
486f3d5f661SLaurent Pinchart  * Camera Protocol Operations
487f3d5f661SLaurent Pinchart  */
488f3d5f661SLaurent Pinchart 
gb_camera_capabilities(struct gb_camera * gcam,u8 * capabilities,size_t * size)48948b15a9bSLaurent Pinchart static int gb_camera_capabilities(struct gb_camera *gcam,
49048b15a9bSLaurent Pinchart 				  u8 *capabilities, size_t *size)
49148b15a9bSLaurent Pinchart {
49248b15a9bSLaurent Pinchart 	int ret;
49348b15a9bSLaurent Pinchart 
494211634f2SDavid Lin 	ret = gb_pm_runtime_get_sync(gcam->bundle);
495211634f2SDavid Lin 	if (ret)
496211634f2SDavid Lin 		return ret;
497211634f2SDavid Lin 
49817ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
49917ca6770SEvgeniy Borisov 
50017ca6770SEvgeniy Borisov 	if (!gcam->connection) {
50117ca6770SEvgeniy Borisov 		ret = -EINVAL;
50217ca6770SEvgeniy Borisov 		goto done;
50317ca6770SEvgeniy Borisov 	}
50417ca6770SEvgeniy Borisov 
505fdf73c00SJacopo Mondi 	ret = gb_camera_operation_sync_flags(gcam->connection,
506fdf73c00SJacopo Mondi 					     GB_CAMERA_TYPE_CAPABILITIES,
50748b15a9bSLaurent Pinchart 					     GB_OPERATION_FLAG_SHORT_RESPONSE,
508fdf73c00SJacopo Mondi 					     NULL, 0,
509fdf73c00SJacopo Mondi 					     (void *)capabilities, size);
510fdf73c00SJacopo Mondi 	if (ret)
51148b15a9bSLaurent Pinchart 		gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret);
51248b15a9bSLaurent Pinchart 
51348b15a9bSLaurent Pinchart done:
51417ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
515211634f2SDavid Lin 
516211634f2SDavid Lin 	gb_pm_runtime_put_autosuspend(gcam->bundle);
517211634f2SDavid Lin 
51848b15a9bSLaurent Pinchart 	return ret;
51948b15a9bSLaurent Pinchart }
52048b15a9bSLaurent Pinchart 
gb_camera_configure_streams(struct gb_camera * gcam,unsigned int * num_streams,unsigned int * flags,struct gb_camera_stream_config * streams,struct gb_camera_csi_params * csi_params)5213265edafSLaurent Pinchart static int gb_camera_configure_streams(struct gb_camera *gcam,
5224068487cSLaurent Pinchart 				       unsigned int *num_streams,
5234068487cSLaurent Pinchart 				       unsigned int *flags,
524b4905038SEvgeniy Borisov 				       struct gb_camera_stream_config *streams,
525b4905038SEvgeniy Borisov 				       struct gb_camera_csi_params *csi_params)
5263265edafSLaurent Pinchart {
5273265edafSLaurent Pinchart 	struct gb_camera_configure_streams_request *req;
5283265edafSLaurent Pinchart 	struct gb_camera_configure_streams_response *resp;
5294068487cSLaurent Pinchart 	unsigned int nstreams = *num_streams;
5303265edafSLaurent Pinchart 	unsigned int i;
5313265edafSLaurent Pinchart 	size_t req_size;
5323265edafSLaurent Pinchart 	size_t resp_size;
5333265edafSLaurent Pinchart 	int ret;
5343265edafSLaurent Pinchart 
5353265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
5363265edafSLaurent Pinchart 		return -EINVAL;
5373265edafSLaurent Pinchart 
5383265edafSLaurent Pinchart 	req_size = sizeof(*req) + nstreams * sizeof(req->config[0]);
5393265edafSLaurent Pinchart 	resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]);
5403265edafSLaurent Pinchart 
5413265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
5423265edafSLaurent Pinchart 	resp = kmalloc(resp_size, GFP_KERNEL);
543c9161d72SJacopo Mondi 	if (!req || !resp) {
54417ca6770SEvgeniy Borisov 		kfree(req);
54517ca6770SEvgeniy Borisov 		kfree(resp);
54617ca6770SEvgeniy Borisov 		return -ENOMEM;
5473265edafSLaurent Pinchart 	}
5483265edafSLaurent Pinchart 
549b787d413SJacopo Mondi 	req->num_streams = nstreams;
5504068487cSLaurent Pinchart 	req->flags = *flags;
5513265edafSLaurent Pinchart 	req->padding = 0;
5523265edafSLaurent Pinchart 
5533265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
5543265edafSLaurent Pinchart 		struct gb_camera_stream_config_request *cfg = &req->config[i];
5553265edafSLaurent Pinchart 
556c6622216SLaurent Pinchart 		cfg->width = cpu_to_le16(streams[i].width);
557c6622216SLaurent Pinchart 		cfg->height = cpu_to_le16(streams[i].height);
558c6622216SLaurent Pinchart 		cfg->format = cpu_to_le16(streams[i].format);
5593265edafSLaurent Pinchart 		cfg->padding = 0;
5603265edafSLaurent Pinchart 	}
5613265edafSLaurent Pinchart 
56217ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
56317ca6770SEvgeniy Borisov 
564211634f2SDavid Lin 	ret = gb_pm_runtime_get_sync(gcam->bundle);
565211634f2SDavid Lin 	if (ret)
566211634f2SDavid Lin 		goto done_skip_pm_put;
567211634f2SDavid Lin 
56817ca6770SEvgeniy Borisov 	if (!gcam->connection) {
56917ca6770SEvgeniy Borisov 		ret = -EINVAL;
57017ca6770SEvgeniy Borisov 		goto done;
57117ca6770SEvgeniy Borisov 	}
57217ca6770SEvgeniy Borisov 
573d165a618SJacopo Mondi 	ret = gb_camera_operation_sync_flags(gcam->connection,
5743265edafSLaurent Pinchart 					     GB_CAMERA_TYPE_CONFIGURE_STREAMS,
575d165a618SJacopo Mondi 					     GB_OPERATION_FLAG_SHORT_RESPONSE,
576d165a618SJacopo Mondi 					     req, req_size,
577c9161d72SJacopo Mondi 					     resp, &resp_size);
5783265edafSLaurent Pinchart 	if (ret < 0)
57966c36070SLaurent Pinchart 		goto done;
5803265edafSLaurent Pinchart 
581c9161d72SJacopo Mondi 	ret = gb_camera_configure_streams_validate_response(gcam, resp,
582c9161d72SJacopo Mondi 							    nstreams);
583d165a618SJacopo Mondi 	if (ret < 0)
58466c36070SLaurent Pinchart 		goto done;
5853265edafSLaurent Pinchart 
5867f93eab7SJacopo Mondi 	*flags = resp->flags;
5877f93eab7SJacopo Mondi 	*num_streams = resp->num_streams;
5887f93eab7SJacopo Mondi 
58936ab1108SJacopo Mondi 	for (i = 0; i < resp->num_streams; ++i) {
5903265edafSLaurent Pinchart 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
5913265edafSLaurent Pinchart 
592c6622216SLaurent Pinchart 		streams[i].width = le16_to_cpu(cfg->width);
593c6622216SLaurent Pinchart 		streams[i].height = le16_to_cpu(cfg->height);
594c6622216SLaurent Pinchart 		streams[i].format = le16_to_cpu(cfg->format);
5953265edafSLaurent Pinchart 		streams[i].vc = cfg->virtual_channel;
5963265edafSLaurent Pinchart 		streams[i].dt[0] = cfg->data_type[0];
5973265edafSLaurent Pinchart 		streams[i].dt[1] = cfg->data_type[1];
598c6622216SLaurent Pinchart 		streams[i].max_size = le32_to_cpu(cfg->max_size);
5993265edafSLaurent Pinchart 	}
6003265edafSLaurent Pinchart 
601640924d2SLaurent Pinchart 	if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) ||
6027f93eab7SJacopo Mondi 	    (req->flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY))
60366c36070SLaurent Pinchart 		goto done;
60466c36070SLaurent Pinchart 
6053b8ebfebSLaurent Pinchart 	if (gcam->state == GB_CAMERA_STATE_CONFIGURED) {
6063b8ebfebSLaurent Pinchart 		gb_camera_teardown_data_connection(gcam);
6073b8ebfebSLaurent Pinchart 		gcam->state = GB_CAMERA_STATE_UNCONFIGURED;
608211634f2SDavid Lin 
609211634f2SDavid Lin 		/*
610211634f2SDavid Lin 		 * When unconfiguring streams release the PM runtime reference
611211634f2SDavid Lin 		 * that was acquired when streams were configured. The bundle
612211634f2SDavid Lin 		 * won't be suspended until the PM runtime reference acquired at
613211634f2SDavid Lin 		 * the beginning of this function gets released right before
614211634f2SDavid Lin 		 * returning.
615211634f2SDavid Lin 		 */
616211634f2SDavid Lin 		gb_pm_runtime_put_noidle(gcam->bundle);
6173b8ebfebSLaurent Pinchart 	}
6183b8ebfebSLaurent Pinchart 
619f88b94ecSJacopo Mondi 	if (resp->num_streams == 0)
620f88b94ecSJacopo Mondi 		goto done;
621f88b94ecSJacopo Mondi 
622211634f2SDavid Lin 	/*
623211634f2SDavid Lin 	 * Make sure the bundle won't be suspended until streams get
624211634f2SDavid Lin 	 * unconfigured after the stream is configured successfully
625211634f2SDavid Lin 	 */
626211634f2SDavid Lin 	gb_pm_runtime_get_noresume(gcam->bundle);
627211634f2SDavid Lin 
628f88b94ecSJacopo Mondi 	/* Setup CSI-2 connection from APB-A to AP */
629f3d5f661SLaurent Pinchart 	ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
630f3d5f661SLaurent Pinchart 	if (ret < 0) {
631f3d5f661SLaurent Pinchart 		memset(req, 0, sizeof(*req));
632f3d5f661SLaurent Pinchart 		gb_operation_sync(gcam->connection,
633f3d5f661SLaurent Pinchart 				  GB_CAMERA_TYPE_CONFIGURE_STREAMS,
63480b3982bSJacopo Mondi 				  req, sizeof(*req),
63580b3982bSJacopo Mondi 				  resp, sizeof(*resp));
6367f93eab7SJacopo Mondi 		*flags = 0;
6377f93eab7SJacopo Mondi 		*num_streams = 0;
638211634f2SDavid Lin 		gb_pm_runtime_put_noidle(gcam->bundle);
63966c36070SLaurent Pinchart 		goto done;
640b4905038SEvgeniy Borisov 	}
6413b8ebfebSLaurent Pinchart 
6423b8ebfebSLaurent Pinchart 	gcam->state = GB_CAMERA_STATE_CONFIGURED;
643142b21feSLaurent Pinchart 
6443265edafSLaurent Pinchart done:
645211634f2SDavid Lin 	gb_pm_runtime_put_autosuspend(gcam->bundle);
646211634f2SDavid Lin 
647211634f2SDavid Lin done_skip_pm_put:
64817ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
6493265edafSLaurent Pinchart 	kfree(req);
6503265edafSLaurent Pinchart 	kfree(resp);
6513265edafSLaurent Pinchart 	return ret;
6523265edafSLaurent Pinchart }
6533265edafSLaurent Pinchart 
gb_camera_capture(struct gb_camera * gcam,u32 request_id,unsigned int streams,unsigned int num_frames,size_t settings_size,const void * settings)6543265edafSLaurent Pinchart static int gb_camera_capture(struct gb_camera *gcam, u32 request_id,
6553265edafSLaurent Pinchart 			     unsigned int streams, unsigned int num_frames,
6563265edafSLaurent Pinchart 			     size_t settings_size, const void *settings)
6573265edafSLaurent Pinchart {
6583265edafSLaurent Pinchart 	struct gb_camera_capture_request *req;
6593265edafSLaurent Pinchart 	size_t req_size;
660b9f71bc8SJohan Hovold 	int ret;
6613265edafSLaurent Pinchart 
6623265edafSLaurent Pinchart 	if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE)
6633265edafSLaurent Pinchart 		return -EINVAL;
6643265edafSLaurent Pinchart 
6653265edafSLaurent Pinchart 	req_size = sizeof(*req) + settings_size;
6663265edafSLaurent Pinchart 	req = kmalloc(req_size, GFP_KERNEL);
6673265edafSLaurent Pinchart 	if (!req)
6683265edafSLaurent Pinchart 		return -ENOMEM;
6693265edafSLaurent Pinchart 
670c6622216SLaurent Pinchart 	req->request_id = cpu_to_le32(request_id);
6713265edafSLaurent Pinchart 	req->streams = streams;
6723265edafSLaurent Pinchart 	req->padding = 0;
673c6622216SLaurent Pinchart 	req->num_frames = cpu_to_le16(num_frames);
6743265edafSLaurent Pinchart 	memcpy(req->settings, settings, settings_size);
6753265edafSLaurent Pinchart 
67617ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
67717ca6770SEvgeniy Borisov 
67817ca6770SEvgeniy Borisov 	if (!gcam->connection) {
67917ca6770SEvgeniy Borisov 		ret = -EINVAL;
68017ca6770SEvgeniy Borisov 		goto done;
68117ca6770SEvgeniy Borisov 	}
68217ca6770SEvgeniy Borisov 
683b9f71bc8SJohan Hovold 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE,
6843265edafSLaurent Pinchart 				req, req_size, NULL, 0);
68517ca6770SEvgeniy Borisov done:
68617ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
687b9f71bc8SJohan Hovold 
688b9f71bc8SJohan Hovold 	kfree(req);
689b9f71bc8SJohan Hovold 
690b9f71bc8SJohan Hovold 	return ret;
6913265edafSLaurent Pinchart }
6923265edafSLaurent Pinchart 
gb_camera_flush(struct gb_camera * gcam,u32 * request_id)6933265edafSLaurent Pinchart static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id)
6943265edafSLaurent Pinchart {
6953265edafSLaurent Pinchart 	struct gb_camera_flush_response resp;
6963265edafSLaurent Pinchart 	int ret;
6973265edafSLaurent Pinchart 
69817ca6770SEvgeniy Borisov 	mutex_lock(&gcam->mutex);
69917ca6770SEvgeniy Borisov 
70017ca6770SEvgeniy Borisov 	if (!gcam->connection) {
70117ca6770SEvgeniy Borisov 		ret = -EINVAL;
70217ca6770SEvgeniy Borisov 		goto done;
70317ca6770SEvgeniy Borisov 	}
70417ca6770SEvgeniy Borisov 
7053265edafSLaurent Pinchart 	ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0,
7063265edafSLaurent Pinchart 				&resp, sizeof(resp));
70717ca6770SEvgeniy Borisov 
7083265edafSLaurent Pinchart 	if (ret < 0)
70917ca6770SEvgeniy Borisov 		goto done;
7103265edafSLaurent Pinchart 
7113265edafSLaurent Pinchart 	if (request_id)
712c6622216SLaurent Pinchart 		*request_id = le32_to_cpu(resp.request_id);
7133265edafSLaurent Pinchart 
71417ca6770SEvgeniy Borisov done:
71517ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
71617ca6770SEvgeniy Borisov 
71717ca6770SEvgeniy Borisov 	return ret;
7183265edafSLaurent Pinchart }
7193265edafSLaurent Pinchart 
gb_camera_request_handler(struct gb_operation * op)72068b66c28SLaurent Pinchart static int gb_camera_request_handler(struct gb_operation *op)
7213265edafSLaurent Pinchart {
7220ec30632SGreg Kroah-Hartman 	struct gb_camera *gcam = gb_connection_get_data(op->connection);
7233265edafSLaurent Pinchart 	struct gb_camera_metadata_request *payload;
7243265edafSLaurent Pinchart 	struct gb_message *request;
7253265edafSLaurent Pinchart 
72668b66c28SLaurent Pinchart 	if (op->type != GB_CAMERA_TYPE_METADATA) {
72768b66c28SLaurent Pinchart 		gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type);
7283265edafSLaurent Pinchart 		return -EINVAL;
7293265edafSLaurent Pinchart 	}
7303265edafSLaurent Pinchart 
7313265edafSLaurent Pinchart 	request = op->request;
7323265edafSLaurent Pinchart 
7333265edafSLaurent Pinchart 	if (request->payload_size < sizeof(*payload)) {
7343265edafSLaurent Pinchart 		gcam_err(gcam, "Wrong event size received (%zu < %zu)\n",
7353265edafSLaurent Pinchart 			 request->payload_size, sizeof(*payload));
7363265edafSLaurent Pinchart 		return -EINVAL;
7373265edafSLaurent Pinchart 	}
7383265edafSLaurent Pinchart 
7393265edafSLaurent Pinchart 	payload = request->payload;
7403265edafSLaurent Pinchart 
7413265edafSLaurent Pinchart 	gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n",
7423265edafSLaurent Pinchart 		 payload->request_id, payload->frame_number, payload->stream);
7433265edafSLaurent Pinchart 
7443265edafSLaurent Pinchart 	return 0;
7453265edafSLaurent Pinchart }
7463265edafSLaurent Pinchart 
7473265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
748127bada1SJacopo Mondi  * Interface with HOST gmp camera.
7493a8dba4eSGjorgji Rosikopulos  */
gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code)7503a8dba4eSGjorgji Rosikopulos static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code)
7513a8dba4eSGjorgji Rosikopulos {
7523a8dba4eSGjorgji Rosikopulos 	unsigned int i;
7533a8dba4eSGjorgji Rosikopulos 
7546cc27048SJacopo Mondi 	for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) {
7556cc27048SJacopo Mondi 		if (gb_fmt_info[i].mbus_code == mbus_code)
7566cc27048SJacopo Mondi 			return gb_fmt_info[i].gb_format;
7573a8dba4eSGjorgji Rosikopulos 	}
7586cc27048SJacopo Mondi 	return gb_fmt_info[0].gb_format;
7593a8dba4eSGjorgji Rosikopulos }
7603a8dba4eSGjorgji Rosikopulos 
gb_camera_gb_to_mbus(u16 gb_fmt)7613a8dba4eSGjorgji Rosikopulos static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt)
7623a8dba4eSGjorgji Rosikopulos {
7633a8dba4eSGjorgji Rosikopulos 	unsigned int i;
7643a8dba4eSGjorgji Rosikopulos 
7656cc27048SJacopo Mondi 	for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) {
7666cc27048SJacopo Mondi 		if (gb_fmt_info[i].gb_format == gb_fmt)
7676cc27048SJacopo Mondi 			return gb_fmt_info[i].mbus_code;
7683a8dba4eSGjorgji Rosikopulos 	}
7696cc27048SJacopo Mondi 	return gb_fmt_info[0].mbus_code;
7703a8dba4eSGjorgji Rosikopulos }
7713a8dba4eSGjorgji Rosikopulos 
gb_camera_op_capabilities(void * priv,char * data,size_t len)772a883b0ebSJacopo Mondi static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len)
773a883b0ebSJacopo Mondi {
774a883b0ebSJacopo Mondi 	struct gb_camera *gcam = priv;
775a883b0ebSJacopo Mondi 	size_t capabilities_len = len;
776a883b0ebSJacopo Mondi 	int ret;
777a883b0ebSJacopo Mondi 
778a883b0ebSJacopo Mondi 	ret = gb_camera_capabilities(gcam, data, &capabilities_len);
779a883b0ebSJacopo Mondi 	if (ret)
780a883b0ebSJacopo Mondi 		return ret;
781a883b0ebSJacopo Mondi 
782a883b0ebSJacopo Mondi 	return capabilities_len;
783a883b0ebSJacopo Mondi }
784a883b0ebSJacopo Mondi 
gb_camera_op_configure_streams(void * priv,unsigned int * nstreams,unsigned int * flags,struct gb_camera_stream * streams,struct gb_camera_csi_params * csi_params)7855b032710SGjorgji Rosikopulos static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams,
786b4905038SEvgeniy Borisov 		unsigned int *flags, struct gb_camera_stream *streams,
787b4905038SEvgeniy Borisov 		struct gb_camera_csi_params *csi_params)
7883a8dba4eSGjorgji Rosikopulos {
7893a8dba4eSGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
7903a8dba4eSGjorgji Rosikopulos 	struct gb_camera_stream_config *gb_streams;
7915b032710SGjorgji Rosikopulos 	unsigned int gb_flags = 0;
7925b032710SGjorgji Rosikopulos 	unsigned int gb_nstreams = *nstreams;
7933a8dba4eSGjorgji Rosikopulos 	unsigned int i;
7943a8dba4eSGjorgji Rosikopulos 	int ret;
7953a8dba4eSGjorgji Rosikopulos 
7965b032710SGjorgji Rosikopulos 	if (gb_nstreams > GB_CAMERA_MAX_STREAMS)
7973a8dba4eSGjorgji Rosikopulos 		return -EINVAL;
7983a8dba4eSGjorgji Rosikopulos 
799c4fc2ebcSElise Lennion 	gb_streams = kcalloc(gb_nstreams, sizeof(*gb_streams), GFP_KERNEL);
8003a8dba4eSGjorgji Rosikopulos 	if (!gb_streams)
8013a8dba4eSGjorgji Rosikopulos 		return -ENOMEM;
8023a8dba4eSGjorgji Rosikopulos 
8035b032710SGjorgji Rosikopulos 	for (i = 0; i < gb_nstreams; i++) {
8043a8dba4eSGjorgji Rosikopulos 		gb_streams[i].width = streams[i].width;
8053a8dba4eSGjorgji Rosikopulos 		gb_streams[i].height = streams[i].height;
8063a8dba4eSGjorgji Rosikopulos 		gb_streams[i].format =
8073a8dba4eSGjorgji Rosikopulos 			gb_camera_mbus_to_gb(streams[i].pixel_code);
8083a8dba4eSGjorgji Rosikopulos 	}
8093a8dba4eSGjorgji Rosikopulos 
8105b032710SGjorgji Rosikopulos 	if (*flags & GB_CAMERA_IN_FLAG_TEST)
8115b032710SGjorgji Rosikopulos 		gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY;
8125b032710SGjorgji Rosikopulos 
8135b032710SGjorgji Rosikopulos 	ret = gb_camera_configure_streams(gcam, &gb_nstreams,
814b4905038SEvgeniy Borisov 					  &gb_flags, gb_streams, csi_params);
8153a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
8163a8dba4eSGjorgji Rosikopulos 		goto done;
8175b032710SGjorgji Rosikopulos 	if (gb_nstreams > *nstreams) {
8185b032710SGjorgji Rosikopulos 		ret = -EINVAL;
8195b032710SGjorgji Rosikopulos 		goto done;
8205b032710SGjorgji Rosikopulos 	}
8213a8dba4eSGjorgji Rosikopulos 
8225b032710SGjorgji Rosikopulos 	*flags = 0;
8235b032710SGjorgji Rosikopulos 	if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)
8245b032710SGjorgji Rosikopulos 		*flags |= GB_CAMERA_OUT_FLAG_ADJUSTED;
8255b032710SGjorgji Rosikopulos 
8265b032710SGjorgji Rosikopulos 	for (i = 0; i < gb_nstreams; i++) {
8273a8dba4eSGjorgji Rosikopulos 		streams[i].width = gb_streams[i].width;
8283a8dba4eSGjorgji Rosikopulos 		streams[i].height = gb_streams[i].height;
8293a8dba4eSGjorgji Rosikopulos 		streams[i].vc = gb_streams[i].vc;
8303a8dba4eSGjorgji Rosikopulos 		streams[i].dt[0] = gb_streams[i].dt[0];
8313a8dba4eSGjorgji Rosikopulos 		streams[i].dt[1] = gb_streams[i].dt[1];
8323a8dba4eSGjorgji Rosikopulos 		streams[i].max_size = gb_streams[i].max_size;
8333a8dba4eSGjorgji Rosikopulos 		streams[i].pixel_code =
8343a8dba4eSGjorgji Rosikopulos 			gb_camera_gb_to_mbus(gb_streams[i].format);
8353a8dba4eSGjorgji Rosikopulos 	}
8365b032710SGjorgji Rosikopulos 	*nstreams = gb_nstreams;
8373a8dba4eSGjorgji Rosikopulos 
8383a8dba4eSGjorgji Rosikopulos done:
8393a8dba4eSGjorgji Rosikopulos 	kfree(gb_streams);
8403a8dba4eSGjorgji Rosikopulos 	return ret;
8413a8dba4eSGjorgji Rosikopulos }
8423a8dba4eSGjorgji Rosikopulos 
gb_camera_op_capture(void * priv,u32 request_id,unsigned int streams,unsigned int num_frames,size_t settings_size,const void * settings)8433a8dba4eSGjorgji Rosikopulos static int gb_camera_op_capture(void *priv, u32 request_id,
8443a8dba4eSGjorgji Rosikopulos 				unsigned int streams, unsigned int num_frames,
8453a8dba4eSGjorgji Rosikopulos 				size_t settings_size, const void *settings)
8463a8dba4eSGjorgji Rosikopulos {
8471472ec67SGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
8481472ec67SGjorgji Rosikopulos 
8491472ec67SGjorgji Rosikopulos 	return gb_camera_capture(gcam, request_id, streams, num_frames,
8503a8dba4eSGjorgji Rosikopulos 				 settings_size, settings);
8513a8dba4eSGjorgji Rosikopulos }
8523a8dba4eSGjorgji Rosikopulos 
gb_camera_op_flush(void * priv,u32 * request_id)8533a8dba4eSGjorgji Rosikopulos static int gb_camera_op_flush(void *priv, u32 *request_id)
8543a8dba4eSGjorgji Rosikopulos {
8551472ec67SGjorgji Rosikopulos 	struct gb_camera *gcam = priv;
8561472ec67SGjorgji Rosikopulos 
8571472ec67SGjorgji Rosikopulos 	return gb_camera_flush(gcam, request_id);
8583a8dba4eSGjorgji Rosikopulos }
8593a8dba4eSGjorgji Rosikopulos 
8601472ec67SGjorgji Rosikopulos static const struct gb_camera_ops gb_cam_ops = {
8611472ec67SGjorgji Rosikopulos 	.capabilities = gb_camera_op_capabilities,
8621472ec67SGjorgji Rosikopulos 	.configure_streams = gb_camera_op_configure_streams,
8631472ec67SGjorgji Rosikopulos 	.capture = gb_camera_op_capture,
8641472ec67SGjorgji Rosikopulos 	.flush = gb_camera_op_flush,
8651472ec67SGjorgji Rosikopulos };
8661472ec67SGjorgji Rosikopulos 
8673a8dba4eSGjorgji Rosikopulos /* -----------------------------------------------------------------------------
8683265edafSLaurent Pinchart  * DebugFS
8693265edafSLaurent Pinchart  */
87048b15a9bSLaurent Pinchart 
gb_camera_debugfs_capabilities(struct gb_camera * gcam,char * buf,size_t len)8713265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam,
8723265edafSLaurent Pinchart 					      char *buf, size_t len)
8733265edafSLaurent Pinchart {
87448b15a9bSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
87548b15a9bSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES];
87648b15a9bSLaurent Pinchart 	size_t size = 1024;
87748b15a9bSLaurent Pinchart 	unsigned int i;
87848b15a9bSLaurent Pinchart 	u8 *caps;
87948b15a9bSLaurent Pinchart 	int ret;
88048b15a9bSLaurent Pinchart 
88148b15a9bSLaurent Pinchart 	caps = kmalloc(size, GFP_KERNEL);
88248b15a9bSLaurent Pinchart 	if (!caps)
88348b15a9bSLaurent Pinchart 		return -ENOMEM;
88448b15a9bSLaurent Pinchart 
88548b15a9bSLaurent Pinchart 	ret = gb_camera_capabilities(gcam, caps, &size);
88648b15a9bSLaurent Pinchart 	if (ret < 0)
88748b15a9bSLaurent Pinchart 		goto done;
88848b15a9bSLaurent Pinchart 
88948b15a9bSLaurent Pinchart 	/*
89048b15a9bSLaurent Pinchart 	 * hex_dump_to_buffer() doesn't return the number of bytes dumped prior
89148b15a9bSLaurent Pinchart 	 * to v4.0, we need our own implementation :-(
89248b15a9bSLaurent Pinchart 	 */
89348b15a9bSLaurent Pinchart 	buffer->length = 0;
89448b15a9bSLaurent Pinchart 
89548b15a9bSLaurent Pinchart 	for (i = 0; i < size; i += 16) {
89648b15a9bSLaurent Pinchart 		unsigned int nbytes = min_t(unsigned int, size - i, 16);
89748b15a9bSLaurent Pinchart 
89848b15a9bSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
89948b15a9bSLaurent Pinchart 					  "%*ph\n", nbytes, caps + i);
90048b15a9bSLaurent Pinchart 	}
90148b15a9bSLaurent Pinchart 
90248b15a9bSLaurent Pinchart done:
90348b15a9bSLaurent Pinchart 	kfree(caps);
90448b15a9bSLaurent Pinchart 	return ret;
9053265edafSLaurent Pinchart }
9063265edafSLaurent Pinchart 
gb_camera_debugfs_configure_streams(struct gb_camera * gcam,char * buf,size_t len)9073265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam,
9083265edafSLaurent Pinchart 						   char *buf, size_t len)
9093265edafSLaurent Pinchart {
9103265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
9113265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS];
9123265edafSLaurent Pinchart 	struct gb_camera_stream_config *streams;
9133265edafSLaurent Pinchart 	unsigned int nstreams;
914b787d413SJacopo Mondi 	unsigned int flags;
9153265edafSLaurent Pinchart 	unsigned int i;
9163265edafSLaurent Pinchart 	char *token;
9173265edafSLaurent Pinchart 	int ret;
9183265edafSLaurent Pinchart 
9193265edafSLaurent Pinchart 	/* Retrieve number of streams to configure */
920b787d413SJacopo Mondi 	token = strsep(&buf, ";");
921b5c54c45SSumit Pundir 	if (!token)
9223265edafSLaurent Pinchart 		return -EINVAL;
9233265edafSLaurent Pinchart 
9243265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &nstreams);
9253265edafSLaurent Pinchart 	if (ret < 0)
9263265edafSLaurent Pinchart 		return ret;
9273265edafSLaurent Pinchart 
9283265edafSLaurent Pinchart 	if (nstreams > GB_CAMERA_MAX_STREAMS)
9293265edafSLaurent Pinchart 		return -EINVAL;
9303265edafSLaurent Pinchart 
931b787d413SJacopo Mondi 	token = strsep(&buf, ";");
932b5c54c45SSumit Pundir 	if (!token)
933b787d413SJacopo Mondi 		return -EINVAL;
934b787d413SJacopo Mondi 
935b787d413SJacopo Mondi 	ret = kstrtouint(token, 10, &flags);
936b787d413SJacopo Mondi 	if (ret < 0)
937b787d413SJacopo Mondi 		return ret;
938b787d413SJacopo Mondi 
9393265edafSLaurent Pinchart 	/* For each stream to configure parse width, height and format */
940c4fc2ebcSElise Lennion 	streams = kcalloc(nstreams, sizeof(*streams), GFP_KERNEL);
9413265edafSLaurent Pinchart 	if (!streams)
9423265edafSLaurent Pinchart 		return -ENOMEM;
9433265edafSLaurent Pinchart 
9443265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
9453265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
9463265edafSLaurent Pinchart 
9473265edafSLaurent Pinchart 		/* width */
9483265edafSLaurent Pinchart 		token = strsep(&buf, ";");
949b5c54c45SSumit Pundir 		if (!token) {
9503265edafSLaurent Pinchart 			ret = -EINVAL;
9513265edafSLaurent Pinchart 			goto done;
9523265edafSLaurent Pinchart 		}
9533265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->width);
9543265edafSLaurent Pinchart 		if (ret < 0)
9553265edafSLaurent Pinchart 			goto done;
9563265edafSLaurent Pinchart 
9573265edafSLaurent Pinchart 		/* height */
9583265edafSLaurent Pinchart 		token = strsep(&buf, ";");
959b5c54c45SSumit Pundir 		if (!token)
9603265edafSLaurent Pinchart 			goto done;
9613265edafSLaurent Pinchart 
9623265edafSLaurent Pinchart 		ret = kstrtouint(token, 10, &stream->height);
9633265edafSLaurent Pinchart 		if (ret < 0)
9643265edafSLaurent Pinchart 			goto done;
9653265edafSLaurent Pinchart 
9663265edafSLaurent Pinchart 		/* Image format code */
9673265edafSLaurent Pinchart 		token = strsep(&buf, ";");
968b5c54c45SSumit Pundir 		if (!token)
9693265edafSLaurent Pinchart 			goto done;
9703265edafSLaurent Pinchart 
9713265edafSLaurent Pinchart 		ret = kstrtouint(token, 16, &stream->format);
9723265edafSLaurent Pinchart 		if (ret < 0)
9733265edafSLaurent Pinchart 			goto done;
9743265edafSLaurent Pinchart 	}
9753265edafSLaurent Pinchart 
976b4905038SEvgeniy Borisov 	ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams,
977b4905038SEvgeniy Borisov 					  NULL);
9783265edafSLaurent Pinchart 	if (ret < 0)
9793265edafSLaurent Pinchart 		goto done;
9803265edafSLaurent Pinchart 
9814068487cSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags);
9823265edafSLaurent Pinchart 
9833265edafSLaurent Pinchart 	for (i = 0; i < nstreams; ++i) {
9843265edafSLaurent Pinchart 		struct gb_camera_stream_config *stream = &streams[i];
9853265edafSLaurent Pinchart 
9863265edafSLaurent Pinchart 		buffer->length += sprintf(buffer->data + buffer->length,
9873265edafSLaurent Pinchart 					  "%u;%u;%u;%u;%u;%u;%u;",
9883265edafSLaurent Pinchart 					  stream->width, stream->height,
9893265edafSLaurent Pinchart 					  stream->format, stream->vc,
9903265edafSLaurent Pinchart 					  stream->dt[0], stream->dt[1],
9913265edafSLaurent Pinchart 					  stream->max_size);
9923265edafSLaurent Pinchart 	}
9933265edafSLaurent Pinchart 
9943265edafSLaurent Pinchart 	ret = len;
9953265edafSLaurent Pinchart 
9963265edafSLaurent Pinchart done:
9973265edafSLaurent Pinchart 	kfree(streams);
9983265edafSLaurent Pinchart 	return ret;
9993265edafSLaurent Pinchart };
10003265edafSLaurent Pinchart 
gb_camera_debugfs_capture(struct gb_camera * gcam,char * buf,size_t len)10013265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam,
10023265edafSLaurent Pinchart 					 char *buf, size_t len)
10033265edafSLaurent Pinchart {
10043265edafSLaurent Pinchart 	unsigned int request_id;
10053265edafSLaurent Pinchart 	unsigned int streams_mask;
10063265edafSLaurent Pinchart 	unsigned int num_frames;
10073265edafSLaurent Pinchart 	char *token;
10083265edafSLaurent Pinchart 	int ret;
10093265edafSLaurent Pinchart 
10103265edafSLaurent Pinchart 	/* Request id */
10113265edafSLaurent Pinchart 	token = strsep(&buf, ";");
1012b5c54c45SSumit Pundir 	if (!token)
10133265edafSLaurent Pinchart 		return -EINVAL;
10143265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &request_id);
10153265edafSLaurent Pinchart 	if (ret < 0)
10163265edafSLaurent Pinchart 		return ret;
10173265edafSLaurent Pinchart 
10183265edafSLaurent Pinchart 	/* Stream mask */
10193265edafSLaurent Pinchart 	token = strsep(&buf, ";");
1020b5c54c45SSumit Pundir 	if (!token)
10213265edafSLaurent Pinchart 		return -EINVAL;
10223265edafSLaurent Pinchart 	ret = kstrtouint(token, 16, &streams_mask);
10233265edafSLaurent Pinchart 	if (ret < 0)
10243265edafSLaurent Pinchart 		return ret;
10253265edafSLaurent Pinchart 
10263265edafSLaurent Pinchart 	/* number of frames */
10273265edafSLaurent Pinchart 	token = strsep(&buf, ";");
1028b5c54c45SSumit Pundir 	if (!token)
10293265edafSLaurent Pinchart 		return -EINVAL;
10303265edafSLaurent Pinchart 	ret = kstrtouint(token, 10, &num_frames);
10313265edafSLaurent Pinchart 	if (ret < 0)
10323265edafSLaurent Pinchart 		return ret;
10333265edafSLaurent Pinchart 
10343265edafSLaurent Pinchart 	ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0,
10353265edafSLaurent Pinchart 				NULL);
10363265edafSLaurent Pinchart 	if (ret < 0)
10373265edafSLaurent Pinchart 		return ret;
10383265edafSLaurent Pinchart 
10393265edafSLaurent Pinchart 	return len;
10403265edafSLaurent Pinchart }
10413265edafSLaurent Pinchart 
gb_camera_debugfs_flush(struct gb_camera * gcam,char * buf,size_t len)10423265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam,
10433265edafSLaurent Pinchart 				       char *buf, size_t len)
10443265edafSLaurent Pinchart {
10453265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer =
10463265edafSLaurent Pinchart 		&gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH];
10473265edafSLaurent Pinchart 	unsigned int req_id;
10483265edafSLaurent Pinchart 	int ret;
10493265edafSLaurent Pinchart 
10503265edafSLaurent Pinchart 	ret = gb_camera_flush(gcam, &req_id);
10513265edafSLaurent Pinchart 	if (ret < 0)
10523265edafSLaurent Pinchart 		return ret;
10533265edafSLaurent Pinchart 
10543265edafSLaurent Pinchart 	buffer->length = sprintf(buffer->data, "%u", req_id);
10553265edafSLaurent Pinchart 
10563265edafSLaurent Pinchart 	return len;
10573265edafSLaurent Pinchart }
10583265edafSLaurent Pinchart 
10593265edafSLaurent Pinchart struct gb_camera_debugfs_entry {
10603265edafSLaurent Pinchart 	const char *name;
10613265edafSLaurent Pinchart 	unsigned int mask;
10623265edafSLaurent Pinchart 	unsigned int buffer;
10633265edafSLaurent Pinchart 	ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len);
10643265edafSLaurent Pinchart };
10653265edafSLaurent Pinchart 
10663265edafSLaurent Pinchart static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = {
10673265edafSLaurent Pinchart 	{
10683265edafSLaurent Pinchart 		.name = "capabilities",
10690a8d8522SDerek Robson 		.mask = S_IFREG | 0444,
10703265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
10713265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capabilities,
10723265edafSLaurent Pinchart 	}, {
10733265edafSLaurent Pinchart 		.name = "configure_streams",
10740a8d8522SDerek Robson 		.mask = S_IFREG | 0666,
10753265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
10763265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_configure_streams,
10773265edafSLaurent Pinchart 	}, {
10783265edafSLaurent Pinchart 		.name = "capture",
10790a8d8522SDerek Robson 		.mask = S_IFREG | 0666,
10803265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
10813265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capture,
10823265edafSLaurent Pinchart 	}, {
10833265edafSLaurent Pinchart 		.name = "flush",
10840a8d8522SDerek Robson 		.mask = S_IFREG | 0666,
10853265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
10863265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_flush,
10873265edafSLaurent Pinchart 	},
10883265edafSLaurent Pinchart };
10893265edafSLaurent Pinchart 
gb_camera_debugfs_read(struct file * file,char __user * buf,size_t len,loff_t * offset)10903265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf,
10913265edafSLaurent Pinchart 				      size_t len, loff_t *offset)
10923265edafSLaurent Pinchart {
10933265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
109445063097SAl Viro 	struct gb_camera *gcam = file_inode(file)->i_private;
10953265edafSLaurent Pinchart 	struct gb_camera_debugfs_buffer *buffer;
10963265edafSLaurent Pinchart 	ssize_t ret;
10973265edafSLaurent Pinchart 
10983265edafSLaurent Pinchart 	/* For read-only entries the operation is triggered by a read. */
10990a8d8522SDerek Robson 	if (!(op->mask & 0222)) {
11003265edafSLaurent Pinchart 		ret = op->execute(gcam, NULL, 0);
11013265edafSLaurent Pinchart 		if (ret < 0)
11023265edafSLaurent Pinchart 			return ret;
11033265edafSLaurent Pinchart 	}
11043265edafSLaurent Pinchart 
11053265edafSLaurent Pinchart 	buffer = &gcam->debugfs.buffers[op->buffer];
11063265edafSLaurent Pinchart 
11073265edafSLaurent Pinchart 	return simple_read_from_buffer(buf, len, offset, buffer->data,
11083265edafSLaurent Pinchart 				       buffer->length);
11093265edafSLaurent Pinchart }
11103265edafSLaurent Pinchart 
gb_camera_debugfs_write(struct file * file,const char __user * buf,size_t len,loff_t * offset)11113265edafSLaurent Pinchart static ssize_t gb_camera_debugfs_write(struct file *file,
11123265edafSLaurent Pinchart 				       const char __user *buf, size_t len,
11133265edafSLaurent Pinchart 				       loff_t *offset)
11143265edafSLaurent Pinchart {
11153265edafSLaurent Pinchart 	const struct gb_camera_debugfs_entry *op = file->private_data;
111645063097SAl Viro 	struct gb_camera *gcam = file_inode(file)->i_private;
11173265edafSLaurent Pinchart 	ssize_t ret;
11183265edafSLaurent Pinchart 	char *kbuf;
11193265edafSLaurent Pinchart 
11203265edafSLaurent Pinchart 	if (len > 1024)
11213265edafSLaurent Pinchart 		return -EINVAL;
11223265edafSLaurent Pinchart 
1123*f032e2cdSYang Yingliang 	kbuf = memdup_user_nul(buf, len);
1124*f032e2cdSYang Yingliang 	if (IS_ERR(kbuf))
1125*f032e2cdSYang Yingliang 		return PTR_ERR(kbuf);
11263265edafSLaurent Pinchart 
11273265edafSLaurent Pinchart 	ret = op->execute(gcam, kbuf, len);
11283265edafSLaurent Pinchart 
11293265edafSLaurent Pinchart done:
11303265edafSLaurent Pinchart 	kfree(kbuf);
11313265edafSLaurent Pinchart 	return ret;
11323265edafSLaurent Pinchart }
11333265edafSLaurent Pinchart 
gb_camera_debugfs_open(struct inode * inode,struct file * file)11343265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file)
11353265edafSLaurent Pinchart {
11363265edafSLaurent Pinchart 	unsigned int i;
11373265edafSLaurent Pinchart 
11383265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
11393265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
11403265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
11413265edafSLaurent Pinchart 
11424dda744cSGreg Kroah-Hartman 		if (!strcmp(file->f_path.dentry->d_iname, entry->name)) {
11433265edafSLaurent Pinchart 			file->private_data = (void *)entry;
11443265edafSLaurent Pinchart 			break;
11453265edafSLaurent Pinchart 		}
11463265edafSLaurent Pinchart 	}
11473265edafSLaurent Pinchart 
11483265edafSLaurent Pinchart 	return 0;
11493265edafSLaurent Pinchart }
11503265edafSLaurent Pinchart 
11513265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = {
11523265edafSLaurent Pinchart 	.open = gb_camera_debugfs_open,
11533265edafSLaurent Pinchart 	.read = gb_camera_debugfs_read,
11543265edafSLaurent Pinchart 	.write = gb_camera_debugfs_write,
11553265edafSLaurent Pinchart };
11563265edafSLaurent Pinchart 
gb_camera_debugfs_init(struct gb_camera * gcam)11573265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam)
11583265edafSLaurent Pinchart {
11593265edafSLaurent Pinchart 	struct gb_connection *connection = gcam->connection;
11603265edafSLaurent Pinchart 	char dirname[27];
11613265edafSLaurent Pinchart 	unsigned int i;
11623265edafSLaurent Pinchart 
11633265edafSLaurent Pinchart 	/*
11643265edafSLaurent Pinchart 	 * Create root debugfs entry and a file entry for each camera operation.
11653265edafSLaurent Pinchart 	 */
11663265edafSLaurent Pinchart 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
116768b66c28SLaurent Pinchart 		 gcam->bundle->id);
11683265edafSLaurent Pinchart 
11693265edafSLaurent Pinchart 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
11703265edafSLaurent Pinchart 
117142bc47b3SKees Cook 	gcam->debugfs.buffers =
117242bc47b3SKees Cook 		vmalloc(array_size(GB_CAMERA_DEBUGFS_BUFFER_MAX,
117342bc47b3SKees Cook 				   sizeof(*gcam->debugfs.buffers)));
11743265edafSLaurent Pinchart 	if (!gcam->debugfs.buffers)
11753265edafSLaurent Pinchart 		return -ENOMEM;
11763265edafSLaurent Pinchart 
11773265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
11783265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
11793265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
11803265edafSLaurent Pinchart 
11813265edafSLaurent Pinchart 		gcam->debugfs.buffers[i].length = 0;
11823265edafSLaurent Pinchart 
118300aaa6b1SGreg Kroah-Hartman 		debugfs_create_file(entry->name, entry->mask,
11843265edafSLaurent Pinchart 				    gcam->debugfs.root, gcam,
11853265edafSLaurent Pinchart 				    &gb_camera_debugfs_ops);
11863265edafSLaurent Pinchart 	}
11873265edafSLaurent Pinchart 
11883265edafSLaurent Pinchart 	return 0;
11893265edafSLaurent Pinchart }
11903265edafSLaurent Pinchart 
gb_camera_debugfs_cleanup(struct gb_camera * gcam)11913265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam)
11923265edafSLaurent Pinchart {
11933265edafSLaurent Pinchart 	debugfs_remove_recursive(gcam->debugfs.root);
11943265edafSLaurent Pinchart 
11953265edafSLaurent Pinchart 	vfree(gcam->debugfs.buffers);
11963265edafSLaurent Pinchart }
11973265edafSLaurent Pinchart 
11983265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
11993265edafSLaurent Pinchart  * Init & Cleanup
12003265edafSLaurent Pinchart  */
12013265edafSLaurent Pinchart 
gb_camera_cleanup(struct gb_camera * gcam)12023265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam)
12033265edafSLaurent Pinchart {
12043265edafSLaurent Pinchart 	gb_camera_debugfs_cleanup(gcam);
12053265edafSLaurent Pinchart 
12069120b906SLaurent Pinchart 	mutex_lock(&gcam->mutex);
12073ba9fa5cSJohan Hovold 	if (gcam->data_connection) {
12083ba9fa5cSJohan Hovold 		gb_connection_disable(gcam->data_connection);
12093ba9fa5cSJohan Hovold 		gb_connection_destroy(gcam->data_connection);
121017ca6770SEvgeniy Borisov 		gcam->data_connection = NULL;
12113265edafSLaurent Pinchart 	}
12123265edafSLaurent Pinchart 
121368b66c28SLaurent Pinchart 	if (gcam->connection) {
121468b66c28SLaurent Pinchart 		gb_connection_disable(gcam->connection);
121568b66c28SLaurent Pinchart 		gb_connection_destroy(gcam->connection);
121617ca6770SEvgeniy Borisov 		gcam->connection = NULL;
121717ca6770SEvgeniy Borisov 	}
121817ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
121968b66c28SLaurent Pinchart }
122068b66c28SLaurent Pinchart 
gb_camera_release_module(struct kref * ref)122117ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref)
122217ca6770SEvgeniy Borisov {
122317ca6770SEvgeniy Borisov 	struct gb_camera_module *cam_mod =
122417ca6770SEvgeniy Borisov 		container_of(ref, struct gb_camera_module, refcount);
122517ca6770SEvgeniy Borisov 	kfree(cam_mod->priv);
12263265edafSLaurent Pinchart }
12273265edafSLaurent Pinchart 
gb_camera_probe(struct gb_bundle * bundle,const struct greybus_bundle_id * id)122868b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle,
122968b66c28SLaurent Pinchart 			   const struct greybus_bundle_id *id)
12303265edafSLaurent Pinchart {
123168b66c28SLaurent Pinchart 	struct gb_connection *conn;
12323265edafSLaurent Pinchart 	struct gb_camera *gcam;
123368b66c28SLaurent Pinchart 	u16 mgmt_cport_id = 0;
123468b66c28SLaurent Pinchart 	u16 data_cport_id = 0;
123568b66c28SLaurent Pinchart 	unsigned int i;
12363265edafSLaurent Pinchart 	int ret;
12373265edafSLaurent Pinchart 
123868b66c28SLaurent Pinchart 	/*
123968b66c28SLaurent Pinchart 	 * The camera bundle must contain exactly two CPorts, one for the
124068b66c28SLaurent Pinchart 	 * camera management protocol and one for the camera data protocol.
124168b66c28SLaurent Pinchart 	 */
124268b66c28SLaurent Pinchart 	if (bundle->num_cports != 2)
124368b66c28SLaurent Pinchart 		return -ENODEV;
124468b66c28SLaurent Pinchart 
124568b66c28SLaurent Pinchart 	for (i = 0; i < bundle->num_cports; ++i) {
124668b66c28SLaurent Pinchart 		struct greybus_descriptor_cport *desc = &bundle->cport_desc[i];
124768b66c28SLaurent Pinchart 
124868b66c28SLaurent Pinchart 		switch (desc->protocol_id) {
124968b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_MGMT:
125068b66c28SLaurent Pinchart 			mgmt_cport_id = le16_to_cpu(desc->id);
125168b66c28SLaurent Pinchart 			break;
125268b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_DATA:
125368b66c28SLaurent Pinchart 			data_cport_id = le16_to_cpu(desc->id);
125468b66c28SLaurent Pinchart 			break;
125568b66c28SLaurent Pinchart 		default:
125668b66c28SLaurent Pinchart 			return -ENODEV;
125768b66c28SLaurent Pinchart 		}
125868b66c28SLaurent Pinchart 	}
125968b66c28SLaurent Pinchart 
126068b66c28SLaurent Pinchart 	if (!mgmt_cport_id || !data_cport_id)
126168b66c28SLaurent Pinchart 		return -ENODEV;
126268b66c28SLaurent Pinchart 
12633265edafSLaurent Pinchart 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
12643265edafSLaurent Pinchart 	if (!gcam)
12653265edafSLaurent Pinchart 		return -ENOMEM;
12663265edafSLaurent Pinchart 
1267d9e4c4eeSViresh Kumar 	mutex_init(&gcam->mutex);
12683265edafSLaurent Pinchart 
12693b8ebfebSLaurent Pinchart 	gcam->bundle = bundle;
12703b8ebfebSLaurent Pinchart 	gcam->state = GB_CAMERA_STATE_UNCONFIGURED;
12719120b906SLaurent Pinchart 	gcam->data_cport_id = data_cport_id;
12723b8ebfebSLaurent Pinchart 
127368b66c28SLaurent Pinchart 	conn = gb_connection_create(bundle, mgmt_cport_id,
127468b66c28SLaurent Pinchart 				    gb_camera_request_handler);
127568b66c28SLaurent Pinchart 	if (IS_ERR(conn)) {
127668b66c28SLaurent Pinchart 		ret = PTR_ERR(conn);
12773ba9fa5cSJohan Hovold 		goto error;
12783ba9fa5cSJohan Hovold 	}
12793ba9fa5cSJohan Hovold 
128068b66c28SLaurent Pinchart 	gcam->connection = conn;
128168b66c28SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
128268b66c28SLaurent Pinchart 
128368b66c28SLaurent Pinchart 	ret = gb_connection_enable(conn);
128468b66c28SLaurent Pinchart 	if (ret)
128568b66c28SLaurent Pinchart 		goto error;
128668b66c28SLaurent Pinchart 
12873265edafSLaurent Pinchart 	ret = gb_camera_debugfs_init(gcam);
12883265edafSLaurent Pinchart 	if (ret < 0)
12893265edafSLaurent Pinchart 		goto error;
12903265edafSLaurent Pinchart 
129117ca6770SEvgeniy Borisov 	gcam->module.priv = gcam;
129217ca6770SEvgeniy Borisov 	gcam->module.ops = &gb_cam_ops;
129317ca6770SEvgeniy Borisov 	gcam->module.interface_id = gcam->connection->intf->interface_id;
129417ca6770SEvgeniy Borisov 	gcam->module.release = gb_camera_release_module;
129517ca6770SEvgeniy Borisov 	ret = gb_camera_register(&gcam->module);
12963a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
12973a8dba4eSGjorgji Rosikopulos 		goto error;
12983a8dba4eSGjorgji Rosikopulos 
129968b66c28SLaurent Pinchart 	greybus_set_drvdata(bundle, gcam);
130068b66c28SLaurent Pinchart 
1301211634f2SDavid Lin 	gb_pm_runtime_put_autosuspend(gcam->bundle);
1302211634f2SDavid Lin 
13033265edafSLaurent Pinchart 	return 0;
13043265edafSLaurent Pinchart 
13053265edafSLaurent Pinchart error:
13063265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
130717ca6770SEvgeniy Borisov 	kfree(gcam);
13083265edafSLaurent Pinchart 	return ret;
13093265edafSLaurent Pinchart }
13103265edafSLaurent Pinchart 
gb_camera_disconnect(struct gb_bundle * bundle)131168b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle)
13123265edafSLaurent Pinchart {
131368b66c28SLaurent Pinchart 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
1314211634f2SDavid Lin 	int ret;
1315211634f2SDavid Lin 
1316211634f2SDavid Lin 	ret = gb_pm_runtime_get_sync(bundle);
1317211634f2SDavid Lin 	if (ret)
1318211634f2SDavid Lin 		gb_pm_runtime_get_noresume(bundle);
13193265edafSLaurent Pinchart 
13203265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
132117ca6770SEvgeniy Borisov 	gb_camera_unregister(&gcam->module);
13223265edafSLaurent Pinchart }
13233265edafSLaurent Pinchart 
132468b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = {
132568b66c28SLaurent Pinchart 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
132668b66c28SLaurent Pinchart 	{ },
13273265edafSLaurent Pinchart };
13283265edafSLaurent Pinchart 
1329948c6227SGreg Kroah-Hartman #ifdef CONFIG_PM
gb_camera_suspend(struct device * dev)1330211634f2SDavid Lin static int gb_camera_suspend(struct device *dev)
1331211634f2SDavid Lin {
1332211634f2SDavid Lin 	struct gb_bundle *bundle = to_gb_bundle(dev);
1333211634f2SDavid Lin 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
1334211634f2SDavid Lin 
1335211634f2SDavid Lin 	if (gcam->data_connection)
1336211634f2SDavid Lin 		gb_connection_disable(gcam->data_connection);
1337211634f2SDavid Lin 
1338211634f2SDavid Lin 	gb_connection_disable(gcam->connection);
1339211634f2SDavid Lin 
1340211634f2SDavid Lin 	return 0;
1341211634f2SDavid Lin }
1342211634f2SDavid Lin 
gb_camera_resume(struct device * dev)1343211634f2SDavid Lin static int gb_camera_resume(struct device *dev)
1344211634f2SDavid Lin {
1345211634f2SDavid Lin 	struct gb_bundle *bundle = to_gb_bundle(dev);
1346211634f2SDavid Lin 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
1347211634f2SDavid Lin 	int ret;
1348211634f2SDavid Lin 
1349211634f2SDavid Lin 	ret = gb_connection_enable(gcam->connection);
1350211634f2SDavid Lin 	if (ret) {
1351211634f2SDavid Lin 		gcam_err(gcam, "failed to enable connection: %d\n", ret);
1352211634f2SDavid Lin 		return ret;
1353211634f2SDavid Lin 	}
1354211634f2SDavid Lin 
1355211634f2SDavid Lin 	if (gcam->data_connection) {
1356211634f2SDavid Lin 		ret = gb_connection_enable(gcam->data_connection);
1357211634f2SDavid Lin 		if (ret) {
1358211634f2SDavid Lin 			gcam_err(gcam,
1359211634f2SDavid Lin 				 "failed to enable data connection: %d\n", ret);
1360211634f2SDavid Lin 			return ret;
1361211634f2SDavid Lin 		}
1362211634f2SDavid Lin 	}
1363211634f2SDavid Lin 
1364211634f2SDavid Lin 	return 0;
1365211634f2SDavid Lin }
1366211634f2SDavid Lin #endif
1367211634f2SDavid Lin 
1368211634f2SDavid Lin static const struct dev_pm_ops gb_camera_pm_ops = {
1369211634f2SDavid Lin 	SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL)
1370211634f2SDavid Lin };
1371211634f2SDavid Lin 
137268b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = {
137368b66c28SLaurent Pinchart 	.name		= "camera",
137468b66c28SLaurent Pinchart 	.probe		= gb_camera_probe,
137568b66c28SLaurent Pinchart 	.disconnect	= gb_camera_disconnect,
137668b66c28SLaurent Pinchart 	.id_table	= gb_camera_id_table,
1377211634f2SDavid Lin 	.driver.pm	= &gb_camera_pm_ops,
137868b66c28SLaurent Pinchart };
137968b66c28SLaurent Pinchart 
138068b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver);
13813265edafSLaurent Pinchart 
13823265edafSLaurent Pinchart MODULE_LICENSE("GPL v2");
1383