From 5b0fa4fff17d944b6a0d4cb35d8e8b5ad0ac3669 Mon Sep 17 00:00:00 2001
From: Oliver Endriss <o.endriss@gmx.de>
Date: Mon, 9 Jan 2006 18:21:37 -0200
Subject: [PATCH] V4L/DVB (3325): WSS output interface for av7110

- Implemented v4l2 api for sliced vbi data output
to pass WSS data from userspace to the av7110

Signed-off-by: Oliver Endriss <o.endriss@gmx.de>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
---
 drivers/media/common/saa7146_fops.c  |  37 ++++++++-
 drivers/media/dvb/ttpci/av7110.h     |   3 +
 drivers/media/dvb/ttpci/av7110_hw.h  |   3 +-
 drivers/media/dvb/ttpci/av7110_v4l.c | 116 +++++++++++++++++++++++++--
 include/media/saa7146_vv.h           |   2 +
 5 files changed, 149 insertions(+), 12 deletions(-)

diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index a9ff5b5af1cf..b614612be7b4 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -253,7 +253,10 @@ static int fops_open(struct inode *inode, struct file *file)
 
 	if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
 		DEB_S(("initializing vbi...\n"));
-		result = saa7146_vbi_uops.open(dev,file);
+		if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+			result = saa7146_vbi_uops.open(dev,file);
+		if (dev->ext_vv_data->vbi_fops.open)
+			dev->ext_vv_data->vbi_fops.open(inode, file);
 	} else {
 		DEB_S(("initializing video...\n"));
 		result = saa7146_video_uops.open(dev,file);
@@ -289,7 +292,10 @@ static int fops_release(struct inode *inode, struct file *file)
 		return -ERESTARTSYS;
 
 	if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-		saa7146_vbi_uops.release(dev,file);
+		if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+			saa7146_vbi_uops.release(dev,file);
+		if (dev->ext_vv_data->vbi_fops.release)
+			dev->ext_vv_data->vbi_fops.release(inode, file);
 	} else {
 		saa7146_video_uops.release(dev,file);
 	}
@@ -382,7 +388,10 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof
 		}
 	case V4L2_BUF_TYPE_VBI_CAPTURE: {
 //		DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
-		return saa7146_vbi_uops.read(file,data,count,ppos);
+		if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+			return saa7146_vbi_uops.read(file,data,count,ppos);
+		else
+			return -EINVAL;
 		}
 		break;
 	default:
@@ -391,12 +400,31 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof
 	}
 }
 
+static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+	struct saa7146_fh *fh = file->private_data;
+
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return -EINVAL;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		if (fh->dev->ext_vv_data->vbi_fops.write)
+			return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
+		else
+			return -EINVAL;
+	default:
+		BUG();
+		return -EINVAL;
+	}
+}
+
 static struct file_operations video_fops =
 {
 	.owner		= THIS_MODULE,
 	.open		= fops_open,
 	.release	= fops_release,
 	.read		= fops_read,
+	.write		= fops_write,
 	.poll		= fops_poll,
 	.mmap		= fops_mmap,
 	.ioctl		= fops_ioctl,
@@ -468,7 +496,8 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
 	memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
 
 	saa7146_video_uops.init(dev,vv);
-	saa7146_vbi_uops.init(dev,vv);
+	if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+		saa7146_vbi_uops.init(dev,vv);
 
 	dev->vv_data = vv;
 	dev->vv_callback = &vv_callback;
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
index 6cf395e01d34..6ea30df2e823 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -229,6 +229,9 @@ struct av7110 {
 	struct dvb_video_events  video_events;
 	video_size_t		 video_size;
 
+	u16			wssMode;
+	u16			wssData;
+
 	u32			ir_config;
 	u32			ir_command;
 	void			(*ir_handler)(struct av7110 *av7110, u32 ircom);
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h
index 2a5e87ba1052..84b83299b8be 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.h
+++ b/drivers/media/dvb/ttpci/av7110_hw.h
@@ -167,7 +167,8 @@ enum av7110_encoder_command {
 	LoadVidCode,
 	SetMonitorType,
 	SetPanScanType,
-	SetFreezeMode
+	SetFreezeMode,
+	SetWSSConfig
 };
 
 enum av7110_rec_play_state {
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index e3296b07caac..94cf38c7e8a8 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -490,6 +490,58 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
 		dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
 		break;
 	}
+	case VIDIOC_G_SLICED_VBI_CAP:
+	{
+		struct v4l2_sliced_vbi_cap *cap = arg;
+		dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
+		memset(cap, 0, sizeof *cap);
+		if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+			cap->service_set = V4L2_SLICED_WSS_625;
+			cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+		}
+		break;
+	}
+	case VIDIOC_G_FMT:
+	{
+		struct v4l2_format *f = arg;
+		dprintk(2, "VIDIOC_G_FMT:\n");
+		if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ||
+		    FW_VERSION(av7110->arm_app) < 0x2623)
+			return -EAGAIN; /* handled by core driver */
+		memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+		if (av7110->wssMode) {
+			f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+			f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+			f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data);
+		}
+		break;
+	}
+	case VIDIOC_S_FMT:
+	{
+		struct v4l2_format *f = arg;
+		dprintk(2, "VIDIOC_S_FMT\n");
+		if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ||
+		    FW_VERSION(av7110->arm_app) < 0x2623)
+			return -EAGAIN; /* handled by core driver */
+		if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
+		    f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
+			memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+			/* WSS controlled by firmware */
+			av7110->wssMode = 0;
+			av7110->wssData = 0;
+			return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+					     SetWSSConfig, 1, 0);
+		} else {
+			memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+			f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+			f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+			f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data);
+			/* WSS controlled by userspace */
+			av7110->wssMode = 1;
+			av7110->wssData = 0;
+		}
+		break;
+	}
 	default:
 		printk("no such ioctl\n");
 		return -ENOIOCTLCMD;
@@ -497,6 +549,46 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
 	return 0;
 }
 
+static int av7110_vbi_reset(struct inode *inode, struct file *file)
+{
+	struct saa7146_fh *fh = file->private_data;
+	struct saa7146_dev *dev = fh->dev;
+	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+
+	dprintk(2, "%s\n", __FUNCTION__);
+	av7110->wssMode = 0;
+	av7110->wssData = 0;
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		return 0;
+	else
+		return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);
+}
+
+static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+	struct saa7146_fh *fh = file->private_data;
+	struct saa7146_dev *dev = fh->dev;
+	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+	struct v4l2_sliced_vbi_data d;
+	int rc;
+
+	dprintk(2, "%s\n", __FUNCTION__);
+	if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d)
+		return -EINVAL;
+	if (copy_from_user(&d, data, count))
+		return -EFAULT;
+	if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23)
+		return -EINVAL;
+	if (d.id) {
+		av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0];
+		rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig,
+				   2, 1, av7110->wssData);
+	} else {
+		av7110->wssData = 0;
+		rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);
+	}
+	return (rc < 0) ? rc : count;
+}
 
 /****************************************************************************
  * INITIALIZATION
@@ -512,6 +604,9 @@ static struct saa7146_extension_ioctls ioctls[] = {
 	{ VIDIOC_S_TUNER,	SAA7146_EXCLUSIVE },
 	{ VIDIOC_G_AUDIO,	SAA7146_EXCLUSIVE },
 	{ VIDIOC_S_AUDIO,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_G_SLICED_VBI_CAP, SAA7146_EXCLUSIVE },
+	{ VIDIOC_G_FMT,		SAA7146_BEFORE },
+	{ VIDIOC_S_FMT,		SAA7146_BEFORE },
 	{ 0, 0 }
 };
 
@@ -692,12 +787,11 @@ int av7110_init_v4l(struct av7110 *av7110)
 		saa7146_vv_release(dev);
 		return -ENODEV;
 	}
-	if (av7110->analog_tuner_flags) {
-		if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) {
-			ERR(("cannot register vbi v4l2 device. skipping.\n"));
-		} else {
+	if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) {
+		ERR(("cannot register vbi v4l2 device. skipping.\n"));
+	} else {
+		if (av7110->analog_tuner_flags)
 			av7110->analog_tuner_flags |= ANALOG_TUNER_VBI;
-		}
 	}
 	return 0;
 }
@@ -778,7 +872,7 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
 static struct saa7146_ext_vv av7110_vv_data_st = {
 	.inputs		= 1,
 	.audios		= 1,
-	.capabilities	= 0,
+	.capabilities	= V4L2_CAP_SLICED_VBI_OUTPUT,
 	.flags		= 0,
 
 	.stds		= &standard[0],
@@ -787,12 +881,16 @@ static struct saa7146_ext_vv av7110_vv_data_st = {
 
 	.ioctls		= &ioctls[0],
 	.ioctl		= av7110_ioctl,
+
+	.vbi_fops.open	= av7110_vbi_reset,
+	.vbi_fops.release = av7110_vbi_reset,
+	.vbi_fops.write	= av7110_vbi_write,
 };
 
 static struct saa7146_ext_vv av7110_vv_data_c = {
 	.inputs		= 1,
 	.audios		= 1,
-	.capabilities	= V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE,
+	.capabilities	= V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT,
 	.flags		= SAA7146_USE_PORT_B_FOR_VBI,
 
 	.stds		= &standard[0],
@@ -801,5 +899,9 @@ static struct saa7146_ext_vv av7110_vv_data_c = {
 
 	.ioctls		= &ioctls[0],
 	.ioctl		= av7110_ioctl,
+
+	.vbi_fops.open	= av7110_vbi_reset,
+	.vbi_fops.release = av7110_vbi_reset,
+	.vbi_fops.write	= av7110_vbi_write,
 };
 
diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h
index 16af9299315f..e5e749e984ee 100644
--- a/include/media/saa7146_vv.h
+++ b/include/media/saa7146_vv.h
@@ -178,6 +178,8 @@ struct saa7146_ext_vv
 
 	struct saa7146_extension_ioctls *ioctls;
 	int (*ioctl)(struct saa7146_fh*, unsigned int cmd, void *arg);
+
+	struct file_operations vbi_fops;
 };
 
 struct saa7146_use_ops  {
-- 
GitLab