18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * kinect sensor device camera, gspca driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on the OpenKinect project and libfreenect
88c2ecf20Sopenharmony_ci * http://openkinect.org/wiki/Init_Analysis
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect
118c2ecf20Sopenharmony_ci * sensor device which I tested the driver on.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define MODULE_NAME "kinect"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "gspca.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define CTRL_TIMEOUT 500
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic bool depth_mode;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct pkt_hdr {
298c2ecf20Sopenharmony_ci	uint8_t magic[2];
308c2ecf20Sopenharmony_ci	uint8_t pad;
318c2ecf20Sopenharmony_ci	uint8_t flag;
328c2ecf20Sopenharmony_ci	uint8_t unk1;
338c2ecf20Sopenharmony_ci	uint8_t seq;
348c2ecf20Sopenharmony_ci	uint8_t unk2;
358c2ecf20Sopenharmony_ci	uint8_t unk3;
368c2ecf20Sopenharmony_ci	uint32_t timestamp;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct cam_hdr {
408c2ecf20Sopenharmony_ci	uint8_t magic[2];
418c2ecf20Sopenharmony_ci	__le16 len;
428c2ecf20Sopenharmony_ci	__le16 cmd;
438c2ecf20Sopenharmony_ci	__le16 tag;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* specific webcam descriptor */
478c2ecf20Sopenharmony_cistruct sd {
488c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev; /* !! must be the first item */
498c2ecf20Sopenharmony_ci	uint16_t cam_tag;           /* a sequence number for packets */
508c2ecf20Sopenharmony_ci	uint8_t stream_flag;        /* to identify different stream types */
518c2ecf20Sopenharmony_ci	uint8_t obuf[0x400];        /* output buffer for control commands */
528c2ecf20Sopenharmony_ci	uint8_t ibuf[0x200];        /* input buffer for control commands */
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define MODE_640x480   0x0001
568c2ecf20Sopenharmony_ci#define MODE_640x488   0x0002
578c2ecf20Sopenharmony_ci#define MODE_1280x1024 0x0004
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define FORMAT_BAYER   0x0010
608c2ecf20Sopenharmony_ci#define FORMAT_UYVY    0x0020
618c2ecf20Sopenharmony_ci#define FORMAT_Y10B    0x0040
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define FPS_HIGH       0x0100
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format depth_camera_mode[] = {
668c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
678c2ecf20Sopenharmony_ci	 .bytesperline = 640 * 10 / 8,
688c2ecf20Sopenharmony_ci	 .sizeimage =  640 * 480 * 10 / 8,
698c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
708c2ecf20Sopenharmony_ci	 .priv = MODE_640x488 | FORMAT_Y10B},
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format video_camera_mode[] = {
748c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
758c2ecf20Sopenharmony_ci	 .bytesperline = 640,
768c2ecf20Sopenharmony_ci	 .sizeimage = 640 * 480,
778c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
788c2ecf20Sopenharmony_ci	 .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH},
798c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
808c2ecf20Sopenharmony_ci	 .bytesperline = 640 * 2,
818c2ecf20Sopenharmony_ci	 .sizeimage = 640 * 480 * 2,
828c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
838c2ecf20Sopenharmony_ci	 .priv = MODE_640x480 | FORMAT_UYVY},
848c2ecf20Sopenharmony_ci	{1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
858c2ecf20Sopenharmony_ci	 .bytesperline = 1280,
868c2ecf20Sopenharmony_ci	 .sizeimage = 1280 * 1024,
878c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
888c2ecf20Sopenharmony_ci	 .priv = MODE_1280x1024 | FORMAT_BAYER},
898c2ecf20Sopenharmony_ci	{640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
908c2ecf20Sopenharmony_ci	 .bytesperline = 640 * 10 / 8,
918c2ecf20Sopenharmony_ci	 .sizeimage =  640 * 488 * 10 / 8,
928c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
938c2ecf20Sopenharmony_ci	 .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH},
948c2ecf20Sopenharmony_ci	{1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
958c2ecf20Sopenharmony_ci	 .bytesperline = 1280 * 10 / 8,
968c2ecf20Sopenharmony_ci	 .sizeimage =  1280 * 1024 * 10 / 8,
978c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
988c2ecf20Sopenharmony_ci	 .priv = MODE_1280x1024 | FORMAT_Y10B},
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int kinect_write(struct usb_device *udev, uint8_t *data,
1028c2ecf20Sopenharmony_ci			uint16_t wLength)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	return usb_control_msg(udev,
1058c2ecf20Sopenharmony_ci			      usb_sndctrlpipe(udev, 0),
1068c2ecf20Sopenharmony_ci			      0x00,
1078c2ecf20Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1088c2ecf20Sopenharmony_ci			      0, 0, data, wLength, CTRL_TIMEOUT);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return usb_control_msg(udev,
1148c2ecf20Sopenharmony_ci			      usb_rcvctrlpipe(udev, 0),
1158c2ecf20Sopenharmony_ci			      0x00,
1168c2ecf20Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1178c2ecf20Sopenharmony_ci			      0, 0, data, wLength, CTRL_TIMEOUT);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
1218c2ecf20Sopenharmony_ci		unsigned int cmd_len, void *replybuf, unsigned int reply_len)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
1248c2ecf20Sopenharmony_ci	struct usb_device *udev = gspca_dev->dev;
1258c2ecf20Sopenharmony_ci	int res, actual_len;
1268c2ecf20Sopenharmony_ci	uint8_t *obuf = sd->obuf;
1278c2ecf20Sopenharmony_ci	uint8_t *ibuf = sd->ibuf;
1288c2ecf20Sopenharmony_ci	struct cam_hdr *chdr = (void *)obuf;
1298c2ecf20Sopenharmony_ci	struct cam_hdr *rhdr = (void *)ibuf;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) {
1328c2ecf20Sopenharmony_ci		pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len);
1338c2ecf20Sopenharmony_ci		return -1;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	chdr->magic[0] = 0x47;
1378c2ecf20Sopenharmony_ci	chdr->magic[1] = 0x4d;
1388c2ecf20Sopenharmony_ci	chdr->cmd = cpu_to_le16(cmd);
1398c2ecf20Sopenharmony_ci	chdr->tag = cpu_to_le16(sd->cam_tag);
1408c2ecf20Sopenharmony_ci	chdr->len = cpu_to_le16(cmd_len / 2);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr));
1458c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d\n",
1468c2ecf20Sopenharmony_ci		  cmd,
1478c2ecf20Sopenharmony_ci		  sd->cam_tag, cmd_len, res);
1488c2ecf20Sopenharmony_ci	if (res < 0) {
1498c2ecf20Sopenharmony_ci		pr_err("send_cmd: Output control transfer failed (%d)\n", res);
1508c2ecf20Sopenharmony_ci		return res;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	do {
1548c2ecf20Sopenharmony_ci		actual_len = kinect_read(udev, ibuf, 0x200);
1558c2ecf20Sopenharmony_ci	} while (actual_len == 0);
1568c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "Control reply: %d\n", actual_len);
1578c2ecf20Sopenharmony_ci	if (actual_len < (int)sizeof(*rhdr)) {
1588c2ecf20Sopenharmony_ci		pr_err("send_cmd: Input control transfer failed (%d)\n",
1598c2ecf20Sopenharmony_ci		       actual_len);
1608c2ecf20Sopenharmony_ci		return actual_len < 0 ? actual_len : -EREMOTEIO;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	actual_len -= sizeof(*rhdr);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) {
1658c2ecf20Sopenharmony_ci		pr_err("send_cmd: Bad magic %02x %02x\n",
1668c2ecf20Sopenharmony_ci		       rhdr->magic[0], rhdr->magic[1]);
1678c2ecf20Sopenharmony_ci		return -1;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	if (rhdr->cmd != chdr->cmd) {
1708c2ecf20Sopenharmony_ci		pr_err("send_cmd: Bad cmd %02x != %02x\n",
1718c2ecf20Sopenharmony_ci		       rhdr->cmd, chdr->cmd);
1728c2ecf20Sopenharmony_ci		return -1;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	if (rhdr->tag != chdr->tag) {
1758c2ecf20Sopenharmony_ci		pr_err("send_cmd: Bad tag %04x != %04x\n",
1768c2ecf20Sopenharmony_ci		       rhdr->tag, chdr->tag);
1778c2ecf20Sopenharmony_ci		return -1;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	if (le16_to_cpu(rhdr->len) != (actual_len/2)) {
1808c2ecf20Sopenharmony_ci		pr_err("send_cmd: Bad len %04x != %04x\n",
1818c2ecf20Sopenharmony_ci		       le16_to_cpu(rhdr->len), (int)(actual_len/2));
1828c2ecf20Sopenharmony_ci		return -1;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (actual_len > reply_len) {
1868c2ecf20Sopenharmony_ci		pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n",
1878c2ecf20Sopenharmony_ci			reply_len, actual_len);
1888c2ecf20Sopenharmony_ci		memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len);
1898c2ecf20Sopenharmony_ci	} else {
1908c2ecf20Sopenharmony_ci		memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	sd->cam_tag++;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return actual_len;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
1998c2ecf20Sopenharmony_ci			uint16_t data)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	uint16_t reply[2];
2028c2ecf20Sopenharmony_ci	__le16 cmd[2];
2038c2ecf20Sopenharmony_ci	int res;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	cmd[0] = cpu_to_le16(reg);
2068c2ecf20Sopenharmony_ci	cmd[1] = cpu_to_le16(data);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "Write Reg 0x%04x <= 0x%02x\n", reg, data);
2098c2ecf20Sopenharmony_ci	res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4);
2108c2ecf20Sopenharmony_ci	if (res < 0)
2118c2ecf20Sopenharmony_ci		return res;
2128c2ecf20Sopenharmony_ci	if (res != 2) {
2138c2ecf20Sopenharmony_ci		pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n",
2148c2ecf20Sopenharmony_ci			res, reply[0], reply[1]);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* this function is called at probe time */
2208c2ecf20Sopenharmony_cistatic int sd_config_video(struct gspca_dev *gspca_dev,
2218c2ecf20Sopenharmony_ci		     const struct usb_device_id *id)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
2248c2ecf20Sopenharmony_ci	struct cam *cam;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	sd->cam_tag = 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	sd->stream_flag = 0x80;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	cam = &gspca_dev->cam;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	cam->cam_mode = video_camera_mode;
2338c2ecf20Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(video_camera_mode);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	gspca_dev->xfer_ep = 0x81;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#if 0
2388c2ecf20Sopenharmony_ci	/* Setting those values is not needed for video stream */
2398c2ecf20Sopenharmony_ci	cam->npkt = 15;
2408c2ecf20Sopenharmony_ci	gspca_dev->pkt_size = 960 * 2;
2418c2ecf20Sopenharmony_ci#endif
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int sd_config_depth(struct gspca_dev *gspca_dev,
2478c2ecf20Sopenharmony_ci		     const struct usb_device_id *id)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
2508c2ecf20Sopenharmony_ci	struct cam *cam;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	sd->cam_tag = 0;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	sd->stream_flag = 0x70;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	cam = &gspca_dev->cam;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	cam->cam_mode = depth_camera_mode;
2598c2ecf20Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(depth_camera_mode);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	gspca_dev->xfer_ep = 0x82;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return 0;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
2678c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Kinect Camera device.\n");
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int sd_start_video(struct gspca_dev *gspca_dev)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	int mode;
2778c2ecf20Sopenharmony_ci	uint8_t fmt_reg, fmt_val;
2788c2ecf20Sopenharmony_ci	uint8_t res_reg, res_val;
2798c2ecf20Sopenharmony_ci	uint8_t fps_reg, fps_val;
2808c2ecf20Sopenharmony_ci	uint8_t mode_val;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (mode & FORMAT_Y10B) {
2858c2ecf20Sopenharmony_ci		fmt_reg = 0x19;
2868c2ecf20Sopenharmony_ci		res_reg = 0x1a;
2878c2ecf20Sopenharmony_ci		fps_reg = 0x1b;
2888c2ecf20Sopenharmony_ci		mode_val = 0x03;
2898c2ecf20Sopenharmony_ci	} else {
2908c2ecf20Sopenharmony_ci		fmt_reg = 0x0c;
2918c2ecf20Sopenharmony_ci		res_reg = 0x0d;
2928c2ecf20Sopenharmony_ci		fps_reg = 0x0e;
2938c2ecf20Sopenharmony_ci		mode_val = 0x01;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* format */
2978c2ecf20Sopenharmony_ci	if (mode & FORMAT_UYVY)
2988c2ecf20Sopenharmony_ci		fmt_val = 0x05;
2998c2ecf20Sopenharmony_ci	else
3008c2ecf20Sopenharmony_ci		fmt_val = 0x00;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (mode & MODE_1280x1024)
3038c2ecf20Sopenharmony_ci		res_val = 0x02;
3048c2ecf20Sopenharmony_ci	else
3058c2ecf20Sopenharmony_ci		res_val = 0x01;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (mode & FPS_HIGH)
3088c2ecf20Sopenharmony_ci		fps_val = 0x1e;
3098c2ecf20Sopenharmony_ci	else
3108c2ecf20Sopenharmony_ci		fps_val = 0x0f;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* turn off IR-reset function */
3148c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x105, 0x00);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Reset video stream */
3178c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x05, 0x00);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Due to some ridiculous condition in the firmware, we have to start
3208c2ecf20Sopenharmony_ci	 * and stop the depth stream before the camera will hand us 1280x1024
3218c2ecf20Sopenharmony_ci	 * IR.  This is a stupid workaround, but we've yet to find a better
3228c2ecf20Sopenharmony_ci	 * solution.
3238c2ecf20Sopenharmony_ci	 *
3248c2ecf20Sopenharmony_ci	 * Thanks to Drew Fisher for figuring this out.
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	if (mode & (FORMAT_Y10B | MODE_1280x1024)) {
3278c2ecf20Sopenharmony_ci		write_register(gspca_dev, 0x13, 0x01);
3288c2ecf20Sopenharmony_ci		write_register(gspca_dev, 0x14, 0x1e);
3298c2ecf20Sopenharmony_ci		write_register(gspca_dev, 0x06, 0x02);
3308c2ecf20Sopenharmony_ci		write_register(gspca_dev, 0x06, 0x00);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	write_register(gspca_dev, fmt_reg, fmt_val);
3348c2ecf20Sopenharmony_ci	write_register(gspca_dev, res_reg, res_val);
3358c2ecf20Sopenharmony_ci	write_register(gspca_dev, fps_reg, fps_val);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* Start video stream */
3388c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x05, mode_val);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* disable Hflip */
3418c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x47, 0x00);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int sd_start_depth(struct gspca_dev *gspca_dev)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	/* turn off IR-reset function */
3498c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x105, 0x00);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* reset depth stream */
3528c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x06, 0x00);
3538c2ecf20Sopenharmony_ci	/* Depth Stream Format 0x03: 11 bit stream | 0x02: 10 bit */
3548c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x12, 0x02);
3558c2ecf20Sopenharmony_ci	/* Depth Stream Resolution 1: standard (640x480) */
3568c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x13, 0x01);
3578c2ecf20Sopenharmony_ci	/* Depth Framerate / 0x1e (30): 30 fps */
3588c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x14, 0x1e);
3598c2ecf20Sopenharmony_ci	/* Depth Stream Control  / 2: Open Depth Stream */
3608c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x06, 0x02);
3618c2ecf20Sopenharmony_ci	/* disable depth hflip / LSB = 0: Smoothing Disabled */
3628c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x17, 0x00);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void sd_stopN_video(struct gspca_dev *gspca_dev)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	/* reset video stream */
3708c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x05, 0x00);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void sd_stopN_depth(struct gspca_dev *gspca_dev)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	/* reset depth stream */
3768c2ecf20Sopenharmony_ci	write_register(gspca_dev, 0x06, 0x00);
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	struct pkt_hdr *hdr = (void *)__data;
3848c2ecf20Sopenharmony_ci	uint8_t *data = __data + sizeof(*hdr);
3858c2ecf20Sopenharmony_ci	int datalen = len - sizeof(*hdr);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	uint8_t sof = sd->stream_flag | 1;
3888c2ecf20Sopenharmony_ci	uint8_t mof = sd->stream_flag | 2;
3898c2ecf20Sopenharmony_ci	uint8_t eof = sd->stream_flag | 5;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (len < 12)
3928c2ecf20Sopenharmony_ci		return;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') {
3958c2ecf20Sopenharmony_ci		pr_warn("[Stream %02x] Invalid magic %02x%02x\n",
3968c2ecf20Sopenharmony_ci			sd->stream_flag, hdr->magic[0], hdr->magic[1]);
3978c2ecf20Sopenharmony_ci		return;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (hdr->flag == sof)
4018c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	else if (hdr->flag == mof)
4048c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	else if (hdr->flag == eof)
4078c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	else
4108c2ecf20Sopenharmony_ci		pr_warn("Packet type not recognized...\n");
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/* sub-driver description */
4148c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc_video = {
4158c2ecf20Sopenharmony_ci	.name      = MODULE_NAME,
4168c2ecf20Sopenharmony_ci	.config    = sd_config_video,
4178c2ecf20Sopenharmony_ci	.init      = sd_init,
4188c2ecf20Sopenharmony_ci	.start     = sd_start_video,
4198c2ecf20Sopenharmony_ci	.stopN     = sd_stopN_video,
4208c2ecf20Sopenharmony_ci	.pkt_scan  = sd_pkt_scan,
4218c2ecf20Sopenharmony_ci	/*
4228c2ecf20Sopenharmony_ci	.get_streamparm = sd_get_streamparm,
4238c2ecf20Sopenharmony_ci	.set_streamparm = sd_set_streamparm,
4248c2ecf20Sopenharmony_ci	*/
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc_depth = {
4278c2ecf20Sopenharmony_ci	.name      = MODULE_NAME,
4288c2ecf20Sopenharmony_ci	.config    = sd_config_depth,
4298c2ecf20Sopenharmony_ci	.init      = sd_init,
4308c2ecf20Sopenharmony_ci	.start     = sd_start_depth,
4318c2ecf20Sopenharmony_ci	.stopN     = sd_stopN_depth,
4328c2ecf20Sopenharmony_ci	.pkt_scan  = sd_pkt_scan,
4338c2ecf20Sopenharmony_ci	/*
4348c2ecf20Sopenharmony_ci	.get_streamparm = sd_get_streamparm,
4358c2ecf20Sopenharmony_ci	.set_streamparm = sd_set_streamparm,
4368c2ecf20Sopenharmony_ci	*/
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/* -- module initialisation -- */
4408c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
4418c2ecf20Sopenharmony_ci	{USB_DEVICE(0x045e, 0x02ae)},
4428c2ecf20Sopenharmony_ci	{USB_DEVICE(0x045e, 0x02bf)},
4438c2ecf20Sopenharmony_ci	{}
4448c2ecf20Sopenharmony_ci};
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci/* -- device connect -- */
4498c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	if (depth_mode)
4528c2ecf20Sopenharmony_ci		return gspca_dev_probe(intf, id, &sd_desc_depth,
4538c2ecf20Sopenharmony_ci				       sizeof(struct sd), THIS_MODULE);
4548c2ecf20Sopenharmony_ci	else
4558c2ecf20Sopenharmony_ci		return gspca_dev_probe(intf, id, &sd_desc_video,
4568c2ecf20Sopenharmony_ci				       sizeof(struct sd), THIS_MODULE);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
4608c2ecf20Sopenharmony_ci	.name       = MODULE_NAME,
4618c2ecf20Sopenharmony_ci	.id_table   = device_table,
4628c2ecf20Sopenharmony_ci	.probe      = sd_probe,
4638c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
4648c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4658c2ecf20Sopenharmony_ci	.suspend    = gspca_suspend,
4668c2ecf20Sopenharmony_ci	.resume     = gspca_resume,
4678c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
4688c2ecf20Sopenharmony_ci#endif
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cimodule_param(depth_mode, bool, 0644);
4748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(depth_mode, "0=video 1=depth");
475