Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/media/usb/cx231xx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 50 kB image not shown  

Quelle  cx231xx-video.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
   cx231xx-video.c - driver for Conexant Cx23100/101/102
     USB video capture devices

   Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
Based on cx23885 driver
Based on cx88 driver

 */


#include "cx231xx.h"
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bitmap.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/slab.h>

#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/drv-intf/msp3400.h>
#include <media/tuner.h>

#include <media/dvb_frontend.h>

#include "cx231xx-vbi.h"

#define CX231XX_VERSION "0.0.3"

#define DRIVER_AUTHOR   "Srinivasa Deevi "
#define DRIVER_DESC     "Conexant cx231xx based USB video device driver"

#define cx231xx_videodbg(fmt, arg...) do {\
 if (video_debug) \
  printk(KERN_INFO "%s %s :"fmt, \
    dev->name, __func__ , ##arg); } while (0)

static unsigned int isoc_debug;
module_param(isoc_debug, int, 0644);
MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");

#define cx231xx_isocdbg(fmt, arg...) \
do {\
 if (isoc_debug) { \
  printk(KERN_INFO "%s %s :"fmt, \
    dev->name, __func__ , ##arg); \
 } \
  } while (0)

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(CX231XX_VERSION);

static unsigned int card[]     = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
static unsigned int vbi_nr[]   = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };

module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
module_param_array(radio_nr, int, NULL, 0444);

MODULE_PARM_DESC(card, "card type");
MODULE_PARM_DESC(video_nr, "video device numbers");
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
MODULE_PARM_DESC(radio_nr, "radio device numbers");

static unsigned int video_debug;
module_param(video_debug, int, 0644);
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");

/* supported video standards */
static struct cx231xx_fmt format[] = {
 {
  .fourcc = V4L2_PIX_FMT_YUYV,
  .depth = 16,
  .reg = 0,
  },
};


static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
{
#ifdef CONFIG_MEDIA_CONTROLLER
 struct media_device *mdev = dev->media_dev;
 struct media_entity  *entity, *decoder = NULL, *source;
 struct media_link *link, *found_link = NULL;
 int ret, active_links = 0;

 if (!mdev)
  return 0;

 /*
 * This will find the tuner that is connected into the decoder.
 * Technically, this is not 100% correct, as the device may be
 * using an analog input instead of the tuner. However, as we can't
 * do DVB streaming while the DMA engine is being used for V4L2,
 * this should be enough for the actual needs.
 */

 media_device_for_each_entity(entity, mdev) {
  if (entity->function == MEDIA_ENT_F_ATV_DECODER) {
   decoder = entity;
   break;
  }
 }
 if (!decoder)
  return 0;

 list_for_each_entry(link, &decoder->links, list) {
  if (link->sink->entity == decoder) {
   found_link = link;
   if (link->flags & MEDIA_LNK_FL_ENABLED)
    active_links++;
   break;
  }
 }

 if (active_links == 1 || !found_link)
  return 0;

 source = found_link->source->entity;
 list_for_each_entry(link, &source->links, list) {
  struct media_entity *sink;
  int flags = 0;

  sink = link->sink->entity;

  if (sink == entity)
   flags = MEDIA_LNK_FL_ENABLED;

  ret = media_entity_setup_link(link, flags);
  if (ret) {
   dev_err(dev->dev,
    "Couldn't change link %s->%s to %s. Error %d\n",
    source->name, sink->name,
    flags ? "enabled" : "disabled",
    ret);
   return ret;
  } else
   dev_dbg(dev->dev,
    "link %s->%s was %s\n",
    source->name, sink->name,
    flags ? "ENABLED" : "disabled");
 }
#endif
 return 0;
}

/* ------------------------------------------------------------------
Video buffer and parser functions
   ------------------------------------------------------------------*/


/*
 * Announces that a buffer were filled and request the next
 */

static inline void buffer_filled(struct cx231xx *dev,
     struct cx231xx_dmaqueue *dma_q,
     struct cx231xx_buffer *buf)
{
 /* Advice that buffer was filled */
 cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.vb2_buf.index);
 buf->vb.sequence = dma_q->sequence++;
 buf->vb.field = V4L2_FIELD_INTERLACED;
 buf->vb.vb2_buf.timestamp = ktime_get_ns();
 vb2_set_plane_payload(&buf->vb.vb2_buf, 0, dev->size);

 if (dev->USE_ISO)
  dev->video_mode.isoc_ctl.buf = NULL;
 else
  dev->video_mode.bulk_ctl.buf = NULL;

 list_del(&buf->list);
 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}

static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
 char *errmsg = "Unknown";

 switch (status) {
 case -ENOENT:
  errmsg = "unlinked synchronously";
  break;
 case -ECONNRESET:
  errmsg = "unlinked asynchronously";
  break;
 case -ENOSR:
  errmsg = "Buffer error (overrun)";
  break;
 case -EPIPE:
  errmsg = "Stalled (device not responding)";
  break;
 case -EOVERFLOW:
  errmsg = "Babble (bad cable?)";
  break;
 case -EPROTO:
  errmsg = "Bit-stuff error (bad cable?)";
  break;
 case -EILSEQ:
  errmsg = "CRC/Timeout (could be anything)";
  break;
 case -ETIME:
  errmsg = "Device does not respond";
  break;
 }
 if (packet < 0) {
  cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
 } else {
  cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
    packet, status, errmsg);
 }
}

/*
 * generic routine to get the next available buffer
 */

static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
    struct cx231xx_buffer **buf)
{
 struct cx231xx_video_mode *vmode =
     container_of(dma_q, struct cx231xx_video_mode, vidq);
 struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);

 char *outp;

 if (list_empty(&dma_q->active)) {
  cx231xx_isocdbg("No active queue to serve\n");
  if (dev->USE_ISO)
   dev->video_mode.isoc_ctl.buf = NULL;
  else
   dev->video_mode.bulk_ctl.buf = NULL;
  *buf = NULL;
  return;
 }

 /* Get the next buffer */
 *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list);

 /* Cleans up buffer - Useful for testing for frame/URB loss */
 outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
 memset(outp, 0, dev->size);

 if (dev->USE_ISO)
  dev->video_mode.isoc_ctl.buf = *buf;
 else
  dev->video_mode.bulk_ctl.buf = *buf;

 return;
}

/*
 * Controls the isoc copy of each urb packet
 */

static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
{
 struct cx231xx_dmaqueue *dma_q = urb->context;
 int i;
 unsigned char *p_buffer;
 u32 bytes_parsed = 0, buffer_size = 0;
 u8 sav_eav = 0;

 if (!dev)
  return 0;

 if (dev->state & DEV_DISCONNECTED)
  return 0;

 if (urb->status < 0) {
  print_err_status(dev, -1, urb->status);
  if (urb->status == -ENOENT)
   return 0;
 }

 for (i = 0; i < urb->number_of_packets; i++) {
  int status = urb->iso_frame_desc[i].status;

  if (status < 0) {
   print_err_status(dev, i, status);
   if (urb->iso_frame_desc[i].status != -EPROTO)
    continue;
  }

  if (urb->iso_frame_desc[i].actual_length <= 0) {
   /* cx231xx_isocdbg("packet %d is empty",i); - spammy */
   continue;
  }
  if (urb->iso_frame_desc[i].actual_length >
      dev->video_mode.max_pkt_size) {
   cx231xx_isocdbg("packet bigger than packet size");
   continue;
  }

  /*  get buffer pointer and length */
  p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
  buffer_size = urb->iso_frame_desc[i].actual_length;
  bytes_parsed = 0;

  if (dma_q->is_partial_line) {
   /* Handle the case of a partial line */
   sav_eav = dma_q->last_sav;
  } else {
   /* Check for a SAV/EAV overlapping
the buffer boundary */

   sav_eav =
       cx231xx_find_boundary_SAV_EAV(p_buffer,
         dma_q->partial_buf,
         &bytes_parsed);
  }

  sav_eav &= 0xF0;
  /* Get the first line if we have some portion of an SAV/EAV from
   the last buffer or a partial line  */

  if (sav_eav) {
   bytes_parsed += cx231xx_get_video_line(dev, dma_q,
    sav_eav, /* SAV/EAV */
    p_buffer + bytes_parsed, /* p_buffer */
    buffer_size - bytes_parsed);/* buf size */
  }

  /* Now parse data that is completely in this buffer */
  /* dma_q->is_partial_line = 0;  */

  while (bytes_parsed < buffer_size) {
   u32 bytes_used = 0;

   sav_eav = cx231xx_find_next_SAV_EAV(
    p_buffer + bytes_parsed, /* p_buffer */
    buffer_size - bytes_parsed, /* buf size */
    &bytes_used);/* bytes used to get SAV/EAV */

   bytes_parsed += bytes_used;

   sav_eav &= 0xF0;
   if (sav_eav && (bytes_parsed < buffer_size)) {
    bytes_parsed += cx231xx_get_video_line(dev,
     dma_q, sav_eav, /* SAV/EAV */
     p_buffer + bytes_parsed,/* p_buffer */
     buffer_size - bytes_parsed);/*buf size*/
   }
  }

  /* Save the last four bytes of the buffer so we can check the
   buffer boundary condition next time */

  memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
  bytes_parsed = 0;

 }
 return 1;
}

static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
{
 struct cx231xx_dmaqueue *dma_q = urb->context;
 unsigned char *p_buffer;
 u32 bytes_parsed = 0, buffer_size = 0;
 u8 sav_eav = 0;

 if (!dev)
  return 0;

 if (dev->state & DEV_DISCONNECTED)
  return 0;

 if (urb->status < 0) {
  print_err_status(dev, -1, urb->status);
  if (urb->status == -ENOENT)
   return 0;
 }

 if (1) {

  /*  get buffer pointer and length */
  p_buffer = urb->transfer_buffer;
  buffer_size = urb->actual_length;
  bytes_parsed = 0;

  if (dma_q->is_partial_line) {
   /* Handle the case of a partial line */
   sav_eav = dma_q->last_sav;
  } else {
   /* Check for a SAV/EAV overlapping
the buffer boundary */

   sav_eav =
       cx231xx_find_boundary_SAV_EAV(p_buffer,
         dma_q->partial_buf,
         &bytes_parsed);
  }

  sav_eav &= 0xF0;
  /* Get the first line if we have some portion of an SAV/EAV from
   the last buffer or a partial line  */

  if (sav_eav) {
   bytes_parsed += cx231xx_get_video_line(dev, dma_q,
    sav_eav, /* SAV/EAV */
    p_buffer + bytes_parsed, /* p_buffer */
    buffer_size - bytes_parsed);/* buf size */
  }

  /* Now parse data that is completely in this buffer */
  /* dma_q->is_partial_line = 0;  */

  while (bytes_parsed < buffer_size) {
   u32 bytes_used = 0;

   sav_eav = cx231xx_find_next_SAV_EAV(
    p_buffer + bytes_parsed, /* p_buffer */
    buffer_size - bytes_parsed, /* buf size */
    &bytes_used);/* bytes used to get SAV/EAV */

   bytes_parsed += bytes_used;

   sav_eav &= 0xF0;
   if (sav_eav && (bytes_parsed < buffer_size)) {
    bytes_parsed += cx231xx_get_video_line(dev,
     dma_q, sav_eav, /* SAV/EAV */
     p_buffer + bytes_parsed,/* p_buffer */
     buffer_size - bytes_parsed);/*buf size*/
   }
  }

  /* Save the last four bytes of the buffer so we can check the
   buffer boundary condition next time */

  memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
  bytes_parsed = 0;

 }
 return 1;
}


u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
     u32 *p_bytes_used)
{
 u32 bytes_used;
 u8 boundary_bytes[8];
 u8 sav_eav = 0;

 *p_bytes_used = 0;

 /* Create an array of the last 4 bytes of the last buffer and the first
   4 bytes of the current buffer. */


 memcpy(boundary_bytes, partial_buf, 4);
 memcpy(boundary_bytes + 4, p_buffer, 4);

 /* Check for the SAV/EAV in the boundary buffer */
 sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
         &bytes_used);

 if (sav_eav) {
  /* found a boundary SAV/EAV.  Updates the bytes used to reflect
   only those used in the new buffer */

  *p_bytes_used = bytes_used - 4;
 }

 return sav_eav;
}

u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
{
 u32 i;
 u8 sav_eav = 0;

 /*
 * Don't search if the buffer size is less than 4.  It causes a page
 * fault since buffer_size - 4 evaluates to a large number in that
 * case.
 */

 if (buffer_size < 4) {
  *p_bytes_used = buffer_size;
  return 0;
 }

 for (i = 0; i < (buffer_size - 3); i++) {

  if ((p_buffer[i] == 0xFF) &&
      (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {

   *p_bytes_used = i + 4;
   sav_eav = p_buffer[i + 3];
   return sav_eav;
  }
 }

 *p_bytes_used = buffer_size;
 return 0;
}

u32 cx231xx_get_video_line(struct cx231xx *dev,
      struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
      u8 *p_buffer, u32 buffer_size)
{
 u32 bytes_copied = 0;
 int current_field = -1;

 switch (sav_eav) {
 case SAV_ACTIVE_VIDEO_FIELD1:
  /* looking for skipped line which occurred in PAL 720x480 mode.
   In this case, there will be no active data contained
   between the SAV and EAV */

  if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
      (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
      ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
       (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
       (p_buffer[3] == EAV_VBLANK_FIELD1) ||
       (p_buffer[3] == EAV_VBLANK_FIELD2)))
   return bytes_copied;
  current_field = 1;
  break;

 case SAV_ACTIVE_VIDEO_FIELD2:
  /* looking for skipped line which occurred in PAL 720x480 mode.
   In this case, there will be no active data contained between
   the SAV and EAV */

  if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
      (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
      ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
       (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
       (p_buffer[3] == EAV_VBLANK_FIELD1)       ||
       (p_buffer[3] == EAV_VBLANK_FIELD2)))
   return bytes_copied;
  current_field = 2;
  break;
 }

 dma_q->last_sav = sav_eav;

 bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
            buffer_size, current_field);

 return bytes_copied;
}

u32 cx231xx_copy_video_line(struct cx231xx *dev,
       struct cx231xx_dmaqueue *dma_q, u8 *p_line,
       u32 length, int field_number)
{
 u32 bytes_to_copy;
 struct cx231xx_buffer *buf;
 u32 _line_size = dev->width * 2;

 if (dma_q->current_field != field_number)
  cx231xx_reset_video_buffer(dev, dma_q);

 /* get the buffer pointer */
 if (dev->USE_ISO)
  buf = dev->video_mode.isoc_ctl.buf;
 else
  buf = dev->video_mode.bulk_ctl.buf;

 /* Remember the field number for next time */
 dma_q->current_field = field_number;

 bytes_to_copy = dma_q->bytes_left_in_line;
 if (bytes_to_copy > length)
  bytes_to_copy = length;

 if (dma_q->lines_completed >= dma_q->lines_per_field) {
  dma_q->bytes_left_in_line -= bytes_to_copy;
  dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
       0 : 1;
  return 0;
 }

 dma_q->is_partial_line = 1;

 /* If we don't have a buffer, just return the number of bytes we would
   have copied if we had a buffer. */

 if (!buf) {
  dma_q->bytes_left_in_line -= bytes_to_copy;
  dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
      ? 0 : 1;
  return bytes_to_copy;
 }

 /* copy the data to video buffer */
 cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);

 dma_q->pos += bytes_to_copy;
 dma_q->bytes_left_in_line -= bytes_to_copy;

 if (dma_q->bytes_left_in_line == 0) {
  dma_q->bytes_left_in_line = _line_size;
  dma_q->lines_completed++;
  dma_q->is_partial_line = 0;

  if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
   buffer_filled(dev, dma_q, buf);

   dma_q->pos = 0;
   buf = NULL;
   dma_q->lines_completed = 0;
  }
 }

 return bytes_to_copy;
}

void cx231xx_reset_video_buffer(struct cx231xx *dev,
    struct cx231xx_dmaqueue *dma_q)
{
 struct cx231xx_buffer *buf;

 /* handle the switch from field 1 to field 2 */
 if (dma_q->current_field == 1) {
  if (dma_q->lines_completed >= dma_q->lines_per_field)
   dma_q->field1_done = 1;
  else
   dma_q->field1_done = 0;
 }

 if (dev->USE_ISO)
  buf = dev->video_mode.isoc_ctl.buf;
 else
  buf = dev->video_mode.bulk_ctl.buf;

 if (buf == NULL) {
  /* first try to get the buffer */
  get_next_buf(dma_q, &buf);

  dma_q->pos = 0;
  dma_q->field1_done = 0;
  dma_q->current_field = -1;
 }

 /* reset the counters */
 dma_q->bytes_left_in_line = dev->width << 1;
 dma_q->lines_completed = 0;
}

int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
      u8 *p_buffer, u32 bytes_to_copy)
{
 u8 *p_out_buffer = NULL;
 u32 current_line_bytes_copied = 0;
 struct cx231xx_buffer *buf;
 u32 _line_size = dev->width << 1;
 void *startwrite;
 int offset, lencopy;

 if (dev->USE_ISO)
  buf = dev->video_mode.isoc_ctl.buf;
 else
  buf = dev->video_mode.bulk_ctl.buf;

 if (buf == NULL)
  return -1;

 p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);

 current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;

 /* Offset field 2 one line from the top of the buffer */
 offset = (dma_q->current_field == 1) ? 0 : _line_size;

 /* Offset for field 2 */
 startwrite = p_out_buffer + offset;

 /* lines already completed in the current field */
 startwrite += (dma_q->lines_completed * _line_size * 2);

 /* bytes already completed in the current line */
 startwrite += current_line_bytes_copied;

 lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
    bytes_to_copy : dma_q->bytes_left_in_line;

 if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + dev->size))
  return 0;

 /* The below copies the UYVY data straight into video buffer */
 cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);

 return 0;
}

void cx231xx_swab(u16 *from, u16 *to, u16 len)
{
 u16 i;

 if (len <= 0)
  return;

 for (i = 0; i < len / 2; i++)
  to[i] = (from[i] << 8) | (from[i] >> 8);
}

u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
{
 u8 buffer_complete = 0;

 /* Dual field stream */
 buffer_complete = ((dma_q->current_field == 2) &&
      (dma_q->lines_completed >= dma_q->lines_per_field) &&
       dma_q->field1_done);

 return buffer_complete;
}

/* ------------------------------------------------------------------
Videobuf operations
   ------------------------------------------------------------------*/


static int queue_setup(struct vb2_queue *vq,
         unsigned int *nbuffers, unsigned int *nplanes,
         unsigned int sizes[], struct device *alloc_devs[])
{
 struct cx231xx *dev = vb2_get_drv_priv(vq);
 unsigned int q_num_bufs = vb2_get_num_buffers(vq);

 dev->size = (dev->width * dev->height * dev->format->depth + 7) >> 3;

 if (q_num_bufs + *nbuffers < CX231XX_MIN_BUF)
  *nbuffers = CX231XX_MIN_BUF - q_num_bufs;

 if (*nplanes)
  return sizes[0] < dev->size ? -EINVAL : 0;
 *nplanes = 1;
 sizes[0] = dev->size;

 return 0;
}

static void buffer_queue(struct vb2_buffer *vb)
{
 struct cx231xx_buffer *buf =
     container_of(vb, struct cx231xx_buffer, vb.vb2_buf);
 struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
 struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
 unsigned long flags;

 spin_lock_irqsave(&dev->video_mode.slock, flags);
 list_add_tail(&buf->list, &vidq->active);
 spin_unlock_irqrestore(&dev->video_mode.slock, flags);
}

static void return_all_buffers(struct cx231xx *dev,
          enum vb2_buffer_state state)
{
 struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
 struct cx231xx_buffer *buf, *node;
 unsigned long flags;

 spin_lock_irqsave(&dev->video_mode.slock, flags);
 if (dev->USE_ISO)
  dev->video_mode.isoc_ctl.buf = NULL;
 else
  dev->video_mode.bulk_ctl.buf = NULL;
 list_for_each_entry_safe(buf, node, &vidq->active, list) {
  list_del(&buf->list);
  vb2_buffer_done(&buf->vb.vb2_buf, state);
 }
 spin_unlock_irqrestore(&dev->video_mode.slock, flags);
}

static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
 struct cx231xx *dev = vb2_get_drv_priv(vq);
 struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
 int ret = 0;

 vidq->sequence = 0;
 dev->mode_tv = 0;

 cx231xx_enable_analog_tuner(dev);
 if (dev->USE_ISO)
  ret = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
     CX231XX_NUM_BUFS,
     dev->video_mode.max_pkt_size,
     cx231xx_isoc_copy);
 else
  ret = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
     CX231XX_NUM_BUFS,
     dev->video_mode.max_pkt_size,
     cx231xx_bulk_copy);
 if (ret)
  return_all_buffers(dev, VB2_BUF_STATE_QUEUED);
 call_all(dev, video, s_stream, 1);
 return ret;
}

static void stop_streaming(struct vb2_queue *vq)
{
 struct cx231xx *dev = vb2_get_drv_priv(vq);

 call_all(dev, video, s_stream, 0);
 return_all_buffers(dev, VB2_BUF_STATE_ERROR);
}

static const struct vb2_ops cx231xx_video_qops = {
 .queue_setup  = queue_setup,
 .buf_queue  = buffer_queue,
 .start_streaming = start_streaming,
 .stop_streaming  = stop_streaming,
};

/*********************  v4l2 interface  **************************************/

void video_mux(struct cx231xx *dev, int index)
{
 dev->video_input = index;
 dev->ctl_ainput = INPUT(index)->amux;

 cx231xx_set_video_input_mux(dev, index);

 cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);

 cx231xx_set_audio_input(dev, dev->ctl_ainput);

 dev_dbg(dev->dev, "video_mux : %d\n", index);

 /* do mode control overrides if required */
 cx231xx_do_mode_ctrl_overrides(dev);
}

/* ------------------------------------------------------------------
IOCTL vidioc handling
   ------------------------------------------------------------------*/


static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
    struct v4l2_format *f)
{
 struct cx231xx *dev = video_drvdata(file);

 f->fmt.pix.width = dev->width;
 f->fmt.pix.height = dev->height;
 f->fmt.pix.pixelformat = dev->format->fourcc;
 f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
 f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
 f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;

 f->fmt.pix.field = V4L2_FIELD_INTERLACED;

 return 0;
}

static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
{
 unsigned int i;

 for (i = 0; i < ARRAY_SIZE(format); i++)
  if (format[i].fourcc == fourcc)
   return &format[i];

 return NULL;
}

static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
      struct v4l2_format *f)
{
 struct cx231xx *dev = video_drvdata(file);
 unsigned int width = f->fmt.pix.width;
 unsigned int height = f->fmt.pix.height;
 unsigned int maxw = norm_maxw(dev);
 unsigned int maxh = norm_maxh(dev);
 struct cx231xx_fmt *fmt;

 fmt = format_by_fourcc(f->fmt.pix.pixelformat);
 if (!fmt) {
  cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
     f->fmt.pix.pixelformat);
  return -EINVAL;
 }

 /* width must even because of the YUYV format
   height must be even because of interlacing */

 v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);

 f->fmt.pix.width = width;
 f->fmt.pix.height = height;
 f->fmt.pix.pixelformat = fmt->fourcc;
 f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3;
 f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
 f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
 f->fmt.pix.field = V4L2_FIELD_INTERLACED;

 return 0;
}

static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
    struct v4l2_format *f)
{
 struct cx231xx *dev = video_drvdata(file);
 struct v4l2_subdev_format format = {
  .which = V4L2_SUBDEV_FORMAT_ACTIVE,
 };
 int rc;

 rc = vidioc_try_fmt_vid_cap(file, priv, f);
 if (rc)
  return rc;

 if (vb2_is_busy(&dev->vidq)) {
  dev_err(dev->dev, "%s: queue busy\n", __func__);
  return -EBUSY;
 }

 /* set new image size */
 dev->width = f->fmt.pix.width;
 dev->height = f->fmt.pix.height;
 dev->format = format_by_fourcc(f->fmt.pix.pixelformat);

 v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
 call_all(dev, pad, set_fmt, NULL, &format);
 v4l2_fill_pix_format(&f->fmt.pix, &format.format);

 return rc;
}

static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
 struct cx231xx *dev = video_drvdata(file);

 *id = dev->norm;
 return 0;
}

static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
{
 struct cx231xx *dev = video_drvdata(file);
 struct v4l2_subdev_format format = {
  .which = V4L2_SUBDEV_FORMAT_ACTIVE,
 };

 if (dev->norm == norm)
  return 0;

 if (vb2_is_busy(&dev->vidq))
  return -EBUSY;

 dev->norm = norm;

 /* Adjusts width/height, if needed */
 dev->width = 720;
 dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480;

 call_all(dev, video, s_std, dev->norm);

 /* We need to reset basic properties in the decoder related to
   resolution (since a standard change effects things like the number
   of lines in VACT, etc) */

 format.format.code = MEDIA_BUS_FMT_FIXED;
 format.format.width = dev->width;
 format.format.height = dev->height;
 call_all(dev, pad, set_fmt, NULL, &format);

 /* do mode control overrides */
 cx231xx_do_mode_ctrl_overrides(dev);

 return 0;
}

static const char *iname[] = {
 [CX231XX_VMUX_COMPOSITE1] = "Composite1",
 [CX231XX_VMUX_SVIDEO]     = "S-Video",
 [CX231XX_VMUX_TELEVISION] = "Television",
 [CX231XX_VMUX_CABLE]      = "Cable TV",
 [CX231XX_VMUX_DVB]        = "DVB",
};

void cx231xx_v4l2_create_entities(struct cx231xx *dev)
{
#if defined(CONFIG_MEDIA_CONTROLLER)
 int ret, i;

 /* Create entities for each input connector */
 for (i = 0; i < MAX_CX231XX_INPUT; i++) {
  struct media_entity *ent = &dev->input_ent[i];

  if (!INPUT(i)->type)
   break;

  ent->name = iname[INPUT(i)->type];
  ent->flags = MEDIA_ENT_FL_CONNECTOR;
  dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;

  switch (INPUT(i)->type) {
  case CX231XX_VMUX_COMPOSITE1:
   ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
   break;
  case CX231XX_VMUX_SVIDEO:
   ent->function = MEDIA_ENT_F_CONN_SVIDEO;
   break;
  case CX231XX_VMUX_TELEVISION:
  case CX231XX_VMUX_CABLE:
  case CX231XX_VMUX_DVB:
   /* The DVB core will handle it */
   if (dev->tuner_type == TUNER_ABSENT)
    continue;
   fallthrough;
  default/* just to shut up a gcc warning */
   ent->function = MEDIA_ENT_F_CONN_RF;
   break;
  }

  ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
  if (ret < 0)
   pr_err("failed to initialize input pad[%d]!\n", i);

  ret = media_device_register_entity(dev->media_dev, ent);
  if (ret < 0)
   pr_err("failed to register input entity %d!\n", i);
 }
#endif
}

int cx231xx_enum_input(struct file *file, void *priv,
        struct v4l2_input *i)
{
 struct cx231xx *dev = video_drvdata(file);
 u32 gen_stat;
 unsigned int n;
 int ret;

 n = i->index;
 if (n >= MAX_CX231XX_INPUT)
  return -EINVAL;
 if (0 == INPUT(n)->type)
  return -EINVAL;

 i->index = n;
 i->type = V4L2_INPUT_TYPE_CAMERA;

 strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name));

 if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
     (CX231XX_VMUX_CABLE == INPUT(n)->type))
  i->type = V4L2_INPUT_TYPE_TUNER;

 i->std = dev->vdev.tvnorms;

 /* If they are asking about the active input, read signal status */
 if (n == dev->video_input) {
  ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
         GEN_STAT, 2, &gen_stat, 4);
  if (ret > 0) {
   if ((gen_stat & FLD_VPRES) == 0x00)
    i->status |= V4L2_IN_ST_NO_SIGNAL;
   if ((gen_stat & FLD_HLOCK) == 0x00)
    i->status |= V4L2_IN_ST_NO_H_LOCK;
  }
 }

 return 0;
}

int cx231xx_g_input(struct file *file, void *priv, unsigned int *i)
{
 struct cx231xx *dev = video_drvdata(file);

 *i = dev->video_input;

 return 0;
}

int cx231xx_s_input(struct file *file, void *priv, unsigned int i)
{
 struct cx231xx *dev = video_drvdata(file);

 dev->mode_tv = 0;

 if (i >= MAX_CX231XX_INPUT)
  return -EINVAL;
 if (0 == INPUT(i)->type)
  return -EINVAL;

 video_mux(dev, i);

 if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
     INPUT(i)->type == CX231XX_VMUX_CABLE) {
  /* There's a tuner, so reset the standard and put it on the
   last known frequency (since it was probably powered down
   until now */

  call_all(dev, video, s_std, dev->norm);
 }

 return 0;
}

int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
{
 struct cx231xx *dev = video_drvdata(file);

 if (0 != t->index)
  return -EINVAL;

 strscpy(t->name, "Tuner"sizeof(t->name));

 t->type = V4L2_TUNER_ANALOG_TV;
 t->capability = V4L2_TUNER_CAP_NORM;
 t->rangehigh = 0xffffffffUL;
 t->signal = 0xffff; /* LOCKED */
 call_all(dev, tuner, g_tuner, t);

 return 0;
}

int cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
{
 if (0 != t->index)
  return -EINVAL;
 return 0;
}

int cx231xx_g_frequency(struct file *file, void *priv,
         struct v4l2_frequency *f)
{
 struct cx231xx *dev = video_drvdata(file);

 if (f->tuner)
  return -EINVAL;

 f->frequency = dev->ctl_freq;

 return 0;
}

int cx231xx_s_frequency(struct file *file, void *priv,
         const struct v4l2_frequency *f)
{
 struct cx231xx *dev = video_drvdata(file);
 struct v4l2_frequency new_freq = *f;
 int rc, need_if_freq = 0;
 u32 if_frequency = 5400000;

 dev_dbg(dev->dev,
  "Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
  f->frequency, f->type);

 if (0 != f->tuner)
  return -EINVAL;

 /* set pre channel change settings in DIF first */
 rc = cx231xx_tuner_pre_channel_change(dev);

 switch (dev->model) { /* i2c device tuners */
 case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
 case CX231XX_BOARD_HAUPPAUGE_935C:
 case CX231XX_BOARD_HAUPPAUGE_955Q:
 case CX231XX_BOARD_HAUPPAUGE_975:
 case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
  if (dev->cx231xx_set_analog_freq)
   dev->cx231xx_set_analog_freq(dev, f->frequency);
  dev->ctl_freq = f->frequency;
  need_if_freq = 1;
  break;
 default:
  call_all(dev, tuner, s_frequency, f);
  call_all(dev, tuner, g_frequency, &new_freq);
  dev->ctl_freq = new_freq.frequency;
  break;
 }

 pr_debug("%s() %u : %u\n", __func__, f->frequency, dev->ctl_freq);

 /* set post channel change settings in DIF first */
 rc = cx231xx_tuner_post_channel_change(dev);

 if (need_if_freq || dev->tuner_type == TUNER_NXP_TDA18271) {
  if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
   if_frequency = 5400000;  /*5.4MHz */
  else if (dev->norm & V4L2_STD_B)
   if_frequency = 6000000;  /*6.0MHz */
  else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
   if_frequency = 6900000;  /*6.9MHz */
  else if (dev->norm & V4L2_STD_GH)
   if_frequency = 7100000;  /*7.1MHz */
  else if (dev->norm & V4L2_STD_PAL_I)
   if_frequency = 7250000;  /*7.25MHz */
  else if (dev->norm & V4L2_STD_SECAM_L)
   if_frequency = 6900000;  /*6.9MHz */
  else if (dev->norm & V4L2_STD_SECAM_LC)
   if_frequency = 1250000;  /*1.25MHz */

  dev_dbg(dev->dev,
   "if_frequency is set to %d\n", if_frequency);
  cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);

  update_HH_register_after_set_DIF(dev);
 }

 dev_dbg(dev->dev, "Set New FREQUENCY to %d\n", f->frequency);

 return rc;
}

#ifdef CONFIG_VIDEO_ADV_DEBUG

int cx231xx_g_chip_info(struct file *file, void *fh,
   struct v4l2_dbg_chip_info *chip)
{
 switch (chip->match.addr) {
 case 0: /* Cx231xx - internal registers */
  return 0;
 case 1: /* AFE - read byte */
  strscpy(chip->name, "AFE (byte)"sizeof(chip->name));
  return 0;
 case 2: /* Video Block - read byte */
  strscpy(chip->name, "Video (byte)"sizeof(chip->name));
  return 0;
 case 3: /* I2S block - read byte */
  strscpy(chip->name, "I2S (byte)"sizeof(chip->name));
  return 0;
 case 4: /* AFE - read dword */
  strscpy(chip->name, "AFE (dword)"sizeof(chip->name));
  return 0;
 case 5: /* Video Block - read dword */
  strscpy(chip->name, "Video (dword)"sizeof(chip->name));
  return 0;
 case 6: /* I2S Block - read dword */
  strscpy(chip->name, "I2S (dword)"sizeof(chip->name));
  return 0;
 }
 return -EINVAL;
}

int cx231xx_g_register(struct file *file, void *priv,
        struct v4l2_dbg_register *reg)
{
 struct cx231xx *dev = video_drvdata(file);
 int ret;
 u8 value[4] = { 0, 0, 0, 0 };
 u32 data = 0;

 switch (reg->match.addr) {
 case 0: /* Cx231xx - internal registers */
  ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
    (u16)reg->reg, value, 4);
  reg->val = value[0] | value[1] << 8 |
   value[2] << 16 | (u32)value[3] << 24;
  reg->size = 4;
  break;
 case 1: /* AFE - read byte */
  ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
    (u16)reg->reg, 2, &data, 1);
  reg->val = data;
  reg->size = 1;
  break;
 case 2: /* Video Block - read byte */
  ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
    (u16)reg->reg, 2, &data, 1);
  reg->val = data;
  reg->size = 1;
  break;
 case 3: /* I2S block - read byte */
  ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
    (u16)reg->reg, 1, &data, 1);
  reg->val = data;
  reg->size = 1;
  break;
 case 4: /* AFE - read dword */
  ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
    (u16)reg->reg, 2, &data, 4);
  reg->val = data;
  reg->size = 4;
  break;
 case 5: /* Video Block - read dword */
  ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
    (u16)reg->reg, 2, &data, 4);
  reg->val = data;
  reg->size = 4;
  break;
 case 6: /* I2S Block - read dword */
  ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
    (u16)reg->reg, 1, &data, 4);
  reg->val = data;
  reg->size = 4;
  break;
 default:
  return -EINVAL;
 }
 return ret < 0 ? ret : 0;
}

int cx231xx_s_register(struct file *file, void *priv,
        const struct v4l2_dbg_register *reg)
{
 struct cx231xx *dev = video_drvdata(file);
 int ret;
 u8 data[4] = { 0, 0, 0, 0 };

 switch (reg->match.addr) {
 case 0: /* cx231xx internal registers */
  data[0] = (u8) reg->val;
  data[1] = (u8) (reg->val >> 8);
  data[2] = (u8) (reg->val >> 16);
  data[3] = (u8) (reg->val >> 24);
  ret = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
    (u16)reg->reg, data, 4);
  break;
 case 1: /* AFE - write byte */
  ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
    (u16)reg->reg, 2, reg->val, 1);
  break;
 case 2: /* Video Block - write byte */
  ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
    (u16)reg->reg, 2, reg->val, 1);
  break;
 case 3: /* I2S block - write byte */
  ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
    (u16)reg->reg, 1, reg->val, 1);
  break;
 case 4: /* AFE - write dword */
  ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
    (u16)reg->reg, 2, reg->val, 4);
  break;
 case 5: /* Video Block - write dword */
  ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
    (u16)reg->reg, 2, reg->val, 4);
  break;
 case 6: /* I2S block - write dword */
  ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
    (u16)reg->reg, 1, reg->val, 4);
  break;
 default:
  return -EINVAL;
 }
 return ret < 0 ? ret : 0;
}
#endif

static int vidioc_g_pixelaspect(struct file *file, void *priv,
    int type, struct v4l2_fract *f)
{
 struct cx231xx *dev = video_drvdata(file);
 bool is_50hz = dev->norm & V4L2_STD_625_50;

 if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
  return -EINVAL;

 f->numerator = is_50hz ? 54 : 11;
 f->denominator = is_50hz ? 59 : 10;

 return 0;
}

static int vidioc_g_selection(struct file *file, void *priv,
         struct v4l2_selection *s)
{
 struct cx231xx *dev = video_drvdata(file);

 if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
  return -EINVAL;

 switch (s->target) {
 case V4L2_SEL_TGT_CROP_BOUNDS:
 case V4L2_SEL_TGT_CROP_DEFAULT:
  s->r.left = 0;
  s->r.top = 0;
  s->r.width = dev->width;
  s->r.height = dev->height;
  break;
 default:
  return -EINVAL;
 }
 return 0;
}

int cx231xx_querycap(struct file *file, void *priv,
      struct v4l2_capability *cap)
{
 struct cx231xx *dev = video_drvdata(file);

 strscpy(cap->driver, "cx231xx"sizeof(cap->driver));
 strscpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
 usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 cap->capabilities = V4L2_CAP_READWRITE |
  V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE |
  V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
 if (video_is_registered(&dev->radio_dev))
  cap->capabilities |= V4L2_CAP_RADIO;

 switch (dev->model) {
 case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
 case CX231XX_BOARD_HAUPPAUGE_935C:
 case CX231XX_BOARD_HAUPPAUGE_955Q:
 case CX231XX_BOARD_HAUPPAUGE_975:
 case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
  cap->capabilities |= V4L2_CAP_TUNER;
  break;
 default:
  if (dev->tuner_type != TUNER_ABSENT)
   cap->capabilities |= V4L2_CAP_TUNER;
  break;
 }
 return 0;
}

static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
       struct v4l2_fmtdesc *f)
{
 if (unlikely(f->index >= ARRAY_SIZE(format)))
  return -EINVAL;

 f->pixelformat = format[f->index].fourcc;

 return 0;
}

/* RAW VBI ioctls */

static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
    struct v4l2_format *f)
{
 struct cx231xx *dev = video_drvdata(file);

 f->fmt.vbi.sampling_rate = 6750000 * 4;
 f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
 f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
 f->fmt.vbi.offset = 0;
 f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
     PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
 f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
     PAL_VBI_LINES : NTSC_VBI_LINES;
 f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
     PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
 f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
 memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));

 return 0;

}

static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
      struct v4l2_format *f)
{
 struct cx231xx *dev = video_drvdata(file);

 f->fmt.vbi.sampling_rate = 6750000 * 4;
 f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
 f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
 f->fmt.vbi.offset = 0;
 f->fmt.vbi.flags = 0;
 f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
     PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
 f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
     PAL_VBI_LINES : NTSC_VBI_LINES;
 f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
     PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
 f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
 memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));

 return 0;

}

static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
      struct v4l2_format *f)
{
 return vidioc_try_fmt_vbi_cap(file, priv, f);
}

/* ----------------------------------------------------------- */
/* RADIO ESPECIFIC IOCTLS                                      */
/* ----------------------------------------------------------- */

static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
{
 struct cx231xx *dev = video_drvdata(file);

 if (t->index)
  return -EINVAL;

 strscpy(t->name, "Radio"sizeof(t->name));

 call_all(dev, tuner, g_tuner, t);

 return 0;
}
static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
{
 struct cx231xx *dev = video_drvdata(file);

 if (t->index)
  return -EINVAL;

 call_all(dev, tuner, s_tuner, t);

 return 0;
}

/*
 * cx231xx_v4l2_open()
 * inits the device and starts isoc transfer
 */

static int cx231xx_v4l2_open(struct file *filp)
{
 struct video_device *vdev = video_devdata(filp);
 struct cx231xx *dev = video_drvdata(filp);
 int ret;

 if (mutex_lock_interruptible(&dev->lock))
  return -ERESTARTSYS;

 ret = v4l2_fh_open(filp);
 if (ret) {
  mutex_unlock(&dev->lock);
  return ret;
 }

 if (dev->users++ == 0) {
  /* Power up in Analog TV mode */
  if (dev->board.external_av)
   cx231xx_set_power_mode(dev,
     POLARIS_AVMODE_ENXTERNAL_AV);
  else
   cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);

  /* set video alternate setting */
  cx231xx_set_video_alternate(dev);

  /* Needed, since GPIO might have disabled power of
   some i2c device */

  cx231xx_config_i2c(dev);

  /* device needs to be initialized before isoc transfer */
  dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
 }

 if (vdev->vfl_type == VFL_TYPE_RADIO) {
  cx231xx_videodbg("video_open: setting radio device\n");

  /* cx231xx_start_radio(dev); */

  call_all(dev, tuner, s_radio);
 }
 if (vdev->vfl_type == VFL_TYPE_VBI) {
  /* Set the required alternate setting  VBI interface works in
   Bulk mode only */

  cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
 }
 mutex_unlock(&dev->lock);
 return 0;
}

/*
 * cx231xx_realease_resources()
 * unregisters the v4l2,i2c and usb devices
 * called when the device gets disconnected or at module unload
*/

void cx231xx_release_analog_resources(struct cx231xx *dev)
{

 /*FIXME: I2C IR should be disconnected */

 if (video_is_registered(&dev->radio_dev))
  video_unregister_device(&dev->radio_dev);
 if (video_is_registered(&dev->vbi_dev)) {
  dev_info(dev->dev, "V4L2 device %s deregistered\n",
   video_device_node_name(&dev->vbi_dev));
  video_unregister_device(&dev->vbi_dev);
 }
 if (video_is_registered(&dev->vdev)) {
  dev_info(dev->dev, "V4L2 device %s deregistered\n",
   video_device_node_name(&dev->vdev));

  if (dev->board.has_417)
   cx231xx_417_unregister(dev);

  video_unregister_device(&dev->vdev);
 }
 v4l2_ctrl_handler_free(&dev->ctrl_handler);
 v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
}

/*
 * cx231xx_close()
 * stops streaming and deallocates all resources allocated by the v4l2
 * calls and ioctls
 */

static int cx231xx_close(struct file *filp)
{
 struct cx231xx *dev = video_drvdata(filp);
 struct video_device *vdev = video_devdata(filp);

 _vb2_fop_release(filp, NULL);

 if (--dev->users == 0) {
  /* Save some power by putting tuner to sleep */
  call_all(dev, tuner, standby);

  /* do this before setting alternate! */
  if (dev->USE_ISO)
   cx231xx_uninit_isoc(dev);
  else
   cx231xx_uninit_bulk(dev);
  cx231xx_set_mode(dev, CX231XX_SUSPEND);
 }

 /*
 * To workaround error number=-71 on EP0 for VideoGrabber,
 *  need exclude following.
 * FIXME: It is probably safe to remove most of these, as we're
 * now avoiding the alternate setting for INDEX_VANC
 */

 if (!dev->board.no_alt_vanc && vdev->vfl_type == VFL_TYPE_VBI) {
  /* do this before setting alternate! */
  cx231xx_uninit_vbi_isoc(dev);

  /* set alternate 0 */
  if (!dev->vbi_or_sliced_cc_mode)
   cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
  else
   cx231xx_set_alt_setting(dev, INDEX_HANC, 0);

  wake_up_interruptible_nr(&dev->open, 1);
  return 0;
 }

 if (dev->users == 0) {
  /* set alternate 0 */
  cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
 }

 wake_up_interruptible(&dev->open);
 return 0;
}

static int cx231xx_v4l2_close(struct file *filp)
{
 struct cx231xx *dev = video_drvdata(filp);
 int rc;

 mutex_lock(&dev->lock);
 rc = cx231xx_close(filp);
 mutex_unlock(&dev->lock);
 return rc;
}

static const struct v4l2_file_operations cx231xx_v4l_fops = {
 .owner   = THIS_MODULE,
 .open    = cx231xx_v4l2_open,
 .release = cx231xx_v4l2_close,
 .read    = vb2_fop_read,
 .poll    = vb2_fop_poll,
 .mmap    = vb2_fop_mmap,
 .unlocked_ioctl   = video_ioctl2,
};

static const struct v4l2_ioctl_ops video_ioctl_ops = {
 .vidioc_querycap               = cx231xx_querycap,
 .vidioc_enum_fmt_vid_cap       = vidioc_enum_fmt_vid_cap,
 .vidioc_g_fmt_vid_cap          = vidioc_g_fmt_vid_cap,
 .vidioc_try_fmt_vid_cap        = vidioc_try_fmt_vid_cap,
 .vidioc_s_fmt_vid_cap          = vidioc_s_fmt_vid_cap,
 .vidioc_g_fmt_vbi_cap          = vidioc_g_fmt_vbi_cap,
 .vidioc_try_fmt_vbi_cap        = vidioc_try_fmt_vbi_cap,
 .vidioc_s_fmt_vbi_cap          = vidioc_s_fmt_vbi_cap,
 .vidioc_g_pixelaspect          = vidioc_g_pixelaspect,
 .vidioc_g_selection            = vidioc_g_selection,
 .vidioc_reqbufs                = vb2_ioctl_reqbufs,
 .vidioc_querybuf               = vb2_ioctl_querybuf,
 .vidioc_qbuf                   = vb2_ioctl_qbuf,
 .vidioc_dqbuf                  = vb2_ioctl_dqbuf,
 .vidioc_s_std                  = vidioc_s_std,
 .vidioc_g_std                  = vidioc_g_std,
 .vidioc_enum_input             = cx231xx_enum_input,
 .vidioc_g_input                = cx231xx_g_input,
 .vidioc_s_input                = cx231xx_s_input,
 .vidioc_streamon               = vb2_ioctl_streamon,
 .vidioc_streamoff              = vb2_ioctl_streamoff,
 .vidioc_g_tuner                = cx231xx_g_tuner,
 .vidioc_s_tuner                = cx231xx_s_tuner,
 .vidioc_g_frequency            = cx231xx_g_frequency,
 .vidioc_s_frequency            = cx231xx_s_frequency,
#ifdef CONFIG_VIDEO_ADV_DEBUG
 .vidioc_g_chip_info            = cx231xx_g_chip_info,
 .vidioc_g_register             = cx231xx_g_register,
 .vidioc_s_register             = cx231xx_s_register,
#endif
 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};

static struct video_device cx231xx_vbi_template;

static const struct video_device cx231xx_video_template = {
 .fops         = &cx231xx_v4l_fops,
 .release      = video_device_release_empty,
 .ioctl_ops    = &video_ioctl_ops,
 .tvnorms      = V4L2_STD_ALL,
};

static const struct v4l2_file_operations radio_fops = {
 .owner   = THIS_MODULE,
 .open   = cx231xx_v4l2_open,
 .release = cx231xx_v4l2_close,
 .poll = v4l2_ctrl_poll,
 .unlocked_ioctl = video_ioctl2,
};

static const struct v4l2_ioctl_ops radio_ioctl_ops = {
 .vidioc_querycap    = cx231xx_querycap,
 .vidioc_g_tuner     = radio_g_tuner,
 .vidioc_s_tuner     = radio_s_tuner,
 .vidioc_g_frequency = cx231xx_g_frequency,
 .vidioc_s_frequency = cx231xx_s_frequency,
#ifdef CONFIG_VIDEO_ADV_DEBUG
 .vidioc_g_chip_info = cx231xx_g_chip_info,
 .vidioc_g_register  = cx231xx_g_register,
 .vidioc_s_register  = cx231xx_s_register,
#endif
 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};

static struct video_device cx231xx_radio_template = {
 .name      = "cx231xx-radio",
 .fops      = &radio_fops,
 .ioctl_ops = &radio_ioctl_ops,
};

/******************************** usb interface ******************************/

static void cx231xx_vdev_init(struct cx231xx *dev,
  struct video_device *vfd,
  const struct video_device *template,
  const char *type_name)
{
 *vfd = *template;
 vfd->v4l2_dev = &dev->v4l2_dev;
 vfd->release = video_device_release_empty;
 vfd->lock = &dev->lock;

 snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);

 video_set_drvdata(vfd, dev);
 if (dev->tuner_type == TUNER_ABSENT) {
  switch (dev->model) {
  case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
  case CX231XX_BOARD_HAUPPAUGE_935C:
  case CX231XX_BOARD_HAUPPAUGE_955Q:
  case CX231XX_BOARD_HAUPPAUGE_975:
  case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
   break;
  default:
   v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY);
   v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY);
   v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
   v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
   break;
  }
 }
}

int cx231xx_register_analog_devices(struct cx231xx *dev)
{
 struct vb2_queue *q;
 int ret;

 dev_info(dev->dev, "v4l2 driver version %s\n", CX231XX_VERSION);

 /* set default norm */
 dev->norm = V4L2_STD_PAL;
 dev->width = norm_maxw(dev);
 dev->height = norm_maxh(dev);
 dev->interlaced = 0;

 /* Analog specific initialization */
 dev->format = &format[0];

 /* Set the initial input */
 video_mux(dev, dev->video_input);

 call_all(dev, video, s_std, dev->norm);

 v4l2_ctrl_handler_init(&dev->ctrl_handler, 10);
 v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5);

 if (dev->sd_cx25840) {
  v4l2_ctrl_add_handler(&dev->ctrl_handler,
    dev->sd_cx25840->ctrl_handler, NULL, true);
  v4l2_ctrl_add_handler(&dev->radio_ctrl_handler,
    dev->sd_cx25840->ctrl_handler,
    v4l2_ctrl_radio_filter, true);
 }

 if (dev->ctrl_handler.error)
  return dev->ctrl_handler.error;
 if (dev->radio_ctrl_handler.error)
  return dev->radio_ctrl_handler.error;

 /* enable vbi capturing */
 /* write code here...  */

 /* allocate and fill video video_device struct */
 cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video");
#if defined(CONFIG_MEDIA_CONTROLLER)
 dev->video_pad.flags = MEDIA_PAD_FL_SINK;
 ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad);
 if (ret < 0)
  dev_err(dev->dev, "failed to initialize video media entity!\n");
#endif
 dev->vdev.ctrl_handler = &dev->ctrl_handler;

 q = &dev->vidq;
 q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ;
 q->drv_priv = dev;
 q->buf_struct_size = sizeof(struct cx231xx_buffer);
 q->ops = &cx231xx_video_qops;
 q->mem_ops = &vb2_vmalloc_memops;
 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
 q->min_queued_buffers = 1;
 q->lock = &dev->lock;
 ret = vb2_queue_init(q);
 if (ret)
  return ret;
 dev->vdev.queue = q;
 dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
    V4L2_CAP_VIDEO_CAPTURE;

 switch (dev->model) { /* i2c device tuners */
 case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
 case CX231XX_BOARD_HAUPPAUGE_935C:
 case CX231XX_BOARD_HAUPPAUGE_955Q:
 case CX231XX_BOARD_HAUPPAUGE_975:
 case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
  dev->vdev.device_caps |= V4L2_CAP_TUNER;
  break;
 default:
  if (dev->tuner_type != TUNER_ABSENT)
   dev->vdev.device_caps |= V4L2_CAP_TUNER;
  break;
 }

 /* register v4l2 video video_device */
 ret = video_register_device(&dev->vdev, VFL_TYPE_VIDEO,
        video_nr[dev->devno]);
 if (ret) {
  dev_err(dev->dev,
   "unable to register video device (error=%i).\n",
   ret);
  return ret;
 }

 dev_info(dev->dev, "Registered video device %s [v4l2]\n",
  video_device_node_name(&dev->vdev));

 /* Initialize VBI template */
 cx231xx_vbi_template = cx231xx_video_template;
 strscpy(cx231xx_vbi_template.name, "cx231xx-vbi",
  sizeof(cx231xx_vbi_template.name));

 /* Allocate and fill vbi video_device struct */
 cx231xx_vdev_init(dev, &dev->vbi_dev, &cx231xx_vbi_template, "vbi");

#if defined(CONFIG_MEDIA_CONTROLLER)
 dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
 ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad);
 if (ret < 0)
  dev_err(dev->dev, "failed to initialize vbi media entity!\n");
#endif
 dev->vbi_dev.ctrl_handler = &dev->ctrl_handler;

 q = &dev->vbiq;
 q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
 q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ;
 q->drv_priv = dev;
 q->buf_struct_size = sizeof(struct cx231xx_buffer);
 q->ops = &cx231xx_vbi_qops;
 q->mem_ops = &vb2_vmalloc_memops;
 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
 q->min_queued_buffers = 1;
 q->lock = &dev->lock;
 ret = vb2_queue_init(q);
 if (ret)
  return ret;
 dev->vbi_dev.queue = q;
 dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
       V4L2_CAP_VBI_CAPTURE;
 switch (dev->model) { /* i2c device tuners */
 case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
 case CX231XX_BOARD_HAUPPAUGE_935C:
 case CX231XX_BOARD_HAUPPAUGE_955Q:
 case CX231XX_BOARD_HAUPPAUGE_975:
 case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
  dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
  break;
 default:
  if (dev->tuner_type != TUNER_ABSENT)
   dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
 }

 /* register v4l2 vbi video_device */
 ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
        vbi_nr[dev->devno]);
 if (ret < 0) {
  dev_err(dev->dev, "unable to register vbi device\n");
  return ret;
 }

 dev_info(dev->dev, "Registered VBI device %s\n",
  video_device_node_name(&dev->vbi_dev));

 if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
  cx231xx_vdev_init(dev, &dev->radio_dev,
    &cx231xx_radio_template, "radio");
  dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler;
  dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
  ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
         radio_nr[dev->devno]);
  if (ret < 0) {
   dev_err(dev->dev,
    "can't register radio device\n");
   return ret;
  }
  dev_info(dev->dev, "Registered radio device as %s\n",
   video_device_node_name(&dev->radio_dev));
 }

 return 0;
}

Messung V0.5
C=93 H=98 G=95

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.