xref: /openbmc/linux/drivers/staging/greybus/camera.c (revision c9161d72)
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
459120b906SLaurent Pinchart  * @data_cport_id: the data CPort ID on the module side
463b8ebfebSLaurent Pinchart  * @mutex: protects the connection and state fields
473b8ebfebSLaurent Pinchart  * @state: the current module state
483265edafSLaurent Pinchart  * @debugfs: debugfs entries for camera protocol operations testing
49c3d77f71SGjorgji Rosikopulos  * @module: Greybus camera module registered to HOST processor.
503265edafSLaurent Pinchart  */
513265edafSLaurent Pinchart struct gb_camera {
5268b66c28SLaurent Pinchart 	struct gb_bundle *bundle;
533265edafSLaurent Pinchart 	struct gb_connection *connection;
543ba9fa5cSJohan Hovold 	struct gb_connection *data_connection;
559120b906SLaurent Pinchart 	u16 data_cport_id;
563b8ebfebSLaurent Pinchart 
5717ca6770SEvgeniy Borisov 	struct mutex mutex;
583b8ebfebSLaurent Pinchart 	enum gb_camera_state state;
593265edafSLaurent Pinchart 
603265edafSLaurent Pinchart 	struct {
613265edafSLaurent Pinchart 		struct dentry *root;
623265edafSLaurent Pinchart 		struct gb_camera_debugfs_buffer *buffers;
633265edafSLaurent Pinchart 	} debugfs;
64c3d77f71SGjorgji Rosikopulos 
65c3d77f71SGjorgji Rosikopulos 	struct gb_camera_module module;
663265edafSLaurent Pinchart };
673265edafSLaurent Pinchart 
683265edafSLaurent Pinchart struct gb_camera_stream_config {
693265edafSLaurent Pinchart 	unsigned int width;
703265edafSLaurent Pinchart 	unsigned int height;
713265edafSLaurent Pinchart 	unsigned int format;
723265edafSLaurent Pinchart 	unsigned int vc;
733265edafSLaurent Pinchart 	unsigned int dt[2];
743265edafSLaurent Pinchart 	unsigned int max_size;
753265edafSLaurent Pinchart };
763265edafSLaurent Pinchart 
776cc27048SJacopo Mondi struct gb_camera_fmt_info {
783a8dba4eSGjorgji Rosikopulos 	enum v4l2_mbus_pixelcode mbus_code;
793a8dba4eSGjorgji Rosikopulos 	unsigned int gb_format;
806cc27048SJacopo Mondi 	unsigned int bpp;
813a8dba4eSGjorgji Rosikopulos };
823a8dba4eSGjorgji Rosikopulos 
833a8dba4eSGjorgji Rosikopulos /* GB format to media code map */
846cc27048SJacopo Mondi static const struct gb_camera_fmt_info gb_fmt_info[] = {
853a8dba4eSGjorgji Rosikopulos 	{
863a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
873a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x01,
886cc27048SJacopo Mondi 		.bpp	   = 16,
893a8dba4eSGjorgji Rosikopulos 	},
903a8dba4eSGjorgji Rosikopulos 	{
917c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV12_1x8,
927c154711SGjorgji Rosikopulos 		.gb_format = 0x12,
936cc27048SJacopo Mondi 		.bpp	   = 12,
947c154711SGjorgji Rosikopulos 	},
957c154711SGjorgji Rosikopulos 	{
967c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_NV21_1x8,
977c154711SGjorgji Rosikopulos 		.gb_format = 0x13,
986cc27048SJacopo Mondi 		.bpp	   = 12,
997c154711SGjorgji Rosikopulos 	},
1007c154711SGjorgji Rosikopulos 	{
1017c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YU12_1x8,
1023a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x16,
1036cc27048SJacopo Mondi 		.bpp	   = 12,
1043a8dba4eSGjorgji Rosikopulos 	},
1053a8dba4eSGjorgji Rosikopulos 	{
1067c154711SGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_YV12_1x8,
1073a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x17,
1086cc27048SJacopo Mondi 		.bpp	   = 12,
1093a8dba4eSGjorgji Rosikopulos 	},
1103a8dba4eSGjorgji Rosikopulos 	{
1113a8dba4eSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
1123a8dba4eSGjorgji Rosikopulos 		.gb_format = 0x40,
1136cc27048SJacopo Mondi 		.bpp	   = 0,
114dc5cc72cSGjorgji Rosikopulos 	},
115dc5cc72cSGjorgji Rosikopulos 	{
116563c742aSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_GB_CAM_METADATA_1X8,
117dc5cc72cSGjorgji Rosikopulos 		.gb_format = 0x41,
1186cc27048SJacopo Mondi 		.bpp	   = 0,
119cb14e976SGjorgji Rosikopulos 	},
120cb14e976SGjorgji Rosikopulos 	{
121563c742aSGjorgji Rosikopulos 		.mbus_code = V4L2_MBUS_FMT_GB_CAM_DEBUG_DATA_1X8,
122cb14e976SGjorgji Rosikopulos 		.gb_format = 0x42,
1236cc27048SJacopo Mondi 		.bpp	   = 0,
124cb14e976SGjorgji Rosikopulos 	},
125caad3090SEvgeniy Borisov 	{
126caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10,
127caad3090SEvgeniy Borisov 		.gb_format = 0x80,
1286cc27048SJacopo Mondi 		.bpp	   = 10,
129caad3090SEvgeniy Borisov 	},
130caad3090SEvgeniy Borisov 	{
131caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10,
132caad3090SEvgeniy Borisov 		.gb_format = 0x81,
1336cc27048SJacopo Mondi 		.bpp	   = 10,
134caad3090SEvgeniy Borisov 	},
135caad3090SEvgeniy Borisov 	{
136caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
137caad3090SEvgeniy Borisov 		.gb_format = 0x82,
1386cc27048SJacopo Mondi 		.bpp	   = 10,
139caad3090SEvgeniy Borisov 	},
140caad3090SEvgeniy Borisov 	{
141caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10,
142caad3090SEvgeniy Borisov 		.gb_format = 0x83,
1436cc27048SJacopo Mondi 		.bpp	   = 10,
144caad3090SEvgeniy Borisov 	},
145caad3090SEvgeniy Borisov 	{
146caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12,
147caad3090SEvgeniy Borisov 		.gb_format = 0x84,
1486cc27048SJacopo Mondi 		.bpp	   = 12,
149caad3090SEvgeniy Borisov 	},
150caad3090SEvgeniy Borisov 	{
151caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12,
152caad3090SEvgeniy Borisov 		.gb_format = 0x85,
1536cc27048SJacopo Mondi 		.bpp	   = 12,
154caad3090SEvgeniy Borisov 	},
155caad3090SEvgeniy Borisov 	{
156caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
157caad3090SEvgeniy Borisov 		.gb_format = 0x86,
1586cc27048SJacopo Mondi 		.bpp	   = 12,
159caad3090SEvgeniy Borisov 	},
160caad3090SEvgeniy Borisov 	{
161caad3090SEvgeniy Borisov 		.mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12,
162caad3090SEvgeniy Borisov 		.gb_format = 0x87,
1636cc27048SJacopo Mondi 		.bpp	   = 12,
164caad3090SEvgeniy Borisov 	},
1653a8dba4eSGjorgji Rosikopulos };
1663a8dba4eSGjorgji Rosikopulos 
167d165a618SJacopo Mondi static const struct gb_camera_fmt_info *gb_camera_get_format_info(u16 gb_fmt)
168d165a618SJacopo Mondi {
169d165a618SJacopo Mondi 	unsigned int i;
170d165a618SJacopo Mondi 
171d165a618SJacopo Mondi 	for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) {
172d165a618SJacopo Mondi 		if (gb_fmt_info[i].gb_format == gb_fmt)
173d165a618SJacopo Mondi 			return &gb_fmt_info[i];
174d165a618SJacopo Mondi 	}
175d165a618SJacopo Mondi 
176d165a618SJacopo Mondi 	return NULL;
177d165a618SJacopo Mondi }
178d165a618SJacopo Mondi 
1793265edafSLaurent Pinchart #define ES2_APB_CDSI0_CPORT		16
1803265edafSLaurent Pinchart #define ES2_APB_CDSI1_CPORT		17
1813265edafSLaurent Pinchart 
1823265edafSLaurent Pinchart #define GB_CAMERA_MAX_SETTINGS_SIZE	8192
1833265edafSLaurent Pinchart 
18468b66c28SLaurent Pinchart #define gcam_dbg(gcam, format...)	dev_dbg(&gcam->bundle->dev, format)
18568b66c28SLaurent Pinchart #define gcam_info(gcam, format...)	dev_info(&gcam->bundle->dev, format)
18668b66c28SLaurent Pinchart #define gcam_err(gcam, format...)	dev_err(&gcam->bundle->dev, format)
1873265edafSLaurent Pinchart 
188fdf73c00SJacopo Mondi static int gb_camera_operation_sync_flags(struct gb_connection *connection,
189fdf73c00SJacopo Mondi 					  int type, unsigned int flags,
190fdf73c00SJacopo Mondi 					  void *request, size_t request_size,
191fdf73c00SJacopo Mondi 					  void *response, size_t *response_size)
192fdf73c00SJacopo Mondi {
193fdf73c00SJacopo Mondi 	struct gb_operation *operation;
194fdf73c00SJacopo Mondi 	int ret;
195fdf73c00SJacopo Mondi 
196fdf73c00SJacopo Mondi 	operation = gb_operation_create_flags(connection, type, request_size,
197fdf73c00SJacopo Mondi 					      *response_size, flags,
198fdf73c00SJacopo Mondi 					      GFP_KERNEL);
199fdf73c00SJacopo Mondi 	if (!operation)
200fdf73c00SJacopo Mondi 		return  -ENOMEM;
201fdf73c00SJacopo Mondi 
202fdf73c00SJacopo Mondi 	if (request_size)
203fdf73c00SJacopo Mondi 		memcpy(operation->request->payload, request, request_size);
204fdf73c00SJacopo Mondi 
205fdf73c00SJacopo Mondi 	ret = gb_operation_request_send_sync(operation);
206fdf73c00SJacopo Mondi 	if (ret) {
207fdf73c00SJacopo Mondi 		dev_err(&connection->hd->dev,
208fdf73c00SJacopo Mondi 			"%s: synchronous operation of type 0x%02x failed: %d\n",
209fdf73c00SJacopo Mondi 			connection->name, type, ret);
210fdf73c00SJacopo Mondi 	} else {
211fdf73c00SJacopo Mondi 		*response_size = operation->response->payload_size;
212fdf73c00SJacopo Mondi 
213fdf73c00SJacopo Mondi 		if (operation->response->payload_size)
214fdf73c00SJacopo Mondi 			memcpy(response, operation->response->payload,
215fdf73c00SJacopo Mondi 			       operation->response->payload_size);
216fdf73c00SJacopo Mondi 	}
217fdf73c00SJacopo Mondi 
218fdf73c00SJacopo Mondi 	gb_operation_put(operation);
219fdf73c00SJacopo Mondi 
220fdf73c00SJacopo Mondi 	return ret;
221fdf73c00SJacopo Mondi }
222fdf73c00SJacopo Mondi 
223f88b94ecSJacopo Mondi static int gb_camera_get_max_pkt_size(struct gb_camera *gcam,
224f88b94ecSJacopo Mondi 		struct gb_camera_configure_streams_response *resp)
225f88b94ecSJacopo Mondi {
226f88b94ecSJacopo Mondi 	unsigned int max_pkt_size = 0;
227f88b94ecSJacopo Mondi 	unsigned int i;
228f88b94ecSJacopo Mondi 
229f88b94ecSJacopo Mondi 	for (i = 0; i < resp->num_streams; i++) {
230f88b94ecSJacopo Mondi 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
231f88b94ecSJacopo Mondi 		const struct gb_camera_fmt_info *fmt_info;
232f88b94ecSJacopo Mondi 		unsigned int pkt_size;
233f88b94ecSJacopo Mondi 
234f88b94ecSJacopo Mondi 		fmt_info = gb_camera_get_format_info(cfg->format);
235f88b94ecSJacopo Mondi 		if (!fmt_info) {
236f88b94ecSJacopo Mondi 			gcam_err(gcam, "unsupported greybus image format: %d\n",
237f88b94ecSJacopo Mondi 				 cfg->format);
238f88b94ecSJacopo Mondi 			return -EIO;
239f88b94ecSJacopo Mondi 		}
240f88b94ecSJacopo Mondi 
241f88b94ecSJacopo Mondi 		if (fmt_info->bpp == 0) {
242f88b94ecSJacopo Mondi 			pkt_size = le32_to_cpu(cfg->max_pkt_size);
243f88b94ecSJacopo Mondi 
244f88b94ecSJacopo Mondi 			if (pkt_size == 0) {
245f88b94ecSJacopo Mondi 				gcam_err(gcam,
246f88b94ecSJacopo Mondi 					 "Stream %u: invalid zero maximum packet size\n",
247f88b94ecSJacopo Mondi 					 i);
248f88b94ecSJacopo Mondi 				return -EIO;
249f88b94ecSJacopo Mondi 			}
250f88b94ecSJacopo Mondi 		} else {
251f88b94ecSJacopo Mondi 			pkt_size = le16_to_cpu(cfg->width) * fmt_info->bpp / 8;
252f88b94ecSJacopo Mondi 
253f88b94ecSJacopo Mondi 			if (pkt_size != le32_to_cpu(cfg->max_pkt_size)) {
254f88b94ecSJacopo Mondi 				gcam_err(gcam,
255f88b94ecSJacopo Mondi 					 "Stream %u: maximum packet size mismatch (%u/%u)\n",
256f88b94ecSJacopo Mondi 					 i, pkt_size, cfg->max_pkt_size);
257f88b94ecSJacopo Mondi 				return -EIO;
258f88b94ecSJacopo Mondi 			}
259f88b94ecSJacopo Mondi 		}
260f88b94ecSJacopo Mondi 
261f88b94ecSJacopo Mondi 		max_pkt_size = max(pkt_size, max_pkt_size);
262f88b94ecSJacopo Mondi 	}
263f88b94ecSJacopo Mondi 
264f88b94ecSJacopo Mondi 	return max_pkt_size;
265f88b94ecSJacopo Mondi }
266f88b94ecSJacopo Mondi 
267d165a618SJacopo Mondi /*
268d165a618SJacopo Mondi  * Validate the stream configuration response verifying padding is correctly
269d165a618SJacopo Mondi  * set and the returned number of streams is supported
270d165a618SJacopo Mondi  */
271c9161d72SJacopo Mondi static const int gb_camera_configure_streams_validate_response(
272d165a618SJacopo Mondi 		struct gb_camera *gcam,
273c9161d72SJacopo Mondi 		struct gb_camera_configure_streams_response *resp,
274d165a618SJacopo Mondi 		unsigned int nstreams)
275d165a618SJacopo Mondi {
276d165a618SJacopo Mondi 	unsigned int i;
277d165a618SJacopo Mondi 
278d165a618SJacopo Mondi 	/* Validate the returned response structure */
279c9161d72SJacopo Mondi 	if (resp->padding[0] || resp->padding[1]) {
280d165a618SJacopo Mondi 		gcam_err(gcam, "response padding != 0\n");
281d165a618SJacopo Mondi 		return -EIO;
282d165a618SJacopo Mondi 	}
283d165a618SJacopo Mondi 
284c9161d72SJacopo Mondi 	if (resp->num_streams > nstreams) {
285d165a618SJacopo Mondi 		gcam_err(gcam, "got #streams %u > request %u\n",
286d165a618SJacopo Mondi 			 resp->num_streams, nstreams);
287d165a618SJacopo Mondi 		return -EIO;
288d165a618SJacopo Mondi 	}
289d165a618SJacopo Mondi 
290c9161d72SJacopo Mondi 	for (i = 0; i < resp->num_streams; i++) {
291d165a618SJacopo Mondi 		struct gb_camera_stream_config_response *cfg = &resp->config[i];
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 
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 
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 
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 
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 
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 
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 
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 
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 
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  */
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 
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 
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 
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 
7995b032710SGjorgji Rosikopulos 	gb_streams = kzalloc(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 
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 
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 
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 
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, ";");
9213265edafSLaurent Pinchart 	if (token == NULL)
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, ";");
932b787d413SJacopo Mondi 	if (token == NULL)
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 */
9403265edafSLaurent Pinchart 	streams = kzalloc(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, ";");
9493265edafSLaurent Pinchart 		if (token == NULL) {
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, ";");
9593265edafSLaurent Pinchart 		if (token == NULL)
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, ";");
9683265edafSLaurent Pinchart 		if (token == NULL)
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 
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, ";");
10123265edafSLaurent Pinchart 	if (token == NULL)
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, ";");
10203265edafSLaurent Pinchart 	if (token == NULL)
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, ";");
10283265edafSLaurent Pinchart 	if (token == NULL)
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 
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",
10693265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO,
10703265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES,
10713265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capabilities,
10723265edafSLaurent Pinchart 	}, {
10733265edafSLaurent Pinchart 		.name = "configure_streams",
10743265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
10753265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS,
10763265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_configure_streams,
10773265edafSLaurent Pinchart 	}, {
10783265edafSLaurent Pinchart 		.name = "capture",
10793265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
10803265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE,
10813265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_capture,
10823265edafSLaurent Pinchart 	}, {
10833265edafSLaurent Pinchart 		.name = "flush",
10843265edafSLaurent Pinchart 		.mask = S_IFREG | S_IRUGO | S_IWUGO,
10853265edafSLaurent Pinchart 		.buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH,
10863265edafSLaurent Pinchart 		.execute = gb_camera_debugfs_flush,
10873265edafSLaurent Pinchart 	},
10883265edafSLaurent Pinchart };
10893265edafSLaurent Pinchart 
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;
10943265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->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. */
10993265edafSLaurent Pinchart 	if (!(op->mask & S_IWUGO)) {
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 
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;
11163265edafSLaurent Pinchart 	struct gb_camera *gcam = file->f_inode->i_private;
11173265edafSLaurent Pinchart 	ssize_t ret;
11183265edafSLaurent Pinchart 	char *kbuf;
11193265edafSLaurent Pinchart 
11203265edafSLaurent Pinchart 	if (len > 1024)
11213265edafSLaurent Pinchart 	       return -EINVAL;
11223265edafSLaurent Pinchart 
11233265edafSLaurent Pinchart 	kbuf = kmalloc(len + 1, GFP_KERNEL);
11243265edafSLaurent Pinchart 	if (kbuf == NULL)
11253265edafSLaurent Pinchart 		return -ENOMEM;
11263265edafSLaurent Pinchart 
11273265edafSLaurent Pinchart 	if (copy_from_user(kbuf, buf, len)) {
11283265edafSLaurent Pinchart 		ret = -EFAULT;
11293265edafSLaurent Pinchart 		goto done;
11303265edafSLaurent Pinchart 	}
11313265edafSLaurent Pinchart 
11323265edafSLaurent Pinchart 	kbuf[len] = '\0';
11333265edafSLaurent Pinchart 
11343265edafSLaurent Pinchart 	ret = op->execute(gcam, kbuf, len);
11353265edafSLaurent Pinchart 
11363265edafSLaurent Pinchart done:
11373265edafSLaurent Pinchart 	kfree(kbuf);
11383265edafSLaurent Pinchart 	return ret;
11393265edafSLaurent Pinchart }
11403265edafSLaurent Pinchart 
11413265edafSLaurent Pinchart static int gb_camera_debugfs_open(struct inode *inode, struct file *file)
11423265edafSLaurent Pinchart {
11433265edafSLaurent Pinchart 	unsigned int i;
11443265edafSLaurent Pinchart 
11453265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
11463265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
11473265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
11483265edafSLaurent Pinchart 
11494dda744cSGreg Kroah-Hartman 		if (!strcmp(file->f_path.dentry->d_iname, entry->name)) {
11503265edafSLaurent Pinchart 			file->private_data = (void *)entry;
11513265edafSLaurent Pinchart 			break;
11523265edafSLaurent Pinchart 		}
11533265edafSLaurent Pinchart 	}
11543265edafSLaurent Pinchart 
11553265edafSLaurent Pinchart 	return 0;
11563265edafSLaurent Pinchart }
11573265edafSLaurent Pinchart 
11583265edafSLaurent Pinchart static const struct file_operations gb_camera_debugfs_ops = {
11593265edafSLaurent Pinchart 	.open = gb_camera_debugfs_open,
11603265edafSLaurent Pinchart 	.read = gb_camera_debugfs_read,
11613265edafSLaurent Pinchart 	.write = gb_camera_debugfs_write,
11623265edafSLaurent Pinchart };
11633265edafSLaurent Pinchart 
11643265edafSLaurent Pinchart static int gb_camera_debugfs_init(struct gb_camera *gcam)
11653265edafSLaurent Pinchart {
11663265edafSLaurent Pinchart 	struct gb_connection *connection = gcam->connection;
11673265edafSLaurent Pinchart 	char dirname[27];
11683265edafSLaurent Pinchart 	unsigned int i;
11693265edafSLaurent Pinchart 
11703265edafSLaurent Pinchart 	/*
11713265edafSLaurent Pinchart 	 * Create root debugfs entry and a file entry for each camera operation.
11723265edafSLaurent Pinchart 	 */
11733265edafSLaurent Pinchart 	snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id,
117468b66c28SLaurent Pinchart 		 gcam->bundle->id);
11753265edafSLaurent Pinchart 
11763265edafSLaurent Pinchart 	gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get());
11773265edafSLaurent Pinchart 	if (IS_ERR(gcam->debugfs.root)) {
11783265edafSLaurent Pinchart 		gcam_err(gcam, "debugfs root create failed (%ld)\n",
11793265edafSLaurent Pinchart 			 PTR_ERR(gcam->debugfs.root));
11803265edafSLaurent Pinchart 		return PTR_ERR(gcam->debugfs.root);
11813265edafSLaurent Pinchart 	}
11823265edafSLaurent Pinchart 
11833265edafSLaurent Pinchart 	gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) *
11843265edafSLaurent Pinchart 					GB_CAMERA_DEBUGFS_BUFFER_MAX);
11853265edafSLaurent Pinchart 	if (!gcam->debugfs.buffers)
11863265edafSLaurent Pinchart 		return -ENOMEM;
11873265edafSLaurent Pinchart 
11883265edafSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) {
11893265edafSLaurent Pinchart 		const struct gb_camera_debugfs_entry *entry =
11903265edafSLaurent Pinchart 			&gb_camera_debugfs_entries[i];
11913265edafSLaurent Pinchart 		struct dentry *dentry;
11923265edafSLaurent Pinchart 
11933265edafSLaurent Pinchart 		gcam->debugfs.buffers[i].length = 0;
11943265edafSLaurent Pinchart 
11953265edafSLaurent Pinchart 		dentry = debugfs_create_file(entry->name, entry->mask,
11963265edafSLaurent Pinchart 					     gcam->debugfs.root, gcam,
11973265edafSLaurent Pinchart 					     &gb_camera_debugfs_ops);
11983265edafSLaurent Pinchart 		if (IS_ERR(dentry)) {
11993265edafSLaurent Pinchart 			gcam_err(gcam,
12003265edafSLaurent Pinchart 				 "debugfs operation %s create failed (%ld)\n",
1201f9340fc7SAlex Elder 				 entry->name, PTR_ERR(dentry));
12023265edafSLaurent Pinchart 			return PTR_ERR(dentry);
12033265edafSLaurent Pinchart 		}
12043265edafSLaurent Pinchart 	}
12053265edafSLaurent Pinchart 
12063265edafSLaurent Pinchart 	return 0;
12073265edafSLaurent Pinchart }
12083265edafSLaurent Pinchart 
12093265edafSLaurent Pinchart static void gb_camera_debugfs_cleanup(struct gb_camera *gcam)
12103265edafSLaurent Pinchart {
12113265edafSLaurent Pinchart 	debugfs_remove_recursive(gcam->debugfs.root);
12123265edafSLaurent Pinchart 
12133265edafSLaurent Pinchart 	vfree(gcam->debugfs.buffers);
12143265edafSLaurent Pinchart }
12153265edafSLaurent Pinchart 
12163265edafSLaurent Pinchart /* -----------------------------------------------------------------------------
12173265edafSLaurent Pinchart  * Init & Cleanup
12183265edafSLaurent Pinchart  */
12193265edafSLaurent Pinchart 
12203265edafSLaurent Pinchart static void gb_camera_cleanup(struct gb_camera *gcam)
12213265edafSLaurent Pinchart {
12223265edafSLaurent Pinchart 	gb_camera_debugfs_cleanup(gcam);
12233265edafSLaurent Pinchart 
12249120b906SLaurent Pinchart 	mutex_lock(&gcam->mutex);
12253ba9fa5cSJohan Hovold 	if (gcam->data_connection) {
12263ba9fa5cSJohan Hovold 		gb_connection_disable(gcam->data_connection);
12273ba9fa5cSJohan Hovold 		gb_connection_destroy(gcam->data_connection);
122817ca6770SEvgeniy Borisov 		gcam->data_connection = NULL;
12293265edafSLaurent Pinchart 	}
12303265edafSLaurent Pinchart 
123168b66c28SLaurent Pinchart 	if (gcam->connection) {
123268b66c28SLaurent Pinchart 		gb_connection_disable(gcam->connection);
123368b66c28SLaurent Pinchart 		gb_connection_destroy(gcam->connection);
123417ca6770SEvgeniy Borisov 		gcam->connection = NULL;
123517ca6770SEvgeniy Borisov 	}
123617ca6770SEvgeniy Borisov 	mutex_unlock(&gcam->mutex);
123768b66c28SLaurent Pinchart }
123868b66c28SLaurent Pinchart 
123917ca6770SEvgeniy Borisov static void gb_camera_release_module(struct kref *ref)
124017ca6770SEvgeniy Borisov {
124117ca6770SEvgeniy Borisov 	struct gb_camera_module *cam_mod =
124217ca6770SEvgeniy Borisov 		container_of(ref, struct gb_camera_module, refcount);
124317ca6770SEvgeniy Borisov 	kfree(cam_mod->priv);
12443265edafSLaurent Pinchart }
12453265edafSLaurent Pinchart 
124668b66c28SLaurent Pinchart static int gb_camera_probe(struct gb_bundle *bundle,
124768b66c28SLaurent Pinchart 			   const struct greybus_bundle_id *id)
12483265edafSLaurent Pinchart {
124968b66c28SLaurent Pinchart 	struct gb_connection *conn;
12503265edafSLaurent Pinchart 	struct gb_camera *gcam;
125168b66c28SLaurent Pinchart 	u16 mgmt_cport_id = 0;
125268b66c28SLaurent Pinchart 	u16 data_cport_id = 0;
125368b66c28SLaurent Pinchart 	unsigned int i;
12543265edafSLaurent Pinchart 	int ret;
12553265edafSLaurent Pinchart 
125668b66c28SLaurent Pinchart 	/*
125768b66c28SLaurent Pinchart 	 * The camera bundle must contain exactly two CPorts, one for the
125868b66c28SLaurent Pinchart 	 * camera management protocol and one for the camera data protocol.
125968b66c28SLaurent Pinchart 	 */
126068b66c28SLaurent Pinchart 	if (bundle->num_cports != 2)
126168b66c28SLaurent Pinchart 		return -ENODEV;
126268b66c28SLaurent Pinchart 
126368b66c28SLaurent Pinchart 	for (i = 0; i < bundle->num_cports; ++i) {
126468b66c28SLaurent Pinchart 		struct greybus_descriptor_cport *desc = &bundle->cport_desc[i];
126568b66c28SLaurent Pinchart 
126668b66c28SLaurent Pinchart 		switch (desc->protocol_id) {
126768b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_MGMT:
126868b66c28SLaurent Pinchart 			mgmt_cport_id = le16_to_cpu(desc->id);
126968b66c28SLaurent Pinchart 			break;
127068b66c28SLaurent Pinchart 		case GREYBUS_PROTOCOL_CAMERA_DATA:
127168b66c28SLaurent Pinchart 			data_cport_id = le16_to_cpu(desc->id);
127268b66c28SLaurent Pinchart 			break;
127368b66c28SLaurent Pinchart 		default:
127468b66c28SLaurent Pinchart 			return -ENODEV;
127568b66c28SLaurent Pinchart 		}
127668b66c28SLaurent Pinchart 	}
127768b66c28SLaurent Pinchart 
127868b66c28SLaurent Pinchart 	if (!mgmt_cport_id || !data_cport_id)
127968b66c28SLaurent Pinchart 		return -ENODEV;
128068b66c28SLaurent Pinchart 
12813265edafSLaurent Pinchart 	gcam = kzalloc(sizeof(*gcam), GFP_KERNEL);
12823265edafSLaurent Pinchart 	if (!gcam)
12833265edafSLaurent Pinchart 		return -ENOMEM;
12843265edafSLaurent Pinchart 
1285d9e4c4eeSViresh Kumar 	mutex_init(&gcam->mutex);
12863265edafSLaurent Pinchart 
12873b8ebfebSLaurent Pinchart 	gcam->bundle = bundle;
12883b8ebfebSLaurent Pinchart 	gcam->state = GB_CAMERA_STATE_UNCONFIGURED;
12899120b906SLaurent Pinchart 	gcam->data_cport_id = data_cport_id;
12903b8ebfebSLaurent Pinchart 
129168b66c28SLaurent Pinchart 	conn = gb_connection_create(bundle, mgmt_cport_id,
129268b66c28SLaurent Pinchart 				    gb_camera_request_handler);
129368b66c28SLaurent Pinchart 	if (IS_ERR(conn)) {
129468b66c28SLaurent Pinchart 		ret = PTR_ERR(conn);
12953ba9fa5cSJohan Hovold 		goto error;
12963ba9fa5cSJohan Hovold 	}
12973ba9fa5cSJohan Hovold 
129868b66c28SLaurent Pinchart 	gcam->connection = conn;
129968b66c28SLaurent Pinchart 	gb_connection_set_data(conn, gcam);
130068b66c28SLaurent Pinchart 
130168b66c28SLaurent Pinchart 	ret = gb_connection_enable(conn);
130268b66c28SLaurent Pinchart 	if (ret)
130368b66c28SLaurent Pinchart 		goto error;
130468b66c28SLaurent Pinchart 
13053265edafSLaurent Pinchart 	ret = gb_camera_debugfs_init(gcam);
13063265edafSLaurent Pinchart 	if (ret < 0)
13073265edafSLaurent Pinchart 		goto error;
13083265edafSLaurent Pinchart 
130917ca6770SEvgeniy Borisov 	gcam->module.priv = gcam;
131017ca6770SEvgeniy Borisov 	gcam->module.ops = &gb_cam_ops;
131117ca6770SEvgeniy Borisov 	gcam->module.interface_id = gcam->connection->intf->interface_id;
131217ca6770SEvgeniy Borisov 	gcam->module.release = gb_camera_release_module;
131317ca6770SEvgeniy Borisov 	ret = gb_camera_register(&gcam->module);
13143a8dba4eSGjorgji Rosikopulos 	if (ret < 0)
13153a8dba4eSGjorgji Rosikopulos 		goto error;
13163a8dba4eSGjorgji Rosikopulos 
131768b66c28SLaurent Pinchart 	greybus_set_drvdata(bundle, gcam);
131868b66c28SLaurent Pinchart 
1319211634f2SDavid Lin 	gb_pm_runtime_put_autosuspend(gcam->bundle);
1320211634f2SDavid Lin 
13213265edafSLaurent Pinchart 	return 0;
13223265edafSLaurent Pinchart 
13233265edafSLaurent Pinchart error:
13243265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
132517ca6770SEvgeniy Borisov 	kfree(gcam);
13263265edafSLaurent Pinchart 	return ret;
13273265edafSLaurent Pinchart }
13283265edafSLaurent Pinchart 
132968b66c28SLaurent Pinchart static void gb_camera_disconnect(struct gb_bundle *bundle)
13303265edafSLaurent Pinchart {
133168b66c28SLaurent Pinchart 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
1332211634f2SDavid Lin 	int ret;
1333211634f2SDavid Lin 
1334211634f2SDavid Lin 	ret = gb_pm_runtime_get_sync(bundle);
1335211634f2SDavid Lin 	if (ret)
1336211634f2SDavid Lin 		gb_pm_runtime_get_noresume(bundle);
13373265edafSLaurent Pinchart 
13383265edafSLaurent Pinchart 	gb_camera_cleanup(gcam);
133917ca6770SEvgeniy Borisov 	gb_camera_unregister(&gcam->module);
13403265edafSLaurent Pinchart }
13413265edafSLaurent Pinchart 
134268b66c28SLaurent Pinchart static const struct greybus_bundle_id gb_camera_id_table[] = {
134368b66c28SLaurent Pinchart 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
134468b66c28SLaurent Pinchart 	{ },
13453265edafSLaurent Pinchart };
13463265edafSLaurent Pinchart 
1347211634f2SDavid Lin #ifdef CONFIG_PM_RUNTIME
1348211634f2SDavid Lin static int gb_camera_suspend(struct device *dev)
1349211634f2SDavid Lin {
1350211634f2SDavid Lin 	struct gb_bundle *bundle = to_gb_bundle(dev);
1351211634f2SDavid Lin 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
1352211634f2SDavid Lin 
1353211634f2SDavid Lin 	if (gcam->data_connection)
1354211634f2SDavid Lin 		gb_connection_disable(gcam->data_connection);
1355211634f2SDavid Lin 
1356211634f2SDavid Lin 	gb_connection_disable(gcam->connection);
1357211634f2SDavid Lin 
1358211634f2SDavid Lin 	return 0;
1359211634f2SDavid Lin }
1360211634f2SDavid Lin 
1361211634f2SDavid Lin static int gb_camera_resume(struct device *dev)
1362211634f2SDavid Lin {
1363211634f2SDavid Lin 	struct gb_bundle *bundle = to_gb_bundle(dev);
1364211634f2SDavid Lin 	struct gb_camera *gcam = greybus_get_drvdata(bundle);
1365211634f2SDavid Lin 	int ret;
1366211634f2SDavid Lin 
1367211634f2SDavid Lin 	ret = gb_connection_enable(gcam->connection);
1368211634f2SDavid Lin 	if (ret) {
1369211634f2SDavid Lin 		gcam_err(gcam, "failed to enable connection: %d\n", ret);
1370211634f2SDavid Lin 		return ret;
1371211634f2SDavid Lin 	}
1372211634f2SDavid Lin 
1373211634f2SDavid Lin 	if (gcam->data_connection) {
1374211634f2SDavid Lin 		ret = gb_connection_enable(gcam->data_connection);
1375211634f2SDavid Lin 		if (ret) {
1376211634f2SDavid Lin 			gcam_err(gcam,
1377211634f2SDavid Lin 				 "failed to enable data connection: %d\n", ret);
1378211634f2SDavid Lin 			return ret;
1379211634f2SDavid Lin 		}
1380211634f2SDavid Lin 	}
1381211634f2SDavid Lin 
1382211634f2SDavid Lin 	return 0;
1383211634f2SDavid Lin }
1384211634f2SDavid Lin #endif
1385211634f2SDavid Lin 
1386211634f2SDavid Lin static const struct dev_pm_ops gb_camera_pm_ops = {
1387211634f2SDavid Lin 	SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL)
1388211634f2SDavid Lin };
1389211634f2SDavid Lin 
139068b66c28SLaurent Pinchart static struct greybus_driver gb_camera_driver = {
139168b66c28SLaurent Pinchart 	.name		= "camera",
139268b66c28SLaurent Pinchart 	.probe		= gb_camera_probe,
139368b66c28SLaurent Pinchart 	.disconnect	= gb_camera_disconnect,
139468b66c28SLaurent Pinchart 	.id_table	= gb_camera_id_table,
1395211634f2SDavid Lin 	.driver.pm	= &gb_camera_pm_ops,
139668b66c28SLaurent Pinchart };
139768b66c28SLaurent Pinchart 
139868b66c28SLaurent Pinchart module_greybus_driver(gb_camera_driver);
13993265edafSLaurent Pinchart 
14003265edafSLaurent Pinchart MODULE_LICENSE("GPL v2");
1401