18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB 48c2ecf20Sopenharmony_ci// video capture devices 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> 78c2ecf20Sopenharmony_ci// Markus Rechberger <mrechberger@gmail.com> 88c2ecf20Sopenharmony_ci// Mauro Carvalho Chehab <mchehab@kernel.org> 98c2ecf20Sopenharmony_ci// Sascha Sommer <saschasommer@freenet.de> 108c2ecf20Sopenharmony_ci// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> 118c2ecf20Sopenharmony_ci// 128c2ecf20Sopenharmony_ci// Some parts based on SN9C10x PC Camera Controllers GPL driver made 138c2ecf20Sopenharmony_ci// by Luca Risolia <luca.risolia@studio.unibo.it> 148c2ecf20Sopenharmony_ci// 158c2ecf20Sopenharmony_ci// This program is free software; you can redistribute it and/or modify 168c2ecf20Sopenharmony_ci// it under the terms of the GNU General Public License as published by 178c2ecf20Sopenharmony_ci// the Free Software Foundation; either version 2 of the License, or 188c2ecf20Sopenharmony_ci// (at your option) any later version. 198c2ecf20Sopenharmony_ci// 208c2ecf20Sopenharmony_ci// This program is distributed in the hope that it will be useful, 218c2ecf20Sopenharmony_ci// but WITHOUT ANY WARRANTY; without even the implied warranty of 228c2ecf20Sopenharmony_ci// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 238c2ecf20Sopenharmony_ci// GNU General Public License for more details. 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "em28xx.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/init.h> 288c2ecf20Sopenharmony_ci#include <linux/list.h> 298c2ecf20Sopenharmony_ci#include <linux/module.h> 308c2ecf20Sopenharmony_ci#include <linux/kernel.h> 318c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 328c2ecf20Sopenharmony_ci#include <linux/usb.h> 338c2ecf20Sopenharmony_ci#include <linux/i2c.h> 348c2ecf20Sopenharmony_ci#include <linux/mm.h> 358c2ecf20Sopenharmony_ci#include <linux/mutex.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "em28xx-v4l.h" 398c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 408c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 418c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 428c2ecf20Sopenharmony_ci#include <media/drv-intf/msp3400.h> 438c2ecf20Sopenharmony_ci#include <media/tuner.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \ 468c2ecf20Sopenharmony_ci "Markus Rechberger <mrechberger@gmail.com>, " \ 478c2ecf20Sopenharmony_ci "Mauro Carvalho Chehab <mchehab@kernel.org>, " \ 488c2ecf20Sopenharmony_ci "Sascha Sommer <saschasommer@freenet.de>" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic unsigned int isoc_debug; 518c2ecf20Sopenharmony_cimodule_param(isoc_debug, int, 0644); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic unsigned int disable_vbi; 558c2ecf20Sopenharmony_cimodule_param(disable_vbi, int, 0644); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_vbi, "disable vbi support"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int alt; 598c2ecf20Sopenharmony_cimodule_param(alt, int, 0644); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define em28xx_videodbg(fmt, arg...) do { \ 638c2ecf20Sopenharmony_ci if (video_debug) \ 648c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &dev->intf->dev, \ 658c2ecf20Sopenharmony_ci "video: %s: " fmt, __func__, ## arg); \ 668c2ecf20Sopenharmony_ci} while (0) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define em28xx_isocdbg(fmt, arg...) do {\ 698c2ecf20Sopenharmony_ci if (isoc_debug) \ 708c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &dev->intf->dev, \ 718c2ecf20Sopenharmony_ci "isoc: %s: " fmt, __func__, ## arg); \ 728c2ecf20Sopenharmony_ci} while (0) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface"); 768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 778c2ecf20Sopenharmony_ciMODULE_VERSION(EM28XX_VERSION); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE1 0x02 808c2ecf20Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE 0x20 818c2ecf20Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_FRAME_END 0x02 828c2ecf20Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_FRAME_ID 0x01 838c2ecf20Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_MASK (EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE | \ 848c2ecf20Sopenharmony_ci EM25XX_FRMDATAHDR_BYTE2_FRAME_END | \ 858c2ecf20Sopenharmony_ci EM25XX_FRMDATAHDR_BYTE2_FRAME_ID) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; 888c2ecf20Sopenharmony_cistatic unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; 898c2ecf20Sopenharmony_cistatic unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444); 928c2ecf20Sopenharmony_cimodule_param_array(vbi_nr, int, NULL, 0444); 938c2ecf20Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444); 948c2ecf20Sopenharmony_ciMODULE_PARM_DESC(video_nr, "video device numbers"); 958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "vbi device numbers"); 968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "radio device numbers"); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic unsigned int video_debug; 998c2ecf20Sopenharmony_cimodule_param(video_debug, int, 0644); 1008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(video_debug, "enable debug messages [video]"); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* supported video standards */ 1038c2ecf20Sopenharmony_cistatic struct em28xx_fmt format[] = { 1048c2ecf20Sopenharmony_ci { 1058c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 1068c2ecf20Sopenharmony_ci .depth = 16, 1078c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_YUV422_Y0UY1V, 1088c2ecf20Sopenharmony_ci }, { 1098c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 1108c2ecf20Sopenharmony_ci .depth = 16, 1118c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_RGB_16_656, 1128c2ecf20Sopenharmony_ci }, { 1138c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB8, 1148c2ecf20Sopenharmony_ci .depth = 8, 1158c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_RGB_8_RGRG, 1168c2ecf20Sopenharmony_ci }, { 1178c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR8, 1188c2ecf20Sopenharmony_ci .depth = 8, 1198c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_RGB_8_BGBG, 1208c2ecf20Sopenharmony_ci }, { 1218c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG8, 1228c2ecf20Sopenharmony_ci .depth = 8, 1238c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_RGB_8_GRGR, 1248c2ecf20Sopenharmony_ci }, { 1258c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG8, 1268c2ecf20Sopenharmony_ci .depth = 8, 1278c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_RGB_8_GBGB, 1288c2ecf20Sopenharmony_ci }, { 1298c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUV411P, 1308c2ecf20Sopenharmony_ci .depth = 12, 1318c2ecf20Sopenharmony_ci .reg = EM28XX_OUTFMT_YUV411, 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/*FIXME: maxw should be dependent of alt mode */ 1368c2ecf20Sopenharmony_cistatic inline unsigned int norm_maxw(struct em28xx *dev) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (dev->is_webcam) 1418c2ecf20Sopenharmony_ci return v4l2->sensor_xres; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (dev->board.max_range_640_480) 1448c2ecf20Sopenharmony_ci return 640; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 720; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic inline unsigned int norm_maxh(struct em28xx *dev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (dev->is_webcam) 1548c2ecf20Sopenharmony_ci return v4l2->sensor_yres; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (dev->board.max_range_640_480) 1578c2ecf20Sopenharmony_ci return 480; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return (v4l2->norm & V4L2_STD_625_50) ? 576 : 480; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int em28xx_vbi_supported(struct em28xx *dev) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci /* Modprobe option to manually disable */ 1658c2ecf20Sopenharmony_ci if (disable_vbi == 1) 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (dev->is_webcam) 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* FIXME: check subdevices for VBI support */ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (dev->chip_id == CHIP_ID_EM2860 || 1748c2ecf20Sopenharmony_ci dev->chip_id == CHIP_ID_EM2883) 1758c2ecf20Sopenharmony_ci return 1; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Version of em28xx that does not support VBI */ 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * em28xx_wake_i2c() 1838c2ecf20Sopenharmony_ci * configure i2c attached devices 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic void em28xx_wake_i2c(struct em28xx *dev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, core, reset, 0); 1908c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, video, s_routing, 1918c2ecf20Sopenharmony_ci INPUT(dev->ctl_input)->vmux, 0, 0); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int em28xx_colorlevels_set_default(struct em28xx *dev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R20_YGAIN, CONTRAST_DEFAULT); 1978c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R21_YOFFSET, BRIGHTNESS_DEFAULT); 1988c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R22_UVGAIN, SATURATION_DEFAULT); 1998c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R23_UOFFSET, BLUE_BALANCE_DEFAULT); 2008c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R24_VOFFSET, RED_BALANCE_DEFAULT); 2018c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, SHARPNESS_DEFAULT); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); 2048c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); 2058c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); 2068c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); 2078c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); 2088c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); 2098c2ecf20Sopenharmony_ci return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int em28xx_set_outfmt(struct em28xx *dev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int ret; 2158c2ecf20Sopenharmony_ci u8 fmt, vinctrl; 2168c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci fmt = v4l2->format->reg; 2198c2ecf20Sopenharmony_ci if (!dev->is_em25xx) 2208c2ecf20Sopenharmony_ci fmt |= 0x20; 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * NOTE: it's not clear if this is really needed ! 2238c2ecf20Sopenharmony_ci * The datasheets say bit 5 is a reserved bit and devices seem to work 2248c2ecf20Sopenharmony_ci * fine without it. But the Windows driver sets it for em2710/50+em28xx 2258c2ecf20Sopenharmony_ci * devices and we've always been setting it, too. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * em2765 (em25xx, em276x/7x/8x) devices do NOT work with this bit set, 2288c2ecf20Sopenharmony_ci * it's likely used for an additional (compressed ?) format there. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R27_OUTFMT, fmt); 2318c2ecf20Sopenharmony_ci if (ret < 0) 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, v4l2->vinmode); 2358c2ecf20Sopenharmony_ci if (ret < 0) 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci vinctrl = v4l2->vinctl; 2398c2ecf20Sopenharmony_ci if (em28xx_vbi_supported(dev) == 1) { 2408c2ecf20Sopenharmony_ci vinctrl |= EM28XX_VINCTRL_VBI_RAW; 2418c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); 2428c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 2438c2ecf20Sopenharmony_ci v4l2->vbi_width / 4); 2448c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height); 2458c2ecf20Sopenharmony_ci if (v4l2->norm & V4L2_STD_525_60) { 2468c2ecf20Sopenharmony_ci /* NTSC */ 2478c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); 2488c2ecf20Sopenharmony_ci } else if (v4l2->norm & V4L2_STD_625_50) { 2498c2ecf20Sopenharmony_ci /* PAL */ 2508c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, 2588c2ecf20Sopenharmony_ci u8 ymin, u8 ymax) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci em28xx_videodbg("em28xx Scale: (%d,%d)-(%d,%d)\n", 2618c2ecf20Sopenharmony_ci xmin, ymin, xmax, ymax); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); 2648c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); 2658c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); 2668c2ecf20Sopenharmony_ci return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, 2708c2ecf20Sopenharmony_ci u16 width, u16 height) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci u8 cwidth = width >> 2; 2738c2ecf20Sopenharmony_ci u8 cheight = height >> 2; 2748c2ecf20Sopenharmony_ci u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); 2758c2ecf20Sopenharmony_ci /* NOTE: size limit: 2047x1023 = 2MPix */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci em28xx_videodbg("capture area set to (%d,%d): %dx%d\n", 2788c2ecf20Sopenharmony_ci hstart, vstart, 2798c2ecf20Sopenharmony_ci ((overflow & 2) << 9 | cwidth << 2), 2808c2ecf20Sopenharmony_ci ((overflow & 1) << 10 | cheight << 2)); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); 2838c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); 2848c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); 2858c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); 2868c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* FIXME: function/meaning of these registers ? */ 2898c2ecf20Sopenharmony_ci /* FIXME: align width+height to multiples of 4 ?! */ 2908c2ecf20Sopenharmony_ci if (dev->is_em25xx) { 2918c2ecf20Sopenharmony_ci em28xx_write_reg(dev, 0x34, width >> 4); 2928c2ecf20Sopenharmony_ci em28xx_write_reg(dev, 0x35, height >> 4); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci u8 mode = 0x00; 2998c2ecf20Sopenharmony_ci /* the em2800 scaler only supports scaling down to 50% */ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (dev->board.is_em2800) { 3028c2ecf20Sopenharmony_ci mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); 3038c2ecf20Sopenharmony_ci } else { 3048c2ecf20Sopenharmony_ci u8 buf[2]; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci buf[0] = h; 3078c2ecf20Sopenharmony_ci buf[1] = h >> 8; 3088c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci buf[0] = v; 3118c2ecf20Sopenharmony_ci buf[1] = v >> 8; 3128c2ecf20Sopenharmony_ci em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * it seems that both H and V scalers must be active 3158c2ecf20Sopenharmony_ci * to work correctly 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci mode = (h || v) ? 0x30 : 0x00; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* FIXME: this only function read values from dev */ 3238c2ecf20Sopenharmony_cistatic int em28xx_resolution_set(struct em28xx *dev) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 3268c2ecf20Sopenharmony_ci int width = norm_maxw(dev); 3278c2ecf20Sopenharmony_ci int height = norm_maxh(dev); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Properly setup VBI */ 3308c2ecf20Sopenharmony_ci v4l2->vbi_width = 720; 3318c2ecf20Sopenharmony_ci if (v4l2->norm & V4L2_STD_525_60) 3328c2ecf20Sopenharmony_ci v4l2->vbi_height = 12; 3338c2ecf20Sopenharmony_ci else 3348c2ecf20Sopenharmony_ci v4l2->vbi_height = 18; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci em28xx_set_outfmt(dev); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * If we don't set the start position to 2 in VBI mode, we end up 3428c2ecf20Sopenharmony_ci * with line 20/21 being YUYV encoded instead of being in 8-bit 3438c2ecf20Sopenharmony_ci * greyscale. The core of the issue is that line 21 (and line 23 for 3448c2ecf20Sopenharmony_ci * PAL WSS) are inside of active video region, and as a result they 3458c2ecf20Sopenharmony_ci * get the pixelformatting associated with that area. So by cropping 3468c2ecf20Sopenharmony_ci * it out, we end up with the same format as the rest of the VBI 3478c2ecf20Sopenharmony_ci * region 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci if (em28xx_vbi_supported(dev) == 1) 3508c2ecf20Sopenharmony_ci em28xx_capture_area_set(dev, 0, 2, width, height); 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci em28xx_capture_area_set(dev, 0, 0, width, height); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* Set USB alternate setting for analog video */ 3588c2ecf20Sopenharmony_cistatic int em28xx_set_alternate(struct em28xx *dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 3618c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(dev->intf); 3628c2ecf20Sopenharmony_ci int err; 3638c2ecf20Sopenharmony_ci int i; 3648c2ecf20Sopenharmony_ci unsigned int min_pkt_size = v4l2->width * 2 + 4; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * NOTE: for isoc transfers, only alt settings > 0 are allowed 3688c2ecf20Sopenharmony_ci * bulk transfers seem to work only with alt=0 ! 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci dev->alt = 0; 3718c2ecf20Sopenharmony_ci if (alt > 0 && alt < dev->num_alt) { 3728c2ecf20Sopenharmony_ci em28xx_videodbg("alternate forced to %d\n", dev->alt); 3738c2ecf20Sopenharmony_ci dev->alt = alt; 3748c2ecf20Sopenharmony_ci goto set_alt; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci if (dev->analog_xfer_bulk) 3778c2ecf20Sopenharmony_ci goto set_alt; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * When image size is bigger than a certain value, 3818c2ecf20Sopenharmony_ci * the frame size should be increased, otherwise, only 3828c2ecf20Sopenharmony_ci * green screen will be received. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2) 3858c2ecf20Sopenharmony_ci min_pkt_size *= 2; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_alt; i++) { 3888c2ecf20Sopenharmony_ci /* stop when the selected alt setting offers enough bandwidth */ 3898c2ecf20Sopenharmony_ci if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { 3908c2ecf20Sopenharmony_ci dev->alt = i; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * otherwise make sure that we end up with the maximum 3948c2ecf20Sopenharmony_ci * bandwidth because the min_pkt_size equation might be wrong. 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci } else if (dev->alt_max_pkt_size_isoc[i] > 3988c2ecf20Sopenharmony_ci dev->alt_max_pkt_size_isoc[dev->alt]) 3998c2ecf20Sopenharmony_ci dev->alt = i; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ciset_alt: 4038c2ecf20Sopenharmony_ci /* 4048c2ecf20Sopenharmony_ci * NOTE: for bulk transfers, we need to call usb_set_interface() 4058c2ecf20Sopenharmony_ci * even if the previous settings were the same. Otherwise streaming 4068c2ecf20Sopenharmony_ci * fails with all urbs having status = -EOVERFLOW ! 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci if (dev->analog_xfer_bulk) { 4098c2ecf20Sopenharmony_ci dev->max_pkt_size = 512; /* USB 2.0 spec */ 4108c2ecf20Sopenharmony_ci dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; 4118c2ecf20Sopenharmony_ci } else { /* isoc */ 4128c2ecf20Sopenharmony_ci em28xx_videodbg("minimum isoc packet size: %u (alt=%d)\n", 4138c2ecf20Sopenharmony_ci min_pkt_size, dev->alt); 4148c2ecf20Sopenharmony_ci dev->max_pkt_size = 4158c2ecf20Sopenharmony_ci dev->alt_max_pkt_size_isoc[dev->alt]; 4168c2ecf20Sopenharmony_ci dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n", 4198c2ecf20Sopenharmony_ci dev->alt, dev->max_pkt_size); 4208c2ecf20Sopenharmony_ci err = usb_set_interface(udev, dev->ifnum, dev->alt); 4218c2ecf20Sopenharmony_ci if (err < 0) { 4228c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 4238c2ecf20Sopenharmony_ci "cannot change alternate number to %d (error=%i)\n", 4248c2ecf20Sopenharmony_ci dev->alt, err); 4258c2ecf20Sopenharmony_ci return err; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* 4318c2ecf20Sopenharmony_ci * DMA and thread functions 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* 4358c2ecf20Sopenharmony_ci * Finish the current buffer 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_cistatic inline void finish_buffer(struct em28xx *dev, 4388c2ecf20Sopenharmony_ci struct em28xx_buffer *buf) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci buf->vb.sequence = dev->v4l2->field_count++; 4438c2ecf20Sopenharmony_ci if (dev->v4l2->progressive) 4448c2ecf20Sopenharmony_ci buf->vb.field = V4L2_FIELD_NONE; 4458c2ecf20Sopenharmony_ci else 4468c2ecf20Sopenharmony_ci buf->vb.field = V4L2_FIELD_INTERLACED; 4478c2ecf20Sopenharmony_ci buf->vb.vb2_buf.timestamp = ktime_get_ns(); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* 4538c2ecf20Sopenharmony_ci * Copy picture data from USB buffer to videobuf buffer 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_cistatic void em28xx_copy_video(struct em28xx *dev, 4568c2ecf20Sopenharmony_ci struct em28xx_buffer *buf, 4578c2ecf20Sopenharmony_ci unsigned char *usb_buf, 4588c2ecf20Sopenharmony_ci unsigned long len) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 4618c2ecf20Sopenharmony_ci void *fieldstart, *startwrite, *startread; 4628c2ecf20Sopenharmony_ci int linesdone, currlinedone, offset, lencopy, remain; 4638c2ecf20Sopenharmony_ci int bytesperline = v4l2->width << 1; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (buf->pos + len > buf->length) 4668c2ecf20Sopenharmony_ci len = buf->length - buf->pos; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci startread = usb_buf; 4698c2ecf20Sopenharmony_ci remain = len; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (v4l2->progressive || buf->top_field) 4728c2ecf20Sopenharmony_ci fieldstart = buf->vb_buf; 4738c2ecf20Sopenharmony_ci else /* interlaced mode, even nr. of lines */ 4748c2ecf20Sopenharmony_ci fieldstart = buf->vb_buf + bytesperline; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci linesdone = buf->pos / bytesperline; 4778c2ecf20Sopenharmony_ci currlinedone = buf->pos % bytesperline; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (v4l2->progressive) 4808c2ecf20Sopenharmony_ci offset = linesdone * bytesperline + currlinedone; 4818c2ecf20Sopenharmony_ci else 4828c2ecf20Sopenharmony_ci offset = linesdone * bytesperline * 2 + currlinedone; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci startwrite = fieldstart + offset; 4858c2ecf20Sopenharmony_ci lencopy = bytesperline - currlinedone; 4868c2ecf20Sopenharmony_ci lencopy = lencopy > remain ? remain : lencopy; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { 4898c2ecf20Sopenharmony_ci em28xx_isocdbg("Overflow of %zu bytes past buffer end (1)\n", 4908c2ecf20Sopenharmony_ci ((char *)startwrite + lencopy) - 4918c2ecf20Sopenharmony_ci ((char *)buf->vb_buf + buf->length)); 4928c2ecf20Sopenharmony_ci remain = (char *)buf->vb_buf + buf->length - 4938c2ecf20Sopenharmony_ci (char *)startwrite; 4948c2ecf20Sopenharmony_ci lencopy = remain; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci if (lencopy <= 0) 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci memcpy(startwrite, startread, lencopy); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci remain -= lencopy; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci while (remain > 0) { 5038c2ecf20Sopenharmony_ci if (v4l2->progressive) 5048c2ecf20Sopenharmony_ci startwrite += lencopy; 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci startwrite += lencopy + bytesperline; 5078c2ecf20Sopenharmony_ci startread += lencopy; 5088c2ecf20Sopenharmony_ci if (bytesperline > remain) 5098c2ecf20Sopenharmony_ci lencopy = remain; 5108c2ecf20Sopenharmony_ci else 5118c2ecf20Sopenharmony_ci lencopy = bytesperline; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if ((char *)startwrite + lencopy > (char *)buf->vb_buf + 5148c2ecf20Sopenharmony_ci buf->length) { 5158c2ecf20Sopenharmony_ci em28xx_isocdbg("Overflow of %zu bytes past buffer end(2)\n", 5168c2ecf20Sopenharmony_ci ((char *)startwrite + lencopy) - 5178c2ecf20Sopenharmony_ci ((char *)buf->vb_buf + buf->length)); 5188c2ecf20Sopenharmony_ci remain = (char *)buf->vb_buf + buf->length - 5198c2ecf20Sopenharmony_ci (char *)startwrite; 5208c2ecf20Sopenharmony_ci lencopy = remain; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci if (lencopy <= 0) 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci memcpy(startwrite, startread, lencopy); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci remain -= lencopy; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci buf->pos += len; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* 5348c2ecf20Sopenharmony_ci * Copy VBI data from USB buffer to videobuf buffer 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_cistatic void em28xx_copy_vbi(struct em28xx *dev, 5378c2ecf20Sopenharmony_ci struct em28xx_buffer *buf, 5388c2ecf20Sopenharmony_ci unsigned char *usb_buf, 5398c2ecf20Sopenharmony_ci unsigned long len) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci unsigned int offset; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (buf->pos + len > buf->length) 5448c2ecf20Sopenharmony_ci len = buf->length - buf->pos; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci offset = buf->pos; 5478c2ecf20Sopenharmony_ci /* Make sure the bottom field populates the second half of the frame */ 5488c2ecf20Sopenharmony_ci if (buf->top_field == 0) 5498c2ecf20Sopenharmony_ci offset += dev->v4l2->vbi_width * dev->v4l2->vbi_height; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci memcpy(buf->vb_buf + offset, usb_buf, len); 5528c2ecf20Sopenharmony_ci buf->pos += len; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic inline void print_err_status(struct em28xx *dev, 5568c2ecf20Sopenharmony_ci int packet, int status) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci char *errmsg = "Unknown"; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci switch (status) { 5618c2ecf20Sopenharmony_ci case -ENOENT: 5628c2ecf20Sopenharmony_ci errmsg = "unlinked synchronously"; 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci case -ECONNRESET: 5658c2ecf20Sopenharmony_ci errmsg = "unlinked asynchronously"; 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci case -ENOSR: 5688c2ecf20Sopenharmony_ci errmsg = "Buffer error (overrun)"; 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci case -EPIPE: 5718c2ecf20Sopenharmony_ci errmsg = "Stalled (device not responding)"; 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci case -EOVERFLOW: 5748c2ecf20Sopenharmony_ci errmsg = "Babble (bad cable?)"; 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci case -EPROTO: 5778c2ecf20Sopenharmony_ci errmsg = "Bit-stuff error (bad cable?)"; 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci case -EILSEQ: 5808c2ecf20Sopenharmony_ci errmsg = "CRC/Timeout (could be anything)"; 5818c2ecf20Sopenharmony_ci break; 5828c2ecf20Sopenharmony_ci case -ETIME: 5838c2ecf20Sopenharmony_ci errmsg = "Device does not respond"; 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci if (packet < 0) { 5878c2ecf20Sopenharmony_ci em28xx_isocdbg("URB status %d [%s].\n", status, errmsg); 5888c2ecf20Sopenharmony_ci } else { 5898c2ecf20Sopenharmony_ci em28xx_isocdbg("URB packet %d, status %d [%s].\n", 5908c2ecf20Sopenharmony_ci packet, status, errmsg); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci/* 5958c2ecf20Sopenharmony_ci * get the next available buffer from dma queue 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_cistatic inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, 5988c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *dma_q) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct em28xx_buffer *buf; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (list_empty(&dma_q->active)) { 6038c2ecf20Sopenharmony_ci em28xx_isocdbg("No active queue to serve\n"); 6048c2ecf20Sopenharmony_ci return NULL; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Get the next buffer */ 6088c2ecf20Sopenharmony_ci buf = list_entry(dma_q->active.next, struct em28xx_buffer, list); 6098c2ecf20Sopenharmony_ci /* Cleans up buffer - Useful for testing for frame/URB loss */ 6108c2ecf20Sopenharmony_ci list_del(&buf->list); 6118c2ecf20Sopenharmony_ci buf->pos = 0; 6128c2ecf20Sopenharmony_ci buf->vb_buf = buf->mem; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return buf; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci/* 6188c2ecf20Sopenharmony_ci * Finish the current buffer if completed and prepare for the next field 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_cistatic struct em28xx_buffer * 6218c2ecf20Sopenharmony_cifinish_field_prepare_next(struct em28xx *dev, 6228c2ecf20Sopenharmony_ci struct em28xx_buffer *buf, 6238c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *dma_q) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */ 6288c2ecf20Sopenharmony_ci if (buf) 6298c2ecf20Sopenharmony_ci finish_buffer(dev, buf); 6308c2ecf20Sopenharmony_ci buf = get_next_buf(dev, dma_q); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci if (buf) { 6338c2ecf20Sopenharmony_ci buf->top_field = v4l2->top_field; 6348c2ecf20Sopenharmony_ci buf->pos = 0; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return buf; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci/* 6418c2ecf20Sopenharmony_ci * Process data packet according to the em2710/em2750/em28xx frame data format 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_cistatic inline void process_frame_data_em28xx(struct em28xx *dev, 6448c2ecf20Sopenharmony_ci unsigned char *data_pkt, 6458c2ecf20Sopenharmony_ci unsigned int data_len) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 6488c2ecf20Sopenharmony_ci struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; 6498c2ecf20Sopenharmony_ci struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; 6508c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *dma_q = &dev->vidq; 6518c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* 6548c2ecf20Sopenharmony_ci * capture type 0 = vbi start 6558c2ecf20Sopenharmony_ci * capture type 1 = vbi in progress 6568c2ecf20Sopenharmony_ci * capture type 2 = video start 6578c2ecf20Sopenharmony_ci * capture type 3 = video in progress 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci if (data_len >= 4) { 6608c2ecf20Sopenharmony_ci /* 6618c2ecf20Sopenharmony_ci * NOTE: Headers are always 4 bytes and 6628c2ecf20Sopenharmony_ci * never split across packets 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && 6658c2ecf20Sopenharmony_ci data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { 6668c2ecf20Sopenharmony_ci /* Continuation */ 6678c2ecf20Sopenharmony_ci data_pkt += 4; 6688c2ecf20Sopenharmony_ci data_len -= 4; 6698c2ecf20Sopenharmony_ci } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { 6708c2ecf20Sopenharmony_ci /* Field start (VBI mode) */ 6718c2ecf20Sopenharmony_ci v4l2->capture_type = 0; 6728c2ecf20Sopenharmony_ci v4l2->vbi_read = 0; 6738c2ecf20Sopenharmony_ci em28xx_isocdbg("VBI START HEADER !!!\n"); 6748c2ecf20Sopenharmony_ci v4l2->top_field = !(data_pkt[2] & 1); 6758c2ecf20Sopenharmony_ci data_pkt += 4; 6768c2ecf20Sopenharmony_ci data_len -= 4; 6778c2ecf20Sopenharmony_ci } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { 6788c2ecf20Sopenharmony_ci /* Field start (VBI disabled) */ 6798c2ecf20Sopenharmony_ci v4l2->capture_type = 2; 6808c2ecf20Sopenharmony_ci em28xx_isocdbg("VIDEO START HEADER !!!\n"); 6818c2ecf20Sopenharmony_ci v4l2->top_field = !(data_pkt[2] & 1); 6828c2ecf20Sopenharmony_ci data_pkt += 4; 6838c2ecf20Sopenharmony_ci data_len -= 4; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci /* 6878c2ecf20Sopenharmony_ci * NOTE: With bulk transfers, intermediate data packets 6888c2ecf20Sopenharmony_ci * have no continuation header 6898c2ecf20Sopenharmony_ci */ 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (v4l2->capture_type == 0) { 6928c2ecf20Sopenharmony_ci vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); 6938c2ecf20Sopenharmony_ci dev->usb_ctl.vbi_buf = vbi_buf; 6948c2ecf20Sopenharmony_ci v4l2->capture_type = 1; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (v4l2->capture_type == 1) { 6988c2ecf20Sopenharmony_ci int vbi_size = v4l2->vbi_width * v4l2->vbi_height; 6998c2ecf20Sopenharmony_ci int vbi_data_len = ((v4l2->vbi_read + data_len) > vbi_size) ? 7008c2ecf20Sopenharmony_ci (vbi_size - v4l2->vbi_read) : data_len; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Copy VBI data */ 7038c2ecf20Sopenharmony_ci if (vbi_buf) 7048c2ecf20Sopenharmony_ci em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); 7058c2ecf20Sopenharmony_ci v4l2->vbi_read += vbi_data_len; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (vbi_data_len < data_len) { 7088c2ecf20Sopenharmony_ci /* Continue with copying video data */ 7098c2ecf20Sopenharmony_ci v4l2->capture_type = 2; 7108c2ecf20Sopenharmony_ci data_pkt += vbi_data_len; 7118c2ecf20Sopenharmony_ci data_len -= vbi_data_len; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (v4l2->capture_type == 2) { 7168c2ecf20Sopenharmony_ci buf = finish_field_prepare_next(dev, buf, dma_q); 7178c2ecf20Sopenharmony_ci dev->usb_ctl.vid_buf = buf; 7188c2ecf20Sopenharmony_ci v4l2->capture_type = 3; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (v4l2->capture_type == 3 && buf && data_len > 0) 7228c2ecf20Sopenharmony_ci em28xx_copy_video(dev, buf, data_pkt, data_len); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/* 7268c2ecf20Sopenharmony_ci * Process data packet according to the em25xx/em276x/7x/8x frame data format 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic inline void process_frame_data_em25xx(struct em28xx *dev, 7298c2ecf20Sopenharmony_ci unsigned char *data_pkt, 7308c2ecf20Sopenharmony_ci unsigned int data_len) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; 7338c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *dmaq = &dev->vidq; 7348c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 7358c2ecf20Sopenharmony_ci bool frame_end = false; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* Check for header */ 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * NOTE: at least with bulk transfers, only the first packet 7408c2ecf20Sopenharmony_ci * has a header and has always set the FRAME_END bit 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ci if (data_len >= 2) { /* em25xx header is only 2 bytes long */ 7438c2ecf20Sopenharmony_ci if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) && 7448c2ecf20Sopenharmony_ci ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) { 7458c2ecf20Sopenharmony_ci v4l2->top_field = !(data_pkt[1] & 7468c2ecf20Sopenharmony_ci EM25XX_FRMDATAHDR_BYTE2_FRAME_ID); 7478c2ecf20Sopenharmony_ci frame_end = data_pkt[1] & 7488c2ecf20Sopenharmony_ci EM25XX_FRMDATAHDR_BYTE2_FRAME_END; 7498c2ecf20Sopenharmony_ci data_pkt += 2; 7508c2ecf20Sopenharmony_ci data_len -= 2; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* Finish field and prepare next (BULK only) */ 7548c2ecf20Sopenharmony_ci if (dev->analog_xfer_bulk && frame_end) { 7558c2ecf20Sopenharmony_ci buf = finish_field_prepare_next(dev, buf, dmaq); 7568c2ecf20Sopenharmony_ci dev->usb_ctl.vid_buf = buf; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci /* 7598c2ecf20Sopenharmony_ci * NOTE: in ISOC mode when a new frame starts and buf==NULL, 7608c2ecf20Sopenharmony_ci * we COULD already prepare a buffer here to avoid skipping the 7618c2ecf20Sopenharmony_ci * first frame. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Copy data */ 7668c2ecf20Sopenharmony_ci if (buf && data_len > 0) 7678c2ecf20Sopenharmony_ci em28xx_copy_video(dev, buf, data_pkt, data_len); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Finish frame (ISOC only) => avoids lag of 1 frame */ 7708c2ecf20Sopenharmony_ci if (!dev->analog_xfer_bulk && frame_end) { 7718c2ecf20Sopenharmony_ci buf = finish_field_prepare_next(dev, buf, dmaq); 7728c2ecf20Sopenharmony_ci dev->usb_ctl.vid_buf = buf; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* 7768c2ecf20Sopenharmony_ci * NOTES: 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * 1) Tested with USB bulk transfers only ! 7798c2ecf20Sopenharmony_ci * The wording in the datasheet suggests that isoc might work different. 7808c2ecf20Sopenharmony_ci * The current code assumes that with isoc transfers each packet has a 7818c2ecf20Sopenharmony_ci * header like with the other em28xx devices. 7828c2ecf20Sopenharmony_ci * 7838c2ecf20Sopenharmony_ci * 2) Support for interlaced mode is pure theory. It has not been 7848c2ecf20Sopenharmony_ci * tested and it is unknown if these devices actually support it. 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci/* Processes and copies the URB data content (video and VBI data) */ 7898c2ecf20Sopenharmony_cistatic inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci int xfer_bulk, num_packets, i; 7928c2ecf20Sopenharmony_ci unsigned char *usb_data_pkt; 7938c2ecf20Sopenharmony_ci unsigned int usb_data_len; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (!dev) 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (dev->disconnected) 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (urb->status < 0) 8028c2ecf20Sopenharmony_ci print_err_status(dev, -1, urb->status); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci xfer_bulk = usb_pipebulk(urb->pipe); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (xfer_bulk) /* bulk */ 8078c2ecf20Sopenharmony_ci num_packets = 1; 8088c2ecf20Sopenharmony_ci else /* isoc */ 8098c2ecf20Sopenharmony_ci num_packets = urb->number_of_packets; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci for (i = 0; i < num_packets; i++) { 8128c2ecf20Sopenharmony_ci if (xfer_bulk) { /* bulk */ 8138c2ecf20Sopenharmony_ci usb_data_len = urb->actual_length; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci usb_data_pkt = urb->transfer_buffer; 8168c2ecf20Sopenharmony_ci } else { /* isoc */ 8178c2ecf20Sopenharmony_ci if (urb->iso_frame_desc[i].status < 0) { 8188c2ecf20Sopenharmony_ci print_err_status(dev, i, 8198c2ecf20Sopenharmony_ci urb->iso_frame_desc[i].status); 8208c2ecf20Sopenharmony_ci if (urb->iso_frame_desc[i].status != -EPROTO) 8218c2ecf20Sopenharmony_ci continue; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci usb_data_len = urb->iso_frame_desc[i].actual_length; 8258c2ecf20Sopenharmony_ci if (usb_data_len > dev->max_pkt_size) { 8268c2ecf20Sopenharmony_ci em28xx_isocdbg("packet bigger than packet size"); 8278c2ecf20Sopenharmony_ci continue; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci usb_data_pkt = urb->transfer_buffer + 8318c2ecf20Sopenharmony_ci urb->iso_frame_desc[i].offset; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (usb_data_len == 0) { 8358c2ecf20Sopenharmony_ci /* NOTE: happens very often with isoc transfers */ 8368c2ecf20Sopenharmony_ci /* em28xx_usbdbg("packet %d is empty",i); - spammy */ 8378c2ecf20Sopenharmony_ci continue; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (dev->is_em25xx) 8418c2ecf20Sopenharmony_ci process_frame_data_em25xx(dev, 8428c2ecf20Sopenharmony_ci usb_data_pkt, usb_data_len); 8438c2ecf20Sopenharmony_ci else 8448c2ecf20Sopenharmony_ci process_frame_data_em28xx(dev, 8458c2ecf20Sopenharmony_ci usb_data_pkt, usb_data_len); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci return 1; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic int get_resource(enum v4l2_buf_type f_type) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci switch (f_type) { 8538c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 8548c2ecf20Sopenharmony_ci return EM28XX_RESOURCE_VIDEO; 8558c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VBI_CAPTURE: 8568c2ecf20Sopenharmony_ci return EM28XX_RESOURCE_VBI; 8578c2ecf20Sopenharmony_ci default: 8588c2ecf20Sopenharmony_ci WARN_ON(1); 8598c2ecf20Sopenharmony_ci return -1; /* Indicate that device is busy */ 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci/* Usage lock check functions */ 8648c2ecf20Sopenharmony_cistatic int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci int res_type = get_resource(f_type); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* is it free? */ 8698c2ecf20Sopenharmony_ci if (dev->resources & res_type) { 8708c2ecf20Sopenharmony_ci /* no, someone else uses it */ 8718c2ecf20Sopenharmony_ci return -EBUSY; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* it's free, grab it */ 8758c2ecf20Sopenharmony_ci dev->resources |= res_type; 8768c2ecf20Sopenharmony_ci em28xx_videodbg("res: get %d\n", res_type); 8778c2ecf20Sopenharmony_ci return 0; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci int res_type = get_resource(f_type); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci dev->resources &= ~res_type; 8858c2ecf20Sopenharmony_ci em28xx_videodbg("res: put %d\n", res_type); 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void em28xx_v4l2_media_release(struct em28xx *dev) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 8918c2ecf20Sopenharmony_ci int i; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci for (i = 0; i < MAX_EM28XX_INPUT; i++) { 8948c2ecf20Sopenharmony_ci if (!INPUT(i)->type) 8958c2ecf20Sopenharmony_ci return; 8968c2ecf20Sopenharmony_ci media_device_unregister_entity(&dev->input_ent[i]); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci#endif 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/* 9028c2ecf20Sopenharmony_ci * Media Controller helper functions 9038c2ecf20Sopenharmony_ci */ 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int em28xx_enable_analog_tuner(struct em28xx *dev) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 9088c2ecf20Sopenharmony_ci struct media_device *mdev = dev->media_dev; 9098c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 9108c2ecf20Sopenharmony_ci struct media_entity *source; 9118c2ecf20Sopenharmony_ci struct media_link *link, *found_link = NULL; 9128c2ecf20Sopenharmony_ci int ret, active_links = 0; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (!mdev || !v4l2->decoder) 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* 9188c2ecf20Sopenharmony_ci * This will find the tuner that is connected into the decoder. 9198c2ecf20Sopenharmony_ci * Technically, this is not 100% correct, as the device may be 9208c2ecf20Sopenharmony_ci * using an analog input instead of the tuner. However, as we can't 9218c2ecf20Sopenharmony_ci * do DVB streaming while the DMA engine is being used for V4L2, 9228c2ecf20Sopenharmony_ci * this should be enough for the actual needs. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_ci list_for_each_entry(link, &v4l2->decoder->links, list) { 9258c2ecf20Sopenharmony_ci if (link->sink->entity == v4l2->decoder) { 9268c2ecf20Sopenharmony_ci found_link = link; 9278c2ecf20Sopenharmony_ci if (link->flags & MEDIA_LNK_FL_ENABLED) 9288c2ecf20Sopenharmony_ci active_links++; 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (active_links == 1 || !found_link) 9348c2ecf20Sopenharmony_ci return 0; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci source = found_link->source->entity; 9378c2ecf20Sopenharmony_ci list_for_each_entry(link, &source->links, list) { 9388c2ecf20Sopenharmony_ci struct media_entity *sink; 9398c2ecf20Sopenharmony_ci int flags = 0; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci sink = link->sink->entity; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (sink == v4l2->decoder) 9448c2ecf20Sopenharmony_ci flags = MEDIA_LNK_FL_ENABLED; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ret = media_entity_setup_link(link, flags); 9478c2ecf20Sopenharmony_ci if (ret) { 9488c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 9498c2ecf20Sopenharmony_ci "Couldn't change link %s->%s to %s. Error %d\n", 9508c2ecf20Sopenharmony_ci source->name, sink->name, 9518c2ecf20Sopenharmony_ci flags ? "enabled" : "disabled", 9528c2ecf20Sopenharmony_ci ret); 9538c2ecf20Sopenharmony_ci return ret; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci em28xx_videodbg("link %s->%s was %s\n", 9578c2ecf20Sopenharmony_ci source->name, sink->name, 9588c2ecf20Sopenharmony_ci flags ? "ENABLED" : "disabled"); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci#endif 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic const char * const iname[] = { 9658c2ecf20Sopenharmony_ci [EM28XX_VMUX_COMPOSITE] = "Composite", 9668c2ecf20Sopenharmony_ci [EM28XX_VMUX_SVIDEO] = "S-Video", 9678c2ecf20Sopenharmony_ci [EM28XX_VMUX_TELEVISION] = "Television", 9688c2ecf20Sopenharmony_ci [EM28XX_RADIO] = "Radio", 9698c2ecf20Sopenharmony_ci}; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic void em28xx_v4l2_create_entities(struct em28xx *dev) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER) 9748c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 9758c2ecf20Sopenharmony_ci int ret, i; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* Initialize Video, VBI and Radio pads */ 9788c2ecf20Sopenharmony_ci v4l2->video_pad.flags = MEDIA_PAD_FL_SINK; 9798c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&v4l2->vdev.entity, 1, &v4l2->video_pad); 9808c2ecf20Sopenharmony_ci if (ret < 0) 9818c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 9828c2ecf20Sopenharmony_ci "failed to initialize video media entity!\n"); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (em28xx_vbi_supported(dev)) { 9858c2ecf20Sopenharmony_ci v4l2->vbi_pad.flags = MEDIA_PAD_FL_SINK; 9868c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&v4l2->vbi_dev.entity, 1, 9878c2ecf20Sopenharmony_ci &v4l2->vbi_pad); 9888c2ecf20Sopenharmony_ci if (ret < 0) 9898c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 9908c2ecf20Sopenharmony_ci "failed to initialize vbi media entity!\n"); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* Webcams don't have input connectors */ 9948c2ecf20Sopenharmony_ci if (dev->is_webcam) 9958c2ecf20Sopenharmony_ci return; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* Create entities for each input connector */ 9988c2ecf20Sopenharmony_ci for (i = 0; i < MAX_EM28XX_INPUT; i++) { 9998c2ecf20Sopenharmony_ci struct media_entity *ent = &dev->input_ent[i]; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (!INPUT(i)->type) 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci ent->name = iname[INPUT(i)->type]; 10058c2ecf20Sopenharmony_ci ent->flags = MEDIA_ENT_FL_CONNECTOR; 10068c2ecf20Sopenharmony_ci dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci switch (INPUT(i)->type) { 10098c2ecf20Sopenharmony_ci case EM28XX_VMUX_COMPOSITE: 10108c2ecf20Sopenharmony_ci ent->function = MEDIA_ENT_F_CONN_COMPOSITE; 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci case EM28XX_VMUX_SVIDEO: 10138c2ecf20Sopenharmony_ci ent->function = MEDIA_ENT_F_CONN_SVIDEO; 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci default: /* EM28XX_VMUX_TELEVISION or EM28XX_RADIO */ 10168c2ecf20Sopenharmony_ci if (dev->tuner_type != TUNER_ABSENT) 10178c2ecf20Sopenharmony_ci ent->function = MEDIA_ENT_F_CONN_RF; 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]); 10228c2ecf20Sopenharmony_ci if (ret < 0) 10238c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 10248c2ecf20Sopenharmony_ci "failed to initialize input pad[%d]!\n", i); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci ret = media_device_register_entity(dev->media_dev, ent); 10278c2ecf20Sopenharmony_ci if (ret < 0) 10288c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 10298c2ecf20Sopenharmony_ci "failed to register input entity %d!\n", i); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci#endif 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci/* 10358c2ecf20Sopenharmony_ci * Videobuf2 operations 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 10398c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 10408c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct em28xx *dev = vb2_get_drv_priv(vq); 10438c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 10448c2ecf20Sopenharmony_ci unsigned long size = 10458c2ecf20Sopenharmony_ci (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (*nplanes) 10488c2ecf20Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 10498c2ecf20Sopenharmony_ci *nplanes = 1; 10508c2ecf20Sopenharmony_ci sizes[0] = size; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci em28xx_enable_analog_tuner(dev); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci return 0; 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic int 10588c2ecf20Sopenharmony_cibuffer_prepare(struct vb2_buffer *vb) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 10618c2ecf20Sopenharmony_ci struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); 10628c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 10638c2ecf20Sopenharmony_ci unsigned long size; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci em28xx_videodbg("%s, field=%d\n", __func__, vbuf->field); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci size = (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 10708c2ecf20Sopenharmony_ci em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n", 10718c2ecf20Sopenharmony_ci __func__, vb2_plane_size(vb, 0), size); 10728c2ecf20Sopenharmony_ci return -EINVAL; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci return 0; 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ciint em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci struct em28xx *dev = vb2_get_drv_priv(vq); 10828c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 10838c2ecf20Sopenharmony_ci struct v4l2_frequency f; 10848c2ecf20Sopenharmony_ci struct v4l2_fh *owner; 10858c2ecf20Sopenharmony_ci int rc = 0; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci em28xx_videodbg("%s\n", __func__); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci dev->v4l2->field_count = 0; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* 10928c2ecf20Sopenharmony_ci * Make sure streaming is not already in progress for this type 10938c2ecf20Sopenharmony_ci * of filehandle (e.g. video, vbi) 10948c2ecf20Sopenharmony_ci */ 10958c2ecf20Sopenharmony_ci rc = res_get(dev, vq->type); 10968c2ecf20Sopenharmony_ci if (rc) 10978c2ecf20Sopenharmony_ci return rc; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (v4l2->streaming_users == 0) { 11008c2ecf20Sopenharmony_ci /* First active streaming user, so allocate all the URBs */ 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* Allocate the USB bandwidth */ 11038c2ecf20Sopenharmony_ci em28xx_set_alternate(dev); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* 11068c2ecf20Sopenharmony_ci * Needed, since GPIO might have disabled power of 11078c2ecf20Sopenharmony_ci * some i2c device 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_ci em28xx_wake_i2c(dev); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci v4l2->capture_type = -1; 11128c2ecf20Sopenharmony_ci rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 11138c2ecf20Sopenharmony_ci dev->analog_xfer_bulk, 11148c2ecf20Sopenharmony_ci EM28XX_NUM_BUFS, 11158c2ecf20Sopenharmony_ci dev->max_pkt_size, 11168c2ecf20Sopenharmony_ci dev->packet_multiplier, 11178c2ecf20Sopenharmony_ci em28xx_urb_data_copy); 11188c2ecf20Sopenharmony_ci if (rc < 0) 11198c2ecf20Sopenharmony_ci return rc; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* 11228c2ecf20Sopenharmony_ci * djh: it's not clear whether this code is still needed. I'm 11238c2ecf20Sopenharmony_ci * leaving it in here for now entirely out of concern for 11248c2ecf20Sopenharmony_ci * backward compatibility (the old code did it) 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* Ask tuner to go to analog or radio mode */ 11288c2ecf20Sopenharmony_ci memset(&f, 0, sizeof(f)); 11298c2ecf20Sopenharmony_ci f.frequency = v4l2->frequency; 11308c2ecf20Sopenharmony_ci owner = (struct v4l2_fh *)vq->owner; 11318c2ecf20Sopenharmony_ci if (owner && owner->vdev->vfl_type == VFL_TYPE_RADIO) 11328c2ecf20Sopenharmony_ci f.type = V4L2_TUNER_RADIO; 11338c2ecf20Sopenharmony_ci else 11348c2ecf20Sopenharmony_ci f.type = V4L2_TUNER_ANALOG_TV; 11358c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 11368c2ecf20Sopenharmony_ci 0, tuner, s_frequency, &f); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci /* Enable video stream at TV decoder */ 11398c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 1); 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci v4l2->streaming_users++; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return rc; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void em28xx_stop_streaming(struct vb2_queue *vq) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct em28xx *dev = vb2_get_drv_priv(vq); 11508c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 11518c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *vidq = &dev->vidq; 11528c2ecf20Sopenharmony_ci unsigned long flags = 0; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci em28xx_videodbg("%s\n", __func__); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci res_free(dev, vq->type); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (v4l2->streaming_users-- == 1) { 11598c2ecf20Sopenharmony_ci /* Disable video stream at TV decoder */ 11608c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* Last active user, so shutdown all the URBS */ 11638c2ecf20Sopenharmony_ci em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 11678c2ecf20Sopenharmony_ci if (dev->usb_ctl.vid_buf) { 11688c2ecf20Sopenharmony_ci vb2_buffer_done(&dev->usb_ctl.vid_buf->vb.vb2_buf, 11698c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR); 11708c2ecf20Sopenharmony_ci dev->usb_ctl.vid_buf = NULL; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci while (!list_empty(&vidq->active)) { 11738c2ecf20Sopenharmony_ci struct em28xx_buffer *buf; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci buf = list_entry(vidq->active.next, struct em28xx_buffer, list); 11768c2ecf20Sopenharmony_ci list_del(&buf->list); 11778c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_civoid em28xx_stop_vbi_streaming(struct vb2_queue *vq) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct em28xx *dev = vb2_get_drv_priv(vq); 11858c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 11868c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *vbiq = &dev->vbiq; 11878c2ecf20Sopenharmony_ci unsigned long flags = 0; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci em28xx_videodbg("%s\n", __func__); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci res_free(dev, vq->type); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (v4l2->streaming_users-- == 1) { 11948c2ecf20Sopenharmony_ci /* Disable video stream at TV decoder */ 11958c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* Last active user, so shutdown all the URBS */ 11988c2ecf20Sopenharmony_ci em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 12028c2ecf20Sopenharmony_ci if (dev->usb_ctl.vbi_buf) { 12038c2ecf20Sopenharmony_ci vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb.vb2_buf, 12048c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR); 12058c2ecf20Sopenharmony_ci dev->usb_ctl.vbi_buf = NULL; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci while (!list_empty(&vbiq->active)) { 12088c2ecf20Sopenharmony_ci struct em28xx_buffer *buf; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci buf = list_entry(vbiq->active.next, struct em28xx_buffer, list); 12118c2ecf20Sopenharmony_ci list_del(&buf->list); 12128c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void 12188c2ecf20Sopenharmony_cibuffer_queue(struct vb2_buffer *vb) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 12218c2ecf20Sopenharmony_ci struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); 12228c2ecf20Sopenharmony_ci struct em28xx_buffer *buf = 12238c2ecf20Sopenharmony_ci container_of(vbuf, struct em28xx_buffer, vb); 12248c2ecf20Sopenharmony_ci struct em28xx_dmaqueue *vidq = &dev->vidq; 12258c2ecf20Sopenharmony_ci unsigned long flags = 0; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci em28xx_videodbg("%s\n", __func__); 12288c2ecf20Sopenharmony_ci buf->mem = vb2_plane_vaddr(vb, 0); 12298c2ecf20Sopenharmony_ci buf->length = vb2_plane_size(vb, 0); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 12328c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &vidq->active); 12338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic const struct vb2_ops em28xx_video_qops = { 12378c2ecf20Sopenharmony_ci .queue_setup = queue_setup, 12388c2ecf20Sopenharmony_ci .buf_prepare = buffer_prepare, 12398c2ecf20Sopenharmony_ci .buf_queue = buffer_queue, 12408c2ecf20Sopenharmony_ci .start_streaming = em28xx_start_analog_streaming, 12418c2ecf20Sopenharmony_ci .stop_streaming = em28xx_stop_streaming, 12428c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 12438c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 12448c2ecf20Sopenharmony_ci}; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_cistatic int em28xx_vb2_setup(struct em28xx *dev) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci int rc; 12498c2ecf20Sopenharmony_ci struct vb2_queue *q; 12508c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* Setup Videobuf2 for Video capture */ 12538c2ecf20Sopenharmony_ci q = &v4l2->vb_vidq; 12548c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 12558c2ecf20Sopenharmony_ci q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 12568c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 12578c2ecf20Sopenharmony_ci q->drv_priv = dev; 12588c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct em28xx_buffer); 12598c2ecf20Sopenharmony_ci q->ops = &em28xx_video_qops; 12608c2ecf20Sopenharmony_ci q->mem_ops = &vb2_vmalloc_memops; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci rc = vb2_queue_init(q); 12638c2ecf20Sopenharmony_ci if (rc < 0) 12648c2ecf20Sopenharmony_ci return rc; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Setup Videobuf2 for VBI capture */ 12678c2ecf20Sopenharmony_ci q = &v4l2->vb_vbiq; 12688c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VBI_CAPTURE; 12698c2ecf20Sopenharmony_ci q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; 12708c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 12718c2ecf20Sopenharmony_ci q->drv_priv = dev; 12728c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct em28xx_buffer); 12738c2ecf20Sopenharmony_ci q->ops = &em28xx_vbi_qops; 12748c2ecf20Sopenharmony_ci q->mem_ops = &vb2_vmalloc_memops; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci rc = vb2_queue_init(q); 12778c2ecf20Sopenharmony_ci if (rc < 0) 12788c2ecf20Sopenharmony_ci return rc; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci/* 12848c2ecf20Sopenharmony_ci * v4l2 interface 12858c2ecf20Sopenharmony_ci */ 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void video_mux(struct em28xx *dev, int index) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci dev->ctl_input = index; 12928c2ecf20Sopenharmony_ci dev->ctl_ainput = INPUT(index)->amux; 12938c2ecf20Sopenharmony_ci dev->ctl_aoutput = INPUT(index)->aout; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (!dev->ctl_aoutput) 12968c2ecf20Sopenharmony_ci dev->ctl_aoutput = EM28XX_AOUT_MASTER; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, video, s_routing, 12998c2ecf20Sopenharmony_ci INPUT(index)->vmux, 0, 0); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci if (dev->has_msp34xx) { 13028c2ecf20Sopenharmony_ci if (dev->i2s_speed) { 13038c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, audio, 13048c2ecf20Sopenharmony_ci s_i2s_clock_freq, dev->i2s_speed); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci /* Note: this is msp3400 specific */ 13078c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, 13088c2ecf20Sopenharmony_ci dev->ctl_ainput, 13098c2ecf20Sopenharmony_ci MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (dev->board.adecoder != EM28XX_NOADECODER) { 13138c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, 13148c2ecf20Sopenharmony_ci dev->ctl_ainput, dev->ctl_aoutput, 0); 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci em28xx_audio_analog_set(dev); 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci struct em28xx *dev = priv; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* 13258c2ecf20Sopenharmony_ci * In the case of non-AC97 volume controls, we still need 13268c2ecf20Sopenharmony_ci * to do some setups at em28xx, in order to mute/unmute 13278c2ecf20Sopenharmony_ci * and to adjust audio volume. However, the value ranges 13288c2ecf20Sopenharmony_ci * should be checked by the corresponding V4L subdriver. 13298c2ecf20Sopenharmony_ci */ 13308c2ecf20Sopenharmony_ci switch (ctrl->id) { 13318c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 13328c2ecf20Sopenharmony_ci dev->mute = ctrl->val; 13338c2ecf20Sopenharmony_ci em28xx_audio_analog_set(dev); 13348c2ecf20Sopenharmony_ci break; 13358c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: 13368c2ecf20Sopenharmony_ci dev->volume = ctrl->val; 13378c2ecf20Sopenharmony_ci em28xx_audio_analog_set(dev); 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = 13458c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct em28xx_v4l2, ctrl_handler); 13468c2ecf20Sopenharmony_ci struct em28xx *dev = v4l2->dev; 13478c2ecf20Sopenharmony_ci int ret = -EINVAL; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci switch (ctrl->id) { 13508c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 13518c2ecf20Sopenharmony_ci dev->mute = ctrl->val; 13528c2ecf20Sopenharmony_ci ret = em28xx_audio_analog_set(dev); 13538c2ecf20Sopenharmony_ci break; 13548c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: 13558c2ecf20Sopenharmony_ci dev->volume = ctrl->val; 13568c2ecf20Sopenharmony_ci ret = em28xx_audio_analog_set(dev); 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 13598c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R20_YGAIN, ctrl->val); 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 13628c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R21_YOFFSET, ctrl->val); 13638c2ecf20Sopenharmony_ci break; 13648c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 13658c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R22_UVGAIN, ctrl->val); 13668c2ecf20Sopenharmony_ci break; 13678c2ecf20Sopenharmony_ci case V4L2_CID_BLUE_BALANCE: 13688c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R23_UOFFSET, ctrl->val); 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci case V4L2_CID_RED_BALANCE: 13718c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R24_VOFFSET, ctrl->val); 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 13748c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, ctrl->val); 13758c2ecf20Sopenharmony_ci break; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci return (ret < 0) ? ret : 0; 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops em28xx_ctrl_ops = { 13828c2ecf20Sopenharmony_ci .s_ctrl = em28xx_s_ctrl, 13838c2ecf20Sopenharmony_ci}; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cistatic void size_to_scale(struct em28xx *dev, 13868c2ecf20Sopenharmony_ci unsigned int width, unsigned int height, 13878c2ecf20Sopenharmony_ci unsigned int *hscale, unsigned int *vscale) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci unsigned int maxw = norm_maxw(dev); 13908c2ecf20Sopenharmony_ci unsigned int maxh = norm_maxh(dev); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci *hscale = (((unsigned long)maxw) << 12) / width - 4096L; 13938c2ecf20Sopenharmony_ci if (*hscale > EM28XX_HVSCALE_MAX) 13948c2ecf20Sopenharmony_ci *hscale = EM28XX_HVSCALE_MAX; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci *vscale = (((unsigned long)maxh) << 12) / height - 4096L; 13978c2ecf20Sopenharmony_ci if (*vscale > EM28XX_HVSCALE_MAX) 13988c2ecf20Sopenharmony_ci *vscale = EM28XX_HVSCALE_MAX; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic void scale_to_size(struct em28xx *dev, 14028c2ecf20Sopenharmony_ci unsigned int hscale, unsigned int vscale, 14038c2ecf20Sopenharmony_ci unsigned int *width, unsigned int *height) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci unsigned int maxw = norm_maxw(dev); 14068c2ecf20Sopenharmony_ci unsigned int maxh = norm_maxh(dev); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci *width = (((unsigned long)maxw) << 12) / (hscale + 4096L); 14098c2ecf20Sopenharmony_ci *height = (((unsigned long)maxh) << 12) / (vscale + 4096L); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* Don't let width or height to be zero */ 14128c2ecf20Sopenharmony_ci if (*width < 1) 14138c2ecf20Sopenharmony_ci *width = 1; 14148c2ecf20Sopenharmony_ci if (*height < 1) 14158c2ecf20Sopenharmony_ci *height = 1; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci/* 14198c2ecf20Sopenharmony_ci * IOCTL vidioc handling 14208c2ecf20Sopenharmony_ci */ 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 14238c2ecf20Sopenharmony_ci struct v4l2_format *f) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 14268c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci f->fmt.pix.width = v4l2->width; 14298c2ecf20Sopenharmony_ci f->fmt.pix.height = v4l2->height; 14308c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = v4l2->format->fourcc; 14318c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = (v4l2->width * v4l2->format->depth + 7) >> 3; 14328c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * v4l2->height; 14338c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ 14368c2ecf20Sopenharmony_ci if (v4l2->progressive) 14378c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 14388c2ecf20Sopenharmony_ci else 14398c2ecf20Sopenharmony_ci f->fmt.pix.field = v4l2->interlaced_fieldmode ? 14408c2ecf20Sopenharmony_ci V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; 14418c2ecf20Sopenharmony_ci return 0; 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic struct em28xx_fmt *format_by_fourcc(unsigned int fourcc) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci unsigned int i; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(format); i++) 14498c2ecf20Sopenharmony_ci if (format[i].fourcc == fourcc) 14508c2ecf20Sopenharmony_ci return &format[i]; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci return NULL; 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 14568c2ecf20Sopenharmony_ci struct v4l2_format *f) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 14598c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 14608c2ecf20Sopenharmony_ci unsigned int width = f->fmt.pix.width; 14618c2ecf20Sopenharmony_ci unsigned int height = f->fmt.pix.height; 14628c2ecf20Sopenharmony_ci unsigned int maxw = norm_maxw(dev); 14638c2ecf20Sopenharmony_ci unsigned int maxh = norm_maxh(dev); 14648c2ecf20Sopenharmony_ci unsigned int hscale, vscale; 14658c2ecf20Sopenharmony_ci struct em28xx_fmt *fmt; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci fmt = format_by_fourcc(f->fmt.pix.pixelformat); 14688c2ecf20Sopenharmony_ci if (!fmt) { 14698c2ecf20Sopenharmony_ci fmt = &format[0]; 14708c2ecf20Sopenharmony_ci em28xx_videodbg("Fourcc format (%08x) invalid. Using default (%08x).\n", 14718c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat, fmt->fourcc); 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (dev->board.is_em2800) { 14758c2ecf20Sopenharmony_ci /* the em2800 can only scale down to 50% */ 14768c2ecf20Sopenharmony_ci height = height > (3 * maxh / 4) ? maxh : maxh / 2; 14778c2ecf20Sopenharmony_ci width = width > (3 * maxw / 4) ? maxw : maxw / 2; 14788c2ecf20Sopenharmony_ci /* 14798c2ecf20Sopenharmony_ci * MaxPacketSize for em2800 is too small to capture at full 14808c2ecf20Sopenharmony_ci * resolution use half of maxw as the scaler can only scale 14818c2ecf20Sopenharmony_ci * to 50% 14828c2ecf20Sopenharmony_ci */ 14838c2ecf20Sopenharmony_ci if (width == maxw && height == maxh) 14848c2ecf20Sopenharmony_ci width /= 2; 14858c2ecf20Sopenharmony_ci } else { 14868c2ecf20Sopenharmony_ci /* 14878c2ecf20Sopenharmony_ci * width must even because of the YUYV format 14888c2ecf20Sopenharmony_ci * height must be even because of interlacing 14898c2ecf20Sopenharmony_ci */ 14908c2ecf20Sopenharmony_ci v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 14918c2ecf20Sopenharmony_ci 1, 0); 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci /* Avoid division by zero at size_to_scale */ 14948c2ecf20Sopenharmony_ci if (width < 1) 14958c2ecf20Sopenharmony_ci width = 1; 14968c2ecf20Sopenharmony_ci if (height < 1) 14978c2ecf20Sopenharmony_ci height = 1; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci size_to_scale(dev, width, height, &hscale, &vscale); 15008c2ecf20Sopenharmony_ci scale_to_size(dev, hscale, vscale, &width, &height); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci f->fmt.pix.width = width; 15038c2ecf20Sopenharmony_ci f->fmt.pix.height = height; 15048c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = fmt->fourcc; 15058c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3; 15068c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; 15078c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 15088c2ecf20Sopenharmony_ci if (v4l2->progressive) 15098c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 15108c2ecf20Sopenharmony_ci else 15118c2ecf20Sopenharmony_ci f->fmt.pix.field = v4l2->interlaced_fieldmode ? 15128c2ecf20Sopenharmony_ci V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci return 0; 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_cistatic int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, 15188c2ecf20Sopenharmony_ci unsigned int width, unsigned int height) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci struct em28xx_fmt *fmt; 15218c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci fmt = format_by_fourcc(fourcc); 15248c2ecf20Sopenharmony_ci if (!fmt) 15258c2ecf20Sopenharmony_ci return -EINVAL; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci v4l2->format = fmt; 15288c2ecf20Sopenharmony_ci v4l2->width = width; 15298c2ecf20Sopenharmony_ci v4l2->height = height; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci /* set new image size */ 15328c2ecf20Sopenharmony_ci size_to_scale(dev, v4l2->width, v4l2->height, 15338c2ecf20Sopenharmony_ci &v4l2->hscale, &v4l2->vscale); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci em28xx_resolution_set(dev); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci return 0; 15388c2ecf20Sopenharmony_ci} 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 15418c2ecf20Sopenharmony_ci struct v4l2_format *f) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 15448c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (vb2_is_busy(&v4l2->vb_vidq)) 15478c2ecf20Sopenharmony_ci return -EBUSY; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci vidioc_try_fmt_vid_cap(file, priv, f); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, 15528c2ecf20Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height); 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci *norm = dev->v4l2->norm; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci return 0; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci return 0; 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 15768c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 15778c2ecf20Sopenharmony_ci struct v4l2_format f; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (norm == v4l2->norm) 15808c2ecf20Sopenharmony_ci return 0; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (v4l2->streaming_users > 0) 15838c2ecf20Sopenharmony_ci return -EBUSY; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci v4l2->norm = norm; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* Adjusts width/height, if needed */ 15888c2ecf20Sopenharmony_ci f.fmt.pix.width = 720; 15898c2ecf20Sopenharmony_ci f.fmt.pix.height = (norm & V4L2_STD_525_60) ? 480 : 576; 15908c2ecf20Sopenharmony_ci vidioc_try_fmt_vid_cap(file, priv, &f); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci /* set new image size */ 15938c2ecf20Sopenharmony_ci v4l2->width = f.fmt.pix.width; 15948c2ecf20Sopenharmony_ci v4l2->height = f.fmt.pix.height; 15958c2ecf20Sopenharmony_ci size_to_scale(dev, v4l2->width, v4l2->height, 15968c2ecf20Sopenharmony_ci &v4l2->hscale, &v4l2->vscale); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci em28xx_resolution_set(dev); 15998c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci return 0; 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_cistatic int vidioc_g_parm(struct file *file, void *priv, 16058c2ecf20Sopenharmony_ci struct v4l2_streamparm *p) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval ival = { 0 }; 16088c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 16098c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 16108c2ecf20Sopenharmony_ci int rc = 0; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && 16138c2ecf20Sopenharmony_ci p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 16148c2ecf20Sopenharmony_ci return -EINVAL; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci p->parm.capture.readbuffers = EM28XX_MIN_BUF; 16178c2ecf20Sopenharmony_ci p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 16188c2ecf20Sopenharmony_ci if (dev->is_webcam) { 16198c2ecf20Sopenharmony_ci rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0, 16208c2ecf20Sopenharmony_ci video, g_frame_interval, &ival); 16218c2ecf20Sopenharmony_ci if (!rc) 16228c2ecf20Sopenharmony_ci p->parm.capture.timeperframe = ival.interval; 16238c2ecf20Sopenharmony_ci } else { 16248c2ecf20Sopenharmony_ci v4l2_video_std_frame_period(v4l2->norm, 16258c2ecf20Sopenharmony_ci &p->parm.capture.timeperframe); 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci return rc; 16298c2ecf20Sopenharmony_ci} 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_cistatic int vidioc_s_parm(struct file *file, void *priv, 16328c2ecf20Sopenharmony_ci struct v4l2_streamparm *p) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 16358c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval ival = { 16368c2ecf20Sopenharmony_ci 0, 16378c2ecf20Sopenharmony_ci p->parm.capture.timeperframe 16388c2ecf20Sopenharmony_ci }; 16398c2ecf20Sopenharmony_ci int rc = 0; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (!dev->is_webcam) 16428c2ecf20Sopenharmony_ci return -ENOTTY; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && 16458c2ecf20Sopenharmony_ci p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 16468c2ecf20Sopenharmony_ci return -EINVAL; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci memset(&p->parm, 0, sizeof(p->parm)); 16498c2ecf20Sopenharmony_ci p->parm.capture.readbuffers = EM28XX_MIN_BUF; 16508c2ecf20Sopenharmony_ci p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 16518c2ecf20Sopenharmony_ci rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0, 16528c2ecf20Sopenharmony_ci video, s_frame_interval, &ival); 16538c2ecf20Sopenharmony_ci if (!rc) 16548c2ecf20Sopenharmony_ci p->parm.capture.timeperframe = ival.interval; 16558c2ecf20Sopenharmony_ci return rc; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv, 16598c2ecf20Sopenharmony_ci struct v4l2_input *i) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 16628c2ecf20Sopenharmony_ci unsigned int n; 16638c2ecf20Sopenharmony_ci int j; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci n = i->index; 16668c2ecf20Sopenharmony_ci if (n >= MAX_EM28XX_INPUT) 16678c2ecf20Sopenharmony_ci return -EINVAL; 16688c2ecf20Sopenharmony_ci if (!INPUT(n)->type) 16698c2ecf20Sopenharmony_ci return -EINVAL; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name)); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (INPUT(n)->type == EM28XX_VMUX_TELEVISION) 16768c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_TUNER; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci i->std = dev->v4l2->vdev.tvnorms; 16798c2ecf20Sopenharmony_ci /* webcams do not have the STD API */ 16808c2ecf20Sopenharmony_ci if (dev->is_webcam) 16818c2ecf20Sopenharmony_ci i->capabilities = 0; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* Dynamically generates an audioset bitmask */ 16848c2ecf20Sopenharmony_ci i->audioset = 0; 16858c2ecf20Sopenharmony_ci for (j = 0; j < MAX_EM28XX_INPUT; j++) 16868c2ecf20Sopenharmony_ci if (dev->amux_map[j] != EM28XX_AMUX_UNUSED) 16878c2ecf20Sopenharmony_ci i->audioset |= 1 << j; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci return 0; 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci *i = dev->ctl_input; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci return 0; 16998c2ecf20Sopenharmony_ci} 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci if (i >= MAX_EM28XX_INPUT) 17068c2ecf20Sopenharmony_ci return -EINVAL; 17078c2ecf20Sopenharmony_ci if (!INPUT(i)->type) 17088c2ecf20Sopenharmony_ci return -EINVAL; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci video_mux(dev, i); 17118c2ecf20Sopenharmony_ci return 0; 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic int em28xx_fill_audio_input(struct em28xx *dev, 17158c2ecf20Sopenharmony_ci const char *s, 17168c2ecf20Sopenharmony_ci struct v4l2_audio *a, 17178c2ecf20Sopenharmony_ci unsigned int index) 17188c2ecf20Sopenharmony_ci{ 17198c2ecf20Sopenharmony_ci unsigned int idx = dev->amux_map[index]; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* 17228c2ecf20Sopenharmony_ci * With msp3400, almost all mappings use the default (amux = 0). 17238c2ecf20Sopenharmony_ci * The only one may use a different value is WinTV USB2, where it 17248c2ecf20Sopenharmony_ci * can also be SCART1 input. 17258c2ecf20Sopenharmony_ci * As it is very doubtful that we would see new boards with msp3400, 17268c2ecf20Sopenharmony_ci * let's just reuse the existing switch. 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_ci if (dev->has_msp34xx && idx != EM28XX_AMUX_UNUSED) 17298c2ecf20Sopenharmony_ci idx = EM28XX_AMUX_LINE_IN; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci switch (idx) { 17328c2ecf20Sopenharmony_ci case EM28XX_AMUX_VIDEO: 17338c2ecf20Sopenharmony_ci strscpy(a->name, "Television", sizeof(a->name)); 17348c2ecf20Sopenharmony_ci break; 17358c2ecf20Sopenharmony_ci case EM28XX_AMUX_LINE_IN: 17368c2ecf20Sopenharmony_ci strscpy(a->name, "Line In", sizeof(a->name)); 17378c2ecf20Sopenharmony_ci break; 17388c2ecf20Sopenharmony_ci case EM28XX_AMUX_VIDEO2: 17398c2ecf20Sopenharmony_ci strscpy(a->name, "Television alt", sizeof(a->name)); 17408c2ecf20Sopenharmony_ci break; 17418c2ecf20Sopenharmony_ci case EM28XX_AMUX_PHONE: 17428c2ecf20Sopenharmony_ci strscpy(a->name, "Phone", sizeof(a->name)); 17438c2ecf20Sopenharmony_ci break; 17448c2ecf20Sopenharmony_ci case EM28XX_AMUX_MIC: 17458c2ecf20Sopenharmony_ci strscpy(a->name, "Mic", sizeof(a->name)); 17468c2ecf20Sopenharmony_ci break; 17478c2ecf20Sopenharmony_ci case EM28XX_AMUX_CD: 17488c2ecf20Sopenharmony_ci strscpy(a->name, "CD", sizeof(a->name)); 17498c2ecf20Sopenharmony_ci break; 17508c2ecf20Sopenharmony_ci case EM28XX_AMUX_AUX: 17518c2ecf20Sopenharmony_ci strscpy(a->name, "Aux", sizeof(a->name)); 17528c2ecf20Sopenharmony_ci break; 17538c2ecf20Sopenharmony_ci case EM28XX_AMUX_PCM_OUT: 17548c2ecf20Sopenharmony_ci strscpy(a->name, "PCM", sizeof(a->name)); 17558c2ecf20Sopenharmony_ci break; 17568c2ecf20Sopenharmony_ci case EM28XX_AMUX_UNUSED: 17578c2ecf20Sopenharmony_ci default: 17588c2ecf20Sopenharmony_ci return -EINVAL; 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci a->index = index; 17618c2ecf20Sopenharmony_ci a->capability = V4L2_AUDCAP_STEREO; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci em28xx_videodbg("%s: audio input index %d is '%s'\n", 17648c2ecf20Sopenharmony_ci s, a->index, a->name); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci return 0; 17678c2ecf20Sopenharmony_ci} 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_cistatic int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) 17708c2ecf20Sopenharmony_ci{ 17718c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci if (a->index >= MAX_EM28XX_INPUT) 17748c2ecf20Sopenharmony_ci return -EINVAL; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci return em28xx_fill_audio_input(dev, __func__, a, a->index); 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_cistatic int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) 17808c2ecf20Sopenharmony_ci{ 17818c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 17828c2ecf20Sopenharmony_ci int i; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci for (i = 0; i < MAX_EM28XX_INPUT; i++) 17858c2ecf20Sopenharmony_ci if (dev->ctl_ainput == dev->amux_map[i]) 17868c2ecf20Sopenharmony_ci return em28xx_fill_audio_input(dev, __func__, a, i); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci /* Should never happen! */ 17898c2ecf20Sopenharmony_ci return -EINVAL; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic int vidioc_s_audio(struct file *file, void *priv, 17938c2ecf20Sopenharmony_ci const struct v4l2_audio *a) 17948c2ecf20Sopenharmony_ci{ 17958c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 17968c2ecf20Sopenharmony_ci int idx, i; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci if (a->index >= MAX_EM28XX_INPUT) 17998c2ecf20Sopenharmony_ci return -EINVAL; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci idx = dev->amux_map[a->index]; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (idx == EM28XX_AMUX_UNUSED) 18048c2ecf20Sopenharmony_ci return -EINVAL; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci dev->ctl_ainput = idx; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci /* 18098c2ecf20Sopenharmony_ci * FIXME: This is wrong, as different inputs at em28xx_cards 18108c2ecf20Sopenharmony_ci * may have different audio outputs. So, the right thing 18118c2ecf20Sopenharmony_ci * to do is to implement VIDIOC_G_AUDOUT/VIDIOC_S_AUDOUT. 18128c2ecf20Sopenharmony_ci * With the current board definitions, this would work fine, 18138c2ecf20Sopenharmony_ci * as, currently, all boards fit. 18148c2ecf20Sopenharmony_ci */ 18158c2ecf20Sopenharmony_ci for (i = 0; i < MAX_EM28XX_INPUT; i++) 18168c2ecf20Sopenharmony_ci if (idx == dev->amux_map[i]) 18178c2ecf20Sopenharmony_ci break; 18188c2ecf20Sopenharmony_ci if (i == MAX_EM28XX_INPUT) 18198c2ecf20Sopenharmony_ci return -EINVAL; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci dev->ctl_aoutput = INPUT(i)->aout; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (!dev->ctl_aoutput) 18248c2ecf20Sopenharmony_ci dev->ctl_aoutput = EM28XX_AOUT_MASTER; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci em28xx_videodbg("%s: set audio input to %d\n", __func__, 18278c2ecf20Sopenharmony_ci dev->ctl_ainput); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci return 0; 18308c2ecf20Sopenharmony_ci} 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, 18338c2ecf20Sopenharmony_ci struct v4l2_tuner *t) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci if (t->index != 0) 18388c2ecf20Sopenharmony_ci return -EINVAL; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci strscpy(t->name, "Tuner", sizeof(t->name)); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); 18438c2ecf20Sopenharmony_ci return 0; 18448c2ecf20Sopenharmony_ci} 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv, 18478c2ecf20Sopenharmony_ci const struct v4l2_tuner *t) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci if (t->index != 0) 18528c2ecf20Sopenharmony_ci return -EINVAL; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); 18558c2ecf20Sopenharmony_ci return 0; 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv, 18598c2ecf20Sopenharmony_ci struct v4l2_frequency *f) 18608c2ecf20Sopenharmony_ci{ 18618c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 18628c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (f->tuner != 0) 18658c2ecf20Sopenharmony_ci return -EINVAL; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci f->frequency = v4l2->frequency; 18688c2ecf20Sopenharmony_ci return 0; 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv, 18728c2ecf20Sopenharmony_ci const struct v4l2_frequency *f) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci struct v4l2_frequency new_freq = *f; 18758c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 18768c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci if (f->tuner != 0) 18798c2ecf20Sopenharmony_ci return -EINVAL; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); 18828c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq); 18838c2ecf20Sopenharmony_ci v4l2->frequency = new_freq.frequency; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 18898c2ecf20Sopenharmony_cistatic int vidioc_g_chip_info(struct file *file, void *priv, 18908c2ecf20Sopenharmony_ci struct v4l2_dbg_chip_info *chip) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (chip->match.addr > 1) 18958c2ecf20Sopenharmony_ci return -EINVAL; 18968c2ecf20Sopenharmony_ci if (chip->match.addr == 1) 18978c2ecf20Sopenharmony_ci strscpy(chip->name, "ac97", sizeof(chip->name)); 18988c2ecf20Sopenharmony_ci else 18998c2ecf20Sopenharmony_ci strscpy(chip->name, 19008c2ecf20Sopenharmony_ci dev->v4l2->v4l2_dev.name, sizeof(chip->name)); 19018c2ecf20Sopenharmony_ci return 0; 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_cistatic int em28xx_reg_len(int reg) 19058c2ecf20Sopenharmony_ci{ 19068c2ecf20Sopenharmony_ci switch (reg) { 19078c2ecf20Sopenharmony_ci case EM28XX_R40_AC97LSB: 19088c2ecf20Sopenharmony_ci case EM28XX_R30_HSCALELOW: 19098c2ecf20Sopenharmony_ci case EM28XX_R32_VSCALELOW: 19108c2ecf20Sopenharmony_ci return 2; 19118c2ecf20Sopenharmony_ci default: 19128c2ecf20Sopenharmony_ci return 1; 19138c2ecf20Sopenharmony_ci } 19148c2ecf20Sopenharmony_ci} 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *priv, 19178c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 19188c2ecf20Sopenharmony_ci{ 19198c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 19208c2ecf20Sopenharmony_ci int ret; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci if (reg->match.addr > 1) 19238c2ecf20Sopenharmony_ci return -EINVAL; 19248c2ecf20Sopenharmony_ci if (reg->match.addr) { 19258c2ecf20Sopenharmony_ci ret = em28xx_read_ac97(dev, reg->reg); 19268c2ecf20Sopenharmony_ci if (ret < 0) 19278c2ecf20Sopenharmony_ci return ret; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci reg->val = ret; 19308c2ecf20Sopenharmony_ci reg->size = 1; 19318c2ecf20Sopenharmony_ci return 0; 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci /* Match host */ 19358c2ecf20Sopenharmony_ci reg->size = em28xx_reg_len(reg->reg); 19368c2ecf20Sopenharmony_ci if (reg->size == 1) { 19378c2ecf20Sopenharmony_ci ret = em28xx_read_reg(dev, reg->reg); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (ret < 0) 19408c2ecf20Sopenharmony_ci return ret; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci reg->val = ret; 19438c2ecf20Sopenharmony_ci } else { 19448c2ecf20Sopenharmony_ci __le16 val = 0; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci ret = dev->em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, 19478c2ecf20Sopenharmony_ci reg->reg, (char *)&val, 2); 19488c2ecf20Sopenharmony_ci if (ret < 0) 19498c2ecf20Sopenharmony_ci return ret; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci reg->val = le16_to_cpu(val); 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci return 0; 19558c2ecf20Sopenharmony_ci} 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *priv, 19588c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 19618c2ecf20Sopenharmony_ci __le16 buf; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (reg->match.addr > 1) 19648c2ecf20Sopenharmony_ci return -EINVAL; 19658c2ecf20Sopenharmony_ci if (reg->match.addr) 19668c2ecf20Sopenharmony_ci return em28xx_write_ac97(dev, reg->reg, reg->val); 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci /* Match host */ 19698c2ecf20Sopenharmony_ci buf = cpu_to_le16(reg->val); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci return em28xx_write_regs(dev, reg->reg, (char *)&buf, 19728c2ecf20Sopenharmony_ci em28xx_reg_len(reg->reg)); 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci#endif 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 19778c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 19808c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 19818c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(dev->intf); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci strscpy(cap->driver, "em28xx", sizeof(cap->driver)); 19848c2ecf20Sopenharmony_ci strscpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); 19858c2ecf20Sopenharmony_ci usb_make_path(udev, cap->bus_info, sizeof(cap->bus_info)); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | 19888c2ecf20Sopenharmony_ci V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 19898c2ecf20Sopenharmony_ci if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) 19908c2ecf20Sopenharmony_ci cap->capabilities |= V4L2_CAP_AUDIO; 19918c2ecf20Sopenharmony_ci if (dev->tuner_type != TUNER_ABSENT) 19928c2ecf20Sopenharmony_ci cap->capabilities |= V4L2_CAP_TUNER; 19938c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->vbi_dev)) 19948c2ecf20Sopenharmony_ci cap->capabilities |= V4L2_CAP_VBI_CAPTURE; 19958c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->radio_dev)) 19968c2ecf20Sopenharmony_ci cap->capabilities |= V4L2_CAP_RADIO; 19978c2ecf20Sopenharmony_ci return 0; 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 20018c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 20028c2ecf20Sopenharmony_ci{ 20038c2ecf20Sopenharmony_ci if (unlikely(f->index >= ARRAY_SIZE(format))) 20048c2ecf20Sopenharmony_ci return -EINVAL; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci f->pixelformat = format[f->index].fourcc; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci return 0; 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic int vidioc_enum_framesizes(struct file *file, void *priv, 20128c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 20138c2ecf20Sopenharmony_ci{ 20148c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 20158c2ecf20Sopenharmony_ci struct em28xx_fmt *fmt; 20168c2ecf20Sopenharmony_ci unsigned int maxw = norm_maxw(dev); 20178c2ecf20Sopenharmony_ci unsigned int maxh = norm_maxh(dev); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci fmt = format_by_fourcc(fsize->pixel_format); 20208c2ecf20Sopenharmony_ci if (!fmt) { 20218c2ecf20Sopenharmony_ci em28xx_videodbg("Fourcc format (%08x) invalid.\n", 20228c2ecf20Sopenharmony_ci fsize->pixel_format); 20238c2ecf20Sopenharmony_ci return -EINVAL; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (dev->board.is_em2800) { 20278c2ecf20Sopenharmony_ci if (fsize->index > 1) 20288c2ecf20Sopenharmony_ci return -EINVAL; 20298c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 20308c2ecf20Sopenharmony_ci fsize->discrete.width = maxw / (1 + fsize->index); 20318c2ecf20Sopenharmony_ci fsize->discrete.height = maxh / (1 + fsize->index); 20328c2ecf20Sopenharmony_ci return 0; 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci if (fsize->index != 0) 20368c2ecf20Sopenharmony_ci return -EINVAL; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci /* Report a continuous range */ 20398c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 20408c2ecf20Sopenharmony_ci scale_to_size(dev, EM28XX_HVSCALE_MAX, EM28XX_HVSCALE_MAX, 20418c2ecf20Sopenharmony_ci &fsize->stepwise.min_width, &fsize->stepwise.min_height); 20428c2ecf20Sopenharmony_ci if (fsize->stepwise.min_width < 48) 20438c2ecf20Sopenharmony_ci fsize->stepwise.min_width = 48; 20448c2ecf20Sopenharmony_ci if (fsize->stepwise.min_height < 38) 20458c2ecf20Sopenharmony_ci fsize->stepwise.min_height = 38; 20468c2ecf20Sopenharmony_ci fsize->stepwise.max_width = maxw; 20478c2ecf20Sopenharmony_ci fsize->stepwise.max_height = maxh; 20488c2ecf20Sopenharmony_ci fsize->stepwise.step_width = 1; 20498c2ecf20Sopenharmony_ci fsize->stepwise.step_height = 1; 20508c2ecf20Sopenharmony_ci return 0; 20518c2ecf20Sopenharmony_ci} 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci/* RAW VBI ioctls */ 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, 20568c2ecf20Sopenharmony_ci struct v4l2_format *format) 20578c2ecf20Sopenharmony_ci{ 20588c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 20598c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci format->fmt.vbi.samples_per_line = v4l2->vbi_width; 20628c2ecf20Sopenharmony_ci format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; 20638c2ecf20Sopenharmony_ci format->fmt.vbi.offset = 0; 20648c2ecf20Sopenharmony_ci format->fmt.vbi.flags = 0; 20658c2ecf20Sopenharmony_ci format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; 20668c2ecf20Sopenharmony_ci format->fmt.vbi.count[0] = v4l2->vbi_height; 20678c2ecf20Sopenharmony_ci format->fmt.vbi.count[1] = v4l2->vbi_height; 20688c2ecf20Sopenharmony_ci memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci /* Varies by video standard (NTSC, PAL, etc.) */ 20718c2ecf20Sopenharmony_ci if (v4l2->norm & V4L2_STD_525_60) { 20728c2ecf20Sopenharmony_ci /* NTSC */ 20738c2ecf20Sopenharmony_ci format->fmt.vbi.start[0] = 10; 20748c2ecf20Sopenharmony_ci format->fmt.vbi.start[1] = 273; 20758c2ecf20Sopenharmony_ci } else if (v4l2->norm & V4L2_STD_625_50) { 20768c2ecf20Sopenharmony_ci /* PAL */ 20778c2ecf20Sopenharmony_ci format->fmt.vbi.start[0] = 6; 20788c2ecf20Sopenharmony_ci format->fmt.vbi.start[1] = 318; 20798c2ecf20Sopenharmony_ci } 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci return 0; 20828c2ecf20Sopenharmony_ci} 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci/* 20858c2ecf20Sopenharmony_ci * RADIO ESPECIFIC IOCTLS 20868c2ecf20Sopenharmony_ci */ 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_cistatic int radio_g_tuner(struct file *file, void *priv, 20898c2ecf20Sopenharmony_ci struct v4l2_tuner *t) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (unlikely(t->index > 0)) 20948c2ecf20Sopenharmony_ci return -EINVAL; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci strscpy(t->name, "Radio", sizeof(t->name)); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci return 0; 21018c2ecf20Sopenharmony_ci} 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_cistatic int radio_s_tuner(struct file *file, void *priv, 21048c2ecf20Sopenharmony_ci const struct v4l2_tuner *t) 21058c2ecf20Sopenharmony_ci{ 21068c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(file); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci if (t->index != 0) 21098c2ecf20Sopenharmony_ci return -EINVAL; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci return 0; 21148c2ecf20Sopenharmony_ci} 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci/* 21178c2ecf20Sopenharmony_ci * em28xx_free_v4l2() - Free struct em28xx_v4l2 21188c2ecf20Sopenharmony_ci * 21198c2ecf20Sopenharmony_ci * @ref: struct kref for struct em28xx_v4l2 21208c2ecf20Sopenharmony_ci * 21218c2ecf20Sopenharmony_ci * Called when all users of struct em28xx_v4l2 are gone 21228c2ecf20Sopenharmony_ci */ 21238c2ecf20Sopenharmony_cistatic void em28xx_free_v4l2(struct kref *ref) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci v4l2->dev->v4l2 = NULL; 21288c2ecf20Sopenharmony_ci kfree(v4l2); 21298c2ecf20Sopenharmony_ci} 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci/* 21328c2ecf20Sopenharmony_ci * em28xx_v4l2_open() 21338c2ecf20Sopenharmony_ci * inits the device and starts isoc transfer 21348c2ecf20Sopenharmony_ci */ 21358c2ecf20Sopenharmony_cistatic int em28xx_v4l2_open(struct file *filp) 21368c2ecf20Sopenharmony_ci{ 21378c2ecf20Sopenharmony_ci struct video_device *vdev = video_devdata(filp); 21388c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(filp); 21398c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 21408c2ecf20Sopenharmony_ci enum v4l2_buf_type fh_type = 0; 21418c2ecf20Sopenharmony_ci int ret; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci switch (vdev->vfl_type) { 21448c2ecf20Sopenharmony_ci case VFL_TYPE_VIDEO: 21458c2ecf20Sopenharmony_ci fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 21468c2ecf20Sopenharmony_ci break; 21478c2ecf20Sopenharmony_ci case VFL_TYPE_VBI: 21488c2ecf20Sopenharmony_ci fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; 21498c2ecf20Sopenharmony_ci break; 21508c2ecf20Sopenharmony_ci case VFL_TYPE_RADIO: 21518c2ecf20Sopenharmony_ci break; 21528c2ecf20Sopenharmony_ci default: 21538c2ecf20Sopenharmony_ci return -EINVAL; 21548c2ecf20Sopenharmony_ci } 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci em28xx_videodbg("open dev=%s type=%s users=%d\n", 21578c2ecf20Sopenharmony_ci video_device_node_name(vdev), v4l2_type_names[fh_type], 21588c2ecf20Sopenharmony_ci v4l2->users); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->lock)) 21618c2ecf20Sopenharmony_ci return -ERESTARTSYS; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci ret = v4l2_fh_open(filp); 21648c2ecf20Sopenharmony_ci if (ret) { 21658c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 21668c2ecf20Sopenharmony_ci "%s: v4l2_fh_open() returned error %d\n", 21678c2ecf20Sopenharmony_ci __func__, ret); 21688c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 21698c2ecf20Sopenharmony_ci return ret; 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci if (v4l2->users == 0) { 21738c2ecf20Sopenharmony_ci em28xx_set_mode(dev, EM28XX_ANALOG_MODE); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci if (vdev->vfl_type != VFL_TYPE_RADIO) 21768c2ecf20Sopenharmony_ci em28xx_resolution_set(dev); 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci /* 21798c2ecf20Sopenharmony_ci * Needed, since GPIO might have disabled power 21808c2ecf20Sopenharmony_ci * of some i2c devices 21818c2ecf20Sopenharmony_ci */ 21828c2ecf20Sopenharmony_ci em28xx_wake_i2c(dev); 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_RADIO) { 21868c2ecf20Sopenharmony_ci em28xx_videodbg("video_open: setting radio device\n"); 21878c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); 21888c2ecf20Sopenharmony_ci } 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci kref_get(&dev->ref); 21918c2ecf20Sopenharmony_ci kref_get(&v4l2->ref); 21928c2ecf20Sopenharmony_ci v4l2->users++; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci return 0; 21978c2ecf20Sopenharmony_ci} 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci/* 22008c2ecf20Sopenharmony_ci * em28xx_v4l2_fini() 22018c2ecf20Sopenharmony_ci * unregisters the v4l2,i2c and usb devices 22028c2ecf20Sopenharmony_ci * called when the device gets disconnected or at module unload 22038c2ecf20Sopenharmony_ci */ 22048c2ecf20Sopenharmony_cistatic int em28xx_v4l2_fini(struct em28xx *dev) 22058c2ecf20Sopenharmony_ci{ 22068c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci if (dev->is_audio_only) { 22098c2ecf20Sopenharmony_ci /* Shouldn't initialize IR for this interface */ 22108c2ecf20Sopenharmony_ci return 0; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (!dev->has_video) { 22148c2ecf20Sopenharmony_ci /* This device does not support the v4l2 extension */ 22158c2ecf20Sopenharmony_ci return 0; 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci if (!v4l2) 22198c2ecf20Sopenharmony_ci return 0; 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "Closing video extension\n"); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci v4l2_device_disconnect(&v4l2->v4l2_dev); 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci em28xx_v4l2_media_release(dev); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->radio_dev)) { 22328c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", 22338c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->radio_dev)); 22348c2ecf20Sopenharmony_ci video_unregister_device(&v4l2->radio_dev); 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->vbi_dev)) { 22378c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", 22388c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->vbi_dev)); 22398c2ecf20Sopenharmony_ci video_unregister_device(&v4l2->vbi_dev); 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->vdev)) { 22428c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", 22438c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->vdev)); 22448c2ecf20Sopenharmony_ci video_unregister_device(&v4l2->vdev); 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&v4l2->ctrl_handler); 22488c2ecf20Sopenharmony_ci v4l2_device_unregister(&v4l2->v4l2_dev); 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci kref_put(&v4l2->ref, em28xx_free_v4l2); 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci kref_put(&dev->ref, em28xx_free_device); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci return 0; 22578c2ecf20Sopenharmony_ci} 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_cistatic int em28xx_v4l2_suspend(struct em28xx *dev) 22608c2ecf20Sopenharmony_ci{ 22618c2ecf20Sopenharmony_ci if (dev->is_audio_only) 22628c2ecf20Sopenharmony_ci return 0; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci if (!dev->has_video) 22658c2ecf20Sopenharmony_ci return 0; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "Suspending video extension\n"); 22688c2ecf20Sopenharmony_ci em28xx_stop_urbs(dev); 22698c2ecf20Sopenharmony_ci return 0; 22708c2ecf20Sopenharmony_ci} 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_cistatic int em28xx_v4l2_resume(struct em28xx *dev) 22738c2ecf20Sopenharmony_ci{ 22748c2ecf20Sopenharmony_ci if (dev->is_audio_only) 22758c2ecf20Sopenharmony_ci return 0; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (!dev->has_video) 22788c2ecf20Sopenharmony_ci return 0; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "Resuming video extension\n"); 22818c2ecf20Sopenharmony_ci /* what do we do here */ 22828c2ecf20Sopenharmony_ci return 0; 22838c2ecf20Sopenharmony_ci} 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci/* 22868c2ecf20Sopenharmony_ci * em28xx_v4l2_close() 22878c2ecf20Sopenharmony_ci * stops streaming and deallocates all resources allocated by the v4l2 22888c2ecf20Sopenharmony_ci * calls and ioctls 22898c2ecf20Sopenharmony_ci */ 22908c2ecf20Sopenharmony_cistatic int em28xx_v4l2_close(struct file *filp) 22918c2ecf20Sopenharmony_ci{ 22928c2ecf20Sopenharmony_ci struct em28xx *dev = video_drvdata(filp); 22938c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 22948c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(dev->intf); 22958c2ecf20Sopenharmony_ci int err; 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci em28xx_videodbg("users=%d\n", v4l2->users); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci vb2_fop_release(filp); 23008c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci if (v4l2->users == 1) { 23038c2ecf20Sopenharmony_ci /* No sense to try to write to the device */ 23048c2ecf20Sopenharmony_ci if (dev->disconnected) 23058c2ecf20Sopenharmony_ci goto exit; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci /* Save some power by putting tuner to sleep */ 23088c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci /* do this before setting alternate! */ 23118c2ecf20Sopenharmony_ci em28xx_set_mode(dev, EM28XX_SUSPEND); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci /* set alternate 0 */ 23148c2ecf20Sopenharmony_ci dev->alt = 0; 23158c2ecf20Sopenharmony_ci em28xx_videodbg("setting alternate 0\n"); 23168c2ecf20Sopenharmony_ci err = usb_set_interface(udev, 0, 0); 23178c2ecf20Sopenharmony_ci if (err < 0) { 23188c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 23198c2ecf20Sopenharmony_ci "cannot change alternate number to 0 (error=%i)\n", 23208c2ecf20Sopenharmony_ci err); 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci } 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ciexit: 23258c2ecf20Sopenharmony_ci v4l2->users--; 23268c2ecf20Sopenharmony_ci kref_put(&v4l2->ref, em28xx_free_v4l2); 23278c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 23288c2ecf20Sopenharmony_ci kref_put(&dev->ref, em28xx_free_device); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci return 0; 23318c2ecf20Sopenharmony_ci} 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations em28xx_v4l_fops = { 23348c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 23358c2ecf20Sopenharmony_ci .open = em28xx_v4l2_open, 23368c2ecf20Sopenharmony_ci .release = em28xx_v4l2_close, 23378c2ecf20Sopenharmony_ci .read = vb2_fop_read, 23388c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 23398c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 23408c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 23418c2ecf20Sopenharmony_ci}; 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = { 23448c2ecf20Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 23458c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 23468c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 23478c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 23488c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 23498c2ecf20Sopenharmony_ci .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, 23508c2ecf20Sopenharmony_ci .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, 23518c2ecf20Sopenharmony_ci .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, 23528c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = vidioc_enum_framesizes, 23538c2ecf20Sopenharmony_ci .vidioc_enumaudio = vidioc_enumaudio, 23548c2ecf20Sopenharmony_ci .vidioc_g_audio = vidioc_g_audio, 23558c2ecf20Sopenharmony_ci .vidioc_s_audio = vidioc_s_audio, 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 23588c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 23598c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 23608c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 23618c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 23628c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci .vidioc_g_std = vidioc_g_std, 23658c2ecf20Sopenharmony_ci .vidioc_querystd = vidioc_querystd, 23668c2ecf20Sopenharmony_ci .vidioc_s_std = vidioc_s_std, 23678c2ecf20Sopenharmony_ci .vidioc_g_parm = vidioc_g_parm, 23688c2ecf20Sopenharmony_ci .vidioc_s_parm = vidioc_s_parm, 23698c2ecf20Sopenharmony_ci .vidioc_enum_input = vidioc_enum_input, 23708c2ecf20Sopenharmony_ci .vidioc_g_input = vidioc_g_input, 23718c2ecf20Sopenharmony_ci .vidioc_s_input = vidioc_s_input, 23728c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 23738c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 23748c2ecf20Sopenharmony_ci .vidioc_g_tuner = vidioc_g_tuner, 23758c2ecf20Sopenharmony_ci .vidioc_s_tuner = vidioc_s_tuner, 23768c2ecf20Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 23778c2ecf20Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 23788c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 23798c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 23808c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 23818c2ecf20Sopenharmony_ci .vidioc_g_chip_info = vidioc_g_chip_info, 23828c2ecf20Sopenharmony_ci .vidioc_g_register = vidioc_g_register, 23838c2ecf20Sopenharmony_ci .vidioc_s_register = vidioc_s_register, 23848c2ecf20Sopenharmony_ci#endif 23858c2ecf20Sopenharmony_ci}; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_cistatic const struct video_device em28xx_video_template = { 23888c2ecf20Sopenharmony_ci .fops = &em28xx_v4l_fops, 23898c2ecf20Sopenharmony_ci .ioctl_ops = &video_ioctl_ops, 23908c2ecf20Sopenharmony_ci .release = video_device_release_empty, 23918c2ecf20Sopenharmony_ci .tvnorms = V4L2_STD_ALL, 23928c2ecf20Sopenharmony_ci}; 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations radio_fops = { 23958c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 23968c2ecf20Sopenharmony_ci .open = em28xx_v4l2_open, 23978c2ecf20Sopenharmony_ci .release = em28xx_v4l2_close, 23988c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 23998c2ecf20Sopenharmony_ci}; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops radio_ioctl_ops = { 24028c2ecf20Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 24038c2ecf20Sopenharmony_ci .vidioc_g_tuner = radio_g_tuner, 24048c2ecf20Sopenharmony_ci .vidioc_s_tuner = radio_s_tuner, 24058c2ecf20Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 24068c2ecf20Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 24078c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 24088c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 24098c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 24108c2ecf20Sopenharmony_ci .vidioc_g_chip_info = vidioc_g_chip_info, 24118c2ecf20Sopenharmony_ci .vidioc_g_register = vidioc_g_register, 24128c2ecf20Sopenharmony_ci .vidioc_s_register = vidioc_s_register, 24138c2ecf20Sopenharmony_ci#endif 24148c2ecf20Sopenharmony_ci}; 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_cistatic struct video_device em28xx_radio_template = { 24178c2ecf20Sopenharmony_ci .fops = &radio_fops, 24188c2ecf20Sopenharmony_ci .ioctl_ops = &radio_ioctl_ops, 24198c2ecf20Sopenharmony_ci .release = video_device_release_empty, 24208c2ecf20Sopenharmony_ci}; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ 24238c2ecf20Sopenharmony_cistatic unsigned short saa711x_addrs[] = { 24248c2ecf20Sopenharmony_ci 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ 24258c2ecf20Sopenharmony_ci 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ 24268c2ecf20Sopenharmony_ci I2C_CLIENT_END }; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_cistatic unsigned short tvp5150_addrs[] = { 24298c2ecf20Sopenharmony_ci 0xb8 >> 1, 24308c2ecf20Sopenharmony_ci 0xba >> 1, 24318c2ecf20Sopenharmony_ci I2C_CLIENT_END 24328c2ecf20Sopenharmony_ci}; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_cistatic unsigned short msp3400_addrs[] = { 24358c2ecf20Sopenharmony_ci 0x80 >> 1, 24368c2ecf20Sopenharmony_ci 0x88 >> 1, 24378c2ecf20Sopenharmony_ci I2C_CLIENT_END 24388c2ecf20Sopenharmony_ci}; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci/******************************** usb interface ******************************/ 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_cistatic void em28xx_vdev_init(struct em28xx *dev, 24438c2ecf20Sopenharmony_ci struct video_device *vfd, 24448c2ecf20Sopenharmony_ci const struct video_device *template, 24458c2ecf20Sopenharmony_ci const char *type_name) 24468c2ecf20Sopenharmony_ci{ 24478c2ecf20Sopenharmony_ci *vfd = *template; 24488c2ecf20Sopenharmony_ci vfd->v4l2_dev = &dev->v4l2->v4l2_dev; 24498c2ecf20Sopenharmony_ci vfd->lock = &dev->lock; 24508c2ecf20Sopenharmony_ci if (dev->is_webcam) 24518c2ecf20Sopenharmony_ci vfd->tvnorms = 0; 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci snprintf(vfd->name, sizeof(vfd->name), "%s %s", 24548c2ecf20Sopenharmony_ci dev_name(&dev->intf->dev), type_name); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci video_set_drvdata(vfd, dev); 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_cistatic void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr) 24608c2ecf20Sopenharmony_ci{ 24618c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 24628c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; 24638c2ecf20Sopenharmony_ci struct tuner_setup tun_setup; 24648c2ecf20Sopenharmony_ci struct v4l2_frequency f; 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci memset(&tun_setup, 0, sizeof(tun_setup)); 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; 24698c2ecf20Sopenharmony_ci tun_setup.tuner_callback = em28xx_tuner_callback; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci if (dev->board.radio.type) { 24728c2ecf20Sopenharmony_ci tun_setup.type = dev->board.radio.type; 24738c2ecf20Sopenharmony_ci tun_setup.addr = dev->board.radio_addr; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 24768c2ecf20Sopenharmony_ci 0, tuner, s_type_addr, &tun_setup); 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci if (dev->tuner_type != TUNER_ABSENT && dev->tuner_type) { 24808c2ecf20Sopenharmony_ci tun_setup.type = dev->tuner_type; 24818c2ecf20Sopenharmony_ci tun_setup.addr = tuner_addr; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 24848c2ecf20Sopenharmony_ci 0, tuner, s_type_addr, &tun_setup); 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci if (dev->board.tda9887_conf) { 24888c2ecf20Sopenharmony_ci struct v4l2_priv_tun_config tda9887_cfg; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci tda9887_cfg.tuner = TUNER_TDA9887; 24918c2ecf20Sopenharmony_ci tda9887_cfg.priv = &dev->board.tda9887_conf; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 24948c2ecf20Sopenharmony_ci 0, tuner, s_config, &tda9887_cfg); 24958c2ecf20Sopenharmony_ci } 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci if (dev->tuner_type == TUNER_XC2028) { 24988c2ecf20Sopenharmony_ci struct v4l2_priv_tun_config xc2028_cfg; 24998c2ecf20Sopenharmony_ci struct xc2028_ctrl ctl; 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); 25028c2ecf20Sopenharmony_ci memset(&ctl, 0, sizeof(ctl)); 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci em28xx_setup_xc3028(dev, &ctl); 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci xc2028_cfg.tuner = TUNER_XC2028; 25078c2ecf20Sopenharmony_ci xc2028_cfg.priv = &ctl; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, tuner, s_config, &xc2028_cfg); 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci /* configure tuner */ 25138c2ecf20Sopenharmony_ci f.tuner = 0; 25148c2ecf20Sopenharmony_ci f.type = V4L2_TUNER_ANALOG_TV; 25158c2ecf20Sopenharmony_ci f.frequency = 9076; /* just a magic number */ 25168c2ecf20Sopenharmony_ci v4l2->frequency = f.frequency; 25178c2ecf20Sopenharmony_ci v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency, &f); 25188c2ecf20Sopenharmony_ci} 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_cistatic int em28xx_v4l2_init(struct em28xx *dev) 25218c2ecf20Sopenharmony_ci{ 25228c2ecf20Sopenharmony_ci u8 val; 25238c2ecf20Sopenharmony_ci int ret; 25248c2ecf20Sopenharmony_ci unsigned int maxw; 25258c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 25268c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci if (dev->is_audio_only) { 25298c2ecf20Sopenharmony_ci /* Shouldn't initialize IR for this interface */ 25308c2ecf20Sopenharmony_ci return 0; 25318c2ecf20Sopenharmony_ci } 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci if (!dev->has_video) { 25348c2ecf20Sopenharmony_ci /* This device does not support the v4l2 extension */ 25358c2ecf20Sopenharmony_ci return 0; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, "Registering V4L2 extension\n"); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci v4l2 = kzalloc(sizeof(*v4l2), GFP_KERNEL); 25438c2ecf20Sopenharmony_ci if (!v4l2) { 25448c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 25458c2ecf20Sopenharmony_ci return -ENOMEM; 25468c2ecf20Sopenharmony_ci } 25478c2ecf20Sopenharmony_ci kref_init(&v4l2->ref); 25488c2ecf20Sopenharmony_ci v4l2->dev = dev; 25498c2ecf20Sopenharmony_ci dev->v4l2 = v4l2; 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 25528c2ecf20Sopenharmony_ci v4l2->v4l2_dev.mdev = dev->media_dev; 25538c2ecf20Sopenharmony_ci#endif 25548c2ecf20Sopenharmony_ci ret = v4l2_device_register(&dev->intf->dev, &v4l2->v4l2_dev); 25558c2ecf20Sopenharmony_ci if (ret < 0) { 25568c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 25578c2ecf20Sopenharmony_ci "Call to v4l2_device_register() failed!\n"); 25588c2ecf20Sopenharmony_ci goto err; 25598c2ecf20Sopenharmony_ci } 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci hdl = &v4l2->ctrl_handler; 25628c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 8); 25638c2ecf20Sopenharmony_ci v4l2->v4l2_dev.ctrl_handler = hdl; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci if (dev->is_webcam) 25668c2ecf20Sopenharmony_ci v4l2->progressive = true; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci /* 25698c2ecf20Sopenharmony_ci * Default format, used for tvp5150 or saa711x output formats 25708c2ecf20Sopenharmony_ci */ 25718c2ecf20Sopenharmony_ci v4l2->vinmode = EM28XX_VINMODE_YUV422_CbYCrY; 25728c2ecf20Sopenharmony_ci v4l2->vinctl = EM28XX_VINCTRL_INTERLACED | 25738c2ecf20Sopenharmony_ci EM28XX_VINCTRL_CCIR656_ENABLE; 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci /* request some modules */ 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci if (dev->has_msp34xx) 25788c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 25798c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 25808c2ecf20Sopenharmony_ci "msp3400", 0, msp3400_addrs); 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci if (dev->board.decoder == EM28XX_SAA711X) 25838c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 25848c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 25858c2ecf20Sopenharmony_ci "saa7115_auto", 0, saa711x_addrs); 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci if (dev->board.decoder == EM28XX_TVP5150) 25888c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 25898c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 25908c2ecf20Sopenharmony_ci "tvp5150", 0, tvp5150_addrs); 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci if (dev->board.adecoder == EM28XX_TVAUDIO) 25938c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 25948c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 25958c2ecf20Sopenharmony_ci "tvaudio", dev->board.tvaudio_addr, NULL); 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci /* Initialize tuner and camera */ 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci if (dev->board.tuner_type != TUNER_ABSENT) { 26008c2ecf20Sopenharmony_ci unsigned short tuner_addr = dev->board.tuner_addr; 26018c2ecf20Sopenharmony_ci int has_demod = (dev->board.tda9887_conf & TDA9887_PRESENT); 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci if (dev->board.radio.type) 26048c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 26058c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 26068c2ecf20Sopenharmony_ci "tuner", dev->board.radio_addr, 26078c2ecf20Sopenharmony_ci NULL); 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci if (has_demod) 26108c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 26118c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 26128c2ecf20Sopenharmony_ci "tuner", 0, 26138c2ecf20Sopenharmony_ci v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); 26148c2ecf20Sopenharmony_ci if (tuner_addr == 0) { 26158c2ecf20Sopenharmony_ci enum v4l2_i2c_tuner_type type = 26168c2ecf20Sopenharmony_ci has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; 26178c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci sd = v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 26208c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 26218c2ecf20Sopenharmony_ci "tuner", 0, 26228c2ecf20Sopenharmony_ci v4l2_i2c_tuner_addrs(type)); 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci if (sd) 26258c2ecf20Sopenharmony_ci tuner_addr = v4l2_i2c_subdev_addr(sd); 26268c2ecf20Sopenharmony_ci } else { 26278c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev(&v4l2->v4l2_dev, 26288c2ecf20Sopenharmony_ci &dev->i2c_adap[dev->def_i2c_bus], 26298c2ecf20Sopenharmony_ci "tuner", tuner_addr, NULL); 26308c2ecf20Sopenharmony_ci } 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci em28xx_tuner_setup(dev, tuner_addr); 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci if (dev->em28xx_sensor != EM28XX_NOSENSOR) 26368c2ecf20Sopenharmony_ci em28xx_init_camera(dev); 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci /* Configure audio */ 26398c2ecf20Sopenharmony_ci ret = em28xx_audio_setup(dev); 26408c2ecf20Sopenharmony_ci if (ret < 0) { 26418c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 26428c2ecf20Sopenharmony_ci "%s: Error while setting audio - error [%d]!\n", 26438c2ecf20Sopenharmony_ci __func__, ret); 26448c2ecf20Sopenharmony_ci goto unregister_dev; 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { 26478c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 26488c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 26498c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 26508c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); 26518c2ecf20Sopenharmony_ci } else { 26528c2ecf20Sopenharmony_ci /* install the em28xx notify callback */ 26538c2ecf20Sopenharmony_ci v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), 26548c2ecf20Sopenharmony_ci em28xx_ctrl_notify, dev); 26558c2ecf20Sopenharmony_ci v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), 26568c2ecf20Sopenharmony_ci em28xx_ctrl_notify, dev); 26578c2ecf20Sopenharmony_ci } 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci /* wake i2c devices */ 26608c2ecf20Sopenharmony_ci em28xx_wake_i2c(dev); 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci /* init video dma queues */ 26638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->vidq.active); 26648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->vbiq.active); 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci if (dev->has_msp34xx) { 26678c2ecf20Sopenharmony_ci /* Send a reset to other chips via gpio */ 26688c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7); 26698c2ecf20Sopenharmony_ci if (ret < 0) { 26708c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 26718c2ecf20Sopenharmony_ci "%s: em28xx_write_reg - msp34xx(1) failed! error [%d]\n", 26728c2ecf20Sopenharmony_ci __func__, ret); 26738c2ecf20Sopenharmony_ci goto unregister_dev; 26748c2ecf20Sopenharmony_ci } 26758c2ecf20Sopenharmony_ci usleep_range(10000, 11000); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); 26788c2ecf20Sopenharmony_ci if (ret < 0) { 26798c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 26808c2ecf20Sopenharmony_ci "%s: em28xx_write_reg - msp34xx(2) failed! error [%d]\n", 26818c2ecf20Sopenharmony_ci __func__, ret); 26828c2ecf20Sopenharmony_ci goto unregister_dev; 26838c2ecf20Sopenharmony_ci } 26848c2ecf20Sopenharmony_ci usleep_range(10000, 11000); 26858c2ecf20Sopenharmony_ci } 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci /* set default norm */ 26888c2ecf20Sopenharmony_ci v4l2->norm = V4L2_STD_PAL; 26898c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); 26908c2ecf20Sopenharmony_ci v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci /* Analog specific initialization */ 26938c2ecf20Sopenharmony_ci v4l2->format = &format[0]; 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci maxw = norm_maxw(dev); 26968c2ecf20Sopenharmony_ci /* 26978c2ecf20Sopenharmony_ci * MaxPacketSize for em2800 is too small to capture at full resolution 26988c2ecf20Sopenharmony_ci * use half of maxw as the scaler can only scale to 50% 26998c2ecf20Sopenharmony_ci */ 27008c2ecf20Sopenharmony_ci if (dev->board.is_em2800) 27018c2ecf20Sopenharmony_ci maxw /= 2; 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci em28xx_set_video_format(dev, format[0].fourcc, 27048c2ecf20Sopenharmony_ci maxw, norm_maxh(dev)); 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci video_mux(dev, 0); 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci /* Audio defaults */ 27098c2ecf20Sopenharmony_ci dev->mute = 1; 27108c2ecf20Sopenharmony_ci dev->volume = 0x1f; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ 27138c2ecf20Sopenharmony_ci val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); 27148c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R0F_XCLK, 27158c2ecf20Sopenharmony_ci (EM28XX_XCLK_AUDIO_UNMUTE | val)); 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci em28xx_set_outfmt(dev); 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci /* Add image controls */ 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci /* 27228c2ecf20Sopenharmony_ci * NOTE: at this point, the subdevices are already registered, so 27238c2ecf20Sopenharmony_ci * bridge controls are only added/enabled when no subdevice provides 27248c2ecf20Sopenharmony_ci * them 27258c2ecf20Sopenharmony_ci */ 27268c2ecf20Sopenharmony_ci if (!v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST)) 27278c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 27288c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 27298c2ecf20Sopenharmony_ci 0, 0x1f, 1, CONTRAST_DEFAULT); 27308c2ecf20Sopenharmony_ci if (!v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS)) 27318c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 27328c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 27338c2ecf20Sopenharmony_ci -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT); 27348c2ecf20Sopenharmony_ci if (!v4l2_ctrl_find(hdl, V4L2_CID_SATURATION)) 27358c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 27368c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, 27378c2ecf20Sopenharmony_ci 0, 0x1f, 1, SATURATION_DEFAULT); 27388c2ecf20Sopenharmony_ci if (!v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE)) 27398c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 27408c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 27418c2ecf20Sopenharmony_ci -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT); 27428c2ecf20Sopenharmony_ci if (!v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE)) 27438c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 27448c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 27458c2ecf20Sopenharmony_ci -0x30, 0x30, 1, RED_BALANCE_DEFAULT); 27468c2ecf20Sopenharmony_ci if (!v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS)) 27478c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, 27488c2ecf20Sopenharmony_ci V4L2_CID_SHARPNESS, 27498c2ecf20Sopenharmony_ci 0, 0x0f, 1, SHARPNESS_DEFAULT); 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci /* Reset image controls */ 27528c2ecf20Sopenharmony_ci em28xx_colorlevels_set_default(dev); 27538c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(hdl); 27548c2ecf20Sopenharmony_ci ret = hdl->error; 27558c2ecf20Sopenharmony_ci if (ret) 27568c2ecf20Sopenharmony_ci goto unregister_dev; 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci /* allocate and fill video video_device struct */ 27598c2ecf20Sopenharmony_ci em28xx_vdev_init(dev, &v4l2->vdev, &em28xx_video_template, "video"); 27608c2ecf20Sopenharmony_ci mutex_init(&v4l2->vb_queue_lock); 27618c2ecf20Sopenharmony_ci mutex_init(&v4l2->vb_vbi_queue_lock); 27628c2ecf20Sopenharmony_ci v4l2->vdev.queue = &v4l2->vb_vidq; 27638c2ecf20Sopenharmony_ci v4l2->vdev.queue->lock = &v4l2->vb_queue_lock; 27648c2ecf20Sopenharmony_ci v4l2->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | 27658c2ecf20Sopenharmony_ci V4L2_CAP_STREAMING; 27668c2ecf20Sopenharmony_ci if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) 27678c2ecf20Sopenharmony_ci v4l2->vdev.device_caps |= V4L2_CAP_AUDIO; 27688c2ecf20Sopenharmony_ci if (dev->tuner_type != TUNER_ABSENT) 27698c2ecf20Sopenharmony_ci v4l2->vdev.device_caps |= V4L2_CAP_TUNER; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci /* disable inapplicable ioctls */ 27738c2ecf20Sopenharmony_ci if (dev->is_webcam) { 27748c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD); 27758c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_STD); 27768c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_STD); 27778c2ecf20Sopenharmony_ci } else { 27788c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM); 27798c2ecf20Sopenharmony_ci } 27808c2ecf20Sopenharmony_ci if (dev->tuner_type == TUNER_ABSENT) { 27818c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER); 27828c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER); 27838c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY); 27848c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_FREQUENCY); 27858c2ecf20Sopenharmony_ci } 27868c2ecf20Sopenharmony_ci if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) { 27878c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_AUDIO); 27888c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO); 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci /* register v4l2 video video_device */ 27928c2ecf20Sopenharmony_ci ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO, 27938c2ecf20Sopenharmony_ci video_nr[dev->devno]); 27948c2ecf20Sopenharmony_ci if (ret) { 27958c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 27968c2ecf20Sopenharmony_ci "unable to register video device (error=%i).\n", ret); 27978c2ecf20Sopenharmony_ci goto unregister_dev; 27988c2ecf20Sopenharmony_ci } 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci /* Allocate and fill vbi video_device struct */ 28018c2ecf20Sopenharmony_ci if (em28xx_vbi_supported(dev) == 1) { 28028c2ecf20Sopenharmony_ci em28xx_vdev_init(dev, &v4l2->vbi_dev, &em28xx_video_template, 28038c2ecf20Sopenharmony_ci "vbi"); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci v4l2->vbi_dev.queue = &v4l2->vb_vbiq; 28068c2ecf20Sopenharmony_ci v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; 28078c2ecf20Sopenharmony_ci v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING | 28088c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; 28098c2ecf20Sopenharmony_ci if (dev->tuner_type != TUNER_ABSENT) 28108c2ecf20Sopenharmony_ci v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci /* disable inapplicable ioctls */ 28138c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM); 28148c2ecf20Sopenharmony_ci if (dev->tuner_type == TUNER_ABSENT) { 28158c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_TUNER); 28168c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_TUNER); 28178c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_FREQUENCY); 28188c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_FREQUENCY); 28198c2ecf20Sopenharmony_ci } 28208c2ecf20Sopenharmony_ci if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) { 28218c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_AUDIO); 28228c2ecf20Sopenharmony_ci v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_AUDIO); 28238c2ecf20Sopenharmony_ci } 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci /* register v4l2 vbi video_device */ 28268c2ecf20Sopenharmony_ci ret = video_register_device(&v4l2->vbi_dev, VFL_TYPE_VBI, 28278c2ecf20Sopenharmony_ci vbi_nr[dev->devno]); 28288c2ecf20Sopenharmony_ci if (ret < 0) { 28298c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 28308c2ecf20Sopenharmony_ci "unable to register vbi device\n"); 28318c2ecf20Sopenharmony_ci goto unregister_dev; 28328c2ecf20Sopenharmony_ci } 28338c2ecf20Sopenharmony_ci } 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { 28368c2ecf20Sopenharmony_ci em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template, 28378c2ecf20Sopenharmony_ci "radio"); 28388c2ecf20Sopenharmony_ci v4l2->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; 28398c2ecf20Sopenharmony_ci ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO, 28408c2ecf20Sopenharmony_ci radio_nr[dev->devno]); 28418c2ecf20Sopenharmony_ci if (ret < 0) { 28428c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 28438c2ecf20Sopenharmony_ci "can't register radio device\n"); 28448c2ecf20Sopenharmony_ci goto unregister_dev; 28458c2ecf20Sopenharmony_ci } 28468c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 28478c2ecf20Sopenharmony_ci "Registered radio device as %s\n", 28488c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->radio_dev)); 28498c2ecf20Sopenharmony_ci } 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci /* Init entities at the Media Controller */ 28528c2ecf20Sopenharmony_ci em28xx_v4l2_create_entities(dev); 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 28558c2ecf20Sopenharmony_ci ret = v4l2_mc_create_media_graph(dev->media_dev); 28568c2ecf20Sopenharmony_ci if (ret) { 28578c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 28588c2ecf20Sopenharmony_ci "failed to create media graph\n"); 28598c2ecf20Sopenharmony_ci em28xx_v4l2_media_release(dev); 28608c2ecf20Sopenharmony_ci goto unregister_dev; 28618c2ecf20Sopenharmony_ci } 28628c2ecf20Sopenharmony_ci#endif 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 28658c2ecf20Sopenharmony_ci "V4L2 video device registered as %s\n", 28668c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->vdev)); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->vbi_dev)) 28698c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 28708c2ecf20Sopenharmony_ci "V4L2 VBI device registered as %s\n", 28718c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->vbi_dev)); 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci /* Save some power by putting tuner to sleep */ 28748c2ecf20Sopenharmony_ci v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci /* initialize videobuf2 stuff */ 28778c2ecf20Sopenharmony_ci em28xx_vb2_setup(dev); 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 28808c2ecf20Sopenharmony_ci "V4L2 extension successfully initialized\n"); 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci kref_get(&dev->ref); 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 28858c2ecf20Sopenharmony_ci return 0; 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ciunregister_dev: 28888c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->radio_dev)) { 28898c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 28908c2ecf20Sopenharmony_ci "V4L2 device %s deregistered\n", 28918c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->radio_dev)); 28928c2ecf20Sopenharmony_ci video_unregister_device(&v4l2->radio_dev); 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->vbi_dev)) { 28958c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 28968c2ecf20Sopenharmony_ci "V4L2 device %s deregistered\n", 28978c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->vbi_dev)); 28988c2ecf20Sopenharmony_ci video_unregister_device(&v4l2->vbi_dev); 28998c2ecf20Sopenharmony_ci } 29008c2ecf20Sopenharmony_ci if (video_is_registered(&v4l2->vdev)) { 29018c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 29028c2ecf20Sopenharmony_ci "V4L2 device %s deregistered\n", 29038c2ecf20Sopenharmony_ci video_device_node_name(&v4l2->vdev)); 29048c2ecf20Sopenharmony_ci video_unregister_device(&v4l2->vdev); 29058c2ecf20Sopenharmony_ci } 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&v4l2->ctrl_handler); 29088c2ecf20Sopenharmony_ci v4l2_device_unregister(&v4l2->v4l2_dev); 29098c2ecf20Sopenharmony_cierr: 29108c2ecf20Sopenharmony_ci dev->v4l2 = NULL; 29118c2ecf20Sopenharmony_ci kref_put(&v4l2->ref, em28xx_free_v4l2); 29128c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 29138c2ecf20Sopenharmony_ci return ret; 29148c2ecf20Sopenharmony_ci} 29158c2ecf20Sopenharmony_ci 29168c2ecf20Sopenharmony_cistatic struct em28xx_ops v4l2_ops = { 29178c2ecf20Sopenharmony_ci .id = EM28XX_V4L2, 29188c2ecf20Sopenharmony_ci .name = "Em28xx v4l2 Extension", 29198c2ecf20Sopenharmony_ci .init = em28xx_v4l2_init, 29208c2ecf20Sopenharmony_ci .fini = em28xx_v4l2_fini, 29218c2ecf20Sopenharmony_ci .suspend = em28xx_v4l2_suspend, 29228c2ecf20Sopenharmony_ci .resume = em28xx_v4l2_resume, 29238c2ecf20Sopenharmony_ci}; 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_cistatic int __init em28xx_video_register(void) 29268c2ecf20Sopenharmony_ci{ 29278c2ecf20Sopenharmony_ci return em28xx_register_extension(&v4l2_ops); 29288c2ecf20Sopenharmony_ci} 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_cistatic void __exit em28xx_video_unregister(void) 29318c2ecf20Sopenharmony_ci{ 29328c2ecf20Sopenharmony_ci em28xx_unregister_extension(&v4l2_ops); 29338c2ecf20Sopenharmony_ci} 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_cimodule_init(em28xx_video_register); 29368c2ecf20Sopenharmony_cimodule_exit(em28xx_video_unregister); 2937