18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * STK1160 driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Ezequiel Garcia 68c2ecf20Sopenharmony_ci * <elezegarcia--a.t--gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on Easycap driver by R.M. Thomas 98c2ecf20Sopenharmony_ci * Copyright (C) 2010 R.M. Thomas 108c2ecf20Sopenharmony_ci * <rmthomas--a.t--sciolus.org> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 248c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <media/i2c/saa7115.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "stk1160.h" 298c2ecf20Sopenharmony_ci#include "stk1160-reg.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic bool keep_buffers; 328c2ecf20Sopenharmony_cimodule_param(keep_buffers, bool, 0644); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cienum stk1160_decimate_mode { 368c2ecf20Sopenharmony_ci STK1160_DECIMATE_MORE_THAN_HALF, 378c2ecf20Sopenharmony_ci STK1160_DECIMATE_LESS_THAN_HALF, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct stk1160_decimate_ctrl { 418c2ecf20Sopenharmony_ci bool col_en, row_en; 428c2ecf20Sopenharmony_ci enum stk1160_decimate_mode col_mode, row_mode; 438c2ecf20Sopenharmony_ci unsigned int col_n, row_n; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* supported video standards */ 478c2ecf20Sopenharmony_cistatic struct stk1160_fmt format[] = { 488c2ecf20Sopenharmony_ci { 498c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 508c2ecf20Sopenharmony_ci .depth = 16, 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Helper to find the next divisor that results in modulo being zero. 568c2ecf20Sopenharmony_ci * This is required to guarantee valid decimation unit counts. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic unsigned int 598c2ecf20Sopenharmony_cidiv_round_integer(unsigned int x, unsigned int y) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci for (;; y++) { 628c2ecf20Sopenharmony_ci if (x % y == 0) 638c2ecf20Sopenharmony_ci return x / y; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void stk1160_set_std(struct stk1160 *dev) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci int i; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci static struct regval std525[] = { 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* 720x480 */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Frame start */ 768c2ecf20Sopenharmony_ci {STK116_CFSPO_STX_L, 0x0000}, 778c2ecf20Sopenharmony_ci {STK116_CFSPO_STX_H, 0x0000}, 788c2ecf20Sopenharmony_ci {STK116_CFSPO_STY_L, 0x0003}, 798c2ecf20Sopenharmony_ci {STK116_CFSPO_STY_H, 0x0000}, 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Frame end */ 828c2ecf20Sopenharmony_ci {STK116_CFEPO_ENX_L, 0x05a0}, 838c2ecf20Sopenharmony_ci {STK116_CFEPO_ENX_H, 0x0005}, 848c2ecf20Sopenharmony_ci {STK116_CFEPO_ENY_L, 0x00f3}, 858c2ecf20Sopenharmony_ci {STK116_CFEPO_ENY_H, 0x0000}, 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci {0xffff, 0xffff} 888c2ecf20Sopenharmony_ci }; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci static struct regval std625[] = { 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* 720x576 */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* TODO: Each line of frame has some junk at the end */ 958c2ecf20Sopenharmony_ci /* Frame start */ 968c2ecf20Sopenharmony_ci {STK116_CFSPO, 0x0000}, 978c2ecf20Sopenharmony_ci {STK116_CFSPO+1, 0x0000}, 988c2ecf20Sopenharmony_ci {STK116_CFSPO+2, 0x0001}, 998c2ecf20Sopenharmony_ci {STK116_CFSPO+3, 0x0000}, 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Frame end */ 1028c2ecf20Sopenharmony_ci {STK116_CFEPO, 0x05a0}, 1038c2ecf20Sopenharmony_ci {STK116_CFEPO+1, 0x0005}, 1048c2ecf20Sopenharmony_ci {STK116_CFEPO+2, 0x0121}, 1058c2ecf20Sopenharmony_ci {STK116_CFEPO+3, 0x0001}, 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci {0xffff, 0xffff} 1088c2ecf20Sopenharmony_ci }; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (dev->norm & V4L2_STD_525_60) { 1118c2ecf20Sopenharmony_ci stk1160_dbg("registers to NTSC like standard\n"); 1128c2ecf20Sopenharmony_ci for (i = 0; std525[i].reg != 0xffff; i++) 1138c2ecf20Sopenharmony_ci stk1160_write_reg(dev, std525[i].reg, std525[i].val); 1148c2ecf20Sopenharmony_ci } else { 1158c2ecf20Sopenharmony_ci stk1160_dbg("registers to PAL like standard\n"); 1168c2ecf20Sopenharmony_ci for (i = 0; std625[i].reg != 0xffff; i++) 1178c2ecf20Sopenharmony_ci stk1160_write_reg(dev, std625[i].reg, std625[i].val); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void stk1160_set_fmt(struct stk1160 *dev, 1238c2ecf20Sopenharmony_ci struct stk1160_decimate_ctrl *ctrl) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci u32 val = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (ctrl) { 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Since the format is UYVY, the device must skip or send 1308c2ecf20Sopenharmony_ci * a number of rows/columns multiple of four. This way, the 1318c2ecf20Sopenharmony_ci * colour format is preserved. The STK1160_DEC_UNIT_SIZE bit 1328c2ecf20Sopenharmony_ci * does exactly this. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci val |= STK1160_DEC_UNIT_SIZE; 1358c2ecf20Sopenharmony_ci val |= ctrl->col_en ? STK1160_H_DEC_EN : 0; 1368c2ecf20Sopenharmony_ci val |= ctrl->row_en ? STK1160_V_DEC_EN : 0; 1378c2ecf20Sopenharmony_ci val |= ctrl->col_mode == 1388c2ecf20Sopenharmony_ci STK1160_DECIMATE_MORE_THAN_HALF ? 1398c2ecf20Sopenharmony_ci STK1160_H_DEC_MODE : 0; 1408c2ecf20Sopenharmony_ci val |= ctrl->row_mode == 1418c2ecf20Sopenharmony_ci STK1160_DECIMATE_MORE_THAN_HALF ? 1428c2ecf20Sopenharmony_ci STK1160_V_DEC_MODE : 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Horizontal count units */ 1458c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DMCTRL_H_UNITS, ctrl->col_n); 1468c2ecf20Sopenharmony_ci /* Vertical count units */ 1478c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DMCTRL_V_UNITS, ctrl->row_n); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci stk1160_dbg("decimate 0x%x, column units %d, row units %d\n", 1508c2ecf20Sopenharmony_ci val, ctrl->col_n, ctrl->row_n); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Decimation control */ 1548c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DMCTRL, val); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * Set a new alternate setting. 1598c2ecf20Sopenharmony_ci * Returns true is dev->max_pkt_size has changed, false otherwise. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic bool stk1160_set_alternate(struct stk1160 *dev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int i, prev_alt = dev->alt; 1648c2ecf20Sopenharmony_ci unsigned int min_pkt_size; 1658c2ecf20Sopenharmony_ci bool new_pkt_size; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * If we don't set right alternate, 1698c2ecf20Sopenharmony_ci * then we will get a green screen with junk. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci min_pkt_size = STK1160_MIN_PKT_SIZE; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_alt; i++) { 1748c2ecf20Sopenharmony_ci /* stop when the selected alt setting offers enough bandwidth */ 1758c2ecf20Sopenharmony_ci if (dev->alt_max_pkt_size[i] >= min_pkt_size) { 1768c2ecf20Sopenharmony_ci dev->alt = i; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * otherwise make sure that we end up with the maximum bandwidth 1808c2ecf20Sopenharmony_ci * because the min_pkt_size equation might be wrong... 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci } else if (dev->alt_max_pkt_size[i] > 1838c2ecf20Sopenharmony_ci dev->alt_max_pkt_size[dev->alt]) 1848c2ecf20Sopenharmony_ci dev->alt = i; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci stk1160_dbg("setting alternate %d\n", dev->alt); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (dev->alt != prev_alt) { 1908c2ecf20Sopenharmony_ci stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n", 1918c2ecf20Sopenharmony_ci min_pkt_size, dev->alt); 1928c2ecf20Sopenharmony_ci stk1160_dbg("setting alt %d with wMaxPacketSize=%u\n", 1938c2ecf20Sopenharmony_ci dev->alt, dev->alt_max_pkt_size[dev->alt]); 1948c2ecf20Sopenharmony_ci usb_set_interface(dev->udev, 0, dev->alt); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci new_pkt_size = dev->max_pkt_size != dev->alt_max_pkt_size[dev->alt]; 1988c2ecf20Sopenharmony_ci dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return new_pkt_size; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int stk1160_start_streaming(struct stk1160 *dev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci bool new_pkt_size; 2068c2ecf20Sopenharmony_ci int rc = 0; 2078c2ecf20Sopenharmony_ci int i; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Check device presence */ 2108c2ecf20Sopenharmony_ci if (!dev->udev) 2118c2ecf20Sopenharmony_ci return -ENODEV; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->v4l_lock)) 2148c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * For some reason it is mandatory to set alternate *first* 2178c2ecf20Sopenharmony_ci * and only *then* initialize isoc urbs. 2188c2ecf20Sopenharmony_ci * Someone please explain me why ;) 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci new_pkt_size = stk1160_set_alternate(dev); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* 2238c2ecf20Sopenharmony_ci * We (re)allocate isoc urbs if: 2248c2ecf20Sopenharmony_ci * there is no allocated isoc urbs, OR 2258c2ecf20Sopenharmony_ci * a new dev->max_pkt_size is detected 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci if (!dev->isoc_ctl.num_bufs || new_pkt_size) { 2288c2ecf20Sopenharmony_ci rc = stk1160_alloc_isoc(dev); 2298c2ecf20Sopenharmony_ci if (rc < 0) 2308c2ecf20Sopenharmony_ci goto out_stop_hw; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* submit urbs and enables IRQ */ 2348c2ecf20Sopenharmony_ci for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { 2358c2ecf20Sopenharmony_ci rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL); 2368c2ecf20Sopenharmony_ci if (rc) { 2378c2ecf20Sopenharmony_ci stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); 2388c2ecf20Sopenharmony_ci goto out_uninit; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Start saa711x */ 2438c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dev->sequence = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Start stk1160 */ 2488c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL, 0xb3); 2498c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci stk1160_dbg("streaming started\n"); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci mutex_unlock(&dev->v4l_lock); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciout_uninit: 2588c2ecf20Sopenharmony_ci stk1160_uninit_isoc(dev); 2598c2ecf20Sopenharmony_ciout_stop_hw: 2608c2ecf20Sopenharmony_ci usb_set_interface(dev->udev, 0, 0); 2618c2ecf20Sopenharmony_ci stk1160_clear_queue(dev, VB2_BUF_STATE_QUEUED); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci mutex_unlock(&dev->v4l_lock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return rc; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* Must be called with v4l_lock hold */ 2698c2ecf20Sopenharmony_cistatic void stk1160_stop_hw(struct stk1160 *dev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci /* If the device is not physically present, there is nothing to do */ 2728c2ecf20Sopenharmony_ci if (!dev->udev) 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* set alternate 0 */ 2768c2ecf20Sopenharmony_ci dev->alt = 0; 2778c2ecf20Sopenharmony_ci stk1160_dbg("setting alternate %d\n", dev->alt); 2788c2ecf20Sopenharmony_ci usb_set_interface(dev->udev, 0, 0); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Stop stk1160 */ 2818c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL, 0x00); 2828c2ecf20Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Stop saa711x */ 2858c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int stk1160_stop_streaming(struct stk1160 *dev) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->v4l_lock)) 2918c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci * Once URBs are cancelled, the URB complete handler 2958c2ecf20Sopenharmony_ci * won't be running. This is required to safely release the 2968c2ecf20Sopenharmony_ci * current buffer (dev->isoc_ctl.buf). 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci stk1160_cancel_isoc(dev); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * It is possible to keep buffers around using a module parameter. 3028c2ecf20Sopenharmony_ci * This is intended to avoid memory fragmentation. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci if (!keep_buffers) 3058c2ecf20Sopenharmony_ci stk1160_free_isoc(dev); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci stk1160_stop_hw(dev); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci stk1160_clear_queue(dev, VB2_BUF_STATE_ERROR); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci stk1160_dbg("streaming stopped\n"); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mutex_unlock(&dev->v4l_lock); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations stk1160_fops = { 3198c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3208c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 3218c2ecf20Sopenharmony_ci .release = vb2_fop_release, 3228c2ecf20Sopenharmony_ci .read = vb2_fop_read, 3238c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 3248c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 3258c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * vidioc ioctls 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, 3328c2ecf20Sopenharmony_ci void *priv, struct v4l2_capability *cap) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci strscpy(cap->driver, "stk1160", sizeof(cap->driver)); 3378c2ecf20Sopenharmony_ci strscpy(cap->card, "stk1160", sizeof(cap->card)); 3388c2ecf20Sopenharmony_ci usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 3438c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci if (f->index != 0) 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci f->pixelformat = format[f->index].fourcc; 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 3538c2ecf20Sopenharmony_ci struct v4l2_format *f) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci f->fmt.pix.width = dev->width; 3588c2ecf20Sopenharmony_ci f->fmt.pix.height = dev->height; 3598c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 3608c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = dev->fmt->fourcc; 3618c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = dev->width * 2; 3628c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; 3638c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int stk1160_try_fmt(struct stk1160 *dev, struct v4l2_format *f, 3698c2ecf20Sopenharmony_ci struct stk1160_decimate_ctrl *ctrl) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci unsigned int width, height; 3728c2ecf20Sopenharmony_ci unsigned int base_width, base_height; 3738c2ecf20Sopenharmony_ci unsigned int col_n, row_n; 3748c2ecf20Sopenharmony_ci enum stk1160_decimate_mode col_mode, row_mode; 3758c2ecf20Sopenharmony_ci bool col_en, row_en; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci base_width = 720; 3788c2ecf20Sopenharmony_ci base_height = (dev->norm & V4L2_STD_525_60) ? 480 : 576; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Minimum width and height is 5% the frame size */ 3818c2ecf20Sopenharmony_ci width = clamp_t(unsigned int, f->fmt.pix.width, 3828c2ecf20Sopenharmony_ci base_width / 20, base_width); 3838c2ecf20Sopenharmony_ci height = clamp_t(unsigned int, f->fmt.pix.height, 3848c2ecf20Sopenharmony_ci base_height / 20, base_height); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Let's set default no decimation values */ 3878c2ecf20Sopenharmony_ci col_n = 0; 3888c2ecf20Sopenharmony_ci row_n = 0; 3898c2ecf20Sopenharmony_ci col_en = false; 3908c2ecf20Sopenharmony_ci row_en = false; 3918c2ecf20Sopenharmony_ci f->fmt.pix.width = base_width; 3928c2ecf20Sopenharmony_ci f->fmt.pix.height = base_height; 3938c2ecf20Sopenharmony_ci row_mode = STK1160_DECIMATE_LESS_THAN_HALF; 3948c2ecf20Sopenharmony_ci col_mode = STK1160_DECIMATE_LESS_THAN_HALF; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (width < base_width && width > base_width / 2) { 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * The device will send count units for each 3998c2ecf20Sopenharmony_ci * unit skipped. This means count unit is: 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * n = width / (frame width - width) 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * And the width is: 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * width = (n / n + 1) * frame width 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci col_n = div_round_integer(width, base_width - width); 4088c2ecf20Sopenharmony_ci if (col_n > 0 && col_n <= 255) { 4098c2ecf20Sopenharmony_ci col_en = true; 4108c2ecf20Sopenharmony_ci col_mode = STK1160_DECIMATE_LESS_THAN_HALF; 4118c2ecf20Sopenharmony_ci f->fmt.pix.width = (base_width * col_n) / (col_n + 1); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci } else if (width <= base_width / 2) { 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * The device will skip count units for each 4188c2ecf20Sopenharmony_ci * unit sent. This means count is: 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * n = (frame width / width) - 1 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * And the width is: 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci * width = frame width / (n + 1) 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci col_n = div_round_integer(base_width, width) - 1; 4278c2ecf20Sopenharmony_ci if (col_n > 0 && col_n <= 255) { 4288c2ecf20Sopenharmony_ci col_en = true; 4298c2ecf20Sopenharmony_ci col_mode = STK1160_DECIMATE_MORE_THAN_HALF; 4308c2ecf20Sopenharmony_ci f->fmt.pix.width = base_width / (col_n + 1); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (height < base_height && height > base_height / 2) { 4358c2ecf20Sopenharmony_ci row_n = div_round_integer(height, base_height - height); 4368c2ecf20Sopenharmony_ci if (row_n > 0 && row_n <= 255) { 4378c2ecf20Sopenharmony_ci row_en = true; 4388c2ecf20Sopenharmony_ci row_mode = STK1160_DECIMATE_LESS_THAN_HALF; 4398c2ecf20Sopenharmony_ci f->fmt.pix.height = (base_height * row_n) / (row_n + 1); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci } else if (height <= base_height / 2) { 4438c2ecf20Sopenharmony_ci row_n = div_round_integer(base_height, height) - 1; 4448c2ecf20Sopenharmony_ci if (row_n > 0 && row_n <= 255) { 4458c2ecf20Sopenharmony_ci row_en = true; 4468c2ecf20Sopenharmony_ci row_mode = STK1160_DECIMATE_MORE_THAN_HALF; 4478c2ecf20Sopenharmony_ci f->fmt.pix.height = base_height / (row_n + 1); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = dev->fmt->fourcc; 4528c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 4538c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = f->fmt.pix.width * 2; 4548c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 4558c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (ctrl) { 4588c2ecf20Sopenharmony_ci ctrl->col_en = col_en; 4598c2ecf20Sopenharmony_ci ctrl->col_n = col_n; 4608c2ecf20Sopenharmony_ci ctrl->col_mode = col_mode; 4618c2ecf20Sopenharmony_ci ctrl->row_en = row_en; 4628c2ecf20Sopenharmony_ci ctrl->row_n = row_n; 4638c2ecf20Sopenharmony_ci ctrl->row_mode = row_mode; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci stk1160_dbg("width %d, height %d\n", 4678c2ecf20Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height); 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 4728c2ecf20Sopenharmony_ci struct v4l2_format *f) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return stk1160_try_fmt(dev, f, NULL); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 4808c2ecf20Sopenharmony_ci struct v4l2_format *f) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 4838c2ecf20Sopenharmony_ci struct vb2_queue *q = &dev->vb_vidq; 4848c2ecf20Sopenharmony_ci struct stk1160_decimate_ctrl ctrl; 4858c2ecf20Sopenharmony_ci int rc; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (vb2_is_busy(q)) 4888c2ecf20Sopenharmony_ci return -EBUSY; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci rc = stk1160_try_fmt(dev, f, &ctrl); 4918c2ecf20Sopenharmony_ci if (rc < 0) 4928c2ecf20Sopenharmony_ci return rc; 4938c2ecf20Sopenharmony_ci dev->width = f->fmt.pix.width; 4948c2ecf20Sopenharmony_ci dev->height = f->fmt.pix.height; 4958c2ecf20Sopenharmony_ci stk1160_set_fmt(dev, &ctrl); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5038c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci *norm = dev->norm; 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5188c2ecf20Sopenharmony_ci struct vb2_queue *q = &dev->vb_vidq; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (dev->norm == norm) 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (vb2_is_busy(q)) 5248c2ecf20Sopenharmony_ci return -EBUSY; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Check device presence */ 5278c2ecf20Sopenharmony_ci if (!dev->udev) 5288c2ecf20Sopenharmony_ci return -ENODEV; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* We need to set this now, before we call stk1160_set_std */ 5318c2ecf20Sopenharmony_ci dev->width = 720; 5328c2ecf20Sopenharmony_ci dev->height = (norm & V4L2_STD_525_60) ? 480 : 576; 5338c2ecf20Sopenharmony_ci dev->norm = norm; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci stk1160_set_std(dev); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Calling with NULL disables frame decimation */ 5388c2ecf20Sopenharmony_ci stk1160_set_fmt(dev, NULL); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, 5418c2ecf20Sopenharmony_ci dev->norm); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv, 5488c2ecf20Sopenharmony_ci struct v4l2_input *i) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (i->index > STK1160_MAX_INPUT) 5538c2ecf20Sopenharmony_ci return -EINVAL; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* S-Video special handling */ 5568c2ecf20Sopenharmony_ci if (i->index == STK1160_SVIDEO_INPUT) 5578c2ecf20Sopenharmony_ci sprintf(i->name, "S-Video"); 5588c2ecf20Sopenharmony_ci else 5598c2ecf20Sopenharmony_ci sprintf(i->name, "Composite%d", i->index); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 5628c2ecf20Sopenharmony_ci i->std = dev->vdev.tvnorms; 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5698c2ecf20Sopenharmony_ci *i = dev->ctl_input; 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (i > STK1160_MAX_INPUT) 5788c2ecf20Sopenharmony_ci return -EINVAL; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci dev->ctl_input = i; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci stk1160_select_input(dev); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 5888c2ecf20Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *priv, 5898c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 5928c2ecf20Sopenharmony_ci int rc; 5938c2ecf20Sopenharmony_ci u8 val; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Match host */ 5968c2ecf20Sopenharmony_ci rc = stk1160_read_reg(dev, reg->reg, &val); 5978c2ecf20Sopenharmony_ci reg->val = val; 5988c2ecf20Sopenharmony_ci reg->size = 1; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return rc; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *priv, 6048c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Match host */ 6098c2ecf20Sopenharmony_ci return stk1160_write_reg(dev, reg->reg, reg->val); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci#endif 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops stk1160_ioctl_ops = { 6148c2ecf20Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 6158c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 6168c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 6178c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 6188c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 6198c2ecf20Sopenharmony_ci .vidioc_querystd = vidioc_querystd, 6208c2ecf20Sopenharmony_ci .vidioc_g_std = vidioc_g_std, 6218c2ecf20Sopenharmony_ci .vidioc_s_std = vidioc_s_std, 6228c2ecf20Sopenharmony_ci .vidioc_enum_input = vidioc_enum_input, 6238c2ecf20Sopenharmony_ci .vidioc_g_input = vidioc_g_input, 6248c2ecf20Sopenharmony_ci .vidioc_s_input = vidioc_s_input, 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* vb2 takes care of these */ 6278c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 6288c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 6298c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 6308c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 6318c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 6328c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 6338c2ecf20Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 6368c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 6378c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 6408c2ecf20Sopenharmony_ci .vidioc_g_register = vidioc_g_register, 6418c2ecf20Sopenharmony_ci .vidioc_s_register = vidioc_s_register, 6428c2ecf20Sopenharmony_ci#endif 6438c2ecf20Sopenharmony_ci}; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/********************************************************************/ 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* 6488c2ecf20Sopenharmony_ci * Videobuf2 operations 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 6518c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 6528c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vq); 6558c2ecf20Sopenharmony_ci unsigned long size; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci size = dev->width * dev->height * 2; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* 6608c2ecf20Sopenharmony_ci * Here we can change the number of buffers being requested. 6618c2ecf20Sopenharmony_ci * So, we set a minimum and a maximum like this: 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci *nbuffers = clamp_t(unsigned int, *nbuffers, 6648c2ecf20Sopenharmony_ci STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (*nplanes) 6678c2ecf20Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* This means a packed colorformat */ 6708c2ecf20Sopenharmony_ci *nplanes = 1; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci sizes[0] = size; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci stk1160_dbg("%s: buffer count %d, each %ld bytes\n", 6758c2ecf20Sopenharmony_ci __func__, *nbuffers, size); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci unsigned long flags; 6838c2ecf20Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue); 6848c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 6858c2ecf20Sopenharmony_ci struct stk1160_buffer *buf = 6868c2ecf20Sopenharmony_ci container_of(vbuf, struct stk1160_buffer, vb); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buf_lock, flags); 6898c2ecf20Sopenharmony_ci if (!dev->udev) { 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * If the device is disconnected return the buffer to userspace 6928c2ecf20Sopenharmony_ci * directly. The next QBUF call will fail with -ENODEV. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 6958c2ecf20Sopenharmony_ci } else { 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci buf->mem = vb2_plane_vaddr(vb, 0); 6988c2ecf20Sopenharmony_ci buf->length = vb2_plane_size(vb, 0); 6998c2ecf20Sopenharmony_ci buf->bytesused = 0; 7008c2ecf20Sopenharmony_ci buf->pos = 0; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* 7038c2ecf20Sopenharmony_ci * If buffer length is less from expected then we return 7048c2ecf20Sopenharmony_ci * the buffer to userspace directly. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci if (buf->length < dev->width * dev->height * 2) 7078c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 7088c2ecf20Sopenharmony_ci else 7098c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dev->avail_bufs); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buf_lock, flags); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vq); 7188c2ecf20Sopenharmony_ci return stk1160_start_streaming(dev); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */ 7228c2ecf20Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vq); 7258c2ecf20Sopenharmony_ci stk1160_stop_streaming(dev); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const struct vb2_ops stk1160_video_qops = { 7298c2ecf20Sopenharmony_ci .queue_setup = queue_setup, 7308c2ecf20Sopenharmony_ci .buf_queue = buffer_queue, 7318c2ecf20Sopenharmony_ci .start_streaming = start_streaming, 7328c2ecf20Sopenharmony_ci .stop_streaming = stop_streaming, 7338c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 7348c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 7358c2ecf20Sopenharmony_ci}; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic const struct video_device v4l_template = { 7388c2ecf20Sopenharmony_ci .name = "stk1160", 7398c2ecf20Sopenharmony_ci .tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50, 7408c2ecf20Sopenharmony_ci .fops = &stk1160_fops, 7418c2ecf20Sopenharmony_ci .ioctl_ops = &stk1160_ioctl_ops, 7428c2ecf20Sopenharmony_ci .release = video_device_release_empty, 7438c2ecf20Sopenharmony_ci}; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci/********************************************************************/ 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci/* Must be called with both v4l_lock and vb_queue_lock hold */ 7488c2ecf20Sopenharmony_civoid stk1160_clear_queue(struct stk1160 *dev, enum vb2_buffer_state vb2_state) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct stk1160_buffer *buf; 7518c2ecf20Sopenharmony_ci unsigned long flags; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* Release all active buffers */ 7548c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buf_lock, flags); 7558c2ecf20Sopenharmony_ci while (!list_empty(&dev->avail_bufs)) { 7568c2ecf20Sopenharmony_ci buf = list_first_entry(&dev->avail_bufs, 7578c2ecf20Sopenharmony_ci struct stk1160_buffer, list); 7588c2ecf20Sopenharmony_ci list_del(&buf->list); 7598c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, vb2_state); 7608c2ecf20Sopenharmony_ci stk1160_dbg("buffer [%p/%d] aborted\n", 7618c2ecf20Sopenharmony_ci buf, buf->vb.vb2_buf.index); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* It's important to release the current buffer */ 7658c2ecf20Sopenharmony_ci if (dev->isoc_ctl.buf) { 7668c2ecf20Sopenharmony_ci buf = dev->isoc_ctl.buf; 7678c2ecf20Sopenharmony_ci dev->isoc_ctl.buf = NULL; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, vb2_state); 7708c2ecf20Sopenharmony_ci stk1160_dbg("buffer [%p/%d] aborted\n", 7718c2ecf20Sopenharmony_ci buf, buf->vb.vb2_buf.index); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buf_lock, flags); 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciint stk1160_vb2_setup(struct stk1160 *dev) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci int rc; 7798c2ecf20Sopenharmony_ci struct vb2_queue *q; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci q = &dev->vb_vidq; 7828c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 7838c2ecf20Sopenharmony_ci q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 7848c2ecf20Sopenharmony_ci q->drv_priv = dev; 7858c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct stk1160_buffer); 7868c2ecf20Sopenharmony_ci q->ops = &stk1160_video_qops; 7878c2ecf20Sopenharmony_ci q->mem_ops = &vb2_vmalloc_memops; 7888c2ecf20Sopenharmony_ci q->lock = &dev->vb_queue_lock; 7898c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci rc = vb2_queue_init(q); 7928c2ecf20Sopenharmony_ci if (rc < 0) 7938c2ecf20Sopenharmony_ci return rc; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* initialize video dma queue */ 7968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->avail_bufs); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return 0; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ciint stk1160_video_register(struct stk1160 *dev) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci int rc; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* Initialize video_device with a template structure */ 8068c2ecf20Sopenharmony_ci dev->vdev = v4l_template; 8078c2ecf20Sopenharmony_ci dev->vdev.queue = &dev->vb_vidq; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* 8108c2ecf20Sopenharmony_ci * Provide mutexes for v4l2 core and for videobuf2 queue. 8118c2ecf20Sopenharmony_ci * It will be used to protect *only* v4l2 ioctls. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ci dev->vdev.lock = &dev->v4l_lock; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci /* This will be used to set video_device parent */ 8168c2ecf20Sopenharmony_ci dev->vdev.v4l2_dev = &dev->v4l2_dev; 8178c2ecf20Sopenharmony_ci dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 8188c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* NTSC is default */ 8218c2ecf20Sopenharmony_ci dev->norm = V4L2_STD_NTSC_M; 8228c2ecf20Sopenharmony_ci dev->width = 720; 8238c2ecf20Sopenharmony_ci dev->height = 480; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* set default format */ 8268c2ecf20Sopenharmony_ci dev->fmt = &format[0]; 8278c2ecf20Sopenharmony_ci stk1160_set_std(dev); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, 8308c2ecf20Sopenharmony_ci dev->norm); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci video_set_drvdata(&dev->vdev, dev); 8338c2ecf20Sopenharmony_ci rc = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); 8348c2ecf20Sopenharmony_ci if (rc < 0) { 8358c2ecf20Sopenharmony_ci stk1160_err("video_register_device failed (%d)\n", rc); 8368c2ecf20Sopenharmony_ci return rc; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", 8408c2ecf20Sopenharmony_ci video_device_node_name(&dev->vdev)); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci} 844