162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * STK1160 driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Ezequiel Garcia 662306a36Sopenharmony_ci * <elezegarcia--a.t--gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on Easycap driver by R.M. Thomas 962306a36Sopenharmony_ci * Copyright (C) 2010 R.M. Thomas 1062306a36Sopenharmony_ci * <rmthomas--a.t--sciolus.org> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/usb.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/videodev2.h> 1962306a36Sopenharmony_ci#include <media/v4l2-device.h> 2062306a36Sopenharmony_ci#include <media/v4l2-common.h> 2162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2262306a36Sopenharmony_ci#include <media/v4l2-fh.h> 2362306a36Sopenharmony_ci#include <media/v4l2-event.h> 2462306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <media/i2c/saa7115.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "stk1160.h" 2962306a36Sopenharmony_ci#include "stk1160-reg.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic bool keep_buffers; 3262306a36Sopenharmony_cimodule_param(keep_buffers, bool, 0644); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum stk1160_decimate_mode { 3662306a36Sopenharmony_ci STK1160_DECIMATE_MORE_THAN_HALF, 3762306a36Sopenharmony_ci STK1160_DECIMATE_LESS_THAN_HALF, 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct stk1160_decimate_ctrl { 4162306a36Sopenharmony_ci bool col_en, row_en; 4262306a36Sopenharmony_ci enum stk1160_decimate_mode col_mode, row_mode; 4362306a36Sopenharmony_ci unsigned int col_n, row_n; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* supported video standards */ 4762306a36Sopenharmony_cistatic struct stk1160_fmt format[] = { 4862306a36Sopenharmony_ci { 4962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 5062306a36Sopenharmony_ci .depth = 16, 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Helper to find the next divisor that results in modulo being zero. 5662306a36Sopenharmony_ci * This is required to guarantee valid decimation unit counts. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic unsigned int 5962306a36Sopenharmony_cidiv_round_integer(unsigned int x, unsigned int y) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci for (;; y++) { 6262306a36Sopenharmony_ci if (x % y == 0) 6362306a36Sopenharmony_ci return x / y; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void stk1160_set_std(struct stk1160 *dev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci int i; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci static struct regval std525[] = { 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 720x480 */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Frame start */ 7662306a36Sopenharmony_ci {STK116_CFSPO_STX_L, 0x0000}, 7762306a36Sopenharmony_ci {STK116_CFSPO_STX_H, 0x0000}, 7862306a36Sopenharmony_ci {STK116_CFSPO_STY_L, 0x0003}, 7962306a36Sopenharmony_ci {STK116_CFSPO_STY_H, 0x0000}, 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Frame end */ 8262306a36Sopenharmony_ci {STK116_CFEPO_ENX_L, 0x05a0}, 8362306a36Sopenharmony_ci {STK116_CFEPO_ENX_H, 0x0005}, 8462306a36Sopenharmony_ci {STK116_CFEPO_ENY_L, 0x00f3}, 8562306a36Sopenharmony_ci {STK116_CFEPO_ENY_H, 0x0000}, 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci {0xffff, 0xffff} 8862306a36Sopenharmony_ci }; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci static struct regval std625[] = { 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* 720x576 */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* TODO: Each line of frame has some junk at the end */ 9562306a36Sopenharmony_ci /* Frame start */ 9662306a36Sopenharmony_ci {STK116_CFSPO, 0x0000}, 9762306a36Sopenharmony_ci {STK116_CFSPO+1, 0x0000}, 9862306a36Sopenharmony_ci {STK116_CFSPO+2, 0x0001}, 9962306a36Sopenharmony_ci {STK116_CFSPO+3, 0x0000}, 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Frame end */ 10262306a36Sopenharmony_ci {STK116_CFEPO, 0x05a0}, 10362306a36Sopenharmony_ci {STK116_CFEPO+1, 0x0005}, 10462306a36Sopenharmony_ci {STK116_CFEPO+2, 0x0121}, 10562306a36Sopenharmony_ci {STK116_CFEPO+3, 0x0001}, 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci {0xffff, 0xffff} 10862306a36Sopenharmony_ci }; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (dev->norm & V4L2_STD_525_60) { 11162306a36Sopenharmony_ci stk1160_dbg("registers to NTSC like standard\n"); 11262306a36Sopenharmony_ci for (i = 0; std525[i].reg != 0xffff; i++) 11362306a36Sopenharmony_ci stk1160_write_reg(dev, std525[i].reg, std525[i].val); 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci stk1160_dbg("registers to PAL like standard\n"); 11662306a36Sopenharmony_ci for (i = 0; std625[i].reg != 0xffff; i++) 11762306a36Sopenharmony_ci stk1160_write_reg(dev, std625[i].reg, std625[i].val); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void stk1160_set_fmt(struct stk1160 *dev, 12362306a36Sopenharmony_ci struct stk1160_decimate_ctrl *ctrl) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 val = 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (ctrl) { 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * Since the format is UYVY, the device must skip or send 13062306a36Sopenharmony_ci * a number of rows/columns multiple of four. This way, the 13162306a36Sopenharmony_ci * colour format is preserved. The STK1160_DEC_UNIT_SIZE bit 13262306a36Sopenharmony_ci * does exactly this. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci val |= STK1160_DEC_UNIT_SIZE; 13562306a36Sopenharmony_ci val |= ctrl->col_en ? STK1160_H_DEC_EN : 0; 13662306a36Sopenharmony_ci val |= ctrl->row_en ? STK1160_V_DEC_EN : 0; 13762306a36Sopenharmony_ci val |= ctrl->col_mode == 13862306a36Sopenharmony_ci STK1160_DECIMATE_MORE_THAN_HALF ? 13962306a36Sopenharmony_ci STK1160_H_DEC_MODE : 0; 14062306a36Sopenharmony_ci val |= ctrl->row_mode == 14162306a36Sopenharmony_ci STK1160_DECIMATE_MORE_THAN_HALF ? 14262306a36Sopenharmony_ci STK1160_V_DEC_MODE : 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Horizontal count units */ 14562306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DMCTRL_H_UNITS, ctrl->col_n); 14662306a36Sopenharmony_ci /* Vertical count units */ 14762306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DMCTRL_V_UNITS, ctrl->row_n); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci stk1160_dbg("decimate 0x%x, column units %d, row units %d\n", 15062306a36Sopenharmony_ci val, ctrl->col_n, ctrl->row_n); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Decimation control */ 15462306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DMCTRL, val); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * Set a new alternate setting. 15962306a36Sopenharmony_ci * Returns true is dev->max_pkt_size has changed, false otherwise. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic bool stk1160_set_alternate(struct stk1160 *dev) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int i, prev_alt = dev->alt; 16462306a36Sopenharmony_ci unsigned int min_pkt_size; 16562306a36Sopenharmony_ci bool new_pkt_size; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * If we don't set right alternate, 16962306a36Sopenharmony_ci * then we will get a green screen with junk. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci min_pkt_size = STK1160_MIN_PKT_SIZE; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (i = 0; i < dev->num_alt; i++) { 17462306a36Sopenharmony_ci /* stop when the selected alt setting offers enough bandwidth */ 17562306a36Sopenharmony_ci if (dev->alt_max_pkt_size[i] >= min_pkt_size) { 17662306a36Sopenharmony_ci dev->alt = i; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * otherwise make sure that we end up with the maximum bandwidth 18062306a36Sopenharmony_ci * because the min_pkt_size equation might be wrong... 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci } else if (dev->alt_max_pkt_size[i] > 18362306a36Sopenharmony_ci dev->alt_max_pkt_size[dev->alt]) 18462306a36Sopenharmony_ci dev->alt = i; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci stk1160_dbg("setting alternate %d\n", dev->alt); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (dev->alt != prev_alt) { 19062306a36Sopenharmony_ci stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n", 19162306a36Sopenharmony_ci min_pkt_size, dev->alt); 19262306a36Sopenharmony_ci stk1160_dbg("setting alt %d with wMaxPacketSize=%u\n", 19362306a36Sopenharmony_ci dev->alt, dev->alt_max_pkt_size[dev->alt]); 19462306a36Sopenharmony_ci usb_set_interface(dev->udev, 0, dev->alt); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci new_pkt_size = dev->max_pkt_size != dev->alt_max_pkt_size[dev->alt]; 19862306a36Sopenharmony_ci dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return new_pkt_size; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int stk1160_start_streaming(struct stk1160 *dev) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci bool new_pkt_size; 20662306a36Sopenharmony_ci int rc = 0; 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Check device presence */ 21062306a36Sopenharmony_ci if (!dev->udev) 21162306a36Sopenharmony_ci return -ENODEV; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->v4l_lock)) 21462306a36Sopenharmony_ci return -ERESTARTSYS; 21562306a36Sopenharmony_ci /* 21662306a36Sopenharmony_ci * For some reason it is mandatory to set alternate *first* 21762306a36Sopenharmony_ci * and only *then* initialize isoc urbs. 21862306a36Sopenharmony_ci * Someone please explain me why ;) 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci new_pkt_size = stk1160_set_alternate(dev); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * We (re)allocate isoc urbs if: 22462306a36Sopenharmony_ci * there is no allocated isoc urbs, OR 22562306a36Sopenharmony_ci * a new dev->max_pkt_size is detected 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci if (!dev->isoc_ctl.num_bufs || new_pkt_size) { 22862306a36Sopenharmony_ci rc = stk1160_alloc_isoc(dev); 22962306a36Sopenharmony_ci if (rc < 0) 23062306a36Sopenharmony_ci goto out_stop_hw; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* submit urbs and enables IRQ */ 23462306a36Sopenharmony_ci for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { 23562306a36Sopenharmony_ci struct stk1160_urb *stk_urb = &dev->isoc_ctl.urb_ctl[i]; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci dma_sync_sgtable_for_device(stk1160_get_dmadev(dev), stk_urb->sgt, 23862306a36Sopenharmony_ci DMA_FROM_DEVICE); 23962306a36Sopenharmony_ci rc = usb_submit_urb(dev->isoc_ctl.urb_ctl[i].urb, GFP_KERNEL); 24062306a36Sopenharmony_ci if (rc) { 24162306a36Sopenharmony_ci stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); 24262306a36Sopenharmony_ci goto out_uninit; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Start saa711x */ 24762306a36Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dev->sequence = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Start stk1160 */ 25262306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL, 0xb3); 25362306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci stk1160_dbg("streaming started\n"); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci mutex_unlock(&dev->v4l_lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciout_uninit: 26262306a36Sopenharmony_ci stk1160_uninit_isoc(dev); 26362306a36Sopenharmony_ciout_stop_hw: 26462306a36Sopenharmony_ci usb_set_interface(dev->udev, 0, 0); 26562306a36Sopenharmony_ci stk1160_clear_queue(dev, VB2_BUF_STATE_QUEUED); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_unlock(&dev->v4l_lock); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return rc; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* Must be called with v4l_lock hold */ 27362306a36Sopenharmony_cistatic void stk1160_stop_hw(struct stk1160 *dev) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci /* If the device is not physically present, there is nothing to do */ 27662306a36Sopenharmony_ci if (!dev->udev) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* set alternate 0 */ 28062306a36Sopenharmony_ci dev->alt = 0; 28162306a36Sopenharmony_ci stk1160_dbg("setting alternate %d\n", dev->alt); 28262306a36Sopenharmony_ci usb_set_interface(dev->udev, 0, 0); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Stop stk1160 */ 28562306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL, 0x00); 28662306a36Sopenharmony_ci stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Stop saa711x */ 28962306a36Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int stk1160_stop_streaming(struct stk1160 *dev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->v4l_lock)) 29562306a36Sopenharmony_ci return -ERESTARTSYS; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * Once URBs are cancelled, the URB complete handler 29962306a36Sopenharmony_ci * won't be running. This is required to safely release the 30062306a36Sopenharmony_ci * current buffer (dev->isoc_ctl.buf). 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci stk1160_cancel_isoc(dev); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * It is possible to keep buffers around using a module parameter. 30662306a36Sopenharmony_ci * This is intended to avoid memory fragmentation. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci if (!keep_buffers) 30962306a36Sopenharmony_ci stk1160_free_isoc(dev); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci stk1160_stop_hw(dev); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci stk1160_clear_queue(dev, VB2_BUF_STATE_ERROR); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci stk1160_dbg("streaming stopped\n"); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mutex_unlock(&dev->v4l_lock); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic const struct v4l2_file_operations stk1160_fops = { 32362306a36Sopenharmony_ci .owner = THIS_MODULE, 32462306a36Sopenharmony_ci .open = v4l2_fh_open, 32562306a36Sopenharmony_ci .release = vb2_fop_release, 32662306a36Sopenharmony_ci .read = vb2_fop_read, 32762306a36Sopenharmony_ci .poll = vb2_fop_poll, 32862306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 32962306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * vidioc ioctls 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, 33662306a36Sopenharmony_ci void *priv, struct v4l2_capability *cap) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci strscpy(cap->driver, "stk1160", sizeof(cap->driver)); 34162306a36Sopenharmony_ci strscpy(cap->card, "stk1160", sizeof(cap->card)); 34262306a36Sopenharmony_ci usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 34762306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci if (f->index != 0) 35062306a36Sopenharmony_ci return -EINVAL; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci f->pixelformat = format[f->index].fourcc; 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 35762306a36Sopenharmony_ci struct v4l2_format *f) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci f->fmt.pix.width = dev->width; 36262306a36Sopenharmony_ci f->fmt.pix.height = dev->height; 36362306a36Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 36462306a36Sopenharmony_ci f->fmt.pix.pixelformat = dev->fmt->fourcc; 36562306a36Sopenharmony_ci f->fmt.pix.bytesperline = dev->width * 2; 36662306a36Sopenharmony_ci f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; 36762306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int stk1160_try_fmt(struct stk1160 *dev, struct v4l2_format *f, 37362306a36Sopenharmony_ci struct stk1160_decimate_ctrl *ctrl) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci unsigned int width, height; 37662306a36Sopenharmony_ci unsigned int base_width, base_height; 37762306a36Sopenharmony_ci unsigned int col_n, row_n; 37862306a36Sopenharmony_ci enum stk1160_decimate_mode col_mode, row_mode; 37962306a36Sopenharmony_ci bool col_en, row_en; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci base_width = 720; 38262306a36Sopenharmony_ci base_height = (dev->norm & V4L2_STD_525_60) ? 480 : 576; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Minimum width and height is 5% the frame size */ 38562306a36Sopenharmony_ci width = clamp_t(unsigned int, f->fmt.pix.width, 38662306a36Sopenharmony_ci base_width / 20, base_width); 38762306a36Sopenharmony_ci height = clamp_t(unsigned int, f->fmt.pix.height, 38862306a36Sopenharmony_ci base_height / 20, base_height); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Let's set default no decimation values */ 39162306a36Sopenharmony_ci col_n = 0; 39262306a36Sopenharmony_ci row_n = 0; 39362306a36Sopenharmony_ci col_en = false; 39462306a36Sopenharmony_ci row_en = false; 39562306a36Sopenharmony_ci f->fmt.pix.width = base_width; 39662306a36Sopenharmony_ci f->fmt.pix.height = base_height; 39762306a36Sopenharmony_ci row_mode = STK1160_DECIMATE_LESS_THAN_HALF; 39862306a36Sopenharmony_ci col_mode = STK1160_DECIMATE_LESS_THAN_HALF; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (width < base_width && width > base_width / 2) { 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * The device will send count units for each 40362306a36Sopenharmony_ci * unit skipped. This means count unit is: 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * n = width / (frame width - width) 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * And the width is: 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * width = (n / n + 1) * frame width 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci col_n = div_round_integer(width, base_width - width); 41262306a36Sopenharmony_ci if (col_n > 0 && col_n <= 255) { 41362306a36Sopenharmony_ci col_en = true; 41462306a36Sopenharmony_ci col_mode = STK1160_DECIMATE_LESS_THAN_HALF; 41562306a36Sopenharmony_ci f->fmt.pix.width = (base_width * col_n) / (col_n + 1); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci } else if (width <= base_width / 2) { 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * The device will skip count units for each 42262306a36Sopenharmony_ci * unit sent. This means count is: 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * n = (frame width / width) - 1 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * And the width is: 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * width = frame width / (n + 1) 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci col_n = div_round_integer(base_width, width) - 1; 43162306a36Sopenharmony_ci if (col_n > 0 && col_n <= 255) { 43262306a36Sopenharmony_ci col_en = true; 43362306a36Sopenharmony_ci col_mode = STK1160_DECIMATE_MORE_THAN_HALF; 43462306a36Sopenharmony_ci f->fmt.pix.width = base_width / (col_n + 1); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (height < base_height && height > base_height / 2) { 43962306a36Sopenharmony_ci row_n = div_round_integer(height, base_height - height); 44062306a36Sopenharmony_ci if (row_n > 0 && row_n <= 255) { 44162306a36Sopenharmony_ci row_en = true; 44262306a36Sopenharmony_ci row_mode = STK1160_DECIMATE_LESS_THAN_HALF; 44362306a36Sopenharmony_ci f->fmt.pix.height = (base_height * row_n) / (row_n + 1); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci } else if (height <= base_height / 2) { 44762306a36Sopenharmony_ci row_n = div_round_integer(base_height, height) - 1; 44862306a36Sopenharmony_ci if (row_n > 0 && row_n <= 255) { 44962306a36Sopenharmony_ci row_en = true; 45062306a36Sopenharmony_ci row_mode = STK1160_DECIMATE_MORE_THAN_HALF; 45162306a36Sopenharmony_ci f->fmt.pix.height = base_height / (row_n + 1); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci f->fmt.pix.pixelformat = dev->fmt->fourcc; 45662306a36Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 45762306a36Sopenharmony_ci f->fmt.pix.bytesperline = f->fmt.pix.width * 2; 45862306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 45962306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (ctrl) { 46262306a36Sopenharmony_ci ctrl->col_en = col_en; 46362306a36Sopenharmony_ci ctrl->col_n = col_n; 46462306a36Sopenharmony_ci ctrl->col_mode = col_mode; 46562306a36Sopenharmony_ci ctrl->row_en = row_en; 46662306a36Sopenharmony_ci ctrl->row_n = row_n; 46762306a36Sopenharmony_ci ctrl->row_mode = row_mode; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci stk1160_dbg("width %d, height %d\n", 47162306a36Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height); 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 47662306a36Sopenharmony_ci struct v4l2_format *f) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return stk1160_try_fmt(dev, f, NULL); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 48462306a36Sopenharmony_ci struct v4l2_format *f) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 48762306a36Sopenharmony_ci struct vb2_queue *q = &dev->vb_vidq; 48862306a36Sopenharmony_ci struct stk1160_decimate_ctrl ctrl; 48962306a36Sopenharmony_ci int rc; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (vb2_is_busy(q)) 49262306a36Sopenharmony_ci return -EBUSY; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci rc = stk1160_try_fmt(dev, f, &ctrl); 49562306a36Sopenharmony_ci if (rc < 0) 49662306a36Sopenharmony_ci return rc; 49762306a36Sopenharmony_ci dev->width = f->fmt.pix.width; 49862306a36Sopenharmony_ci dev->height = f->fmt.pix.height; 49962306a36Sopenharmony_ci stk1160_set_fmt(dev, &ctrl); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 50762306a36Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci *norm = dev->norm; 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 52262306a36Sopenharmony_ci struct vb2_queue *q = &dev->vb_vidq; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (dev->norm == norm) 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (vb2_is_busy(q)) 52862306a36Sopenharmony_ci return -EBUSY; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Check device presence */ 53162306a36Sopenharmony_ci if (!dev->udev) 53262306a36Sopenharmony_ci return -ENODEV; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* We need to set this now, before we call stk1160_set_std */ 53562306a36Sopenharmony_ci dev->width = 720; 53662306a36Sopenharmony_ci dev->height = (norm & V4L2_STD_525_60) ? 480 : 576; 53762306a36Sopenharmony_ci dev->norm = norm; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci stk1160_set_std(dev); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Calling with NULL disables frame decimation */ 54262306a36Sopenharmony_ci stk1160_set_fmt(dev, NULL); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, 54562306a36Sopenharmony_ci dev->norm); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv, 55262306a36Sopenharmony_ci struct v4l2_input *i) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (i->index > STK1160_MAX_INPUT) 55762306a36Sopenharmony_ci return -EINVAL; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* S-Video special handling */ 56062306a36Sopenharmony_ci if (i->index == STK1160_SVIDEO_INPUT) 56162306a36Sopenharmony_ci sprintf(i->name, "S-Video"); 56262306a36Sopenharmony_ci else 56362306a36Sopenharmony_ci sprintf(i->name, "Composite%d", i->index); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 56662306a36Sopenharmony_ci i->std = dev->vdev.tvnorms; 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 57362306a36Sopenharmony_ci *i = dev->ctl_input; 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (i > STK1160_MAX_INPUT) 58262306a36Sopenharmony_ci return -EINVAL; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci dev->ctl_input = i; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci stk1160_select_input(dev); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 59262306a36Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *priv, 59362306a36Sopenharmony_ci struct v4l2_dbg_register *reg) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 59662306a36Sopenharmony_ci int rc; 59762306a36Sopenharmony_ci u8 val; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Match host */ 60062306a36Sopenharmony_ci rc = stk1160_read_reg(dev, reg->reg, &val); 60162306a36Sopenharmony_ci reg->val = val; 60262306a36Sopenharmony_ci reg->size = 1; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return rc; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *priv, 60862306a36Sopenharmony_ci const struct v4l2_dbg_register *reg) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct stk1160 *dev = video_drvdata(file); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Match host */ 61362306a36Sopenharmony_ci return stk1160_write_reg(dev, reg->reg, reg->val); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci#endif 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops stk1160_ioctl_ops = { 61862306a36Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 61962306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 62062306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 62162306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 62262306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 62362306a36Sopenharmony_ci .vidioc_querystd = vidioc_querystd, 62462306a36Sopenharmony_ci .vidioc_g_std = vidioc_g_std, 62562306a36Sopenharmony_ci .vidioc_s_std = vidioc_s_std, 62662306a36Sopenharmony_ci .vidioc_enum_input = vidioc_enum_input, 62762306a36Sopenharmony_ci .vidioc_g_input = vidioc_g_input, 62862306a36Sopenharmony_ci .vidioc_s_input = vidioc_s_input, 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* vb2 takes care of these */ 63162306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 63262306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 63362306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 63462306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 63562306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 63662306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 63762306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 64062306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 64162306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 64462306a36Sopenharmony_ci .vidioc_g_register = vidioc_g_register, 64562306a36Sopenharmony_ci .vidioc_s_register = vidioc_s_register, 64662306a36Sopenharmony_ci#endif 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/********************************************************************/ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* 65262306a36Sopenharmony_ci * Videobuf2 operations 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 65562306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 65662306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vq); 65962306a36Sopenharmony_ci unsigned long size; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci size = dev->width * dev->height * 2; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* 66462306a36Sopenharmony_ci * Here we can change the number of buffers being requested. 66562306a36Sopenharmony_ci * So, we set a minimum and a maximum like this: 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci *nbuffers = clamp_t(unsigned int, *nbuffers, 66862306a36Sopenharmony_ci STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (*nplanes) 67162306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* This means a packed colorformat */ 67462306a36Sopenharmony_ci *nplanes = 1; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci sizes[0] = size; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci stk1160_dbg("%s: buffer count %d, each %ld bytes\n", 67962306a36Sopenharmony_ci __func__, *nbuffers, size); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci unsigned long flags; 68762306a36Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue); 68862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 68962306a36Sopenharmony_ci struct stk1160_buffer *buf = 69062306a36Sopenharmony_ci container_of(vbuf, struct stk1160_buffer, vb); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci spin_lock_irqsave(&dev->buf_lock, flags); 69362306a36Sopenharmony_ci if (!dev->udev) { 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * If the device is disconnected return the buffer to userspace 69662306a36Sopenharmony_ci * directly. The next QBUF call will fail with -ENODEV. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 69962306a36Sopenharmony_ci } else { 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci buf->mem = vb2_plane_vaddr(vb, 0); 70262306a36Sopenharmony_ci buf->length = vb2_plane_size(vb, 0); 70362306a36Sopenharmony_ci buf->bytesused = 0; 70462306a36Sopenharmony_ci buf->pos = 0; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * If buffer length is less from expected then we return 70862306a36Sopenharmony_ci * the buffer to userspace directly. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci if (buf->length < dev->width * dev->height * 2) 71162306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 71262306a36Sopenharmony_ci else 71362306a36Sopenharmony_ci list_add_tail(&buf->list, &dev->avail_bufs); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->buf_lock, flags); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vq); 72262306a36Sopenharmony_ci return stk1160_start_streaming(dev); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/* abort streaming and wait for last buffer */ 72662306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct stk1160 *dev = vb2_get_drv_priv(vq); 72962306a36Sopenharmony_ci stk1160_stop_streaming(dev); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic const struct vb2_ops stk1160_video_qops = { 73362306a36Sopenharmony_ci .queue_setup = queue_setup, 73462306a36Sopenharmony_ci .buf_queue = buffer_queue, 73562306a36Sopenharmony_ci .start_streaming = start_streaming, 73662306a36Sopenharmony_ci .stop_streaming = stop_streaming, 73762306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 73862306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 73962306a36Sopenharmony_ci}; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic const struct video_device v4l_template = { 74262306a36Sopenharmony_ci .name = "stk1160", 74362306a36Sopenharmony_ci .tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50, 74462306a36Sopenharmony_ci .fops = &stk1160_fops, 74562306a36Sopenharmony_ci .ioctl_ops = &stk1160_ioctl_ops, 74662306a36Sopenharmony_ci .release = video_device_release_empty, 74762306a36Sopenharmony_ci}; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/********************************************************************/ 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* Must be called with both v4l_lock and vb_queue_lock hold */ 75262306a36Sopenharmony_civoid stk1160_clear_queue(struct stk1160 *dev, enum vb2_buffer_state vb2_state) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct stk1160_buffer *buf; 75562306a36Sopenharmony_ci unsigned long flags; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* Release all active buffers */ 75862306a36Sopenharmony_ci spin_lock_irqsave(&dev->buf_lock, flags); 75962306a36Sopenharmony_ci while (!list_empty(&dev->avail_bufs)) { 76062306a36Sopenharmony_ci buf = list_first_entry(&dev->avail_bufs, 76162306a36Sopenharmony_ci struct stk1160_buffer, list); 76262306a36Sopenharmony_ci list_del(&buf->list); 76362306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, vb2_state); 76462306a36Sopenharmony_ci stk1160_dbg("buffer [%p/%d] aborted\n", 76562306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* It's important to release the current buffer */ 76962306a36Sopenharmony_ci if (dev->isoc_ctl.buf) { 77062306a36Sopenharmony_ci buf = dev->isoc_ctl.buf; 77162306a36Sopenharmony_ci dev->isoc_ctl.buf = NULL; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, vb2_state); 77462306a36Sopenharmony_ci stk1160_dbg("buffer [%p/%d] aborted\n", 77562306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index); 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->buf_lock, flags); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ciint stk1160_vb2_setup(struct stk1160 *dev) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci int rc; 78362306a36Sopenharmony_ci struct vb2_queue *q; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci q = &dev->vb_vidq; 78662306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 78762306a36Sopenharmony_ci q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 78862306a36Sopenharmony_ci q->drv_priv = dev; 78962306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct stk1160_buffer); 79062306a36Sopenharmony_ci q->ops = &stk1160_video_qops; 79162306a36Sopenharmony_ci q->mem_ops = &vb2_vmalloc_memops; 79262306a36Sopenharmony_ci q->lock = &dev->vb_queue_lock; 79362306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci rc = vb2_queue_init(q); 79662306a36Sopenharmony_ci if (rc < 0) 79762306a36Sopenharmony_ci return rc; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* initialize video dma queue */ 80062306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->avail_bufs); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ciint stk1160_video_register(struct stk1160 *dev) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci int rc; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Initialize video_device with a template structure */ 81062306a36Sopenharmony_ci dev->vdev = v4l_template; 81162306a36Sopenharmony_ci dev->vdev.queue = &dev->vb_vidq; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* 81462306a36Sopenharmony_ci * Provide mutexes for v4l2 core and for videobuf2 queue. 81562306a36Sopenharmony_ci * It will be used to protect *only* v4l2 ioctls. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci dev->vdev.lock = &dev->v4l_lock; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* This will be used to set video_device parent */ 82062306a36Sopenharmony_ci dev->vdev.v4l2_dev = &dev->v4l2_dev; 82162306a36Sopenharmony_ci dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 82262306a36Sopenharmony_ci V4L2_CAP_READWRITE; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* NTSC is default */ 82562306a36Sopenharmony_ci dev->norm = V4L2_STD_NTSC_M; 82662306a36Sopenharmony_ci dev->width = 720; 82762306a36Sopenharmony_ci dev->height = 480; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* set default format */ 83062306a36Sopenharmony_ci dev->fmt = &format[0]; 83162306a36Sopenharmony_ci stk1160_set_std(dev); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, 83462306a36Sopenharmony_ci dev->norm); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci video_set_drvdata(&dev->vdev, dev); 83762306a36Sopenharmony_ci rc = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); 83862306a36Sopenharmony_ci if (rc < 0) { 83962306a36Sopenharmony_ci stk1160_err("video_register_device failed (%d)\n", rc); 84062306a36Sopenharmony_ci return rc; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", 84462306a36Sopenharmony_ci video_device_node_name(&dev->vdev)); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 848