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