0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/errno.h>
0018 #include <linux/init.h>
0019 #include <linux/mm.h>
0020 #include <linux/module.h>
0021 #include <linux/poll.h>
0022 #include <linux/vmalloc.h>
0023 #include <linux/jiffies.h>
0024 #include <asm/io.h>
0025
0026 #include "pwc.h"
0027
0028 #define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl)
0029
0030 static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
0031 static int pwc_s_ctrl(struct v4l2_ctrl *ctrl);
0032
0033 static const struct v4l2_ctrl_ops pwc_ctrl_ops = {
0034 .g_volatile_ctrl = pwc_g_volatile_ctrl,
0035 .s_ctrl = pwc_s_ctrl,
0036 };
0037
0038 enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto };
0039 enum { custom_autocontour, custom_contour, custom_noise_reduction,
0040 custom_awb_speed, custom_awb_delay,
0041 custom_save_user, custom_restore_user, custom_restore_factory };
0042
0043 static const char * const pwc_auto_whitebal_qmenu[] = {
0044 "Indoor (Incandescant Lighting) Mode",
0045 "Outdoor (Sunlight) Mode",
0046 "Indoor (Fluorescent Lighting) Mode",
0047 "Manual Mode",
0048 "Auto Mode",
0049 NULL
0050 };
0051
0052 static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = {
0053 .ops = &pwc_ctrl_ops,
0054 .id = V4L2_CID_AUTO_WHITE_BALANCE,
0055 .type = V4L2_CTRL_TYPE_MENU,
0056 .max = awb_auto,
0057 .qmenu = pwc_auto_whitebal_qmenu,
0058 };
0059
0060 static const struct v4l2_ctrl_config pwc_autocontour_cfg = {
0061 .ops = &pwc_ctrl_ops,
0062 .id = PWC_CID_CUSTOM(autocontour),
0063 .type = V4L2_CTRL_TYPE_BOOLEAN,
0064 .name = "Auto contour",
0065 .min = 0,
0066 .max = 1,
0067 .step = 1,
0068 };
0069
0070 static const struct v4l2_ctrl_config pwc_contour_cfg = {
0071 .ops = &pwc_ctrl_ops,
0072 .id = PWC_CID_CUSTOM(contour),
0073 .type = V4L2_CTRL_TYPE_INTEGER,
0074 .name = "Contour",
0075 .flags = V4L2_CTRL_FLAG_SLIDER,
0076 .min = 0,
0077 .max = 63,
0078 .step = 1,
0079 };
0080
0081 static const struct v4l2_ctrl_config pwc_backlight_cfg = {
0082 .ops = &pwc_ctrl_ops,
0083 .id = V4L2_CID_BACKLIGHT_COMPENSATION,
0084 .type = V4L2_CTRL_TYPE_BOOLEAN,
0085 .min = 0,
0086 .max = 1,
0087 .step = 1,
0088 };
0089
0090 static const struct v4l2_ctrl_config pwc_flicker_cfg = {
0091 .ops = &pwc_ctrl_ops,
0092 .id = V4L2_CID_BAND_STOP_FILTER,
0093 .type = V4L2_CTRL_TYPE_BOOLEAN,
0094 .min = 0,
0095 .max = 1,
0096 .step = 1,
0097 };
0098
0099 static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = {
0100 .ops = &pwc_ctrl_ops,
0101 .id = PWC_CID_CUSTOM(noise_reduction),
0102 .type = V4L2_CTRL_TYPE_INTEGER,
0103 .name = "Dynamic Noise Reduction",
0104 .min = 0,
0105 .max = 3,
0106 .step = 1,
0107 };
0108
0109 static const struct v4l2_ctrl_config pwc_save_user_cfg = {
0110 .ops = &pwc_ctrl_ops,
0111 .id = PWC_CID_CUSTOM(save_user),
0112 .type = V4L2_CTRL_TYPE_BUTTON,
0113 .name = "Save User Settings",
0114 };
0115
0116 static const struct v4l2_ctrl_config pwc_restore_user_cfg = {
0117 .ops = &pwc_ctrl_ops,
0118 .id = PWC_CID_CUSTOM(restore_user),
0119 .type = V4L2_CTRL_TYPE_BUTTON,
0120 .name = "Restore User Settings",
0121 };
0122
0123 static const struct v4l2_ctrl_config pwc_restore_factory_cfg = {
0124 .ops = &pwc_ctrl_ops,
0125 .id = PWC_CID_CUSTOM(restore_factory),
0126 .type = V4L2_CTRL_TYPE_BUTTON,
0127 .name = "Restore Factory Settings",
0128 };
0129
0130 static const struct v4l2_ctrl_config pwc_awb_speed_cfg = {
0131 .ops = &pwc_ctrl_ops,
0132 .id = PWC_CID_CUSTOM(awb_speed),
0133 .type = V4L2_CTRL_TYPE_INTEGER,
0134 .name = "Auto White Balance Speed",
0135 .min = 1,
0136 .max = 32,
0137 .step = 1,
0138 };
0139
0140 static const struct v4l2_ctrl_config pwc_awb_delay_cfg = {
0141 .ops = &pwc_ctrl_ops,
0142 .id = PWC_CID_CUSTOM(awb_delay),
0143 .type = V4L2_CTRL_TYPE_INTEGER,
0144 .name = "Auto White Balance Delay",
0145 .min = 0,
0146 .max = 63,
0147 .step = 1,
0148 };
0149
0150 int pwc_init_controls(struct pwc_device *pdev)
0151 {
0152 struct v4l2_ctrl_handler *hdl;
0153 struct v4l2_ctrl_config cfg;
0154 int r, def;
0155
0156 hdl = &pdev->ctrl_handler;
0157 r = v4l2_ctrl_handler_init(hdl, 20);
0158 if (r)
0159 return r;
0160
0161
0162 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def);
0163 if (r || def > 127)
0164 def = 63;
0165 pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0166 V4L2_CID_BRIGHTNESS, 0, 127, 1, def);
0167
0168 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def);
0169 if (r || def > 63)
0170 def = 31;
0171 pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0172 V4L2_CID_CONTRAST, 0, 63, 1, def);
0173
0174 if (pdev->type >= 675) {
0175 if (pdev->type < 730)
0176 pdev->saturation_fmt = SATURATION_MODE_FORMATTER2;
0177 else
0178 pdev->saturation_fmt = SATURATION_MODE_FORMATTER1;
0179 r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt,
0180 &def);
0181 if (r || def < -100 || def > 100)
0182 def = 0;
0183 pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0184 V4L2_CID_SATURATION, -100, 100, 1, def);
0185 }
0186
0187 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def);
0188 if (r || def > 31)
0189 def = 15;
0190 pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0191 V4L2_CID_GAMMA, 0, 31, 1, def);
0192
0193
0194 r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def);
0195 if (r || def > awb_auto)
0196 def = awb_auto;
0197 cfg = pwc_auto_white_balance_cfg;
0198 cfg.name = v4l2_ctrl_get_name(cfg.id);
0199 cfg.def = def;
0200 pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0201
0202 if (!pdev->auto_white_balance)
0203 return hdl->error;
0204
0205 r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
0206 PRESET_MANUAL_RED_GAIN_FORMATTER, &def);
0207 if (r)
0208 def = 127;
0209 pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0210 V4L2_CID_RED_BALANCE, 0, 255, 1, def);
0211
0212 r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
0213 PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def);
0214 if (r)
0215 def = 127;
0216 pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0217 V4L2_CID_BLUE_BALANCE, 0, 255, 1, def);
0218
0219 v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true);
0220
0221
0222 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def);
0223 if (r || (def != 0 && def != 0xff))
0224 def = 0;
0225
0226 pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0227 V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0);
0228 if (!pdev->autogain)
0229 return hdl->error;
0230
0231 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def);
0232 if (r || def > 63)
0233 def = 31;
0234 pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0235 V4L2_CID_GAIN, 0, 63, 1, def);
0236
0237
0238 if (DEVICE_USE_CODEC2(pdev->type)) {
0239 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER,
0240 &def);
0241 if (r || (def != 0 && def != 0xff))
0242 def = 0;
0243
0244
0245
0246
0247 pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl,
0248 &pwc_ctrl_ops,
0249 V4L2_CID_EXPOSURE_AUTO,
0250 1, 0, def != 0);
0251 if (!pdev->exposure_auto)
0252 return hdl->error;
0253
0254
0255 r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
0256 READ_SHUTTER_FORMATTER, &def);
0257 if (r || def > 655)
0258 def = 655;
0259 pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0260 V4L2_CID_EXPOSURE, 0, 655, 1, def);
0261
0262 v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true);
0263 v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto,
0264 V4L2_EXPOSURE_MANUAL, true);
0265 } else if (DEVICE_USE_CODEC3(pdev->type)) {
0266
0267 r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
0268 READ_SHUTTER_FORMATTER, &def);
0269 if (r || def > 255)
0270 def = 255;
0271 pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0272 V4L2_CID_EXPOSURE, 0, 255, 1, def);
0273
0274 pdev->autogain_expo_cluster[0] = pdev->autogain;
0275 pdev->autogain_expo_cluster[1] = pdev->gain;
0276 pdev->autogain_expo_cluster[2] = pdev->exposure;
0277 v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster,
0278 0, true);
0279 }
0280
0281
0282 r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER,
0283 &def);
0284 if (r || (def != 0 && def != 0xff))
0285 def = 0xff;
0286
0287 pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops,
0288 V4L2_CID_COLORFX, 1, 0, def == 0);
0289
0290
0291 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def);
0292 if (r || (def != 0 && def != 0xff))
0293 def = 0;
0294 cfg = pwc_autocontour_cfg;
0295 cfg.def = def == 0;
0296 pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0297 if (!pdev->autocontour)
0298 return hdl->error;
0299
0300 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def);
0301 if (r || def > 63)
0302 def = 31;
0303 cfg = pwc_contour_cfg;
0304 cfg.def = def;
0305 pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0306
0307 v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false);
0308
0309
0310 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
0311 BACK_LIGHT_COMPENSATION_FORMATTER, &def);
0312 if (r || (def != 0 && def != 0xff))
0313 def = 0;
0314 cfg = pwc_backlight_cfg;
0315 cfg.name = v4l2_ctrl_get_name(cfg.id);
0316 cfg.def = def == 0;
0317 pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0318
0319
0320 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
0321 FLICKERLESS_MODE_FORMATTER, &def);
0322 if (r || (def != 0 && def != 0xff))
0323 def = 0;
0324 cfg = pwc_flicker_cfg;
0325 cfg.name = v4l2_ctrl_get_name(cfg.id);
0326 cfg.def = def == 0;
0327 pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0328
0329
0330 r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
0331 DYNAMIC_NOISE_CONTROL_FORMATTER, &def);
0332 if (r || def > 3)
0333 def = 2;
0334 cfg = pwc_noise_reduction_cfg;
0335 cfg.def = def;
0336 pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0337
0338
0339 pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL);
0340 pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg,
0341 NULL);
0342 if (pdev->restore_user)
0343 pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE;
0344 pdev->restore_factory = v4l2_ctrl_new_custom(hdl,
0345 &pwc_restore_factory_cfg,
0346 NULL);
0347 if (pdev->restore_factory)
0348 pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE;
0349
0350
0351 r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
0352 AWB_CONTROL_SPEED_FORMATTER, &def);
0353 if (r || def < 1 || def > 32)
0354 def = 1;
0355 cfg = pwc_awb_speed_cfg;
0356 cfg.def = def;
0357 pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0358
0359 r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
0360 AWB_CONTROL_DELAY_FORMATTER, &def);
0361 if (r || def > 63)
0362 def = 0;
0363 cfg = pwc_awb_delay_cfg;
0364 cfg.def = def;
0365 pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
0366
0367 if (!(pdev->features & FEATURE_MOTOR_PANTILT))
0368 return hdl->error;
0369
0370
0371 pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0372 V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0);
0373 if (!pdev->motor_pan)
0374 return hdl->error;
0375 pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0376 V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0);
0377 pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0378 V4L2_CID_PAN_RESET, 0, 0, 0, 0);
0379 pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
0380 V4L2_CID_TILT_RESET, 0, 0, 0, 0);
0381 v4l2_ctrl_cluster(4, &pdev->motor_pan);
0382
0383 return hdl->error;
0384 }
0385
0386 static void pwc_vidioc_fill_fmt(struct v4l2_format *f,
0387 int width, int height, u32 pixfmt)
0388 {
0389 memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
0390 f->fmt.pix.width = width;
0391 f->fmt.pix.height = height;
0392 f->fmt.pix.field = V4L2_FIELD_NONE;
0393 f->fmt.pix.pixelformat = pixfmt;
0394 f->fmt.pix.bytesperline = f->fmt.pix.width;
0395 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2;
0396 f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
0397 PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
0398 f->fmt.pix.width,
0399 f->fmt.pix.height,
0400 f->fmt.pix.bytesperline,
0401 f->fmt.pix.sizeimage,
0402 (f->fmt.pix.pixelformat)&255,
0403 (f->fmt.pix.pixelformat>>8)&255,
0404 (f->fmt.pix.pixelformat>>16)&255,
0405 (f->fmt.pix.pixelformat>>24)&255);
0406 }
0407
0408
0409 static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f)
0410 {
0411 int size;
0412
0413 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
0414 PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
0415 return -EINVAL;
0416 }
0417
0418 switch (f->fmt.pix.pixelformat) {
0419 case V4L2_PIX_FMT_YUV420:
0420 break;
0421 case V4L2_PIX_FMT_PWC1:
0422 if (DEVICE_USE_CODEC23(pdev->type)) {
0423 PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n");
0424 f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
0425 }
0426 break;
0427 case V4L2_PIX_FMT_PWC2:
0428 if (DEVICE_USE_CODEC1(pdev->type)) {
0429 PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n");
0430 f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
0431 }
0432 break;
0433 default:
0434 PWC_DEBUG_IOCTL("Unsupported pixel format\n");
0435 f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
0436 }
0437
0438 size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height);
0439 pwc_vidioc_fill_fmt(f,
0440 pwc_image_sizes[size][0],
0441 pwc_image_sizes[size][1],
0442 f->fmt.pix.pixelformat);
0443
0444 return 0;
0445 }
0446
0447
0448
0449 static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
0450 {
0451 struct pwc_device *pdev = video_drvdata(file);
0452 int ret, pixelformat, compression = 0;
0453
0454 ret = pwc_vidioc_try_fmt(pdev, f);
0455 if (ret < 0)
0456 return ret;
0457
0458 if (vb2_is_busy(&pdev->vb_queue))
0459 return -EBUSY;
0460
0461 pixelformat = f->fmt.pix.pixelformat;
0462
0463 PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d format=%c%c%c%c\n",
0464 f->fmt.pix.width, f->fmt.pix.height, pdev->vframes,
0465 (pixelformat)&255,
0466 (pixelformat>>8)&255,
0467 (pixelformat>>16)&255,
0468 (pixelformat>>24)&255);
0469
0470 ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height,
0471 pixelformat, 30, &compression, 0);
0472
0473 PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
0474
0475 pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
0476 return ret;
0477 }
0478
0479 static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
0480 {
0481 struct pwc_device *pdev = video_drvdata(file);
0482
0483 strscpy(cap->driver, PWC_NAME, sizeof(cap->driver));
0484 strscpy(cap->card, pdev->vdev.name, sizeof(cap->card));
0485 usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
0486 return 0;
0487 }
0488
0489 static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
0490 {
0491 if (i->index)
0492 return -EINVAL;
0493
0494 strscpy(i->name, "Camera", sizeof(i->name));
0495 i->type = V4L2_INPUT_TYPE_CAMERA;
0496 return 0;
0497 }
0498
0499 static int pwc_g_input(struct file *file, void *fh, unsigned int *i)
0500 {
0501 *i = 0;
0502 return 0;
0503 }
0504
0505 static int pwc_s_input(struct file *file, void *fh, unsigned int i)
0506 {
0507 return i ? -EINVAL : 0;
0508 }
0509
0510 static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
0511 {
0512 struct pwc_device *pdev =
0513 container_of(ctrl->handler, struct pwc_device, ctrl_handler);
0514 int ret = 0;
0515
0516 switch (ctrl->id) {
0517 case V4L2_CID_AUTO_WHITE_BALANCE:
0518 if (pdev->color_bal_valid &&
0519 (pdev->auto_white_balance->val != awb_auto ||
0520 time_before(jiffies,
0521 pdev->last_color_bal_update + HZ / 4))) {
0522 pdev->red_balance->val = pdev->last_red_balance;
0523 pdev->blue_balance->val = pdev->last_blue_balance;
0524 break;
0525 }
0526 ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
0527 READ_RED_GAIN_FORMATTER,
0528 &pdev->red_balance->val);
0529 if (ret)
0530 break;
0531 ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
0532 READ_BLUE_GAIN_FORMATTER,
0533 &pdev->blue_balance->val);
0534 if (ret)
0535 break;
0536 pdev->last_red_balance = pdev->red_balance->val;
0537 pdev->last_blue_balance = pdev->blue_balance->val;
0538 pdev->last_color_bal_update = jiffies;
0539 pdev->color_bal_valid = true;
0540 break;
0541 case V4L2_CID_AUTOGAIN:
0542 if (pdev->gain_valid && time_before(jiffies,
0543 pdev->last_gain_update + HZ / 4)) {
0544 pdev->gain->val = pdev->last_gain;
0545 break;
0546 }
0547 ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
0548 READ_AGC_FORMATTER, &pdev->gain->val);
0549 if (ret)
0550 break;
0551 pdev->last_gain = pdev->gain->val;
0552 pdev->last_gain_update = jiffies;
0553 pdev->gain_valid = true;
0554 if (!DEVICE_USE_CODEC3(pdev->type))
0555 break;
0556
0557 fallthrough;
0558 case V4L2_CID_EXPOSURE_AUTO:
0559 if (pdev->exposure_valid && time_before(jiffies,
0560 pdev->last_exposure_update + HZ / 4)) {
0561 pdev->exposure->val = pdev->last_exposure;
0562 break;
0563 }
0564 ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
0565 READ_SHUTTER_FORMATTER,
0566 &pdev->exposure->val);
0567 if (ret)
0568 break;
0569 pdev->last_exposure = pdev->exposure->val;
0570 pdev->last_exposure_update = jiffies;
0571 pdev->exposure_valid = true;
0572 break;
0573 default:
0574 ret = -EINVAL;
0575 }
0576
0577 if (ret)
0578 PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
0579
0580 return ret;
0581 }
0582
0583 static int pwc_set_awb(struct pwc_device *pdev)
0584 {
0585 int ret;
0586
0587 if (pdev->auto_white_balance->is_new) {
0588 ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
0589 WB_MODE_FORMATTER,
0590 pdev->auto_white_balance->val);
0591 if (ret)
0592 return ret;
0593
0594 if (pdev->auto_white_balance->val != awb_manual)
0595 pdev->color_bal_valid = false;
0596
0597
0598
0599
0600
0601 if (pdev->auto_white_balance->val == awb_indoor ||
0602 pdev->auto_white_balance->val == awb_outdoor ||
0603 pdev->auto_white_balance->val == awb_fl)
0604 pwc_g_volatile_ctrl(pdev->auto_white_balance);
0605 }
0606 if (pdev->auto_white_balance->val != awb_manual)
0607 return 0;
0608
0609 if (pdev->red_balance->is_new) {
0610 ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
0611 PRESET_MANUAL_RED_GAIN_FORMATTER,
0612 pdev->red_balance->val);
0613 if (ret)
0614 return ret;
0615 }
0616
0617 if (pdev->blue_balance->is_new) {
0618 ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
0619 PRESET_MANUAL_BLUE_GAIN_FORMATTER,
0620 pdev->blue_balance->val);
0621 if (ret)
0622 return ret;
0623 }
0624 return 0;
0625 }
0626
0627
0628 static int pwc_set_autogain(struct pwc_device *pdev)
0629 {
0630 int ret;
0631
0632 if (pdev->autogain->is_new) {
0633 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0634 AGC_MODE_FORMATTER,
0635 pdev->autogain->val ? 0 : 0xff);
0636 if (ret)
0637 return ret;
0638
0639 if (pdev->autogain->val)
0640 pdev->gain_valid = false;
0641 }
0642
0643 if (pdev->autogain->val)
0644 return 0;
0645
0646 if (pdev->gain->is_new) {
0647 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0648 PRESET_AGC_FORMATTER,
0649 pdev->gain->val);
0650 if (ret)
0651 return ret;
0652 }
0653 return 0;
0654 }
0655
0656
0657 static int pwc_set_exposure_auto(struct pwc_device *pdev)
0658 {
0659 int ret;
0660 int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO;
0661
0662 if (pdev->exposure_auto->is_new) {
0663 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0664 SHUTTER_MODE_FORMATTER,
0665 is_auto ? 0 : 0xff);
0666 if (ret)
0667 return ret;
0668
0669 if (is_auto)
0670 pdev->exposure_valid = false;
0671 }
0672
0673 if (is_auto)
0674 return 0;
0675
0676 if (pdev->exposure->is_new) {
0677 ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
0678 PRESET_SHUTTER_FORMATTER,
0679 pdev->exposure->val);
0680 if (ret)
0681 return ret;
0682 }
0683 return 0;
0684 }
0685
0686
0687 static int pwc_set_autogain_expo(struct pwc_device *pdev)
0688 {
0689 int ret;
0690
0691 if (pdev->autogain->is_new) {
0692 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0693 AGC_MODE_FORMATTER,
0694 pdev->autogain->val ? 0 : 0xff);
0695 if (ret)
0696 return ret;
0697
0698 if (pdev->autogain->val) {
0699 pdev->gain_valid = false;
0700 pdev->exposure_valid = false;
0701 }
0702 }
0703
0704 if (pdev->autogain->val)
0705 return 0;
0706
0707 if (pdev->gain->is_new) {
0708 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0709 PRESET_AGC_FORMATTER,
0710 pdev->gain->val);
0711 if (ret)
0712 return ret;
0713 }
0714
0715 if (pdev->exposure->is_new) {
0716 ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
0717 PRESET_SHUTTER_FORMATTER,
0718 pdev->exposure->val);
0719 if (ret)
0720 return ret;
0721 }
0722 return 0;
0723 }
0724
0725 static int pwc_set_motor(struct pwc_device *pdev)
0726 {
0727 int ret;
0728
0729 pdev->ctrl_buf[0] = 0;
0730 if (pdev->motor_pan_reset->is_new)
0731 pdev->ctrl_buf[0] |= 0x01;
0732 if (pdev->motor_tilt_reset->is_new)
0733 pdev->ctrl_buf[0] |= 0x02;
0734 if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) {
0735 ret = send_control_msg(pdev, SET_MPT_CTL,
0736 PT_RESET_CONTROL_FORMATTER,
0737 pdev->ctrl_buf, 1);
0738 if (ret < 0)
0739 return ret;
0740 }
0741
0742 memset(pdev->ctrl_buf, 0, 4);
0743 if (pdev->motor_pan->is_new) {
0744 pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF;
0745 pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8);
0746 }
0747 if (pdev->motor_tilt->is_new) {
0748 pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF;
0749 pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8);
0750 }
0751 if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) {
0752 ret = send_control_msg(pdev, SET_MPT_CTL,
0753 PT_RELATIVE_CONTROL_FORMATTER,
0754 pdev->ctrl_buf, 4);
0755 if (ret < 0)
0756 return ret;
0757 }
0758
0759 return 0;
0760 }
0761
0762 static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
0763 {
0764 struct pwc_device *pdev =
0765 container_of(ctrl->handler, struct pwc_device, ctrl_handler);
0766 int ret = 0;
0767
0768 switch (ctrl->id) {
0769 case V4L2_CID_BRIGHTNESS:
0770 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0771 BRIGHTNESS_FORMATTER, ctrl->val);
0772 break;
0773 case V4L2_CID_CONTRAST:
0774 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0775 CONTRAST_FORMATTER, ctrl->val);
0776 break;
0777 case V4L2_CID_SATURATION:
0778 ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL,
0779 pdev->saturation_fmt, ctrl->val);
0780 break;
0781 case V4L2_CID_GAMMA:
0782 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0783 GAMMA_FORMATTER, ctrl->val);
0784 break;
0785 case V4L2_CID_AUTO_WHITE_BALANCE:
0786 ret = pwc_set_awb(pdev);
0787 break;
0788 case V4L2_CID_AUTOGAIN:
0789 if (DEVICE_USE_CODEC2(pdev->type))
0790 ret = pwc_set_autogain(pdev);
0791 else if (DEVICE_USE_CODEC3(pdev->type))
0792 ret = pwc_set_autogain_expo(pdev);
0793 else
0794 ret = -EINVAL;
0795 break;
0796 case V4L2_CID_EXPOSURE_AUTO:
0797 if (DEVICE_USE_CODEC2(pdev->type))
0798 ret = pwc_set_exposure_auto(pdev);
0799 else
0800 ret = -EINVAL;
0801 break;
0802 case V4L2_CID_COLORFX:
0803 ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
0804 COLOUR_MODE_FORMATTER,
0805 ctrl->val ? 0 : 0xff);
0806 break;
0807 case PWC_CID_CUSTOM(autocontour):
0808 if (pdev->autocontour->is_new) {
0809 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0810 AUTO_CONTOUR_FORMATTER,
0811 pdev->autocontour->val ? 0 : 0xff);
0812 }
0813 if (ret == 0 && pdev->contour->is_new) {
0814 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0815 PRESET_CONTOUR_FORMATTER,
0816 pdev->contour->val);
0817 }
0818 break;
0819 case V4L2_CID_BACKLIGHT_COMPENSATION:
0820 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0821 BACK_LIGHT_COMPENSATION_FORMATTER,
0822 ctrl->val ? 0 : 0xff);
0823 break;
0824 case V4L2_CID_BAND_STOP_FILTER:
0825 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0826 FLICKERLESS_MODE_FORMATTER,
0827 ctrl->val ? 0 : 0xff);
0828 break;
0829 case PWC_CID_CUSTOM(noise_reduction):
0830 ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
0831 DYNAMIC_NOISE_CONTROL_FORMATTER,
0832 ctrl->val);
0833 break;
0834 case PWC_CID_CUSTOM(save_user):
0835 ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
0836 break;
0837 case PWC_CID_CUSTOM(restore_user):
0838 ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
0839 break;
0840 case PWC_CID_CUSTOM(restore_factory):
0841 ret = pwc_button_ctrl(pdev,
0842 RESTORE_FACTORY_DEFAULTS_FORMATTER);
0843 break;
0844 case PWC_CID_CUSTOM(awb_speed):
0845 ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
0846 AWB_CONTROL_SPEED_FORMATTER,
0847 ctrl->val);
0848 break;
0849 case PWC_CID_CUSTOM(awb_delay):
0850 ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
0851 AWB_CONTROL_DELAY_FORMATTER,
0852 ctrl->val);
0853 break;
0854 case V4L2_CID_PAN_RELATIVE:
0855 ret = pwc_set_motor(pdev);
0856 break;
0857 default:
0858 ret = -EINVAL;
0859 }
0860
0861 if (ret)
0862 PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
0863
0864 return ret;
0865 }
0866
0867 static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
0868 {
0869 struct pwc_device *pdev = video_drvdata(file);
0870
0871
0872 switch (f->index) {
0873 case 0:
0874
0875 f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2;
0876 break;
0877 case 1:
0878 f->pixelformat = V4L2_PIX_FMT_YUV420;
0879 break;
0880 default:
0881 return -EINVAL;
0882 }
0883 return 0;
0884 }
0885
0886 static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
0887 {
0888 struct pwc_device *pdev = video_drvdata(file);
0889
0890 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
0891 return -EINVAL;
0892
0893 PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
0894 pdev->width, pdev->height);
0895 pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
0896 return 0;
0897 }
0898
0899 static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
0900 {
0901 struct pwc_device *pdev = video_drvdata(file);
0902
0903 return pwc_vidioc_try_fmt(pdev, f);
0904 }
0905
0906 static int pwc_enum_framesizes(struct file *file, void *fh,
0907 struct v4l2_frmsizeenum *fsize)
0908 {
0909 struct pwc_device *pdev = video_drvdata(file);
0910 unsigned int i = 0, index = fsize->index;
0911
0912 if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 ||
0913 (fsize->pixel_format == V4L2_PIX_FMT_PWC1 &&
0914 DEVICE_USE_CODEC1(pdev->type)) ||
0915 (fsize->pixel_format == V4L2_PIX_FMT_PWC2 &&
0916 DEVICE_USE_CODEC23(pdev->type))) {
0917 for (i = 0; i < PSZ_MAX; i++) {
0918 if (!(pdev->image_mask & (1UL << i)))
0919 continue;
0920 if (!index--) {
0921 fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
0922 fsize->discrete.width = pwc_image_sizes[i][0];
0923 fsize->discrete.height = pwc_image_sizes[i][1];
0924 return 0;
0925 }
0926 }
0927 }
0928 return -EINVAL;
0929 }
0930
0931 static int pwc_enum_frameintervals(struct file *file, void *fh,
0932 struct v4l2_frmivalenum *fival)
0933 {
0934 struct pwc_device *pdev = video_drvdata(file);
0935 int size = -1;
0936 unsigned int i;
0937
0938 for (i = 0; i < PSZ_MAX; i++) {
0939 if (pwc_image_sizes[i][0] == fival->width &&
0940 pwc_image_sizes[i][1] == fival->height) {
0941 size = i;
0942 break;
0943 }
0944 }
0945
0946
0947 if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420)
0948 return -EINVAL;
0949
0950 i = pwc_get_fps(pdev, fival->index, size);
0951 if (!i)
0952 return -EINVAL;
0953
0954 fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
0955 fival->discrete.numerator = 1;
0956 fival->discrete.denominator = i;
0957
0958 return 0;
0959 }
0960
0961 static int pwc_g_parm(struct file *file, void *fh,
0962 struct v4l2_streamparm *parm)
0963 {
0964 struct pwc_device *pdev = video_drvdata(file);
0965
0966 if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
0967 return -EINVAL;
0968
0969 memset(parm, 0, sizeof(*parm));
0970
0971 parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0972 parm->parm.capture.readbuffers = MIN_FRAMES;
0973 parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
0974 parm->parm.capture.timeperframe.denominator = pdev->vframes;
0975 parm->parm.capture.timeperframe.numerator = 1;
0976
0977 return 0;
0978 }
0979
0980 static int pwc_s_parm(struct file *file, void *fh,
0981 struct v4l2_streamparm *parm)
0982 {
0983 struct pwc_device *pdev = video_drvdata(file);
0984 int compression = 0;
0985 int ret, fps;
0986
0987 if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
0988 return -EINVAL;
0989
0990
0991
0992
0993 if (parm->parm.capture.timeperframe.numerator == 0 ||
0994 parm->parm.capture.timeperframe.denominator == 0)
0995 fps = 30;
0996 else
0997 fps = parm->parm.capture.timeperframe.denominator /
0998 parm->parm.capture.timeperframe.numerator;
0999
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
1011 const 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 };