/* initialize variables depending on type and decompressor */ void pwc_construct(struct pwc_device *pdev) { if (DEVICE_USE_CODEC1(pdev->type)) { pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; pdev->vcinterface = 2; pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; } else if (DEVICE_USE_CODEC3(pdev->type)) { pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; pdev->vcinterface = 3; pdev->vendpoint = 5; pdev->frame_header_size = TOUCAM_HEADER_SIZE; pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; } else { /* if (DEVICE_USE_CODEC2(pdev->type)) */ pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; pdev->vcinterface = 3; pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; } }
int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int pixfmt, int frames, int *compression, int send_to_cam) { int ret, size; PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pixfmt); size = pwc_get_size(pdev, width, height); PWC_TRACE("decode_size = %d.\n", size); if (DEVICE_USE_CODEC1(pdev->type)) { ret = set_video_mode_Nala(pdev, size, pixfmt, frames, compression, send_to_cam); } else if (DEVICE_USE_CODEC3(pdev->type)) { ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, compression, send_to_cam); } else { ret = set_video_mode_Timon(pdev, size, pixfmt, frames, compression, send_to_cam); } if (ret < 0) { PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); return ret; } pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); return 0; }
/* initialize variables depending on type and decompressor*/ void pwc_construct(struct pwc_device *pdev) { if (DEVICE_USE_CODEC1(pdev->type)) { pdev->view_min.x = 128; pdev->view_min.y = 96; pdev->view_max.x = 352; pdev->view_max.y = 288; pdev->abs_max.x = 352; pdev->abs_max.y = 288; pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; pdev->vcinterface = 2; pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; } else if (DEVICE_USE_CODEC3(pdev->type)) { pdev->view_min.x = 160; pdev->view_min.y = 120; pdev->view_max.x = 640; pdev->view_max.y = 480; pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; pdev->abs_max.x = 640; pdev->abs_max.y = 480; pdev->vcinterface = 3; pdev->vendpoint = 5; pdev->frame_header_size = TOUCAM_HEADER_SIZE; pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ { pdev->view_min.x = 128; pdev->view_min.y = 96; /* Anthill bug #38: PWC always reports max size, even without PWCX */ pdev->view_max.x = 640; pdev->view_max.y = 480; pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; pdev->abs_max.x = 640; pdev->abs_max.y = 480; pdev->vcinterface = 3; pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; } pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */ pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; /* length of image, in YUV format; always allocate enough memory. */ pdev->len_per_image = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2); }
unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) { unsigned int ret; if (DEVICE_USE_CODEC1(pdev->type)) { ret = pwc_get_fps_Nala(pdev, index, size); } else if (DEVICE_USE_CODEC3(pdev->type)) { ret = pwc_get_fps_Kiara(pdev, index, size); } else { ret = pwc_get_fps_Timon(pdev, index, size); } return ret; }
static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct pwc_device *pdev = container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (pdev->color_bal_valid && (pdev->auto_white_balance->val != awb_auto || time_before(jiffies, pdev->last_color_bal_update + HZ / 4))) { pdev->red_balance->val = pdev->last_red_balance; pdev->blue_balance->val = pdev->last_blue_balance; break; } ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, &pdev->red_balance->val); if (ret) break; ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, &pdev->blue_balance->val); if (ret) break; pdev->last_red_balance = pdev->red_balance->val; pdev->last_blue_balance = pdev->blue_balance->val; pdev->last_color_bal_update = jiffies; pdev->color_bal_valid = true; break; case V4L2_CID_AUTOGAIN: if (pdev->gain_valid && time_before(jiffies, pdev->last_gain_update + HZ / 4)) { pdev->gain->val = pdev->last_gain; break; } ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, READ_AGC_FORMATTER, &pdev->gain->val); if (ret) break; pdev->last_gain = pdev->gain->val; pdev->last_gain_update = jiffies; pdev->gain_valid = true; if (!DEVICE_USE_CODEC3(pdev->type)) break; /* Fall through for CODEC3 where autogain also controls expo */ case V4L2_CID_EXPOSURE_AUTO: if (pdev->exposure_valid && time_before(jiffies, pdev->last_exposure_update + HZ / 4)) { pdev->exposure->val = pdev->last_exposure; break; } ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &pdev->exposure->val); if (ret) break; pdev->last_exposure = pdev->exposure->val; pdev->last_exposure_update = jiffies; pdev->exposure_valid = true; break; default: ret = -EINVAL; } if (ret) PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); return ret; }
int pwc_init_controls(struct pwc_device *pdev) { struct v4l2_ctrl_handler *hdl; struct v4l2_ctrl_config cfg; int r, def; hdl = &pdev->ctrl_handler; r = v4l2_ctrl_handler_init(hdl, 20); if (r) return r; /* Brightness, contrast, saturation, gamma */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def); if (r || def > 127) def = 63; pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 127, 1, def); r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def); if (r || def > 63) def = 31; pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_CONTRAST, 0, 63, 1, def); if (pdev->type >= 675) { if (pdev->type < 730) pdev->saturation_fmt = SATURATION_MODE_FORMATTER2; else pdev->saturation_fmt = SATURATION_MODE_FORMATTER1; r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt, &def); if (r || def < -100 || def > 100) def = 0; pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_SATURATION, -100, 100, 1, def); } r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def); if (r || def > 31) def = 15; pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_GAMMA, 0, 31, 1, def); /* auto white balance, red gain, blue gain */ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def); if (r || def > awb_auto) def = awb_auto; cfg = pwc_auto_white_balance_cfg; cfg.name = v4l2_ctrl_get_name(cfg.id); cfg.def = def; pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL); /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */ if (!pdev->auto_white_balance) return hdl->error; r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, &def); if (r) def = 127; pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_RED_BALANCE, 0, 255, 1, def); r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def); if (r) def = 127; pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_BLUE_BALANCE, 0, 255, 1, def); v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true); /* autogain, gain */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def); if (r || (def != 0 && def != 0xff)) def = 0; /* Note a register value if 0 means auto gain is on */ pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0); if (!pdev->autogain) return hdl->error; r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def); if (r || def > 63) def = 31; pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_GAIN, 0, 63, 1, def); /* auto exposure, exposure */ if (DEVICE_USE_CODEC2(pdev->type)) { r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER, &def); if (r || (def != 0 && def != 0xff)) def = 0; /* * def = 0 auto, def = ff manual * menu idx 0 = auto, idx 1 = manual */ pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, def != 0); if (!pdev->exposure_auto) return hdl->error; /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &def); if (r || def > 655) def = 655; pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_EXPOSURE, 0, 655, 1, def); /* CODEC2: separate auto gain & auto exposure */ v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true); v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto, V4L2_EXPOSURE_MANUAL, true); } else if (DEVICE_USE_CODEC3(pdev->type)) { /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &def); if (r || def > 255) def = 255; pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_EXPOSURE, 0, 255, 1, def); /* CODEC3: both gain and exposure controlled by autogain */ pdev->autogain_expo_cluster[0] = pdev->autogain; pdev->autogain_expo_cluster[1] = pdev->gain; pdev->autogain_expo_cluster[2] = pdev->exposure; v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster, 0, true); } /* color / bw setting */ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER, &def); if (r || (def != 0 && def != 0xff)) def = 0xff; /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */ pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops, V4L2_CID_COLORFX, 1, 0, def == 0); /* autocontour, contour */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def); if (r || (def != 0 && def != 0xff)) def = 0; cfg = pwc_autocontour_cfg; cfg.def = def == 0; pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); if (!pdev->autocontour) return hdl->error; r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def); if (r || def > 63) def = 31; cfg = pwc_contour_cfg; cfg.def = def; pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false); /* backlight */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, &def); if (r || (def != 0 && def != 0xff)) def = 0; cfg = pwc_backlight_cfg; cfg.name = v4l2_ctrl_get_name(cfg.id); cfg.def = def == 0; pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL); /* flikker rediction */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &def); if (r || (def != 0 && def != 0xff)) def = 0; cfg = pwc_flicker_cfg; cfg.name = v4l2_ctrl_get_name(cfg.id); cfg.def = def == 0; pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL); /* Dynamic noise reduction */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, &def); if (r || def > 3) def = 2; cfg = pwc_noise_reduction_cfg; cfg.def = def; pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL); /* Save / Restore User / Factory Settings */ pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL); pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg, NULL); if (pdev->restore_user) pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE; pdev->restore_factory = v4l2_ctrl_new_custom(hdl, &pwc_restore_factory_cfg, NULL); if (pdev->restore_factory) pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE; /* Auto White Balance speed & delay */ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &def); if (r || def < 1 || def > 32) def = 1; cfg = pwc_awb_speed_cfg; cfg.def = def; pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL); r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &def); if (r || def > 63) def = 0; cfg = pwc_awb_delay_cfg; cfg.def = def; pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL); if (!(pdev->features & FEATURE_MOTOR_PANTILT)) return hdl->error; /* Motor pan / tilt / reset */ pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0); if (!pdev->motor_pan) return hdl->error; pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0); pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_PAN_RESET, 0, 0, 0, 0); pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_TILT_RESET, 0, 0, 0, 0); v4l2_ctrl_cluster(4, &pdev->motor_pan); return hdl->error; }