162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2013,2016 Lubomir Rintel 362306a36Sopenharmony_ci * All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 662306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 762306a36Sopenharmony_ci * are met: 862306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 962306a36Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 1062306a36Sopenharmony_ci * without modification. 1162306a36Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products 1262306a36Sopenharmony_ci * derived from this software without specific prior written permission. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 1562306a36Sopenharmony_ci * GNU General Public License ("GPL"). 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1862306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1962306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2062306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2162306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2262306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2362306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2462306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2562306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2662306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2762306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Fushicai USBTV007 Audio-Video Grabber Driver 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Product web site: 3362306a36Sopenharmony_ci * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Following LWN articles were very useful in construction of this driver: 3662306a36Sopenharmony_ci * Video4Linux2 API series: http://lwn.net/Articles/203924/ 3762306a36Sopenharmony_ci * videobuf2 API explanation: http://lwn.net/Articles/447435/ 3862306a36Sopenharmony_ci * Thanks go to Jonathan Corbet for providing this quality documentation. 3962306a36Sopenharmony_ci * He is awesome. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * No physical hardware was harmed running Windows during the 4262306a36Sopenharmony_ci * reverse-engineering activity 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 4662306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include "usbtv.h" 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct usbtv_norm_params norm_params[] = { 5162306a36Sopenharmony_ci { 5262306a36Sopenharmony_ci .norm = V4L2_STD_525_60, 5362306a36Sopenharmony_ci .cap_width = 720, 5462306a36Sopenharmony_ci .cap_height = 480, 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci { 5762306a36Sopenharmony_ci .norm = V4L2_STD_625_50, 5862306a36Sopenharmony_ci .cap_width = 720, 5962306a36Sopenharmony_ci .cap_height = 576, 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int i, ret = 0; 6662306a36Sopenharmony_ci const struct usbtv_norm_params *params = NULL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(norm_params); i++) { 6962306a36Sopenharmony_ci if (norm_params[i].norm & norm) { 7062306a36Sopenharmony_ci params = &norm_params[i]; 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (params) { 7662306a36Sopenharmony_ci usbtv->width = params->cap_width; 7762306a36Sopenharmony_ci usbtv->height = params->cap_height; 7862306a36Sopenharmony_ci usbtv->n_chunks = usbtv->width * usbtv->height 7962306a36Sopenharmony_ci / 4 / USBTV_CHUNK; 8062306a36Sopenharmony_ci usbtv->norm = norm; 8162306a36Sopenharmony_ci } else 8262306a36Sopenharmony_ci ret = -EINVAL; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int usbtv_select_input(struct usbtv *usbtv, int input) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int ret; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci static const u16 composite[][2] = { 9262306a36Sopenharmony_ci { USBTV_BASE + 0x0105, 0x0060 }, 9362306a36Sopenharmony_ci { USBTV_BASE + 0x011f, 0x00f2 }, 9462306a36Sopenharmony_ci { USBTV_BASE + 0x0127, 0x0060 }, 9562306a36Sopenharmony_ci { USBTV_BASE + 0x00ae, 0x0010 }, 9662306a36Sopenharmony_ci { USBTV_BASE + 0x0239, 0x0060 }, 9762306a36Sopenharmony_ci }; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci static const u16 svideo[][2] = { 10062306a36Sopenharmony_ci { USBTV_BASE + 0x0105, 0x0010 }, 10162306a36Sopenharmony_ci { USBTV_BASE + 0x011f, 0x00ff }, 10262306a36Sopenharmony_ci { USBTV_BASE + 0x0127, 0x0060 }, 10362306a36Sopenharmony_ci { USBTV_BASE + 0x00ae, 0x0030 }, 10462306a36Sopenharmony_ci { USBTV_BASE + 0x0239, 0x0060 }, 10562306a36Sopenharmony_ci }; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci switch (input) { 10862306a36Sopenharmony_ci case USBTV_COMPOSITE_INPUT: 10962306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, composite, ARRAY_SIZE(composite)); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci case USBTV_SVIDEO_INPUT: 11262306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, svideo, ARRAY_SIZE(svideo)); 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci default: 11562306a36Sopenharmony_ci ret = -EINVAL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!ret) 11962306a36Sopenharmony_ci usbtv->input = input; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic uint16_t usbtv_norm_to_16f_reg(v4l2_std_id norm) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci /* NTSC M/M-JP/M-KR */ 12762306a36Sopenharmony_ci if (norm & V4L2_STD_NTSC) 12862306a36Sopenharmony_ci return 0x00b8; 12962306a36Sopenharmony_ci /* PAL BG/DK/H/I */ 13062306a36Sopenharmony_ci if (norm & V4L2_STD_PAL) 13162306a36Sopenharmony_ci return 0x00ee; 13262306a36Sopenharmony_ci /* SECAM B/D/G/H/K/K1/L/Lc */ 13362306a36Sopenharmony_ci if (norm & V4L2_STD_SECAM) 13462306a36Sopenharmony_ci return 0x00ff; 13562306a36Sopenharmony_ci if (norm & V4L2_STD_NTSC_443) 13662306a36Sopenharmony_ci return 0x00a8; 13762306a36Sopenharmony_ci if (norm & (V4L2_STD_PAL_M | V4L2_STD_PAL_60)) 13862306a36Sopenharmony_ci return 0x00bc; 13962306a36Sopenharmony_ci if (norm & V4L2_STD_PAL_Nc) 14062306a36Sopenharmony_ci return 0x00fe; 14162306a36Sopenharmony_ci /* Fallback to automatic detection for other standards */ 14262306a36Sopenharmony_ci return 0x0000; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int usbtv_select_norm(struct usbtv *usbtv, v4l2_std_id norm) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int ret; 14862306a36Sopenharmony_ci /* These are the series of register values used to configure the 14962306a36Sopenharmony_ci * decoder for a specific standard. 15062306a36Sopenharmony_ci * The first 21 register writes are copied from the 15162306a36Sopenharmony_ci * Settings\DecoderDefaults registry keys present in the Windows driver 15262306a36Sopenharmony_ci * .INF file, and control various image tuning parameters (color 15362306a36Sopenharmony_ci * correction, sharpness, ...). 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci static const u16 pal[][2] = { 15662306a36Sopenharmony_ci /* "AVPAL" tuning sequence from .INF file */ 15762306a36Sopenharmony_ci { USBTV_BASE + 0x0003, 0x0004 }, 15862306a36Sopenharmony_ci { USBTV_BASE + 0x001a, 0x0068 }, 15962306a36Sopenharmony_ci { USBTV_BASE + 0x0100, 0x00d3 }, 16062306a36Sopenharmony_ci { USBTV_BASE + 0x010e, 0x0072 }, 16162306a36Sopenharmony_ci { USBTV_BASE + 0x010f, 0x00a2 }, 16262306a36Sopenharmony_ci { USBTV_BASE + 0x0112, 0x00b0 }, 16362306a36Sopenharmony_ci { USBTV_BASE + 0x0115, 0x0015 }, 16462306a36Sopenharmony_ci { USBTV_BASE + 0x0117, 0x0001 }, 16562306a36Sopenharmony_ci { USBTV_BASE + 0x0118, 0x002c }, 16662306a36Sopenharmony_ci { USBTV_BASE + 0x012d, 0x0010 }, 16762306a36Sopenharmony_ci { USBTV_BASE + 0x012f, 0x0020 }, 16862306a36Sopenharmony_ci { USBTV_BASE + 0x0220, 0x002e }, 16962306a36Sopenharmony_ci { USBTV_BASE + 0x0225, 0x0008 }, 17062306a36Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 17162306a36Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 17262306a36Sopenharmony_ci { USBTV_BASE + 0x0254, 0x0059 }, 17362306a36Sopenharmony_ci { USBTV_BASE + 0x025a, 0x0016 }, 17462306a36Sopenharmony_ci { USBTV_BASE + 0x025b, 0x0035 }, 17562306a36Sopenharmony_ci { USBTV_BASE + 0x0263, 0x0017 }, 17662306a36Sopenharmony_ci { USBTV_BASE + 0x0266, 0x0016 }, 17762306a36Sopenharmony_ci { USBTV_BASE + 0x0267, 0x0036 }, 17862306a36Sopenharmony_ci /* End image tuning */ 17962306a36Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 18062306a36Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 18162306a36Sopenharmony_ci }; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci static const u16 ntsc[][2] = { 18462306a36Sopenharmony_ci /* "AVNTSC" tuning sequence from .INF file */ 18562306a36Sopenharmony_ci { USBTV_BASE + 0x0003, 0x0004 }, 18662306a36Sopenharmony_ci { USBTV_BASE + 0x001a, 0x0079 }, 18762306a36Sopenharmony_ci { USBTV_BASE + 0x0100, 0x00d3 }, 18862306a36Sopenharmony_ci { USBTV_BASE + 0x010e, 0x0068 }, 18962306a36Sopenharmony_ci { USBTV_BASE + 0x010f, 0x009c }, 19062306a36Sopenharmony_ci { USBTV_BASE + 0x0112, 0x00f0 }, 19162306a36Sopenharmony_ci { USBTV_BASE + 0x0115, 0x0015 }, 19262306a36Sopenharmony_ci { USBTV_BASE + 0x0117, 0x0000 }, 19362306a36Sopenharmony_ci { USBTV_BASE + 0x0118, 0x00fc }, 19462306a36Sopenharmony_ci { USBTV_BASE + 0x012d, 0x0004 }, 19562306a36Sopenharmony_ci { USBTV_BASE + 0x012f, 0x0008 }, 19662306a36Sopenharmony_ci { USBTV_BASE + 0x0220, 0x002e }, 19762306a36Sopenharmony_ci { USBTV_BASE + 0x0225, 0x0008 }, 19862306a36Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 19962306a36Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0001 }, 20062306a36Sopenharmony_ci { USBTV_BASE + 0x0254, 0x005f }, 20162306a36Sopenharmony_ci { USBTV_BASE + 0x025a, 0x0012 }, 20262306a36Sopenharmony_ci { USBTV_BASE + 0x025b, 0x0001 }, 20362306a36Sopenharmony_ci { USBTV_BASE + 0x0263, 0x001c }, 20462306a36Sopenharmony_ci { USBTV_BASE + 0x0266, 0x0011 }, 20562306a36Sopenharmony_ci { USBTV_BASE + 0x0267, 0x0005 }, 20662306a36Sopenharmony_ci /* End image tuning */ 20762306a36Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 20862306a36Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 20962306a36Sopenharmony_ci }; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci static const u16 secam[][2] = { 21262306a36Sopenharmony_ci /* "AVSECAM" tuning sequence from .INF file */ 21362306a36Sopenharmony_ci { USBTV_BASE + 0x0003, 0x0004 }, 21462306a36Sopenharmony_ci { USBTV_BASE + 0x001a, 0x0073 }, 21562306a36Sopenharmony_ci { USBTV_BASE + 0x0100, 0x00dc }, 21662306a36Sopenharmony_ci { USBTV_BASE + 0x010e, 0x0072 }, 21762306a36Sopenharmony_ci { USBTV_BASE + 0x010f, 0x00a2 }, 21862306a36Sopenharmony_ci { USBTV_BASE + 0x0112, 0x0090 }, 21962306a36Sopenharmony_ci { USBTV_BASE + 0x0115, 0x0035 }, 22062306a36Sopenharmony_ci { USBTV_BASE + 0x0117, 0x0001 }, 22162306a36Sopenharmony_ci { USBTV_BASE + 0x0118, 0x0030 }, 22262306a36Sopenharmony_ci { USBTV_BASE + 0x012d, 0x0004 }, 22362306a36Sopenharmony_ci { USBTV_BASE + 0x012f, 0x0008 }, 22462306a36Sopenharmony_ci { USBTV_BASE + 0x0220, 0x002d }, 22562306a36Sopenharmony_ci { USBTV_BASE + 0x0225, 0x0028 }, 22662306a36Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0008 }, 22762306a36Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 22862306a36Sopenharmony_ci { USBTV_BASE + 0x0254, 0x0069 }, 22962306a36Sopenharmony_ci { USBTV_BASE + 0x025a, 0x0016 }, 23062306a36Sopenharmony_ci { USBTV_BASE + 0x025b, 0x0035 }, 23162306a36Sopenharmony_ci { USBTV_BASE + 0x0263, 0x0021 }, 23262306a36Sopenharmony_ci { USBTV_BASE + 0x0266, 0x0016 }, 23362306a36Sopenharmony_ci { USBTV_BASE + 0x0267, 0x0036 }, 23462306a36Sopenharmony_ci /* End image tuning */ 23562306a36Sopenharmony_ci { USBTV_BASE + 0x024e, 0x0002 }, 23662306a36Sopenharmony_ci { USBTV_BASE + 0x024f, 0x0002 }, 23762306a36Sopenharmony_ci }; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret = usbtv_configure_for_norm(usbtv, norm); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!ret) { 24262306a36Sopenharmony_ci /* Masks for norms using a NTSC or PAL color encoding. */ 24362306a36Sopenharmony_ci static const v4l2_std_id ntsc_mask = 24462306a36Sopenharmony_ci V4L2_STD_NTSC | V4L2_STD_NTSC_443; 24562306a36Sopenharmony_ci static const v4l2_std_id pal_mask = 24662306a36Sopenharmony_ci V4L2_STD_PAL | V4L2_STD_PAL_60 | V4L2_STD_PAL_M | 24762306a36Sopenharmony_ci V4L2_STD_PAL_Nc; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (norm & ntsc_mask) 25062306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, ntsc, ARRAY_SIZE(ntsc)); 25162306a36Sopenharmony_ci else if (norm & pal_mask) 25262306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, pal, ARRAY_SIZE(pal)); 25362306a36Sopenharmony_ci else if (norm & V4L2_STD_SECAM) 25462306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, secam, ARRAY_SIZE(secam)); 25562306a36Sopenharmony_ci else 25662306a36Sopenharmony_ci ret = -EINVAL; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (!ret) { 26062306a36Sopenharmony_ci /* Configure the decoder for the color standard */ 26162306a36Sopenharmony_ci const u16 cfg[][2] = { 26262306a36Sopenharmony_ci { USBTV_BASE + 0x016f, usbtv_norm_to_16f_reg(norm) } 26362306a36Sopenharmony_ci }; 26462306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, cfg, ARRAY_SIZE(cfg)); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int usbtv_setup_capture(struct usbtv *usbtv) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci static const u16 setup[][2] = { 27462306a36Sopenharmony_ci /* These seem to enable the device. */ 27562306a36Sopenharmony_ci { USBTV_BASE + 0x0008, 0x0001 }, 27662306a36Sopenharmony_ci { USBTV_BASE + 0x01d0, 0x00ff }, 27762306a36Sopenharmony_ci { USBTV_BASE + 0x01d9, 0x0002 }, 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* These seem to influence color parameters, such as 28062306a36Sopenharmony_ci * brightness, etc. */ 28162306a36Sopenharmony_ci { USBTV_BASE + 0x0239, 0x0040 }, 28262306a36Sopenharmony_ci { USBTV_BASE + 0x0240, 0x0000 }, 28362306a36Sopenharmony_ci { USBTV_BASE + 0x0241, 0x0000 }, 28462306a36Sopenharmony_ci { USBTV_BASE + 0x0242, 0x0002 }, 28562306a36Sopenharmony_ci { USBTV_BASE + 0x0243, 0x0080 }, 28662306a36Sopenharmony_ci { USBTV_BASE + 0x0244, 0x0012 }, 28762306a36Sopenharmony_ci { USBTV_BASE + 0x0245, 0x0090 }, 28862306a36Sopenharmony_ci { USBTV_BASE + 0x0246, 0x0000 }, 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci { USBTV_BASE + 0x0278, 0x002d }, 29162306a36Sopenharmony_ci { USBTV_BASE + 0x0279, 0x000a }, 29262306a36Sopenharmony_ci { USBTV_BASE + 0x027a, 0x0032 }, 29362306a36Sopenharmony_ci { 0xf890, 0x000c }, 29462306a36Sopenharmony_ci { 0xf894, 0x0086 }, 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci { USBTV_BASE + 0x00ac, 0x00c0 }, 29762306a36Sopenharmony_ci { USBTV_BASE + 0x00ad, 0x0000 }, 29862306a36Sopenharmony_ci { USBTV_BASE + 0x00a2, 0x0012 }, 29962306a36Sopenharmony_ci { USBTV_BASE + 0x00a3, 0x00e0 }, 30062306a36Sopenharmony_ci { USBTV_BASE + 0x00a4, 0x0028 }, 30162306a36Sopenharmony_ci { USBTV_BASE + 0x00a5, 0x0082 }, 30262306a36Sopenharmony_ci { USBTV_BASE + 0x00a7, 0x0080 }, 30362306a36Sopenharmony_ci { USBTV_BASE + 0x0000, 0x0014 }, 30462306a36Sopenharmony_ci { USBTV_BASE + 0x0006, 0x0003 }, 30562306a36Sopenharmony_ci { USBTV_BASE + 0x0090, 0x0099 }, 30662306a36Sopenharmony_ci { USBTV_BASE + 0x0091, 0x0090 }, 30762306a36Sopenharmony_ci { USBTV_BASE + 0x0094, 0x0068 }, 30862306a36Sopenharmony_ci { USBTV_BASE + 0x0095, 0x0070 }, 30962306a36Sopenharmony_ci { USBTV_BASE + 0x009c, 0x0030 }, 31062306a36Sopenharmony_ci { USBTV_BASE + 0x009d, 0x00c0 }, 31162306a36Sopenharmony_ci { USBTV_BASE + 0x009e, 0x00e0 }, 31262306a36Sopenharmony_ci { USBTV_BASE + 0x0019, 0x0006 }, 31362306a36Sopenharmony_ci { USBTV_BASE + 0x008c, 0x00ba }, 31462306a36Sopenharmony_ci { USBTV_BASE + 0x0101, 0x00ff }, 31562306a36Sopenharmony_ci { USBTV_BASE + 0x010c, 0x00b3 }, 31662306a36Sopenharmony_ci { USBTV_BASE + 0x01b2, 0x0080 }, 31762306a36Sopenharmony_ci { USBTV_BASE + 0x01b4, 0x00a0 }, 31862306a36Sopenharmony_ci { USBTV_BASE + 0x014c, 0x00ff }, 31962306a36Sopenharmony_ci { USBTV_BASE + 0x014d, 0x00ca }, 32062306a36Sopenharmony_ci { USBTV_BASE + 0x0113, 0x0053 }, 32162306a36Sopenharmony_ci { USBTV_BASE + 0x0119, 0x008a }, 32262306a36Sopenharmony_ci { USBTV_BASE + 0x013c, 0x0003 }, 32362306a36Sopenharmony_ci { USBTV_BASE + 0x0150, 0x009c }, 32462306a36Sopenharmony_ci { USBTV_BASE + 0x0151, 0x0071 }, 32562306a36Sopenharmony_ci { USBTV_BASE + 0x0152, 0x00c6 }, 32662306a36Sopenharmony_ci { USBTV_BASE + 0x0153, 0x0084 }, 32762306a36Sopenharmony_ci { USBTV_BASE + 0x0154, 0x00bc }, 32862306a36Sopenharmony_ci { USBTV_BASE + 0x0155, 0x00a0 }, 32962306a36Sopenharmony_ci { USBTV_BASE + 0x0156, 0x00a0 }, 33062306a36Sopenharmony_ci { USBTV_BASE + 0x0157, 0x009c }, 33162306a36Sopenharmony_ci { USBTV_BASE + 0x0158, 0x001f }, 33262306a36Sopenharmony_ci { USBTV_BASE + 0x0159, 0x0006 }, 33362306a36Sopenharmony_ci { USBTV_BASE + 0x015d, 0x0000 }, 33462306a36Sopenharmony_ci }; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = usbtv_set_regs(usbtv, setup, ARRAY_SIZE(setup)); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = usbtv_select_norm(usbtv, usbtv->norm); 34162306a36Sopenharmony_ci if (ret) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = usbtv_select_input(usbtv, usbtv->input); 34562306a36Sopenharmony_ci if (ret) 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&usbtv->ctrl); 34962306a36Sopenharmony_ci if (ret) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* Copy data from chunk into a frame buffer, deinterlacing the data 35662306a36Sopenharmony_ci * into every second line. Unfortunately, they don't align nicely into 35762306a36Sopenharmony_ci * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels. 35862306a36Sopenharmony_ci * Therefore, we break down the chunk into two halves before copying, 35962306a36Sopenharmony_ci * so that we can interleave a line if needed. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * Each "chunk" is 240 words; a word in this context equals 4 bytes. 36262306a36Sopenharmony_ci * Image format is YUYV/YUV 4:2:2, consisting of Y Cr Y Cb, defining two 36362306a36Sopenharmony_ci * pixels, the Cr and Cb shared between the two pixels, but each having 36462306a36Sopenharmony_ci * separate Y values. Thus, the 240 words equal 480 pixels. It therefore, 36562306a36Sopenharmony_ci * takes 1.5 chunks to make a 720 pixel-wide line for the frame. 36662306a36Sopenharmony_ci * The image is interlaced, so there is a "scan" of odd lines, followed 36762306a36Sopenharmony_ci * by "scan" of even numbered lines. 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Following code is writing the chunks in correct sequence, skipping 37062306a36Sopenharmony_ci * the rows based on "odd" value. 37162306a36Sopenharmony_ci * line 1: chunk[0][ 0..479] chunk[0][480..959] chunk[1][ 0..479] 37262306a36Sopenharmony_ci * line 3: chunk[1][480..959] chunk[2][ 0..479] chunk[2][480..959] 37362306a36Sopenharmony_ci * ...etc. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_cistatic void usbtv_chunk_to_vbuf(u32 *frame, __be32 *src, int chunk_no, int odd) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci int half; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci for (half = 0; half < 2; half++) { 38062306a36Sopenharmony_ci int part_no = chunk_no * 2 + half; 38162306a36Sopenharmony_ci int line = part_no / 3; 38262306a36Sopenharmony_ci int part_index = (line * 2 + !odd) * 3 + (part_no % 3); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci u32 *dst = &frame[part_index * USBTV_CHUNK/2]; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci memcpy(dst, src, USBTV_CHUNK/2 * sizeof(*src)); 38762306a36Sopenharmony_ci src += USBTV_CHUNK/2; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* Called for each 256-byte image chunk. 39262306a36Sopenharmony_ci * First word identifies the chunk, followed by 240 words of image 39362306a36Sopenharmony_ci * data and padding. */ 39462306a36Sopenharmony_cistatic void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int frame_id, odd, chunk_no; 39762306a36Sopenharmony_ci u32 *frame; 39862306a36Sopenharmony_ci struct usbtv_buf *buf; 39962306a36Sopenharmony_ci unsigned long flags; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Ignore corrupted lines. */ 40262306a36Sopenharmony_ci if (!USBTV_MAGIC_OK(chunk)) 40362306a36Sopenharmony_ci return; 40462306a36Sopenharmony_ci frame_id = USBTV_FRAME_ID(chunk); 40562306a36Sopenharmony_ci odd = USBTV_ODD(chunk); 40662306a36Sopenharmony_ci chunk_no = USBTV_CHUNK_NO(chunk); 40762306a36Sopenharmony_ci if (chunk_no >= usbtv->n_chunks) 40862306a36Sopenharmony_ci return; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Beginning of a frame. */ 41162306a36Sopenharmony_ci if (chunk_no == 0) { 41262306a36Sopenharmony_ci usbtv->frame_id = frame_id; 41362306a36Sopenharmony_ci usbtv->chunks_done = 0; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (usbtv->frame_id != frame_id) 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci spin_lock_irqsave(&usbtv->buflock, flags); 42062306a36Sopenharmony_ci if (list_empty(&usbtv->bufs)) { 42162306a36Sopenharmony_ci /* No free buffers. Userspace likely too slow. */ 42262306a36Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 42362306a36Sopenharmony_ci return; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* First available buffer. */ 42762306a36Sopenharmony_ci buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list); 42862306a36Sopenharmony_ci frame = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Copy the chunk data. */ 43162306a36Sopenharmony_ci usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd); 43262306a36Sopenharmony_ci usbtv->chunks_done++; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Last chunk in a field */ 43562306a36Sopenharmony_ci if (chunk_no == usbtv->n_chunks-1) { 43662306a36Sopenharmony_ci /* Last chunk in a frame, signalling an end */ 43762306a36Sopenharmony_ci if (odd && !usbtv->last_odd) { 43862306a36Sopenharmony_ci int size = vb2_plane_size(&buf->vb.vb2_buf, 0); 43962306a36Sopenharmony_ci enum vb2_buffer_state state = usbtv->chunks_done == 44062306a36Sopenharmony_ci usbtv->n_chunks ? 44162306a36Sopenharmony_ci VB2_BUF_STATE_DONE : 44262306a36Sopenharmony_ci VB2_BUF_STATE_ERROR; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci buf->vb.field = V4L2_FIELD_INTERLACED; 44562306a36Sopenharmony_ci buf->vb.sequence = usbtv->sequence++; 44662306a36Sopenharmony_ci buf->vb.vb2_buf.timestamp = ktime_get_ns(); 44762306a36Sopenharmony_ci vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); 44862306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 44962306a36Sopenharmony_ci list_del(&buf->list); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci usbtv->last_odd = odd; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* Got image data. Each packet contains a number of 256-word chunks we 45862306a36Sopenharmony_ci * compose the image from. */ 45962306a36Sopenharmony_cistatic void usbtv_iso_cb(struct urb *ip) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci int ret; 46262306a36Sopenharmony_ci int i; 46362306a36Sopenharmony_ci struct usbtv *usbtv = (struct usbtv *)ip->context; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci switch (ip->status) { 46662306a36Sopenharmony_ci /* All fine. */ 46762306a36Sopenharmony_ci case 0: 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci /* Device disconnected or capture stopped? */ 47062306a36Sopenharmony_ci case -ENODEV: 47162306a36Sopenharmony_ci case -ENOENT: 47262306a36Sopenharmony_ci case -ECONNRESET: 47362306a36Sopenharmony_ci case -ESHUTDOWN: 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci /* Unknown error. Retry. */ 47662306a36Sopenharmony_ci default: 47762306a36Sopenharmony_ci dev_warn(usbtv->dev, "Bad response for ISO request.\n"); 47862306a36Sopenharmony_ci goto resubmit; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (i = 0; i < ip->number_of_packets; i++) { 48262306a36Sopenharmony_ci int size = ip->iso_frame_desc[i].actual_length; 48362306a36Sopenharmony_ci unsigned char *data = ip->transfer_buffer + 48462306a36Sopenharmony_ci ip->iso_frame_desc[i].offset; 48562306a36Sopenharmony_ci int offset; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci for (offset = 0; USBTV_CHUNK_SIZE * offset < size; offset++) 48862306a36Sopenharmony_ci usbtv_image_chunk(usbtv, 48962306a36Sopenharmony_ci (__be32 *)&data[USBTV_CHUNK_SIZE * offset]); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ciresubmit: 49362306a36Sopenharmony_ci ret = usb_submit_urb(ip, GFP_ATOMIC); 49462306a36Sopenharmony_ci if (ret < 0) 49562306a36Sopenharmony_ci dev_warn(usbtv->dev, "Could not resubmit ISO URB\n"); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic struct urb *usbtv_setup_iso_transfer(struct usbtv *usbtv) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct urb *ip; 50162306a36Sopenharmony_ci int size = usbtv->iso_size; 50262306a36Sopenharmony_ci int i; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci ip = usb_alloc_urb(USBTV_ISOC_PACKETS, GFP_KERNEL); 50562306a36Sopenharmony_ci if (ip == NULL) 50662306a36Sopenharmony_ci return NULL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ip->dev = usbtv->udev; 50962306a36Sopenharmony_ci ip->context = usbtv; 51062306a36Sopenharmony_ci ip->pipe = usb_rcvisocpipe(usbtv->udev, USBTV_VIDEO_ENDP); 51162306a36Sopenharmony_ci ip->interval = 1; 51262306a36Sopenharmony_ci ip->transfer_flags = URB_ISO_ASAP; 51362306a36Sopenharmony_ci ip->transfer_buffer = kcalloc(USBTV_ISOC_PACKETS, size, 51462306a36Sopenharmony_ci GFP_KERNEL); 51562306a36Sopenharmony_ci if (!ip->transfer_buffer) { 51662306a36Sopenharmony_ci usb_free_urb(ip); 51762306a36Sopenharmony_ci return NULL; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci ip->complete = usbtv_iso_cb; 52062306a36Sopenharmony_ci ip->number_of_packets = USBTV_ISOC_PACKETS; 52162306a36Sopenharmony_ci ip->transfer_buffer_length = size * USBTV_ISOC_PACKETS; 52262306a36Sopenharmony_ci for (i = 0; i < USBTV_ISOC_PACKETS; i++) { 52362306a36Sopenharmony_ci ip->iso_frame_desc[i].offset = size * i; 52462306a36Sopenharmony_ci ip->iso_frame_desc[i].length = size; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return ip; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic void usbtv_stop(struct usbtv *usbtv) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci int i; 53362306a36Sopenharmony_ci unsigned long flags; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Cancel running transfers. */ 53662306a36Sopenharmony_ci for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) { 53762306a36Sopenharmony_ci struct urb *ip = usbtv->isoc_urbs[i]; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (ip == NULL) 54062306a36Sopenharmony_ci continue; 54162306a36Sopenharmony_ci usb_kill_urb(ip); 54262306a36Sopenharmony_ci kfree(ip->transfer_buffer); 54362306a36Sopenharmony_ci usb_free_urb(ip); 54462306a36Sopenharmony_ci usbtv->isoc_urbs[i] = NULL; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Return buffers to userspace. */ 54862306a36Sopenharmony_ci spin_lock_irqsave(&usbtv->buflock, flags); 54962306a36Sopenharmony_ci while (!list_empty(&usbtv->bufs)) { 55062306a36Sopenharmony_ci struct usbtv_buf *buf = list_first_entry(&usbtv->bufs, 55162306a36Sopenharmony_ci struct usbtv_buf, list); 55262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 55362306a36Sopenharmony_ci list_del(&buf->list); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int usbtv_start(struct usbtv *usbtv) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci int i; 56162306a36Sopenharmony_ci int ret; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci usbtv_audio_suspend(usbtv); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = usb_set_interface(usbtv->udev, 0, 0); 56662306a36Sopenharmony_ci if (ret < 0) 56762306a36Sopenharmony_ci return ret; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = usbtv_setup_capture(usbtv); 57062306a36Sopenharmony_ci if (ret < 0) 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ret = usb_set_interface(usbtv->udev, 0, 1); 57462306a36Sopenharmony_ci if (ret < 0) 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci usbtv_audio_resume(usbtv); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) { 58062306a36Sopenharmony_ci struct urb *ip; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ip = usbtv_setup_iso_transfer(usbtv); 58362306a36Sopenharmony_ci if (ip == NULL) { 58462306a36Sopenharmony_ci ret = -ENOMEM; 58562306a36Sopenharmony_ci goto start_fail; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci usbtv->isoc_urbs[i] = ip; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = usb_submit_urb(ip, GFP_KERNEL); 59062306a36Sopenharmony_ci if (ret < 0) 59162306a36Sopenharmony_ci goto start_fail; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistart_fail: 59762306a36Sopenharmony_ci usbtv_stop(usbtv); 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int usbtv_querycap(struct file *file, void *priv, 60262306a36Sopenharmony_ci struct v4l2_capability *cap) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct usbtv *dev = video_drvdata(file); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci strscpy(cap->driver, "usbtv", sizeof(cap->driver)); 60762306a36Sopenharmony_ci strscpy(cap->card, "usbtv", sizeof(cap->card)); 60862306a36Sopenharmony_ci usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int usbtv_enum_input(struct file *file, void *priv, 61362306a36Sopenharmony_ci struct v4l2_input *i) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct usbtv *dev = video_drvdata(file); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci switch (i->index) { 61862306a36Sopenharmony_ci case USBTV_COMPOSITE_INPUT: 61962306a36Sopenharmony_ci strscpy(i->name, "Composite", sizeof(i->name)); 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case USBTV_SVIDEO_INPUT: 62262306a36Sopenharmony_ci strscpy(i->name, "S-Video", sizeof(i->name)); 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci default: 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 62962306a36Sopenharmony_ci i->std = dev->vdev.tvnorms; 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int usbtv_enum_fmt_vid_cap(struct file *file, void *priv, 63462306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci if (f->index > 0) 63762306a36Sopenharmony_ci return -EINVAL; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUYV; 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int usbtv_fmt_vid_cap(struct file *file, void *priv, 64462306a36Sopenharmony_ci struct v4l2_format *f) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci f->fmt.pix.width = usbtv->width; 64962306a36Sopenharmony_ci f->fmt.pix.height = usbtv->height; 65062306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 65162306a36Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 65262306a36Sopenharmony_ci f->fmt.pix.bytesperline = usbtv->width * 2; 65362306a36Sopenharmony_ci f->fmt.pix.sizeimage = (f->fmt.pix.bytesperline * f->fmt.pix.height); 65462306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int usbtv_g_std(struct file *file, void *priv, v4l2_std_id *norm) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 66262306a36Sopenharmony_ci *norm = usbtv->norm; 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci int ret = -EINVAL; 66962306a36Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (norm & USBTV_TV_STD) 67262306a36Sopenharmony_ci ret = usbtv_select_norm(usbtv, norm); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int usbtv_g_input(struct file *file, void *priv, unsigned int *i) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 68062306a36Sopenharmony_ci *i = usbtv->input; 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int usbtv_s_input(struct file *file, void *priv, unsigned int i) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct usbtv *usbtv = video_drvdata(file); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return usbtv_select_input(usbtv, i); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops usbtv_ioctl_ops = { 69262306a36Sopenharmony_ci .vidioc_querycap = usbtv_querycap, 69362306a36Sopenharmony_ci .vidioc_enum_input = usbtv_enum_input, 69462306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = usbtv_enum_fmt_vid_cap, 69562306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = usbtv_fmt_vid_cap, 69662306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = usbtv_fmt_vid_cap, 69762306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = usbtv_fmt_vid_cap, 69862306a36Sopenharmony_ci .vidioc_g_std = usbtv_g_std, 69962306a36Sopenharmony_ci .vidioc_s_std = usbtv_s_std, 70062306a36Sopenharmony_ci .vidioc_g_input = usbtv_g_input, 70162306a36Sopenharmony_ci .vidioc_s_input = usbtv_s_input, 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 70462306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 70562306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 70662306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 70762306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 70862306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 70962306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 71062306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 71162306a36Sopenharmony_ci}; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic const struct v4l2_file_operations usbtv_fops = { 71462306a36Sopenharmony_ci .owner = THIS_MODULE, 71562306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 71662306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 71762306a36Sopenharmony_ci .open = v4l2_fh_open, 71862306a36Sopenharmony_ci .release = vb2_fop_release, 71962306a36Sopenharmony_ci .read = vb2_fop_read, 72062306a36Sopenharmony_ci .poll = vb2_fop_poll, 72162306a36Sopenharmony_ci}; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int usbtv_queue_setup(struct vb2_queue *vq, 72462306a36Sopenharmony_ci unsigned int *nbuffers, 72562306a36Sopenharmony_ci unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vq); 72862306a36Sopenharmony_ci unsigned size = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 73162306a36Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 73262306a36Sopenharmony_ci if (*nplanes) 73362306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 73462306a36Sopenharmony_ci *nplanes = 1; 73562306a36Sopenharmony_ci sizes[0] = size; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void usbtv_buf_queue(struct vb2_buffer *vb) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 74362306a36Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vb->vb2_queue); 74462306a36Sopenharmony_ci struct usbtv_buf *buf = container_of(vbuf, struct usbtv_buf, vb); 74562306a36Sopenharmony_ci unsigned long flags; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (usbtv->udev == NULL) { 74862306a36Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 74962306a36Sopenharmony_ci return; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci spin_lock_irqsave(&usbtv->buflock, flags); 75362306a36Sopenharmony_ci list_add_tail(&buf->list, &usbtv->bufs); 75462306a36Sopenharmony_ci spin_unlock_irqrestore(&usbtv->buflock, flags); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vq); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (usbtv->udev == NULL) 76262306a36Sopenharmony_ci return -ENODEV; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci usbtv->last_odd = 1; 76562306a36Sopenharmony_ci usbtv->sequence = 0; 76662306a36Sopenharmony_ci return usbtv_start(usbtv); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void usbtv_stop_streaming(struct vb2_queue *vq) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct usbtv *usbtv = vb2_get_drv_priv(vq); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (usbtv->udev) 77462306a36Sopenharmony_ci usbtv_stop(usbtv); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic const struct vb2_ops usbtv_vb2_ops = { 77862306a36Sopenharmony_ci .queue_setup = usbtv_queue_setup, 77962306a36Sopenharmony_ci .buf_queue = usbtv_buf_queue, 78062306a36Sopenharmony_ci .start_streaming = usbtv_start_streaming, 78162306a36Sopenharmony_ci .stop_streaming = usbtv_stop_streaming, 78262306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 78362306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 78462306a36Sopenharmony_ci}; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct usbtv *usbtv = container_of(ctrl->handler, struct usbtv, 78962306a36Sopenharmony_ci ctrl); 79062306a36Sopenharmony_ci u8 *data; 79162306a36Sopenharmony_ci u16 index, size; 79262306a36Sopenharmony_ci int ret; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci data = kmalloc(3, GFP_KERNEL); 79562306a36Sopenharmony_ci if (!data) 79662306a36Sopenharmony_ci return -ENOMEM; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* 79962306a36Sopenharmony_ci * Read in the current brightness/contrast registers. We need them 80062306a36Sopenharmony_ci * both, because the values are for some reason interleaved. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci if (ctrl->id == V4L2_CID_BRIGHTNESS || ctrl->id == V4L2_CID_CONTRAST) { 80362306a36Sopenharmony_ci ret = usb_control_msg(usbtv->udev, 80462306a36Sopenharmony_ci usb_rcvctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, 80562306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 80662306a36Sopenharmony_ci 0, USBTV_BASE + 0x0244, (void *)data, 3, 80762306a36Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 80862306a36Sopenharmony_ci if (ret < 0) 80962306a36Sopenharmony_ci goto error; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci switch (ctrl->id) { 81362306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 81462306a36Sopenharmony_ci index = USBTV_BASE + 0x0244; 81562306a36Sopenharmony_ci size = 3; 81662306a36Sopenharmony_ci data[0] &= 0xf0; 81762306a36Sopenharmony_ci data[0] |= (ctrl->val >> 8) & 0xf; 81862306a36Sopenharmony_ci data[2] = ctrl->val & 0xff; 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 82162306a36Sopenharmony_ci index = USBTV_BASE + 0x0244; 82262306a36Sopenharmony_ci size = 3; 82362306a36Sopenharmony_ci data[0] &= 0x0f; 82462306a36Sopenharmony_ci data[0] |= (ctrl->val >> 4) & 0xf0; 82562306a36Sopenharmony_ci data[1] = ctrl->val & 0xff; 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci case V4L2_CID_SATURATION: 82862306a36Sopenharmony_ci index = USBTV_BASE + 0x0242; 82962306a36Sopenharmony_ci data[0] = ctrl->val >> 8; 83062306a36Sopenharmony_ci data[1] = ctrl->val & 0xff; 83162306a36Sopenharmony_ci size = 2; 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci case V4L2_CID_HUE: 83462306a36Sopenharmony_ci index = USBTV_BASE + 0x0240; 83562306a36Sopenharmony_ci size = 2; 83662306a36Sopenharmony_ci if (ctrl->val > 0) { 83762306a36Sopenharmony_ci data[0] = 0x92 + (ctrl->val >> 8); 83862306a36Sopenharmony_ci data[1] = ctrl->val & 0xff; 83962306a36Sopenharmony_ci } else { 84062306a36Sopenharmony_ci data[0] = 0x82 + (-ctrl->val >> 8); 84162306a36Sopenharmony_ci data[1] = -ctrl->val & 0xff; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci case V4L2_CID_SHARPNESS: 84562306a36Sopenharmony_ci index = USBTV_BASE + 0x0239; 84662306a36Sopenharmony_ci data[0] = 0; 84762306a36Sopenharmony_ci data[1] = ctrl->val; 84862306a36Sopenharmony_ci size = 2; 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci default: 85162306a36Sopenharmony_ci kfree(data); 85262306a36Sopenharmony_ci return -EINVAL; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci ret = usb_control_msg(usbtv->udev, usb_sndctrlpipe(usbtv->udev, 0), 85662306a36Sopenharmony_ci USBTV_CONTROL_REG, 85762306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 85862306a36Sopenharmony_ci 0, index, (void *)data, size, USB_CTRL_SET_TIMEOUT); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cierror: 86162306a36Sopenharmony_ci if (ret < 0) 86262306a36Sopenharmony_ci dev_warn(usbtv->dev, "Failed to submit a control request.\n"); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci kfree(data); 86562306a36Sopenharmony_ci return ret; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops usbtv_ctrl_ops = { 86962306a36Sopenharmony_ci .s_ctrl = usbtv_s_ctrl, 87062306a36Sopenharmony_ci}; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void usbtv_release(struct v4l2_device *v4l2_dev) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct usbtv *usbtv = container_of(v4l2_dev, struct usbtv, v4l2_dev); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci v4l2_device_unregister(&usbtv->v4l2_dev); 87762306a36Sopenharmony_ci v4l2_ctrl_handler_free(&usbtv->ctrl); 87862306a36Sopenharmony_ci kfree(usbtv); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciint usbtv_video_init(struct usbtv *usbtv) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci int ret; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci (void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci spin_lock_init(&usbtv->buflock); 88862306a36Sopenharmony_ci mutex_init(&usbtv->v4l2_lock); 88962306a36Sopenharmony_ci mutex_init(&usbtv->vb2q_lock); 89062306a36Sopenharmony_ci INIT_LIST_HEAD(&usbtv->bufs); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* videobuf2 structure */ 89362306a36Sopenharmony_ci usbtv->vb2q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 89462306a36Sopenharmony_ci usbtv->vb2q.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; 89562306a36Sopenharmony_ci usbtv->vb2q.drv_priv = usbtv; 89662306a36Sopenharmony_ci usbtv->vb2q.buf_struct_size = sizeof(struct usbtv_buf); 89762306a36Sopenharmony_ci usbtv->vb2q.ops = &usbtv_vb2_ops; 89862306a36Sopenharmony_ci usbtv->vb2q.mem_ops = &vb2_vmalloc_memops; 89962306a36Sopenharmony_ci usbtv->vb2q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 90062306a36Sopenharmony_ci usbtv->vb2q.lock = &usbtv->vb2q_lock; 90162306a36Sopenharmony_ci ret = vb2_queue_init(&usbtv->vb2q); 90262306a36Sopenharmony_ci if (ret < 0) { 90362306a36Sopenharmony_ci dev_warn(usbtv->dev, "Could not initialize videobuf2 queue\n"); 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci /* controls */ 90862306a36Sopenharmony_ci v4l2_ctrl_handler_init(&usbtv->ctrl, 4); 90962306a36Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 91062306a36Sopenharmony_ci V4L2_CID_CONTRAST, 0, 0x3ff, 1, 0x1d0); 91162306a36Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 91262306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 0x3ff, 1, 0x1c0); 91362306a36Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 91462306a36Sopenharmony_ci V4L2_CID_SATURATION, 0, 0x3ff, 1, 0x200); 91562306a36Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 91662306a36Sopenharmony_ci V4L2_CID_HUE, -0xdff, 0xdff, 1, 0x000); 91762306a36Sopenharmony_ci v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, 91862306a36Sopenharmony_ci V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x60); 91962306a36Sopenharmony_ci ret = usbtv->ctrl.error; 92062306a36Sopenharmony_ci if (ret < 0) { 92162306a36Sopenharmony_ci dev_warn(usbtv->dev, "Could not initialize controls\n"); 92262306a36Sopenharmony_ci goto ctrl_fail; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* v4l2 structure */ 92662306a36Sopenharmony_ci usbtv->v4l2_dev.ctrl_handler = &usbtv->ctrl; 92762306a36Sopenharmony_ci usbtv->v4l2_dev.release = usbtv_release; 92862306a36Sopenharmony_ci ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev); 92962306a36Sopenharmony_ci if (ret < 0) { 93062306a36Sopenharmony_ci dev_warn(usbtv->dev, "Could not register v4l2 device\n"); 93162306a36Sopenharmony_ci goto v4l2_fail; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* Video structure */ 93562306a36Sopenharmony_ci strscpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name)); 93662306a36Sopenharmony_ci usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev; 93762306a36Sopenharmony_ci usbtv->vdev.release = video_device_release_empty; 93862306a36Sopenharmony_ci usbtv->vdev.fops = &usbtv_fops; 93962306a36Sopenharmony_ci usbtv->vdev.ioctl_ops = &usbtv_ioctl_ops; 94062306a36Sopenharmony_ci usbtv->vdev.tvnorms = USBTV_TV_STD; 94162306a36Sopenharmony_ci usbtv->vdev.queue = &usbtv->vb2q; 94262306a36Sopenharmony_ci usbtv->vdev.lock = &usbtv->v4l2_lock; 94362306a36Sopenharmony_ci usbtv->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | 94462306a36Sopenharmony_ci V4L2_CAP_STREAMING; 94562306a36Sopenharmony_ci video_set_drvdata(&usbtv->vdev, usbtv); 94662306a36Sopenharmony_ci ret = video_register_device(&usbtv->vdev, VFL_TYPE_VIDEO, -1); 94762306a36Sopenharmony_ci if (ret < 0) { 94862306a36Sopenharmony_ci dev_warn(usbtv->dev, "Could not register video device\n"); 94962306a36Sopenharmony_ci goto vdev_fail; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_civdev_fail: 95562306a36Sopenharmony_ci v4l2_device_unregister(&usbtv->v4l2_dev); 95662306a36Sopenharmony_civ4l2_fail: 95762306a36Sopenharmony_cictrl_fail: 95862306a36Sopenharmony_ci v4l2_ctrl_handler_free(&usbtv->ctrl); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return ret; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_civoid usbtv_video_free(struct usbtv *usbtv) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci mutex_lock(&usbtv->vb2q_lock); 96662306a36Sopenharmony_ci mutex_lock(&usbtv->v4l2_lock); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci usbtv_stop(usbtv); 96962306a36Sopenharmony_ci vb2_video_unregister_device(&usbtv->vdev); 97062306a36Sopenharmony_ci v4l2_device_disconnect(&usbtv->v4l2_dev); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci mutex_unlock(&usbtv->v4l2_lock); 97362306a36Sopenharmony_ci mutex_unlock(&usbtv->vb2q_lock); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci v4l2_device_put(&usbtv->v4l2_dev); 97662306a36Sopenharmony_ci} 977