cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pwc-v4l.c (28846B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* Linux driver for Philips webcam
      3   USB and Video4Linux interface part.
      4   (C) 1999-2004 Nemosoft Unv.
      5   (C) 2004-2006 Luc Saillard (luc@saillard.org)
      6   (C) 2011 Hans de Goede <hdegoede@redhat.com>
      7
      8   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
      9   driver and thus may have bugs that are not present in the original version.
     10   Please send bug reports and support requests to <luc@saillard.org>.
     11   The decompression routines have been implemented by reverse-engineering the
     12   Nemosoft binary pwcx module. Caveat emptor.
     13
     14
     15*/
     16
     17#include <linux/errno.h>
     18#include <linux/init.h>
     19#include <linux/mm.h>
     20#include <linux/module.h>
     21#include <linux/poll.h>
     22#include <linux/vmalloc.h>
     23#include <linux/jiffies.h>
     24#include <asm/io.h>
     25
     26#include "pwc.h"
     27
     28#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl)
     29
     30static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
     31static int pwc_s_ctrl(struct v4l2_ctrl *ctrl);
     32
     33static const struct v4l2_ctrl_ops pwc_ctrl_ops = {
     34	.g_volatile_ctrl = pwc_g_volatile_ctrl,
     35	.s_ctrl = pwc_s_ctrl,
     36};
     37
     38enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto };
     39enum { custom_autocontour, custom_contour, custom_noise_reduction,
     40	custom_awb_speed, custom_awb_delay,
     41	custom_save_user, custom_restore_user, custom_restore_factory };
     42
     43static const char * const pwc_auto_whitebal_qmenu[] = {
     44	"Indoor (Incandescant Lighting) Mode",
     45	"Outdoor (Sunlight) Mode",
     46	"Indoor (Fluorescent Lighting) Mode",
     47	"Manual Mode",
     48	"Auto Mode",
     49	NULL
     50};
     51
     52static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = {
     53	.ops	= &pwc_ctrl_ops,
     54	.id	= V4L2_CID_AUTO_WHITE_BALANCE,
     55	.type	= V4L2_CTRL_TYPE_MENU,
     56	.max	= awb_auto,
     57	.qmenu	= pwc_auto_whitebal_qmenu,
     58};
     59
     60static const struct v4l2_ctrl_config pwc_autocontour_cfg = {
     61	.ops	= &pwc_ctrl_ops,
     62	.id	= PWC_CID_CUSTOM(autocontour),
     63	.type	= V4L2_CTRL_TYPE_BOOLEAN,
     64	.name	= "Auto contour",
     65	.min	= 0,
     66	.max	= 1,
     67	.step	= 1,
     68};
     69
     70static const struct v4l2_ctrl_config pwc_contour_cfg = {
     71	.ops	= &pwc_ctrl_ops,
     72	.id	= PWC_CID_CUSTOM(contour),
     73	.type	= V4L2_CTRL_TYPE_INTEGER,
     74	.name	= "Contour",
     75	.flags  = V4L2_CTRL_FLAG_SLIDER,
     76	.min	= 0,
     77	.max	= 63,
     78	.step	= 1,
     79};
     80
     81static const struct v4l2_ctrl_config pwc_backlight_cfg = {
     82	.ops	= &pwc_ctrl_ops,
     83	.id	= V4L2_CID_BACKLIGHT_COMPENSATION,
     84	.type	= V4L2_CTRL_TYPE_BOOLEAN,
     85	.min	= 0,
     86	.max	= 1,
     87	.step	= 1,
     88};
     89
     90static const struct v4l2_ctrl_config pwc_flicker_cfg = {
     91	.ops	= &pwc_ctrl_ops,
     92	.id	= V4L2_CID_BAND_STOP_FILTER,
     93	.type	= V4L2_CTRL_TYPE_BOOLEAN,
     94	.min	= 0,
     95	.max	= 1,
     96	.step	= 1,
     97};
     98
     99static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = {
    100	.ops	= &pwc_ctrl_ops,
    101	.id	= PWC_CID_CUSTOM(noise_reduction),
    102	.type	= V4L2_CTRL_TYPE_INTEGER,
    103	.name	= "Dynamic Noise Reduction",
    104	.min	= 0,
    105	.max	= 3,
    106	.step	= 1,
    107};
    108
    109static const struct v4l2_ctrl_config pwc_save_user_cfg = {
    110	.ops	= &pwc_ctrl_ops,
    111	.id	= PWC_CID_CUSTOM(save_user),
    112	.type	= V4L2_CTRL_TYPE_BUTTON,
    113	.name    = "Save User Settings",
    114};
    115
    116static const struct v4l2_ctrl_config pwc_restore_user_cfg = {
    117	.ops	= &pwc_ctrl_ops,
    118	.id	= PWC_CID_CUSTOM(restore_user),
    119	.type	= V4L2_CTRL_TYPE_BUTTON,
    120	.name    = "Restore User Settings",
    121};
    122
    123static const struct v4l2_ctrl_config pwc_restore_factory_cfg = {
    124	.ops	= &pwc_ctrl_ops,
    125	.id	= PWC_CID_CUSTOM(restore_factory),
    126	.type	= V4L2_CTRL_TYPE_BUTTON,
    127	.name    = "Restore Factory Settings",
    128};
    129
    130static const struct v4l2_ctrl_config pwc_awb_speed_cfg = {
    131	.ops	= &pwc_ctrl_ops,
    132	.id	= PWC_CID_CUSTOM(awb_speed),
    133	.type	= V4L2_CTRL_TYPE_INTEGER,
    134	.name	= "Auto White Balance Speed",
    135	.min	= 1,
    136	.max	= 32,
    137	.step	= 1,
    138};
    139
    140static const struct v4l2_ctrl_config pwc_awb_delay_cfg = {
    141	.ops	= &pwc_ctrl_ops,
    142	.id	= PWC_CID_CUSTOM(awb_delay),
    143	.type	= V4L2_CTRL_TYPE_INTEGER,
    144	.name	= "Auto White Balance Delay",
    145	.min	= 0,
    146	.max	= 63,
    147	.step	= 1,
    148};
    149
    150int pwc_init_controls(struct pwc_device *pdev)
    151{
    152	struct v4l2_ctrl_handler *hdl;
    153	struct v4l2_ctrl_config cfg;
    154	int r, def;
    155
    156	hdl = &pdev->ctrl_handler;
    157	r = v4l2_ctrl_handler_init(hdl, 20);
    158	if (r)
    159		return r;
    160
    161	/* Brightness, contrast, saturation, gamma */
    162	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def);
    163	if (r || def > 127)
    164		def = 63;
    165	pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    166				V4L2_CID_BRIGHTNESS, 0, 127, 1, def);
    167
    168	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def);
    169	if (r || def > 63)
    170		def = 31;
    171	pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    172				V4L2_CID_CONTRAST, 0, 63, 1, def);
    173
    174	if (pdev->type >= 675) {
    175		if (pdev->type < 730)
    176			pdev->saturation_fmt = SATURATION_MODE_FORMATTER2;
    177		else
    178			pdev->saturation_fmt = SATURATION_MODE_FORMATTER1;
    179		r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt,
    180				    &def);
    181		if (r || def < -100 || def > 100)
    182			def = 0;
    183		pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    184				      V4L2_CID_SATURATION, -100, 100, 1, def);
    185	}
    186
    187	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def);
    188	if (r || def > 31)
    189		def = 15;
    190	pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    191				V4L2_CID_GAMMA, 0, 31, 1, def);
    192
    193	/* auto white balance, red gain, blue gain */
    194	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def);
    195	if (r || def > awb_auto)
    196		def = awb_auto;
    197	cfg = pwc_auto_white_balance_cfg;
    198	cfg.name = v4l2_ctrl_get_name(cfg.id);
    199	cfg.def = def;
    200	pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    201	/* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */
    202	if (!pdev->auto_white_balance)
    203		return hdl->error;
    204
    205	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
    206			    PRESET_MANUAL_RED_GAIN_FORMATTER, &def);
    207	if (r)
    208		def = 127;
    209	pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    210				V4L2_CID_RED_BALANCE, 0, 255, 1, def);
    211
    212	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
    213			    PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def);
    214	if (r)
    215		def = 127;
    216	pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    217				V4L2_CID_BLUE_BALANCE, 0, 255, 1, def);
    218
    219	v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true);
    220
    221	/* autogain, gain */
    222	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def);
    223	if (r || (def != 0 && def != 0xff))
    224		def = 0;
    225	/* Note a register value if 0 means auto gain is on */
    226	pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    227				V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0);
    228	if (!pdev->autogain)
    229		return hdl->error;
    230
    231	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def);
    232	if (r || def > 63)
    233		def = 31;
    234	pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    235				V4L2_CID_GAIN, 0, 63, 1, def);
    236
    237	/* auto exposure, exposure */
    238	if (DEVICE_USE_CODEC2(pdev->type)) {
    239		r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER,
    240				    &def);
    241		if (r || (def != 0 && def != 0xff))
    242			def = 0;
    243		/*
    244		 * def = 0 auto, def = ff manual
    245		 * menu idx 0 = auto, idx 1 = manual
    246		 */
    247		pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl,
    248					&pwc_ctrl_ops,
    249					V4L2_CID_EXPOSURE_AUTO,
    250					1, 0, def != 0);
    251		if (!pdev->exposure_auto)
    252			return hdl->error;
    253
    254		/* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
    255		r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
    256				     READ_SHUTTER_FORMATTER, &def);
    257		if (r || def > 655)
    258			def = 655;
    259		pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    260					V4L2_CID_EXPOSURE, 0, 655, 1, def);
    261		/* CODEC2: separate auto gain & auto exposure */
    262		v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true);
    263		v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto,
    264				       V4L2_EXPOSURE_MANUAL, true);
    265	} else if (DEVICE_USE_CODEC3(pdev->type)) {
    266		/* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
    267		r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
    268				     READ_SHUTTER_FORMATTER, &def);
    269		if (r || def > 255)
    270			def = 255;
    271		pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    272					V4L2_CID_EXPOSURE, 0, 255, 1, def);
    273		/* CODEC3: both gain and exposure controlled by autogain */
    274		pdev->autogain_expo_cluster[0] = pdev->autogain;
    275		pdev->autogain_expo_cluster[1] = pdev->gain;
    276		pdev->autogain_expo_cluster[2] = pdev->exposure;
    277		v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster,
    278				       0, true);
    279	}
    280
    281	/* color / bw setting */
    282	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER,
    283			 &def);
    284	if (r || (def != 0 && def != 0xff))
    285		def = 0xff;
    286	/* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */
    287	pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops,
    288				V4L2_CID_COLORFX, 1, 0, def == 0);
    289
    290	/* autocontour, contour */
    291	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def);
    292	if (r || (def != 0 && def != 0xff))
    293		def = 0;
    294	cfg = pwc_autocontour_cfg;
    295	cfg.def = def == 0;
    296	pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    297	if (!pdev->autocontour)
    298		return hdl->error;
    299
    300	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def);
    301	if (r || def > 63)
    302		def = 31;
    303	cfg = pwc_contour_cfg;
    304	cfg.def = def;
    305	pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    306
    307	v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false);
    308
    309	/* backlight */
    310	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
    311			    BACK_LIGHT_COMPENSATION_FORMATTER, &def);
    312	if (r || (def != 0 && def != 0xff))
    313		def = 0;
    314	cfg = pwc_backlight_cfg;
    315	cfg.name = v4l2_ctrl_get_name(cfg.id);
    316	cfg.def = def == 0;
    317	pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    318
    319	/* flikker rediction */
    320	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
    321			    FLICKERLESS_MODE_FORMATTER, &def);
    322	if (r || (def != 0 && def != 0xff))
    323		def = 0;
    324	cfg = pwc_flicker_cfg;
    325	cfg.name = v4l2_ctrl_get_name(cfg.id);
    326	cfg.def = def == 0;
    327	pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    328
    329	/* Dynamic noise reduction */
    330	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
    331			    DYNAMIC_NOISE_CONTROL_FORMATTER, &def);
    332	if (r || def > 3)
    333		def = 2;
    334	cfg = pwc_noise_reduction_cfg;
    335	cfg.def = def;
    336	pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    337
    338	/* Save / Restore User / Factory Settings */
    339	pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL);
    340	pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg,
    341						  NULL);
    342	if (pdev->restore_user)
    343		pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE;
    344	pdev->restore_factory = v4l2_ctrl_new_custom(hdl,
    345						     &pwc_restore_factory_cfg,
    346						     NULL);
    347	if (pdev->restore_factory)
    348		pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE;
    349
    350	/* Auto White Balance speed & delay */
    351	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
    352			    AWB_CONTROL_SPEED_FORMATTER, &def);
    353	if (r || def < 1 || def > 32)
    354		def = 1;
    355	cfg = pwc_awb_speed_cfg;
    356	cfg.def = def;
    357	pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    358
    359	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
    360			    AWB_CONTROL_DELAY_FORMATTER, &def);
    361	if (r || def > 63)
    362		def = 0;
    363	cfg = pwc_awb_delay_cfg;
    364	cfg.def = def;
    365	pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
    366
    367	if (!(pdev->features & FEATURE_MOTOR_PANTILT))
    368		return hdl->error;
    369
    370	/* Motor pan / tilt / reset */
    371	pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    372				V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0);
    373	if (!pdev->motor_pan)
    374		return hdl->error;
    375	pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    376				V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0);
    377	pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    378				V4L2_CID_PAN_RESET, 0, 0, 0, 0);
    379	pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
    380				V4L2_CID_TILT_RESET, 0, 0, 0, 0);
    381	v4l2_ctrl_cluster(4, &pdev->motor_pan);
    382
    383	return hdl->error;
    384}
    385
    386static void pwc_vidioc_fill_fmt(struct v4l2_format *f,
    387	int width, int height, u32 pixfmt)
    388{
    389	memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
    390	f->fmt.pix.width        = width;
    391	f->fmt.pix.height       = height;
    392	f->fmt.pix.field        = V4L2_FIELD_NONE;
    393	f->fmt.pix.pixelformat  = pixfmt;
    394	f->fmt.pix.bytesperline = f->fmt.pix.width;
    395	f->fmt.pix.sizeimage	= f->fmt.pix.height * f->fmt.pix.width * 3 / 2;
    396	f->fmt.pix.colorspace	= V4L2_COLORSPACE_SRGB;
    397	PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
    398			f->fmt.pix.width,
    399			f->fmt.pix.height,
    400			f->fmt.pix.bytesperline,
    401			f->fmt.pix.sizeimage,
    402			(f->fmt.pix.pixelformat)&255,
    403			(f->fmt.pix.pixelformat>>8)&255,
    404			(f->fmt.pix.pixelformat>>16)&255,
    405			(f->fmt.pix.pixelformat>>24)&255);
    406}
    407
    408/* ioctl(VIDIOC_TRY_FMT) */
    409static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f)
    410{
    411	int size;
    412
    413	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
    414		PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
    415		return -EINVAL;
    416	}
    417
    418	switch (f->fmt.pix.pixelformat) {
    419		case V4L2_PIX_FMT_YUV420:
    420			break;
    421		case V4L2_PIX_FMT_PWC1:
    422			if (DEVICE_USE_CODEC23(pdev->type)) {
    423				PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n");
    424				f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    425			}
    426			break;
    427		case V4L2_PIX_FMT_PWC2:
    428			if (DEVICE_USE_CODEC1(pdev->type)) {
    429				PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n");
    430				f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    431			}
    432			break;
    433		default:
    434			PWC_DEBUG_IOCTL("Unsupported pixel format\n");
    435			f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    436	}
    437
    438	size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height);
    439	pwc_vidioc_fill_fmt(f,
    440			    pwc_image_sizes[size][0],
    441			    pwc_image_sizes[size][1],
    442			    f->fmt.pix.pixelformat);
    443
    444	return 0;
    445}
    446
    447/* ioctl(VIDIOC_SET_FMT) */
    448
    449static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
    450{
    451	struct pwc_device *pdev = video_drvdata(file);
    452	int ret, pixelformat, compression = 0;
    453
    454	ret = pwc_vidioc_try_fmt(pdev, f);
    455	if (ret < 0)
    456		return ret;
    457
    458	if (vb2_is_busy(&pdev->vb_queue))
    459		return -EBUSY;
    460
    461	pixelformat = f->fmt.pix.pixelformat;
    462
    463	PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d format=%c%c%c%c\n",
    464			f->fmt.pix.width, f->fmt.pix.height, pdev->vframes,
    465			(pixelformat)&255,
    466			(pixelformat>>8)&255,
    467			(pixelformat>>16)&255,
    468			(pixelformat>>24)&255);
    469
    470	ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height,
    471				 pixelformat, 30, &compression, 0);
    472
    473	PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
    474
    475	pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
    476	return ret;
    477}
    478
    479static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
    480{
    481	struct pwc_device *pdev = video_drvdata(file);
    482
    483	strscpy(cap->driver, PWC_NAME, sizeof(cap->driver));
    484	strscpy(cap->card, pdev->vdev.name, sizeof(cap->card));
    485	usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
    486	return 0;
    487}
    488
    489static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
    490{
    491	if (i->index)	/* Only one INPUT is supported */
    492		return -EINVAL;
    493
    494	strscpy(i->name, "Camera", sizeof(i->name));
    495	i->type = V4L2_INPUT_TYPE_CAMERA;
    496	return 0;
    497}
    498
    499static int pwc_g_input(struct file *file, void *fh, unsigned int *i)
    500{
    501	*i = 0;
    502	return 0;
    503}
    504
    505static int pwc_s_input(struct file *file, void *fh, unsigned int i)
    506{
    507	return i ? -EINVAL : 0;
    508}
    509
    510static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
    511{
    512	struct pwc_device *pdev =
    513		container_of(ctrl->handler, struct pwc_device, ctrl_handler);
    514	int ret = 0;
    515
    516	switch (ctrl->id) {
    517	case V4L2_CID_AUTO_WHITE_BALANCE:
    518		if (pdev->color_bal_valid &&
    519			(pdev->auto_white_balance->val != awb_auto ||
    520			 time_before(jiffies,
    521				pdev->last_color_bal_update + HZ / 4))) {
    522			pdev->red_balance->val  = pdev->last_red_balance;
    523			pdev->blue_balance->val = pdev->last_blue_balance;
    524			break;
    525		}
    526		ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
    527				      READ_RED_GAIN_FORMATTER,
    528				      &pdev->red_balance->val);
    529		if (ret)
    530			break;
    531		ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
    532				      READ_BLUE_GAIN_FORMATTER,
    533				      &pdev->blue_balance->val);
    534		if (ret)
    535			break;
    536		pdev->last_red_balance  = pdev->red_balance->val;
    537		pdev->last_blue_balance = pdev->blue_balance->val;
    538		pdev->last_color_bal_update = jiffies;
    539		pdev->color_bal_valid = true;
    540		break;
    541	case V4L2_CID_AUTOGAIN:
    542		if (pdev->gain_valid && time_before(jiffies,
    543				pdev->last_gain_update + HZ / 4)) {
    544			pdev->gain->val = pdev->last_gain;
    545			break;
    546		}
    547		ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
    548				      READ_AGC_FORMATTER, &pdev->gain->val);
    549		if (ret)
    550			break;
    551		pdev->last_gain = pdev->gain->val;
    552		pdev->last_gain_update = jiffies;
    553		pdev->gain_valid = true;
    554		if (!DEVICE_USE_CODEC3(pdev->type))
    555			break;
    556		/* For CODEC3 where autogain also controls expo */
    557		fallthrough;
    558	case V4L2_CID_EXPOSURE_AUTO:
    559		if (pdev->exposure_valid && time_before(jiffies,
    560				pdev->last_exposure_update + HZ / 4)) {
    561			pdev->exposure->val = pdev->last_exposure;
    562			break;
    563		}
    564		ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
    565				       READ_SHUTTER_FORMATTER,
    566				       &pdev->exposure->val);
    567		if (ret)
    568			break;
    569		pdev->last_exposure = pdev->exposure->val;
    570		pdev->last_exposure_update = jiffies;
    571		pdev->exposure_valid = true;
    572		break;
    573	default:
    574		ret = -EINVAL;
    575	}
    576
    577	if (ret)
    578		PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
    579
    580	return ret;
    581}
    582
    583static int pwc_set_awb(struct pwc_device *pdev)
    584{
    585	int ret;
    586
    587	if (pdev->auto_white_balance->is_new) {
    588		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
    589				      WB_MODE_FORMATTER,
    590				      pdev->auto_white_balance->val);
    591		if (ret)
    592			return ret;
    593
    594		if (pdev->auto_white_balance->val != awb_manual)
    595			pdev->color_bal_valid = false; /* Force cache update */
    596
    597		/*
    598		 * If this is a preset, update our red / blue balance values
    599		 * so that events get generated for the new preset values
    600		 */
    601		if (pdev->auto_white_balance->val == awb_indoor ||
    602		    pdev->auto_white_balance->val == awb_outdoor ||
    603		    pdev->auto_white_balance->val == awb_fl)
    604			pwc_g_volatile_ctrl(pdev->auto_white_balance);
    605	}
    606	if (pdev->auto_white_balance->val != awb_manual)
    607		return 0;
    608
    609	if (pdev->red_balance->is_new) {
    610		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
    611				      PRESET_MANUAL_RED_GAIN_FORMATTER,
    612				      pdev->red_balance->val);
    613		if (ret)
    614			return ret;
    615	}
    616
    617	if (pdev->blue_balance->is_new) {
    618		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
    619				      PRESET_MANUAL_BLUE_GAIN_FORMATTER,
    620				      pdev->blue_balance->val);
    621		if (ret)
    622			return ret;
    623	}
    624	return 0;
    625}
    626
    627/* For CODEC2 models which have separate autogain and auto exposure */
    628static int pwc_set_autogain(struct pwc_device *pdev)
    629{
    630	int ret;
    631
    632	if (pdev->autogain->is_new) {
    633		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    634				      AGC_MODE_FORMATTER,
    635				      pdev->autogain->val ? 0 : 0xff);
    636		if (ret)
    637			return ret;
    638
    639		if (pdev->autogain->val)
    640			pdev->gain_valid = false; /* Force cache update */
    641	}
    642
    643	if (pdev->autogain->val)
    644		return 0;
    645
    646	if (pdev->gain->is_new) {
    647		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    648				      PRESET_AGC_FORMATTER,
    649				      pdev->gain->val);
    650		if (ret)
    651			return ret;
    652	}
    653	return 0;
    654}
    655
    656/* For CODEC2 models which have separate autogain and auto exposure */
    657static int pwc_set_exposure_auto(struct pwc_device *pdev)
    658{
    659	int ret;
    660	int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO;
    661
    662	if (pdev->exposure_auto->is_new) {
    663		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    664				      SHUTTER_MODE_FORMATTER,
    665				      is_auto ? 0 : 0xff);
    666		if (ret)
    667			return ret;
    668
    669		if (is_auto)
    670			pdev->exposure_valid = false; /* Force cache update */
    671	}
    672
    673	if (is_auto)
    674		return 0;
    675
    676	if (pdev->exposure->is_new) {
    677		ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
    678				       PRESET_SHUTTER_FORMATTER,
    679				       pdev->exposure->val);
    680		if (ret)
    681			return ret;
    682	}
    683	return 0;
    684}
    685
    686/* For CODEC3 models which have autogain controlling both gain and exposure */
    687static int pwc_set_autogain_expo(struct pwc_device *pdev)
    688{
    689	int ret;
    690
    691	if (pdev->autogain->is_new) {
    692		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    693				      AGC_MODE_FORMATTER,
    694				      pdev->autogain->val ? 0 : 0xff);
    695		if (ret)
    696			return ret;
    697
    698		if (pdev->autogain->val) {
    699			pdev->gain_valid     = false; /* Force cache update */
    700			pdev->exposure_valid = false; /* Force cache update */
    701		}
    702	}
    703
    704	if (pdev->autogain->val)
    705		return 0;
    706
    707	if (pdev->gain->is_new) {
    708		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    709				      PRESET_AGC_FORMATTER,
    710				      pdev->gain->val);
    711		if (ret)
    712			return ret;
    713	}
    714
    715	if (pdev->exposure->is_new) {
    716		ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
    717				       PRESET_SHUTTER_FORMATTER,
    718				       pdev->exposure->val);
    719		if (ret)
    720			return ret;
    721	}
    722	return 0;
    723}
    724
    725static int pwc_set_motor(struct pwc_device *pdev)
    726{
    727	int ret;
    728
    729	pdev->ctrl_buf[0] = 0;
    730	if (pdev->motor_pan_reset->is_new)
    731		pdev->ctrl_buf[0] |= 0x01;
    732	if (pdev->motor_tilt_reset->is_new)
    733		pdev->ctrl_buf[0] |= 0x02;
    734	if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) {
    735		ret = send_control_msg(pdev, SET_MPT_CTL,
    736				       PT_RESET_CONTROL_FORMATTER,
    737				       pdev->ctrl_buf, 1);
    738		if (ret < 0)
    739			return ret;
    740	}
    741
    742	memset(pdev->ctrl_buf, 0, 4);
    743	if (pdev->motor_pan->is_new) {
    744		pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF;
    745		pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8);
    746	}
    747	if (pdev->motor_tilt->is_new) {
    748		pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF;
    749		pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8);
    750	}
    751	if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) {
    752		ret = send_control_msg(pdev, SET_MPT_CTL,
    753				       PT_RELATIVE_CONTROL_FORMATTER,
    754				       pdev->ctrl_buf, 4);
    755		if (ret < 0)
    756			return ret;
    757	}
    758
    759	return 0;
    760}
    761
    762static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
    763{
    764	struct pwc_device *pdev =
    765		container_of(ctrl->handler, struct pwc_device, ctrl_handler);
    766	int ret = 0;
    767
    768	switch (ctrl->id) {
    769	case V4L2_CID_BRIGHTNESS:
    770		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    771				      BRIGHTNESS_FORMATTER, ctrl->val);
    772		break;
    773	case V4L2_CID_CONTRAST:
    774		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    775				      CONTRAST_FORMATTER, ctrl->val);
    776		break;
    777	case V4L2_CID_SATURATION:
    778		ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL,
    779				      pdev->saturation_fmt, ctrl->val);
    780		break;
    781	case V4L2_CID_GAMMA:
    782		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    783				      GAMMA_FORMATTER, ctrl->val);
    784		break;
    785	case V4L2_CID_AUTO_WHITE_BALANCE:
    786		ret = pwc_set_awb(pdev);
    787		break;
    788	case V4L2_CID_AUTOGAIN:
    789		if (DEVICE_USE_CODEC2(pdev->type))
    790			ret = pwc_set_autogain(pdev);
    791		else if (DEVICE_USE_CODEC3(pdev->type))
    792			ret = pwc_set_autogain_expo(pdev);
    793		else
    794			ret = -EINVAL;
    795		break;
    796	case V4L2_CID_EXPOSURE_AUTO:
    797		if (DEVICE_USE_CODEC2(pdev->type))
    798			ret = pwc_set_exposure_auto(pdev);
    799		else
    800			ret = -EINVAL;
    801		break;
    802	case V4L2_CID_COLORFX:
    803		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
    804				      COLOUR_MODE_FORMATTER,
    805				      ctrl->val ? 0 : 0xff);
    806		break;
    807	case PWC_CID_CUSTOM(autocontour):
    808		if (pdev->autocontour->is_new) {
    809			ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    810					AUTO_CONTOUR_FORMATTER,
    811					pdev->autocontour->val ? 0 : 0xff);
    812		}
    813		if (ret == 0 && pdev->contour->is_new) {
    814			ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    815					      PRESET_CONTOUR_FORMATTER,
    816					      pdev->contour->val);
    817		}
    818		break;
    819	case V4L2_CID_BACKLIGHT_COMPENSATION:
    820		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    821				      BACK_LIGHT_COMPENSATION_FORMATTER,
    822				      ctrl->val ? 0 : 0xff);
    823		break;
    824	case V4L2_CID_BAND_STOP_FILTER:
    825		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    826				      FLICKERLESS_MODE_FORMATTER,
    827				      ctrl->val ? 0 : 0xff);
    828		break;
    829	case PWC_CID_CUSTOM(noise_reduction):
    830		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
    831				      DYNAMIC_NOISE_CONTROL_FORMATTER,
    832				      ctrl->val);
    833		break;
    834	case PWC_CID_CUSTOM(save_user):
    835		ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
    836		break;
    837	case PWC_CID_CUSTOM(restore_user):
    838		ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
    839		break;
    840	case PWC_CID_CUSTOM(restore_factory):
    841		ret = pwc_button_ctrl(pdev,
    842				      RESTORE_FACTORY_DEFAULTS_FORMATTER);
    843		break;
    844	case PWC_CID_CUSTOM(awb_speed):
    845		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
    846				      AWB_CONTROL_SPEED_FORMATTER,
    847				      ctrl->val);
    848		break;
    849	case PWC_CID_CUSTOM(awb_delay):
    850		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
    851				      AWB_CONTROL_DELAY_FORMATTER,
    852				      ctrl->val);
    853		break;
    854	case V4L2_CID_PAN_RELATIVE:
    855		ret = pwc_set_motor(pdev);
    856		break;
    857	default:
    858		ret = -EINVAL;
    859	}
    860
    861	if (ret)
    862		PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
    863
    864	return ret;
    865}
    866
    867static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
    868{
    869	struct pwc_device *pdev = video_drvdata(file);
    870
    871	/* We only support two format: the raw format, and YUV */
    872	switch (f->index) {
    873	case 0:
    874		/* RAW format */
    875		f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2;
    876		break;
    877	case 1:
    878		f->pixelformat = V4L2_PIX_FMT_YUV420;
    879		break;
    880	default:
    881		return -EINVAL;
    882	}
    883	return 0;
    884}
    885
    886static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
    887{
    888	struct pwc_device *pdev = video_drvdata(file);
    889
    890	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    891		return -EINVAL;
    892
    893	PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
    894			pdev->width, pdev->height);
    895	pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
    896	return 0;
    897}
    898
    899static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
    900{
    901	struct pwc_device *pdev = video_drvdata(file);
    902
    903	return pwc_vidioc_try_fmt(pdev, f);
    904}
    905
    906static int pwc_enum_framesizes(struct file *file, void *fh,
    907					 struct v4l2_frmsizeenum *fsize)
    908{
    909	struct pwc_device *pdev = video_drvdata(file);
    910	unsigned int i = 0, index = fsize->index;
    911
    912	if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 ||
    913	    (fsize->pixel_format == V4L2_PIX_FMT_PWC1 &&
    914			DEVICE_USE_CODEC1(pdev->type)) ||
    915	    (fsize->pixel_format == V4L2_PIX_FMT_PWC2 &&
    916			DEVICE_USE_CODEC23(pdev->type))) {
    917		for (i = 0; i < PSZ_MAX; i++) {
    918			if (!(pdev->image_mask & (1UL << i)))
    919				continue;
    920			if (!index--) {
    921				fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
    922				fsize->discrete.width = pwc_image_sizes[i][0];
    923				fsize->discrete.height = pwc_image_sizes[i][1];
    924				return 0;
    925			}
    926		}
    927	}
    928	return -EINVAL;
    929}
    930
    931static int pwc_enum_frameintervals(struct file *file, void *fh,
    932					   struct v4l2_frmivalenum *fival)
    933{
    934	struct pwc_device *pdev = video_drvdata(file);
    935	int size = -1;
    936	unsigned int i;
    937
    938	for (i = 0; i < PSZ_MAX; i++) {
    939		if (pwc_image_sizes[i][0] == fival->width &&
    940				pwc_image_sizes[i][1] == fival->height) {
    941			size = i;
    942			break;
    943		}
    944	}
    945
    946	/* TODO: Support raw format */
    947	if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420)
    948		return -EINVAL;
    949
    950	i = pwc_get_fps(pdev, fival->index, size);
    951	if (!i)
    952		return -EINVAL;
    953
    954	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
    955	fival->discrete.numerator = 1;
    956	fival->discrete.denominator = i;
    957
    958	return 0;
    959}
    960
    961static int pwc_g_parm(struct file *file, void *fh,
    962		      struct v4l2_streamparm *parm)
    963{
    964	struct pwc_device *pdev = video_drvdata(file);
    965
    966	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    967		return -EINVAL;
    968
    969	memset(parm, 0, sizeof(*parm));
    970
    971	parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    972	parm->parm.capture.readbuffers = MIN_FRAMES;
    973	parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
    974	parm->parm.capture.timeperframe.denominator = pdev->vframes;
    975	parm->parm.capture.timeperframe.numerator = 1;
    976
    977	return 0;
    978}
    979
    980static int pwc_s_parm(struct file *file, void *fh,
    981		      struct v4l2_streamparm *parm)
    982{
    983	struct pwc_device *pdev = video_drvdata(file);
    984	int compression = 0;
    985	int ret, fps;
    986
    987	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    988		return -EINVAL;
    989
    990	/* If timeperframe == 0, then reset the framerate to the nominal value.
    991	   We pick a high framerate here, and let pwc_set_video_mode() figure
    992	   out the best match. */
    993	if (parm->parm.capture.timeperframe.numerator == 0 ||
    994	    parm->parm.capture.timeperframe.denominator == 0)
    995		fps = 30;
    996	else
    997		fps = parm->parm.capture.timeperframe.denominator /
    998		      parm->parm.capture.timeperframe.numerator;
    999
   1000	if (vb2_is_busy(&pdev->vb_queue))
   1001		return -EBUSY;
   1002
   1003	ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
   1004				 fps, &compression, 0);
   1005
   1006	pwc_g_parm(file, fh, parm);
   1007
   1008	return ret;
   1009}
   1010
   1011const struct v4l2_ioctl_ops pwc_ioctl_ops = {
   1012	.vidioc_querycap		    = pwc_querycap,
   1013	.vidioc_enum_input		    = pwc_enum_input,
   1014	.vidioc_g_input			    = pwc_g_input,
   1015	.vidioc_s_input			    = pwc_s_input,
   1016	.vidioc_enum_fmt_vid_cap	    = pwc_enum_fmt_vid_cap,
   1017	.vidioc_g_fmt_vid_cap		    = pwc_g_fmt_vid_cap,
   1018	.vidioc_s_fmt_vid_cap		    = pwc_s_fmt_vid_cap,
   1019	.vidioc_try_fmt_vid_cap		    = pwc_try_fmt_vid_cap,
   1020	.vidioc_reqbufs			    = vb2_ioctl_reqbufs,
   1021	.vidioc_querybuf		    = vb2_ioctl_querybuf,
   1022	.vidioc_qbuf			    = vb2_ioctl_qbuf,
   1023	.vidioc_dqbuf			    = vb2_ioctl_dqbuf,
   1024	.vidioc_streamon		    = vb2_ioctl_streamon,
   1025	.vidioc_streamoff		    = vb2_ioctl_streamoff,
   1026	.vidioc_log_status		    = v4l2_ctrl_log_status,
   1027	.vidioc_enum_framesizes		    = pwc_enum_framesizes,
   1028	.vidioc_enum_frameintervals	    = pwc_enum_frameintervals,
   1029	.vidioc_g_parm			    = pwc_g_parm,
   1030	.vidioc_s_parm			    = pwc_s_parm,
   1031	.vidioc_subscribe_event		    = v4l2_ctrl_subscribe_event,
   1032	.vidioc_unsubscribe_event	    = v4l2_event_unsubscribe,
   1033};