162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	webcam.c -- USB webcam gadget driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Copyright (C) 2009-2010
662306a36Sopenharmony_ci *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/usb/video.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "u_uvc.h"
1562306a36Sopenharmony_ci#include "uvc_configfs.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS();
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* module parameters specific to the Video streaming endpoint */
2262306a36Sopenharmony_cistatic unsigned int streaming_interval = 1;
2362306a36Sopenharmony_cimodule_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
2462306a36Sopenharmony_ciMODULE_PARM_DESC(streaming_interval, "1 - 16");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic unsigned int streaming_maxpacket = 1024;
2762306a36Sopenharmony_cimodule_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
2862306a36Sopenharmony_ciMODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic unsigned int streaming_maxburst;
3162306a36Sopenharmony_cimodule_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
3262306a36Sopenharmony_ciMODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* --------------------------------------------------------------------------
3562306a36Sopenharmony_ci * Device descriptor
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define WEBCAM_VENDOR_ID		0x1d6b	/* Linux Foundation */
3962306a36Sopenharmony_ci#define WEBCAM_PRODUCT_ID		0x0102	/* Webcam A/V gadget */
4062306a36Sopenharmony_ci#define WEBCAM_DEVICE_BCD		0x0010	/* 0.10 */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic char webcam_vendor_label[] = "Linux Foundation";
4362306a36Sopenharmony_cistatic char webcam_product_label[] = "Webcam gadget";
4462306a36Sopenharmony_cistatic char webcam_config_label[] = "Video";
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* string IDs are assigned dynamically */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct usb_string webcam_strings[] = {
5162306a36Sopenharmony_ci	[USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
5262306a36Sopenharmony_ci	[USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
5362306a36Sopenharmony_ci	[USB_GADGET_SERIAL_IDX].s = "",
5462306a36Sopenharmony_ci	[STRING_DESCRIPTION_IDX].s = webcam_config_label,
5562306a36Sopenharmony_ci	{  }
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic struct usb_gadget_strings webcam_stringtab = {
5962306a36Sopenharmony_ci	.language = 0x0409,	/* en-us */
6062306a36Sopenharmony_ci	.strings = webcam_strings,
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic struct usb_gadget_strings *webcam_device_strings[] = {
6462306a36Sopenharmony_ci	&webcam_stringtab,
6562306a36Sopenharmony_ci	NULL,
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct usb_function_instance *fi_uvc;
6962306a36Sopenharmony_cistatic struct usb_function *f_uvc;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct usb_device_descriptor webcam_device_descriptor = {
7262306a36Sopenharmony_ci	.bLength		= USB_DT_DEVICE_SIZE,
7362306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_DEVICE,
7462306a36Sopenharmony_ci	/* .bcdUSB = DYNAMIC */
7562306a36Sopenharmony_ci	.bDeviceClass		= USB_CLASS_MISC,
7662306a36Sopenharmony_ci	.bDeviceSubClass	= 0x02,
7762306a36Sopenharmony_ci	.bDeviceProtocol	= 0x01,
7862306a36Sopenharmony_ci	.bMaxPacketSize0	= 0, /* dynamic */
7962306a36Sopenharmony_ci	.idVendor		= cpu_to_le16(WEBCAM_VENDOR_ID),
8062306a36Sopenharmony_ci	.idProduct		= cpu_to_le16(WEBCAM_PRODUCT_ID),
8162306a36Sopenharmony_ci	.bcdDevice		= cpu_to_le16(WEBCAM_DEVICE_BCD),
8262306a36Sopenharmony_ci	.iManufacturer		= 0, /* dynamic */
8362306a36Sopenharmony_ci	.iProduct		= 0, /* dynamic */
8462306a36Sopenharmony_ci	.iSerialNumber		= 0, /* dynamic */
8562306a36Sopenharmony_ci	.bNumConfigurations	= 0, /* dynamic */
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
8962306a36Sopenharmony_ci	.bLength		= UVC_DT_HEADER_SIZE(1),
9062306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
9162306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VC_HEADER,
9262306a36Sopenharmony_ci	.bcdUVC			= cpu_to_le16(0x0110),
9362306a36Sopenharmony_ci	.wTotalLength		= 0, /* dynamic */
9462306a36Sopenharmony_ci	.dwClockFrequency	= cpu_to_le32(48000000),
9562306a36Sopenharmony_ci	.bInCollection		= 0, /* dynamic */
9662306a36Sopenharmony_ci	.baInterfaceNr[0]	= 0, /* dynamic */
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
10062306a36Sopenharmony_ci	.bLength		= UVC_DT_CAMERA_TERMINAL_SIZE(3),
10162306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
10262306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VC_INPUT_TERMINAL,
10362306a36Sopenharmony_ci	.bTerminalID		= 1,
10462306a36Sopenharmony_ci	.wTerminalType		= cpu_to_le16(0x0201),
10562306a36Sopenharmony_ci	.bAssocTerminal		= 0,
10662306a36Sopenharmony_ci	.iTerminal		= 0,
10762306a36Sopenharmony_ci	.wObjectiveFocalLengthMin	= cpu_to_le16(0),
10862306a36Sopenharmony_ci	.wObjectiveFocalLengthMax	= cpu_to_le16(0),
10962306a36Sopenharmony_ci	.wOcularFocalLength		= cpu_to_le16(0),
11062306a36Sopenharmony_ci	.bControlSize		= 3,
11162306a36Sopenharmony_ci	.bmControls[0]		= 2,
11262306a36Sopenharmony_ci	.bmControls[1]		= 0,
11362306a36Sopenharmony_ci	.bmControls[2]		= 0,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const struct uvc_processing_unit_descriptor uvc_processing = {
11762306a36Sopenharmony_ci	.bLength		= UVC_DT_PROCESSING_UNIT_SIZE(2),
11862306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
11962306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VC_PROCESSING_UNIT,
12062306a36Sopenharmony_ci	.bUnitID		= 2,
12162306a36Sopenharmony_ci	.bSourceID		= 1,
12262306a36Sopenharmony_ci	.wMaxMultiplier		= cpu_to_le16(16*1024),
12362306a36Sopenharmony_ci	.bControlSize		= 2,
12462306a36Sopenharmony_ci	.bmControls[0]		= 1,
12562306a36Sopenharmony_ci	.bmControls[1]		= 0,
12662306a36Sopenharmony_ci	.iProcessing		= 0,
12762306a36Sopenharmony_ci	.bmVideoStandards	= 0,
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic const struct uvc_output_terminal_descriptor uvc_output_terminal = {
13162306a36Sopenharmony_ci	.bLength		= UVC_DT_OUTPUT_TERMINAL_SIZE,
13262306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
13362306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VC_OUTPUT_TERMINAL,
13462306a36Sopenharmony_ci	.bTerminalID		= 3,
13562306a36Sopenharmony_ci	.wTerminalType		= cpu_to_le16(0x0101),
13662306a36Sopenharmony_ci	.bAssocTerminal		= 0,
13762306a36Sopenharmony_ci	.bSourceID		= 2,
13862306a36Sopenharmony_ci	.iTerminal		= 0,
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciDECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
14462306a36Sopenharmony_ci	.bLength		= UVC_DT_INPUT_HEADER_SIZE(1, 2),
14562306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
14662306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VS_INPUT_HEADER,
14762306a36Sopenharmony_ci	.bNumFormats		= 2,
14862306a36Sopenharmony_ci	.wTotalLength		= 0, /* dynamic */
14962306a36Sopenharmony_ci	.bEndpointAddress	= 0, /* dynamic */
15062306a36Sopenharmony_ci	.bmInfo			= 0,
15162306a36Sopenharmony_ci	.bTerminalLink		= 3,
15262306a36Sopenharmony_ci	.bStillCaptureMethod	= 0,
15362306a36Sopenharmony_ci	.bTriggerSupport	= 0,
15462306a36Sopenharmony_ci	.bTriggerUsage		= 0,
15562306a36Sopenharmony_ci	.bControlSize		= 1,
15662306a36Sopenharmony_ci	.bmaControls[0][0]	= 0,
15762306a36Sopenharmony_ci	.bmaControls[1][0]	= 4,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct uvcg_color_matching uvcg_color_matching = {
16162306a36Sopenharmony_ci	.desc = {
16262306a36Sopenharmony_ci		.bLength		= UVC_DT_COLOR_MATCHING_SIZE,
16362306a36Sopenharmony_ci		.bDescriptorType	= USB_DT_CS_INTERFACE,
16462306a36Sopenharmony_ci		.bDescriptorSubType	= UVC_VS_COLORFORMAT,
16562306a36Sopenharmony_ci		.bColorPrimaries	= 1,
16662306a36Sopenharmony_ci		.bTransferCharacteristics	= 1,
16762306a36Sopenharmony_ci		.bMatrixCoefficients	= 4,
16862306a36Sopenharmony_ci	},
16962306a36Sopenharmony_ci};
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic struct uvcg_uncompressed uvcg_format_yuv = {
17262306a36Sopenharmony_ci	.fmt = {
17362306a36Sopenharmony_ci		.type			= UVCG_UNCOMPRESSED,
17462306a36Sopenharmony_ci		/* add to .frames and fill .num_frames at runtime */
17562306a36Sopenharmony_ci		.color_matching		= (struct uvcg_color_matching *)&uvcg_color_matching,
17662306a36Sopenharmony_ci	},
17762306a36Sopenharmony_ci	.desc = {
17862306a36Sopenharmony_ci		.bLength		= UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
17962306a36Sopenharmony_ci		.bDescriptorType	= USB_DT_CS_INTERFACE,
18062306a36Sopenharmony_ci		.bDescriptorSubType	= UVC_VS_FORMAT_UNCOMPRESSED,
18162306a36Sopenharmony_ci		.bFormatIndex		= 1,
18262306a36Sopenharmony_ci		.bNumFrameDescriptors	= 2,
18362306a36Sopenharmony_ci		.guidFormat		= {
18462306a36Sopenharmony_ci			'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00,
18562306a36Sopenharmony_ci			 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
18662306a36Sopenharmony_ci		},
18762306a36Sopenharmony_ci		.bBitsPerPixel		= 16,
18862306a36Sopenharmony_ci		.bDefaultFrameIndex	= 1,
18962306a36Sopenharmony_ci		.bAspectRatioX		= 0,
19062306a36Sopenharmony_ci		.bAspectRatioY		= 0,
19162306a36Sopenharmony_ci		.bmInterlaceFlags	= 0,
19262306a36Sopenharmony_ci		.bCopyProtect		= 0,
19362306a36Sopenharmony_ci	},
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic struct uvcg_format_ptr uvcg_format_ptr_yuv = {
19762306a36Sopenharmony_ci	.fmt = &uvcg_format_yuv.fmt,
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciDECLARE_UVC_FRAME_UNCOMPRESSED(1);
20162306a36Sopenharmony_ciDECLARE_UVC_FRAME_UNCOMPRESSED(3);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci#define UVCG_WIDTH_360P			640
20462306a36Sopenharmony_ci#define UVCG_HEIGHT_360P		360
20562306a36Sopenharmony_ci#define UVCG_MIN_BITRATE_360P		18432000
20662306a36Sopenharmony_ci#define UVCG_MAX_BITRATE_360P		55296000
20762306a36Sopenharmony_ci#define UVCG_MAX_VIDEO_FB_SZ_360P	460800
20862306a36Sopenharmony_ci#define UVCG_FRM_INTERV_0_360P		666666
20962306a36Sopenharmony_ci#define UVCG_FRM_INTERV_1_360P		1000000
21062306a36Sopenharmony_ci#define UVCG_FRM_INTERV_2_360P		5000000
21162306a36Sopenharmony_ci#define UVCG_DEFAULT_FRM_INTERV_360P	UVCG_FRM_INTERV_0_360P
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
21462306a36Sopenharmony_ci	.bLength		= UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
21562306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
21662306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VS_FRAME_UNCOMPRESSED,
21762306a36Sopenharmony_ci	.bFrameIndex		= 1,
21862306a36Sopenharmony_ci	.bmCapabilities		= 0,
21962306a36Sopenharmony_ci	.wWidth			= cpu_to_le16(UVCG_WIDTH_360P),
22062306a36Sopenharmony_ci	.wHeight		= cpu_to_le16(UVCG_HEIGHT_360P),
22162306a36Sopenharmony_ci	.dwMinBitRate		= cpu_to_le32(UVCG_MIN_BITRATE_360P),
22262306a36Sopenharmony_ci	.dwMaxBitRate		= cpu_to_le32(UVCG_MAX_BITRATE_360P),
22362306a36Sopenharmony_ci	.dwMaxVideoFrameBufferSize	= cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
22462306a36Sopenharmony_ci	.dwDefaultFrameInterval	= cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
22562306a36Sopenharmony_ci	.bFrameIntervalType	= 3,
22662306a36Sopenharmony_ci	.dwFrameInterval[0]	= cpu_to_le32(UVCG_FRM_INTERV_0_360P),
22762306a36Sopenharmony_ci	.dwFrameInterval[1]	= cpu_to_le32(UVCG_FRM_INTERV_1_360P),
22862306a36Sopenharmony_ci	.dwFrameInterval[2]	= cpu_to_le32(UVCG_FRM_INTERV_2_360P),
22962306a36Sopenharmony_ci};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic u32 uvcg_frame_yuv_360p_dw_frame_interval[] = {
23262306a36Sopenharmony_ci	[0] = UVCG_FRM_INTERV_0_360P,
23362306a36Sopenharmony_ci	[1] = UVCG_FRM_INTERV_1_360P,
23462306a36Sopenharmony_ci	[2] = UVCG_FRM_INTERV_2_360P,
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_yuv_360p = {
23862306a36Sopenharmony_ci	.fmt_type		= UVCG_UNCOMPRESSED,
23962306a36Sopenharmony_ci	.frame = {
24062306a36Sopenharmony_ci		.b_length			= UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
24162306a36Sopenharmony_ci		.b_descriptor_type		= USB_DT_CS_INTERFACE,
24262306a36Sopenharmony_ci		.b_descriptor_subtype		= UVC_VS_FRAME_UNCOMPRESSED,
24362306a36Sopenharmony_ci		.b_frame_index			= 1,
24462306a36Sopenharmony_ci		.bm_capabilities		= 0,
24562306a36Sopenharmony_ci		.w_width			= UVCG_WIDTH_360P,
24662306a36Sopenharmony_ci		.w_height			= UVCG_HEIGHT_360P,
24762306a36Sopenharmony_ci		.dw_min_bit_rate		= UVCG_MIN_BITRATE_360P,
24862306a36Sopenharmony_ci		.dw_max_bit_rate		= UVCG_MAX_BITRATE_360P,
24962306a36Sopenharmony_ci		.dw_max_video_frame_buffer_size	= UVCG_MAX_VIDEO_FB_SZ_360P,
25062306a36Sopenharmony_ci		.dw_default_frame_interval	= UVCG_DEFAULT_FRM_INTERV_360P,
25162306a36Sopenharmony_ci		.b_frame_interval_type		= 3,
25262306a36Sopenharmony_ci	},
25362306a36Sopenharmony_ci	.dw_frame_interval	= uvcg_frame_yuv_360p_dw_frame_interval,
25462306a36Sopenharmony_ci};
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = {
25762306a36Sopenharmony_ci	.frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p,
25862306a36Sopenharmony_ci};
25962306a36Sopenharmony_ci#define UVCG_WIDTH_720P			1280
26062306a36Sopenharmony_ci#define UVCG_HEIGHT_720P		720
26162306a36Sopenharmony_ci#define UVCG_MIN_BITRATE_720P		29491200
26262306a36Sopenharmony_ci#define UVCG_MAX_BITRATE_720P		29491200
26362306a36Sopenharmony_ci#define UVCG_MAX_VIDEO_FB_SZ_720P	1843200
26462306a36Sopenharmony_ci#define UVCG_FRM_INTERV_0_720P		5000000
26562306a36Sopenharmony_ci#define UVCG_DEFAULT_FRM_INTERV_720P	UVCG_FRM_INTERV_0_720P
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
26862306a36Sopenharmony_ci	.bLength		= UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
26962306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
27062306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VS_FRAME_UNCOMPRESSED,
27162306a36Sopenharmony_ci	.bFrameIndex		= 2,
27262306a36Sopenharmony_ci	.bmCapabilities		= 0,
27362306a36Sopenharmony_ci	.wWidth			= cpu_to_le16(UVCG_WIDTH_720P),
27462306a36Sopenharmony_ci	.wHeight		= cpu_to_le16(UVCG_HEIGHT_720P),
27562306a36Sopenharmony_ci	.dwMinBitRate		= cpu_to_le32(UVCG_MIN_BITRATE_720P),
27662306a36Sopenharmony_ci	.dwMaxBitRate		= cpu_to_le32(UVCG_MAX_BITRATE_720P),
27762306a36Sopenharmony_ci	.dwMaxVideoFrameBufferSize	= cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
27862306a36Sopenharmony_ci	.dwDefaultFrameInterval	= cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
27962306a36Sopenharmony_ci	.bFrameIntervalType	= 1,
28062306a36Sopenharmony_ci	.dwFrameInterval[0]	= cpu_to_le32(UVCG_FRM_INTERV_0_720P),
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic u32 uvcg_frame_yuv_720p_dw_frame_interval[] = {
28462306a36Sopenharmony_ci	[0] = UVCG_FRM_INTERV_0_720P,
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_yuv_720p = {
28862306a36Sopenharmony_ci	.fmt_type		= UVCG_UNCOMPRESSED,
28962306a36Sopenharmony_ci	.frame = {
29062306a36Sopenharmony_ci		.b_length			= UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
29162306a36Sopenharmony_ci		.b_descriptor_type		= USB_DT_CS_INTERFACE,
29262306a36Sopenharmony_ci		.b_descriptor_subtype		= UVC_VS_FRAME_UNCOMPRESSED,
29362306a36Sopenharmony_ci		.b_frame_index			= 2,
29462306a36Sopenharmony_ci		.bm_capabilities		= 0,
29562306a36Sopenharmony_ci		.w_width			= UVCG_WIDTH_720P,
29662306a36Sopenharmony_ci		.w_height			= UVCG_HEIGHT_720P,
29762306a36Sopenharmony_ci		.dw_min_bit_rate		= UVCG_MIN_BITRATE_720P,
29862306a36Sopenharmony_ci		.dw_max_bit_rate		= UVCG_MAX_BITRATE_720P,
29962306a36Sopenharmony_ci		.dw_max_video_frame_buffer_size	= UVCG_MAX_VIDEO_FB_SZ_720P,
30062306a36Sopenharmony_ci		.dw_default_frame_interval	= UVCG_DEFAULT_FRM_INTERV_720P,
30162306a36Sopenharmony_ci		.b_frame_interval_type		= 1,
30262306a36Sopenharmony_ci	},
30362306a36Sopenharmony_ci	.dw_frame_interval	= uvcg_frame_yuv_720p_dw_frame_interval,
30462306a36Sopenharmony_ci};
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = {
30762306a36Sopenharmony_ci	.frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p,
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic struct uvcg_mjpeg uvcg_format_mjpeg = {
31162306a36Sopenharmony_ci	.fmt = {
31262306a36Sopenharmony_ci		.type			= UVCG_MJPEG,
31362306a36Sopenharmony_ci		/* add to .frames and fill .num_frames at runtime */
31462306a36Sopenharmony_ci		.color_matching		= (struct uvcg_color_matching *)&uvcg_color_matching,
31562306a36Sopenharmony_ci	},
31662306a36Sopenharmony_ci	.desc = {
31762306a36Sopenharmony_ci		.bLength		= UVC_DT_FORMAT_MJPEG_SIZE,
31862306a36Sopenharmony_ci		.bDescriptorType	= USB_DT_CS_INTERFACE,
31962306a36Sopenharmony_ci		.bDescriptorSubType	= UVC_VS_FORMAT_MJPEG,
32062306a36Sopenharmony_ci		.bFormatIndex		= 2,
32162306a36Sopenharmony_ci		.bNumFrameDescriptors	= 2,
32262306a36Sopenharmony_ci		.bmFlags		= 0,
32362306a36Sopenharmony_ci		.bDefaultFrameIndex	= 1,
32462306a36Sopenharmony_ci		.bAspectRatioX		= 0,
32562306a36Sopenharmony_ci		.bAspectRatioY		= 0,
32662306a36Sopenharmony_ci		.bmInterlaceFlags	= 0,
32762306a36Sopenharmony_ci		.bCopyProtect		= 0,
32862306a36Sopenharmony_ci	},
32962306a36Sopenharmony_ci};
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic struct uvcg_format_ptr uvcg_format_ptr_mjpeg = {
33262306a36Sopenharmony_ci	.fmt = &uvcg_format_mjpeg.fmt,
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciDECLARE_UVC_FRAME_MJPEG(1);
33662306a36Sopenharmony_ciDECLARE_UVC_FRAME_MJPEG(3);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
33962306a36Sopenharmony_ci	.bLength		= UVC_DT_FRAME_MJPEG_SIZE(3),
34062306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
34162306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VS_FRAME_MJPEG,
34262306a36Sopenharmony_ci	.bFrameIndex		= 1,
34362306a36Sopenharmony_ci	.bmCapabilities		= 0,
34462306a36Sopenharmony_ci	.wWidth			= cpu_to_le16(UVCG_WIDTH_360P),
34562306a36Sopenharmony_ci	.wHeight		= cpu_to_le16(UVCG_HEIGHT_360P),
34662306a36Sopenharmony_ci	.dwMinBitRate		= cpu_to_le32(UVCG_MIN_BITRATE_360P),
34762306a36Sopenharmony_ci	.dwMaxBitRate		= cpu_to_le32(UVCG_MAX_BITRATE_360P),
34862306a36Sopenharmony_ci	.dwMaxVideoFrameBufferSize	= cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
34962306a36Sopenharmony_ci	.dwDefaultFrameInterval	= cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
35062306a36Sopenharmony_ci	.bFrameIntervalType	= 3,
35162306a36Sopenharmony_ci	.dwFrameInterval[0]	= cpu_to_le32(UVCG_FRM_INTERV_0_360P),
35262306a36Sopenharmony_ci	.dwFrameInterval[1]	= cpu_to_le32(UVCG_FRM_INTERV_1_360P),
35362306a36Sopenharmony_ci	.dwFrameInterval[2]	= cpu_to_le32(UVCG_FRM_INTERV_2_360P),
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = {
35762306a36Sopenharmony_ci	[0] = UVCG_FRM_INTERV_0_360P,
35862306a36Sopenharmony_ci	[1] = UVCG_FRM_INTERV_1_360P,
35962306a36Sopenharmony_ci	[2] = UVCG_FRM_INTERV_2_360P,
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_mjpeg_360p = {
36362306a36Sopenharmony_ci	.fmt_type		= UVCG_MJPEG,
36462306a36Sopenharmony_ci	.frame = {
36562306a36Sopenharmony_ci		.b_length			= UVC_DT_FRAME_MJPEG_SIZE(3),
36662306a36Sopenharmony_ci		.b_descriptor_type		= USB_DT_CS_INTERFACE,
36762306a36Sopenharmony_ci		.b_descriptor_subtype		= UVC_VS_FRAME_MJPEG,
36862306a36Sopenharmony_ci		.b_frame_index			= 1,
36962306a36Sopenharmony_ci		.bm_capabilities		= 0,
37062306a36Sopenharmony_ci		.w_width			= UVCG_WIDTH_360P,
37162306a36Sopenharmony_ci		.w_height			= UVCG_HEIGHT_360P,
37262306a36Sopenharmony_ci		.dw_min_bit_rate		= UVCG_MIN_BITRATE_360P,
37362306a36Sopenharmony_ci		.dw_max_bit_rate		= UVCG_MAX_BITRATE_360P,
37462306a36Sopenharmony_ci		.dw_max_video_frame_buffer_size	= UVCG_MAX_VIDEO_FB_SZ_360P,
37562306a36Sopenharmony_ci		.dw_default_frame_interval	= UVCG_DEFAULT_FRM_INTERV_360P,
37662306a36Sopenharmony_ci		.b_frame_interval_type		= 3,
37762306a36Sopenharmony_ci	},
37862306a36Sopenharmony_ci	.dw_frame_interval	= uvcg_frame_mjpeg_360p_dw_frame_interval,
37962306a36Sopenharmony_ci};
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = {
38262306a36Sopenharmony_ci	.frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p,
38362306a36Sopenharmony_ci};
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
38662306a36Sopenharmony_ci	.bLength		= UVC_DT_FRAME_MJPEG_SIZE(1),
38762306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_CS_INTERFACE,
38862306a36Sopenharmony_ci	.bDescriptorSubType	= UVC_VS_FRAME_MJPEG,
38962306a36Sopenharmony_ci	.bFrameIndex		= 2,
39062306a36Sopenharmony_ci	.bmCapabilities		= 0,
39162306a36Sopenharmony_ci	.wWidth			= cpu_to_le16(UVCG_WIDTH_720P),
39262306a36Sopenharmony_ci	.wHeight		= cpu_to_le16(UVCG_HEIGHT_720P),
39362306a36Sopenharmony_ci	.dwMinBitRate		= cpu_to_le32(UVCG_MIN_BITRATE_720P),
39462306a36Sopenharmony_ci	.dwMaxBitRate		= cpu_to_le32(UVCG_MAX_BITRATE_720P),
39562306a36Sopenharmony_ci	.dwMaxVideoFrameBufferSize	= cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
39662306a36Sopenharmony_ci	.dwDefaultFrameInterval	= cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
39762306a36Sopenharmony_ci	.bFrameIntervalType	= 1,
39862306a36Sopenharmony_ci	.dwFrameInterval[0]	= cpu_to_le32(UVCG_FRM_INTERV_0_720P),
39962306a36Sopenharmony_ci};
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = {
40262306a36Sopenharmony_ci	[0] = UVCG_FRM_INTERV_0_720P,
40362306a36Sopenharmony_ci};
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_mjpeg_720p = {
40662306a36Sopenharmony_ci	.fmt_type		= UVCG_MJPEG,
40762306a36Sopenharmony_ci	.frame = {
40862306a36Sopenharmony_ci		.b_length			= UVC_DT_FRAME_MJPEG_SIZE(1),
40962306a36Sopenharmony_ci		.b_descriptor_type		= USB_DT_CS_INTERFACE,
41062306a36Sopenharmony_ci		.b_descriptor_subtype		= UVC_VS_FRAME_MJPEG,
41162306a36Sopenharmony_ci		.b_frame_index			= 2,
41262306a36Sopenharmony_ci		.bm_capabilities		= 0,
41362306a36Sopenharmony_ci		.w_width			= UVCG_WIDTH_720P,
41462306a36Sopenharmony_ci		.w_height			= UVCG_HEIGHT_720P,
41562306a36Sopenharmony_ci		.dw_min_bit_rate		= UVCG_MIN_BITRATE_720P,
41662306a36Sopenharmony_ci		.dw_max_bit_rate		= UVCG_MAX_BITRATE_720P,
41762306a36Sopenharmony_ci		.dw_max_video_frame_buffer_size	= UVCG_MAX_VIDEO_FB_SZ_720P,
41862306a36Sopenharmony_ci		.dw_default_frame_interval	= UVCG_DEFAULT_FRM_INTERV_720P,
41962306a36Sopenharmony_ci		.b_frame_interval_type		= 1,
42062306a36Sopenharmony_ci	},
42162306a36Sopenharmony_ci	.dw_frame_interval	= uvcg_frame_mjpeg_720p_dw_frame_interval,
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = {
42562306a36Sopenharmony_ci	.frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p,
42662306a36Sopenharmony_ci};
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic struct uvcg_streaming_header uvcg_streaming_header = {
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
43262306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_control_header,
43362306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_camera_terminal,
43462306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_processing,
43562306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_output_terminal,
43662306a36Sopenharmony_ci	NULL,
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
44062306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_control_header,
44162306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_camera_terminal,
44262306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_processing,
44362306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_output_terminal,
44462306a36Sopenharmony_ci	NULL,
44562306a36Sopenharmony_ci};
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
44862306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_input_header,
44962306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
45062306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
45162306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
45262306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
45362306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
45462306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
45562306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
45662306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
45762306a36Sopenharmony_ci	NULL,
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
46162306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_input_header,
46262306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
46362306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
46462306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
46562306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
46662306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
46762306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
46862306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
46962306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
47062306a36Sopenharmony_ci	NULL,
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
47462306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_input_header,
47562306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
47662306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
47762306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
47862306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
47962306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
48062306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
48162306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
48262306a36Sopenharmony_ci	(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
48362306a36Sopenharmony_ci	NULL,
48462306a36Sopenharmony_ci};
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* --------------------------------------------------------------------------
48762306a36Sopenharmony_ci * USB configuration
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int
49162306a36Sopenharmony_ciwebcam_config_bind(struct usb_configuration *c)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int status = 0;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	f_uvc = usb_get_function(fi_uvc);
49662306a36Sopenharmony_ci	if (IS_ERR(f_uvc))
49762306a36Sopenharmony_ci		return PTR_ERR(f_uvc);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	status = usb_add_function(c, f_uvc);
50062306a36Sopenharmony_ci	if (status < 0)
50162306a36Sopenharmony_ci		usb_put_function(f_uvc);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return status;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic struct usb_configuration webcam_config_driver = {
50762306a36Sopenharmony_ci	.label			= webcam_config_label,
50862306a36Sopenharmony_ci	.bConfigurationValue	= 1,
50962306a36Sopenharmony_ci	.iConfiguration		= 0, /* dynamic */
51062306a36Sopenharmony_ci	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
51162306a36Sopenharmony_ci	.MaxPower		= CONFIG_USB_GADGET_VBUS_DRAW,
51262306a36Sopenharmony_ci};
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int
51562306a36Sopenharmony_ciwebcam_unbind(struct usb_composite_dev *cdev)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_uvc))
51862306a36Sopenharmony_ci		usb_put_function(f_uvc);
51962306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(fi_uvc))
52062306a36Sopenharmony_ci		usb_put_function_instance(fi_uvc);
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int
52562306a36Sopenharmony_ciwebcam_bind(struct usb_composite_dev *cdev)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct f_uvc_opts *uvc_opts;
52862306a36Sopenharmony_ci	int ret;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	fi_uvc = usb_get_function_instance("uvc");
53162306a36Sopenharmony_ci	if (IS_ERR(fi_uvc))
53262306a36Sopenharmony_ci		return PTR_ERR(fi_uvc);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	uvc_opts->streaming_interval = streaming_interval;
53762306a36Sopenharmony_ci	uvc_opts->streaming_maxpacket = streaming_maxpacket;
53862306a36Sopenharmony_ci	uvc_opts->streaming_maxburst = streaming_maxburst;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	uvc_opts->fs_control = uvc_fs_control_cls;
54162306a36Sopenharmony_ci	uvc_opts->ss_control = uvc_ss_control_cls;
54262306a36Sopenharmony_ci	uvc_opts->fs_streaming = uvc_fs_streaming_cls;
54362306a36Sopenharmony_ci	uvc_opts->hs_streaming = uvc_hs_streaming_cls;
54462306a36Sopenharmony_ci	uvc_opts->ss_streaming = uvc_ss_streaming_cls;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames);
54762306a36Sopenharmony_ci	list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames);
54862306a36Sopenharmony_ci	list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames);
54962306a36Sopenharmony_ci	uvcg_format_yuv.fmt.num_frames = 2;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames);
55262306a36Sopenharmony_ci	list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames);
55362306a36Sopenharmony_ci	list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames);
55462306a36Sopenharmony_ci	uvcg_format_mjpeg.fmt.num_frames = 2;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	INIT_LIST_HEAD(&uvcg_streaming_header.formats);
55762306a36Sopenharmony_ci	list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats);
55862306a36Sopenharmony_ci	list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats);
55962306a36Sopenharmony_ci	uvcg_streaming_header.num_fmt = 2;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	uvc_opts->header = &uvcg_streaming_header;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/* Allocate string descriptor numbers ... note that string contents
56462306a36Sopenharmony_ci	 * can be overridden by the composite_dev glue.
56562306a36Sopenharmony_ci	 */
56662306a36Sopenharmony_ci	ret = usb_string_ids_tab(cdev, webcam_strings);
56762306a36Sopenharmony_ci	if (ret < 0)
56862306a36Sopenharmony_ci		goto error;
56962306a36Sopenharmony_ci	webcam_device_descriptor.iManufacturer =
57062306a36Sopenharmony_ci		webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
57162306a36Sopenharmony_ci	webcam_device_descriptor.iProduct =
57262306a36Sopenharmony_ci		webcam_strings[USB_GADGET_PRODUCT_IDX].id;
57362306a36Sopenharmony_ci	webcam_config_driver.iConfiguration =
57462306a36Sopenharmony_ci		webcam_strings[STRING_DESCRIPTION_IDX].id;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/* Register our configuration. */
57762306a36Sopenharmony_ci	if ((ret = usb_add_config(cdev, &webcam_config_driver,
57862306a36Sopenharmony_ci					webcam_config_bind)) < 0)
57962306a36Sopenharmony_ci		goto error;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	usb_composite_overwrite_options(cdev, &coverwrite);
58262306a36Sopenharmony_ci	INFO(cdev, "Webcam Video Gadget\n");
58362306a36Sopenharmony_ci	return 0;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cierror:
58662306a36Sopenharmony_ci	usb_put_function_instance(fi_uvc);
58762306a36Sopenharmony_ci	return ret;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/* --------------------------------------------------------------------------
59162306a36Sopenharmony_ci * Driver
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic struct usb_composite_driver webcam_driver = {
59562306a36Sopenharmony_ci	.name		= "g_webcam",
59662306a36Sopenharmony_ci	.dev		= &webcam_device_descriptor,
59762306a36Sopenharmony_ci	.strings	= webcam_device_strings,
59862306a36Sopenharmony_ci	.max_speed	= USB_SPEED_SUPER,
59962306a36Sopenharmony_ci	.bind		= webcam_bind,
60062306a36Sopenharmony_ci	.unbind		= webcam_unbind,
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cimodule_usb_composite_driver(webcam_driver);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ciMODULE_AUTHOR("Laurent Pinchart");
60662306a36Sopenharmony_ciMODULE_DESCRIPTION("Webcam Video Gadget");
60762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
60862306a36Sopenharmony_ci
609