18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Linux driver for Philips webcam 38c2ecf20Sopenharmony_ci USB and Video4Linux interface part. 48c2ecf20Sopenharmony_ci (C) 1999-2004 Nemosoft Unv. 58c2ecf20Sopenharmony_ci (C) 2004-2006 Luc Saillard (luc@saillard.org) 68c2ecf20Sopenharmony_ci (C) 2011 Hans de Goede <hdegoede@redhat.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx 98c2ecf20Sopenharmony_ci driver and thus may have bugs that are not present in the original version. 108c2ecf20Sopenharmony_ci Please send bug reports and support requests to <luc@saillard.org>. 118c2ecf20Sopenharmony_ci The decompression routines have been implemented by reverse-engineering the 128c2ecf20Sopenharmony_ci Nemosoft binary pwcx module. Caveat emptor. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci*/ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/poll.h> 228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 238c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 248c2ecf20Sopenharmony_ci#include <asm/io.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "pwc.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl); 318c2ecf20Sopenharmony_cistatic int pwc_s_ctrl(struct v4l2_ctrl *ctrl); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops pwc_ctrl_ops = { 348c2ecf20Sopenharmony_ci .g_volatile_ctrl = pwc_g_volatile_ctrl, 358c2ecf20Sopenharmony_ci .s_ctrl = pwc_s_ctrl, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cienum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto }; 398c2ecf20Sopenharmony_cienum { custom_autocontour, custom_contour, custom_noise_reduction, 408c2ecf20Sopenharmony_ci custom_awb_speed, custom_awb_delay, 418c2ecf20Sopenharmony_ci custom_save_user, custom_restore_user, custom_restore_factory }; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const char * const pwc_auto_whitebal_qmenu[] = { 448c2ecf20Sopenharmony_ci "Indoor (Incandescant Lighting) Mode", 458c2ecf20Sopenharmony_ci "Outdoor (Sunlight) Mode", 468c2ecf20Sopenharmony_ci "Indoor (Fluorescent Lighting) Mode", 478c2ecf20Sopenharmony_ci "Manual Mode", 488c2ecf20Sopenharmony_ci "Auto Mode", 498c2ecf20Sopenharmony_ci NULL 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = { 538c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 548c2ecf20Sopenharmony_ci .id = V4L2_CID_AUTO_WHITE_BALANCE, 558c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_MENU, 568c2ecf20Sopenharmony_ci .max = awb_auto, 578c2ecf20Sopenharmony_ci .qmenu = pwc_auto_whitebal_qmenu, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_autocontour_cfg = { 618c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 628c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(autocontour), 638c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 648c2ecf20Sopenharmony_ci .name = "Auto contour", 658c2ecf20Sopenharmony_ci .min = 0, 668c2ecf20Sopenharmony_ci .max = 1, 678c2ecf20Sopenharmony_ci .step = 1, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_contour_cfg = { 718c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 728c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(contour), 738c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 748c2ecf20Sopenharmony_ci .name = "Contour", 758c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 768c2ecf20Sopenharmony_ci .min = 0, 778c2ecf20Sopenharmony_ci .max = 63, 788c2ecf20Sopenharmony_ci .step = 1, 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_backlight_cfg = { 828c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 838c2ecf20Sopenharmony_ci .id = V4L2_CID_BACKLIGHT_COMPENSATION, 848c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 858c2ecf20Sopenharmony_ci .min = 0, 868c2ecf20Sopenharmony_ci .max = 1, 878c2ecf20Sopenharmony_ci .step = 1, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_flicker_cfg = { 918c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 928c2ecf20Sopenharmony_ci .id = V4L2_CID_BAND_STOP_FILTER, 938c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 948c2ecf20Sopenharmony_ci .min = 0, 958c2ecf20Sopenharmony_ci .max = 1, 968c2ecf20Sopenharmony_ci .step = 1, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_noise_reduction_cfg = { 1008c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 1018c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(noise_reduction), 1028c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 1038c2ecf20Sopenharmony_ci .name = "Dynamic Noise Reduction", 1048c2ecf20Sopenharmony_ci .min = 0, 1058c2ecf20Sopenharmony_ci .max = 3, 1068c2ecf20Sopenharmony_ci .step = 1, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_save_user_cfg = { 1108c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 1118c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(save_user), 1128c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BUTTON, 1138c2ecf20Sopenharmony_ci .name = "Save User Settings", 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_restore_user_cfg = { 1178c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 1188c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(restore_user), 1198c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BUTTON, 1208c2ecf20Sopenharmony_ci .name = "Restore User Settings", 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_restore_factory_cfg = { 1248c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 1258c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(restore_factory), 1268c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BUTTON, 1278c2ecf20Sopenharmony_ci .name = "Restore Factory Settings", 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_awb_speed_cfg = { 1318c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 1328c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(awb_speed), 1338c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 1348c2ecf20Sopenharmony_ci .name = "Auto White Balance Speed", 1358c2ecf20Sopenharmony_ci .min = 1, 1368c2ecf20Sopenharmony_ci .max = 32, 1378c2ecf20Sopenharmony_ci .step = 1, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config pwc_awb_delay_cfg = { 1418c2ecf20Sopenharmony_ci .ops = &pwc_ctrl_ops, 1428c2ecf20Sopenharmony_ci .id = PWC_CID_CUSTOM(awb_delay), 1438c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 1448c2ecf20Sopenharmony_ci .name = "Auto White Balance Delay", 1458c2ecf20Sopenharmony_ci .min = 0, 1468c2ecf20Sopenharmony_ci .max = 63, 1478c2ecf20Sopenharmony_ci .step = 1, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciint pwc_init_controls(struct pwc_device *pdev) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 1538c2ecf20Sopenharmony_ci struct v4l2_ctrl_config cfg; 1548c2ecf20Sopenharmony_ci int r, def; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci hdl = &pdev->ctrl_handler; 1578c2ecf20Sopenharmony_ci r = v4l2_ctrl_handler_init(hdl, 20); 1588c2ecf20Sopenharmony_ci if (r) 1598c2ecf20Sopenharmony_ci return r; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Brightness, contrast, saturation, gamma */ 1628c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def); 1638c2ecf20Sopenharmony_ci if (r || def > 127) 1648c2ecf20Sopenharmony_ci def = 63; 1658c2ecf20Sopenharmony_ci pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 1668c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 127, 1, def); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def); 1698c2ecf20Sopenharmony_ci if (r || def > 63) 1708c2ecf20Sopenharmony_ci def = 31; 1718c2ecf20Sopenharmony_ci pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 1728c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 0, 63, 1, def); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (pdev->type >= 675) { 1758c2ecf20Sopenharmony_ci if (pdev->type < 730) 1768c2ecf20Sopenharmony_ci pdev->saturation_fmt = SATURATION_MODE_FORMATTER2; 1778c2ecf20Sopenharmony_ci else 1788c2ecf20Sopenharmony_ci pdev->saturation_fmt = SATURATION_MODE_FORMATTER1; 1798c2ecf20Sopenharmony_ci r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt, 1808c2ecf20Sopenharmony_ci &def); 1818c2ecf20Sopenharmony_ci if (r || def < -100 || def > 100) 1828c2ecf20Sopenharmony_ci def = 0; 1838c2ecf20Sopenharmony_ci pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 1848c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, -100, 100, 1, def); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def); 1888c2ecf20Sopenharmony_ci if (r || def > 31) 1898c2ecf20Sopenharmony_ci def = 15; 1908c2ecf20Sopenharmony_ci pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 1918c2ecf20Sopenharmony_ci V4L2_CID_GAMMA, 0, 31, 1, def); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* auto white balance, red gain, blue gain */ 1948c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def); 1958c2ecf20Sopenharmony_ci if (r || def > awb_auto) 1968c2ecf20Sopenharmony_ci def = awb_auto; 1978c2ecf20Sopenharmony_ci cfg = pwc_auto_white_balance_cfg; 1988c2ecf20Sopenharmony_ci cfg.name = v4l2_ctrl_get_name(cfg.id); 1998c2ecf20Sopenharmony_ci cfg.def = def; 2008c2ecf20Sopenharmony_ci pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 2018c2ecf20Sopenharmony_ci /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */ 2028c2ecf20Sopenharmony_ci if (!pdev->auto_white_balance) 2038c2ecf20Sopenharmony_ci return hdl->error; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, 2068c2ecf20Sopenharmony_ci PRESET_MANUAL_RED_GAIN_FORMATTER, &def); 2078c2ecf20Sopenharmony_ci if (r) 2088c2ecf20Sopenharmony_ci def = 127; 2098c2ecf20Sopenharmony_ci pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 2108c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 255, 1, def); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, 2138c2ecf20Sopenharmony_ci PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def); 2148c2ecf20Sopenharmony_ci if (r) 2158c2ecf20Sopenharmony_ci def = 127; 2168c2ecf20Sopenharmony_ci pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 2178c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 255, 1, def); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* autogain, gain */ 2228c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def); 2238c2ecf20Sopenharmony_ci if (r || (def != 0 && def != 0xff)) 2248c2ecf20Sopenharmony_ci def = 0; 2258c2ecf20Sopenharmony_ci /* Note a register value if 0 means auto gain is on */ 2268c2ecf20Sopenharmony_ci pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 2278c2ecf20Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0); 2288c2ecf20Sopenharmony_ci if (!pdev->autogain) 2298c2ecf20Sopenharmony_ci return hdl->error; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def); 2328c2ecf20Sopenharmony_ci if (r || def > 63) 2338c2ecf20Sopenharmony_ci def = 31; 2348c2ecf20Sopenharmony_ci pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 2358c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 63, 1, def); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* auto exposure, exposure */ 2388c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC2(pdev->type)) { 2398c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER, 2408c2ecf20Sopenharmony_ci &def); 2418c2ecf20Sopenharmony_ci if (r || (def != 0 && def != 0xff)) 2428c2ecf20Sopenharmony_ci def = 0; 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * def = 0 auto, def = ff manual 2458c2ecf20Sopenharmony_ci * menu idx 0 = auto, idx 1 = manual 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl, 2488c2ecf20Sopenharmony_ci &pwc_ctrl_ops, 2498c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 2508c2ecf20Sopenharmony_ci 1, 0, def != 0); 2518c2ecf20Sopenharmony_ci if (!pdev->exposure_auto) 2528c2ecf20Sopenharmony_ci return hdl->error; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ 2558c2ecf20Sopenharmony_ci r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, 2568c2ecf20Sopenharmony_ci READ_SHUTTER_FORMATTER, &def); 2578c2ecf20Sopenharmony_ci if (r || def > 655) 2588c2ecf20Sopenharmony_ci def = 655; 2598c2ecf20Sopenharmony_ci pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 2608c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 655, 1, def); 2618c2ecf20Sopenharmony_ci /* CODEC2: separate auto gain & auto exposure */ 2628c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true); 2638c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto, 2648c2ecf20Sopenharmony_ci V4L2_EXPOSURE_MANUAL, true); 2658c2ecf20Sopenharmony_ci } else if (DEVICE_USE_CODEC3(pdev->type)) { 2668c2ecf20Sopenharmony_ci /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ 2678c2ecf20Sopenharmony_ci r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, 2688c2ecf20Sopenharmony_ci READ_SHUTTER_FORMATTER, &def); 2698c2ecf20Sopenharmony_ci if (r || def > 255) 2708c2ecf20Sopenharmony_ci def = 255; 2718c2ecf20Sopenharmony_ci pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 2728c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 255, 1, def); 2738c2ecf20Sopenharmony_ci /* CODEC3: both gain and exposure controlled by autogain */ 2748c2ecf20Sopenharmony_ci pdev->autogain_expo_cluster[0] = pdev->autogain; 2758c2ecf20Sopenharmony_ci pdev->autogain_expo_cluster[1] = pdev->gain; 2768c2ecf20Sopenharmony_ci pdev->autogain_expo_cluster[2] = pdev->exposure; 2778c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster, 2788c2ecf20Sopenharmony_ci 0, true); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* color / bw setting */ 2828c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER, 2838c2ecf20Sopenharmony_ci &def); 2848c2ecf20Sopenharmony_ci if (r || (def != 0 && def != 0xff)) 2858c2ecf20Sopenharmony_ci def = 0xff; 2868c2ecf20Sopenharmony_ci /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */ 2878c2ecf20Sopenharmony_ci pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops, 2888c2ecf20Sopenharmony_ci V4L2_CID_COLORFX, 1, 0, def == 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* autocontour, contour */ 2918c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def); 2928c2ecf20Sopenharmony_ci if (r || (def != 0 && def != 0xff)) 2938c2ecf20Sopenharmony_ci def = 0; 2948c2ecf20Sopenharmony_ci cfg = pwc_autocontour_cfg; 2958c2ecf20Sopenharmony_ci cfg.def = def == 0; 2968c2ecf20Sopenharmony_ci pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 2978c2ecf20Sopenharmony_ci if (!pdev->autocontour) 2988c2ecf20Sopenharmony_ci return hdl->error; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def); 3018c2ecf20Sopenharmony_ci if (r || def > 63) 3028c2ecf20Sopenharmony_ci def = 31; 3038c2ecf20Sopenharmony_ci cfg = pwc_contour_cfg; 3048c2ecf20Sopenharmony_ci cfg.def = def; 3058c2ecf20Sopenharmony_ci pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* backlight */ 3108c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, 3118c2ecf20Sopenharmony_ci BACK_LIGHT_COMPENSATION_FORMATTER, &def); 3128c2ecf20Sopenharmony_ci if (r || (def != 0 && def != 0xff)) 3138c2ecf20Sopenharmony_ci def = 0; 3148c2ecf20Sopenharmony_ci cfg = pwc_backlight_cfg; 3158c2ecf20Sopenharmony_ci cfg.name = v4l2_ctrl_get_name(cfg.id); 3168c2ecf20Sopenharmony_ci cfg.def = def == 0; 3178c2ecf20Sopenharmony_ci pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* flikker rediction */ 3208c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, 3218c2ecf20Sopenharmony_ci FLICKERLESS_MODE_FORMATTER, &def); 3228c2ecf20Sopenharmony_ci if (r || (def != 0 && def != 0xff)) 3238c2ecf20Sopenharmony_ci def = 0; 3248c2ecf20Sopenharmony_ci cfg = pwc_flicker_cfg; 3258c2ecf20Sopenharmony_ci cfg.name = v4l2_ctrl_get_name(cfg.id); 3268c2ecf20Sopenharmony_ci cfg.def = def == 0; 3278c2ecf20Sopenharmony_ci pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Dynamic noise reduction */ 3308c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, 3318c2ecf20Sopenharmony_ci DYNAMIC_NOISE_CONTROL_FORMATTER, &def); 3328c2ecf20Sopenharmony_ci if (r || def > 3) 3338c2ecf20Sopenharmony_ci def = 2; 3348c2ecf20Sopenharmony_ci cfg = pwc_noise_reduction_cfg; 3358c2ecf20Sopenharmony_ci cfg.def = def; 3368c2ecf20Sopenharmony_ci pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Save / Restore User / Factory Settings */ 3398c2ecf20Sopenharmony_ci pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL); 3408c2ecf20Sopenharmony_ci pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg, 3418c2ecf20Sopenharmony_ci NULL); 3428c2ecf20Sopenharmony_ci if (pdev->restore_user) 3438c2ecf20Sopenharmony_ci pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE; 3448c2ecf20Sopenharmony_ci pdev->restore_factory = v4l2_ctrl_new_custom(hdl, 3458c2ecf20Sopenharmony_ci &pwc_restore_factory_cfg, 3468c2ecf20Sopenharmony_ci NULL); 3478c2ecf20Sopenharmony_ci if (pdev->restore_factory) 3488c2ecf20Sopenharmony_ci pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Auto White Balance speed & delay */ 3518c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, 3528c2ecf20Sopenharmony_ci AWB_CONTROL_SPEED_FORMATTER, &def); 3538c2ecf20Sopenharmony_ci if (r || def < 1 || def > 32) 3548c2ecf20Sopenharmony_ci def = 1; 3558c2ecf20Sopenharmony_ci cfg = pwc_awb_speed_cfg; 3568c2ecf20Sopenharmony_ci cfg.def = def; 3578c2ecf20Sopenharmony_ci pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, 3608c2ecf20Sopenharmony_ci AWB_CONTROL_DELAY_FORMATTER, &def); 3618c2ecf20Sopenharmony_ci if (r || def > 63) 3628c2ecf20Sopenharmony_ci def = 0; 3638c2ecf20Sopenharmony_ci cfg = pwc_awb_delay_cfg; 3648c2ecf20Sopenharmony_ci cfg.def = def; 3658c2ecf20Sopenharmony_ci pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!(pdev->features & FEATURE_MOTOR_PANTILT)) 3688c2ecf20Sopenharmony_ci return hdl->error; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Motor pan / tilt / reset */ 3718c2ecf20Sopenharmony_ci pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 3728c2ecf20Sopenharmony_ci V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0); 3738c2ecf20Sopenharmony_ci if (!pdev->motor_pan) 3748c2ecf20Sopenharmony_ci return hdl->error; 3758c2ecf20Sopenharmony_ci pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 3768c2ecf20Sopenharmony_ci V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0); 3778c2ecf20Sopenharmony_ci pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 3788c2ecf20Sopenharmony_ci V4L2_CID_PAN_RESET, 0, 0, 0, 0); 3798c2ecf20Sopenharmony_ci pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, 3808c2ecf20Sopenharmony_ci V4L2_CID_TILT_RESET, 0, 0, 0, 0); 3818c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(4, &pdev->motor_pan); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return hdl->error; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void pwc_vidioc_fill_fmt(struct v4l2_format *f, 3878c2ecf20Sopenharmony_ci int width, int height, u32 pixfmt) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); 3908c2ecf20Sopenharmony_ci f->fmt.pix.width = width; 3918c2ecf20Sopenharmony_ci f->fmt.pix.height = height; 3928c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 3938c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = pixfmt; 3948c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = f->fmt.pix.width; 3958c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2; 3968c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; 3978c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", 3988c2ecf20Sopenharmony_ci f->fmt.pix.width, 3998c2ecf20Sopenharmony_ci f->fmt.pix.height, 4008c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline, 4018c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage, 4028c2ecf20Sopenharmony_ci (f->fmt.pix.pixelformat)&255, 4038c2ecf20Sopenharmony_ci (f->fmt.pix.pixelformat>>8)&255, 4048c2ecf20Sopenharmony_ci (f->fmt.pix.pixelformat>>16)&255, 4058c2ecf20Sopenharmony_ci (f->fmt.pix.pixelformat>>24)&255); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* ioctl(VIDIOC_TRY_FMT) */ 4098c2ecf20Sopenharmony_cistatic int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci int size; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { 4148c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); 4158c2ecf20Sopenharmony_ci return -EINVAL; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci switch (f->fmt.pix.pixelformat) { 4198c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_PWC1: 4228c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC23(pdev->type)) { 4238c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n"); 4248c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_PWC2: 4288c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC1(pdev->type)) { 4298c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n"); 4308c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("Unsupported pixel format\n"); 4358c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height); 4398c2ecf20Sopenharmony_ci pwc_vidioc_fill_fmt(f, 4408c2ecf20Sopenharmony_ci pwc_image_sizes[size][0], 4418c2ecf20Sopenharmony_ci pwc_image_sizes[size][1], 4428c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* ioctl(VIDIOC_SET_FMT) */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 4528c2ecf20Sopenharmony_ci int ret, pixelformat, compression = 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci ret = pwc_vidioc_try_fmt(pdev, f); 4558c2ecf20Sopenharmony_ci if (ret < 0) 4568c2ecf20Sopenharmony_ci return ret; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (vb2_is_busy(&pdev->vb_queue)) 4598c2ecf20Sopenharmony_ci return -EBUSY; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci pixelformat = f->fmt.pix.pixelformat; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d format=%c%c%c%c\n", 4648c2ecf20Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, 4658c2ecf20Sopenharmony_ci (pixelformat)&255, 4668c2ecf20Sopenharmony_ci (pixelformat>>8)&255, 4678c2ecf20Sopenharmony_ci (pixelformat>>16)&255, 4688c2ecf20Sopenharmony_ci (pixelformat>>24)&255); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, 4718c2ecf20Sopenharmony_ci pixelformat, 30, &compression, 0); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci strscpy(cap->driver, PWC_NAME, sizeof(cap->driver)); 4848c2ecf20Sopenharmony_ci strscpy(cap->card, pdev->vdev.name, sizeof(cap->card)); 4858c2ecf20Sopenharmony_ci usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci if (i->index) /* Only one INPUT is supported */ 4928c2ecf20Sopenharmony_ci return -EINVAL; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci strscpy(i->name, "Camera", sizeof(i->name)); 4958c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int pwc_g_input(struct file *file, void *fh, unsigned int *i) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci *i = 0; 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int pwc_s_input(struct file *file, void *fh, unsigned int i) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci return i ? -EINVAL : 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct pwc_device *pdev = 5138c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct pwc_device, ctrl_handler); 5148c2ecf20Sopenharmony_ci int ret = 0; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci switch (ctrl->id) { 5178c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 5188c2ecf20Sopenharmony_ci if (pdev->color_bal_valid && 5198c2ecf20Sopenharmony_ci (pdev->auto_white_balance->val != awb_auto || 5208c2ecf20Sopenharmony_ci time_before(jiffies, 5218c2ecf20Sopenharmony_ci pdev->last_color_bal_update + HZ / 4))) { 5228c2ecf20Sopenharmony_ci pdev->red_balance->val = pdev->last_red_balance; 5238c2ecf20Sopenharmony_ci pdev->blue_balance->val = pdev->last_blue_balance; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, 5278c2ecf20Sopenharmony_ci READ_RED_GAIN_FORMATTER, 5288c2ecf20Sopenharmony_ci &pdev->red_balance->val); 5298c2ecf20Sopenharmony_ci if (ret) 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, 5328c2ecf20Sopenharmony_ci READ_BLUE_GAIN_FORMATTER, 5338c2ecf20Sopenharmony_ci &pdev->blue_balance->val); 5348c2ecf20Sopenharmony_ci if (ret) 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci pdev->last_red_balance = pdev->red_balance->val; 5378c2ecf20Sopenharmony_ci pdev->last_blue_balance = pdev->blue_balance->val; 5388c2ecf20Sopenharmony_ci pdev->last_color_bal_update = jiffies; 5398c2ecf20Sopenharmony_ci pdev->color_bal_valid = true; 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 5428c2ecf20Sopenharmony_ci if (pdev->gain_valid && time_before(jiffies, 5438c2ecf20Sopenharmony_ci pdev->last_gain_update + HZ / 4)) { 5448c2ecf20Sopenharmony_ci pdev->gain->val = pdev->last_gain; 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, 5488c2ecf20Sopenharmony_ci READ_AGC_FORMATTER, &pdev->gain->val); 5498c2ecf20Sopenharmony_ci if (ret) 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci pdev->last_gain = pdev->gain->val; 5528c2ecf20Sopenharmony_ci pdev->last_gain_update = jiffies; 5538c2ecf20Sopenharmony_ci pdev->gain_valid = true; 5548c2ecf20Sopenharmony_ci if (!DEVICE_USE_CODEC3(pdev->type)) 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci /* For CODEC3 where autogain also controls expo */ 5578c2ecf20Sopenharmony_ci fallthrough; 5588c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 5598c2ecf20Sopenharmony_ci if (pdev->exposure_valid && time_before(jiffies, 5608c2ecf20Sopenharmony_ci pdev->last_exposure_update + HZ / 4)) { 5618c2ecf20Sopenharmony_ci pdev->exposure->val = pdev->last_exposure; 5628c2ecf20Sopenharmony_ci break; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, 5658c2ecf20Sopenharmony_ci READ_SHUTTER_FORMATTER, 5668c2ecf20Sopenharmony_ci &pdev->exposure->val); 5678c2ecf20Sopenharmony_ci if (ret) 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci pdev->last_exposure = pdev->exposure->val; 5708c2ecf20Sopenharmony_ci pdev->last_exposure_update = jiffies; 5718c2ecf20Sopenharmony_ci pdev->exposure_valid = true; 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci default: 5748c2ecf20Sopenharmony_ci ret = -EINVAL; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (ret) 5788c2ecf20Sopenharmony_ci PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return ret; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int pwc_set_awb(struct pwc_device *pdev) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci int ret; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (pdev->auto_white_balance->is_new) { 5888c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, 5898c2ecf20Sopenharmony_ci WB_MODE_FORMATTER, 5908c2ecf20Sopenharmony_ci pdev->auto_white_balance->val); 5918c2ecf20Sopenharmony_ci if (ret) 5928c2ecf20Sopenharmony_ci return ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (pdev->auto_white_balance->val != awb_manual) 5958c2ecf20Sopenharmony_ci pdev->color_bal_valid = false; /* Force cache update */ 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* 5988c2ecf20Sopenharmony_ci * If this is a preset, update our red / blue balance values 5998c2ecf20Sopenharmony_ci * so that events get generated for the new preset values 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_ci if (pdev->auto_white_balance->val == awb_indoor || 6028c2ecf20Sopenharmony_ci pdev->auto_white_balance->val == awb_outdoor || 6038c2ecf20Sopenharmony_ci pdev->auto_white_balance->val == awb_fl) 6048c2ecf20Sopenharmony_ci pwc_g_volatile_ctrl(pdev->auto_white_balance); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci if (pdev->auto_white_balance->val != awb_manual) 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (pdev->red_balance->is_new) { 6108c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, 6118c2ecf20Sopenharmony_ci PRESET_MANUAL_RED_GAIN_FORMATTER, 6128c2ecf20Sopenharmony_ci pdev->red_balance->val); 6138c2ecf20Sopenharmony_ci if (ret) 6148c2ecf20Sopenharmony_ci return ret; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (pdev->blue_balance->is_new) { 6188c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, 6198c2ecf20Sopenharmony_ci PRESET_MANUAL_BLUE_GAIN_FORMATTER, 6208c2ecf20Sopenharmony_ci pdev->blue_balance->val); 6218c2ecf20Sopenharmony_ci if (ret) 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* For CODEC2 models which have separate autogain and auto exposure */ 6288c2ecf20Sopenharmony_cistatic int pwc_set_autogain(struct pwc_device *pdev) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci int ret; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (pdev->autogain->is_new) { 6338c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 6348c2ecf20Sopenharmony_ci AGC_MODE_FORMATTER, 6358c2ecf20Sopenharmony_ci pdev->autogain->val ? 0 : 0xff); 6368c2ecf20Sopenharmony_ci if (ret) 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (pdev->autogain->val) 6408c2ecf20Sopenharmony_ci pdev->gain_valid = false; /* Force cache update */ 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (pdev->autogain->val) 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (pdev->gain->is_new) { 6478c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 6488c2ecf20Sopenharmony_ci PRESET_AGC_FORMATTER, 6498c2ecf20Sopenharmony_ci pdev->gain->val); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci return ret; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/* For CODEC2 models which have separate autogain and auto exposure */ 6578c2ecf20Sopenharmony_cistatic int pwc_set_exposure_auto(struct pwc_device *pdev) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci int ret; 6608c2ecf20Sopenharmony_ci int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (pdev->exposure_auto->is_new) { 6638c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 6648c2ecf20Sopenharmony_ci SHUTTER_MODE_FORMATTER, 6658c2ecf20Sopenharmony_ci is_auto ? 0 : 0xff); 6668c2ecf20Sopenharmony_ci if (ret) 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (is_auto) 6708c2ecf20Sopenharmony_ci pdev->exposure_valid = false; /* Force cache update */ 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (is_auto) 6748c2ecf20Sopenharmony_ci return 0; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (pdev->exposure->is_new) { 6778c2ecf20Sopenharmony_ci ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, 6788c2ecf20Sopenharmony_ci PRESET_SHUTTER_FORMATTER, 6798c2ecf20Sopenharmony_ci pdev->exposure->val); 6808c2ecf20Sopenharmony_ci if (ret) 6818c2ecf20Sopenharmony_ci return ret; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/* For CODEC3 models which have autogain controlling both gain and exposure */ 6878c2ecf20Sopenharmony_cistatic int pwc_set_autogain_expo(struct pwc_device *pdev) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci int ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (pdev->autogain->is_new) { 6928c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 6938c2ecf20Sopenharmony_ci AGC_MODE_FORMATTER, 6948c2ecf20Sopenharmony_ci pdev->autogain->val ? 0 : 0xff); 6958c2ecf20Sopenharmony_ci if (ret) 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (pdev->autogain->val) { 6998c2ecf20Sopenharmony_ci pdev->gain_valid = false; /* Force cache update */ 7008c2ecf20Sopenharmony_ci pdev->exposure_valid = false; /* Force cache update */ 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (pdev->autogain->val) 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (pdev->gain->is_new) { 7088c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 7098c2ecf20Sopenharmony_ci PRESET_AGC_FORMATTER, 7108c2ecf20Sopenharmony_ci pdev->gain->val); 7118c2ecf20Sopenharmony_ci if (ret) 7128c2ecf20Sopenharmony_ci return ret; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (pdev->exposure->is_new) { 7168c2ecf20Sopenharmony_ci ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, 7178c2ecf20Sopenharmony_ci PRESET_SHUTTER_FORMATTER, 7188c2ecf20Sopenharmony_ci pdev->exposure->val); 7198c2ecf20Sopenharmony_ci if (ret) 7208c2ecf20Sopenharmony_ci return ret; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic int pwc_set_motor(struct pwc_device *pdev) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci int ret; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = 0; 7308c2ecf20Sopenharmony_ci if (pdev->motor_pan_reset->is_new) 7318c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] |= 0x01; 7328c2ecf20Sopenharmony_ci if (pdev->motor_tilt_reset->is_new) 7338c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] |= 0x02; 7348c2ecf20Sopenharmony_ci if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) { 7358c2ecf20Sopenharmony_ci ret = send_control_msg(pdev, SET_MPT_CTL, 7368c2ecf20Sopenharmony_ci PT_RESET_CONTROL_FORMATTER, 7378c2ecf20Sopenharmony_ci pdev->ctrl_buf, 1); 7388c2ecf20Sopenharmony_ci if (ret < 0) 7398c2ecf20Sopenharmony_ci return ret; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci memset(pdev->ctrl_buf, 0, 4); 7438c2ecf20Sopenharmony_ci if (pdev->motor_pan->is_new) { 7448c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF; 7458c2ecf20Sopenharmony_ci pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8); 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci if (pdev->motor_tilt->is_new) { 7488c2ecf20Sopenharmony_ci pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF; 7498c2ecf20Sopenharmony_ci pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) { 7528c2ecf20Sopenharmony_ci ret = send_control_msg(pdev, SET_MPT_CTL, 7538c2ecf20Sopenharmony_ci PT_RELATIVE_CONTROL_FORMATTER, 7548c2ecf20Sopenharmony_ci pdev->ctrl_buf, 4); 7558c2ecf20Sopenharmony_ci if (ret < 0) 7568c2ecf20Sopenharmony_ci return ret; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int pwc_s_ctrl(struct v4l2_ctrl *ctrl) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct pwc_device *pdev = 7658c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct pwc_device, ctrl_handler); 7668c2ecf20Sopenharmony_ci int ret = 0; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci switch (ctrl->id) { 7698c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 7708c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 7718c2ecf20Sopenharmony_ci BRIGHTNESS_FORMATTER, ctrl->val); 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 7748c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 7758c2ecf20Sopenharmony_ci CONTRAST_FORMATTER, ctrl->val); 7768c2ecf20Sopenharmony_ci break; 7778c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 7788c2ecf20Sopenharmony_ci ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL, 7798c2ecf20Sopenharmony_ci pdev->saturation_fmt, ctrl->val); 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci case V4L2_CID_GAMMA: 7828c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 7838c2ecf20Sopenharmony_ci GAMMA_FORMATTER, ctrl->val); 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 7868c2ecf20Sopenharmony_ci ret = pwc_set_awb(pdev); 7878c2ecf20Sopenharmony_ci break; 7888c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 7898c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC2(pdev->type)) 7908c2ecf20Sopenharmony_ci ret = pwc_set_autogain(pdev); 7918c2ecf20Sopenharmony_ci else if (DEVICE_USE_CODEC3(pdev->type)) 7928c2ecf20Sopenharmony_ci ret = pwc_set_autogain_expo(pdev); 7938c2ecf20Sopenharmony_ci else 7948c2ecf20Sopenharmony_ci ret = -EINVAL; 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 7978c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC2(pdev->type)) 7988c2ecf20Sopenharmony_ci ret = pwc_set_exposure_auto(pdev); 7998c2ecf20Sopenharmony_ci else 8008c2ecf20Sopenharmony_ci ret = -EINVAL; 8018c2ecf20Sopenharmony_ci break; 8028c2ecf20Sopenharmony_ci case V4L2_CID_COLORFX: 8038c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, 8048c2ecf20Sopenharmony_ci COLOUR_MODE_FORMATTER, 8058c2ecf20Sopenharmony_ci ctrl->val ? 0 : 0xff); 8068c2ecf20Sopenharmony_ci break; 8078c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(autocontour): 8088c2ecf20Sopenharmony_ci if (pdev->autocontour->is_new) { 8098c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 8108c2ecf20Sopenharmony_ci AUTO_CONTOUR_FORMATTER, 8118c2ecf20Sopenharmony_ci pdev->autocontour->val ? 0 : 0xff); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci if (ret == 0 && pdev->contour->is_new) { 8148c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 8158c2ecf20Sopenharmony_ci PRESET_CONTOUR_FORMATTER, 8168c2ecf20Sopenharmony_ci pdev->contour->val); 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci case V4L2_CID_BACKLIGHT_COMPENSATION: 8208c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 8218c2ecf20Sopenharmony_ci BACK_LIGHT_COMPENSATION_FORMATTER, 8228c2ecf20Sopenharmony_ci ctrl->val ? 0 : 0xff); 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci case V4L2_CID_BAND_STOP_FILTER: 8258c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 8268c2ecf20Sopenharmony_ci FLICKERLESS_MODE_FORMATTER, 8278c2ecf20Sopenharmony_ci ctrl->val ? 0 : 0xff); 8288c2ecf20Sopenharmony_ci break; 8298c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(noise_reduction): 8308c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, 8318c2ecf20Sopenharmony_ci DYNAMIC_NOISE_CONTROL_FORMATTER, 8328c2ecf20Sopenharmony_ci ctrl->val); 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(save_user): 8358c2ecf20Sopenharmony_ci ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(restore_user): 8388c2ecf20Sopenharmony_ci ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(restore_factory): 8418c2ecf20Sopenharmony_ci ret = pwc_button_ctrl(pdev, 8428c2ecf20Sopenharmony_ci RESTORE_FACTORY_DEFAULTS_FORMATTER); 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(awb_speed): 8458c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, 8468c2ecf20Sopenharmony_ci AWB_CONTROL_SPEED_FORMATTER, 8478c2ecf20Sopenharmony_ci ctrl->val); 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci case PWC_CID_CUSTOM(awb_delay): 8508c2ecf20Sopenharmony_ci ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, 8518c2ecf20Sopenharmony_ci AWB_CONTROL_DELAY_FORMATTER, 8528c2ecf20Sopenharmony_ci ctrl->val); 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci case V4L2_CID_PAN_RELATIVE: 8558c2ecf20Sopenharmony_ci ret = pwc_set_motor(pdev); 8568c2ecf20Sopenharmony_ci break; 8578c2ecf20Sopenharmony_ci default: 8588c2ecf20Sopenharmony_ci ret = -EINVAL; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (ret) 8628c2ecf20Sopenharmony_ci PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return ret; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* We only support two format: the raw format, and YUV */ 8728c2ecf20Sopenharmony_ci switch (f->index) { 8738c2ecf20Sopenharmony_ci case 0: 8748c2ecf20Sopenharmony_ci /* RAW format */ 8758c2ecf20Sopenharmony_ci f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2; 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci case 1: 8788c2ecf20Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUV420; 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci default: 8818c2ecf20Sopenharmony_ci return -EINVAL; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 8918c2ecf20Sopenharmony_ci return -EINVAL; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", 8948c2ecf20Sopenharmony_ci pdev->width, pdev->height); 8958c2ecf20Sopenharmony_ci pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); 8968c2ecf20Sopenharmony_ci return 0; 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return pwc_vidioc_try_fmt(pdev, f); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int pwc_enum_framesizes(struct file *file, void *fh, 9078c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 9108c2ecf20Sopenharmony_ci unsigned int i = 0, index = fsize->index; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 || 9138c2ecf20Sopenharmony_ci (fsize->pixel_format == V4L2_PIX_FMT_PWC1 && 9148c2ecf20Sopenharmony_ci DEVICE_USE_CODEC1(pdev->type)) || 9158c2ecf20Sopenharmony_ci (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && 9168c2ecf20Sopenharmony_ci DEVICE_USE_CODEC23(pdev->type))) { 9178c2ecf20Sopenharmony_ci for (i = 0; i < PSZ_MAX; i++) { 9188c2ecf20Sopenharmony_ci if (!(pdev->image_mask & (1UL << i))) 9198c2ecf20Sopenharmony_ci continue; 9208c2ecf20Sopenharmony_ci if (!index--) { 9218c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 9228c2ecf20Sopenharmony_ci fsize->discrete.width = pwc_image_sizes[i][0]; 9238c2ecf20Sopenharmony_ci fsize->discrete.height = pwc_image_sizes[i][1]; 9248c2ecf20Sopenharmony_ci return 0; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci return -EINVAL; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic int pwc_enum_frameintervals(struct file *file, void *fh, 9328c2ecf20Sopenharmony_ci struct v4l2_frmivalenum *fival) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 9358c2ecf20Sopenharmony_ci int size = -1; 9368c2ecf20Sopenharmony_ci unsigned int i; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci for (i = 0; i < PSZ_MAX; i++) { 9398c2ecf20Sopenharmony_ci if (pwc_image_sizes[i][0] == fival->width && 9408c2ecf20Sopenharmony_ci pwc_image_sizes[i][1] == fival->height) { 9418c2ecf20Sopenharmony_ci size = i; 9428c2ecf20Sopenharmony_ci break; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* TODO: Support raw format */ 9478c2ecf20Sopenharmony_ci if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) 9488c2ecf20Sopenharmony_ci return -EINVAL; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci i = pwc_get_fps(pdev, fival->index, size); 9518c2ecf20Sopenharmony_ci if (!i) 9528c2ecf20Sopenharmony_ci return -EINVAL; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 9558c2ecf20Sopenharmony_ci fival->discrete.numerator = 1; 9568c2ecf20Sopenharmony_ci fival->discrete.denominator = i; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic int pwc_g_parm(struct file *file, void *fh, 9628c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 9678c2ecf20Sopenharmony_ci return -EINVAL; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(*parm)); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 9728c2ecf20Sopenharmony_ci parm->parm.capture.readbuffers = MIN_FRAMES; 9738c2ecf20Sopenharmony_ci parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; 9748c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe.denominator = pdev->vframes; 9758c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe.numerator = 1; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return 0; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int pwc_s_parm(struct file *file, void *fh, 9818c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct pwc_device *pdev = video_drvdata(file); 9848c2ecf20Sopenharmony_ci int compression = 0; 9858c2ecf20Sopenharmony_ci int ret, fps; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 9888c2ecf20Sopenharmony_ci return -EINVAL; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* If timeperframe == 0, then reset the framerate to the nominal value. 9918c2ecf20Sopenharmony_ci We pick a high framerate here, and let pwc_set_video_mode() figure 9928c2ecf20Sopenharmony_ci out the best match. */ 9938c2ecf20Sopenharmony_ci if (parm->parm.capture.timeperframe.numerator == 0 || 9948c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe.denominator == 0) 9958c2ecf20Sopenharmony_ci fps = 30; 9968c2ecf20Sopenharmony_ci else 9978c2ecf20Sopenharmony_ci fps = parm->parm.capture.timeperframe.denominator / 9988c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe.numerator; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (vb2_is_busy(&pdev->vb_queue)) 10018c2ecf20Sopenharmony_ci return -EBUSY; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, 10048c2ecf20Sopenharmony_ci fps, &compression, 0); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci pwc_g_parm(file, fh, parm); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci return ret; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ciconst struct v4l2_ioctl_ops pwc_ioctl_ops = { 10128c2ecf20Sopenharmony_ci .vidioc_querycap = pwc_querycap, 10138c2ecf20Sopenharmony_ci .vidioc_enum_input = pwc_enum_input, 10148c2ecf20Sopenharmony_ci .vidioc_g_input = pwc_g_input, 10158c2ecf20Sopenharmony_ci .vidioc_s_input = pwc_s_input, 10168c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = pwc_enum_fmt_vid_cap, 10178c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap, 10188c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap, 10198c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap, 10208c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 10218c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 10228c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 10238c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 10248c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 10258c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 10268c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 10278c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = pwc_enum_framesizes, 10288c2ecf20Sopenharmony_ci .vidioc_enum_frameintervals = pwc_enum_frameintervals, 10298c2ecf20Sopenharmony_ci .vidioc_g_parm = pwc_g_parm, 10308c2ecf20Sopenharmony_ci .vidioc_s_parm = pwc_s_parm, 10318c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 10328c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 10338c2ecf20Sopenharmony_ci}; 1034