2009-03-19 05:10:04 +08:00
|
|
|
/*
|
2009-03-20 06:00:35 +08:00
|
|
|
* Hauppauge HD PVR USB driver - video 4 linux 2 interface
|
2009-03-19 05:10:04 +08:00
|
|
|
*
|
|
|
|
* Copyright (C) 2008 Janne Grunau (j@jannau.net)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation, version 2.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
2013-03-22 02:46:18 +08:00
|
|
|
#include <linux/kconfig.h>
|
2009-03-19 05:10:04 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
|
|
|
|
#include <linux/videodev2.h>
|
2013-03-21 02:29:52 +08:00
|
|
|
#include <linux/v4l2-dv-timings.h>
|
2009-03-19 05:10:04 +08:00
|
|
|
#include <media/v4l2-dev.h>
|
|
|
|
#include <media/v4l2-common.h>
|
|
|
|
#include <media/v4l2-ioctl.h>
|
2013-02-12 19:26:59 +08:00
|
|
|
#include <media/v4l2-event.h>
|
2009-03-19 05:10:04 +08:00
|
|
|
#include "hdpvr.h"
|
|
|
|
|
2010-07-26 19:50:32 +08:00
|
|
|
#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
#define print_buffer_status() { \
|
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
|
|
|
|
"%s:%d buffer stat: %d free, %d proc\n", \
|
|
|
|
__func__, __LINE__, \
|
|
|
|
list_size(&dev->free_buff_list), \
|
|
|
|
list_size(&dev->rec_buff_list)); }
|
2009-03-27 07:29:39 +08:00
|
|
|
|
2013-03-21 02:29:52 +08:00
|
|
|
static const struct v4l2_dv_timings hdpvr_dv_timings[] = {
|
|
|
|
V4L2_DV_BT_CEA_720X480I59_94,
|
|
|
|
V4L2_DV_BT_CEA_720X576I50,
|
|
|
|
V4L2_DV_BT_CEA_720X480P59_94,
|
|
|
|
V4L2_DV_BT_CEA_720X576P50,
|
|
|
|
V4L2_DV_BT_CEA_1280X720P50,
|
|
|
|
V4L2_DV_BT_CEA_1280X720P60,
|
|
|
|
V4L2_DV_BT_CEA_1920X1080I50,
|
|
|
|
V4L2_DV_BT_CEA_1920X1080I60,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Use 480i59 as the default timings */
|
|
|
|
#define HDPVR_DEF_DV_TIMINGS_IDX (0)
|
|
|
|
|
|
|
|
struct hdpvr_fh {
|
|
|
|
struct v4l2_fh fh;
|
|
|
|
bool legacy_mode;
|
|
|
|
};
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
static uint list_size(struct list_head *list)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
uint count = 0;
|
|
|
|
|
|
|
|
list_for_each(tmp, list) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================*/
|
|
|
|
/* urb callback */
|
|
|
|
static void hdpvr_read_bulk_callback(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
|
|
|
|
struct hdpvr_device *dev = buf->dev;
|
|
|
|
|
|
|
|
/* marking buffer as received and wake waiting */
|
|
|
|
buf->status = BUFSTAT_READY;
|
|
|
|
wake_up_interruptible(&dev->wait_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================*/
|
|
|
|
/* bufffer bits */
|
|
|
|
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
|
|
int hdpvr_cancel_queue(struct hdpvr_device *dev)
|
|
|
|
{
|
|
|
|
struct hdpvr_buffer *buf;
|
|
|
|
|
|
|
|
list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
|
|
|
|
usb_kill_urb(buf->urb);
|
|
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdpvr_free_queue(struct list_head *q)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
struct list_head *p;
|
|
|
|
struct hdpvr_buffer *buf;
|
|
|
|
struct urb *urb;
|
|
|
|
|
|
|
|
for (p = q->next; p != q;) {
|
|
|
|
buf = list_entry(p, struct hdpvr_buffer, buff_list);
|
|
|
|
|
|
|
|
urb = buf->urb;
|
2010-04-12 19:17:25 +08:00
|
|
|
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
|
|
|
urb->transfer_buffer, urb->transfer_dma);
|
2009-03-19 05:10:04 +08:00
|
|
|
usb_free_urb(urb);
|
|
|
|
tmp = p->next;
|
|
|
|
list_del(p);
|
|
|
|
kfree(buf);
|
|
|
|
p = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
|
|
int hdpvr_free_buffers(struct hdpvr_device *dev)
|
|
|
|
{
|
|
|
|
hdpvr_cancel_queue(dev);
|
|
|
|
|
|
|
|
hdpvr_free_queue(&dev->free_buff_list);
|
|
|
|
hdpvr_free_queue(&dev->rec_buff_list);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
|
|
int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
int retval = -ENOMEM;
|
|
|
|
u8 *mem;
|
|
|
|
struct hdpvr_buffer *buf;
|
|
|
|
struct urb *urb;
|
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"allocating %u buffers\n", count);
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
|
|
|
buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
|
|
|
|
if (!buf) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
buf->dev = dev;
|
|
|
|
|
|
|
|
urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
|
if (!urb) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
|
2009-11-21 19:49:41 +08:00
|
|
|
goto exit_urb;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
buf->urb = urb;
|
|
|
|
|
2010-04-12 19:17:25 +08:00
|
|
|
mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
|
|
|
|
&urb->transfer_dma);
|
2009-03-19 05:10:04 +08:00
|
|
|
if (!mem) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev,
|
|
|
|
"cannot allocate usb transfer buffer\n");
|
2009-11-21 19:49:41 +08:00
|
|
|
goto exit_urb_buffer;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
usb_fill_bulk_urb(buf->urb, dev->udev,
|
|
|
|
usb_rcvbulkpipe(dev->udev,
|
|
|
|
dev->bulk_in_endpointAddr),
|
|
|
|
mem, dev->bulk_in_size,
|
|
|
|
hdpvr_read_bulk_callback, buf);
|
|
|
|
|
2010-10-04 06:09:18 +08:00
|
|
|
buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2009-03-19 05:10:04 +08:00
|
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
|
|
list_add_tail(&buf->buff_list, &dev->free_buff_list);
|
|
|
|
}
|
|
|
|
return 0;
|
2009-11-21 19:49:41 +08:00
|
|
|
exit_urb_buffer:
|
|
|
|
usb_free_urb(urb);
|
|
|
|
exit_urb:
|
|
|
|
kfree(buf);
|
2009-03-19 05:10:04 +08:00
|
|
|
exit:
|
|
|
|
hdpvr_free_buffers(dev);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdpvr_submit_buffers(struct hdpvr_device *dev)
|
|
|
|
{
|
|
|
|
struct hdpvr_buffer *buf;
|
|
|
|
struct urb *urb;
|
|
|
|
int ret = 0, err_count = 0;
|
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
|
|
|
|
while (dev->status == STATUS_STREAMING &&
|
|
|
|
!list_empty(&dev->free_buff_list)) {
|
|
|
|
|
|
|
|
buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
|
|
|
|
buff_list);
|
|
|
|
if (buf->status != BUFSTAT_AVAILABLE) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev,
|
2009-04-15 10:14:10 +08:00
|
|
|
"buffer not marked as available\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
urb = buf->urb;
|
|
|
|
urb->status = 0;
|
|
|
|
urb->actual_length = 0;
|
|
|
|
ret = usb_submit_urb(urb, GFP_KERNEL);
|
|
|
|
if (ret) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev,
|
|
|
|
"usb_submit_urb in %s returned %d\n",
|
|
|
|
__func__, ret);
|
2009-03-19 05:10:04 +08:00
|
|
|
if (++err_count > 2)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
buf->status = BUFSTAT_INPROGRESS;
|
|
|
|
list_move_tail(&buf->buff_list, &dev->rec_buff_list);
|
|
|
|
}
|
|
|
|
err:
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
|
|
|
|
{
|
|
|
|
struct hdpvr_buffer *buf;
|
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
|
|
|
|
if (list_empty(&dev->rec_buff_list)) {
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
|
|
|
|
buff_list);
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hdpvr_transmit_buffers(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
|
|
|
|
worker);
|
|
|
|
|
|
|
|
while (dev->status == STATUS_STREAMING) {
|
|
|
|
|
|
|
|
if (hdpvr_submit_buffers(dev)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (wait_event_interruptible(dev->wait_buffer,
|
|
|
|
!list_empty(&dev->free_buff_list) ||
|
|
|
|
dev->status != STATUS_STREAMING))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"transmit worker exited\n");
|
|
|
|
return;
|
|
|
|
error:
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"transmit buffers errored\n");
|
|
|
|
dev->status = STATUS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
|
|
static int hdpvr_start_streaming(struct hdpvr_device *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct hdpvr_video_info *vidinf;
|
|
|
|
|
|
|
|
if (dev->status == STATUS_STREAMING)
|
|
|
|
return 0;
|
|
|
|
else if (dev->status != STATUS_IDLE)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
vidinf = get_video_info(dev);
|
|
|
|
|
|
|
|
if (vidinf) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"video signal: %dx%d@%dhz\n", vidinf->width,
|
|
|
|
vidinf->height, vidinf->fps);
|
|
|
|
kfree(vidinf);
|
|
|
|
|
|
|
|
/* start streaming 2 request */
|
|
|
|
ret = usb_control_msg(dev->udev,
|
|
|
|
usb_sndctrlpipe(dev->udev, 0),
|
|
|
|
0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"encoder start control request returned %d\n", ret);
|
2013-04-25 16:59:56 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-04-25 16:59:56 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2012-02-03 00:35:21 +08:00
|
|
|
dev->status = STATUS_STREAMING;
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
|
|
|
|
queue_work(dev->workqueue, &dev->worker);
|
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"streaming started\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
msleep(250);
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"no video signal at input %d\n", dev->options.video_input);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
|
|
static int hdpvr_stop_streaming(struct hdpvr_device *dev)
|
|
|
|
{
|
2010-01-23 21:44:34 +08:00
|
|
|
int actual_length;
|
|
|
|
uint c = 0;
|
2009-03-28 07:21:17 +08:00
|
|
|
u8 *buf;
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
if (dev->status == STATUS_IDLE)
|
|
|
|
return 0;
|
|
|
|
else if (dev->status != STATUS_STREAMING)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
2009-03-28 07:21:17 +08:00
|
|
|
buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer "
|
|
|
|
"for emptying the internal device buffer. "
|
|
|
|
"Next capture start will be slow\n");
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->status = STATUS_SHUTTING_DOWN;
|
|
|
|
hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
|
2009-03-27 07:56:06 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
wake_up_interruptible(&dev->wait_buffer);
|
|
|
|
msleep(50);
|
|
|
|
|
|
|
|
flush_workqueue(dev->workqueue);
|
|
|
|
|
2009-03-27 07:56:06 +08:00
|
|
|
mutex_lock(&dev->io_mutex);
|
2009-03-19 05:10:04 +08:00
|
|
|
/* kill the still outstanding urbs */
|
|
|
|
hdpvr_cancel_queue(dev);
|
|
|
|
|
2009-03-28 07:21:17 +08:00
|
|
|
/* emptying the device buffer beforeshutting it down */
|
|
|
|
while (buf && ++c < 500 &&
|
|
|
|
!usb_bulk_msg(dev->udev,
|
|
|
|
usb_rcvbulkpipe(dev->udev,
|
|
|
|
dev->bulk_in_endpointAddr),
|
|
|
|
buf, dev->bulk_in_size, &actual_length,
|
|
|
|
BULK_URB_TIMEOUT)) {
|
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
|
|
"%2d: got %d bytes\n", c, actual_length);
|
|
|
|
}
|
|
|
|
kfree(buf);
|
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
|
|
"used %d urbs to empty device buffers\n", c-1);
|
|
|
|
msleep(10);
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->status = STATUS_IDLE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*=======================================================================*/
|
|
|
|
/*
|
|
|
|
* video 4 linux 2 file operations
|
|
|
|
*/
|
|
|
|
|
2013-03-21 02:29:52 +08:00
|
|
|
static int hdpvr_open(struct file *file)
|
|
|
|
{
|
|
|
|
struct hdpvr_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (fh == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
fh->legacy_mode = true;
|
|
|
|
v4l2_fh_init(&fh->fh, video_devdata(file));
|
|
|
|
v4l2_fh_add(&fh->fh);
|
|
|
|
file->private_data = fh;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
static int hdpvr_release(struct file *file)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
2013-04-06 17:00:17 +08:00
|
|
|
if (file->private_data == dev->owner) {
|
2009-03-19 05:10:04 +08:00
|
|
|
hdpvr_stop_streaming(dev);
|
2013-04-06 17:00:17 +08:00
|
|
|
dev->owner = NULL;
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
2013-04-06 17:00:17 +08:00
|
|
|
return v4l2_fh_release(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hdpvr_v4l2_read()
|
|
|
|
* will allocate buffers when called for the first time
|
|
|
|
*/
|
|
|
|
static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
|
|
|
|
loff_t *pos)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
struct hdpvr_buffer *buf = NULL;
|
|
|
|
struct urb *urb;
|
|
|
|
unsigned int ret = 0;
|
|
|
|
int rem, cnt;
|
|
|
|
|
|
|
|
if (*pos)
|
|
|
|
return -ESPIPE;
|
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
if (dev->status == STATUS_IDLE) {
|
|
|
|
if (hdpvr_start_streaming(dev)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
|
|
"start_streaming failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
ret = -EIO;
|
|
|
|
msleep(200);
|
|
|
|
dev->status = STATUS_IDLE;
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
goto err;
|
|
|
|
}
|
2013-04-06 17:00:17 +08:00
|
|
|
dev->owner = file->private_data;
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
|
|
|
/* wait for the first buffer */
|
|
|
|
if (!(file->f_flags & O_NONBLOCK)) {
|
|
|
|
if (wait_event_interruptible(dev->wait_data,
|
|
|
|
hdpvr_get_next_buffer(dev)))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
|
|
|
|
|
|
while (count > 0 && buf) {
|
|
|
|
|
|
|
|
if (buf->status != BUFSTAT_READY &&
|
|
|
|
dev->status != STATUS_DISCONNECTED) {
|
|
|
|
/* return nonblocking */
|
|
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
|
|
if (!ret)
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wait_event_interruptible(dev->wait_data,
|
|
|
|
buf->status == BUFSTAT_READY)) {
|
|
|
|
ret = -ERESTARTSYS;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf->status != BUFSTAT_READY)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* set remaining bytes to copy */
|
|
|
|
urb = buf->urb;
|
|
|
|
rem = urb->actual_length - buf->pos;
|
|
|
|
cnt = rem > count ? count : rem;
|
|
|
|
|
|
|
|
if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
|
|
|
|
cnt)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
if (!ret)
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->pos += cnt;
|
|
|
|
count -= cnt;
|
|
|
|
buffer += cnt;
|
|
|
|
ret += cnt;
|
|
|
|
|
|
|
|
/* finished, take next buffer */
|
|
|
|
if (buf->pos == urb->actual_length) {
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
buf->pos = 0;
|
|
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
|
|
|
|
|
|
list_move_tail(&buf->buff_list, &dev->free_buff_list);
|
|
|
|
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
|
|
|
wake_up_interruptible(&dev->wait_buffer);
|
|
|
|
|
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
if (!ret && !buf)
|
|
|
|
ret = -EAGAIN;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
|
|
|
|
{
|
2013-02-12 19:26:59 +08:00
|
|
|
unsigned long req_events = poll_requested_events(wait);
|
2009-03-27 01:32:54 +08:00
|
|
|
struct hdpvr_buffer *buf = NULL;
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(filp);
|
2013-02-12 19:26:59 +08:00
|
|
|
unsigned int mask = v4l2_ctrl_poll(filp, wait);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-02-12 19:26:59 +08:00
|
|
|
if (!(req_events & (POLLIN | POLLRDNORM)))
|
|
|
|
return mask;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-02-12 19:26:59 +08:00
|
|
|
mutex_lock(&dev->io_mutex);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
if (dev->status == STATUS_IDLE) {
|
|
|
|
if (hdpvr_start_streaming(dev)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
|
|
"start_streaming failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->status = STATUS_IDLE;
|
2013-04-06 17:00:17 +08:00
|
|
|
} else {
|
|
|
|
dev->owner = filp->private_data;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
2009-03-27 01:32:54 +08:00
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
|
|
/* only wait if no data is available */
|
|
|
|
if (!buf || buf->status != BUFSTAT_READY) {
|
|
|
|
poll_wait(filp, &dev->wait_data, wait);
|
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
2009-03-27 01:32:54 +08:00
|
|
|
if (buf && buf->status == BUFSTAT_READY)
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct v4l2_file_operations hdpvr_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
2013-03-21 02:29:52 +08:00
|
|
|
.open = hdpvr_open,
|
2009-03-19 05:10:04 +08:00
|
|
|
.release = hdpvr_release,
|
|
|
|
.read = hdpvr_read,
|
|
|
|
.poll = hdpvr_poll,
|
2009-03-19 07:57:04 +08:00
|
|
|
.unlocked_ioctl = video_ioctl2,
|
2009-03-19 05:10:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*=======================================================================*/
|
|
|
|
/*
|
|
|
|
* V4L2 ioctl handling
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int vidioc_querycap(struct file *file, void *priv,
|
|
|
|
struct v4l2_capability *cap)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
|
|
|
|
strcpy(cap->driver, "hdpvr");
|
2010-02-14 19:57:39 +08:00
|
|
|
strcpy(cap->card, "Hauppauge HD PVR");
|
2009-03-19 05:10:04 +08:00
|
|
|
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
|
2013-02-12 20:21:36 +08:00
|
|
|
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO |
|
|
|
|
V4L2_CAP_READWRITE;
|
|
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
2009-03-19 05:10:04 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
static int vidioc_s_std(struct file *file, void *_fh,
|
2013-03-15 17:10:40 +08:00
|
|
|
v4l2_std_id std)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2013-04-11 01:26:52 +08:00
|
|
|
struct hdpvr_fh *fh = _fh;
|
2009-03-19 05:10:04 +08:00
|
|
|
u8 std_type = 1;
|
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
if (!fh->legacy_mode && dev->options.video_input == HDPVR_COMPONENT)
|
2013-03-19 20:53:29 +08:00
|
|
|
return -ENODATA;
|
|
|
|
if (dev->status != STATUS_IDLE)
|
|
|
|
return -EBUSY;
|
|
|
|
if (std & V4L2_STD_525_60)
|
2009-03-19 05:10:04 +08:00
|
|
|
std_type = 0;
|
2013-03-19 20:53:29 +08:00
|
|
|
dev->cur_std = std;
|
|
|
|
dev->width = 720;
|
|
|
|
dev->height = std_type ? 576 : 480;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
|
|
|
|
}
|
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
static int vidioc_g_std(struct file *file, void *_fh,
|
2013-03-19 20:53:29 +08:00
|
|
|
v4l2_std_id *std)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2013-04-11 01:26:52 +08:00
|
|
|
struct hdpvr_fh *fh = _fh;
|
2013-03-19 20:53:29 +08:00
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
if (!fh->legacy_mode && dev->options.video_input == HDPVR_COMPONENT)
|
2013-03-19 20:53:29 +08:00
|
|
|
return -ENODATA;
|
|
|
|
*std = dev->cur_std;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
static int vidioc_querystd(struct file *file, void *_fh, v4l2_std_id *a)
|
2013-03-19 20:53:29 +08:00
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_video_info *vid_info;
|
2013-04-11 01:26:52 +08:00
|
|
|
struct hdpvr_fh *fh = _fh;
|
2013-03-19 20:53:29 +08:00
|
|
|
|
|
|
|
*a = V4L2_STD_ALL;
|
2013-04-11 01:26:52 +08:00
|
|
|
if (dev->options.video_input == HDPVR_COMPONENT)
|
|
|
|
return fh->legacy_mode ? 0 : -ENODATA;
|
2013-03-19 20:53:29 +08:00
|
|
|
vid_info = get_video_info(dev);
|
|
|
|
if (vid_info == NULL)
|
|
|
|
return 0;
|
|
|
|
if (vid_info->width == 720 &&
|
|
|
|
(vid_info->height == 480 || vid_info->height == 576)) {
|
|
|
|
*a = (vid_info->height == 480) ?
|
|
|
|
V4L2_STD_525_60 : V4L2_STD_625_50;
|
|
|
|
}
|
|
|
|
kfree(vid_info);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-21 02:29:52 +08:00
|
|
|
static int vidioc_s_dv_timings(struct file *file, void *_fh,
|
|
|
|
struct v4l2_dv_timings *timings)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_fh *fh = _fh;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fh->legacy_mode = false;
|
|
|
|
if (dev->options.video_input)
|
|
|
|
return -ENODATA;
|
|
|
|
if (dev->status != STATUS_IDLE)
|
|
|
|
return -EBUSY;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++)
|
|
|
|
if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0))
|
|
|
|
break;
|
|
|
|
if (i == ARRAY_SIZE(hdpvr_dv_timings))
|
|
|
|
return -EINVAL;
|
|
|
|
dev->cur_dv_timings = hdpvr_dv_timings[i];
|
|
|
|
dev->width = hdpvr_dv_timings[i].bt.width;
|
|
|
|
dev->height = hdpvr_dv_timings[i].bt.height;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_g_dv_timings(struct file *file, void *_fh,
|
|
|
|
struct v4l2_dv_timings *timings)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_fh *fh = _fh;
|
|
|
|
|
|
|
|
fh->legacy_mode = false;
|
|
|
|
if (dev->options.video_input)
|
|
|
|
return -ENODATA;
|
|
|
|
*timings = dev->cur_dv_timings;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_query_dv_timings(struct file *file, void *_fh,
|
|
|
|
struct v4l2_dv_timings *timings)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_fh *fh = _fh;
|
|
|
|
struct hdpvr_video_info *vid_info;
|
|
|
|
bool interlaced;
|
|
|
|
int ret = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fh->legacy_mode = false;
|
|
|
|
if (dev->options.video_input)
|
|
|
|
return -ENODATA;
|
|
|
|
vid_info = get_video_info(dev);
|
|
|
|
if (vid_info == NULL)
|
|
|
|
return -ENOLCK;
|
|
|
|
interlaced = vid_info->fps <= 30;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) {
|
|
|
|
const struct v4l2_bt_timings *bt = &hdpvr_dv_timings[i].bt;
|
|
|
|
unsigned hsize;
|
|
|
|
unsigned vsize;
|
|
|
|
unsigned fps;
|
|
|
|
|
|
|
|
hsize = bt->hfrontporch + bt->hsync + bt->hbackporch + bt->width;
|
|
|
|
vsize = bt->vfrontporch + bt->vsync + bt->vbackporch +
|
|
|
|
bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch +
|
|
|
|
bt->height;
|
|
|
|
fps = (unsigned)bt->pixelclock / (hsize * vsize);
|
|
|
|
if (bt->width != vid_info->width ||
|
|
|
|
bt->height != vid_info->height ||
|
|
|
|
bt->interlaced != interlaced ||
|
|
|
|
(fps != vid_info->fps && fps + 1 != vid_info->fps))
|
|
|
|
continue;
|
|
|
|
*timings = hdpvr_dv_timings[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(hdpvr_dv_timings))
|
|
|
|
ret = -ERANGE;
|
|
|
|
kfree(vid_info);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_enum_dv_timings(struct file *file, void *_fh,
|
|
|
|
struct v4l2_enum_dv_timings *timings)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_fh *fh = _fh;
|
|
|
|
|
|
|
|
fh->legacy_mode = false;
|
|
|
|
memset(timings->reserved, 0, sizeof(timings->reserved));
|
|
|
|
if (dev->options.video_input)
|
|
|
|
return -ENODATA;
|
|
|
|
if (timings->index >= ARRAY_SIZE(hdpvr_dv_timings))
|
|
|
|
return -EINVAL;
|
|
|
|
timings->timings = hdpvr_dv_timings[timings->index];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_dv_timings_cap(struct file *file, void *_fh,
|
|
|
|
struct v4l2_dv_timings_cap *cap)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_fh *fh = _fh;
|
|
|
|
|
|
|
|
fh->legacy_mode = false;
|
|
|
|
if (dev->options.video_input)
|
|
|
|
return -ENODATA;
|
|
|
|
cap->type = V4L2_DV_BT_656_1120;
|
|
|
|
cap->bt.min_width = 720;
|
|
|
|
cap->bt.max_width = 1920;
|
|
|
|
cap->bt.min_height = 480;
|
|
|
|
cap->bt.max_height = 1080;
|
|
|
|
cap->bt.min_pixelclock = 27000000;
|
|
|
|
cap->bt.max_pixelclock = 74250000;
|
|
|
|
cap->bt.standards = V4L2_DV_BT_STD_CEA861;
|
|
|
|
cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
static const char *iname[] = {
|
|
|
|
[HDPVR_COMPONENT] = "Component",
|
|
|
|
[HDPVR_SVIDEO] = "S-Video",
|
|
|
|
[HDPVR_COMPOSITE] = "Composite",
|
|
|
|
};
|
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
static int vidioc_enum_input(struct file *file, void *_fh, struct v4l2_input *i)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
|
|
|
unsigned int n;
|
|
|
|
|
|
|
|
n = i->index;
|
|
|
|
if (n >= HDPVR_VIDEO_INPUTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
i->type = V4L2_INPUT_TYPE_CAMERA;
|
|
|
|
|
|
|
|
strncpy(i->name, iname[n], sizeof(i->name) - 1);
|
|
|
|
i->name[sizeof(i->name) - 1] = '\0';
|
|
|
|
|
|
|
|
i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
|
|
|
|
|
2013-03-19 20:53:29 +08:00
|
|
|
i->capabilities = n ? V4L2_IN_CAP_STD : V4L2_IN_CAP_DV_TIMINGS;
|
|
|
|
i->std = n ? V4L2_STD_ALL : 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-11 01:26:52 +08:00
|
|
|
static int vidioc_s_input(struct file *file, void *_fh,
|
2009-03-19 05:10:04 +08:00
|
|
|
unsigned int index)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (index >= HDPVR_VIDEO_INPUTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dev->status != STATUS_IDLE)
|
2013-02-12 20:26:30 +08:00
|
|
|
return -EBUSY;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
|
2013-03-19 20:53:29 +08:00
|
|
|
if (!retval) {
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->options.video_input = index;
|
2013-04-11 01:26:52 +08:00
|
|
|
/*
|
|
|
|
* Unfortunately gstreamer calls ENUMSTD and bails out if it
|
|
|
|
* won't find any formats, even though component input is
|
|
|
|
* selected. This means that we have to leave tvnorms at
|
|
|
|
* V4L2_STD_ALL. We cannot use the 'legacy' trick since
|
|
|
|
* tvnorms is set at the device node level and not at the
|
|
|
|
* filehandle level.
|
|
|
|
*
|
|
|
|
* Comment this out for now, but if the legacy mode can be
|
|
|
|
* removed in the future, then this code should be enabled
|
|
|
|
* again.
|
2013-03-19 20:53:29 +08:00
|
|
|
dev->video_dev->tvnorms =
|
2013-04-11 01:26:52 +08:00
|
|
|
(index != HDPVR_COMPONENT) ? V4L2_STD_ALL : 0;
|
|
|
|
*/
|
2013-03-19 20:53:29 +08:00
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_g_input(struct file *file, void *private_data,
|
|
|
|
unsigned int *index)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
*index = dev->options.video_input;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char *audio_iname[] = {
|
|
|
|
[HDPVR_RCA_FRONT] = "RCA front",
|
|
|
|
[HDPVR_RCA_BACK] = "RCA back",
|
|
|
|
[HDPVR_SPDIF] = "SPDIF",
|
|
|
|
};
|
|
|
|
|
|
|
|
static int vidioc_enumaudio(struct file *file, void *priv,
|
|
|
|
struct v4l2_audio *audio)
|
|
|
|
{
|
|
|
|
unsigned int n;
|
|
|
|
|
|
|
|
n = audio->index;
|
|
|
|
if (n >= HDPVR_AUDIO_INPUTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
audio->capability = V4L2_AUDCAP_STEREO;
|
|
|
|
|
|
|
|
strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
|
|
|
|
audio->name[sizeof(audio->name) - 1] = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_s_audio(struct file *file, void *private_data,
|
2012-09-04 22:59:31 +08:00
|
|
|
const struct v4l2_audio *audio)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (audio->index >= HDPVR_AUDIO_INPUTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dev->status != STATUS_IDLE)
|
2013-02-12 20:26:30 +08:00
|
|
|
return -EBUSY;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
|
|
|
|
if (!retval)
|
|
|
|
dev->options.audio_input = audio->index;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_g_audio(struct file *file, void *private_data,
|
|
|
|
struct v4l2_audio *audio)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
audio->index = dev->options.audio_input;
|
|
|
|
audio->capability = V4L2_AUDCAP_STEREO;
|
|
|
|
strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
|
|
|
|
audio->name[sizeof(audio->name) - 1] = '\0';
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
static int hdpvr_try_ctrl(struct v4l2_ctrl *ctrl)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-03-19 20:30:50 +08:00
|
|
|
struct hdpvr_device *dev =
|
|
|
|
container_of(ctrl->handler, struct hdpvr_device, hdl);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
switch (ctrl->id) {
|
2013-03-19 20:30:50 +08:00
|
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
|
|
if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
|
|
|
|
dev->video_bitrate->val >= dev->video_bitrate_peak->val)
|
|
|
|
dev->video_bitrate_peak->val =
|
|
|
|
dev->video_bitrate->val + 100000;
|
2009-03-19 05:10:04 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
static int hdpvr_s_ctrl(struct v4l2_ctrl *ctrl)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-03-19 20:30:50 +08:00
|
|
|
struct hdpvr_device *dev =
|
|
|
|
container_of(ctrl->handler, struct hdpvr_device, hdl);
|
|
|
|
struct hdpvr_options *opt = &dev->options;
|
|
|
|
int ret = -EINVAL;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
switch (ctrl->id) {
|
|
|
|
case V4L2_CID_BRIGHTNESS:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.brightness = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_CONTRAST:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.contrast = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_SATURATION:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.saturation = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_HUE:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_HUE, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.hue = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_SHARPNESS:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.sharpness = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_MPEG_AUDIO_ENCODING:
|
|
|
|
if (dev->flags & HDPVR_FLAG_AC3_CAP) {
|
2013-03-19 20:30:50 +08:00
|
|
|
opt->audio_codec = ctrl->val;
|
|
|
|
return hdpvr_set_audio(dev, opt->audio_input,
|
2009-03-19 05:10:04 +08:00
|
|
|
opt->audio_codec);
|
|
|
|
}
|
2013-03-19 20:30:50 +08:00
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
2013-03-19 20:30:50 +08:00
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
|
|
|
|
/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
|
|
|
|
/* opt->gop_mode |= 0x2; */
|
|
|
|
/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
|
|
|
|
/* opt->gop_mode); */
|
|
|
|
/* } */
|
|
|
|
/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
|
|
|
|
/* opt->gop_mode &= ~0x2; */
|
|
|
|
/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
|
|
|
|
/* opt->gop_mode); */
|
|
|
|
/* } */
|
|
|
|
/* break; */
|
2013-03-19 20:30:50 +08:00
|
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: {
|
|
|
|
uint peak_bitrate = dev->video_bitrate_peak->val / 100000;
|
|
|
|
uint bitrate = dev->video_bitrate->val / 100000;
|
|
|
|
|
|
|
|
if (ctrl->is_new) {
|
|
|
|
if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
|
|
|
|
opt->bitrate_mode = HDPVR_CONSTANT;
|
|
|
|
else
|
|
|
|
opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
|
2009-03-19 05:10:04 +08:00
|
|
|
hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
|
|
|
|
opt->bitrate_mode);
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_activate(dev->video_bitrate_peak,
|
|
|
|
ctrl->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
if (dev->video_bitrate_peak->is_new ||
|
|
|
|
dev->video_bitrate->is_new) {
|
|
|
|
opt->bitrate = bitrate;
|
2009-03-19 05:10:04 +08:00
|
|
|
opt->peak_bitrate = peak_bitrate;
|
|
|
|
hdpvr_set_bitrate(dev);
|
2013-03-19 20:30:50 +08:00
|
|
|
}
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
case V4L2_CID_MPEG_STREAM_TYPE:
|
2013-03-19 20:30:50 +08:00
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
default:
|
2013-03-19 20:30:50 +08:00
|
|
|
break;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
|
|
|
|
struct v4l2_fmtdesc *f)
|
|
|
|
{
|
2013-02-12 20:26:30 +08:00
|
|
|
if (f->index != 0)
|
2009-03-19 05:10:04 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
f->flags = V4L2_FMT_FLAG_COMPRESSED;
|
|
|
|
strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
|
|
|
|
f->pixelformat = V4L2_PIX_FMT_MPEG;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-21 02:29:52 +08:00
|
|
|
static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh,
|
2009-03-19 05:10:04 +08:00
|
|
|
struct v4l2_format *f)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2013-03-21 02:29:52 +08:00
|
|
|
struct hdpvr_fh *fh = _fh;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The original driver would always returns the current detected
|
|
|
|
* resolution as the format (and EFAULT if it couldn't be detected).
|
|
|
|
* With the introduction of VIDIOC_QUERY_DV_TIMINGS there is now a
|
|
|
|
* better way of doing this, but to stay compatible with existing
|
|
|
|
* applications we assume legacy mode every time an application opens
|
|
|
|
* the device. Only if one of the new DV_TIMINGS ioctls is called
|
|
|
|
* will the filehandle go into 'normal' mode where g_fmt returns the
|
|
|
|
* last set format.
|
|
|
|
*/
|
|
|
|
if (fh->legacy_mode) {
|
|
|
|
struct hdpvr_video_info *vid_info;
|
|
|
|
|
|
|
|
vid_info = get_video_info(dev);
|
|
|
|
if (!vid_info)
|
|
|
|
return -EFAULT;
|
|
|
|
f->fmt.pix.width = vid_info->width;
|
|
|
|
f->fmt.pix.height = vid_info->height;
|
|
|
|
kfree(vid_info);
|
|
|
|
} else {
|
|
|
|
f->fmt.pix.width = dev->width;
|
|
|
|
f->fmt.pix.height = dev->height;
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
|
|
|
|
f->fmt.pix.sizeimage = dev->bulk_in_size;
|
|
|
|
f->fmt.pix.bytesperline = 0;
|
2013-03-21 02:29:52 +08:00
|
|
|
f->fmt.pix.priv = 0;
|
|
|
|
if (f->fmt.pix.width == 720) {
|
|
|
|
/* SDTV formats */
|
|
|
|
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
|
|
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
|
|
} else {
|
|
|
|
/* HDTV formats */
|
|
|
|
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE240M;
|
|
|
|
f->fmt.pix.field = V4L2_FIELD_NONE;
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-19 07:57:04 +08:00
|
|
|
static int vidioc_encoder_cmd(struct file *filp, void *priv,
|
|
|
|
struct v4l2_encoder_cmd *a)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(filp);
|
|
|
|
int res = 0;
|
2009-03-19 07:57:04 +08:00
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
2013-04-06 17:00:17 +08:00
|
|
|
a->flags = 0;
|
2009-03-19 07:57:04 +08:00
|
|
|
|
|
|
|
switch (a->cmd) {
|
|
|
|
case V4L2_ENC_CMD_START:
|
2013-04-06 17:00:17 +08:00
|
|
|
if (dev->owner && filp->private_data != dev->owner) {
|
|
|
|
res = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dev->status == STATUS_STREAMING)
|
|
|
|
break;
|
2009-03-19 07:57:04 +08:00
|
|
|
res = hdpvr_start_streaming(dev);
|
2013-04-06 17:00:17 +08:00
|
|
|
if (!res)
|
|
|
|
dev->owner = filp->private_data;
|
|
|
|
else
|
|
|
|
dev->status = STATUS_IDLE;
|
2009-03-19 07:57:04 +08:00
|
|
|
break;
|
|
|
|
case V4L2_ENC_CMD_STOP:
|
2013-04-06 17:00:17 +08:00
|
|
|
if (dev->owner && filp->private_data != dev->owner) {
|
|
|
|
res = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dev->status == STATUS_IDLE)
|
|
|
|
break;
|
2009-03-19 07:57:04 +08:00
|
|
|
res = hdpvr_stop_streaming(dev);
|
2013-04-06 17:00:17 +08:00
|
|
|
if (!res)
|
|
|
|
dev->owner = NULL;
|
2009-03-19 07:57:04 +08:00
|
|
|
break;
|
|
|
|
default:
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 07:57:04 +08:00
|
|
|
"Unsupported encoder cmd %d\n", a->cmd);
|
2009-03-27 07:56:06 +08:00
|
|
|
res = -EINVAL;
|
2013-04-06 17:00:17 +08:00
|
|
|
break;
|
2009-03-19 07:57:04 +08:00
|
|
|
}
|
2013-04-06 17:00:17 +08:00
|
|
|
|
2009-03-19 07:57:04 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
|
|
|
|
struct v4l2_encoder_cmd *a)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
a->flags = 0;
|
2009-03-19 07:57:04 +08:00
|
|
|
switch (a->cmd) {
|
|
|
|
case V4L2_ENC_CMD_START:
|
|
|
|
case V4L2_ENC_CMD_STOP:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
|
|
|
|
.vidioc_querycap = vidioc_querycap,
|
|
|
|
.vidioc_s_std = vidioc_s_std,
|
2013-03-19 20:53:29 +08:00
|
|
|
.vidioc_g_std = vidioc_g_std,
|
|
|
|
.vidioc_querystd = vidioc_querystd,
|
2013-03-21 02:29:52 +08:00
|
|
|
.vidioc_s_dv_timings = vidioc_s_dv_timings,
|
|
|
|
.vidioc_g_dv_timings = vidioc_g_dv_timings,
|
|
|
|
.vidioc_query_dv_timings= vidioc_query_dv_timings,
|
|
|
|
.vidioc_enum_dv_timings = vidioc_enum_dv_timings,
|
|
|
|
.vidioc_dv_timings_cap = vidioc_dv_timings_cap,
|
2009-03-19 05:10:04 +08:00
|
|
|
.vidioc_enum_input = vidioc_enum_input,
|
|
|
|
.vidioc_g_input = vidioc_g_input,
|
|
|
|
.vidioc_s_input = vidioc_s_input,
|
|
|
|
.vidioc_enumaudio = vidioc_enumaudio,
|
|
|
|
.vidioc_g_audio = vidioc_g_audio,
|
|
|
|
.vidioc_s_audio = vidioc_s_audio,
|
2013-02-12 19:26:59 +08:00
|
|
|
.vidioc_enum_fmt_vid_cap= vidioc_enum_fmt_vid_cap,
|
|
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
2013-03-21 02:29:52 +08:00
|
|
|
.vidioc_s_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
|
|
.vidioc_try_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
2009-03-19 07:57:04 +08:00
|
|
|
.vidioc_encoder_cmd = vidioc_encoder_cmd,
|
|
|
|
.vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
|
2013-02-12 19:26:59 +08:00
|
|
|
.vidioc_log_status = v4l2_ctrl_log_status,
|
|
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
2009-03-19 05:10:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static void hdpvr_device_release(struct video_device *vdev)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_get_drvdata(vdev);
|
|
|
|
|
|
|
|
hdpvr_delete(dev);
|
2010-05-02 19:01:04 +08:00
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
destroy_workqueue(dev->workqueue);
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
|
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_handler_free(&dev->hdl);
|
2010-05-02 19:01:04 +08:00
|
|
|
|
|
|
|
/* deregister I2C adapter */
|
2013-03-22 02:46:18 +08:00
|
|
|
#if IS_ENABLED(CONFIG_I2C)
|
2010-05-02 19:01:04 +08:00
|
|
|
mutex_lock(&dev->i2c_mutex);
|
[media] hdpvr: enable IR part
A number of things going on here, but the end result is that the IR part
on the hdpvr gets enabled, and can be used with ir-kbd-i2c and/or
lirc_zilog.
First up, there are some conditional build fixes that come into play
whether i2c is built-in or modular. Second, we're swapping out
i2c_new_probed_device() for i2c_new_device(), as in my testing, probing
always fails, but we *know* that all hdpvr devices have a z8 chip at
0x70 and 0x71. Third, we're poking at an i2c address directly without a
client, and writing some magic bits to actually turn on this IR part
(this could use some improvement in the future). Fourth, some of the
i2c_adapter storage has been reworked, as the existing implementation
used to lead to an oops following i2c changes c. 2.6.31.
Earlier editions of this patch have been floating around the 'net for a
while, including being patched into Fedora kernels, and they *do* work.
This specific version isn't yet tested, beyond loading ir-kbd-i2c and
confirming that it does bind to the RX address of the hdpvr.
[mchehab@redhat.com: I2C_CLASS_TV_ANALOG is not defined. Fix compilation bug]
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Acked-by: Andy Walls <awalls@md.metrocast.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-01-15 03:25:21 +08:00
|
|
|
i2c_del_adapter(&dev->i2c_adapter);
|
2010-05-02 19:01:04 +08:00
|
|
|
mutex_unlock(&dev->i2c_mutex);
|
|
|
|
#endif /* CONFIG_I2C */
|
|
|
|
|
|
|
|
kfree(dev->usbc_buf);
|
|
|
|
kfree(dev);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct video_device hdpvr_video_template = {
|
|
|
|
.fops = &hdpvr_fops,
|
|
|
|
.release = hdpvr_device_release,
|
|
|
|
.ioctl_ops = &hdpvr_ioctl_ops,
|
2013-04-11 01:26:52 +08:00
|
|
|
.tvnorms = V4L2_STD_ALL,
|
2009-03-19 05:10:04 +08:00
|
|
|
};
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
static const struct v4l2_ctrl_ops hdpvr_ctrl_ops = {
|
|
|
|
.try_ctrl = hdpvr_try_ctrl,
|
|
|
|
.s_ctrl = hdpvr_s_ctrl,
|
|
|
|
};
|
|
|
|
|
2009-03-27 01:40:55 +08:00
|
|
|
int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
|
|
|
|
int devnum)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-03-19 20:30:50 +08:00
|
|
|
struct v4l2_ctrl_handler *hdl = &dev->hdl;
|
|
|
|
bool ac3 = dev->flags & HDPVR_FLAG_AC3_CAP;
|
|
|
|
int res;
|
|
|
|
|
2013-03-19 20:53:29 +08:00
|
|
|
dev->cur_std = V4L2_STD_525_60;
|
|
|
|
dev->width = 720;
|
|
|
|
dev->height = 480;
|
2013-03-21 02:29:52 +08:00
|
|
|
dev->cur_dv_timings = hdpvr_dv_timings[HDPVR_DEF_DV_TIMINGS_IDX];
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_handler_init(hdl, 11);
|
|
|
|
if (dev->fw_ver > 0x15) {
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x40);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x40);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_HUE, 0x0, 0x1e, 1, 0xf);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80);
|
|
|
|
} else {
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x86);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_HUE, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80);
|
|
|
|
}
|
|
|
|
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_STREAM_TYPE,
|
|
|
|
V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
|
|
|
|
0x1, V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_AUDIO_ENCODING,
|
|
|
|
ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : V4L2_MPEG_AUDIO_ENCODING_AAC,
|
|
|
|
0x7, V4L2_MPEG_AUDIO_ENCODING_AAC);
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_ENCODING,
|
|
|
|
V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 0x3,
|
|
|
|
V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
|
|
|
|
|
|
|
|
dev->video_mode = v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
|
|
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
|
|
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
|
|
|
|
|
|
|
|
dev->video_bitrate = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_BITRATE,
|
|
|
|
1000000, 13500000, 100000, 6500000);
|
|
|
|
dev->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
|
|
|
|
1100000, 20200000, 100000, 9000000);
|
|
|
|
dev->v4l2_dev.ctrl_handler = hdl;
|
|
|
|
if (hdl->error) {
|
|
|
|
res = hdl->error;
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Could not register controls\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
v4l2_ctrl_cluster(3, &dev->video_mode);
|
|
|
|
res = v4l2_ctrl_handler_setup(hdl);
|
|
|
|
if (res < 0) {
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Could not setup controls\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
/* setup and register video device */
|
|
|
|
dev->video_dev = video_device_alloc();
|
|
|
|
if (!dev->video_dev) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
|
2013-03-19 20:30:50 +08:00
|
|
|
res = -ENOMEM;
|
2009-03-19 05:10:04 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2013-04-06 17:00:17 +08:00
|
|
|
*dev->video_dev = hdpvr_video_template;
|
2009-03-19 05:10:04 +08:00
|
|
|
strcpy(dev->video_dev->name, "Hauppauge HD PVR");
|
2013-04-06 17:00:17 +08:00
|
|
|
dev->video_dev->v4l2_dev = &dev->v4l2_dev;
|
2009-03-19 05:10:04 +08:00
|
|
|
video_set_drvdata(dev->video_dev, dev);
|
2013-02-12 19:26:59 +08:00
|
|
|
set_bit(V4L2_FL_USE_FH_PRIO, &dev->video_dev->flags);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
res = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum);
|
|
|
|
if (res < 0) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_handler_free(hdl);
|
|
|
|
return res;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|