18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Driver for Philips webcam 38c2ecf20Sopenharmony_ci Functions that send various control messages to the webcam, including 48c2ecf20Sopenharmony_ci video modes. 58c2ecf20Sopenharmony_ci (C) 1999-2003 Nemosoft Unv. 68c2ecf20Sopenharmony_ci (C) 2004-2006 Luc Saillard (luc@saillard.org) 78c2ecf20Sopenharmony_ci (C) 2011 Hans de Goede <hdegoede@redhat.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx 108c2ecf20Sopenharmony_ci driver and thus may have bugs that are not present in the original version. 118c2ecf20Sopenharmony_ci Please send bug reports and support requests to <luc@saillard.org>. 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx 148c2ecf20Sopenharmony_ci driver and thus may have bugs that are not present in the original version. 158c2ecf20Sopenharmony_ci Please send bug reports and support requests to <luc@saillard.org>. 168c2ecf20Sopenharmony_ci The decompression routines have been implemented by reverse-engineering the 178c2ecf20Sopenharmony_ci Nemosoft binary pwcx module. Caveat emptor. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci*/ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci Changes 238c2ecf20Sopenharmony_ci 2001/08/03 Alvarado Added methods for changing white balance and 248c2ecf20Sopenharmony_ci red/green gains 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Control functions for the cam; brightness, contrast, video mode, etc. */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#ifdef __KERNEL__ 308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci#include <asm/errno.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "pwc.h" 358c2ecf20Sopenharmony_ci#include "pwc-kiara.h" 368c2ecf20Sopenharmony_ci#include "pwc-timon.h" 378c2ecf20Sopenharmony_ci#include "pwc-dec1.h" 388c2ecf20Sopenharmony_ci#include "pwc-dec23.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Selectors for status controls used only in this file */ 418c2ecf20Sopenharmony_ci#define GET_STATUS_B00 0x0B00 428c2ecf20Sopenharmony_ci#define SENSOR_TYPE_FORMATTER1 0x0C00 438c2ecf20Sopenharmony_ci#define GET_STATUS_3000 0x3000 448c2ecf20Sopenharmony_ci#define READ_RAW_Y_MEAN_FORMATTER 0x3100 458c2ecf20Sopenharmony_ci#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 468c2ecf20Sopenharmony_ci#define MIRROR_IMAGE_FORMATTER 0x3300 478c2ecf20Sopenharmony_ci#define LED_FORMATTER 0x3400 488c2ecf20Sopenharmony_ci#define LOWLIGHT 0x3500 498c2ecf20Sopenharmony_ci#define GET_STATUS_3600 0x3600 508c2ecf20Sopenharmony_ci#define SENSOR_TYPE_FORMATTER2 0x3700 518c2ecf20Sopenharmony_ci#define GET_STATUS_3800 0x3800 528c2ecf20Sopenharmony_ci#define GET_STATUS_4000 0x4000 538c2ecf20Sopenharmony_ci#define GET_STATUS_4100 0x4100 /* Get */ 548c2ecf20Sopenharmony_ci#define CTL_STATUS_4200 0x4200 /* [GS] 1 */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ 578c2ecf20Sopenharmony_ci#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const char *size2name[PSZ_MAX] = 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci "subQCIF", 628c2ecf20Sopenharmony_ci "QSIF", 638c2ecf20Sopenharmony_ci "QCIF", 648c2ecf20Sopenharmony_ci "SIF", 658c2ecf20Sopenharmony_ci "CIF", 668c2ecf20Sopenharmony_ci "VGA", 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/********/ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Entries for the Nala (645/646) camera; the Nala doesn't have compression 728c2ecf20Sopenharmony_ci preferences, so you either get compressed or non-compressed streams. 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci An alternate value of 0 means this mode is not available at all. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define PWC_FPS_MAX_NALA 8 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct Nala_table_entry { 808c2ecf20Sopenharmony_ci char alternate; /* USB alternate setting */ 818c2ecf20Sopenharmony_ci int compressed; /* Compressed yes/no */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci unsigned char mode[3]; /* precomputed mode table */ 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci#include "pwc-nala.h" 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/****************************************************************************/ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int recv_control_msg(struct pwc_device *pdev, 968c2ecf20Sopenharmony_ci u8 request, u16 value, int recv_count) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int rc; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), 1018c2ecf20Sopenharmony_ci request, 1028c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1038c2ecf20Sopenharmony_ci value, pdev->vcinterface, 1048c2ecf20Sopenharmony_ci pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT); 1058c2ecf20Sopenharmony_ci if (rc < 0) 1068c2ecf20Sopenharmony_ci PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", 1078c2ecf20Sopenharmony_ci rc, request, value); 1088c2ecf20Sopenharmony_ci return rc; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic inline int send_video_command(struct pwc_device *pdev, 1128c2ecf20Sopenharmony_ci int index, const unsigned char *buf, int buflen) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int rc; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci memcpy(pdev->ctrl_buf, buf, buflen); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), 1198c2ecf20Sopenharmony_ci SET_EP_STREAM_CTL, 1208c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1218c2ecf20Sopenharmony_ci VIDEO_OUTPUT_CONTROL_FORMATTER, index, 1228c2ecf20Sopenharmony_ci pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT); 1238c2ecf20Sopenharmony_ci if (rc >= 0) 1248c2ecf20Sopenharmony_ci memcpy(pdev->cmd_buf, buf, buflen); 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci PWC_ERROR("send_video_command error %d\n", rc); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return rc; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint send_control_msg(struct pwc_device *pdev, 1328c2ecf20Sopenharmony_ci u8 request, u16 value, void *buf, int buflen) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), 1358c2ecf20Sopenharmony_ci request, 1368c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1378c2ecf20Sopenharmony_ci value, pdev->vcinterface, 1388c2ecf20Sopenharmony_ci buf, buflen, USB_CTRL_SET_TIMEOUT); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, 1428c2ecf20Sopenharmony_ci int frames, int *compression, int send_to_cam) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int fps, ret = 0; 1458c2ecf20Sopenharmony_ci struct Nala_table_entry *pEntry; 1468c2ecf20Sopenharmony_ci int frames2frames[31] = 1478c2ecf20Sopenharmony_ci { /* closest match of framerate */ 1488c2ecf20Sopenharmony_ci 0, 0, 0, 0, 4, /* 0-4 */ 1498c2ecf20Sopenharmony_ci 5, 5, 7, 7, 10, /* 5-9 */ 1508c2ecf20Sopenharmony_ci 10, 10, 12, 12, 15, /* 10-14 */ 1518c2ecf20Sopenharmony_ci 15, 15, 15, 20, 20, /* 15-19 */ 1528c2ecf20Sopenharmony_ci 20, 20, 20, 24, 24, /* 20-24 */ 1538c2ecf20Sopenharmony_ci 24, 24, 24, 24, 24, /* 25-29 */ 1548c2ecf20Sopenharmony_ci 24 /* 30 */ 1558c2ecf20Sopenharmony_ci }; 1568c2ecf20Sopenharmony_ci int frames2table[31] = 1578c2ecf20Sopenharmony_ci { 0, 0, 0, 0, 0, /* 0-4 */ 1588c2ecf20Sopenharmony_ci 1, 1, 1, 2, 2, /* 5-9 */ 1598c2ecf20Sopenharmony_ci 3, 3, 4, 4, 4, /* 10-14 */ 1608c2ecf20Sopenharmony_ci 5, 5, 5, 5, 5, /* 15-19 */ 1618c2ecf20Sopenharmony_ci 6, 6, 6, 6, 7, /* 20-24 */ 1628c2ecf20Sopenharmony_ci 7, 7, 7, 7, 7, /* 25-29 */ 1638c2ecf20Sopenharmony_ci 7 /* 30 */ 1648c2ecf20Sopenharmony_ci }; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (size < 0 || size > PSZ_CIF) 1678c2ecf20Sopenharmony_ci return -EINVAL; 1688c2ecf20Sopenharmony_ci if (frames < 4) 1698c2ecf20Sopenharmony_ci frames = 4; 1708c2ecf20Sopenharmony_ci else if (size > PSZ_QCIF && frames > 15) 1718c2ecf20Sopenharmony_ci frames = 15; 1728c2ecf20Sopenharmony_ci else if (frames > 25) 1738c2ecf20Sopenharmony_ci frames = 25; 1748c2ecf20Sopenharmony_ci frames = frames2frames[frames]; 1758c2ecf20Sopenharmony_ci fps = frames2table[frames]; 1768c2ecf20Sopenharmony_ci pEntry = &Nala_table[size][fps]; 1778c2ecf20Sopenharmony_ci if (pEntry->alternate == 0) 1788c2ecf20Sopenharmony_ci return -EINVAL; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (send_to_cam) 1818c2ecf20Sopenharmony_ci ret = send_video_command(pdev, pdev->vendpoint, 1828c2ecf20Sopenharmony_ci pEntry->mode, 3); 1838c2ecf20Sopenharmony_ci if (ret < 0) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) 1878c2ecf20Sopenharmony_ci pwc_dec1_init(pdev, pEntry->mode); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Set various parameters */ 1908c2ecf20Sopenharmony_ci pdev->pixfmt = pixfmt; 1918c2ecf20Sopenharmony_ci pdev->vframes = frames; 1928c2ecf20Sopenharmony_ci pdev->valternate = pEntry->alternate; 1938c2ecf20Sopenharmony_ci pdev->width = pwc_image_sizes[size][0]; 1948c2ecf20Sopenharmony_ci pdev->height = pwc_image_sizes[size][1]; 1958c2ecf20Sopenharmony_ci pdev->frame_size = (pdev->width * pdev->height * 3) / 2; 1968c2ecf20Sopenharmony_ci if (pEntry->compressed) { 1978c2ecf20Sopenharmony_ci if (pdev->release < 5) { /* 4 fold compression */ 1988c2ecf20Sopenharmony_ci pdev->vbandlength = 528; 1998c2ecf20Sopenharmony_ci pdev->frame_size /= 4; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci else { 2028c2ecf20Sopenharmony_ci pdev->vbandlength = 704; 2038c2ecf20Sopenharmony_ci pdev->frame_size /= 3; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci else 2078c2ecf20Sopenharmony_ci pdev->vbandlength = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Let pwc-if.c:isoc_init know we don't support higher compression */ 2108c2ecf20Sopenharmony_ci *compression = 3; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, 2178c2ecf20Sopenharmony_ci int frames, int *compression, int send_to_cam) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci const struct Timon_table_entry *pChoose; 2208c2ecf20Sopenharmony_ci int fps, ret = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (size >= PSZ_MAX || *compression < 0 || *compression > 3) 2238c2ecf20Sopenharmony_ci return -EINVAL; 2248c2ecf20Sopenharmony_ci if (frames < 5) 2258c2ecf20Sopenharmony_ci frames = 5; 2268c2ecf20Sopenharmony_ci else if (size == PSZ_VGA && frames > 15) 2278c2ecf20Sopenharmony_ci frames = 15; 2288c2ecf20Sopenharmony_ci else if (frames > 30) 2298c2ecf20Sopenharmony_ci frames = 30; 2308c2ecf20Sopenharmony_ci fps = (frames / 5) - 1; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Find a supported framerate with progressively higher compression */ 2338c2ecf20Sopenharmony_ci do { 2348c2ecf20Sopenharmony_ci pChoose = &Timon_table[size][fps][*compression]; 2358c2ecf20Sopenharmony_ci if (pChoose->alternate != 0) 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci (*compression)++; 2388c2ecf20Sopenharmony_ci } while (*compression <= 3); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (pChoose->alternate == 0) 2418c2ecf20Sopenharmony_ci return -ENOENT; /* Not supported. */ 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (send_to_cam) 2448c2ecf20Sopenharmony_ci ret = send_video_command(pdev, pdev->vendpoint, 2458c2ecf20Sopenharmony_ci pChoose->mode, 13); 2468c2ecf20Sopenharmony_ci if (ret < 0) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) 2508c2ecf20Sopenharmony_ci pwc_dec23_init(pdev, pChoose->mode); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Set various parameters */ 2538c2ecf20Sopenharmony_ci pdev->pixfmt = pixfmt; 2548c2ecf20Sopenharmony_ci pdev->vframes = (fps + 1) * 5; 2558c2ecf20Sopenharmony_ci pdev->valternate = pChoose->alternate; 2568c2ecf20Sopenharmony_ci pdev->width = pwc_image_sizes[size][0]; 2578c2ecf20Sopenharmony_ci pdev->height = pwc_image_sizes[size][1]; 2588c2ecf20Sopenharmony_ci pdev->vbandlength = pChoose->bandlength; 2598c2ecf20Sopenharmony_ci if (pChoose->bandlength > 0) 2608c2ecf20Sopenharmony_ci pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci pdev->frame_size = (pdev->width * pdev->height * 12) / 8; 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, 2688c2ecf20Sopenharmony_ci int frames, int *compression, int send_to_cam) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci const struct Kiara_table_entry *pChoose; 2718c2ecf20Sopenharmony_ci int fps, ret = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (size >= PSZ_MAX || *compression < 0 || *compression > 3) 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci if (frames < 5) 2768c2ecf20Sopenharmony_ci frames = 5; 2778c2ecf20Sopenharmony_ci else if (size == PSZ_VGA && frames > 15) 2788c2ecf20Sopenharmony_ci frames = 15; 2798c2ecf20Sopenharmony_ci else if (frames > 30) 2808c2ecf20Sopenharmony_ci frames = 30; 2818c2ecf20Sopenharmony_ci fps = (frames / 5) - 1; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Find a supported framerate with progressively higher compression */ 2848c2ecf20Sopenharmony_ci do { 2858c2ecf20Sopenharmony_ci pChoose = &Kiara_table[size][fps][*compression]; 2868c2ecf20Sopenharmony_ci if (pChoose->alternate != 0) 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci (*compression)++; 2898c2ecf20Sopenharmony_ci } while (*compression <= 3); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (pChoose->alternate == 0) 2928c2ecf20Sopenharmony_ci return -ENOENT; /* Not supported. */ 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ 2958c2ecf20Sopenharmony_ci if (send_to_cam) 2968c2ecf20Sopenharmony_ci ret = send_video_command(pdev, 4, pChoose->mode, 12); 2978c2ecf20Sopenharmony_ci if (ret < 0) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) 3018c2ecf20Sopenharmony_ci pwc_dec23_init(pdev, pChoose->mode); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* All set and go */ 3048c2ecf20Sopenharmony_ci pdev->pixfmt = pixfmt; 3058c2ecf20Sopenharmony_ci pdev->vframes = (fps + 1) * 5; 3068c2ecf20Sopenharmony_ci pdev->valternate = pChoose->alternate; 3078c2ecf20Sopenharmony_ci pdev->width = pwc_image_sizes[size][0]; 3088c2ecf20Sopenharmony_ci pdev->height = pwc_image_sizes[size][1]; 3098c2ecf20Sopenharmony_ci pdev->vbandlength = pChoose->bandlength; 3108c2ecf20Sopenharmony_ci if (pdev->vbandlength > 0) 3118c2ecf20Sopenharmony_ci pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; 3128c2ecf20Sopenharmony_ci else 3138c2ecf20Sopenharmony_ci pdev->frame_size = (pdev->width * pdev->height * 12) / 8; 3148c2ecf20Sopenharmony_ci PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n", 3158c2ecf20Sopenharmony_ci pdev->frame_size, pdev->vframes, size, pdev->vbandlength); 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciint pwc_set_video_mode(struct pwc_device *pdev, int width, int height, 3208c2ecf20Sopenharmony_ci int pixfmt, int frames, int *compression, int send_to_cam) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int ret, size; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", 3258c2ecf20Sopenharmony_ci width, height, frames, pixfmt); 3268c2ecf20Sopenharmony_ci size = pwc_get_size(pdev, width, height); 3278c2ecf20Sopenharmony_ci PWC_TRACE("decode_size = %d.\n", size); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC1(pdev->type)) { 3308c2ecf20Sopenharmony_ci ret = set_video_mode_Nala(pdev, size, pixfmt, frames, 3318c2ecf20Sopenharmony_ci compression, send_to_cam); 3328c2ecf20Sopenharmony_ci } else if (DEVICE_USE_CODEC3(pdev->type)) { 3338c2ecf20Sopenharmony_ci ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, 3348c2ecf20Sopenharmony_ci compression, send_to_cam); 3358c2ecf20Sopenharmony_ci } else { 3368c2ecf20Sopenharmony_ci ret = set_video_mode_Timon(pdev, size, pixfmt, frames, 3378c2ecf20Sopenharmony_ci compression, send_to_cam); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci if (ret < 0) { 3408c2ecf20Sopenharmony_ci PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; 3448c2ecf20Sopenharmony_ci PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci unsigned int i; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci for (i = 0; i < PWC_FPS_MAX_NALA; i++) { 3538c2ecf20Sopenharmony_ci if (Nala_table[size][i].alternate) { 3548c2ecf20Sopenharmony_ci if (index--==0) return Nala_fps_vector[i]; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci unsigned int i; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { 3658c2ecf20Sopenharmony_ci if (Kiara_table[size][i][3].alternate) { 3668c2ecf20Sopenharmony_ci if (index--==0) return Kiara_fps_vector[i]; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci unsigned int i; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i=0; i < PWC_FPS_MAX_TIMON; i++) { 3778c2ecf20Sopenharmony_ci if (Timon_table[size][i][3].alternate) { 3788c2ecf20Sopenharmony_ci if (index--==0) return Timon_fps_vector[i]; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciunsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci unsigned int ret; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (DEVICE_USE_CODEC1(pdev->type)) { 3898c2ecf20Sopenharmony_ci ret = pwc_get_fps_Nala(pdev, index, size); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci } else if (DEVICE_USE_CODEC3(pdev->type)) { 3928c2ecf20Sopenharmony_ci ret = pwc_get_fps_Kiara(pdev, index, size); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci ret = pwc_get_fps_Timon(pdev, index, size); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ciint pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci int ret; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci ret = recv_control_msg(pdev, request, value, 1); 4068c2ecf20Sopenharmony_ci if (ret < 0) 4078c2ecf20Sopenharmony_ci return ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci *data = pdev->ctrl_buf[0]; 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ciint pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci int ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = data; 4188c2ecf20Sopenharmony_ci ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); 4198c2ecf20Sopenharmony_ci if (ret < 0) 4208c2ecf20Sopenharmony_ci return ret; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ciint pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ret = recv_control_msg(pdev, request, value, 1); 4308c2ecf20Sopenharmony_ci if (ret < 0) 4318c2ecf20Sopenharmony_ci return ret; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci *data = ((s8 *)pdev->ctrl_buf)[0]; 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciint pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci int ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ret = recv_control_msg(pdev, request, value, 2); 4428c2ecf20Sopenharmony_ci if (ret < 0) 4438c2ecf20Sopenharmony_ci return ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ciint pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = data & 0xff; 4548c2ecf20Sopenharmony_ci pdev->ctrl_buf[1] = data >> 8; 4558c2ecf20Sopenharmony_ci ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); 4568c2ecf20Sopenharmony_ci if (ret < 0) 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciint pwc_button_ctrl(struct pwc_device *pdev, u16 value) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); 4678c2ecf20Sopenharmony_ci if (ret < 0) 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* POWER */ 4748c2ecf20Sopenharmony_civoid pwc_camera_power(struct pwc_device *pdev, int power) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int r; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (!pdev->power_save) 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) 4828c2ecf20Sopenharmony_ci return; /* Not supported by Nala or Timon < release 6 */ 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (power) 4858c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = 0x00; /* active */ 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = 0xFF; /* power save */ 4888c2ecf20Sopenharmony_ci r = send_control_msg(pdev, SET_STATUS_CTL, 4898c2ecf20Sopenharmony_ci SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); 4908c2ecf20Sopenharmony_ci if (r < 0) 4918c2ecf20Sopenharmony_ci PWC_ERROR("Failed to power %s camera (%d)\n", 4928c2ecf20Sopenharmony_ci power ? "on" : "off", r); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ciint pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci int r; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (pdev->type < 730) 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci on_value /= 100; 5028c2ecf20Sopenharmony_ci off_value /= 100; 5038c2ecf20Sopenharmony_ci if (on_value < 0) 5048c2ecf20Sopenharmony_ci on_value = 0; 5058c2ecf20Sopenharmony_ci if (on_value > 0xff) 5068c2ecf20Sopenharmony_ci on_value = 0xff; 5078c2ecf20Sopenharmony_ci if (off_value < 0) 5088c2ecf20Sopenharmony_ci off_value = 0; 5098c2ecf20Sopenharmony_ci if (off_value > 0xff) 5108c2ecf20Sopenharmony_ci off_value = 0xff; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci pdev->ctrl_buf[0] = on_value; 5138c2ecf20Sopenharmony_ci pdev->ctrl_buf[1] = off_value; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci r = send_control_msg(pdev, 5168c2ecf20Sopenharmony_ci SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); 5178c2ecf20Sopenharmony_ci if (r < 0) 5188c2ecf20Sopenharmony_ci PWC_ERROR("Failed to set LED on/off time (%d)\n", r); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return r; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_DEBUG 5248c2ecf20Sopenharmony_ciint pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int ret, request; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (pdev->type < 675) 5298c2ecf20Sopenharmony_ci request = SENSOR_TYPE_FORMATTER1; 5308c2ecf20Sopenharmony_ci else if (pdev->type < 730) 5318c2ecf20Sopenharmony_ci return -1; /* The Vesta series doesn't have this call */ 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci request = SENSOR_TYPE_FORMATTER2; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); 5368c2ecf20Sopenharmony_ci if (ret < 0) 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci if (pdev->type < 675) 5398c2ecf20Sopenharmony_ci *sensor = pdev->ctrl_buf[0] | 0x100; 5408c2ecf20Sopenharmony_ci else 5418c2ecf20Sopenharmony_ci *sensor = pdev->ctrl_buf[0]; 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci#endif 545