Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* Linux driver for Philips webcam
0003    USB and Video4Linux interface part.
0004    (C) 1999-2004 Nemosoft Unv.
0005    (C) 2004-2006 Luc Saillard (luc@saillard.org)
0006    (C) 2011 Hans de Goede <hdegoede@redhat.com>
0007 
0008    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
0009    driver and thus may have bugs that are not present in the original version.
0010    Please send bug reports and support requests to <luc@saillard.org>.
0011    The decompression routines have been implemented by reverse-engineering the
0012    Nemosoft binary pwcx module. Caveat emptor.
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     /* Brightness, contrast, saturation, gamma */
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     /* auto white balance, red gain, blue gain */
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     /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */
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     /* autogain, gain */
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     /* Note a register value if 0 means auto gain is on */
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     /* auto exposure, exposure */
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          * def = 0 auto, def = ff manual
0245          * menu idx 0 = auto, idx 1 = manual
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         /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
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         /* CODEC2: separate auto gain & auto exposure */
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         /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
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         /* CODEC3: both gain and exposure controlled by autogain */
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     /* color / bw setting */
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     /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */
0287     pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops,
0288                 V4L2_CID_COLORFX, 1, 0, def == 0);
0289 
0290     /* autocontour, contour */
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     /* backlight */
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     /* flikker rediction */
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     /* Dynamic noise reduction */
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     /* Save / Restore User / Factory Settings */
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     /* Auto White Balance speed & delay */
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     /* Motor pan / tilt / reset */
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 /* ioctl(VIDIOC_TRY_FMT) */
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 /* ioctl(VIDIOC_SET_FMT) */
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)   /* Only one INPUT is supported */
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         /* For CODEC3 where autogain also controls expo */
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; /* Force cache update */
0596 
0597         /*
0598          * If this is a preset, update our red / blue balance values
0599          * so that events get generated for the new preset values
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 /* For CODEC2 models which have separate autogain and auto exposure */
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; /* Force cache update */
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 /* For CODEC2 models which have separate autogain and auto exposure */
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; /* Force cache update */
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 /* For CODEC3 models which have autogain controlling both gain and exposure */
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; /* Force cache update */
0700             pdev->exposure_valid = false; /* Force cache update */
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     /* We only support two format: the raw format, and YUV */
0872     switch (f->index) {
0873     case 0:
0874         /* RAW format */
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     /* TODO: Support raw format */
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     /* If timeperframe == 0, then reset the framerate to the nominal value.
0991        We pick a high framerate here, and let pwc_set_video_mode() figure
0992        out the best match. */
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 };