18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2013,2016 Lubomir Rintel 38c2ecf20Sopenharmony_ci * All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 68c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 78c2ecf20Sopenharmony_ci * are met: 88c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 98c2ecf20Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 108c2ecf20Sopenharmony_ci * without modification. 118c2ecf20Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products 128c2ecf20Sopenharmony_ci * derived from this software without specific prior written permission. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 158c2ecf20Sopenharmony_ci * GNU General Public License ("GPL"). 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 188c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 198c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 208c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 218c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 228c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 238c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 248c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 258c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 268c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 278c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Fushicai USBTV007 Audio-Video Grabber Driver 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Product web site: 338c2ecf20Sopenharmony_ci * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Following LWN articles were very useful in construction of this driver: 368c2ecf20Sopenharmony_ci * Video4Linux2 API series: http://lwn.net/Articles/203924/ 378c2ecf20Sopenharmony_ci * videobuf2 API explanation: http://lwn.net/Articles/447435/ 388c2ecf20Sopenharmony_ci * Thanks go to Jonathan Corbet for providing this quality documentation. 398c2ecf20Sopenharmony_ci * He is awesome. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * No physical hardware was harmed running Windows during the 428c2ecf20Sopenharmony_ci * reverse-engineering activity 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 468c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "usbtv.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct usbtv_norm_params norm_params[] = { 518c2ecf20Sopenharmony_ci { 528c2ecf20Sopenharmony_ci .norm = V4L2_STD_525_60, 538c2ecf20Sopenharmony_ci .cap_width = 720, 548c2ecf20Sopenharmony_ci .cap_height = 480, 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci { 578c2ecf20Sopenharmony_ci .norm = V4L2_STD_625_50, 588c2ecf20Sopenharmony_ci .cap_width = 720, 598c2ecf20Sopenharmony_ci .cap_height = 576, 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int i, ret = 0; 668c2ecf20Sopenharmony_ci struct usbtv_norm_params *params = NULL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(norm_params); i++) { 698c2ecf20Sopenharmony_ci if (norm_params[i].norm & norm) { 708c2ecf20Sopenharmony_ci params = &norm_params[i]; 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (params) { 768c2ecf20Sopenharmony_ci usbtv->width = params->cap_width; 778c2ecf20Sopenharmony_ci usbtv->height = params->cap_height; 788c2ecf20Sopenharmony_ci usbtv->n_chunks = usbtv->width * usbtv->height 798c2ecf20Sopenharmony_ci / 4 / USBTV_CHUNK; 808c2ecf20Sopenharmony_ci usbtv->norm = norm; 818c2ecf20Sopenharmony_ci } else 828c2ecf20Sopenharmony_ci ret = -EINVAL; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int usbtv_select_input(struct usbtv *usbtv, int input) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int ret; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci static const u16 composite[][2] = { 928c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0105, 0x0060 }, 938c2ecf20Sopenharmony_ci { USBTV_BASE + 0x011f, 0x00f2 }, 948c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0127, 0x0060 }, 958c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00ae, 0x0010 }, 968c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0239, 0x0060 }, 978c2ecf20Sopenharmony_ci }; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci static const u16 svideo[][2] = { 1008c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0105, 0x0010 }, 1018c2ecf20Sopenharmony_ci { USBTV_BASE + 0x011f, 0x00ff }, 1028c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0127, 0x0060 }, 1038c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00ae, 0x0030 }, 1048c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0239, 0x0060 }, 1058c2ecf20Sopenharmony_ci }; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci switch (input) { 1088c2ecf20Sopenharmony_ci case USBTV_COMPOSITE_INPUT: 1098c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, composite, ARRAY_SIZE(composite)); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case USBTV_SVIDEO_INPUT: 1128c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, svideo, ARRAY_SIZE(svideo)); 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci default: 1158c2ecf20Sopenharmony_ci ret = -EINVAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (!ret) 1198c2ecf20Sopenharmony_ci usbtv->input = input; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return ret; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic uint16_t usbtv_norm_to_16f_reg(v4l2_std_id norm) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci /* NTSC M/M-JP/M-KR */ 1278c2ecf20Sopenharmony_ci if (norm & V4L2_STD_NTSC) 1288c2ecf20Sopenharmony_ci return 0x00b8; 1298c2ecf20Sopenharmony_ci /* PAL BG/DK/H/I */ 1308c2ecf20Sopenharmony_ci if (norm & V4L2_STD_PAL) 1318c2ecf20Sopenharmony_ci return 0x00ee; 1328c2ecf20Sopenharmony_ci /* SECAM B/D/G/H/K/K1/L/Lc */ 1338c2ecf20Sopenharmony_ci if (norm & V4L2_STD_SECAM) 1348c2ecf20Sopenharmony_ci return 0x00ff; 1358c2ecf20Sopenharmony_ci if (norm & V4L2_STD_NTSC_443) 1368c2ecf20Sopenharmony_ci return 0x00a8; 1378c2ecf20Sopenharmony_ci if (norm & (V4L2_STD_PAL_M | V4L2_STD_PAL_60)) 1388c2ecf20Sopenharmony_ci return 0x00bc; 1398c2ecf20Sopenharmony_ci /* Fallback to automatic detection for other standards */ 1408c2ecf20Sopenharmony_ci return 0x0000; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int usbtv_select_norm(struct usbtv *usbtv, v4l2_std_id norm) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci /* These are the series of register values used to configure the 1478c2ecf20Sopenharmony_ci * decoder for a specific standard. 1488c2ecf20Sopenharmony_ci * The first 21 register writes are copied from the 1498c2ecf20Sopenharmony_ci * Settings\DecoderDefaults registry keys present in the Windows driver 1508c2ecf20Sopenharmony_ci * .INF file, and control various image tuning parameters (color 1518c2ecf20Sopenharmony_ci * correction, sharpness, ...). 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci static const u16 pal[][2] = { 1548c2ecf20Sopenharmony_ci /* "AVPAL" tuning sequence from .INF file */ 1558c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0003, 0x0004 }, 1568c2ecf20Sopenharmony_ci { USBTV_BASE + 0x001a, 0x0068 }, 1578c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0100, 0x00d3 }, 1588c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010e, 0x0072 }, 1598c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010f, 0x00a2 }, 1608c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0112, 0x00b0 }, 1618c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0115, 0x0015 }, 1628c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0117, 0x0001 }, 1638c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0118, 0x002c }, 1648c2ecf20Sopenharmony_ci { USBTV_BASE + 0x012d, 0x0010 }, 1658c2ecf20Sopenharmony_ci { USBTV_BASE + 0x012f, 0x0020 }, 1668c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0220, 0x002e }, 1678c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0225, 0x0008 }, 1688c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 1698c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 1708c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0254, 0x0059 }, 1718c2ecf20Sopenharmony_ci { USBTV_BASE + 0x025a, 0x0016 }, 1728c2ecf20Sopenharmony_ci { USBTV_BASE + 0x025b, 0x0035 }, 1738c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0263, 0x0017 }, 1748c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0266, 0x0016 }, 1758c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0267, 0x0036 }, 1768c2ecf20Sopenharmony_ci /* End image tuning */ 1778c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 1788c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 1798c2ecf20Sopenharmony_ci }; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci static const u16 ntsc[][2] = { 1828c2ecf20Sopenharmony_ci /* "AVNTSC" tuning sequence from .INF file */ 1838c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0003, 0x0004 }, 1848c2ecf20Sopenharmony_ci { USBTV_BASE + 0x001a, 0x0079 }, 1858c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0100, 0x00d3 }, 1868c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010e, 0x0068 }, 1878c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010f, 0x009c }, 1888c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0112, 0x00f0 }, 1898c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0115, 0x0015 }, 1908c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0117, 0x0000 }, 1918c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0118, 0x00fc }, 1928c2ecf20Sopenharmony_ci { USBTV_BASE + 0x012d, 0x0004 }, 1938c2ecf20Sopenharmony_ci { USBTV_BASE + 0x012f, 0x0008 }, 1948c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0220, 0x002e }, 1958c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0225, 0x0008 }, 1968c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 1978c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0001 }, 1988c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0254, 0x005f }, 1998c2ecf20Sopenharmony_ci { USBTV_BASE + 0x025a, 0x0012 }, 2008c2ecf20Sopenharmony_ci { USBTV_BASE + 0x025b, 0x0001 }, 2018c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0263, 0x001c }, 2028c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0266, 0x0011 }, 2038c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0267, 0x0005 }, 2048c2ecf20Sopenharmony_ci /* End image tuning */ 2058c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 2068c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 2078c2ecf20Sopenharmony_ci }; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci static const u16 secam[][2] = { 2108c2ecf20Sopenharmony_ci /* "AVSECAM" tuning sequence from .INF file */ 2118c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0003, 0x0004 }, 2128c2ecf20Sopenharmony_ci { USBTV_BASE + 0x001a, 0x0073 }, 2138c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0100, 0x00dc }, 2148c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010e, 0x0072 }, 2158c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010f, 0x00a2 }, 2168c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0112, 0x0090 }, 2178c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0115, 0x0035 }, 2188c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0117, 0x0001 }, 2198c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0118, 0x0030 }, 2208c2ecf20Sopenharmony_ci { USBTV_BASE + 0x012d, 0x0004 }, 2218c2ecf20Sopenharmony_ci { USBTV_BASE + 0x012f, 0x0008 }, 2228c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0220, 0x002d }, 2238c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0225, 0x0028 }, 2248c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0008 }, 2258c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 2268c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0254, 0x0069 }, 2278c2ecf20Sopenharmony_ci { USBTV_BASE + 0x025a, 0x0016 }, 2288c2ecf20Sopenharmony_ci { USBTV_BASE + 0x025b, 0x0035 }, 2298c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0263, 0x0021 }, 2308c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0266, 0x0016 }, 2318c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0267, 0x0036 }, 2328c2ecf20Sopenharmony_ci /* End image tuning */ 2338c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 2348c2ecf20Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 2358c2ecf20Sopenharmony_ci }; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ret = usbtv_configure_for_norm(usbtv, norm); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!ret) { 2408c2ecf20Sopenharmony_ci /* Masks for norms using a NTSC or PAL color encoding. */ 2418c2ecf20Sopenharmony_ci static const v4l2_std_id ntsc_mask = 2428c2ecf20Sopenharmony_ci V4L2_STD_NTSC | V4L2_STD_NTSC_443; 2438c2ecf20Sopenharmony_ci static const v4l2_std_id pal_mask = 2448c2ecf20Sopenharmony_ci V4L2_STD_PAL | V4L2_STD_PAL_60 | V4L2_STD_PAL_M; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (norm & ntsc_mask) 2478c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, ntsc, ARRAY_SIZE(ntsc)); 2488c2ecf20Sopenharmony_ci else if (norm & pal_mask) 2498c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, pal, ARRAY_SIZE(pal)); 2508c2ecf20Sopenharmony_ci else if (norm & V4L2_STD_SECAM) 2518c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, secam, ARRAY_SIZE(secam)); 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci ret = -EINVAL; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!ret) { 2578c2ecf20Sopenharmony_ci /* Configure the decoder for the color standard */ 2588c2ecf20Sopenharmony_ci const u16 cfg[][2] = { 2598c2ecf20Sopenharmony_ci { USBTV_BASE + 0x016f, usbtv_norm_to_16f_reg(norm) } 2608c2ecf20Sopenharmony_ci }; 2618c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, cfg, ARRAY_SIZE(cfg)); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int usbtv_setup_capture(struct usbtv *usbtv) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int ret; 2708c2ecf20Sopenharmony_ci static const u16 setup[][2] = { 2718c2ecf20Sopenharmony_ci /* These seem to enable the device. */ 2728c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0008, 0x0001 }, 2738c2ecf20Sopenharmony_ci { USBTV_BASE + 0x01d0, 0x00ff }, 2748c2ecf20Sopenharmony_ci { USBTV_BASE + 0x01d9, 0x0002 }, 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* These seem to influence color parameters, such as 2778c2ecf20Sopenharmony_ci * brightness, etc. */ 2788c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0239, 0x0040 }, 2798c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0240, 0x0000 }, 2808c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0241, 0x0000 }, 2818c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0242, 0x0002 }, 2828c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0243, 0x0080 }, 2838c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0244, 0x0012 }, 2848c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0245, 0x0090 }, 2858c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0246, 0x0000 }, 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0278, 0x002d }, 2888c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0279, 0x000a }, 2898c2ecf20Sopenharmony_ci { USBTV_BASE + 0x027a, 0x0032 }, 2908c2ecf20Sopenharmony_ci { 0xf890, 0x000c }, 2918c2ecf20Sopenharmony_ci { 0xf894, 0x0086 }, 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00ac, 0x00c0 }, 2948c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00ad, 0x0000 }, 2958c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00a2, 0x0012 }, 2968c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00a3, 0x00e0 }, 2978c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00a4, 0x0028 }, 2988c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00a5, 0x0082 }, 2998c2ecf20Sopenharmony_ci { USBTV_BASE + 0x00a7, 0x0080 }, 3008c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0000, 0x0014 }, 3018c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0006, 0x0003 }, 3028c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0090, 0x0099 }, 3038c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0091, 0x0090 }, 3048c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0094, 0x0068 }, 3058c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0095, 0x0070 }, 3068c2ecf20Sopenharmony_ci { USBTV_BASE + 0x009c, 0x0030 }, 3078c2ecf20Sopenharmony_ci { USBTV_BASE + 0x009d, 0x00c0 }, 3088c2ecf20Sopenharmony_ci { USBTV_BASE + 0x009e, 0x00e0 }, 3098c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0019, 0x0006 }, 3108c2ecf20Sopenharmony_ci { USBTV_BASE + 0x008c, 0x00ba }, 3118c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0101, 0x00ff }, 3128c2ecf20Sopenharmony_ci { USBTV_BASE + 0x010c, 0x00b3 }, 3138c2ecf20Sopenharmony_ci { USBTV_BASE + 0x01b2, 0x0080 }, 3148c2ecf20Sopenharmony_ci { USBTV_BASE + 0x01b4, 0x00a0 }, 3158c2ecf20Sopenharmony_ci { USBTV_BASE + 0x014c, 0x00ff }, 3168c2ecf20Sopenharmony_ci { USBTV_BASE + 0x014d, 0x00ca }, 3178c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0113, 0x0053 }, 3188c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0119, 0x008a }, 3198c2ecf20Sopenharmony_ci { USBTV_BASE + 0x013c, 0x0003 }, 3208c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0150, 0x009c }, 3218c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0151, 0x0071 }, 3228c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0152, 0x00c6 }, 3238c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0153, 0x0084 }, 3248c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0154, 0x00bc }, 3258c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0155, 0x00a0 }, 3268c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0156, 0x00a0 }, 3278c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0157, 0x009c }, 3288c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0158, 0x001f }, 3298c2ecf20Sopenharmony_ci { USBTV_BASE + 0x0159, 0x0006 }, 3308c2ecf20Sopenharmony_ci { USBTV_BASE + 0x015d, 0x0000 }, 3318c2ecf20Sopenharmony_ci }; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = usbtv_set_regs(usbtv, setup, ARRAY_SIZE(setup)); 3348c2ecf20Sopenharmony_ci if (ret) 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = usbtv_select_norm(usbtv, usbtv->norm); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = usbtv_select_input(usbtv, usbtv->input); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&usbtv->ctrl); 3468c2ecf20Sopenharmony_ci if (ret) 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* Copy data from chunk into a frame buffer, deinterlacing the data 3538c2ecf20Sopenharmony_ci * into every second line. Unfortunately, they don't align nicely into 3548c2ecf20Sopenharmony_ci * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels. 3558c2ecf20Sopenharmony_ci * Therefore, we break down the chunk into two halves before copying, 3568c2ecf20Sopenharmony_ci * so that we can interleave a line if needed. 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * Each "chunk" is 240 words; a word in this context equals 4 bytes. 3598c2ecf20Sopenharmony_ci * Image format is YUYV/YUV 4:2:2, consisting of Y Cr Y Cb, defining two 3608c2ecf20Sopenharmony_ci * pixels, the Cr and Cb shared between the two pixels, but each having 3618c2ecf20Sopenharmony_ci * separate Y values. Thus, the 240 words equal 480 pixels. It therefore, 3628c2ecf20Sopenharmony_ci * takes 1.5 chunks to make a 720 pixel-wide line for the frame. 3638c2ecf20Sopenharmony_ci * The image is interlaced, so there is a "scan" of odd lines, followed 3648c2ecf20Sopenharmony_ci * by "scan" of even numbered lines. 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * Following code is writing the chunks in correct sequence, skipping 3678c2ecf20Sopenharmony_ci * the rows based on "odd" value. 3688c2ecf20Sopenharmony_ci * line 1: chunk[0][ 0..479] chunk[0][480..959] chunk[1][ 0..479] 3698c2ecf20Sopenharmony_ci * line 3: chunk[1][480..959] chunk[2][ 0..479] chunk[2][480..959] 3708c2ecf20Sopenharmony_ci * ...etc. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_cistatic void usbtv_chunk_to_vbuf(u32 *frame, __be32 *src, int chunk_no, int odd) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci int half; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (half = 0; half < 2; half++) { 3778c2ecf20Sopenharmony_ci int part_no = chunk_no * 2 + half; 3788c2ecf20Sopenharmony_ci int line = part_no / 3; 3798c2ecf20Sopenharmony_ci int part_index = (line * 2 + !odd) * 3 + (part_no % 3); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci u32 *dst = &frame[part_index * USBTV_CHUNK/2]; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci memcpy(dst, src, USBTV_CHUNK/2 * sizeof(*src)); 3848c2ecf20Sopenharmony_ci src += USBTV_CHUNK/2; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/* Called for each 256-byte image chunk. 3898c2ecf20Sopenharmony_ci * First word identifies the chunk, followed by 240 words of image 3908c2ecf20Sopenharmony_ci * data and padding. */ 3918c2ecf20Sopenharmony_cistatic void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci int frame_id, odd, chunk_no; 3948c2ecf20Sopenharmony_ci u32 *frame; 3958c2ecf20Sopenharmony_ci struct usbtv_buf *buf; 3968c2ecf20Sopenharmony_ci unsigned long flags; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Ignore corrupted lines. */ 3998c2ecf20Sopenharmony_ci if (!USBTV_MAGIC_OK(chunk)) 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci frame_id = USBTV_FRAME_ID(chunk); 4028c2ecf20Sopenharmony_ci odd = USBTV_ODD(chunk); 4038c2ecf20Sopenharmony_ci chunk_no = USBTV_CHUNK_NO(chunk); 4048c2ecf20Sopenharmony_ci if (chunk_no >= usbtv->n_chunks) 4058c2ecf20Sopenharmony_ci return; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* Beginning of a frame. */ 4088c2ecf20Sopenharmony_ci if (chunk_no == 0) { 4098c2ecf20Sopenharmony_ci usbtv->frame_id = frame_id; 4108c2ecf20Sopenharmony_ci usbtv->chunks_done = 0; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (usbtv->frame_id != frame_id) 4148c2ecf20Sopenharmony_ci return; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci spin_lock_irqsave(&usbtv->buflock, flags); 4178c2ecf20Sopenharmony_ci if (list_empty(&usbtv->bufs)) { 4188c2ecf20Sopenharmony_ci /* No free buffers. Userspace likely too slow. */ 4198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 4208c2ecf20Sopenharmony_ci return; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* First available buffer. */ 4248c2ecf20Sopenharmony_ci buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list); 4258c2ecf20Sopenharmony_ci frame = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Copy the chunk data. */ 4288c2ecf20Sopenharmony_ci usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd); 4298c2ecf20Sopenharmony_ci usbtv->chunks_done++; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Last chunk in a field */ 4328c2ecf20Sopenharmony_ci if (chunk_no == usbtv->n_chunks-1) { 4338c2ecf20Sopenharmony_ci /* Last chunk in a frame, signalling an end */ 4348c2ecf20Sopenharmony_ci if (odd && !usbtv->last_odd) { 4358c2ecf20Sopenharmony_ci int size = vb2_plane_size(&buf->vb.vb2_buf, 0); 4368c2ecf20Sopenharmony_ci enum vb2_buffer_state state = usbtv->chunks_done == 4378c2ecf20Sopenharmony_ci usbtv->n_chunks ? 4388c2ecf20Sopenharmony_ci VB2_BUF_STATE_DONE : 4398c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci buf->vb.field = V4L2_FIELD_INTERLACED; 4428c2ecf20Sopenharmony_ci buf->vb.sequence = usbtv->sequence++; 4438c2ecf20Sopenharmony_ci buf->vb.vb2_buf.timestamp = ktime_get_ns(); 4448c2ecf20Sopenharmony_ci vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); 4458c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 4468c2ecf20Sopenharmony_ci list_del(&buf->list); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci usbtv->last_odd = odd; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* Got image data. Each packet contains a number of 256-word chunks we 4558c2ecf20Sopenharmony_ci * compose the image from. */ 4568c2ecf20Sopenharmony_cistatic void usbtv_iso_cb(struct urb *ip) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci int ret; 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci struct usbtv *usbtv = (struct usbtv *)ip->context; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci switch (ip->status) { 4638c2ecf20Sopenharmony_ci /* All fine. */ 4648c2ecf20Sopenharmony_ci case 0: 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci /* Device disconnected or capture stopped? */ 4678c2ecf20Sopenharmony_ci case -ENODEV: 4688c2ecf20Sopenharmony_ci case -ENOENT: 4698c2ecf20Sopenharmony_ci case -ECONNRESET: 4708c2ecf20Sopenharmony_ci case -ESHUTDOWN: 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci /* Unknown error. Retry. */ 4738c2ecf20Sopenharmony_ci default: 4748c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Bad response for ISO request.\n"); 4758c2ecf20Sopenharmony_ci goto resubmit; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for (i = 0; i < ip->number_of_packets; i++) { 4798c2ecf20Sopenharmony_ci int size = ip->iso_frame_desc[i].actual_length; 4808c2ecf20Sopenharmony_ci unsigned char *data = ip->transfer_buffer + 4818c2ecf20Sopenharmony_ci ip->iso_frame_desc[i].offset; 4828c2ecf20Sopenharmony_ci int offset; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci for (offset = 0; USBTV_CHUNK_SIZE * offset < size; offset++) 4858c2ecf20Sopenharmony_ci usbtv_image_chunk(usbtv, 4868c2ecf20Sopenharmony_ci (__be32 *)&data[USBTV_CHUNK_SIZE * offset]); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ciresubmit: 4908c2ecf20Sopenharmony_ci ret = usb_submit_urb(ip, GFP_ATOMIC); 4918c2ecf20Sopenharmony_ci if (ret < 0) 4928c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Could not resubmit ISO URB\n"); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic struct urb *usbtv_setup_iso_transfer(struct usbtv *usbtv) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct urb *ip; 4988c2ecf20Sopenharmony_ci int size = usbtv->iso_size; 4998c2ecf20Sopenharmony_ci int i; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ip = usb_alloc_urb(USBTV_ISOC_PACKETS, GFP_KERNEL); 5028c2ecf20Sopenharmony_ci if (ip == NULL) 5038c2ecf20Sopenharmony_ci return NULL; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci ip->dev = usbtv->udev; 5068c2ecf20Sopenharmony_ci ip->context = usbtv; 5078c2ecf20Sopenharmony_ci ip->pipe = usb_rcvisocpipe(usbtv->udev, USBTV_VIDEO_ENDP); 5088c2ecf20Sopenharmony_ci ip->interval = 1; 5098c2ecf20Sopenharmony_ci ip->transfer_flags = URB_ISO_ASAP; 5108c2ecf20Sopenharmony_ci ip->transfer_buffer = kcalloc(USBTV_ISOC_PACKETS, size, 5118c2ecf20Sopenharmony_ci GFP_KERNEL); 5128c2ecf20Sopenharmony_ci if (!ip->transfer_buffer) { 5138c2ecf20Sopenharmony_ci usb_free_urb(ip); 5148c2ecf20Sopenharmony_ci return NULL; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci ip->complete = usbtv_iso_cb; 5178c2ecf20Sopenharmony_ci ip->number_of_packets = USBTV_ISOC_PACKETS; 5188c2ecf20Sopenharmony_ci ip->transfer_buffer_length = size * USBTV_ISOC_PACKETS; 5198c2ecf20Sopenharmony_ci for (i = 0; i < USBTV_ISOC_PACKETS; i++) { 5208c2ecf20Sopenharmony_ci ip->iso_frame_desc[i].offset = size * i; 5218c2ecf20Sopenharmony_ci ip->iso_frame_desc[i].length = size; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return ip; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void usbtv_stop(struct usbtv *usbtv) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int i; 5308c2ecf20Sopenharmony_ci unsigned long flags; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* Cancel running transfers. */ 5338c2ecf20Sopenharmony_ci for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) { 5348c2ecf20Sopenharmony_ci struct urb *ip = usbtv->isoc_urbs[i]; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (ip == NULL) 5378c2ecf20Sopenharmony_ci continue; 5388c2ecf20Sopenharmony_ci usb_kill_urb(ip); 5398c2ecf20Sopenharmony_ci kfree(ip->transfer_buffer); 5408c2ecf20Sopenharmony_ci usb_free_urb(ip); 5418c2ecf20Sopenharmony_ci usbtv->isoc_urbs[i] = NULL; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Return buffers to userspace. */ 5458c2ecf20Sopenharmony_ci spin_lock_irqsave(&usbtv->buflock, flags); 5468c2ecf20Sopenharmony_ci while (!list_empty(&usbtv->bufs)) { 5478c2ecf20Sopenharmony_ci struct usbtv_buf *buf = list_first_entry(&usbtv->bufs, 5488c2ecf20Sopenharmony_ci struct usbtv_buf, list); 5498c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 5508c2ecf20Sopenharmony_ci list_del(&buf->list); 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int usbtv_start(struct usbtv *usbtv) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int i; 5588c2ecf20Sopenharmony_ci int ret; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci usbtv_audio_suspend(usbtv); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci ret = usb_set_interface(usbtv->udev, 0, 0); 5638c2ecf20Sopenharmony_ci if (ret < 0) 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci ret = usbtv_setup_capture(usbtv); 5678c2ecf20Sopenharmony_ci if (ret < 0) 5688c2ecf20Sopenharmony_ci return ret; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ret = usb_set_interface(usbtv->udev, 0, 1); 5718c2ecf20Sopenharmony_ci if (ret < 0) 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci usbtv_audio_resume(usbtv); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) { 5778c2ecf20Sopenharmony_ci struct urb *ip; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ip = usbtv_setup_iso_transfer(usbtv); 5808c2ecf20Sopenharmony_ci if (ip == NULL) { 5818c2ecf20Sopenharmony_ci ret = -ENOMEM; 5828c2ecf20Sopenharmony_ci goto start_fail; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci usbtv->isoc_urbs[i] = ip; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci ret = usb_submit_urb(ip, GFP_KERNEL); 5878c2ecf20Sopenharmony_ci if (ret < 0) 5888c2ecf20Sopenharmony_ci goto start_fail; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistart_fail: 5948c2ecf20Sopenharmony_ci usbtv_stop(usbtv); 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int usbtv_querycap(struct file *file, void *priv, 5998c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct usbtv *dev = video_drvdata(file); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci strscpy(cap->driver, "usbtv", sizeof(cap->driver)); 6048c2ecf20Sopenharmony_ci strscpy(cap->card, "usbtv", sizeof(cap->card)); 6058c2ecf20Sopenharmony_ci usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int usbtv_enum_input(struct file *file, void *priv, 6108c2ecf20Sopenharmony_ci struct v4l2_input *i) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct usbtv *dev = video_drvdata(file); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci switch (i->index) { 6158c2ecf20Sopenharmony_ci case USBTV_COMPOSITE_INPUT: 6168c2ecf20Sopenharmony_ci strscpy(i->name, "Composite", sizeof(i->name)); 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci case USBTV_SVIDEO_INPUT: 6198c2ecf20Sopenharmony_ci strscpy(i->name, "S-Video", sizeof(i->name)); 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci default: 6228c2ecf20Sopenharmony_ci return -EINVAL; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 6268c2ecf20Sopenharmony_ci i->std = dev->vdev.tvnorms; 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int usbtv_enum_fmt_vid_cap(struct file *file, void *priv, 6318c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci if (f->index > 0) 6348c2ecf20Sopenharmony_ci return -EINVAL; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUYV; 6378c2ecf20Sopenharmony_ci return 0; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int usbtv_fmt_vid_cap(struct file *file, void *priv, 6418c2ecf20Sopenharmony_ci struct v4l2_format *f) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci f->fmt.pix.width = usbtv->width; 6468c2ecf20Sopenharmony_ci f->fmt.pix.height = usbtv->height; 6478c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 6488c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 6498c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = usbtv->width * 2; 6508c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = (f->fmt.pix.bytesperline * f->fmt.pix.height); 6518c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int usbtv_g_std(struct file *file, void *priv, v4l2_std_id *norm) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 6598c2ecf20Sopenharmony_ci *norm = usbtv->norm; 6608c2ecf20Sopenharmony_ci return 0; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci int ret = -EINVAL; 6668c2ecf20Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (norm & USBTV_TV_STD) 6698c2ecf20Sopenharmony_ci ret = usbtv_select_norm(usbtv, norm); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return ret; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int usbtv_g_input(struct file *file, void *priv, unsigned int *i) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 6778c2ecf20Sopenharmony_ci *i = usbtv->input; 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic int usbtv_s_input(struct file *file, void *priv, unsigned int i) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return usbtv_select_input(usbtv, i); 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic struct v4l2_ioctl_ops usbtv_ioctl_ops = { 6898c2ecf20Sopenharmony_ci .vidioc_querycap = usbtv_querycap, 6908c2ecf20Sopenharmony_ci .vidioc_enum_input = usbtv_enum_input, 6918c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = usbtv_enum_fmt_vid_cap, 6928c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = usbtv_fmt_vid_cap, 6938c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = usbtv_fmt_vid_cap, 6948c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = usbtv_fmt_vid_cap, 6958c2ecf20Sopenharmony_ci .vidioc_g_std = usbtv_g_std, 6968c2ecf20Sopenharmony_ci .vidioc_s_std = usbtv_s_std, 6978c2ecf20Sopenharmony_ci .vidioc_g_input = usbtv_g_input, 6988c2ecf20Sopenharmony_ci .vidioc_s_input = usbtv_s_input, 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 7018c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 7028c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 7038c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 7048c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 7058c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 7068c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 7078c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 7088c2ecf20Sopenharmony_ci}; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations usbtv_fops = { 7118c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7128c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 7138c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 7148c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 7158c2ecf20Sopenharmony_ci .release = vb2_fop_release, 7168c2ecf20Sopenharmony_ci .read = vb2_fop_read, 7178c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 7188c2ecf20Sopenharmony_ci}; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int usbtv_queue_setup(struct vb2_queue *vq, 7218c2ecf20Sopenharmony_ci unsigned int *nbuffers, 7228c2ecf20Sopenharmony_ci unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vq); 7258c2ecf20Sopenharmony_ci unsigned size = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 7288c2ecf20Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 7298c2ecf20Sopenharmony_ci if (*nplanes) 7308c2ecf20Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 7318c2ecf20Sopenharmony_ci *nplanes = 1; 7328c2ecf20Sopenharmony_ci sizes[0] = size; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return 0; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic void usbtv_buf_queue(struct vb2_buffer *vb) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 7408c2ecf20Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vb->vb2_queue); 7418c2ecf20Sopenharmony_ci struct usbtv_buf *buf = container_of(vbuf, struct usbtv_buf, vb); 7428c2ecf20Sopenharmony_ci unsigned long flags; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (usbtv->udev == NULL) { 7458c2ecf20Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 7468c2ecf20Sopenharmony_ci return; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci spin_lock_irqsave(&usbtv->buflock, flags); 7508c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &usbtv->bufs); 7518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vq); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (usbtv->udev == NULL) 7598c2ecf20Sopenharmony_ci return -ENODEV; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci usbtv->last_odd = 1; 7628c2ecf20Sopenharmony_ci usbtv->sequence = 0; 7638c2ecf20Sopenharmony_ci return usbtv_start(usbtv); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic void usbtv_stop_streaming(struct vb2_queue *vq) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vq); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (usbtv->udev) 7718c2ecf20Sopenharmony_ci usbtv_stop(usbtv); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic const struct vb2_ops usbtv_vb2_ops = { 7758c2ecf20Sopenharmony_ci .queue_setup = usbtv_queue_setup, 7768c2ecf20Sopenharmony_ci .buf_queue = usbtv_buf_queue, 7778c2ecf20Sopenharmony_ci .start_streaming = usbtv_start_streaming, 7788c2ecf20Sopenharmony_ci .stop_streaming = usbtv_stop_streaming, 7798c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 7808c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 7818c2ecf20Sopenharmony_ci}; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct usbtv *usbtv = container_of(ctrl->handler, struct usbtv, 7868c2ecf20Sopenharmony_ci ctrl); 7878c2ecf20Sopenharmony_ci u8 *data; 7888c2ecf20Sopenharmony_ci u16 index, size; 7898c2ecf20Sopenharmony_ci int ret; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci data = kmalloc(3, GFP_KERNEL); 7928c2ecf20Sopenharmony_ci if (!data) 7938c2ecf20Sopenharmony_ci return -ENOMEM; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * Read in the current brightness/contrast registers. We need them 7978c2ecf20Sopenharmony_ci * both, because the values are for some reason interleaved. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_ci if (ctrl->id == V4L2_CID_BRIGHTNESS || ctrl->id == V4L2_CID_CONTRAST) { 8008c2ecf20Sopenharmony_ci ret = usb_control_msg(usbtv->udev, 8018c2ecf20Sopenharmony_ci usb_rcvctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, 8028c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 8038c2ecf20Sopenharmony_ci 0, USBTV_BASE + 0x0244, (void *)data, 3, 8048c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 8058c2ecf20Sopenharmony_ci if (ret < 0) 8068c2ecf20Sopenharmony_ci goto error; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci switch (ctrl->id) { 8108c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 8118c2ecf20Sopenharmony_ci index = USBTV_BASE + 0x0244; 8128c2ecf20Sopenharmony_ci size = 3; 8138c2ecf20Sopenharmony_ci data[0] &= 0xf0; 8148c2ecf20Sopenharmony_ci data[0] |= (ctrl->val >> 8) & 0xf; 8158c2ecf20Sopenharmony_ci data[2] = ctrl->val & 0xff; 8168c2ecf20Sopenharmony_ci break; 8178c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 8188c2ecf20Sopenharmony_ci index = USBTV_BASE + 0x0244; 8198c2ecf20Sopenharmony_ci size = 3; 8208c2ecf20Sopenharmony_ci data[0] &= 0x0f; 8218c2ecf20Sopenharmony_ci data[0] |= (ctrl->val >> 4) & 0xf0; 8228c2ecf20Sopenharmony_ci data[1] = ctrl->val & 0xff; 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 8258c2ecf20Sopenharmony_ci index = USBTV_BASE + 0x0242; 8268c2ecf20Sopenharmony_ci data[0] = ctrl->val >> 8; 8278c2ecf20Sopenharmony_ci data[1] = ctrl->val & 0xff; 8288c2ecf20Sopenharmony_ci size = 2; 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci case V4L2_CID_HUE: 8318c2ecf20Sopenharmony_ci index = USBTV_BASE + 0x0240; 8328c2ecf20Sopenharmony_ci size = 2; 8338c2ecf20Sopenharmony_ci if (ctrl->val > 0) { 8348c2ecf20Sopenharmony_ci data[0] = 0x92 + (ctrl->val >> 8); 8358c2ecf20Sopenharmony_ci data[1] = ctrl->val & 0xff; 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci data[0] = 0x82 + (-ctrl->val >> 8); 8388c2ecf20Sopenharmony_ci data[1] = -ctrl->val & 0xff; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci break; 8418c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 8428c2ecf20Sopenharmony_ci index = USBTV_BASE + 0x0239; 8438c2ecf20Sopenharmony_ci data[0] = 0; 8448c2ecf20Sopenharmony_ci data[1] = ctrl->val; 8458c2ecf20Sopenharmony_ci size = 2; 8468c2ecf20Sopenharmony_ci break; 8478c2ecf20Sopenharmony_ci default: 8488c2ecf20Sopenharmony_ci kfree(data); 8498c2ecf20Sopenharmony_ci return -EINVAL; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci ret = usb_control_msg(usbtv->udev, usb_sndctrlpipe(usbtv->udev, 0), 8538c2ecf20Sopenharmony_ci USBTV_CONTROL_REG, 8548c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 8558c2ecf20Sopenharmony_ci 0, index, (void *)data, size, USB_CTRL_SET_TIMEOUT); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cierror: 8588c2ecf20Sopenharmony_ci if (ret < 0) 8598c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Failed to submit a control request.\n"); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci kfree(data); 8628c2ecf20Sopenharmony_ci return ret; 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops usbtv_ctrl_ops = { 8668c2ecf20Sopenharmony_ci .s_ctrl = usbtv_s_ctrl, 8678c2ecf20Sopenharmony_ci}; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cistatic void usbtv_release(struct v4l2_device *v4l2_dev) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci struct usbtv *usbtv = container_of(v4l2_dev, struct usbtv, v4l2_dev); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci v4l2_device_unregister(&usbtv->v4l2_dev); 8748c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&usbtv->ctrl); 8758c2ecf20Sopenharmony_ci kfree(usbtv); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ciint usbtv_video_init(struct usbtv *usbtv) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci int ret; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci (void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci spin_lock_init(&usbtv->buflock); 8858c2ecf20Sopenharmony_ci mutex_init(&usbtv->v4l2_lock); 8868c2ecf20Sopenharmony_ci mutex_init(&usbtv->vb2q_lock); 8878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&usbtv->bufs); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* videobuf2 structure */ 8908c2ecf20Sopenharmony_ci usbtv->vb2q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 8918c2ecf20Sopenharmony_ci usbtv->vb2q.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; 8928c2ecf20Sopenharmony_ci usbtv->vb2q.drv_priv = usbtv; 8938c2ecf20Sopenharmony_ci usbtv->vb2q.buf_struct_size = sizeof(struct usbtv_buf); 8948c2ecf20Sopenharmony_ci usbtv->vb2q.ops = &usbtv_vb2_ops; 8958c2ecf20Sopenharmony_ci usbtv->vb2q.mem_ops = &vb2_vmalloc_memops; 8968c2ecf20Sopenharmony_ci usbtv->vb2q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 8978c2ecf20Sopenharmony_ci usbtv->vb2q.lock = &usbtv->vb2q_lock; 8988c2ecf20Sopenharmony_ci ret = vb2_queue_init(&usbtv->vb2q); 8998c2ecf20Sopenharmony_ci if (ret < 0) { 9008c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Could not initialize videobuf2 queue\n"); 9018c2ecf20Sopenharmony_ci return ret; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* controls */ 9058c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&usbtv->ctrl, 4); 9068c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 9078c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 0, 0x3ff, 1, 0x1d0); 9088c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 9098c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 0x3ff, 1, 0x1c0); 9108c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 9118c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, 0, 0x3ff, 1, 0x200); 9128c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 9138c2ecf20Sopenharmony_ci V4L2_CID_HUE, -0xdff, 0xdff, 1, 0x000); 9148c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 9158c2ecf20Sopenharmony_ci V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x60); 9168c2ecf20Sopenharmony_ci ret = usbtv->ctrl.error; 9178c2ecf20Sopenharmony_ci if (ret < 0) { 9188c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Could not initialize controls\n"); 9198c2ecf20Sopenharmony_ci goto ctrl_fail; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* v4l2 structure */ 9238c2ecf20Sopenharmony_ci usbtv->v4l2_dev.ctrl_handler = &usbtv->ctrl; 9248c2ecf20Sopenharmony_ci usbtv->v4l2_dev.release = usbtv_release; 9258c2ecf20Sopenharmony_ci ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev); 9268c2ecf20Sopenharmony_ci if (ret < 0) { 9278c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Could not register v4l2 device\n"); 9288c2ecf20Sopenharmony_ci goto v4l2_fail; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Video structure */ 9328c2ecf20Sopenharmony_ci strscpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name)); 9338c2ecf20Sopenharmony_ci usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev; 9348c2ecf20Sopenharmony_ci usbtv->vdev.release = video_device_release_empty; 9358c2ecf20Sopenharmony_ci usbtv->vdev.fops = &usbtv_fops; 9368c2ecf20Sopenharmony_ci usbtv->vdev.ioctl_ops = &usbtv_ioctl_ops; 9378c2ecf20Sopenharmony_ci usbtv->vdev.tvnorms = USBTV_TV_STD; 9388c2ecf20Sopenharmony_ci usbtv->vdev.queue = &usbtv->vb2q; 9398c2ecf20Sopenharmony_ci usbtv->vdev.lock = &usbtv->v4l2_lock; 9408c2ecf20Sopenharmony_ci usbtv->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | 9418c2ecf20Sopenharmony_ci V4L2_CAP_STREAMING; 9428c2ecf20Sopenharmony_ci video_set_drvdata(&usbtv->vdev, usbtv); 9438c2ecf20Sopenharmony_ci ret = video_register_device(&usbtv->vdev, VFL_TYPE_VIDEO, -1); 9448c2ecf20Sopenharmony_ci if (ret < 0) { 9458c2ecf20Sopenharmony_ci dev_warn(usbtv->dev, "Could not register video device\n"); 9468c2ecf20Sopenharmony_ci goto vdev_fail; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci return 0; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_civdev_fail: 9528c2ecf20Sopenharmony_ci v4l2_device_unregister(&usbtv->v4l2_dev); 9538c2ecf20Sopenharmony_civ4l2_fail: 9548c2ecf20Sopenharmony_cictrl_fail: 9558c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&usbtv->ctrl); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci return ret; 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_civoid usbtv_video_free(struct usbtv *usbtv) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci mutex_lock(&usbtv->vb2q_lock); 9638c2ecf20Sopenharmony_ci mutex_lock(&usbtv->v4l2_lock); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci usbtv_stop(usbtv); 9668c2ecf20Sopenharmony_ci vb2_video_unregister_device(&usbtv->vdev); 9678c2ecf20Sopenharmony_ci v4l2_device_disconnect(&usbtv->v4l2_dev); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci mutex_unlock(&usbtv->v4l2_lock); 9708c2ecf20Sopenharmony_ci mutex_unlock(&usbtv->vb2q_lock); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci v4l2_device_put(&usbtv->v4l2_dev); 9738c2ecf20Sopenharmony_ci} 974