* [REVIEW PATCH 00/19] solo6x10: driver overhaul
@ 2013-03-18 12:31 Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo Hans Verkuil
` (18 more replies)
0 siblings, 19 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:31 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno
Hi all,
This is my second attempt. This time I sync to the latest code from Bluecherry
first before applying my patches.
Remaining open issues:
1) Most importantly, the video from video0 is broken: it is supposed to be
either one of the four inputs or a 2x2 image of all four inputs, instead I
always get the first video line of the input repeated for the whole image.
I have no idea why and it would be very nice if someone from Bluecherry
can look at this. I do not see anything wrong in the DMA code, so it is
a mystery to me.
2) I couldn't get it to work on a big-endian system. Perhaps it is because
the PCI card is sitting in a PCIe slot using a PCIe-to-PCI adapter.
3) The 'extended streams' have been disabled. Basically you can get two
encoded streams out of the box: each with different encoder settings
(e.g. high and low bitrates). This should probably be implemented as
an extra video node.
4) There is a custom extension for motion detection. Besides adding two
private ioctls to support regional motion thresholds I left that part
otherwise unchanged as it doesn't look too bad, but I am unable to test
it properly. I've ordered a suitable CCTV camera from dealextreme, but that
will take a few weeks before I have it (dx.com is cheap, but delivery is
quite slow). I'd like to experiment a bit with this.
5) The tw28* 'drivers' should really be split off as subdevice drivers.
6) Capture of video0 is now using dma-contig. It used to be dma-sg, but due
to locking issues Bluecherry changed it to dma-contig. So I've kept it
that way, but it can probably be converted back to dma-sg. But issue 1 should
be fixed first before we mess with this.
So there is still work to be done, but at least the driver is in a much better
state.
If there are no more comments, then I'll add a final patch that prefixes the
sources with 'solo6x10-' and post a pull request on Friday.
The idea is that Bluecherry will develop from this code base and keep the
kernel driver in sync.
Regards,
Hans
^ permalink raw reply [flat|nested] 22+ messages in thread
* [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-24 14:27 ` Mauro Carvalho Chehab
2013-03-18 12:32 ` [REVIEW PATCH 02/19] solo6x10: fix querycap and update driver version Hans Verkuil
` (17 subsequent siblings)
18 siblings, 1 reply; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Synced to commit e9815ac5503ae60cfbf6ff8037035de8f62e2846 from
branch next in git repository https://github.com/bluecherrydvr/solo6x10.git
Only removed some code under #if LINUX_VERSION_CODE < some-kernel-version,
renamed the driver back to solo6x10 from solo6x10-edge and removed the
unnecessary compat.h header.
Otherwise the code is identical.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Ismael Luceno <ismael.luceno@corp.bluecherry.net>
---
drivers/staging/media/solo6x10/Makefile | 2 +-
drivers/staging/media/solo6x10/TODO | 36 +-
drivers/staging/media/solo6x10/core.c | 575 ++++++++--
drivers/staging/media/solo6x10/disp.c | 127 ++-
drivers/staging/media/solo6x10/eeprom.c | 154 +++
drivers/staging/media/solo6x10/enc.c | 240 +++--
drivers/staging/media/solo6x10/g723.c | 91 +-
drivers/staging/media/solo6x10/gpio.c | 13 +-
drivers/staging/media/solo6x10/i2c.c | 26 +-
drivers/staging/media/solo6x10/offsets.h | 79 +-
drivers/staging/media/solo6x10/osd-font.h | 154 ---
drivers/staging/media/solo6x10/p2m.c | 394 +++----
drivers/staging/media/solo6x10/registers.h | 86 +-
drivers/staging/media/solo6x10/solo6x10-jpeg.h | 94 +-
drivers/staging/media/solo6x10/solo6x10.h | 196 ++--
drivers/staging/media/solo6x10/tw28.c | 171 +--
drivers/staging/media/solo6x10/tw28.h | 11 +-
drivers/staging/media/solo6x10/v4l2-enc.c | 1370 ++++++++++++------------
drivers/staging/media/solo6x10/v4l2.c | 290 ++---
19 files changed, 2373 insertions(+), 1736 deletions(-)
create mode 100644 drivers/staging/media/solo6x10/eeprom.c
delete mode 100644 drivers/staging/media/solo6x10/osd-font.h
diff --git a/drivers/staging/media/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile
index 337e38c..9bbde29 100644
--- a/drivers/staging/media/solo6x10/Makefile
+++ b/drivers/staging/media/solo6x10/Makefile
@@ -1,3 +1,3 @@
-solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o
+solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o eeprom.o
obj-$(CONFIG_SOLO6X10) += solo6x10.o
diff --git a/drivers/staging/media/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO
index 539f739..aca9998 100644
--- a/drivers/staging/media/solo6x10/TODO
+++ b/drivers/staging/media/solo6x10/TODO
@@ -1,24 +1,12 @@
-TODO (staging => main):
-
- * Motion detection flags need to be moved to v4l2
- * Some private CIDs need to be moved to v4l2
-
-TODO (general):
-
- * encoder on/off controls
- * mpeg cid bitrate mode (vbr/cbr)
- * mpeg cid bitrate/bitrate-peak
- * mpeg encode of user data
- * mpeg decode of user data
- * switch between 4 frames/irq to 1 when using mjpeg (and then back
- when not)
- * implement a CID control for motion areas/thresholds
- * implement CID controls for mozaic areas
- * allow for higher level of interval (for < 1 fps)
- * sound:
- - implement playback via external sound jack
- - implement loopback of external sound jack with incoming audio?
- - implement pause/resume
-
-Plase send patches to Mauro Carvalho Chehab <mchehab@redhat.com> and Cc Ben Collins
-<bcollins@bluecherry.net>
+- batch up desc requests for more efficient use of p2m?
+- encoder on/off controls
+- mpeg cid bitrate mode (vbr/cbr)
+- mpeg cid bitrate/bitrate-peak
+- mpeg encode of user data
+- mpeg decode of user data
+- implement CID controls for mozaic areas
+
+- sound
+ - implement playback via external sound jack
+ - implement loopback of external sound jack with incoming audio?
+ - implement pause/resume (make use of in bc-server)
diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index fd83d6d..27ae75a 100644
--- a/drivers/staging/media/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -21,29 +26,78 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
-#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/ktime.h>
+
#include "solo6x10.h"
#include "tw28.h"
-MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver");
-MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
+MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver");
+MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>");
MODULE_VERSION(SOLO6X10_VERSION);
MODULE_LICENSE("GPL");
-void solo_irq_on(struct solo_dev *solo_dev, u32 mask)
+unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)");
+
+static int full_eeprom; /* default is only top 64B */
+module_param(full_eeprom, uint, 0644);
+MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)");
+
+
+static void solo_set_time(struct solo_dev *solo_dev)
{
- solo_dev->irq_mask |= mask;
- solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
+ struct timespec ts;
+
+ ktime_get_ts(&ts);
+
+ solo_reg_write(solo_dev, SOLO_TIMER_SEC, ts.tv_sec);
+ solo_reg_write(solo_dev, SOLO_TIMER_USEC, ts.tv_nsec / NSEC_PER_USEC);
}
-void solo_irq_off(struct solo_dev *solo_dev, u32 mask)
+static void solo_timer_sync(struct solo_dev *solo_dev)
{
- solo_dev->irq_mask &= ~mask;
- solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
+ u32 sec, usec;
+ struct timespec ts;
+ long diff;
+
+ if (solo_dev->type != SOLO_DEV_6110)
+ return;
+
+ if (++solo_dev->time_sync < 60)
+ return;
+
+ solo_dev->time_sync = 0;
+
+ sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC);
+ usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC);
+
+ ktime_get_ts(&ts);
+
+ diff = (long)ts.tv_sec - (long)sec;
+ diff = (diff * 1000000)
+ + ((long)(ts.tv_nsec / NSEC_PER_USEC) - (long)usec);
+
+ if (diff > 1000 || diff < -1000) {
+ solo_set_time(solo_dev);
+ } else if (diff) {
+ long usec_lsb = solo_dev->usec_lsb;
+
+ usec_lsb -= diff / 4;
+ if (usec_lsb < 0)
+ usec_lsb = 0;
+ else if (usec_lsb > 255)
+ usec_lsb = 255;
+
+ solo_dev->usec_lsb = usec_lsb;
+ solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB,
+ solo_dev->usec_lsb);
+ }
}
-/* XXX We should check the return value of the sub-device ISR's */
static irqreturn_t solo_isr(int irq, void *data)
{
struct solo_dev *solo_dev = data;
@@ -60,11 +114,8 @@ static irqreturn_t solo_isr(int irq, void *data)
status &= solo_dev->irq_mask;
}
- if (status & SOLO_IRQ_PCI_ERR) {
- u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR);
- solo_p2m_error_isr(solo_dev, err);
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR);
- }
+ if (status & SOLO_IRQ_PCI_ERR)
+ solo_p2m_error_isr(solo_dev);
for (i = 0; i < SOLO_NR_P2M; i++)
if (status & SOLO_IRQ_P2M(i))
@@ -73,12 +124,10 @@ static irqreturn_t solo_isr(int irq, void *data)
if (status & SOLO_IRQ_IIC)
solo_i2c_isr(solo_dev);
- if (status & SOLO_IRQ_VIDEO_IN)
+ if (status & SOLO_IRQ_VIDEO_IN) {
solo_video_in_isr(solo_dev);
-
- /* Call this first so enc gets detected flag set */
- if (status & SOLO_IRQ_MOTION)
- solo_motion_isr(solo_dev);
+ solo_timer_sync(solo_dev);
+ }
if (status & SOLO_IRQ_ENCODER)
solo_enc_v4l2_isr(solo_dev);
@@ -86,6 +135,9 @@ static irqreturn_t solo_isr(int irq, void *data)
if (status & SOLO_IRQ_G723)
solo_g723_isr(solo_dev);
+ /* Clear all interrupts handled */
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
+
return IRQ_HANDLED;
}
@@ -96,6 +148,9 @@ static void free_solo_dev(struct solo_dev *solo_dev)
if (!solo_dev)
return;
+ if (solo_dev->dev.parent)
+ device_unregister(&solo_dev->dev);
+
pdev = solo_dev->pdev;
/* If we never initialized the PCI device, then nothing else
@@ -105,21 +160,22 @@ static void free_solo_dev(struct solo_dev *solo_dev)
return;
}
- /* Bring down the sub-devices first */
- solo_g723_exit(solo_dev);
- solo_enc_v4l2_exit(solo_dev);
- solo_enc_exit(solo_dev);
- solo_v4l2_exit(solo_dev);
- solo_disp_exit(solo_dev);
- solo_gpio_exit(solo_dev);
- solo_p2m_exit(solo_dev);
- solo_i2c_exit(solo_dev);
-
- /* Now cleanup the PCI device */
if (solo_dev->reg_base) {
+ /* Bring down the sub-devices first */
+ solo_g723_exit(solo_dev);
+ solo_enc_v4l2_exit(solo_dev);
+ solo_enc_exit(solo_dev);
+ solo_v4l2_exit(solo_dev);
+ solo_disp_exit(solo_dev);
+ solo_gpio_exit(solo_dev);
+ solo_p2m_exit(solo_dev);
+ solo_i2c_exit(solo_dev);
+
+ /* Now cleanup the PCI device */
solo_irq_off(solo_dev, ~0);
pci_iounmap(pdev, solo_dev->reg_base);
- free_irq(pdev->irq, solo_dev);
+ if (pdev->irq)
+ free_irq(pdev->irq, solo_dev);
}
pci_release_regions(pdev);
@@ -129,29 +185,346 @@ static void free_solo_dev(struct solo_dev *solo_dev)
kfree(solo_dev);
}
-static int solo_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ unsigned short *p = (unsigned short *)buf;
+ int i;
+
+ if (count & 0x1)
+ dev_warn(dev, "EEPROM Write not aligned (truncating)\n");
+
+ if (!full_eeprom && count > 64) {
+ dev_warn(dev, "EEPROM Write truncated to 64 bytes\n");
+ count = 64;
+ } else if (full_eeprom && count > 128) {
+ dev_warn(dev, "EEPROM Write truncated to 128 bytes\n");
+ count = 128;
+ }
+
+ solo_eeprom_ewen(solo_dev, 1);
+
+ for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32),
+ (int)(count / 2)); i++)
+ solo_eeprom_write(solo_dev, i, cpu_to_be16(p[i]));
+
+ solo_eeprom_ewen(solo_dev, 0);
+
+ return count;
+}
+
+static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ unsigned short *p = (unsigned short *)buf;
+ int count = (full_eeprom ? 128 : 64);
+ int i;
+
+ for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++)
+ p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i));
+
+ return count;
+}
+
+static ssize_t video_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+static ssize_t video_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+
+ return sprintf(buf, "%s", solo_dev->video_type ==
+ SOLO_VO_FMT_TYPE_NTSC ? "NTSC" : "PAL");
+}
+
+static ssize_t p2m_timeouts_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+
+ return sprintf(buf, "%d\n", solo_dev->p2m_timeouts);
+}
+
+static ssize_t sdram_size_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+
+ return sprintf(buf, "%dMegs\n", solo_dev->sdram_size >> 20);
+}
+
+static ssize_t tw28xx_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+
+ return sprintf(buf, "tw2815[%d] tw2864[%d] tw2865[%d]\n",
+ hweight32(solo_dev->tw2815),
+ hweight32(solo_dev->tw2864),
+ hweight32(solo_dev->tw2865));
+}
+
+static ssize_t input_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ unsigned int val;
+ char *out = buf;
+
+ val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0);
+ out += sprintf(out, "Channel 0 => Input %d\n", val & 0x1f);
+ out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f);
+ out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f);
+ out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f);
+ out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f);
+ out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f);
+
+ val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1);
+ out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f);
+ out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f);
+ out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f);
+ out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f);
+ out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f);
+ out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f);
+
+ val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2);
+ out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f);
+ out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f);
+ out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f);
+ out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f);
+ out += sprintf(out, "Spot Output => Input %d\n", (val >> 20) & 0x1f);
+
+ return out - buf;
+}
+
+static ssize_t p2m_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ unsigned long ms;
+
+ int ret = kstrtoul(buf, 10, &ms);
+ if (ret < 0 || ms > 200)
+ return -EINVAL;
+ solo_dev->p2m_jiffies = msecs_to_jiffies(ms);
+
+ return count;
+}
+
+static ssize_t p2m_timeout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+
+ return sprintf(buf, "%ums\n", jiffies_to_msecs(solo_dev->p2m_jiffies));
+}
+
+static ssize_t intervals_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ char *out = buf;
+ int fps = solo_dev->fps;
+ int i;
+
+ for (i = 0; i < solo_dev->nr_chans; i++) {
+ out += sprintf(out, "Channel %d: %d/%d (0x%08x)\n",
+ i, solo_dev->v4l2_enc[i]->interval, fps,
+ solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i)));
+ }
+
+ return out - buf;
+}
+
+static ssize_t sdram_offsets_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ char *out = buf;
+
+ out += sprintf(out, "DISP: 0x%08x @ 0x%08x\n",
+ SOLO_DISP_EXT_ADDR,
+ SOLO_DISP_EXT_SIZE);
+
+ out += sprintf(out, "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n",
+ SOLO_EOSD_EXT_ADDR,
+ SOLO_EOSD_EXT_AREA(solo_dev),
+ SOLO_EOSD_EXT_SIZE(solo_dev),
+ SOLO_EOSD_EXT_AREA(solo_dev) /
+ SOLO_EOSD_EXT_SIZE(solo_dev));
+
+ out += sprintf(out, "MOTI: 0x%08x @ 0x%08x\n",
+ SOLO_MOTION_EXT_ADDR(solo_dev),
+ SOLO_MOTION_EXT_SIZE);
+
+ out += sprintf(out, "G723: 0x%08x @ 0x%08x\n",
+ SOLO_G723_EXT_ADDR(solo_dev),
+ SOLO_G723_EXT_SIZE);
+
+ out += sprintf(out, "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n",
+ SOLO_CAP_EXT_ADDR(solo_dev),
+ SOLO_CAP_EXT_SIZE(solo_dev),
+ SOLO_CAP_PAGE_SIZE,
+ SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE);
+
+ out += sprintf(out, "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n",
+ SOLO_EREF_EXT_ADDR(solo_dev),
+ SOLO_EREF_EXT_AREA(solo_dev),
+ SOLO_EREF_EXT_SIZE,
+ SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE);
+
+ out += sprintf(out, "MPEG: 0x%08x @ 0x%08x\n",
+ SOLO_MP4E_EXT_ADDR(solo_dev),
+ SOLO_MP4E_EXT_SIZE(solo_dev));
+
+ out += sprintf(out, "JPEG: 0x%08x @ 0x%08x\n",
+ SOLO_JPEG_EXT_ADDR(solo_dev),
+ SOLO_JPEG_EXT_SIZE(solo_dev));
+
+ return out - buf;
+}
+
+static ssize_t sdram_show(struct file *file, struct kobject *kobj,
+ struct bin_attribute *a, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct solo_dev *solo_dev =
+ container_of(dev, struct solo_dev, dev);
+ const int size = solo_dev->sdram_size;
+
+ if (off >= size)
+ return 0;
+
+ if (off + count > size)
+ count = size - off;
+
+ if (solo_p2m_dma(solo_dev, 0, buf, off, count, 0, 0))
+ return -EIO;
+
+ return count;
+}
+
+static const struct device_attribute solo_dev_attrs[] = {
+ __ATTR(eeprom, 0640, eeprom_show, eeprom_store),
+ __ATTR(video_type, 0644, video_type_show, video_type_store),
+ __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store),
+ __ATTR_RO(p2m_timeouts),
+ __ATTR_RO(sdram_size),
+ __ATTR_RO(tw28xx),
+ __ATTR_RO(input_map),
+ __ATTR_RO(intervals),
+ __ATTR_RO(sdram_offsets),
+};
+
+static void solo_device_release(struct device *dev)
+{
+ /* Do nothing */
+}
+
+static int solo_sysfs_init(struct solo_dev *solo_dev)
+{
+ struct bin_attribute *sdram_attr = &solo_dev->sdram_attr;
+ struct device *dev = &solo_dev->dev;
+ const char *driver;
+ int i;
+
+ if (solo_dev->type == SOLO_DEV_6110)
+ driver = "solo6110";
+ else
+ driver = "solo6010";
+
+ dev->release = solo_device_release;
+ dev->parent = &solo_dev->pdev->dev;
+ set_dev_node(dev, dev_to_node(&solo_dev->pdev->dev));
+ dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num,
+ solo_dev->nr_chans);
+
+ if (device_register(dev)) {
+ dev->parent = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) {
+ if (device_create_file(dev, &solo_dev_attrs[i])) {
+ device_unregister(dev);
+ return -ENOMEM;
+ }
+ }
+
+ sdram_attr->attr.name = "sdram";
+ sdram_attr->attr.mode = 0440;
+ sdram_attr->read = sdram_show;
+ sdram_attr->size = solo_dev->sdram_size;
+
+ if (device_create_bin_file(dev, sdram_attr)) {
+ device_unregister(dev);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct solo_dev *solo_dev;
int ret;
- int sdram;
u8 chip_id;
- u32 reg;
solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL);
if (solo_dev == NULL)
return -ENOMEM;
+ if (id->driver_data == SOLO_DEV_6010)
+ dev_info(&pdev->dev, "Probing Softlogic 6010\n");
+ else
+ dev_info(&pdev->dev, "Probing Softlogic 6110\n");
+
+ solo_dev->type = id->driver_data;
solo_dev->pdev = pdev;
spin_lock_init(&solo_dev->reg_io_lock);
pci_set_drvdata(pdev, solo_dev);
+ /* Only for during init */
+ solo_dev->p2m_jiffies = msecs_to_jiffies(100);
+
ret = pci_enable_device(pdev);
if (ret)
goto fail_probe;
pci_set_master(pdev);
+ /* RETRY/TRDY Timeout disabled */
+ pci_write_config_byte(pdev, 0x40, 0x00);
+ pci_write_config_byte(pdev, 0x41, 0x00);
+
ret = pci_request_regions(pdev, SOLO6X10_NAME);
if (ret)
goto fail_probe;
@@ -163,7 +536,7 @@ static int solo_pci_probe(struct pci_dev *pdev,
}
chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) &
- SOLO_CHIP_ID_MASK;
+ SOLO_CHIP_ID_MASK;
switch (chip_id) {
case 7:
solo_dev->nr_chans = 16;
@@ -174,52 +547,50 @@ static int solo_pci_probe(struct pci_dev *pdev,
solo_dev->nr_ext = 2;
break;
default:
- dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, "
- "defaulting to 4 channels\n",
+ dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n",
chip_id);
case 5:
solo_dev->nr_chans = 4;
solo_dev->nr_ext = 1;
}
- solo_dev->flags = id->driver_data;
-
/* Disable all interrupts to start */
solo_irq_off(solo_dev, ~0);
- reg = SOLO_SYS_CFG_SDRAM64BIT;
/* Initial global settings */
- if (!(solo_dev->flags & FLAGS_6110))
- reg |= SOLO6010_SYS_CFG_INPUTDIV(25) |
- SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) |
- SOLO6010_SYS_CFG_OUTDIV(3);
- solo_reg_write(solo_dev, SOLO_SYS_CFG, reg);
-
- if (solo_dev->flags & FLAGS_6110) {
- u32 sys_clock_MHz = SOLO_CLOCK_MHZ;
- u32 pll_DIVQ;
- u32 pll_DIVF;
-
- if (sys_clock_MHz < 125) {
- pll_DIVQ = 3;
- pll_DIVF = (sys_clock_MHz * 4) / 3;
+ if (solo_dev->type == SOLO_DEV_6010) {
+ solo_dev->clock_mhz = 108;
+ solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT
+ | SOLO_SYS_CFG_INPUTDIV(25)
+ | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2)
+ | SOLO_SYS_CFG_OUTDIV(3);
+ solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config);
+ } else {
+ u32 divq, divf;
+
+ solo_dev->clock_mhz = 135;
+
+ if (solo_dev->clock_mhz < 125) {
+ divq = 3;
+ divf = (solo_dev->clock_mhz * 4) / 3 - 1;
} else {
- pll_DIVQ = 2;
- pll_DIVF = (sys_clock_MHz * 2) / 3;
+ divq = 2;
+ divf = (solo_dev->clock_mhz * 2) / 3 - 1;
}
- solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG,
- SOLO6110_PLL_RANGE_5_10MHZ |
- SOLO6110_PLL_DIVR(9) |
- SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) |
- SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN);
- mdelay(1); /* PLL Locking time (1ms) */
+ solo_reg_write(solo_dev, SOLO_PLL_CONFIG,
+ (1 << 20) | /* PLL_RANGE */
+ (8 << 15) | /* PLL_DIVR */
+ (divq << 12) |
+ (divf << 4) |
+ (1 << 1) /* PLL_FSEN */);
- solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */
- } else
- solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */
+ solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT;
+ }
- solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1);
+ solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config);
+ solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM,
+ solo_dev->clock_mhz - 1);
/* PLL locking time of 1ms */
mdelay(1);
@@ -237,14 +608,27 @@ static int solo_pci_probe(struct pci_dev *pdev,
goto fail_probe;
/* Setup the DMA engine */
- sdram = (solo_dev->nr_chans >= 8) ? 2 : 1;
solo_reg_write(solo_dev, SOLO_DMA_CTRL,
SOLO_DMA_CTRL_REFRESH_CYCLE(1) |
- SOLO_DMA_CTRL_SDRAM_SIZE(sdram) |
+ SOLO_DMA_CTRL_SDRAM_SIZE(2) |
SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
SOLO_DMA_CTRL_READ_CLK_SELECT |
SOLO_DMA_CTRL_LATENCY(1));
+ /* Undocumented crap */
+ solo_reg_write(solo_dev, SOLO_DMA_CTRL1,
+ solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300);
+
+ if (solo_dev->type != SOLO_DEV_6010) {
+ solo_dev->usec_lsb = 0x3f;
+ solo_set_time(solo_dev);
+ }
+
+ /* Disable watchdog */
+ solo_reg_write(solo_dev, SOLO_WATCHDOG, 0);
+
+ /* Initialize sub components */
+
ret = solo_p2m_init(solo_dev);
if (ret)
goto fail_probe;
@@ -261,7 +645,7 @@ static int solo_pci_probe(struct pci_dev *pdev,
if (ret)
goto fail_probe;
- ret = solo_v4l2_init(solo_dev);
+ ret = solo_v4l2_init(solo_dev, video_nr);
if (ret)
goto fail_probe;
@@ -269,7 +653,7 @@ static int solo_pci_probe(struct pci_dev *pdev,
if (ret)
goto fail_probe;
- ret = solo_enc_v4l2_init(solo_dev);
+ ret = solo_enc_v4l2_init(solo_dev, video_nr);
if (ret)
goto fail_probe;
@@ -277,6 +661,13 @@ static int solo_pci_probe(struct pci_dev *pdev,
if (ret)
goto fail_probe;
+ ret = solo_sysfs_init(solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ /* Now that init is over, set this lower */
+ solo_dev->p2m_jiffies = msecs_to_jiffies(20);
+
return 0;
fail_probe:
@@ -291,21 +682,31 @@ static void solo_pci_remove(struct pci_dev *pdev)
free_solo_dev(solo_dev);
}
-static struct pci_device_id solo_id_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(solo_id_table) = {
/* 6010 based cards */
- {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)},
- {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110),
- .driver_data = FLAGS_6110},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)},
+ { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010),
+ .driver_data = SOLO_DEV_6010 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4),
+ .driver_data = SOLO_DEV_6010 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9),
+ .driver_data = SOLO_DEV_6010 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16),
+ .driver_data = SOLO_DEV_6010 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4),
+ .driver_data = SOLO_DEV_6010 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9),
+ .driver_data = SOLO_DEV_6010 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16),
+ .driver_data = SOLO_DEV_6010 },
/* 6110 based cards */
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)},
+ { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110),
+ .driver_data = SOLO_DEV_6110 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4),
+ .driver_data = SOLO_DEV_6110 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8),
+ .driver_data = SOLO_DEV_6110 },
+ { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16),
+ .driver_data = SOLO_DEV_6110 },
{0,}
};
diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c
index 884c0eb..ddd85e7 100644
--- a/drivers/staging/media/solo6x10/disp.c
+++ b/drivers/staging/media/solo6x10/disp.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -21,6 +26,7 @@
#include <linux/module.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
+
#include "solo6x10.h"
#define SOLO_VCLK_DELAY 3
@@ -30,8 +36,8 @@
#define SOLO_MOT_THRESH_H 64
#define SOLO_MOT_THRESH_SIZE 8192
#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H)
-#define SOLO_MOT_FLAG_SIZE 512
-#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32)
+#define SOLO_MOT_FLAG_SIZE 1024
+#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16)
static unsigned video_type;
module_param(video_type, uint, 0644);
@@ -73,7 +79,12 @@ static void solo_vin_config(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT,
SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0));
- solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0);
+ /* On 6110, initialize mozaic darkness stength */
+ if (solo_dev->type == SOLO_DEV_6010)
+ solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0);
+ else
+ solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22);
+
solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2);
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
@@ -97,21 +108,30 @@ static void solo_vin_config(struct solo_dev *solo_dev)
SOLO_VI_PB_HSTOP(16 + 720));
}
-static void solo_disp_config(struct solo_dev *solo_dev)
+static void solo_vout_config_cursor(struct solo_dev *dev)
+{
+ int i;
+
+ /* Load (blank) cursor bitmap mask (2bpp) */
+ for (i = 0; i < 20; i++)
+ solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0);
+
+ solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0);
+
+ solo_reg_write(dev, SOLO_VO_CURSOR_CLR,
+ (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80);
+ solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80);
+}
+
+static void solo_vout_config(struct solo_dev *solo_dev)
{
solo_dev->vout_hstart = 6;
solo_dev->vout_vstart = 8;
- solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR,
- (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88);
- solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR,
- (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f);
- solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR,
- (16 << 24) | (128 << 16) | (16 << 8) | 128);
-
solo_reg_write(solo_dev, SOLO_VO_FMT_ENC,
solo_dev->video_type |
SOLO_VO_USER_COLOR_SET_NAV |
+ SOLO_VO_USER_COLOR_SET_NAH |
SOLO_VO_NA_COLOR_Y(0) |
SOLO_VO_NA_COLOR_CB(0) |
SOLO_VO_NA_COLOR_CR(0));
@@ -130,19 +150,31 @@ static void solo_disp_config(struct solo_dev *solo_dev)
SOLO_VO_H_LEN(solo_dev->video_hsize) |
SOLO_VO_V_LEN(solo_dev->video_vsize));
- solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5);
+ /* Border & background colors */
+ solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR,
+ (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88);
+ solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR,
+ (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f);
+ solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR,
+ (16 << 24) | (128 << 16) | (16 << 8) | 128);
+
+ solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
+
+ solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0);
+
+ solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0);
+ solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0);
solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON |
SOLO_VO_DISP_ERASE_COUNT(8) |
SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR));
- solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
- /* Enable channels we support */
- solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1);
+ solo_vout_config_cursor(solo_dev);
- /* Disable the watchdog */
- solo_reg_write(solo_dev, SOLO_WATCHDOG, 0);
+ /* Enable channels we support */
+ solo_reg_write(solo_dev, SOLO_VI_CH_ENA,
+ (1 << solo_dev->nr_chans) - 1);
}
static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,
@@ -156,26 +188,53 @@ static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,
buf[i] = val;
for (i = 0; i < reg_size; i += sizeof(buf))
- ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf,
+ ret |= solo_p2m_dma(solo_dev, 1, buf,
SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,
- sizeof(buf));
+ sizeof(buf), 0, 0);
return ret;
}
-void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val)
+int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val)
{
if (ch > solo_dev->nr_chans)
- return;
+ return -EINVAL;
- solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
- (ch * SOLO_MOT_THRESH_SIZE * 2),
- val, SOLO_MOT_THRESH_REAL);
+ return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
+ (ch * SOLO_MOT_THRESH_SIZE * 2),
+ val, SOLO_MOT_THRESH_SIZE);
+}
+
+int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, u16 val,
+ u16 block)
+{
+ u16 buf[64];
+ u32 addr;
+ int re;
+
+ addr = SOLO_MOTION_EXT_ADDR(solo_dev) +
+ SOLO_MOT_FLAG_AREA +
+ (SOLO_MOT_THRESH_SIZE * 2 * ch) +
+ (block * 2);
+
+ /* Read and write only on a 128-byte boundary; 4-byte writes with
+ solo_p2m_dma silently failed. Bluecherry bug #908. */
+ re = solo_p2m_dma(solo_dev, 0, &buf, addr & ~0x7f, sizeof(buf), 0, 0);
+ if (re)
+ return re;
+
+ buf[(addr & 0x7f) / 2] = val;
+
+ re = solo_p2m_dma(solo_dev, 1, &buf, addr & ~0x7f, sizeof(buf), 0, 0);
+ if (re)
+ return re;
+
+ return 0;
}
/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k
* threshold and working table for each channel. Atleast that's what the
- * spec says. However, this code (take from rdk) has some mystery 8k
+ * spec says. However, this code (taken from rdk) has some mystery 8k
* block right after the flag area, before the first thresh table. */
static void solo_motion_config(struct solo_dev *solo_dev)
{
@@ -188,9 +247,9 @@ static void solo_motion_config(struct solo_dev *solo_dev)
/* Clear working cache table */
solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
- SOLO_MOT_THRESH_SIZE +
- (i * SOLO_MOT_THRESH_SIZE * 2),
- 0x0000, SOLO_MOT_THRESH_REAL);
+ (i * SOLO_MOT_THRESH_SIZE * 2) +
+ SOLO_MOT_THRESH_SIZE, 0x0000,
+ SOLO_MOT_THRESH_SIZE);
/* Set default threshold table */
solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH);
@@ -202,8 +261,8 @@ static void solo_motion_config(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL,
SOLO_VI_MOTION_FRAME_COUNT(3) |
SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16)
- | /* SOLO_VI_MOTION_INTR_START_STOP | */
- SOLO_VI_MOTION_SAMPLE_COUNT(10));
+ /* | SOLO_VI_MOTION_INTR_START_STOP */
+ | SOLO_VI_MOTION_SAMPLE_COUNT(10));
solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0);
solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0);
@@ -226,7 +285,7 @@ int solo_disp_init(struct solo_dev *solo_dev)
solo_vin_config(solo_dev);
solo_motion_config(solo_dev);
- solo_disp_config(solo_dev);
+ solo_vout_config(solo_dev);
for (i = 0; i < solo_dev->nr_chans; i++)
solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1);
@@ -238,8 +297,6 @@ void solo_disp_exit(struct solo_dev *solo_dev)
{
int i;
- solo_irq_off(solo_dev, SOLO_IRQ_MOTION);
-
solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0);
solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0);
solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0);
diff --git a/drivers/staging/media/solo6x10/eeprom.c b/drivers/staging/media/solo6x10/eeprom.c
new file mode 100644
index 0000000..9d1c9bb
--- /dev/null
+++ b/drivers/staging/media/solo6x10/eeprom.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "solo6x10.h"
+
+/* Control */
+#define EE_SHIFT_CLK 0x04
+#define EE_CS 0x08
+#define EE_DATA_WRITE 0x02
+#define EE_DATA_READ 0x01
+#define EE_ENB (0x80 | EE_CS)
+
+#define eeprom_delay() udelay(100)
+#if 0
+#define eeprom_delay() solo_reg_read(solo_dev, SOLO_EEPROM_CTRL)
+#define eeprom_delay() ({ \
+ int i, ret; \
+ udelay(100); \
+ for (i = ret = 0; i < 1000 && !ret; i++) \
+ ret = solo_eeprom_reg_read(solo_dev); \
+})
+#endif
+#define ADDR_LEN 6
+
+/* Commands */
+#define EE_EWEN_CMD 4
+#define EE_EWDS_CMD 4
+#define EE_WRITE_CMD 5
+#define EE_READ_CMD 6
+#define EE_ERASE_CMD 7
+
+static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev)
+{
+ return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ;
+}
+
+static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data)
+{
+ solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data);
+ eeprom_delay();
+}
+
+static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd)
+{
+ int i;
+
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN);
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
+
+ for (i = 4 + ADDR_LEN; i >= 0; i--) {
+ int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval);
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
+ EE_SHIFT_CLK | dataval);
+ }
+
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
+}
+
+unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en)
+{
+ int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN);
+ unsigned int retval = 0;
+ int i;
+
+ solo_eeprom_cmd(solo_dev, ewen_cmd);
+
+ for (i = 0; i < 16; i++) {
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
+ EE_SHIFT_CLK);
+ retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
+ retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
+ }
+
+ solo_eeprom_reg_write(solo_dev, ~EE_CS);
+ retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
+
+ return retval;
+}
+
+unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc)
+{
+ int read_cmd = loc | (EE_READ_CMD << ADDR_LEN);
+ unsigned short retval = 0;
+ int i;
+
+ solo_eeprom_cmd(solo_dev, read_cmd);
+
+ for (i = 0; i < 16; i++) {
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
+ EE_SHIFT_CLK);
+ retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
+ solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
+ }
+
+ solo_eeprom_reg_write(solo_dev, ~EE_CS);
+
+ return retval;
+}
+
+int solo_eeprom_write(struct solo_dev *solo_dev, int loc,
+ unsigned short data)
+{
+ int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN);
+ unsigned int retval;
+ int i;
+
+ solo_eeprom_cmd(solo_dev, write_cmd);
+
+ for (i = 15; i >= 0; i--) {
+ unsigned int dataval = (data >> i) & 1;
+
+ solo_eeprom_reg_write(solo_dev, EE_ENB);
+ solo_eeprom_reg_write(solo_dev,
+ EE_ENB | (dataval << 1) | EE_SHIFT_CLK);
+ }
+
+ solo_eeprom_reg_write(solo_dev, EE_ENB);
+ solo_eeprom_reg_write(solo_dev, ~EE_CS);
+ solo_eeprom_reg_write(solo_dev, EE_ENB);
+
+ for (i = retval = 0; i < 10000 && !retval; i++)
+ retval = solo_eeprom_reg_read(solo_dev);
+
+ solo_eeprom_reg_write(solo_dev, ~EE_CS);
+
+ return !retval;
+}
diff --git a/drivers/staging/media/solo6x10/enc.c b/drivers/staging/media/solo6x10/enc.c
index de50259..c992639 100644
--- a/drivers/staging/media/solo6x10/enc.c
+++ b/drivers/staging/media/solo6x10/enc.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -18,30 +23,40 @@
*/
#include <linux/kernel.h>
-#include <linux/slab.h>
-#include "solo6x10.h"
-#include "osd-font.h"
+#include <linux/font.h>
+#include <linux/bitrev.h>
-#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */
-#define OSG_BUFFER_SIZE 1024
+#include "solo6x10.h"
#define VI_PROG_HSIZE (1280 - 16)
#define VI_PROG_VSIZE (1024 - 16)
+#define IRQ_LEVEL 2
+
static void solo_capture_config(struct solo_dev *solo_dev)
{
- int i, j;
unsigned long height;
unsigned long width;
- unsigned char *buf;
+ void *buf;
+ int i;
solo_reg_write(solo_dev, SOLO_CAP_BASE,
- SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE *
- solo_dev->nr_chans) |
- SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16));
- solo_reg_write(solo_dev, SOLO_CAP_BTW,
- (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) |
- SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH));
+ SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev)
+ - SOLO_CAP_PAGE_SIZE) >> 16)
+ | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16));
+
+ /* XXX: Undocumented bits at b17 and b24 */
+ if (solo_dev->type == SOLO_DEV_6110) {
+ /* NOTE: Ref driver has (62 << 24) here as well, but it causes
+ * wacked out frame timing on 4-port 6110. */
+ solo_reg_write(solo_dev, SOLO_CAP_BTW,
+ (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) |
+ SOLO_CAP_MAX_BANDWIDTH(36));
+ } else {
+ solo_reg_write(solo_dev, SOLO_CAP_BTW,
+ (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) |
+ SOLO_CAP_MAX_BANDWIDTH(32));
+ }
/* Set scale 1, 9 dimension */
width = solo_dev->video_hsize;
@@ -96,115 +111,212 @@ static void solo_capture_config(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16);
solo_reg_write(solo_dev, SOLO_VE_OSD_CLR,
0xF0 << 16 | 0x80 << 8 | 0x80);
- solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0);
+
+ if (solo_dev->type == SOLO_DEV_6010)
+ solo_reg_write(solo_dev, SOLO_VE_OSD_OPT,
+ SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW);
+ else
+ solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE
+ | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW);
/* Clear OSG buffer */
- buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL);
+ buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL);
if (!buf)
return;
for (i = 0; i < solo_dev->nr_chans; i++) {
- for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) {
- solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf,
- SOLO_EOSD_EXT_ADDR +
- (i * SOLO_EOSD_EXT_SIZE) + j,
- OSG_BUFFER_SIZE);
- }
+ solo_p2m_dma(solo_dev, 1, buf,
+ SOLO_EOSD_EXT_ADDR +
+ (SOLO_EOSD_EXT_SIZE(solo_dev) * i),
+ SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0);
}
kfree(buf);
}
+/* Should be called with enable_lock held */
int solo_osd_print(struct solo_enc_dev *solo_enc)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
- char *str = solo_enc->osd_text;
- u8 *buf;
+ unsigned char *str = solo_enc->osd_text;
+ u8 *buf = solo_enc->osd_buf;
u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH);
- int len = strlen(str);
+ const struct font_desc *vga = find_font("VGA8x16");
+ const unsigned char *vga_data;
+ int len;
int i, j;
- int x = 1, y = 1;
+
+ if (WARN_ON_ONCE(!vga))
+ return -ENODEV;
+
+ len = strlen(str);
if (len == 0) {
+ /* Disable OSD on this channel */
reg &= ~(1 << solo_enc->ch);
solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
return 0;
}
- buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ memset(buf, 0, SOLO_EOSD_EXT_SIZE_MAX);
+ vga_data = (const unsigned char *)vga->data;
for (i = 0; i < len; i++) {
+ unsigned char c = str[i];
+
for (j = 0; j < 16; j++) {
- buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] =
- (solo_osd_font[(str[i] * 4) + (j / 4)]
- >> ((3 - (j % 4)) * 8)) & 0xff;
+ buf[(j * 2) + (i % 2) + (i / 2 * 32)] =
+ bitrev8(vga_data[(c * 16) + j]);
}
}
- solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR +
- (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE);
+ solo_p2m_dma(solo_dev, 1, buf,
+ SOLO_EOSD_EXT_ADDR +
+ (solo_enc->ch * SOLO_EOSD_EXT_SIZE(solo_dev)),
+ SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0);
+
+ /* Enable OSD on this channel */
reg |= (1 << solo_enc->ch);
solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
- kfree(buf);
-
return 0;
}
+/**
+ * Set channel Quality Profile (0-3).
+ */
+void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch,
+ unsigned int qp)
+{
+ unsigned long flags;
+ unsigned int idx, reg;
+
+ if ((ch > 31) || (qp > 3))
+ return;
+
+ if (solo_dev->type == SOLO_DEV_6010)
+ return;
+
+ if (ch < 16) {
+ idx = 0;
+ reg = SOLO_VE_JPEG_QP_CH_L;
+ } else {
+ ch -= 16;
+ idx = 1;
+ reg = SOLO_VE_JPEG_QP_CH_H;
+ }
+ ch *= 2;
+
+ spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags);
+
+ solo_dev->jpeg_qp[idx] &= ~(3 << ch);
+ solo_dev->jpeg_qp[idx] |= (qp & 3) << ch;
+
+ solo_reg_write(solo_dev, reg, solo_dev->jpeg_qp[idx]);
+
+ spin_unlock_irqrestore(&solo_dev->jpeg_qp_lock, flags);
+}
+
+int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch)
+{
+ int idx;
+
+ if (solo_dev->type == SOLO_DEV_6010)
+ return 2;
+
+ if (WARN_ON_ONCE(ch > 31))
+ return 2;
+
+ if (ch < 16) {
+ idx = 0;
+ } else {
+ ch -= 16;
+ idx = 1;
+ }
+ ch *= 2;
+
+ return (solo_dev->jpeg_qp[idx] >> ch) & 3;
+}
+
+#define SOLO_QP_INIT 0xaaaaaaaa
+
static void solo_jpeg_config(struct solo_dev *solo_dev)
{
- u32 reg;
- if (solo_dev->flags & FLAGS_6110)
- reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0);
- else
- reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0);
- solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg);
- solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0);
- solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0);
+ if (solo_dev->type == SOLO_DEV_6010) {
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL,
+ (2 << 24) | (2 << 16) | (2 << 8) | 2);
+ } else {
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL,
+ (4 << 24) | (3 << 16) | (2 << 8) | 1);
+ }
+
+ spin_lock_init(&solo_dev->jpeg_qp_lock);
+
+ /* Initialize Quality Profile for all channels */
+ solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT;
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT);
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT);
+
solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG,
(SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) |
((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff));
solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff);
- /* que limit, samp limit, pos limit */
- solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60);
+ if (solo_dev->type == SOLO_DEV_6110) {
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1,
+ (0 << 16) | (30 << 8) | 60);
+ }
}
static void solo_mp4e_config(struct solo_dev *solo_dev)
{
int i;
- u32 reg;
+ u32 cfg;
- /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */
solo_reg_write(solo_dev, SOLO_VE_CFG0,
- SOLO_VE_INTR_CTRL(0) |
+ SOLO_VE_INTR_CTRL(IRQ_LEVEL) |
SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) |
SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16));
- solo_reg_write(solo_dev, SOLO_VE_CFG1,
- SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0));
+
+ cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX
+ | SOLO_VE_MOTION_MODE(0);
+ if (solo_dev->type != SOLO_DEV_6010) {
+ cfg |= SOLO_VE_MPEG_SIZE_H(
+ (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f);
+ cfg |= SOLO_VE_JPEG_SIZE_H(
+ (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f);
+ }
+ solo_reg_write(solo_dev, SOLO_VE_CFG1, cfg);
solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0);
solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0);
solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0);
+ if (solo_dev->type == SOLO_DEV_6110)
+ solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, 0);
solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0);
solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0);
- reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) |
- SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15);
- if (solo_dev->flags & FLAGS_6110)
- reg |= SOLO_DCT_INTERVAL(10);
- else
- reg |= SOLO_DCT_INTERVAL(36 / 4);
- solo_reg_write(solo_dev, SOLO_VE_ATTR, reg);
+ solo_reg_write(solo_dev, SOLO_VE_ATTR,
+ SOLO_VE_LITTLE_ENDIAN |
+ SOLO_COMP_ATTR_FCODE(1) |
+ SOLO_COMP_TIME_INC(0) |
+ SOLO_COMP_TIME_WIDTH(15) |
+ SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10));
- for (i = 0; i < solo_dev->nr_chans; i++)
+ for (i = 0; i < solo_dev->nr_chans; i++) {
solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i),
(SOLO_EREF_EXT_ADDR(solo_dev) +
(i * SOLO_EREF_EXT_SIZE)) >> 16);
+ solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i),
+ (SOLO_EREF_EXT_ADDR(solo_dev) +
+ ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16);
+ }
- if (solo_dev->flags & FLAGS_6110)
- solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */
+ if (solo_dev->type == SOLO_DEV_6110) {
+ solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, 0x00040008);
+ } else {
+ for (i = 0; i < solo_dev->nr_chans; i++)
+ solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), 0x100);
+ }
}
int solo_enc_init(struct solo_dev *solo_dev)
@@ -220,8 +332,6 @@ int solo_enc_init(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0);
}
- solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
-
return 0;
}
@@ -229,8 +339,6 @@ void solo_enc_exit(struct solo_dev *solo_dev)
{
int i;
- solo_irq_off(solo_dev, SOLO_IRQ_ENCODER);
-
for (i = 0; i < solo_dev->nr_chans; i++) {
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0);
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0);
diff --git a/drivers/staging/media/solo6x10/g723.c b/drivers/staging/media/solo6x10/g723.c
index 2cd0de2..aeff41a 100644
--- a/drivers/staging/media/solo6x10/g723.c
+++ b/drivers/staging/media/solo6x10/g723.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -21,17 +26,17 @@
#include <linux/mempool.h>
#include <linux/poll.h>
#include <linux/kthread.h>
-#include <linux/slab.h>
#include <linux/freezer.h>
-#include <linux/export.h>
+#include <linux/module.h>
+
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/control.h>
+
#include "solo6x10.h"
#include "tw28.h"
-#define G723_INTR_ORDER 0
#define G723_FDMA_PAGES 32
#define G723_PERIOD_BYTES 48
#define G723_PERIOD_BLOCK 1024
@@ -46,36 +51,40 @@
/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page
* is broken down to 20 * 48 byte regions (one for each channel possible)
* with the rest of the page being dummy data. */
-#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX)
-#define IRQ_PAGES 4 /* 0 - 4 */
-#define PERIODS_MIN (1 << IRQ_PAGES)
+#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX)
+#define G723_INTR_ORDER 4 /* 0 - 4 */
+#define PERIODS_MIN (1 << G723_INTR_ORDER)
#define PERIODS_MAX G723_FDMA_PAGES
struct solo_snd_pcm {
- int on;
- spinlock_t lock;
- struct solo_dev *solo_dev;
- unsigned char g723_buf[G723_PERIOD_BYTES];
+ int on;
+ spinlock_t lock;
+ struct solo_dev *solo_dev;
+ unsigned char *g723_buf;
+ dma_addr_t g723_dma;
};
static void solo_g723_config(struct solo_dev *solo_dev)
{
int clk_div;
- clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2);
+ clk_div = (solo_dev->clock_mhz * 1000000)
+ / (SAMPLERATE * (BITRATE * 2) * 2);
solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE,
- SOLO_AUDIO_BITRATE(BITRATE) |
- SOLO_AUDIO_CLK_DIV(clk_div));
+ SOLO_AUDIO_BITRATE(BITRATE)
+ | SOLO_AUDIO_CLK_DIV(clk_div));
solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR,
- SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) |
- SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) |
- SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16));
+ SOLO_AUDIO_FDMA_INTERVAL(1)
+ | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER)
+ | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16));
solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL,
- SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE |
- SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK));
+ SOLO_AUDIO_ENABLE
+ | SOLO_AUDIO_I2S_MODE
+ | SOLO_AUDIO_I2S_MULTI(3)
+ | SOLO_AUDIO_MODE(OUTMODE_MASK));
}
void solo_g723_isr(struct solo_dev *solo_dev)
@@ -85,8 +94,6 @@ void solo_g723_isr(struct solo_dev *solo_dev)
struct snd_pcm_substream *ss;
struct solo_snd_pcm *solo_pcm;
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723);
-
for (ss = pstr->substream; ss != NULL; ss = ss->next) {
if (snd_pcm_substream_chip(ss) == NULL)
continue;
@@ -115,18 +122,18 @@ static int snd_solo_hw_free(struct snd_pcm_substream *ss)
return snd_pcm_lib_free_pages(ss);
}
-static struct snd_pcm_hardware snd_solo_pcm_hw = {
+static const struct snd_pcm_hardware snd_solo_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8,
.rates = SNDRV_PCM_RATE_8000,
- .rate_min = 8000,
- .rate_max = 8000,
+ .rate_min = SAMPLERATE,
+ .rate_max = SAMPLERATE,
.channels_min = 1,
.channels_max = 1,
- .buffer_bytes_max = MAX_BUFFER,
+ .buffer_bytes_max = G723_MAX_BUFFER,
.period_bytes_min = G723_PERIOD_BYTES,
.period_bytes_max = G723_PERIOD_BYTES,
.periods_min = PERIODS_MIN,
@@ -140,7 +147,13 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss)
solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL);
if (solo_pcm == NULL)
- return -ENOMEM;
+ goto oom;
+
+ solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev,
+ G723_PERIOD_BYTES,
+ &solo_pcm->g723_dma);
+ if (solo_pcm->g723_buf == NULL)
+ goto oom;
spin_lock_init(&solo_pcm->lock);
solo_pcm->solo_dev = solo_dev;
@@ -149,6 +162,10 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss)
snd_pcm_substream_chip(ss) = solo_pcm;
return 0;
+
+oom:
+ kfree(solo_pcm);
+ return -ENOMEM;
}
static int snd_solo_pcm_close(struct snd_pcm_substream *ss)
@@ -156,6 +173,8 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss)
struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
snd_pcm_substream_chip(ss) = solo_pcm->solo_dev;
+ pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES,
+ solo_pcm->g723_buf, solo_pcm->g723_dma);
kfree(solo_pcm);
return 0;
@@ -220,12 +239,11 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) {
int page = (pos / G723_FRAMES_PER_PAGE) + i;
- err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0,
- solo_pcm->g723_buf,
- SOLO_G723_EXT_ADDR(solo_dev) +
- (page * G723_PERIOD_BLOCK) +
- (ss->number * G723_PERIOD_BYTES),
- G723_PERIOD_BYTES);
+ err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma,
+ SOLO_G723_EXT_ADDR(solo_dev) +
+ (page * G723_PERIOD_BLOCK) +
+ (ss->number * G723_PERIOD_BYTES),
+ G723_PERIOD_BYTES, 0, 0);
if (err)
return err;
@@ -325,7 +343,7 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev)
ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
- MAX_BUFFER, MAX_BUFFER);
+ G723_MAX_BUFFER, G723_MAX_BUFFER);
if (ret < 0)
return ret;
@@ -368,6 +386,7 @@ int solo_g723_init(struct solo_dev *solo_dev)
strcpy(card->mixername, "SOLO-6x10");
kctl = snd_solo_capture_volume;
kctl.count = solo_dev->nr_chans;
+
ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev));
if (ret < 0)
return ret;
@@ -393,8 +412,12 @@ snd_error:
void solo_g723_exit(struct solo_dev *solo_dev)
{
+ if (!solo_dev->snd_card)
+ return;
+
solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0);
solo_irq_off(solo_dev, SOLO_IRQ_G723);
snd_card_free(solo_dev->snd_card);
+ solo_dev->snd_card = NULL;
}
diff --git a/drivers/staging/media/solo6x10/gpio.c b/drivers/staging/media/solo6x10/gpio.c
index 0925e6f..73276dc 100644
--- a/drivers/staging/media/solo6x10/gpio.c
+++ b/drivers/staging/media/solo6x10/gpio.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -19,7 +24,9 @@
#include <linux/kernel.h>
#include <linux/fs.h>
-#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
#include "solo6x10.h"
static void solo_gpio_mode(struct solo_dev *solo_dev,
diff --git a/drivers/staging/media/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c
index 398070a..01aa417 100644
--- a/drivers/staging/media/solo6x10/i2c.c
+++ b/drivers/staging/media/solo6x10/i2c.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -26,6 +31,7 @@
* thread context, ACK the interrupt, and move on. -- BenC */
#include <linux/kernel.h>
+
#include "solo6x10.h"
u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off)
@@ -173,10 +179,9 @@ int solo_i2c_isr(struct solo_dev *solo_dev)
u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
int ret = -EINVAL;
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC);
- if (status & (SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) ||
- solo_dev->i2c_id < 0) {
+ if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR)
+ || solo_dev->i2c_id < 0) {
solo_i2c_stop(solo_dev);
return -ENXIO;
}
@@ -239,7 +244,8 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap,
timeout = HZ / 2;
for (;;) {
- prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait(&solo_dev->i2c_wait, &wait,
+ TASK_INTERRUPTIBLE);
if (solo_dev->i2c_state == IIC_STATE_STOP)
break;
@@ -267,7 +273,7 @@ static u32 solo_i2c_functionality(struct i2c_adapter *adap)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm solo_i2c_algo = {
+static const struct i2c_algorithm solo_i2c_algo = {
.master_xfer = solo_i2c_master_xfer,
.functionality = solo_i2c_functionality,
};
@@ -288,7 +294,8 @@ int solo_i2c_init(struct solo_dev *solo_dev)
for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
- snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i);
+ snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d",
+ SOLO6X10_NAME, i);
adap->algo = &solo_i2c_algo;
adap->algo_data = solo_dev;
adap->retries = 1;
@@ -311,9 +318,6 @@ int solo_i2c_init(struct solo_dev *solo_dev)
return ret;
}
- dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n",
- SOLO_I2C_ADAPTERS);
-
return 0;
}
diff --git a/drivers/staging/media/solo6x10/offsets.h b/drivers/staging/media/solo6x10/offsets.h
index 3d7e569..f005dca 100644
--- a/drivers/staging/media/solo6x10/offsets.h
+++ b/drivers/staging/media/solo6x10/offsets.h
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -20,55 +25,61 @@
#ifndef __SOLO6X10_OFFSETS_H
#define __SOLO6X10_OFFSETS_H
-/* Offsets and sizes of the external address */
#define SOLO_DISP_EXT_ADDR 0x00000000
#define SOLO_DISP_EXT_SIZE 0x00480000
-#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE)
-#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000
-
-#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE)
-#define SOLO_OSG_EXT_SIZE 0x00120000
+#define SOLO_EOSD_EXT_ADDR \
+ (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE)
+#define SOLO_EOSD_EXT_SIZE(__solo) \
+ (__solo->type == SOLO_DEV_6010 ? 0x10000 : 0x20000)
+#define SOLO_EOSD_EXT_SIZE_MAX 0x20000
+#define SOLO_EOSD_EXT_AREA(__solo) \
+ (SOLO_EOSD_EXT_SIZE(__solo) * 32)
-#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE)
-#define SOLO_EOSD_EXT_SIZE 0x00010000
-
-#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \
- (SOLO_EOSD_EXT_SIZE * __solo->nr_chans))
+#define SOLO_MOTION_EXT_ADDR(__solo) \
+ (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_AREA(__solo))
#define SOLO_MOTION_EXT_SIZE 0x00080000
#define SOLO_G723_EXT_ADDR(__solo) \
- (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE)
+ (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE)
#define SOLO_G723_EXT_SIZE 0x00010000
#define SOLO_CAP_EXT_ADDR(__solo) \
- (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE)
-#define SOLO_CAP_EXT_MAX_PAGE (18 + 15)
-#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536)
+ (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE)
+
+/* 18 is the maximum number of pages required for PAL@D1, the largest frame
+ * possible */
+#define SOLO_CAP_PAGE_SIZE (18 << 16)
+
+/* Always allow the encoder enough for 16 channels, even if we have less. The
+ * exception is if we have card with only 32Megs of memory. */
+#define SOLO_CAP_EXT_SIZE(__solo) \
+ ((((__solo->sdram_size <= (32 << 20)) ? 4 : 16) + 1) \
+ * SOLO_CAP_PAGE_SIZE)
-/* This +1 is very important -- Why?! -- BenC */
#define SOLO_EREF_EXT_ADDR(__solo) \
- (SOLO_CAP_EXT_ADDR(__solo) + \
- (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1)))
+ (SOLO_CAP_EXT_ADDR(__solo) + SOLO_CAP_EXT_SIZE(__solo))
#define SOLO_EREF_EXT_SIZE 0x00140000
+#define SOLO_EREF_EXT_AREA(__solo) \
+ (SOLO_EREF_EXT_SIZE * __solo->nr_chans * 2)
+
+#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000)
#define SOLO_MP4E_EXT_ADDR(__solo) \
- (SOLO_EREF_EXT_ADDR(__solo) + \
- (SOLO_EREF_EXT_SIZE * __solo->nr_chans))
-#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans)
+ (SOLO_EREF_EXT_ADDR(__solo) + SOLO_EREF_EXT_AREA(__solo))
+#define SOLO_MP4E_EXT_SIZE(__solo) \
+ max((__solo->nr_chans * 0x00080000), \
+ min(((__solo->sdram_size - SOLO_MP4E_EXT_ADDR(__solo)) - \
+ __SOLO_JPEG_MIN_SIZE(__solo)), 0x00ff0000))
-#define SOLO_DREF_EXT_ADDR(__solo) \
+#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000)
+#define SOLO_JPEG_EXT_ADDR(__solo) \
(SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo))
-#define SOLO_DREF_EXT_SIZE 0x00140000
+#define SOLO_JPEG_EXT_SIZE(__solo) \
+ max(__SOLO_JPEG_MIN_SIZE(__solo), \
+ min((__solo->sdram_size - SOLO_JPEG_EXT_ADDR(__solo)), 0x00ff0000))
-#define SOLO_MP4D_EXT_ADDR(__solo) \
- (SOLO_DREF_EXT_ADDR(__solo) + \
- (SOLO_DREF_EXT_SIZE * __solo->nr_chans))
-#define SOLO_MP4D_EXT_SIZE 0x00080000
-
-#define SOLO_JPEG_EXT_ADDR(__solo) \
- (SOLO_MP4D_EXT_ADDR(__solo) + \
- (SOLO_MP4D_EXT_SIZE * __solo->nr_chans))
-#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans)
+#define SOLO_SDRAM_END(__solo) \
+ (SOLO_JPEG_EXT_ADDR(__solo) + SOLO_JPEG_EXT_SIZE(__solo))
#endif /* __SOLO6X10_OFFSETS_H */
diff --git a/drivers/staging/media/solo6x10/osd-font.h b/drivers/staging/media/solo6x10/osd-font.h
deleted file mode 100644
index 591e0e8..0000000
--- a/drivers/staging/media/solo6x10/osd-font.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __SOLO6X10_OSD_FONT_H
-#define __SOLO6X10_OSD_FONT_H
-
-static const unsigned int solo_osd_font[] = {
- 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000,
- 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */
- 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000,
- 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000,
- 0x00000000, 0x00001038, 0x10000000, 0x00000000,
- 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000,
- 0x00000000, 0x00384444, 0x44380000, 0x00000000,
- 0x00000000, 0x38448282, 0x82443800, 0x00000000,
- 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000,
- 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000,
- 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000,
- 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000,
- 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000,
- 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000,
- 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000,
- 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000,
- 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000,
- 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000,
- 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000,
- 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000,
- 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000,
- 0x00000082, 0x44288244, 0x38c6827c, 0x00000000,
- 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000,
- 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000,
- 0x00000000, 0x3854929a, 0x82443800, 0x00000000,
- 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000,
- 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000,
- 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000,
- 0x0000007c, 0x82829292, 0x929282fe, 0x00000000,
- 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000,
- 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000,
- 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */
- 0x00000066, 0x66240000, 0x00000000, 0x00000000,
- 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */
- 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000,
- 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */
- 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000,
- 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */
- 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000,
- 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */
- 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000,
- 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */
- 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000,
- 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */
- 0x00000000, 0x00000000, 0x00000006, 0x06000000,
- 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */
- 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000,
- 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */
- 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000,
- 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */
- 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000,
- 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */
- 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000,
- 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */
- 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000,
- 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */
- 0x00000000, 0x18180000, 0x00181800, 0x00000000,
- 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */
- 0x00000060, 0x30180c06, 0x0c183060, 0x00000000,
- 0x00000000, 0x007e0000, 0x007e0000, 0x00000000,
- 0x00000006, 0x0c183060, 0x30180c06, 0x00000000,
- 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000,
- 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000,
- 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */
- 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000,
- 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */
- 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000,
- 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */
- 0x000000fe, 0x0606063e, 0x06060606, 0x00000000,
- 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */
- 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000,
- 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */
- 0x00000060, 0x60606060, 0x6066663c, 0x00000000,
- 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */
- 0x00000006, 0x06060606, 0x060606fe, 0x00000000,
- 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */
- 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000,
- 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */
- 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000,
- 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */
- 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000,
- 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */
- 0x0000007e, 0x18181818, 0x18181818, 0x00000000,
- 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */
- 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000,
- 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */
- 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000,
- 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */
- 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000,
- 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */
- 0x00000002, 0x060c1830, 0x60c08000, 0x00000000,
- 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */
- 0x00001038, 0x6cc60000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00fe0000,
- 0x00001818, 0x30000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000,
- 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000,
- 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000,
- 0x00000060, 0x60607c66, 0x6666667c, 0x00000000,
- 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000,
- 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000,
- 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00,
- 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000,
- 0x00000030, 0x30003830, 0x30303078, 0x00000000,
- 0x00000030, 0x30003c30, 0x30303030, 0x30301f00,
- 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000,
- 0x00000030, 0x30303030, 0x30303030, 0x00000000,
- 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000,
- 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000,
- 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000,
- 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00,
- 0x00000000, 0x00007c66, 0x6666667c, 0x60606000,
- 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000,
- 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000,
- 0x00000000, 0x1818fe18, 0x18181870, 0x00000000,
- 0x00000000, 0x00006666, 0x6666663c, 0x00000000,
- 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000,
- 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000,
- 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000,
- 0x00000000, 0x00006666, 0x6666667c, 0x60603e00,
- 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000,
- 0x00000070, 0x1818180e, 0x18181870, 0x00000000,
- 0x00000018, 0x18181800, 0x18181818, 0x00000000,
- 0x0000000e, 0x18181870, 0x1818180e, 0x00000000,
- 0x000000dc, 0x76000000, 0x00000000, 0x00000000,
- 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000
-};
-
-#endif /* __SOLO6X10_OSD_FONT_H */
diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c
index 58ab61b..3ed4d58 100644
--- a/drivers/staging/media/solo6x10/p2m.c
+++ b/drivers/staging/media/solo6x10/p2m.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -18,28 +23,37 @@
*/
#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/scatterlist.h>
+#include <linux/module.h>
+
#include "solo6x10.h"
-/* #define SOLO_TEST_P2M */
+static int multi_p2m;
+module_param(multi_p2m, uint, 0644);
+MODULE_PARM_DESC(multi_p2m,
+ "Use multiple P2M DMA channels (default: no, 6010-only)");
-int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr,
- void *sys_addr, u32 ext_addr, u32 size)
+static int desc_mode;
+module_param(desc_mode, uint, 0644);
+MODULE_PARM_DESC(desc_mode,
+ "Allow use of descriptor mode DMA (default: no, 6010-only)");
+
+int solo_p2m_dma(struct solo_dev *solo_dev, int wr,
+ void *sys_addr, u32 ext_addr, u32 size,
+ int repeat, u32 ext_size)
{
dma_addr_t dma_addr;
int ret;
- WARN_ON(!size);
- BUG_ON(id >= SOLO_NR_P2M);
-
- if (!size)
+ if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03))
+ return -EINVAL;
+ if (WARN_ON_ONCE(!size))
return -EINVAL;
dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
- ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size);
+ ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size,
+ repeat, ext_size);
pci_unmap_single(solo_dev->pdev, dma_addr, size,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
@@ -47,221 +61,141 @@ int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr,
return ret;
}
-int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr,
- dma_addr_t dma_addr, u32 ext_addr, u32 size)
-{
- struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA);
- int ret;
-
- if (desc == NULL)
- return -ENOMEM;
-
- solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0);
- ret = solo_p2m_dma_desc(solo_dev, id, desc, 2);
- kfree(desc);
-
- return ret;
-}
-
-void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
- u32 ext_addr, u32 size, int repeat, u32 ext_size)
-{
- desc->ta = cpu_to_le32(dma_addr);
- desc->fa = cpu_to_le32(ext_addr);
-
- desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2));
- desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
- (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON);
-
- /* Ext size only matters when we're repeating */
- if (repeat) {
- desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2));
- desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) |
- SOLO_P2M_REPEAT(repeat));
- }
-}
-
-int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id,
- struct p2m_desc *desc, int desc_count)
+/* Mutex must be held for p2m_id before calling this!! */
+int solo_p2m_dma_desc(struct solo_dev *solo_dev,
+ struct solo_p2m_desc *desc, dma_addr_t desc_dma,
+ int desc_cnt)
{
struct solo_p2m_dev *p2m_dev;
unsigned int timeout;
+ unsigned int config = 0;
int ret = 0;
- u32 config = 0;
- dma_addr_t desc_dma = 0;
+ int p2m_id = 0;
- BUG_ON(id >= SOLO_NR_P2M);
- BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC);
+ /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */
+ if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) {
+ p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M;
+ if (p2m_id < 0)
+ p2m_id = -p2m_id;
+ }
- p2m_dev = &solo_dev->p2m_dev[id];
+ p2m_dev = &solo_dev->p2m_dev[p2m_id];
- mutex_lock(&p2m_dev->mutex);
-
- solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
+ if (mutex_lock_interruptible(&p2m_dev->mutex))
+ return -EINTR;
INIT_COMPLETION(p2m_dev->completion);
p2m_dev->error = 0;
- /* Enable the descriptors */
- config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id));
- desc_dma = pci_map_single(solo_dev->pdev, desc,
- desc_count * sizeof(*desc),
- PCI_DMA_TODEVICE);
- solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma);
- solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1);
- solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config |
- SOLO_P2M_DESC_MODE);
-
- /* Should have all descriptors completed from one interrupt */
- timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
-
- solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
+ if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) {
+ /* For 6010 with more than one desc, we can do a one-shot */
+ p2m_dev->desc_count = p2m_dev->desc_idx = 0;
+ config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id));
+
+ solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma);
+ solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt);
+ solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config |
+ SOLO_P2M_DESC_MODE);
+ } else {
+ /* For single descriptors and 6110, we need to run each desc */
+ p2m_dev->desc_count = desc_cnt;
+ p2m_dev->desc_idx = 1;
+ p2m_dev->descs = desc;
+
+ solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id),
+ desc[1].dma_addr);
+ solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id),
+ desc[1].ext_addr);
+ solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id),
+ desc[1].cfg);
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id),
+ desc[1].ctrl);
+ }
- /* Reset back to non-descriptor mode */
- solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config);
- solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0);
- solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0);
- pci_unmap_single(solo_dev->pdev, desc_dma,
- desc_count * sizeof(*desc),
- PCI_DMA_TODEVICE);
+ timeout = wait_for_completion_timeout(&p2m_dev->completion,
+ solo_dev->p2m_jiffies);
- if (p2m_dev->error)
+ if (WARN_ON_ONCE(p2m_dev->error))
ret = -EIO;
- else if (timeout == 0)
+ else if (timeout == 0) {
+ solo_dev->p2m_timeouts++;
ret = -EAGAIN;
-
- mutex_unlock(&p2m_dev->mutex);
-
- WARN_ON_ONCE(ret);
-
- return ret;
-}
-
-int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id,
- struct p2m_desc *pdesc, int wr,
- struct scatterlist *sg, u32 sg_off,
- u32 ext_addr, u32 size)
-{
- int i;
- int idx;
-
- BUG_ON(id >= SOLO_NR_P2M);
-
- if (WARN_ON_ONCE(!size))
- return -EINVAL;
-
- memset(pdesc, 0, sizeof(*pdesc));
-
- /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */
- for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0;
- i++, sg = sg_next(sg)) {
- struct p2m_desc *desc = &pdesc[idx];
- u32 sg_len = sg_dma_len(sg);
- u32 len;
-
- if (sg_off >= sg_len) {
- sg_off -= sg_len;
- continue;
- }
-
- sg_len -= sg_off;
- len = min(sg_len, size);
-
- solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off,
- ext_addr, len, 0, 0);
-
- size -= len;
- ext_addr += len;
- idx++;
-
- sg_off = 0;
}
- WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC);
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0);
- return solo_p2m_dma_desc(solo_dev, id, pdesc, idx);
-}
+ /* Don't write here for the no_desc_mode case, because config is 0.
+ * We can't test no_desc_mode again, it might race. */
+ if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config)
+ solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config);
-#ifdef SOLO_TEST_P2M
+ mutex_unlock(&p2m_dev->mutex);
-#define P2M_TEST_CHAR 0xbe
+ return ret;
+}
-static unsigned long long p2m_test(struct solo_dev *solo_dev, u8 id,
- u32 base, int size)
+void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size,
+ int repeat, u32 ext_size)
{
- u8 *wr_buf;
- u8 *rd_buf;
- int i;
- unsigned long long err_cnt = 0;
+ WARN_ON_ONCE(dma_addr & 0x03);
+ WARN_ON_ONCE(!size);
- wr_buf = kmalloc(size, GFP_KERNEL);
- if (!wr_buf) {
- printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n");
- return size;
- }
+ desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2);
+ desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
+ (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON;
- rd_buf = kmalloc(size, GFP_KERNEL);
- if (!rd_buf) {
- printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n");
- kfree(wr_buf);
- return size;
+ if (repeat) {
+ desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2);
+ desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) |
+ SOLO_P2M_REPEAT(repeat);
}
- memset(wr_buf, P2M_TEST_CHAR, size);
- memset(rd_buf, P2M_TEST_CHAR + 1, size);
-
- solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size);
- solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size);
-
- for (i = 0; i < size; i++)
- if (wr_buf[i] != rd_buf[i])
- err_cnt++;
-
- kfree(wr_buf);
- kfree(rd_buf);
-
- return err_cnt;
+ desc->dma_addr = dma_addr;
+ desc->ext_addr = ext_addr;
}
-#define TEST_CHUNK_SIZE (8 * 1024)
-
-static void run_p2m_test(struct solo_dev *solo_dev)
+int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size,
+ int repeat, u32 ext_size)
{
- unsigned long long errs = 0;
- u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev);
- int i, d;
+ struct solo_p2m_desc desc[2];
- dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n",
- size);
+ solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat,
+ ext_size);
- for (i = 0; i < size; i += TEST_CHUNK_SIZE)
- for (d = 0; d < 4; d++)
- errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE);
-
- dev_warn(&solo_dev->pdev->dev, "Found %llu errors during p2m test\n",
- errs);
-
- return;
+ /* No need for desc_dma since we know it is a single-shot */
+ return solo_p2m_dma_desc(solo_dev, desc, 0, 1);
}
-#else
-#define run_p2m_test(__solo) do {} while (0)
-#endif
void solo_p2m_isr(struct solo_dev *solo_dev, int id)
{
struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id];
+ struct solo_p2m_desc *desc;
+
+ if (p2m_dev->desc_count <= p2m_dev->desc_idx) {
+ complete(&p2m_dev->completion);
+ return;
+ }
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id));
+ /* Setup next descriptor */
+ p2m_dev->desc_idx++;
+ desc = &p2m_dev->descs[p2m_dev->desc_idx];
- complete(&p2m_dev->completion);
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
+ solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr);
+ solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr);
+ solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg);
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl);
}
-void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status)
+void solo_p2m_error_isr(struct solo_dev *solo_dev)
{
+ unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR);
struct solo_p2m_dev *p2m_dev;
int i;
- if (!(status & SOLO_PCI_ERR_P2M))
+ if (!(err & SOLO_PCI_ERR_P2M))
return;
for (i = 0; i < SOLO_NR_P2M; i++) {
@@ -280,6 +214,52 @@ void solo_p2m_exit(struct solo_dev *solo_dev)
solo_irq_off(solo_dev, SOLO_IRQ_P2M(i));
}
+static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size)
+{
+ u32 *wr_buf;
+ u32 *rd_buf;
+ int i;
+ int ret = -EIO;
+ int order = get_order(size);
+
+ wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order);
+ if (wr_buf == NULL)
+ return -1;
+
+ rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order);
+ if (rd_buf == NULL) {
+ free_pages((unsigned long)wr_buf, order);
+ return -1;
+ }
+
+ for (i = 0; i < (size >> 3); i++)
+ *(wr_buf + i) = (i << 16) | (i + 1);
+
+ for (i = (size >> 3); i < (size >> 2); i++)
+ *(wr_buf + i) = ~((i << 16) | (i + 1));
+
+ memset(rd_buf, 0x55, size);
+
+ if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0))
+ goto test_fail;
+
+ if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0))
+ goto test_fail;
+
+ for (i = 0; i < (size >> 2); i++) {
+ if (*(wr_buf + i) != *(rd_buf + i))
+ goto test_fail;
+ }
+
+ ret = 0;
+
+test_fail:
+ free_pages((unsigned long)wr_buf, order);
+ free_pages((unsigned long)rd_buf, order);
+
+ return ret;
+}
+
int solo_p2m_init(struct solo_dev *solo_dev)
{
struct solo_p2m_dev *p2m_dev;
@@ -294,13 +274,57 @@ int solo_p2m_init(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0);
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
SOLO_P2M_CSC_16BIT_565 |
- SOLO_P2M_DMA_INTERVAL(3) |
SOLO_P2M_DESC_INTR_OPT |
+ SOLO_P2M_DMA_INTERVAL(0) |
SOLO_P2M_PCI_MASTER_MODE);
solo_irq_on(solo_dev, SOLO_IRQ_P2M(i));
}
- run_p2m_test(solo_dev);
+ /* Find correct SDRAM size */
+ for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) {
+ solo_reg_write(solo_dev, SOLO_DMA_CTRL,
+ SOLO_DMA_CTRL_REFRESH_CYCLE(1) |
+ SOLO_DMA_CTRL_SDRAM_SIZE(i) |
+ SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
+ SOLO_DMA_CTRL_READ_CLK_SELECT |
+ SOLO_DMA_CTRL_LATENCY(1));
+
+ solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config |
+ SOLO_SYS_CFG_RESET);
+ solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config);
+
+ switch (i) {
+ case 2:
+ if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) ||
+ solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000))
+ continue;
+ break;
+
+ case 1:
+ if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000))
+ continue;
+ break;
+
+ default:
+ if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000))
+ continue;
+ }
+
+ solo_dev->sdram_size = (32 << 20) << i;
+ break;
+ }
+
+ if (!solo_dev->sdram_size) {
+ dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n");
+ return -EIO;
+ }
+
+ if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) {
+ dev_err(&solo_dev->pdev->dev,
+ "SDRAM is not large enough (%u < %u)\n",
+ solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev));
+ return -EIO;
+ }
return 0;
}
diff --git a/drivers/staging/media/solo6x10/registers.h b/drivers/staging/media/solo6x10/registers.h
index aca5444..5e5c7e6 100644
--- a/drivers/staging/media/solo6x10/registers.h
+++ b/drivers/staging/media/solo6x10/registers.h
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -22,18 +27,18 @@
#include "offsets.h"
-/* Global 6X10 system configuration */
+/* Global 6010 system configuration */
#define SOLO_SYS_CFG 0x0000
-#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */
-#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */
-#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */
-#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */
-#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */
-#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */
+#define SOLO_SYS_CFG_FOUT_EN 0x00000001
+#define SOLO_SYS_CFG_PLL_BYPASS 0x00000002
+#define SOLO_SYS_CFG_PLL_PWDN 0x00000004
+#define SOLO_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3)
+#define SOLO_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5)
+#define SOLO_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14)
#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000
#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24)
#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26)
-#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */
+#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000
#define SOLO_SYS_CFG_RESET 0x80000000
#define SOLO_DMA_CTRL 0x0004
@@ -45,7 +50,9 @@
#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3)
#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2)
#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0)
-#define SOLO_DMA_CTRL1 0x0008
+
+/* Some things we set in this are undocumented. Why Softlogic?!?! */
+#define SOLO_DMA_CTRL1 0x0008
#define SOLO_SYS_VCLK 0x000C
#define SOLO_VCLK_INVERT (1<<22)
@@ -61,7 +68,7 @@
#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0)
#define SOLO_IRQ_STAT 0x0010
-#define SOLO_IRQ_ENABLE 0x0014
+#define SOLO_IRQ_MASK 0x0014
#define SOLO_IRQ_P2M(n) (1<<((n)+17))
#define SOLO_IRQ_GPIO (1<<16)
#define SOLO_IRQ_VIDEO_LOSS (1<<15)
@@ -82,22 +89,7 @@
#define SOLO_CHIP_OPTION 0x001C
#define SOLO_CHIP_ID_MASK 0x00000007
-#define SOLO6110_PLL_CONFIG 0x0020
-#define SOLO6110_PLL_RANGE_BYPASS (0 << 20)
-#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20)
-#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20)
-#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20)
-#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20)
-#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20)
-#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20)
-#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20)
-#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15)
-#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12)
-#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4)
-#define SOLO6110_PLL_RESET (1 << 3)
-#define SOLO6110_PLL_BYPASS (1 << 2)
-#define SOLO6110_PLL_FSEN (1 << 1)
-#define SOLO6110_PLL_FB (1 << 0)
+#define SOLO_PLL_CONFIG 0x0020 /* 6110 Only */
#define SOLO_EEPROM_CTRL 0x0060
#define SOLO_EEPROM_ACCESS_EN (1<<7)
@@ -105,7 +97,7 @@
#define SOLO_EEPROM_CLK (1<<2)
#define SOLO_EEPROM_DO (1<<1)
#define SOLO_EEPROM_DI (1<<0)
-#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS)
+#define SOLO_EEPROM_ENABLE (SOLO_EEPROM_ACCESS_EN | SOLO_EEPROM_CS)
#define SOLO_PCI_ERR 0x0070
#define SOLO_PCI_ERR_FATAL 0x00000001
@@ -274,8 +266,8 @@
#define SOLO_VO_FI_CHANGE (1<<20)
#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19)
#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18)
-#define SOLO_VO_USER_COLOR_SET_NAV (1<<17)
-#define SOLO_VO_USER_COLOR_SET_NAH (1<<16)
+#define SOLO_VO_USER_COLOR_SET_NAH (1<<17)
+#define SOLO_VO_USER_COLOR_SET_NAV (1<<16)
#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8)
#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4)
#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0)
@@ -401,12 +393,13 @@
#define SOLO_VE_BLOCK_BASE(n) ((n)<<0)
#define SOLO_VE_CFG1 0x0614
-#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */
-#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */
-#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */
+#define SOLO_VE_BYTE_ALIGN(n) ((n)<<24)
#define SOLO_VE_INSERT_INDEX (1<<18)
#define SOLO_VE_MOTION_MODE(n) ((n)<<16)
#define SOLO_VE_MOTION_BASE(n) ((n)<<0)
+#define SOLO_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 Only */
+#define SOLO_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 Only */
+#define SOLO_VE_INSERT_INDEX_JPEG (1<<19) /* 6110 Only */
#define SOLO_VE_WMRK_POLY 0x061C
#define SOLO_VE_VMRK_INIT_KEY 0x0620
@@ -420,6 +413,7 @@
#define SOLO_COMP_TIME_INC(n) ((n)<<25)
#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21)
#define SOLO_DCT_INTERVAL(n) ((n)<<16)
+#define SOLO_VE_COMPT_MOT 0x0634 /* 6110 Only */
#define SOLO_VE_STATE(n) (0x0640+((n)*4))
@@ -428,14 +422,21 @@
#define SOLO_VE_JPEG_QP_CH_H 0x0678
#define SOLO_VE_JPEG_CFG 0x067C
#define SOLO_VE_JPEG_CTRL 0x0680
-
+#define SOLO_VE_CODE_ENCRYPT 0x0684 /* 6110 Only */
+#define SOLO_VE_JPEG_CFG1 0x0688 /* 6110 Only */
+#define SOLO_VE_WMRK_ENABLE 0x068C /* 6110 Only */
#define SOLO_VE_OSD_CH 0x0690
#define SOLO_VE_OSD_BASE 0x0694
#define SOLO_VE_OSD_CLR 0x0698
#define SOLO_VE_OSD_OPT 0x069C
+#define SOLO_VE_OSD_V_DOUBLE (1<<16) /* 6110 Only */
+#define SOLO_VE_OSD_H_SHADOW (1<<15)
+#define SOLO_VE_OSD_V_SHADOW (1<<14)
+#define SOLO_VE_OSD_H_OFFSET(n) ((n & 0x7f)<<7)
+#define SOLO_VE_OSD_V_OFFSET(n) (n & 0x7f)
#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4))
-#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */
+#define SOLO_VE_CH_MOT(ch) (0x0740+((ch)*4))
#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4))
#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4))
#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4))
@@ -447,7 +448,7 @@
#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8))
#define SOLO_VD_CFG0 0x0900
-#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */
+#define SOLO_VD_CFG_NO_WRITE_NO_WINDOW (1<<24)
#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23)
#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22)
#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21)
@@ -599,9 +600,9 @@
#define SOLO_UART_RX_DATA_POP (1<<8)
#define SOLO_TIMER_CLOCK_NUM 0x0be0
-#define SOLO_TIMER_WATCHDOG 0x0be4
#define SOLO_TIMER_USEC 0x0be8
#define SOLO_TIMER_SEC 0x0bec
+#define SOLO_TIMER_USEC_LSB 0x0d20 /* 6110 Only */
#define SOLO_AUDIO_CONTROL 0x0D00
#define SOLO_AUDIO_ENABLE (1<<31)
@@ -629,9 +630,10 @@
#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10))
#define SOLO_AUDIO_STA 0x0D14
-
-#define SOLO_WATCHDOG 0x0BE4
-#define WATCHDOG_STAT(status) (status<<8)
-#define WATCHDOG_TIME(sec) (sec&0xff)
+/*
+ * Watchdog configuration
+ */
+#define SOLO_WATCHDOG 0x0be4
+#define SOLO_WATCHDOG_SET(status, sec) (status << 8 | (sec & 0xff))
#endif /* __SOLO6X10_REGISTERS_H */
diff --git a/drivers/staging/media/solo6x10/solo6x10-jpeg.h b/drivers/staging/media/solo6x10/solo6x10-jpeg.h
index 50defec..c5218ce 100644
--- a/drivers/staging/media/solo6x10/solo6x10-jpeg.h
+++ b/drivers/staging/media/solo6x10/solo6x10-jpeg.h
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -20,7 +25,7 @@
#ifndef __SOLO6X10_JPEG_H
#define __SOLO6X10_JPEG_H
-static unsigned char jpeg_header[] = {
+static const unsigned char jpeg_header[] = {
0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c,
0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79,
0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16,
@@ -102,4 +107,87 @@ static unsigned char jpeg_header[] = {
/* This is the byte marker for the start of SOF0: 0xffc0 marker */
#define SOF0_START 575
+/* This is the byte marker for the start of the DQT */
+#define DQT_START 17
+#define DQT_LEN 138
+const unsigned char jpeg_dqt[4][DQT_LEN] = {
+ {
+ 0xff, 0xdb, 0x00, 0x43, 0x00,
+ 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07,
+ 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14,
+ 0x0d, 0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13,
+ 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a,
+ 0x1c, 0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22,
+ 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c,
+ 0x30, 0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39,
+ 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32,
+ 0xff, 0xdb, 0x00, 0x43, 0x01,
+ 0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d,
+ 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32
+ }, {
+ 0xff, 0xdb, 0x00, 0x43, 0x00,
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+ 0xff, 0xdb, 0x00, 0x43, 0x01,
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+ }, {
+ 0xff, 0xdb, 0x00, 0x43, 0x00,
+ 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c,
+ 0x1a, 0x1c, 0x24, 0x22, 0x20, 0x26, 0x30, 0x50,
+ 0x34, 0x30, 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a,
+ 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
+ 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88,
+ 0xae, 0x8a, 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae,
+ 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2,
+ 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6,
+ 0xff, 0xdb, 0x00, 0x43, 0x01,
+ 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
+ 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6
+ }, {
+ 0xff, 0xdb, 0x00, 0x43, 0x00,
+ 0x30, 0x21, 0x24, 0x2a, 0x24, 0x1e, 0x30, 0x2a,
+ 0x27, 0x2a, 0x36, 0x33, 0x30, 0x39, 0x48, 0x78,
+ 0x4e, 0x48, 0x42, 0x42, 0x48, 0x93, 0x69, 0x6f,
+ 0x57, 0x78, 0xae, 0x99, 0xb7, 0xb4, 0xab, 0x99,
+ 0xa8, 0xa5, 0xc0, 0xd8, 0xff, 0xea, 0xc0, 0xcc,
+ 0xff, 0xcf, 0xa5, 0xa8, 0xf0, 0xff, 0xf3, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xe7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xdb, 0x00, 0x43, 0x01,
+ 0x33, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
+ 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ }
+};
+
#endif /* __SOLO6X10_JPEG_H */
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index abee721..44ae160 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -23,17 +28,17 @@
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/i2c.h>
-#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <asm/io.h>
+#include <linux/stringify.h>
+#include <linux/io.h>
#include <linux/atomic.h>
+
#include <linux/videodev2.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-core.h>
+
#include "registers.h"
#ifndef PCI_VENDOR_ID_SOFTLOGIC
@@ -58,19 +63,24 @@
#define PCI_DEVICE_ID_BC_6110_16 0x5310
#endif /* Bluecherry */
+/* Used in pci_device_id, and solo_dev->type */
+#define SOLO_DEV_6010 0
+#define SOLO_DEV_6110 1
+
#define SOLO6X10_NAME "solo6x10"
#define SOLO_MAX_CHANNELS 16
/* Make sure these two match */
-#define SOLO6X10_VERSION "2.1.0"
#define SOLO6X10_VER_MAJOR 2
-#define SOLO6X10_VER_MINOR 0
-#define SOLO6X10_VER_SUB 0
+#define SOLO6X10_VER_MINOR 4
+#define SOLO6X10_VER_SUB 4
#define SOLO6X10_VER_NUM \
KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB)
-
-#define FLAGS_6110 1
+#define SOLO6X10_VERSION \
+ __stringify(SOLO6X10_VER_MAJOR) "." \
+ __stringify(SOLO6X10_VER_MINOR) "." \
+ __stringify(SOLO6X10_VER_SUB)
/*
* The SOLO6x10 actually has 8 i2c channels, but we only use 2.
@@ -84,16 +94,7 @@
/* DMA Engine setup */
#define SOLO_NR_P2M 4
#define SOLO_NR_P2M_DESC 256
-/* MPEG and JPEG share the same interrupt and locks so they must be together
- * in the same dma channel. */
-#define SOLO_P2M_DMA_ID_MP4E 0
-#define SOLO_P2M_DMA_ID_JPEG 0
-#define SOLO_P2M_DMA_ID_MP4D 1
-#define SOLO_P2M_DMA_ID_G723D 1
-#define SOLO_P2M_DMA_ID_DISP 2
-#define SOLO_P2M_DMA_ID_OSG 2
-#define SOLO_P2M_DMA_ID_G723E 3
-#define SOLO_P2M_DMA_ID_VIN 3
+#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16)
/* Encoder standard modes */
#define SOLO_ENC_MODE_CIF 2
@@ -103,12 +104,6 @@
#define SOLO_DEFAULT_GOP 30
#define SOLO_DEFAULT_QP 3
-/* There is 8MB memory available for solo to buffer MPEG4 frames.
- * This gives us 512 * 16kbyte queues. */
-#define SOLO_NR_RING_BUFS 512
-
-#define SOLO_CLOCK_MHZ 108
-
#ifndef V4L2_BUF_FLAG_MOTION_ON
#define V4L2_BUF_FLAG_MOTION_ON 0x0400
#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800
@@ -128,64 +123,67 @@ enum SOLO_I2C_STATE {
IIC_STATE_STOP
};
-struct p2m_desc {
- u32 ctrl;
- u32 ext;
- u32 ta;
- u32 fa;
+/* Defined in Table 4-16, Page 68-69 of the 6010 Datasheet */
+struct solo_p2m_desc {
+ u32 ctrl;
+ u32 cfg;
+ u32 dma_addr;
+ u32 ext_addr;
};
struct solo_p2m_dev {
struct mutex mutex;
struct completion completion;
+ int desc_count;
+ int desc_idx;
+ struct solo_p2m_desc *descs;
int error;
};
-#define OSD_TEXT_MAX 30
-
-enum solo_enc_types {
- SOLO_ENC_TYPE_STD,
- SOLO_ENC_TYPE_EXT,
-};
+#define OSD_TEXT_MAX 44
struct solo_enc_dev {
- struct solo_dev *solo_dev;
+ struct solo_dev *solo_dev;
/* V4L2 Items */
struct video_device *vfd;
/* General accounting */
- wait_queue_head_t thread_wait;
- spinlock_t lock;
+ struct mutex enable_lock;
+ spinlock_t motion_lock;
atomic_t readers;
+ atomic_t mpeg_readers;
u8 ch;
u8 mode, gop, qp, interlaced, interval;
- u8 reset_gop;
u8 bw_weight;
- u8 motion_detected;
u16 motion_thresh;
u16 width;
u16 height;
+
+ /* OSD buffers */
char osd_text[OSD_TEXT_MAX + 1];
-};
+ u8 osd_buf[SOLO_EOSD_EXT_SIZE_MAX]
+ __aligned(4);
-struct solo_enc_buf {
- u8 vop;
- u8 ch;
- enum solo_enc_types type;
- u32 off;
- u32 size;
- u32 jpeg_off;
- u32 jpeg_size;
- struct timeval ts;
+ /* VOP stuff */
+ unsigned char vop[64];
+ int vop_len;
+ unsigned char jpeg_header[1024];
+ int jpeg_len;
+
+ /* File handles that are listening for buffers */
+ struct list_head listeners;
};
/* The SOLO6x10 PCI Device */
struct solo_dev {
/* General stuff */
struct pci_dev *pdev;
+ int type;
+ unsigned int time_sync;
+ unsigned int usec_lsb;
+ unsigned int clock_mhz;
u8 __iomem *reg_base;
int nr_chans;
int nr_ext;
- u32 flags;
u32 irq_mask;
u32 motion_mask;
spinlock_t reg_io_lock;
@@ -206,6 +204,9 @@ struct solo_dev {
/* P2M DMA Engine */
struct solo_p2m_dev p2m_dev[SOLO_NR_P2M];
+ atomic_t p2m_count;
+ int p2m_jiffies;
+ unsigned int p2m_timeouts;
/* V4L2 Display items */
struct video_device *vfd;
@@ -219,9 +220,6 @@ struct solo_dev {
u16 enc_bw_remain;
/* IDX into hw mp4 encoder */
u8 enc_idx;
- /* Our software ring of enc buf references */
- u16 enc_wr_idx;
- struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS];
/* Current video settings */
u32 video_type;
@@ -230,11 +228,32 @@ struct solo_dev {
u16 vin_hstart, vin_vstart;
u8 fps;
+ /* JPEG Qp setting */
+ spinlock_t jpeg_qp_lock;
+ u32 jpeg_qp[2];
+
/* Audio components */
struct snd_card *snd_card;
struct snd_pcm *snd_pcm;
atomic_t snd_users;
int g723_hw_idx;
+
+ /* sysfs stuffs */
+ struct device dev;
+ int sdram_size;
+ struct bin_attribute sdram_attr;
+ unsigned int sys_config;
+
+ /* Ring thread */
+ struct task_struct *ring_thread;
+ wait_queue_head_t ring_thread_wait;
+ atomic_t enc_users;
+ atomic_t disp_users;
+
+ /* VOP_HEADER handling */
+ void *vh_buf;
+ dma_addr_t vh_dma;
+ int vh_size;
};
static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg)
@@ -255,7 +274,8 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg)
return ret;
}
-static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data)
+static inline void solo_reg_write(struct solo_dev *solo_dev, int reg,
+ u32 data)
{
unsigned long flags;
u16 val;
@@ -270,8 +290,17 @@ static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data)
spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags);
}
-void solo_irq_on(struct solo_dev *solo_dev, u32 mask);
-void solo_irq_off(struct solo_dev *solo_dev, u32 mask);
+static inline void solo_irq_on(struct solo_dev *dev, u32 mask)
+{
+ dev->irq_mask |= mask;
+ solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask);
+}
+
+static inline void solo_irq_off(struct solo_dev *dev, u32 mask)
+{
+ dev->irq_mask &= ~mask;
+ solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask);
+}
/* Init/exit routeines for subsystems */
int solo_disp_init(struct solo_dev *solo_dev);
@@ -286,13 +315,13 @@ void solo_i2c_exit(struct solo_dev *solo_dev);
int solo_p2m_init(struct solo_dev *solo_dev);
void solo_p2m_exit(struct solo_dev *solo_dev);
-int solo_v4l2_init(struct solo_dev *solo_dev);
+int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr);
void solo_v4l2_exit(struct solo_dev *solo_dev);
int solo_enc_init(struct solo_dev *solo_dev);
void solo_enc_exit(struct solo_dev *solo_dev);
-int solo_enc_v4l2_init(struct solo_dev *solo_dev);
+int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr);
void solo_enc_v4l2_exit(struct solo_dev *solo_dev);
int solo_g723_init(struct solo_dev *solo_dev);
@@ -301,7 +330,7 @@ void solo_g723_exit(struct solo_dev *solo_dev);
/* ISR's */
int solo_i2c_isr(struct solo_dev *solo_dev);
void solo_p2m_isr(struct solo_dev *solo_dev, int id);
-void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status);
+void solo_p2m_error_isr(struct solo_dev *solo_dev);
void solo_enc_v4l2_isr(struct solo_dev *solo_dev);
void solo_g723_isr(struct solo_dev *solo_dev);
void solo_motion_isr(struct solo_dev *solo_dev);
@@ -313,24 +342,39 @@ void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off,
u8 data);
/* P2M DMA */
-int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr,
- dma_addr_t dma_addr, u32 ext_addr, u32 size);
-int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr,
- void *sys_addr, u32 ext_addr, u32 size);
-int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id,
- struct p2m_desc *pdesc, int wr,
- struct scatterlist *sglist, u32 sg_off,
- u32 ext_addr, u32 size);
-void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
- u32 ext_addr, u32 size, int repeat, u32 ext_size);
-int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id,
- struct p2m_desc *desc, int desc_count);
+int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size,
+ int repeat, u32 ext_size);
+int solo_p2m_dma(struct solo_dev *solo_dev, int wr,
+ void *sys_addr, u32 ext_addr, u32 size,
+ int repeat, u32 ext_size);
+void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size,
+ int repeat, u32 ext_size);
+int solo_p2m_dma_desc(struct solo_dev *solo_dev,
+ struct solo_p2m_desc *desc, dma_addr_t desc_dma,
+ int desc_cnt);
/* Set the threshold for motion detection */
-void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val);
+int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val);
+int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, u16 val,
+ u16 block);
#define SOLO_DEF_MOT_THRESH 0x0300
/* Write text on OSD */
int solo_osd_print(struct solo_enc_dev *solo_enc);
+/* EEPROM commands */
+unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en);
+unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc);
+int solo_eeprom_write(struct solo_dev *solo_dev, int loc,
+ unsigned short data);
+
+/* JPEG Qp functions */
+void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch,
+ unsigned int qp);
+int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch);
+
+#define CHK_FLAGS(v, flags) (((v) & (flags)) == (flags))
+
#endif /* __SOLO6X10_H */
diff --git a/drivers/staging/media/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c
index db56b42..365ab10 100644
--- a/drivers/staging/media/solo6x10/tw28.c
+++ b/drivers/staging/media/solo6x10/tw28.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -18,12 +23,12 @@
*/
#include <linux/kernel.h>
+#include <linux/delay.h>
+
#include "solo6x10.h"
#include "tw28.h"
-/* XXX: Some of these values are masked into an 8-bit regs, and shifted
- * around for other 8-bit regs. What are the magic bits in these values? */
-#define DEFAULT_HDELAY_NTSC (32 - 4)
+#define DEFAULT_HDELAY_NTSC (32 - 8)
#define DEFAULT_HACTIVE_NTSC (720 + 16)
#define DEFAULT_VDELAY_NTSC (7 - 2)
#define DEFAULT_VACTIVE_NTSC (240 + 4)
@@ -33,15 +38,16 @@
#define DEFAULT_VDELAY_PAL (6)
#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL)
-static u8 tbl_tw2864_template[] = {
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+
+static const u8 tbl_tw2864_ntsc_template[] = {
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
+ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
+ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
+ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
+ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
@@ -61,14 +67,49 @@ static u8 tbl_tw2864_template[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
- 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
- 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
+ 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
+ 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
};
-static u8 tbl_tw2865_ntsc_template[] = {
+static const u8 tbl_tw2864_pal_template[] = {
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
+ 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
+ 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
+ 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
+ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
+ 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
+ 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
+ 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
+ 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
+ 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
+ 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
+ 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
+ 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
+ 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
+ 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
+ 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
+ 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
+ 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
+ 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
+};
+
+static const u8 tbl_tw2865_ntsc_template[] = {
0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
@@ -103,7 +144,7 @@ static u8 tbl_tw2865_ntsc_template[] = {
0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
};
-static u8 tbl_tw2865_pal_template[] = {
+static const u8 tbl_tw2865_pal_template[] = {
0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
@@ -180,8 +221,8 @@ static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
msleep_interruptible(1);
}
-/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n",
- addr, off, val); */
+/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
+/* addr, off, val); */
}
static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
@@ -216,16 +257,17 @@ static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
for (i = 0; i < 0xff; i++) {
/* Skip read only registers */
- if (i >= 0xb8 && i <= 0xc1)
- continue;
- if ((i & ~0x30) == 0x00 ||
- (i & ~0x30) == 0x0c ||
- (i & ~0x30) == 0x0d)
- continue;
- if (i >= 0xc4 && i <= 0xc7)
+ switch (i) {
+ case 0xb8 ... 0xc1:
+ case 0xc4 ... 0xc7:
+ case 0xfd:
continue;
- if (i == 0xfd)
+ }
+ switch (i & ~0x30) {
+ case 0x00:
+ case 0x0c ... 0x0d:
continue;
+ }
tw_write_and_verify(solo_dev, dev_addr, i,
tbl_tw2865_common[i]);
@@ -236,11 +278,15 @@ static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
{
- u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)];
+ u8 tbl_tw2864_common[256];
int i;
- memcpy(tbl_tw2864_common, tbl_tw2864_template,
- sizeof(tbl_tw2864_common));
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
+ memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
+ sizeof(tbl_tw2864_common));
+ else
+ memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
+ sizeof(tbl_tw2864_common));
if (solo_dev->tw2865 == 0) {
/* IRQ Mode */
@@ -285,33 +331,19 @@ static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
}
}
- /* NTSC or PAL */
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) {
- for (i = 0; i < 4; i++) {
- tbl_tw2864_common[0x07 | (i << 4)] |= 0x10;
- tbl_tw2864_common[0x08 | (i << 4)] |= 0x06;
- tbl_tw2864_common[0x0a | (i << 4)] |= 0x08;
- tbl_tw2864_common[0x0b | (i << 4)] |= 0x13;
- tbl_tw2864_common[0x0e | (i << 4)] |= 0x01;
- }
- tbl_tw2864_common[0x9d] = 0x90;
- tbl_tw2864_common[0xf3] = 0x00;
- tbl_tw2864_common[0xf4] = 0xa0;
- }
-
for (i = 0; i < 0xff; i++) {
/* Skip read only registers */
- if (i >= 0xb8 && i <= 0xc1)
- continue;
- if ((i & ~0x30) == 0x00 ||
- (i & ~0x30) == 0x0c ||
- (i & ~0x30) == 0x0d)
- continue;
- if (i == 0x74 || i == 0x77 || i == 0x78 ||
- i == 0x79 || i == 0x7a)
+ switch (i) {
+ case 0xb8 ... 0xc1:
+ case 0xfd:
continue;
- if (i == 0xfd)
+ }
+ switch (i & ~0x30) {
+ case 0x00:
+ case 0x0c:
+ case 0x0d:
continue;
+ }
tw_write_and_verify(solo_dev, dev_addr, i,
tbl_tw2864_common[i]);
@@ -544,8 +576,8 @@ int solo_tw28_init(struct solo_dev *solo_dev)
int i;
u8 value;
- /* Detect techwell chip type */
- for (i = 0; i < TW_NUM_CHIP; i++) {
+ /* Detect techwell chip type(s) */
+ for (i = 0; i < solo_dev->nr_chans / 4; i++) {
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(i), 0xFF);
@@ -560,7 +592,8 @@ int solo_tw28_init(struct solo_dev *solo_dev)
break;
default:
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
- TW_CHIP_OFFSET_ADDR(i), 0x59);
+ TW_CHIP_OFFSET_ADDR(i),
+ 0x59);
if ((value >> 3) == 0x04) {
solo_dev->tw2815 |= 1 << i;
solo_dev->tw28_cnt++;
@@ -568,8 +601,11 @@ int solo_tw28_init(struct solo_dev *solo_dev)
}
}
- if (!solo_dev->tw28_cnt)
+ if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
+ dev_err(&solo_dev->pdev->dev,
+ "Could not initialize any techwell chips\n");
return -EINVAL;
+ }
saa7128_setup(solo_dev);
@@ -582,17 +618,6 @@ int solo_tw28_init(struct solo_dev *solo_dev)
tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
}
- dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:",
- solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s");
-
- if (solo_dev->tw2865)
- printk(" tw2865[%d]", hweight32(solo_dev->tw2865));
- if (solo_dev->tw2864)
- printk(" tw2864[%d]", hweight32(solo_dev->tw2864));
- if (solo_dev->tw2815)
- printk(" tw2815[%d]", hweight32(solo_dev->tw2815));
- printk("\n");
-
return 0;
}
@@ -610,7 +635,7 @@ int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
chip_num = ch / 4;
ch %= 4;
- val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR,
+ val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
TW_AV_STAT_ADDR) & 0x0f;
return val & (1 << ch) ? 1 : 0;
@@ -626,7 +651,7 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev)
int i;
for (i = 0; i < solo_dev->tw28_cnt; i++) {
- val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR,
+ val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
TW_AV_STAT_ADDR) & 0xf0) >> 4;
status |= val << (i * 4);
}
@@ -635,7 +660,8 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev)
}
#endif
-int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val)
+int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
+ s32 val)
{
char sval;
u8 chip_num;
@@ -676,6 +702,7 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val)
break;
case V4L2_CID_SATURATION:
+ /* 286x chips have a U and V component for saturation */
if (is_tw286x(solo_dev, chip_num)) {
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
diff --git a/drivers/staging/media/solo6x10/tw28.h b/drivers/staging/media/solo6x10/tw28.h
index a44a03a..a03b429 100644
--- a/drivers/staging/media/solo6x10/tw28.h
+++ b/drivers/staging/media/solo6x10/tw28.h
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -36,7 +41,7 @@
#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0))
/* tw286x */
-#define TW286X_AV_STAT_ADDR 0xfd
+#define TW286x_AV_STAT_ADDR 0xfd
#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4))
#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4))
#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4))
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 4977e86..b05b328 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -21,33 +26,100 @@
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/videobuf-dma-sg.h>
+
#include "solo6x10.h"
#include "tw28.h"
#include "solo6x10-jpeg.h"
-#define MIN_VID_BUFFERS 4
-#define FRAME_BUF_SIZE (128 * 1024)
+#define MIN_VID_BUFFERS 2
+#define FRAME_BUF_SIZE (196 * 1024)
#define MP4_QS 16
+#define DMA_ALIGN 4096
-static int solo_enc_thread(void *data);
-
-extern unsigned video_nr;
+enum solo_enc_types {
+ SOLO_ENC_TYPE_STD,
+ SOLO_ENC_TYPE_EXT,
+};
struct solo_enc_fh {
struct solo_enc_dev *enc;
u32 fmt;
- u16 rd_idx;
u8 enc_on;
enum solo_enc_types type;
struct videobuf_queue vidq;
struct list_head vidq_active;
- struct task_struct *kthread;
- struct p2m_desc desc[SOLO_NR_P2M_DESC];
+ int desc_count;
+ int desc_nelts;
+ struct solo_p2m_desc *desc_items;
+ dma_addr_t desc_dma;
+ spinlock_t av_lock;
+ struct list_head list;
+};
+
+struct solo_videobuf {
+ struct videobuf_buffer vb;
+ unsigned int flags;
+};
+
+/* 6010 M4V */
+static unsigned char vop_6010_ntsc_d1[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
+ 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40,
+ 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
+ 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f,
+};
+
+static unsigned char vop_6010_ntsc_cif[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
+ 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40,
+ 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
+ 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f,
+};
+
+static unsigned char vop_6010_pal_d1[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
+ 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40,
+ 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
+ 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f,
+};
+
+static unsigned char vop_6010_pal_cif[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
+ 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40,
+ 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
+ 0x1f, 0x4c, 0x2c, 0x10, 0x90, 0x51, 0x18, 0x3f,
+};
+
+/* 6110 h.264 */
+static unsigned char vop_6110_ntsc_d1[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
+ 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00,
+ 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00,
+};
+
+static unsigned char vop_6110_ntsc_cif[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
+ 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00,
+ 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00,
+};
+
+static unsigned char vop_6110_pal_d1[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
+ 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00,
+ 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00,
+};
+
+static unsigned char vop_6110_pal_cif[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
+ 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00,
+ 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00,
};
+
static const u32 solo_user_ctrls[] = {
V4L2_CID_BRIGHTNESS,
V4L2_CID_CONTRAST,
@@ -82,79 +154,179 @@ static const u32 *solo_ctrl_classes[] = {
NULL
};
+struct vop_header {
+ /* VE_STATUS0 */
+ u32 mpeg_size:20, sad_motion_flag:1, video_motion_flag:1, vop_type:2,
+ channel:5, source_fl:1, interlace:1, progressive:1;
+
+ /* VE_STATUS1 */
+ u32 vsize:8, hsize:8, last_queue:4, nop0:8, scale:4;
+
+ /* VE_STATUS2 */
+ u32 mpeg_off;
+
+ /* VE_STATUS3 */
+ u32 jpeg_off;
+
+ /* VE_STATUS4 */
+ u32 jpeg_size:20, interval:10, nop1:2;
+
+ /* VE_STATUS5/6 */
+ u32 sec, usec;
+
+ /* VE_STATUS7/8/9 */
+ u32 nop2[3];
+
+ /* VE_STATUS10 */
+ u32 mpeg_size_alt:20, nop3:12;
+
+ u32 end_nops[5];
+} __packed;
+
+struct solo_enc_buf {
+ enum solo_enc_types type;
+ struct vop_header *vh;
+ int motion;
+};
+
static int solo_is_motion_on(struct solo_enc_dev *solo_enc)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
- u8 ch = solo_enc->ch;
- if (solo_dev->motion_mask & (1 << ch))
- return 1;
- return 0;
+ return (solo_dev->motion_mask >> solo_enc->ch) & 1;
+}
+
+static int solo_motion_detected(struct solo_enc_dev *solo_enc)
+{
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
+ unsigned long flags;
+ u32 ch_mask = 1 << solo_enc->ch;
+ int ret = 0;
+
+ spin_lock_irqsave(&solo_enc->motion_lock, flags);
+ if (solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS) & ch_mask) {
+ solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, ch_mask);
+ ret = 1;
+ }
+ spin_unlock_irqrestore(&solo_enc->motion_lock, flags);
+
+ return ret;
}
static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
- u8 ch = solo_enc->ch;
+ u32 mask = 1 << solo_enc->ch;
+ unsigned long flags;
- spin_lock(&solo_enc->lock);
+ spin_lock_irqsave(&solo_enc->motion_lock, flags);
if (on)
- solo_dev->motion_mask |= (1 << ch);
+ solo_dev->motion_mask |= mask;
else
- solo_dev->motion_mask &= ~(1 << ch);
+ solo_dev->motion_mask &= ~mask;
- /* Do this regardless of if we are turning on or off */
- solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
- 1 << solo_enc->ch);
- solo_enc->motion_detected = 0;
+ solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, mask);
solo_reg_write(solo_dev, SOLO_VI_MOT_ADR,
SOLO_VI_MOTION_EN(solo_dev->motion_mask) |
(SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
- if (solo_dev->motion_mask)
- solo_irq_on(solo_dev, SOLO_IRQ_MOTION);
- else
- solo_irq_off(solo_dev, SOLO_IRQ_MOTION);
-
- spin_unlock(&solo_enc->lock);
+ spin_unlock_irqrestore(&solo_enc->motion_lock, flags);
}
-/* Should be called with solo_enc->lock held */
+/* MUST be called with solo_enc->enable_lock held */
static void solo_update_mode(struct solo_enc_dev *solo_enc)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
-
- assert_spin_locked(&solo_enc->lock);
+ int vop_len;
+ unsigned char *vop;
solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0;
solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1);
- switch (solo_enc->mode) {
- case SOLO_ENC_MODE_CIF:
+ if (solo_enc->mode == SOLO_ENC_MODE_CIF) {
solo_enc->width = solo_dev->video_hsize >> 1;
solo_enc->height = solo_dev->video_vsize;
- break;
- case SOLO_ENC_MODE_D1:
+ if (solo_dev->type == SOLO_DEV_6110) {
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
+ vop = vop_6110_ntsc_cif;
+ vop_len = sizeof(vop_6110_ntsc_cif);
+ } else {
+ vop = vop_6110_pal_cif;
+ vop_len = sizeof(vop_6110_pal_cif);
+ }
+ } else {
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
+ vop = vop_6010_ntsc_cif;
+ vop_len = sizeof(vop_6010_ntsc_cif);
+ } else {
+ vop = vop_6010_pal_cif;
+ vop_len = sizeof(vop_6010_pal_cif);
+ }
+ }
+ } else {
solo_enc->width = solo_dev->video_hsize;
solo_enc->height = solo_dev->video_vsize << 1;
solo_enc->bw_weight <<= 2;
- break;
- default:
- WARN(1, "mode is unknown\n");
+ if (solo_dev->type == SOLO_DEV_6110) {
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
+ vop = vop_6110_ntsc_d1;
+ vop_len = sizeof(vop_6110_ntsc_d1);
+ } else {
+ vop = vop_6110_pal_d1;
+ vop_len = sizeof(vop_6110_pal_d1);
+ }
+ } else {
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
+ vop = vop_6010_ntsc_d1;
+ vop_len = sizeof(vop_6010_ntsc_d1);
+ } else {
+ vop = vop_6010_pal_d1;
+ vop_len = sizeof(vop_6010_pal_d1);
+ }
+ }
}
+
+ memcpy(solo_enc->vop, vop, vop_len);
+
+ /* Some fixups for 6010/M4V */
+ if (solo_dev->type == SOLO_DEV_6010) {
+ u16 fps = solo_dev->fps * 1000;
+ u16 interval = solo_enc->interval * 1000;
+
+ vop = solo_enc->vop;
+
+ /* Frame rate and interval */
+ vop[22] = fps >> 4;
+ vop[23] = ((fps << 4) & 0xf0) | 0x0c
+ | ((interval >> 13) & 0x3);
+ vop[24] = (interval >> 5) & 0xff;
+ vop[25] = ((interval << 3) & 0xf8) | 0x04;
+ }
+
+ solo_enc->vop_len = vop_len;
+
+ /* Now handle the jpeg header */
+ vop = solo_enc->jpeg_header;
+ vop[SOF0_START + 5] = 0xff & (solo_enc->height >> 8);
+ vop[SOF0_START + 6] = 0xff & solo_enc->height;
+ vop[SOF0_START + 7] = 0xff & (solo_enc->width >> 8);
+ vop[SOF0_START + 8] = 0xff & solo_enc->width;
+
+ memcpy(vop + DQT_START,
+ jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN);
}
-/* Should be called with solo_enc->lock held */
-static int solo_enc_on(struct solo_enc_fh *fh)
+/* MUST be called with solo_enc->enable_lock held */
+static int __solo_enc_on(struct solo_enc_fh *fh)
{
struct solo_enc_dev *solo_enc = fh->enc;
u8 ch = solo_enc->ch;
struct solo_dev *solo_dev = solo_enc->solo_dev;
u8 interval;
- assert_spin_locked(&solo_enc->lock);
+ BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
if (fh->enc_on)
return 0;
@@ -170,13 +342,20 @@ static int solo_enc_on(struct solo_enc_fh *fh)
}
fh->enc_on = 1;
- fh->rd_idx = solo_enc->solo_dev->enc_wr_idx;
+ list_add(&fh->list, &solo_enc->listeners);
if (fh->type == SOLO_ENC_TYPE_EXT)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1);
- if (atomic_inc_return(&solo_enc->readers) > 1)
+ /* Reset the encoder if we are the first mpeg reader, else only reset
+ * on the first mjpeg reader. */
+ if (fh->fmt == V4L2_PIX_FMT_MPEG) {
+ atomic_inc(&solo_enc->readers);
+ if (atomic_inc_return(&solo_enc->mpeg_readers) > 1)
+ return 0;
+ } else if (atomic_inc_return(&solo_enc->readers) > 1) {
return 0;
+ }
/* Disable all encoding for this channel */
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0);
@@ -203,701 +382,414 @@ static int solo_enc_on(struct solo_enc_fh *fh)
/* Enables the standard encoder */
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode);
- /* Settle down Beavis... */
- mdelay(10);
-
return 0;
}
-static void solo_enc_off(struct solo_enc_fh *fh)
+static int solo_enc_on(struct solo_enc_fh *fh)
{
struct solo_enc_dev *solo_enc = fh->enc;
- struct solo_dev *solo_dev = solo_enc->solo_dev;
-
- if (!fh->enc_on)
- return;
-
- if (fh->kthread) {
- kthread_stop(fh->kthread);
- fh->kthread = NULL;
- }
-
- solo_dev->enc_bw_remain += solo_enc->bw_weight;
- fh->enc_on = 0;
+ int ret;
- if (atomic_dec_return(&solo_enc->readers) > 0)
- return;
+ mutex_lock(&solo_enc->enable_lock);
+ ret = __solo_enc_on(fh);
+ mutex_unlock(&solo_enc->enable_lock);
- solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0);
- solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0);
+ return ret;
}
-static int solo_start_fh_thread(struct solo_enc_fh *fh)
+static void __solo_enc_off(struct solo_enc_fh *fh)
{
struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
- fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6X10_NAME "_enc");
-
- /* Oops, we had a problem */
- if (IS_ERR(fh->kthread)) {
- spin_lock(&solo_enc->lock);
- solo_enc_off(fh);
- spin_unlock(&solo_enc->lock);
-
- return PTR_ERR(fh->kthread);
- }
+ BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
- return 0;
-}
+ if (!fh->enc_on)
+ return;
-static void enc_reset_gop(struct solo_dev *solo_dev, u8 ch)
-{
- BUG_ON(ch >= solo_dev->nr_chans);
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1);
- solo_dev->v4l2_enc[ch]->reset_gop = 1;
-}
+ list_del(&fh->list);
+ fh->enc_on = 0;
-static int enc_gop_reset(struct solo_dev *solo_dev, u8 ch, u8 vop)
-{
- BUG_ON(ch >= solo_dev->nr_chans);
- if (!solo_dev->v4l2_enc[ch]->reset_gop)
- return 0;
- if (vop)
- return 1;
- solo_dev->v4l2_enc[ch]->reset_gop = 0;
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch),
- solo_dev->v4l2_enc[ch]->gop);
- return 0;
-}
+ if (fh->fmt == V4L2_PIX_FMT_MPEG)
+ atomic_dec(&solo_enc->mpeg_readers);
-static void enc_write_sg(struct scatterlist *sglist, void *buf, int size)
-{
- struct scatterlist *sg;
- u8 *src = buf;
+ if (atomic_dec_return(&solo_enc->readers) > 0)
+ return;
- for (sg = sglist; sg && size > 0; sg = sg_next(sg)) {
- u8 *p = sg_virt(sg);
- size_t len = sg_dma_len(sg);
- int i;
+ solo_dev->enc_bw_remain += solo_enc->bw_weight;
- for (i = 0; i < len && size; i++)
- p[i] = *(src++);
- }
+ solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0);
+ solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0);
}
-static int enc_get_mpeg_dma_sg(struct solo_dev *solo_dev,
- struct p2m_desc *desc,
- struct scatterlist *sglist, int skip,
- unsigned int off, unsigned int size)
+static void solo_enc_off(struct solo_enc_fh *fh)
{
- int ret;
-
- if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
- return -EINVAL;
-
- if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
- return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E,
- desc, 0, sglist, skip,
- SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
- }
-
- /* Buffer wrap */
- ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0,
- sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off,
- SOLO_MP4E_EXT_SIZE(solo_dev) - off);
-
- ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0,
- sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
- SOLO_MP4E_EXT_ADDR(solo_dev),
- size + off - SOLO_MP4E_EXT_SIZE(solo_dev));
+ struct solo_enc_dev *solo_enc = fh->enc;
- return ret;
+ mutex_lock(&solo_enc->enable_lock);
+ __solo_enc_off(fh);
+ mutex_unlock(&solo_enc->enable_lock);
}
-static int enc_get_mpeg_dma_t(struct solo_dev *solo_dev,
- dma_addr_t buf, unsigned int off,
- unsigned int size)
+static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma,
+ unsigned int off, unsigned int size)
{
int ret;
if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
return -EINVAL;
+ /* Single shot */
if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
- return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
- SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
+ return solo_p2m_dma_t(solo_dev, 0, dma,
+ SOLO_MP4E_EXT_ADDR(solo_dev) + off, size,
+ 0, 0);
}
/* Buffer wrap */
- ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
+ ret = solo_p2m_dma_t(solo_dev, 0, dma,
SOLO_MP4E_EXT_ADDR(solo_dev) + off,
- SOLO_MP4E_EXT_SIZE(solo_dev) - off);
-
- ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0,
- buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
- SOLO_MP4E_EXT_ADDR(solo_dev),
- size + off - SOLO_MP4E_EXT_SIZE(solo_dev));
-
- return ret;
-}
+ SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0);
-static int enc_get_mpeg_dma(struct solo_dev *solo_dev, void *buf,
- unsigned int off, unsigned int size)
-{
- int ret;
-
- dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size,
- PCI_DMA_FROMDEVICE);
- ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size);
- pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE);
+ if (!ret) {
+ ret = solo_p2m_dma_t(solo_dev, 0,
+ dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
+ SOLO_MP4E_EXT_ADDR(solo_dev),
+ size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0);
+ }
return ret;
}
-static int enc_get_jpeg_dma_sg(struct solo_dev *solo_dev,
- struct p2m_desc *desc,
- struct scatterlist *sglist, int skip,
- unsigned int off, unsigned int size)
+/* Build a descriptor queue out of an SG list and send it to the P2M for
+ * processing. */
+static int solo_send_desc(struct solo_enc_fh *fh, int skip,
+ struct videobuf_dmabuf *vbuf, int off, int size,
+ unsigned int base, unsigned int base_size)
{
+ struct solo_dev *solo_dev = fh->enc->solo_dev;
+ struct scatterlist *sg;
+ int i;
int ret;
- if (off > SOLO_JPEG_EXT_SIZE(solo_dev))
+ if (WARN_ON_ONCE(size > FRAME_BUF_SIZE))
return -EINVAL;
- if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) {
- return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG,
- desc, 0, sglist, skip,
- SOLO_JPEG_EXT_ADDR(solo_dev) + off, size);
- }
-
- /* Buffer wrap */
- ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
- sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off,
- SOLO_JPEG_EXT_SIZE(solo_dev) - off);
+ fh->desc_count = 1;
+
+ for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
+ struct solo_p2m_desc *desc;
+ dma_addr_t dma;
+ int len;
+ int left = base_size - off;
+
+ desc = &fh->desc_items[fh->desc_count++];
+ dma = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+
+ /* We assume this is smaller than the scatter size */
+ BUG_ON(skip >= len);
+ if (skip) {
+ len -= skip;
+ dma += skip;
+ size -= skip;
+ skip = 0;
+ }
- ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
- sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
- SOLO_JPEG_EXT_ADDR(solo_dev),
- size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
+ len = min(len, size);
- return ret;
-}
+ if (len <= left) {
+ /* Single descriptor */
+ solo_p2m_fill_desc(desc, 0, dma, base + off,
+ len, 0, 0);
+ } else {
+ /* Buffer wrap */
+ /* XXX: Do these as separate DMA requests, to avoid
+ timeout errors triggered by awkwardly sized
+ descriptors. See
+ <https://github.com/bluecherrydvr/solo6x10/issues/8>
+ */
+ ret = solo_p2m_dma_t(solo_dev, 0, dma, base + off,
+ left, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = solo_p2m_dma_t(solo_dev, 0, dma + left, base,
+ len - left, 0, 0);
+ if (ret)
+ return ret;
+
+ fh->desc_count--;
+ }
-/* Returns true of __chk is within the first __range bytes of __off */
-#define OFF_IN_RANGE(__off, __range, __chk) \
- ((__off <= __chk) && ((__off + __range) >= __chk))
+ size -= len;
+ if (size <= 0)
+ break;
-static void solo_jpeg_header(struct solo_enc_dev *solo_enc,
- struct videobuf_dmabuf *vbuf)
-{
- struct scatterlist *sg;
- void *src = jpeg_header;
- size_t copied = 0;
- size_t to_copy = sizeof(jpeg_header);
-
- for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) {
- size_t this_copy = min(sg_dma_len(sg),
- (unsigned int)(to_copy - copied));
- u8 *p = sg_virt(sg);
-
- memcpy(p, src + copied, this_copy);
-
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5))
- p[(SOF0_START + 5) - copied] =
- 0xff & (solo_enc->height >> 8);
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6))
- p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height;
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7))
- p[(SOF0_START + 7) - copied] =
- 0xff & (solo_enc->width >> 8);
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8))
- p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width;
-
- copied += this_copy;
+ off += len;
+ if (off >= base_size)
+ off -= base_size;
+
+ /* Because we may use two descriptors per loop */
+ if (fh->desc_count >= (fh->desc_nelts - 1)) {
+ ret = solo_p2m_dma_desc(solo_dev, fh->desc_items,
+ fh->desc_dma,
+ fh->desc_count - 1);
+ if (ret)
+ return ret;
+ fh->desc_count = 1;
+ }
}
-}
-
-static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
- struct videobuf_buffer *vb,
- struct videobuf_dmabuf *vbuf)
-{
- struct solo_dev *solo_dev = fh->enc->solo_dev;
- int size = enc_buf->jpeg_size;
-
- /* Copy the header first (direct write) */
- solo_jpeg_header(fh->enc, vbuf);
-
- vb->size = size + sizeof(jpeg_header);
-
- /* Grab the jpeg frame */
- return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
- sizeof(jpeg_header),
- enc_buf->jpeg_off, size);
-}
-
-static inline int vop_interlaced(__le32 *vh)
-{
- return (__le32_to_cpu(vh[0]) >> 30) & 1;
-}
-static inline u32 vop_size(__le32 *vh)
-{
- return __le32_to_cpu(vh[0]) & 0xFFFFF;
-}
-
-static inline u8 vop_hsize(__le32 *vh)
-{
- return (__le32_to_cpu(vh[1]) >> 8) & 0xFF;
-}
-
-static inline u8 vop_vsize(__le32 *vh)
-{
- return __le32_to_cpu(vh[1]) & 0xFF;
-}
-
-/* must be called with *bits % 8 = 0 */
-static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count)
-{
- memcpy(*out, src, count);
- *out += count;
- *bits += count * 8;
-}
-
-static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count)
-{
-
- value <<= 32 - count; // shift to the right
+ if (fh->desc_count <= 1)
+ return 0;
- while (count--) {
- **out <<= 1;
- **out |= !!(value & (1 << 31)); /* MSB */
- value <<= 1;
- if (++(*bits) % 8 == 0)
- (*out)++;
- }
+ return solo_p2m_dma_desc(solo_dev, fh->desc_items, fh->desc_dma,
+ fh->desc_count - 1);
}
-static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */
+static int solo_fill_jpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
+ struct videobuf_dmabuf *vbuf, struct vop_header *vh)
{
- uint32_t max = 0, cnt = 0;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
+ struct solo_videobuf *svb = (struct solo_videobuf *)vb;
+ int frame_size;
- while (value > max) {
- max = (max + 2) * 2 - 2;
- cnt++;
- }
- write_bits(out, bits, 1, cnt + 1);
- write_bits(out, bits, ~(max - value), cnt);
-}
+ svb->flags |= V4L2_BUF_FLAG_KEYFRAME;
-static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */
-{
- if (value <= 0)
- write_ue(out, bits, -value * 2);
- else
- write_ue(out, bits, value * 2 - 1);
-}
+ if (vb->bsize < (vh->jpeg_size + solo_enc->jpeg_len))
+ return -EIO;
-static void write_mpeg4_end(u8 **out, unsigned *bits)
-{
- write_bits(out, bits, 0, 1);
- /* align on 32-bit boundary */
- if (*bits % 32)
- write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32);
-}
+ vb->width = solo_enc->width;
+ vb->height = solo_enc->height;
+ vb->size = vh->jpeg_size + solo_enc->jpeg_len;
-static void write_h264_end(u8 **out, unsigned *bits, int align)
-{
- write_bits(out, bits, 1, 1);
- while ((*bits) % 8)
- write_bits(out, bits, 0, 1);
- if (align)
- while ((*bits) % 32)
- write_bits(out, bits, 0, 1);
-}
+ sg_copy_from_buffer(vbuf->sglist, vbuf->sglen,
+ solo_enc->jpeg_header,
+ solo_enc->jpeg_len);
-static void mpeg4_write_vol(u8 **out, struct solo_dev *solo_dev,
- __le32 *vh, unsigned fps, unsigned interval)
-{
- static const u8 hdr[] = {
- 0, 0, 1, 0x00 /* video_object_start_code */,
- 0, 0, 1, 0x20 /* video_object_layer_start_code */
- };
- unsigned bits = 0;
- unsigned width = vop_hsize(vh) << 4;
- unsigned height = vop_vsize(vh) << 4;
- unsigned interlaced = vop_interlaced(vh);
-
- write_bytes(out, &bits, hdr, sizeof(hdr));
- write_bits(out, &bits, 0, 1); /* random_accessible_vol */
- write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */
- write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */
- write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */
- write_bits(out, &bits, 0, 3); /* video_object_layer_priority */
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
- write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */
- else
- write_bits(out, &bits, 2, 4);
- write_bits(out, &bits, 1, 1); /* vol_control_parameters */
- write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */
- write_bits(out, &bits, 1, 1); /* low_delay */
- write_bits(out, &bits, 0, 1); /* vbv_parameters */
- write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, 1, 1); /* fixed_vop_rate */
- write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, width, 13); /* video_object_layer_width */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, height, 13); /* video_object_layer_height */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, interlaced, 1); /* interlaced */
- write_bits(out, &bits, 1, 1); /* obmc_disable */
- write_bits(out, &bits, 0, 2); /* sprite_enable */
- write_bits(out, &bits, 0, 1); /* not_8_bit */
- write_bits(out, &bits, 1, 0); /* quant_type */
- write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */
- write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */
- write_bits(out, &bits, 0, 1); /* quarter_sample */
- write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */
- write_bits(out, &bits, 1, 1); /* resync_marker_disable */
- write_bits(out, &bits, 0, 1); /* data_partitioned */
- write_bits(out, &bits, 0, 1); /* newpred_enable */
- write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */
- write_bits(out, &bits, 0, 1); /* scalability */
- write_mpeg4_end(out, &bits);
-}
+ frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1))
+ & ~(DMA_ALIGN - 1);
-static void h264_write_vol(u8 **out, struct solo_dev *solo_dev, __le32 *vh)
-{
- static const u8 sps[] = {
- 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */,
- 0 /* constraints */, 30 /* level_idc */
- };
- static const u8 pps[] = {
- 0, 0, 0, 1 /* start code */, 0x68
- };
-
- unsigned bits = 0;
- unsigned mbs_w = vop_hsize(vh);
- unsigned mbs_h = vop_vsize(vh);
-
- write_bytes(out, &bits, sps, sizeof(sps));
- write_ue(out, &bits, 0); /* seq_parameter_set_id */
- write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */
- write_ue(out, &bits, 0); /* pic_order_cnt_type */
- write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */
- write_ue(out, &bits, 1); /* max_num_ref_frames */
- write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */
- write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */
- write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */
- write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */
- write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */
- write_bits(out, &bits, 0, 1); /* frame_cropping_flag */
- write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */
- write_h264_end(out, &bits, 0);
-
- write_bytes(out, &bits, pps, sizeof(pps));
- write_ue(out, &bits, 0); /* pic_parameter_set_id */
- write_ue(out, &bits, 0); /* seq_parameter_set_id */
- write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */
- write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */
- write_ue(out, &bits, 0); /* num_slice_groups_minus1 */
- write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */
- write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */
- write_bits(out, &bits, 0, 1); /* weighted_pred_flag */
- write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */
- write_se(out, &bits, 0); /* pic_init_qp_minus26 */
- write_se(out, &bits, 0); /* pic_init_qs_minus26 */
- write_se(out, &bits, 2); /* chroma_qp_index_offset */
- write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */
- write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */
- write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */
- write_h264_end(out, &bits, 1);
+ return solo_send_desc(fh, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
+ frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
+ SOLO_JPEG_EXT_SIZE(solo_dev));
}
-static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
- struct videobuf_buffer *vb,
- struct videobuf_dmabuf *vbuf)
+static int solo_fill_mpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
+ struct videobuf_dmabuf *vbuf, struct vop_header *vh)
{
struct solo_enc_dev *solo_enc = fh->enc;
struct solo_dev *solo_dev = solo_enc->solo_dev;
-
-#define VH_WORDS 16
-#define MAX_VOL_HEADER_LENGTH 64
-
- __le32 vh[VH_WORDS];
- int ret;
- int frame_size, frame_off;
+ struct solo_videobuf *svb = (struct solo_videobuf *)vb;
+ int frame_off, frame_size;
int skip = 0;
- if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh)))
- return -EINVAL;
-
- /* First get the hardware vop header (not real mpeg) */
- ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh));
- if (WARN_ON_ONCE(ret))
- return ret;
+ if (vb->bsize < vh->mpeg_size)
+ return -EIO;
- if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size))
- return -EINVAL;
+ vb->width = vh->hsize << 4;
+ vb->height = vh->vsize << 4;
+ vb->size = vh->mpeg_size;
- vb->width = vop_hsize(vh) << 4;
- vb->height = vop_vsize(vh) << 4;
- vb->size = vop_size(vh);
+ /* If this is a key frame, add extra header */
+ if (!vh->vop_type) {
+ sg_copy_from_buffer(vbuf->sglist, vbuf->sglen,
+ solo_enc->vop,
+ solo_enc->vop_len);
- /* If this is a key frame, add extra m4v header */
- if (!enc_buf->vop) {
- u8 header[MAX_VOL_HEADER_LENGTH], *out = header;
+ skip = solo_enc->vop_len;
+ vb->size += solo_enc->vop_len;
- if (solo_dev->flags & FLAGS_6110)
- h264_write_vol(&out, solo_dev, vh);
- else
- mpeg4_write_vol(&out, solo_dev, vh,
- solo_dev->fps * 1000,
- solo_enc->interval * 1000);
- skip = out - header;
- enc_write_sg(vbuf->sglist, header, skip);
- /* Adjust the dma buffer past this header */
- vb->size += skip;
+ svb->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ } else {
+ svb->flags |= V4L2_BUF_FLAG_PFRAME;
}
/* Now get the actual mpeg payload */
- frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev);
- frame_size = enc_buf->size - sizeof(vh);
-
- ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
- skip, frame_off, frame_size);
- WARN_ON_ONCE(ret);
+ frame_off = (vh->mpeg_off + sizeof(*vh))
+ % SOLO_MP4E_EXT_SIZE(solo_dev);
+ frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1))
+ & ~(DMA_ALIGN - 1);
- return ret;
+ return solo_send_desc(fh, skip, vbuf, frame_off, frame_size,
+ SOLO_MP4E_EXT_ADDR(solo_dev),
+ SOLO_MP4E_EXT_SIZE(solo_dev));
}
-static void solo_enc_fillbuf(struct solo_enc_fh *fh,
- struct videobuf_buffer *vb)
+static int solo_enc_fillbuf(struct solo_enc_fh *fh,
+ struct videobuf_buffer *vb,
+ struct solo_enc_buf *enc_buf)
{
struct solo_enc_dev *solo_enc = fh->enc;
- struct solo_dev *solo_dev = solo_enc->solo_dev;
- struct solo_enc_buf *enc_buf = NULL;
- struct videobuf_dmabuf *vbuf;
+ struct solo_videobuf *svb = (struct solo_videobuf *)vb;
+ struct videobuf_dmabuf *vbuf = NULL;
+ struct vop_header *vh = enc_buf->vh;
int ret;
- int error = 1;
- u16 idx = fh->rd_idx;
-
- while (idx != solo_dev->enc_wr_idx) {
- struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx];
- idx = (idx + 1) % SOLO_NR_RING_BUFS;
-
- if (ebuf->ch != solo_enc->ch)
- continue;
-
- if (fh->fmt == V4L2_PIX_FMT_MPEG) {
- if (fh->type == ebuf->type) {
- enc_buf = ebuf;
- break;
- }
- } else {
- /* For mjpeg, keep reading to the newest frame */
- enc_buf = ebuf;
- }
+ vbuf = videobuf_to_dma(vb);
+ if (WARN_ON_ONCE(!vbuf)) {
+ ret = -EIO;
+ goto vbuf_error;
}
- fh->rd_idx = idx;
-
- if (WARN_ON_ONCE(!enc_buf))
- goto buf_err;
+ /* Setup some common flags for both types */
+ svb->flags = V4L2_BUF_FLAG_TIMECODE;
+ vb->ts.tv_sec = vh->sec;
+ vb->ts.tv_usec = vh->usec;
- if ((fh->fmt == V4L2_PIX_FMT_MPEG &&
- vb->bsize < enc_buf->size) ||
- (fh->fmt == V4L2_PIX_FMT_MJPEG &&
- vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) {
- WARN_ON_ONCE(1);
- goto buf_err;
+ /* Check for motion flags */
+ if (solo_is_motion_on(solo_enc)) {
+ svb->flags |= V4L2_BUF_FLAG_MOTION_ON;
+ if (enc_buf->motion)
+ svb->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
}
- vbuf = videobuf_to_dma(vb);
- if (WARN_ON_ONCE(!vbuf))
- goto buf_err;
-
if (fh->fmt == V4L2_PIX_FMT_MPEG)
- ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf);
+ ret = solo_fill_mpeg(fh, vb, vbuf, vh);
else
- ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf);
+ ret = solo_fill_jpeg(fh, vb, vbuf, vh);
- if (!ret)
- error = 0;
+vbuf_error:
+ /* On error, we push this buffer back into the queue. The
+ * videobuf-core doesn't handle error packets very well. Plus
+ * we recover nicely internally anyway. */
+ if (ret) {
+ unsigned long flags;
-buf_err:
- if (error) {
- vb->state = VIDEOBUF_ERROR;
+ spin_lock_irqsave(&fh->av_lock, flags);
+ list_add(&vb->queue, &fh->vidq_active);
+ vb->state = VIDEOBUF_QUEUED;
+ spin_unlock_irqrestore(&fh->av_lock, flags);
} else {
- vb->field_count++;
- vb->ts = enc_buf->ts;
vb->state = VIDEOBUF_DONE;
- }
+ vb->field_count++;
+ vb->width = solo_enc->width;
+ vb->height = solo_enc->height;
- wake_up(&vb->done);
+ wake_up(&vb->done);
+ }
- return;
+ return ret;
}
-static void solo_enc_thread_try(struct solo_enc_fh *fh)
+static void solo_enc_handle_one(struct solo_enc_dev *solo_enc,
+ struct solo_enc_buf *enc_buf)
{
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo_dev *solo_dev = solo_enc->solo_dev;
- struct videobuf_buffer *vb;
+ struct solo_enc_fh *fh;
- for (;;) {
- spin_lock(&solo_enc->lock);
+ mutex_lock(&solo_enc->enable_lock);
+
+ list_for_each_entry(fh, &solo_enc->listeners, list) {
+ struct videobuf_buffer *vb;
+ unsigned long flags;
+
+ if (fh->type != enc_buf->type)
+ continue;
- if (fh->rd_idx == solo_dev->enc_wr_idx)
- break;
if (list_empty(&fh->vidq_active))
- break;
+ continue;
+
+ spin_lock_irqsave(&fh->av_lock, flags);
vb = list_first_entry(&fh->vidq_active,
struct videobuf_buffer, queue);
- if (!waitqueue_active(&vb->done))
- break;
-
list_del(&vb->queue);
+ vb->state = VIDEOBUF_ACTIVE;
- spin_unlock(&solo_enc->lock);
-
- solo_enc_fillbuf(fh, vb);
- }
-
- assert_spin_locked(&solo_enc->lock);
- spin_unlock(&solo_enc->lock);
-}
-
-static int solo_enc_thread(void *data)
-{
- struct solo_enc_fh *fh = data;
- struct solo_enc_dev *solo_enc = fh->enc;
- DECLARE_WAITQUEUE(wait, current);
+ spin_unlock_irqrestore(&fh->av_lock, flags);
- set_freezable();
- add_wait_queue(&solo_enc->thread_wait, &wait);
-
- for (;;) {
- long timeout = schedule_timeout_interruptible(HZ);
- if (timeout == -ERESTARTSYS || kthread_should_stop())
- break;
- solo_enc_thread_try(fh);
- try_to_freeze();
+ solo_enc_fillbuf(fh, vb, enc_buf);
}
- remove_wait_queue(&solo_enc->thread_wait, &wait);
-
- return 0;
+ mutex_unlock(&solo_enc->enable_lock);
}
-void solo_motion_isr(struct solo_dev *solo_dev)
+void solo_enc_v4l2_isr(struct solo_dev *solo_dev)
{
- u32 status;
- int i;
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION);
-
- status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS);
-
- for (i = 0; i < solo_dev->nr_chans; i++) {
- struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i];
-
- BUG_ON(solo_enc == NULL);
-
- if (solo_enc->motion_detected)
- continue;
- if (!(status & (1 << i)))
- continue;
-
- solo_enc->motion_detected = 1;
- }
+ wake_up_interruptible_all(&solo_dev->ring_thread_wait);
}
-void solo_enc_v4l2_isr(struct solo_dev *solo_dev)
+static void solo_handle_ring(struct solo_dev *solo_dev)
{
- struct solo_enc_buf *enc_buf;
- u32 mpeg_current, mpeg_next, mpeg_size;
- u32 jpeg_current, jpeg_next, jpeg_size;
- u32 reg_mpeg_size;
- u8 cur_q, vop_type;
- u8 ch;
- enum solo_enc_types enc_type;
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER);
-
- cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS;
-
- reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7;
+ for (;;) {
+ struct solo_enc_dev *solo_enc;
+ struct solo_enc_buf enc_buf;
+ u32 mpeg_current, off;
+ u8 ch;
+ u8 cur_q;
+
+ /* Check if the hardware has any new ones in the queue */
+ cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff;
+ if (cur_q == solo_dev->enc_idx)
+ break;
- while (solo_dev->enc_idx != cur_q) {
mpeg_current = solo_reg_read(solo_dev,
SOLO_VE_MPEG4_QUE(solo_dev->enc_idx));
- jpeg_current = solo_reg_read(solo_dev,
- SOLO_VE_JPEG_QUE(solo_dev->enc_idx));
solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS;
- mpeg_next = solo_reg_read(solo_dev,
- SOLO_VE_MPEG4_QUE(solo_dev->enc_idx));
- jpeg_next = solo_reg_read(solo_dev,
- SOLO_VE_JPEG_QUE(solo_dev->enc_idx));
ch = (mpeg_current >> 24) & 0x1f;
+ off = mpeg_current & 0x00ffffff;
+
if (ch >= SOLO_MAX_CHANNELS) {
ch -= SOLO_MAX_CHANNELS;
- enc_type = SOLO_ENC_TYPE_EXT;
+ enc_buf.type = SOLO_ENC_TYPE_EXT;
} else
- enc_type = SOLO_ENC_TYPE_STD;
-
- vop_type = (mpeg_current >> 29) & 3;
-
- mpeg_current &= 0x00ffffff;
- mpeg_next &= 0x00ffffff;
- jpeg_current &= 0x00ffffff;
- jpeg_next &= 0x00ffffff;
-
- mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) +
- mpeg_next - mpeg_current) %
- SOLO_MP4E_EXT_SIZE(solo_dev);
-
- jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) +
- jpeg_next - jpeg_current) %
- SOLO_JPEG_EXT_SIZE(solo_dev);
+ enc_buf.type = SOLO_ENC_TYPE_STD;
- /* XXX I think this means we had a ring overflow? */
- if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) {
- enc_reset_gop(solo_dev, ch);
+ solo_enc = solo_dev->v4l2_enc[ch];
+ if (solo_enc == NULL) {
+ dev_err(&solo_dev->pdev->dev,
+ "Got spurious packet for channel %d\n", ch);
continue;
}
- /* When resetting the GOP, skip frames until I-frame */
- if (enc_gop_reset(solo_dev, ch, vop_type))
+ /* FAIL... */
+ if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off,
+ sizeof(struct vop_header)))
continue;
- enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx];
+ enc_buf.vh = (struct vop_header *)solo_dev->vh_buf;
+ enc_buf.vh->mpeg_off -= SOLO_MP4E_EXT_ADDR(solo_dev);
+ enc_buf.vh->jpeg_off -= SOLO_JPEG_EXT_ADDR(solo_dev);
- enc_buf->vop = vop_type;
- enc_buf->ch = ch;
- enc_buf->off = mpeg_current;
- enc_buf->size = mpeg_size;
- enc_buf->jpeg_off = jpeg_current;
- enc_buf->jpeg_size = jpeg_size;
- enc_buf->type = enc_type;
+ /* Sanity check */
+ if (enc_buf.vh->mpeg_off != off)
+ continue;
+
+ if (solo_motion_detected(solo_enc))
+ enc_buf.motion = 1;
+ else
+ enc_buf.motion = 0;
+
+ solo_enc_handle_one(solo_enc, &enc_buf);
+ }
+}
- do_gettimeofday(&enc_buf->ts);
+static int solo_ring_thread(void *data)
+{
+ struct solo_dev *solo_dev = data;
+ DECLARE_WAITQUEUE(wait, current);
- solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) %
- SOLO_NR_RING_BUFS;
+ set_freezable();
+ add_wait_queue(&solo_dev->ring_thread_wait, &wait);
- wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait);
+ for (;;) {
+ long timeout = schedule_timeout_interruptible(HZ);
+ if (timeout == -ERESTARTSYS || kthread_should_stop())
+ break;
+ solo_irq_off(solo_dev, SOLO_IRQ_ENCODER);
+ solo_handle_ring(solo_dev);
+ solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
+ try_to_freeze();
}
- return;
+ remove_wait_queue(&solo_dev->ring_thread_wait, &wait);
+
+ return 0;
}
static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
@@ -915,17 +807,12 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
- struct solo_enc_fh *fh = vq->priv_data;
- struct solo_enc_dev *solo_enc = fh->enc;
-
vb->size = FRAME_BUF_SIZE;
if (vb->baddr != 0 && vb->bsize < vb->size)
return -EINVAL;
- /* These properties only change when queue is idle */
- vb->width = solo_enc->width;
- vb->height = solo_enc->height;
- vb->field = field;
+ /* This property only change when queue is idle */
+ vb->field = field;
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL);
@@ -933,7 +820,7 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq,
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma);
- vb->state = VIDEOBUF_NEEDS_INIT;
+
return rc;
}
}
@@ -949,20 +836,18 @@ static void solo_enc_buf_queue(struct videobuf_queue *vq,
vb->state = VIDEOBUF_QUEUED;
list_add_tail(&vb->queue, &fh->vidq_active);
- wake_up_interruptible(&fh->enc->thread_wait);
}
static void solo_enc_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
}
-static struct videobuf_queue_ops solo_enc_video_qops = {
+static const struct videobuf_queue_ops solo_enc_video_qops = {
.buf_setup = solo_enc_buf_setup,
.buf_prepare = solo_enc_buf_prepare,
.buf_queue = solo_enc_buf_queue,
@@ -984,27 +869,78 @@ static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma)
return videobuf_mmap_mapper(&fh->vidq, vma);
}
+static int solo_ring_start(struct solo_dev *solo_dev)
+{
+ if (atomic_inc_return(&solo_dev->enc_users) > 1)
+ return 0;
+
+ solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev,
+ SOLO6X10_NAME "_ring");
+ if (IS_ERR(solo_dev->ring_thread)) {
+ int err = PTR_ERR(solo_dev->ring_thread);
+ solo_dev->ring_thread = NULL;
+ return err;
+ }
+
+ solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
+
+ return 0;
+}
+
+static void solo_ring_stop(struct solo_dev *solo_dev)
+{
+ if (atomic_dec_return(&solo_dev->enc_users) > 0)
+ return;
+
+ if (solo_dev->ring_thread) {
+ kthread_stop(solo_dev->ring_thread);
+ solo_dev->ring_thread = NULL;
+ }
+
+ solo_irq_off(solo_dev, SOLO_IRQ_ENCODER);
+}
+
static int solo_enc_open(struct file *file)
{
struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
struct solo_enc_fh *fh;
+ int ret;
+
+ ret = solo_ring_start(solo_dev);
+ if (ret)
+ return ret;
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL)
+ if (fh == NULL) {
+ solo_ring_stop(solo_dev);
+ return -ENOMEM;
+ }
+
+ fh->desc_nelts = 32;
+ fh->desc_items = pci_alloc_consistent(solo_dev->pdev,
+ sizeof(struct solo_p2m_desc) *
+ fh->desc_nelts, &fh->desc_dma);
+ if (fh->desc_items == NULL) {
+ kfree(fh);
+ solo_ring_stop(solo_dev);
return -ENOMEM;
+ }
fh->enc = solo_enc;
+ spin_lock_init(&fh->av_lock);
file->private_data = fh;
INIT_LIST_HEAD(&fh->vidq_active);
fh->fmt = V4L2_PIX_FMT_MPEG;
fh->type = SOLO_ENC_TYPE_STD;
videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
- &solo_enc->solo_dev->pdev->dev,
- &solo_enc->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct videobuf_buffer), fh, NULL);
+ &solo_dev->pdev->dev,
+ &fh->av_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct solo_videobuf),
+ fh, NULL);
return 0;
}
@@ -1013,22 +949,12 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
struct solo_enc_fh *fh = file->private_data;
- struct solo_enc_dev *solo_enc = fh->enc;
+ int ret;
/* Make sure the encoder is on */
- if (!fh->enc_on) {
- int ret;
-
- spin_lock(&solo_enc->lock);
- ret = solo_enc_on(fh);
- spin_unlock(&solo_enc->lock);
- if (ret)
- return ret;
-
- ret = solo_start_fh_thread(fh);
- if (ret)
- return ret;
- }
+ ret = solo_enc_on(fh);
+ if (ret)
+ return ret;
return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
@@ -1037,17 +963,21 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
static int solo_enc_release(struct file *file)
{
struct solo_enc_fh *fh = file->private_data;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_dev *solo_dev = fh->enc->solo_dev;
+
+ solo_enc_off(fh);
videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq);
- spin_lock(&solo_enc->lock);
- solo_enc_off(fh);
- spin_unlock(&solo_enc->lock);
+ pci_free_consistent(fh->enc->solo_dev->pdev,
+ sizeof(struct solo_p2m_desc) *
+ fh->desc_nelts, fh->desc_items, fh->desc_dma);
kfree(fh);
+ solo_ring_stop(solo_dev);
+
return 0;
}
@@ -1095,7 +1025,8 @@ static int solo_enc_enum_input(struct file *file, void *priv,
return 0;
}
-static int solo_enc_set_input(struct file *file, void *priv, unsigned int index)
+static int solo_enc_set_input(struct file *file, void *priv,
+ unsigned int index)
{
if (index)
return -EINVAL;
@@ -1144,8 +1075,8 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
pix->pixelformat != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
- /* We cannot change width/height in mid read */
- if (atomic_read(&solo_enc->readers) > 0) {
+ /* We cannot change width/height in mid mpeg */
+ if (atomic_read(&solo_enc->mpeg_readers) > 0) {
if (pix->width != solo_enc->width ||
pix->height != solo_enc->height)
return -EBUSY;
@@ -1183,11 +1114,11 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
- spin_lock(&solo_enc->lock);
+ mutex_lock(&solo_enc->enable_lock);
ret = solo_enc_try_fmt_cap(file, priv, f);
if (ret) {
- spin_unlock(&solo_enc->lock);
+ mutex_unlock(&solo_enc->enable_lock);
return ret;
}
@@ -1201,14 +1132,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
if (pix->priv)
fh->type = SOLO_ENC_TYPE_EXT;
- ret = solo_enc_on(fh);
-
- spin_unlock(&solo_enc->lock);
- if (ret)
- return ret;
+ mutex_unlock(&solo_enc->enable_lock);
- return solo_start_fh_thread(fh);
+ return 0;
}
static int solo_enc_get_fmt_cap(struct file *file, void *priv,
@@ -1245,7 +1172,8 @@ static int solo_enc_querybuf(struct file *file, void *priv,
return videobuf_querybuf(&fh->vidq, buf);
}
-static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int solo_enc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
{
struct solo_enc_fh *fh = priv;
@@ -1256,50 +1184,21 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_videobuf *svb;
int ret;
/* Make sure the encoder is on */
- if (!fh->enc_on) {
- spin_lock(&solo_enc->lock);
- ret = solo_enc_on(fh);
- spin_unlock(&solo_enc->lock);
- if (ret)
- return ret;
-
- ret = solo_start_fh_thread(fh);
- if (ret)
- return ret;
- }
+ ret = solo_enc_on(fh);
+ if (ret)
+ return ret;
ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
if (ret)
return ret;
- /* Signal motion detection */
- if (solo_is_motion_on(solo_enc)) {
- buf->flags |= V4L2_BUF_FLAG_MOTION_ON;
- if (solo_enc->motion_detected) {
- buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
- solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
- 1 << solo_enc->ch);
- solo_enc->motion_detected = 0;
- }
- }
-
- /* Check for key frame on mpeg data */
- if (fh->fmt == V4L2_PIX_FMT_MPEG) {
- struct videobuf_dmabuf *vbuf =
- videobuf_to_dma(fh->vidq.bufs[buf->index]);
-
- if (vbuf) {
- u8 *p = sg_virt(vbuf->sglist);
- if (p[3] == 0x00)
- buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
- else
- buf->flags |= V4L2_BUF_FLAG_PFRAME;
- }
- }
+ /* Copy over the flags */
+ svb = (struct solo_videobuf *)fh->vidq.bufs[buf->index];
+ buf->flags |= svb->flags;
return 0;
}
@@ -1319,11 +1218,16 @@ static int solo_enc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type i)
{
struct solo_enc_fh *fh = priv;
+ int ret;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return videobuf_streamoff(&fh->vidq);
+ ret = videobuf_streamoff(&fh->vidq);
+ if (!ret)
+ solo_enc_off(fh);
+
+ return ret;
}
static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i)
@@ -1407,10 +1311,10 @@ static int solo_s_parm(struct file *file, void *priv,
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_captureparm *cp = &sp->parm.capture;
- spin_lock(&solo_enc->lock);
+ mutex_lock(&solo_enc->enable_lock);
- if (atomic_read(&solo_enc->readers) > 0) {
- spin_unlock(&solo_enc->lock);
+ if (atomic_read(&solo_enc->mpeg_readers) > 0) {
+ mutex_unlock(&solo_enc->enable_lock);
return -EBUSY;
}
@@ -1431,10 +1335,9 @@ static int solo_s_parm(struct file *file, void *priv,
cp->capability = V4L2_CAP_TIMEPERFRAME;
- solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1);
solo_update_mode(solo_enc);
- spin_unlock(&solo_enc->lock);
+ mutex_unlock(&solo_enc->enable_lock);
return 0;
}
@@ -1510,6 +1413,7 @@ static int solo_querymenu(struct file *file, void *priv,
int err;
qctrl.id = qmenu->id;
+
err = solo_queryctrl(file, priv, &qctrl);
if (err)
return err;
@@ -1536,6 +1440,8 @@ static int solo_g_ctrl(struct file *file, void *priv,
ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
break;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ if (atomic_read(&solo_enc->readers) > 0)
+ return -EBUSY;
ctrl->value = solo_enc->gop;
break;
case V4L2_CID_MOTION_THRESHOLD:
@@ -1574,19 +1480,32 @@ static int solo_s_ctrl(struct file *file, void *priv,
if (ctrl->value < 1 || ctrl->value > 255)
return -ERANGE;
solo_enc->gop = ctrl->value;
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch),
- solo_enc->gop);
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch),
- solo_enc->gop);
break;
case V4L2_CID_MOTION_THRESHOLD:
- /* TODO accept value on lower 16-bits and use high
- * 16-bits to assign the value to a specific block */
- if (ctrl->value < 0 || ctrl->value > 0xffff)
+ {
+ u16 block = (ctrl->value >> 16) & 0xffff;
+ u16 value = ctrl->value & 0xffff;
+
+ /* Motion thresholds are in a table of 64x64 samples, with
+ * each sample representing 16x16 pixels of the source. In
+ * effect, 44x30 samples are used for NTSC, and 44x36 for PAL.
+ * The 5th sample on the 10th row is (10*64)+5 = 645.
+ *
+ * Block is 0 to set the threshold globally, or any positive
+ * number under 2049 to set block-1 individually. */
+ if (block > 2049)
return -ERANGE;
- solo_enc->motion_thresh = ctrl->value;
- solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value);
+
+ if (block == 0) {
+ solo_enc->motion_thresh = value;
+ return solo_set_motion_threshold(solo_dev,
+ solo_enc->ch, value);
+ } else {
+ return solo_set_motion_block(solo_dev, solo_enc->ch,
+ value, block - 1);
+ }
break;
+ }
case V4L2_CID_MOTION_ENABLE:
solo_motion_toggle(solo_enc, ctrl->value);
break;
@@ -1613,6 +1532,7 @@ static int solo_s_ext_ctrls(struct file *file, void *priv,
if (ctrl->size - 1 > OSD_TEXT_MAX)
err = -ERANGE;
else {
+ mutex_lock(&solo_enc->enable_lock);
err = copy_from_user(solo_enc->osd_text,
ctrl->string,
OSD_TEXT_MAX);
@@ -1621,6 +1541,7 @@ static int solo_s_ext_ctrls(struct file *file, void *priv,
err = solo_osd_print(solo_enc);
else
err = -EFAULT;
+ mutex_unlock(&solo_enc->enable_lock);
}
break;
default:
@@ -1653,11 +1574,13 @@ static int solo_g_ext_ctrls(struct file *file, void *priv,
ctrl->size = OSD_TEXT_MAX;
err = -ENOSPC;
} else {
+ mutex_lock(&solo_enc->enable_lock);
err = copy_to_user(ctrl->string,
solo_enc->osd_text,
OSD_TEXT_MAX);
if (err)
err = -EFAULT;
+ mutex_unlock(&solo_enc->enable_lock);
}
break;
default:
@@ -1717,7 +1640,7 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
.vidioc_s_ext_ctrls = solo_s_ext_ctrls,
};
-static struct video_device solo_enc_template = {
+static const struct video_device solo_enc_template = {
.name = SOLO6X10_NAME,
.fops = &solo_enc_fops,
.ioctl_ops = &solo_enc_ioctl_ops,
@@ -1728,7 +1651,8 @@ static struct video_device solo_enc_template = {
.current_norm = V4L2_STD_NTSC_M,
};
-static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch)
+static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
+ u8 ch, unsigned nr)
{
struct solo_enc_dev *solo_enc;
int ret;
@@ -1748,8 +1672,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch)
*solo_enc->vfd = solo_enc_template;
solo_enc->vfd->parent = &solo_dev->pdev->dev;
- ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER,
- video_nr);
+ ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
video_device_release(solo_enc->vfd);
kfree(solo_enc);
@@ -1762,12 +1685,12 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch)
"%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num,
solo_enc->vfd->num);
- if (video_nr != -1)
- video_nr++;
+ INIT_LIST_HEAD(&solo_enc->listeners);
+ mutex_init(&solo_enc->enable_lock);
+ spin_lock_init(&solo_enc->motion_lock);
- spin_lock_init(&solo_enc->lock);
- init_waitqueue_head(&solo_enc->thread_wait);
atomic_set(&solo_enc->readers, 0);
+ atomic_set(&solo_enc->mpeg_readers, 0);
solo_enc->qp = SOLO_DEFAULT_QP;
solo_enc->gop = solo_dev->fps;
@@ -1775,9 +1698,13 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch)
solo_enc->mode = SOLO_ENC_MODE_CIF;
solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;
- spin_lock(&solo_enc->lock);
+ mutex_lock(&solo_enc->enable_lock);
solo_update_mode(solo_enc);
- spin_unlock(&solo_enc->lock);
+ mutex_unlock(&solo_enc->enable_lock);
+
+ /* Initialize this per encoder */
+ solo_enc->jpeg_len = sizeof(jpeg_header);
+ memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len);
return solo_enc;
}
@@ -1791,12 +1718,22 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc)
kfree(solo_enc);
}
-int solo_enc_v4l2_init(struct solo_dev *solo_dev)
+int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
{
int i;
+ atomic_set(&solo_dev->enc_users, 0);
+ init_waitqueue_head(&solo_dev->ring_thread_wait);
+
+ solo_dev->vh_size = sizeof(struct vop_header);
+ solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev,
+ solo_dev->vh_size,
+ &solo_dev->vh_dma);
+ if (solo_dev->vh_buf == NULL)
+ return -ENOMEM;
+
for (i = 0; i < solo_dev->nr_chans; i++) {
- solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i);
+ solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i, nr);
if (IS_ERR(solo_dev->v4l2_enc[i]))
break;
}
@@ -1805,11 +1742,15 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev)
int ret = PTR_ERR(solo_dev->v4l2_enc[i]);
while (i--)
solo_enc_free(solo_dev->v4l2_enc[i]);
+ pci_free_consistent(solo_dev->pdev, solo_dev->vh_size,
+ solo_dev->vh_buf, solo_dev->vh_dma);
return ret;
}
- /* D1@MAX-FPS * 4 */
- solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4;
+ if (solo_dev->type == SOLO_DEV_6010)
+ solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4;
+ else
+ solo_dev->enc_bw_remain = solo_dev->fps * 4 * 5;
dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n",
solo_dev->v4l2_enc[0]->vfd->num,
@@ -1822,8 +1763,9 @@ void solo_enc_v4l2_exit(struct solo_dev *solo_dev)
{
int i;
- solo_irq_off(solo_dev, SOLO_IRQ_MOTION);
-
for (i = 0; i < solo_dev->nr_chans; i++)
solo_enc_free(solo_dev->v4l2_enc[i]);
+
+ pci_free_consistent(solo_dev->pdev, solo_dev->vh_size,
+ solo_dev->vh_buf, solo_dev->vh_dma);
}
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index ca774cc..f8ec2a3 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.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
@@ -21,47 +26,43 @@
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dma-contig.h>
+
#include "solo6x10.h"
#include "tw28.h"
-#define SOLO_HW_BPL 2048
#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
-/* Image size is two fields, SOLO_HW_BPL is one horizontal line */
+/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */
+#define SOLO_HW_BPL 2048
#define solo_vlines(__solo) (__solo->video_vsize * 2)
#define solo_image_size(__solo) (solo_bytesperline(__solo) * \
solo_vlines(__solo))
#define solo_bytesperline(__solo) (__solo->video_hsize * 2)
-#define MIN_VID_BUFFERS 4
+#define MIN_VID_BUFFERS 2
/* Simple file handle */
struct solo_filehandle {
- struct solo_dev *solo_dev;
+ struct solo_dev *solo_dev;
struct videobuf_queue vidq;
struct task_struct *kthread;
spinlock_t slock;
int old_write;
struct list_head vidq_active;
- struct p2m_desc desc[SOLO_NR_P2M_DESC];
- int desc_idx;
};
-unsigned video_nr = -1;
-module_param(video_nr, uint, 0644);
-MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)");
-
-static void erase_on(struct solo_dev *solo_dev)
+static inline void erase_on(struct solo_dev *solo_dev)
{
solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
solo_dev->erasing = 1;
solo_dev->frame_blank = 0;
}
-static int erase_off(struct solo_dev *solo_dev)
+static inline int erase_off(struct solo_dev *solo_dev)
{
if (!solo_dev->erasing)
return 0;
@@ -78,8 +79,7 @@ static int erase_off(struct solo_dev *solo_dev)
void solo_video_in_isr(struct solo_dev *solo_dev)
{
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN);
- wake_up_interruptible(&solo_dev->disp_thread_wait);
+ wake_up_interruptible_all(&solo_dev->disp_thread_wait);
}
static void solo_win_setup(struct solo_dev *solo_dev, u8 ch,
@@ -202,165 +202,61 @@ static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
return 0;
}
-static void disp_reset_desc(struct solo_filehandle *fh)
-{
- /* We use desc mode, which ignores desc 0 */
- memset(fh->desc, 0, sizeof(*fh->desc));
- fh->desc_idx = 1;
-}
-
-static int disp_flush_descs(struct solo_filehandle *fh)
-{
- int ret;
-
- if (!fh->desc_idx)
- return 0;
-
- ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
- fh->desc, fh->desc_idx);
- disp_reset_desc(fh);
-
- return ret;
-}
-
-static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
- u32 ext_addr, int size, int repeat, int ext_size)
-{
- if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
- int ret = disp_flush_descs(fh);
- if (ret)
- return ret;
- }
-
- solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
- size, repeat, ext_size);
- fh->desc_idx++;
-
- return 0;
-}
-
static void solo_fillbuf(struct solo_filehandle *fh,
struct videobuf_buffer *vb)
{
struct solo_dev *solo_dev = fh->solo_dev;
- struct videobuf_dmabuf *vbuf;
+ dma_addr_t vbuf;
unsigned int fdma_addr;
- int error = 1;
+ int error = -1;
int i;
- struct scatterlist *sg;
- dma_addr_t sg_dma;
- int sg_size_left;
- vbuf = videobuf_to_dma(vb);
+ vbuf = videobuf_to_dma_contig(vb);
if (!vbuf)
goto finish_buf;
if (erase_off(solo_dev)) {
- int i;
-
- /* Just blit to the entire sg list, ignoring size */
- for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
- void *p = sg_virt(sg);
- size_t len = sg_dma_len(sg);
-
- for (i = 0; i < len; i += 2) {
- ((u8 *)p)[i] = 0x80;
- ((u8 *)p)[i + 1] = 0x00;
- }
+ void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
+ int image_size = solo_image_size(solo_dev);
+ for (i = 0; i < image_size; i += 2) {
+ ((u8 *)p)[i] = 0x80;
+ ((u8 *)p)[i + 1] = 0x00;
}
-
error = 0;
- goto finish_buf;
- }
-
- disp_reset_desc(fh);
- sg = vbuf->sglist;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
-
- fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
- (SOLO_HW_BPL * solo_vlines(solo_dev)));
-
- for (i = 0; i < solo_vlines(solo_dev); i++) {
- int line_len = solo_bytesperline(solo_dev);
- int lines;
-
- if (!sg_size_left) {
- sg = sg_next(sg);
- if (sg == NULL)
- goto finish_buf;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
- }
-
- /* No room for an entire line, so chunk it up */
- if (sg_size_left < line_len) {
- int this_addr = fdma_addr;
-
- while (line_len > 0) {
- int this_write;
-
- if (!sg_size_left) {
- sg = sg_next(sg);
- if (sg == NULL)
- goto finish_buf;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
- }
-
- this_write = min(sg_size_left, line_len);
-
- if (disp_push_desc(fh, sg_dma, this_addr,
- this_write, 0, 0))
- goto finish_buf;
-
- line_len -= this_write;
- sg_size_left -= this_write;
- sg_dma += this_write;
- this_addr += this_write;
- }
-
- fdma_addr += SOLO_HW_BPL;
- continue;
- }
-
- /* Shove as many lines into a repeating descriptor as possible */
- lines = min(sg_size_left / line_len,
- solo_vlines(solo_dev) - i);
-
- if (disp_push_desc(fh, sg_dma, fdma_addr, line_len,
- lines - 1, SOLO_HW_BPL))
- goto finish_buf;
+ } else {
+ fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
+ (SOLO_HW_BPL * solo_vlines(solo_dev)));
- i += lines - 1;
- fdma_addr += SOLO_HW_BPL * lines;
- sg_dma += lines * line_len;
- sg_size_left -= lines * line_len;
+ error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr,
+ solo_bytesperline(solo_dev),
+ solo_vlines(solo_dev), SOLO_HW_BPL);
}
- error = disp_flush_descs(fh);
-
finish_buf:
if (error) {
vb->state = VIDEOBUF_ERROR;
} else {
- vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
vb->state = VIDEOBUF_DONE;
vb->field_count++;
- do_gettimeofday(&vb->ts);
}
wake_up(&vb->done);
-
- return;
}
static void solo_thread_try(struct solo_filehandle *fh)
{
struct videobuf_buffer *vb;
- unsigned int cur_write;
+ /* Only "break" from this loop if slock is held, otherwise
+ * just return. */
for (;;) {
+ unsigned int cur_write;
+
+ cur_write = SOLO_VI_STATUS0_PAGE(
+ solo_reg_read(fh->solo_dev, SOLO_VI_STATUS0));
+ if (cur_write == fh->old_write)
+ return;
+
spin_lock(&fh->slock);
if (list_empty(&fh->vidq_active))
@@ -372,13 +268,9 @@ static void solo_thread_try(struct solo_filehandle *fh)
if (!waitqueue_active(&vb->done))
break;
- cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
- SOLO_VI_STATUS0));
- if (cur_write == fh->old_write)
- break;
-
fh->old_write = cur_write;
list_del(&vb->queue);
+ vb->state = VIDEOBUF_ACTIVE;
spin_unlock(&fh->slock);
@@ -413,17 +305,31 @@ static int solo_thread(void *data)
static int solo_start_thread(struct solo_filehandle *fh)
{
+ int ret = 0;
+
+ if (atomic_inc_return(&fh->solo_dev->disp_users) == 1)
+ solo_irq_on(fh->solo_dev, SOLO_IRQ_VIDEO_IN);
+
fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp");
- return PTR_RET(fh->kthread);
+ if (IS_ERR(fh->kthread)) {
+ ret = PTR_ERR(fh->kthread);
+ fh->kthread = NULL;
+ }
+
+ return ret;
}
static void solo_stop_thread(struct solo_filehandle *fh)
{
- if (fh->kthread) {
- kthread_stop(fh->kthread);
- fh->kthread = NULL;
- }
+ if (!fh->kthread)
+ return;
+
+ kthread_stop(fh->kthread);
+ fh->kthread = NULL;
+
+ if (atomic_dec_return(&fh->solo_dev->disp_users) == 0)
+ solo_irq_off(fh->solo_dev, SOLO_IRQ_VIDEO_IN);
}
static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
@@ -459,9 +365,7 @@ static int solo_buf_prepare(struct videobuf_queue *vq,
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
+ videobuf_dma_contig_free(vq, vb);
vb->state = VIDEOBUF_NEEDS_INIT;
return rc;
}
@@ -485,14 +389,11 @@ static void solo_buf_queue(struct videobuf_queue *vq,
static void solo_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
+ videobuf_dma_contig_free(vq, vb);
vb->state = VIDEOBUF_NEEDS_INIT;
}
-static struct videobuf_queue_ops solo_video_qops = {
+static const struct videobuf_queue_ops solo_video_qops = {
.buf_setup = solo_buf_setup,
.buf_prepare = solo_buf_prepare,
.buf_queue = solo_buf_queue,
@@ -535,12 +436,12 @@ static int solo_v4l2_open(struct file *file)
return ret;
}
- videobuf_queue_sg_init(&fh->vidq, &solo_video_qops,
- &solo_dev->pdev->dev, &fh->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- SOLO_DISP_PIX_FIELD,
- sizeof(struct videobuf_buffer), fh, NULL);
-
+ videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
+ &solo_dev->pdev->dev, &fh->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ SOLO_DISP_PIX_FIELD,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
return 0;
}
@@ -557,9 +458,11 @@ static int solo_v4l2_release(struct file *file)
{
struct solo_filehandle *fh = file->private_data;
+ solo_stop_thread(fh);
+
videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq);
- solo_stop_thread(fh);
+
kfree(fh);
return 0;
@@ -585,12 +488,12 @@ static int solo_querycap(struct file *file, void *priv,
static int solo_enum_ext_input(struct solo_dev *solo_dev,
struct v4l2_input *input)
{
- static const char *dispnames_1[] = { "4UP" };
- static const char *dispnames_2[] = { "4UP-1", "4UP-2" };
- static const char *dispnames_5[] = {
+ static const char * const dispnames_1[] = { "4UP" };
+ static const char * const dispnames_2[] = { "4UP-1", "4UP-2" };
+ static const char * const dispnames_5[] = {
"4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP"
};
- const char **dispnames;
+ const char * const *dispnames;
if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext))
return -EINVAL;
@@ -640,8 +543,14 @@ static int solo_enum_input(struct file *file, void *priv,
static int solo_set_input(struct file *file, void *priv, unsigned int index)
{
struct solo_filehandle *fh = priv;
+ int ret = solo_v4l2_set_ch(fh->solo_dev, index);
+
+ if (!ret) {
+ while (erase_off(fh->solo_dev))
+ /* Do nothing */;
+ }
- return solo_v4l2_set_ch(fh->solo_dev, index);
+ return ret;
}
static int solo_get_input(struct file *file, void *priv, unsigned int *index)
@@ -732,7 +641,8 @@ static int solo_reqbufs(struct file *file, void *priv,
return videobuf_reqbufs(&fh->vidq, req);
}
-static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int solo_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
{
struct solo_filehandle *fh = priv;
@@ -901,11 +811,12 @@ static struct video_device solo_v4l2_template = {
.current_norm = V4L2_STD_NTSC_M,
};
-int solo_v4l2_init(struct solo_dev *solo_dev)
+int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
{
int ret;
int i;
+ atomic_set(&solo_dev->disp_users, 0);
init_waitqueue_head(&solo_dev->disp_thread_wait);
solo_dev->vfd = video_device_alloc();
@@ -915,7 +826,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev)
*solo_dev->vfd = solo_v4l2_template;
solo_dev->vfd->parent = &solo_dev->pdev->dev;
- ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr);
+ ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
video_device_release(solo_dev->vfd);
solo_dev->vfd = NULL;
@@ -927,35 +838,30 @@ int solo_v4l2_init(struct solo_dev *solo_dev)
snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
SOLO6X10_NAME, solo_dev->vfd->num);
- if (video_nr != -1)
- video_nr++;
-
- dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with "
- "%d inputs (%d extended)\n", solo_dev->vfd->num,
- solo_dev->nr_chans, solo_dev->nr_ext);
+ dev_info(&solo_dev->pdev->dev,
+ "Display as /dev/video%d with %d inputs (%d extended)\n",
+ solo_dev->vfd->num, solo_dev->nr_chans, solo_dev->nr_ext);
/* Cycle all the channels and clear */
for (i = 0; i < solo_dev->nr_chans; i++) {
solo_v4l2_set_ch(solo_dev, i);
while (erase_off(solo_dev))
- ;/* Do nothing */
+ /* Do nothing */;
}
/* Set the default display channel */
solo_v4l2_set_ch(solo_dev, 0);
while (erase_off(solo_dev))
- ;/* Do nothing */
-
- solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
+ /* Do nothing */;
return 0;
}
void solo_v4l2_exit(struct solo_dev *solo_dev)
{
- solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
- if (solo_dev->vfd) {
- video_unregister_device(solo_dev->vfd);
- solo_dev->vfd = NULL;
- }
+ if (solo_dev->vfd == NULL)
+ return;
+
+ video_unregister_device(solo_dev->vfd);
+ solo_dev->vfd = NULL;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 02/19] solo6x10: fix querycap and update driver version.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 03/19] solo6x10: add v4l2_device Hans Verkuil
` (16 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Setup correct bus_info, let the v4l2 core set the version and add device_caps
support.
Also update the module version to 3.0.0 since this is a major upgrade.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/solo6x10.h | 11 +----------
drivers/staging/media/solo6x10/v4l2-enc.c | 9 ++++-----
drivers/staging/media/solo6x10/v4l2.c | 9 ++++-----
3 files changed, 9 insertions(+), 20 deletions(-)
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 44ae160..a8924a9 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -71,16 +71,7 @@
#define SOLO_MAX_CHANNELS 16
-/* Make sure these two match */
-#define SOLO6X10_VER_MAJOR 2
-#define SOLO6X10_VER_MINOR 4
-#define SOLO6X10_VER_SUB 4
-#define SOLO6X10_VER_NUM \
- KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB)
-#define SOLO6X10_VERSION \
- __stringify(SOLO6X10_VER_MAJOR) "." \
- __stringify(SOLO6X10_VER_MINOR) "." \
- __stringify(SOLO6X10_VER_SUB)
+#define SOLO6X10_VERSION "3.0.0"
/*
* The SOLO6x10 actually has 8 i2c channels, but we only use 2.
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index b05b328..8f02af2 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -991,12 +991,11 @@ static int solo_enc_querycap(struct file *file, void *priv,
strcpy(cap->driver, SOLO6X10_NAME);
snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d",
solo_enc->ch);
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
pci_name(solo_dev->pdev));
- cap->version = SOLO6X10_VER_NUM;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index f8ec2a3..d2f6eb6 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -476,12 +476,11 @@ static int solo_querycap(struct file *file, void *priv,
strcpy(cap->driver, SOLO6X10_NAME);
strcpy(cap->card, "Softlogic 6x10");
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
pci_name(solo_dev->pdev));
- cap->version = SOLO6X10_VER_NUM;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 03/19] solo6x10: add v4l2_device.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 02/19] solo6x10: fix querycap and update driver version Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 04/19] solo6x10: add control framework Hans Verkuil
` (15 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Add support for struct v4l2_device.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/core.c | 8 ++++++--
drivers/staging/media/solo6x10/solo6x10.h | 2 ++
drivers/staging/media/solo6x10/v4l2-enc.c | 2 +-
drivers/staging/media/solo6x10/v4l2.c | 2 +-
4 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index 27ae75a..271759f 100644
--- a/drivers/staging/media/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
@@ -180,6 +180,7 @@ static void free_solo_dev(struct solo_dev *solo_dev)
pci_release_regions(pdev);
pci_disable_device(pdev);
+ v4l2_device_unregister(&solo_dev->v4l2_dev);
pci_set_drvdata(pdev, NULL);
kfree(solo_dev);
@@ -510,7 +511,9 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
solo_dev->type = id->driver_data;
solo_dev->pdev = pdev;
spin_lock_init(&solo_dev->reg_io_lock);
- pci_set_drvdata(pdev, solo_dev);
+ ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev);
+ if (ret)
+ goto fail_probe;
/* Only for during init */
solo_dev->p2m_jiffies = msecs_to_jiffies(100);
@@ -677,7 +680,8 @@ fail_probe:
static void solo_pci_remove(struct pci_dev *pdev)
{
- struct solo_dev *solo_dev = pci_get_drvdata(pdev);
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev);
free_solo_dev(solo_dev);
}
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index a8924a9..77041f5 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -37,6 +37,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
#include "registers.h"
@@ -178,6 +179,7 @@ struct solo_dev {
u32 irq_mask;
u32 motion_mask;
spinlock_t reg_io_lock;
+ struct v4l2_device v4l2_dev;
/* tw28xx accounting */
u8 tw2865, tw2864, tw2815;
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 8f02af2..c4b2a34 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -1670,7 +1670,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
solo_enc->ch = ch;
*solo_enc->vfd = solo_enc_template;
- solo_enc->vfd->parent = &solo_dev->pdev->dev;
+ solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev;
ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
video_device_release(solo_enc->vfd);
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index d2f6eb6..ae1c119 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -823,7 +823,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
return -ENOMEM;
*solo_dev->vfd = solo_v4l2_template;
- solo_dev->vfd->parent = &solo_dev->pdev->dev;
+ solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev;
ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 04/19] solo6x10: add control framework.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (2 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 03/19] solo6x10: add v4l2_device Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 05/19] solo6x10: fix various format-related compliancy issues Hans Verkuil
` (14 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Note that the MOTION_THRESHOLD functionality has been temporarily reduced:
only the global threshold can be set, not the per-block. This will be
addressed in a later patch: controls are not the proper way to do this.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/solo6x10.h | 15 +-
drivers/staging/media/solo6x10/tw28.c | 12 +-
drivers/staging/media/solo6x10/tw28.h | 1 +
drivers/staging/media/solo6x10/v4l2-enc.c | 350 ++++++++---------------------
drivers/staging/media/solo6x10/v4l2.c | 83 +++----
5 files changed, 132 insertions(+), 329 deletions(-)
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 77041f5..82be88c 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -38,6 +38,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/videobuf-core.h>
#include "registers.h"
@@ -100,12 +101,12 @@
#define V4L2_BUF_FLAG_MOTION_ON 0x0400
#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800
#endif
-#ifndef V4L2_CID_MOTION_ENABLE
-#define PRIVATE_CIDS
-#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0)
-#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1)
-#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2)
-#endif
+
+#define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
+#define V4L2_CID_MOTION_ENABLE (SOLO_CID_CUSTOM_BASE+0)
+#define V4L2_CID_MOTION_THRESHOLD (SOLO_CID_CUSTOM_BASE+1)
+#define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2)
+#define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3)
enum SOLO_I2C_STATE {
IIC_STATE_IDLE,
@@ -137,6 +138,7 @@ struct solo_p2m_dev {
struct solo_enc_dev {
struct solo_dev *solo_dev;
/* V4L2 Items */
+ struct v4l2_ctrl_handler hdl;
struct video_device *vfd;
/* General accounting */
struct mutex enable_lock;
@@ -207,6 +209,7 @@ struct solo_dev {
unsigned int frame_blank;
u8 cur_disp_ch;
wait_queue_head_t disp_thread_wait;
+ struct v4l2_ctrl_handler disp_hdl;
/* V4L2 Encoder items */
struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS];
diff --git a/drivers/staging/media/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c
index 365ab10..69baf82 100644
--- a/drivers/staging/media/solo6x10/tw28.c
+++ b/drivers/staging/media/solo6x10/tw28.c
@@ -660,6 +660,11 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev)
}
#endif
+bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
+{
+ return is_tw286x(solo_dev, ch / 4);
+}
+
int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
s32 val)
{
@@ -676,8 +681,6 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
switch (ctrl) {
case V4L2_CID_SHARPNESS:
/* Only 286x has sharpness */
- if (val > 0x0f || val < 0)
- return -ERANGE;
if (is_tw286x(solo_dev, chip_num)) {
u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
@@ -687,8 +690,9 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
TW286x_SHARPNESS(chip_num), v);
- } else if (val != 0)
- return -ERANGE;
+ } else {
+ return -EINVAL;
+ }
break;
case V4L2_CID_HUE:
diff --git a/drivers/staging/media/solo6x10/tw28.h b/drivers/staging/media/solo6x10/tw28.h
index a03b429..1a02c87 100644
--- a/drivers/staging/media/solo6x10/tw28.h
+++ b/drivers/staging/media/solo6x10/tw28.h
@@ -55,6 +55,7 @@ int solo_tw28_init(struct solo_dev *solo_dev);
int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val);
int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val);
+bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch);
u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch);
void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val);
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index c4b2a34..1fbd95f 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -119,41 +119,6 @@ static unsigned char vop_6110_pal_cif[] = {
0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00,
};
-
-static const u32 solo_user_ctrls[] = {
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_SHARPNESS,
- 0
-};
-
-static const u32 solo_mpeg_ctrls[] = {
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_CID_MPEG_VIDEO_GOP_SIZE,
- 0
-};
-
-static const u32 solo_private_ctrls[] = {
- V4L2_CID_MOTION_ENABLE,
- V4L2_CID_MOTION_THRESHOLD,
- 0
-};
-
-static const u32 solo_fmtx_ctrls[] = {
- V4L2_CID_RDS_TX_RADIO_TEXT,
- 0
-};
-
-static const u32 *solo_ctrl_classes[] = {
- solo_user_ctrls,
- solo_mpeg_ctrls,
- solo_fmtx_ctrls,
- solo_private_ctrls,
- NULL
-};
-
struct vop_header {
/* VE_STATUS0 */
u32 mpeg_size:20, sad_motion_flag:1, video_motion_flag:1, vop_type:2,
@@ -1341,128 +1306,13 @@ static int solo_s_parm(struct file *file, void *priv,
return 0;
}
-static int solo_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc =
+ container_of(ctrl->handler, struct solo_enc_dev, hdl);
struct solo_dev *solo_dev = solo_enc->solo_dev;
-
- qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
- if (!qc->id)
- return -EINVAL;
-
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80);
- case V4L2_CID_SHARPNESS:
- return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return v4l2_ctrl_query_fill(
- qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps);
-#ifdef PRIVATE_CIDS
- case V4L2_CID_MOTION_THRESHOLD:
- qc->flags |= V4L2_CTRL_FLAG_SLIDER;
- qc->type = V4L2_CTRL_TYPE_INTEGER;
- qc->minimum = 0;
- qc->maximum = 0xffff;
- qc->step = 1;
- qc->default_value = SOLO_DEF_MOT_THRESH;
- strlcpy(qc->name, "Motion Detection Threshold",
- sizeof(qc->name));
- return 0;
- case V4L2_CID_MOTION_ENABLE:
- qc->type = V4L2_CTRL_TYPE_BOOLEAN;
- qc->minimum = 0;
- qc->maximum = qc->step = 1;
- qc->default_value = 0;
- strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name));
- return 0;
-#else
- case V4L2_CID_MOTION_THRESHOLD:
- return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1,
- SOLO_DEF_MOT_THRESH);
- case V4L2_CID_MOTION_ENABLE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-#endif
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- qc->type = V4L2_CTRL_TYPE_STRING;
- qc->minimum = 0;
- qc->maximum = OSD_TEXT_MAX;
- qc->step = 1;
- qc->default_value = 0;
- strlcpy(qc->name, "OSD Text", sizeof(qc->name));
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int solo_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *qmenu)
-{
- struct v4l2_queryctrl qctrl;
int err;
- qctrl.id = qmenu->id;
-
- err = solo_queryctrl(file, priv, &qctrl);
- if (err)
- return err;
-
- return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
-}
-
-static int solo_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo_dev *solo_dev = solo_enc->solo_dev;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- case V4L2_CID_SHARPNESS:
- return tw28_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch,
- &ctrl->value);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- if (atomic_read(&solo_enc->readers) > 0)
- return -EBUSY;
- ctrl->value = solo_enc->gop;
- break;
- case V4L2_CID_MOTION_THRESHOLD:
- ctrl->value = solo_enc->motion_thresh;
- break;
- case V4L2_CID_MOTION_ENABLE:
- ctrl->value = solo_is_motion_on(solo_enc);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int solo_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo_dev *solo_dev = solo_enc->solo_dev;
-
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_CONTRAST:
@@ -1470,20 +1320,15 @@ static int solo_s_ctrl(struct file *file, void *priv,
case V4L2_CID_HUE:
case V4L2_CID_SHARPNESS:
return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch,
- ctrl->value);
+ ctrl->val);
case V4L2_CID_MPEG_VIDEO_ENCODING:
- if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
- return -ERANGE;
- break;
+ return 0;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- if (ctrl->value < 1 || ctrl->value > 255)
- return -ERANGE;
- solo_enc->gop = ctrl->value;
- break;
- case V4L2_CID_MOTION_THRESHOLD:
- {
- u16 block = (ctrl->value >> 16) & 0xffff;
- u16 value = ctrl->value & 0xffff;
+ solo_enc->gop = ctrl->val;
+ return 0;
+ case V4L2_CID_MOTION_THRESHOLD: {
+ u16 block = (ctrl->val >> 16) & 0xffff;
+ u16 value = ctrl->val & 0xffff;
/* Motion thresholds are in a table of 64x64 samples, with
* each sample representing 16x16 pixels of the source. In
@@ -1492,22 +1337,25 @@ static int solo_s_ctrl(struct file *file, void *priv,
*
* Block is 0 to set the threshold globally, or any positive
* number under 2049 to set block-1 individually. */
- if (block > 2049)
- return -ERANGE;
-
+ /* Currently we limit support to block 0 only. A later patch
+ * will add a new ioctl to set all other blocks. */
if (block == 0) {
solo_enc->motion_thresh = value;
return solo_set_motion_threshold(solo_dev,
solo_enc->ch, value);
- } else {
- return solo_set_motion_block(solo_dev, solo_enc->ch,
- value, block - 1);
}
- break;
+ return solo_set_motion_block(solo_dev, solo_enc->ch,
+ value, block - 1);
}
case V4L2_CID_MOTION_ENABLE:
- solo_motion_toggle(solo_enc, ctrl->value);
- break;
+ solo_motion_toggle(solo_enc, ctrl->val);
+ return 0;
+ case V4L2_CID_OSD_TEXT:
+ mutex_lock(&solo_enc->enable_lock);
+ strcpy(solo_enc->osd_text, ctrl->string);
+ err = solo_osd_print(solo_enc);
+ mutex_unlock(&solo_enc->enable_lock);
+ return err;
default:
return -EINVAL;
}
@@ -1515,86 +1363,6 @@ static int solo_s_ctrl(struct file *file, void *priv,
return 0;
}
-static int solo_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- int i;
-
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = (ctrls->controls + i);
- int err;
-
- switch (ctrl->id) {
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- if (ctrl->size - 1 > OSD_TEXT_MAX)
- err = -ERANGE;
- else {
- mutex_lock(&solo_enc->enable_lock);
- err = copy_from_user(solo_enc->osd_text,
- ctrl->string,
- OSD_TEXT_MAX);
- solo_enc->osd_text[OSD_TEXT_MAX] = '\0';
- if (!err)
- err = solo_osd_print(solo_enc);
- else
- err = -EFAULT;
- mutex_unlock(&solo_enc->enable_lock);
- }
- break;
- default:
- err = -EINVAL;
- }
-
- if (err < 0) {
- ctrls->error_idx = i;
- return err;
- }
- }
-
- return 0;
-}
-
-static int solo_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- int i;
-
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = (ctrls->controls + i);
- int err;
-
- switch (ctrl->id) {
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- if (ctrl->size < OSD_TEXT_MAX) {
- ctrl->size = OSD_TEXT_MAX;
- err = -ENOSPC;
- } else {
- mutex_lock(&solo_enc->enable_lock);
- err = copy_to_user(ctrl->string,
- solo_enc->osd_text,
- OSD_TEXT_MAX);
- if (err)
- err = -EFAULT;
- mutex_unlock(&solo_enc->enable_lock);
- }
- break;
- default:
- err = -EINVAL;
- }
-
- if (err < 0) {
- ctrls->error_idx = i;
- return err;
- }
- }
-
- return 0;
-}
-
static const struct v4l2_file_operations solo_enc_fops = {
.owner = THIS_MODULE,
.open = solo_enc_open,
@@ -1630,13 +1398,6 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
/* Video capture parameters */
.vidioc_s_parm = solo_s_parm,
.vidioc_g_parm = solo_g_parm,
- /* Controls */
- .vidioc_queryctrl = solo_queryctrl,
- .vidioc_querymenu = solo_querymenu,
- .vidioc_g_ctrl = solo_g_ctrl,
- .vidioc_s_ctrl = solo_s_ctrl,
- .vidioc_g_ext_ctrls = solo_g_ext_ctrls,
- .vidioc_s_ext_ctrls = solo_s_ext_ctrls,
};
static const struct video_device solo_enc_template = {
@@ -1650,18 +1411,82 @@ static const struct video_device solo_enc_template = {
.current_norm = V4L2_STD_NTSC_M,
};
+static const struct v4l2_ctrl_ops solo_ctrl_ops = {
+ .s_ctrl = solo_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config solo_motion_threshold_ctrl = {
+ .ops = &solo_ctrl_ops,
+ .id = V4L2_CID_MOTION_THRESHOLD,
+ .name = "Motion Detection Threshold",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .max = 0xffff,
+ .def = SOLO_DEF_MOT_THRESH,
+ .step = 1,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+};
+
+static const struct v4l2_ctrl_config solo_motion_enable_ctrl = {
+ .ops = &solo_ctrl_ops,
+ .id = V4L2_CID_MOTION_ENABLE,
+ .name = "Motion Detection Enable",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config solo_osd_text_ctrl = {
+ .ops = &solo_ctrl_ops,
+ .id = V4L2_CID_OSD_TEXT,
+ .name = "OSD Text",
+ .type = V4L2_CTRL_TYPE_STRING,
+ .max = OSD_TEXT_MAX,
+ .step = 1,
+};
+
static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
u8 ch, unsigned nr)
{
struct solo_enc_dev *solo_enc;
+ struct v4l2_ctrl_handler *hdl;
int ret;
solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL);
if (!solo_enc)
return ERR_PTR(-ENOMEM);
+ hdl = &solo_enc->hdl;
+ v4l2_ctrl_handler_init(hdl, 10);
+ v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
+ V4L2_CID_HUE, 0, 255, 1, 128);
+ if (tw28_has_sharpness(solo_dev, ch))
+ v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 15, 1, 0);
+ v4l2_ctrl_new_std_menu(hdl, &solo_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 3,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
+ v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps);
+ v4l2_ctrl_new_custom(hdl, &solo_motion_threshold_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &solo_motion_enable_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL);
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ kfree(solo_enc);
+ return ERR_PTR(ret);
+ }
+
solo_enc->vfd = video_device_alloc();
if (!solo_enc->vfd) {
+ v4l2_ctrl_handler_free(hdl);
kfree(solo_enc);
return ERR_PTR(-ENOMEM);
}
@@ -1671,9 +1496,11 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
*solo_enc->vfd = solo_enc_template;
solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev;
+ solo_enc->vfd->ctrl_handler = hdl;
ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
video_device_release(solo_enc->vfd);
+ v4l2_ctrl_handler_free(hdl);
kfree(solo_enc);
return ERR_PTR(ret);
}
@@ -1714,6 +1541,7 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc)
return;
video_unregister_device(solo_enc->vfd);
+ v4l2_ctrl_handler_free(&solo_enc->hdl);
kfree(solo_enc);
}
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index ae1c119..21b486c 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -687,64 +687,14 @@ static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
return 0;
}
-static const u32 solo_motion_ctrls[] = {
- V4L2_CID_MOTION_TRACE,
- 0
-};
-
-static const u32 *solo_ctrl_classes[] = {
- solo_motion_ctrls,
- NULL
-};
-
-static int solo_disp_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
- if (!qc->id)
- return -EINVAL;
-
- switch (qc->id) {
-#ifdef PRIVATE_CIDS
- case V4L2_CID_MOTION_TRACE:
- qc->type = V4L2_CTRL_TYPE_BOOLEAN;
- qc->minimum = 0;
- qc->maximum = qc->step = 1;
- qc->default_value = 0;
- strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name));
- return 0;
-#else
- case V4L2_CID_MOTION_TRACE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-#endif
- }
- return -EINVAL;
-}
-
-static int solo_disp_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct solo_filehandle *fh = priv;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev =
+ container_of(ctrl->handler, struct solo_dev, disp_hdl);
switch (ctrl->id) {
case V4L2_CID_MOTION_TRACE:
- ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR)
- ? 1 : 0;
- return 0;
- }
- return -EINVAL;
-}
-
-static int solo_disp_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_filehandle *fh = priv;
- struct solo_dev *solo_dev = fh->solo_dev;
-
- switch (ctrl->id) {
- case V4L2_CID_MOTION_TRACE:
- if (ctrl->value) {
+ if (ctrl->val) {
solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER,
SOLO_VI_MOTION_Y_ADD |
SOLO_VI_MOTION_Y_VALUE(0x20) |
@@ -760,6 +710,8 @@ static int solo_disp_s_ctrl(struct file *file, void *priv,
solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0);
}
return 0;
+ default:
+ break;
}
return -EINVAL;
}
@@ -793,10 +745,6 @@ static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
.vidioc_dqbuf = solo_dqbuf,
.vidioc_streamon = solo_streamon,
.vidioc_streamoff = solo_streamoff,
- /* Controls */
- .vidioc_queryctrl = solo_disp_queryctrl,
- .vidioc_g_ctrl = solo_disp_g_ctrl,
- .vidioc_s_ctrl = solo_disp_s_ctrl,
};
static struct video_device solo_v4l2_template = {
@@ -810,6 +758,19 @@ static struct video_device solo_v4l2_template = {
.current_norm = V4L2_STD_NTSC_M,
};
+static const struct v4l2_ctrl_ops solo_ctrl_ops = {
+ .s_ctrl = solo_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config solo_motion_trace_ctrl = {
+ .ops = &solo_ctrl_ops,
+ .id = V4L2_CID_MOTION_TRACE,
+ .name = "Motion Detection Trace",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .max = 1,
+ .step = 1,
+};
+
int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
{
int ret;
@@ -824,6 +785,11 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
*solo_dev->vfd = solo_v4l2_template;
solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev;
+ v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1);
+ v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL);
+ if (solo_dev->disp_hdl.error)
+ return solo_dev->disp_hdl.error;
+ solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl;
ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
@@ -862,5 +828,6 @@ void solo_v4l2_exit(struct solo_dev *solo_dev)
return;
video_unregister_device(solo_dev->vfd);
+ v4l2_ctrl_handler_free(&solo_dev->disp_hdl);
solo_dev->vfd = NULL;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 05/19] solo6x10: fix various format-related compliancy issues.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (3 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 04/19] solo6x10: add control framework Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 06/19] solo6x10: add support for prio and control event handling Hans Verkuil
` (13 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
- try_fmt should never return -EBUSY.
- invalid pix->field values were not mapped to a valid value.
- the priv field of struct v4l2_pix_format wasn't zeroed.
- the try_fmt error code was not checked in set_fmt.
- enum_framesizes/intervals is valid for both MJPEG and MPEG pixel formats.
- enum_frameintervals didn't check width and height and reported the
wrong range.
- s_parm didn't set readbuffers.
- don't fail on invalid colorspace, just replace with the valid colorspace.
- bytesperline should be 0 for compressed formats.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/v4l2-enc.c | 51 ++++++++++++++++++++---------
drivers/staging/media/solo6x10/v4l2.c | 30 +++++++----------
2 files changed, 46 insertions(+), 35 deletions(-)
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 1fbd95f..bf52a73 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -1039,13 +1039,6 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
pix->pixelformat != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
- /* We cannot change width/height in mid mpeg */
- if (atomic_read(&solo_enc->mpeg_readers) > 0) {
- if (pix->width != solo_enc->width ||
- pix->height != solo_enc->height)
- return -EBUSY;
- }
-
if (pix->width < solo_dev->video_hsize ||
pix->height < solo_dev->video_vsize << 1) {
/* Default to CIF 1/2 size */
@@ -1057,14 +1050,20 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
pix->height = solo_dev->video_vsize << 1;
}
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_INTERLACED;
- else if (pix->field != V4L2_FIELD_INTERLACED)
+ switch (pix->field) {
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_ANY:
+ default:
pix->field = V4L2_FIELD_INTERLACED;
+ break;
+ }
/* Just set these */
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
pix->sizeimage = FRAME_BUF_SIZE;
+ pix->priv = 0;
return 0;
}
@@ -1081,6 +1080,15 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
mutex_lock(&solo_enc->enable_lock);
ret = solo_enc_try_fmt_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ /* We cannot change width/height in mid read */
+ if (!ret && atomic_read(&solo_enc->readers) > 0) {
+ if (pix->width != solo_enc->width ||
+ pix->height != solo_enc->height)
+ ret = -EBUSY;
+ }
if (ret) {
mutex_unlock(&solo_enc->enable_lock);
return ret;
@@ -1116,6 +1124,7 @@ static int solo_enc_get_fmt_cap(struct file *file, void *priv,
V4L2_FIELD_NONE;
pix->sizeimage = FRAME_BUF_SIZE;
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->priv = 0;
return 0;
}
@@ -1205,7 +1214,8 @@ static int solo_enum_framesizes(struct file *file, void *priv,
struct solo_enc_fh *fh = priv;
struct solo_dev *solo_dev = fh->enc->solo_dev;
- if (fsize->pixel_format != V4L2_PIX_FMT_MPEG)
+ if (fsize->pixel_format != V4L2_PIX_FMT_MPEG &&
+ fsize->pixel_format != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
switch (fsize->index) {
@@ -1232,16 +1242,24 @@ static int solo_enum_frameintervals(struct file *file, void *priv,
struct solo_enc_fh *fh = priv;
struct solo_dev *solo_dev = fh->enc->solo_dev;
- if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index)
+ if (fintv->pixel_format != V4L2_PIX_FMT_MPEG &&
+ fintv->pixel_format != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+ if (fintv->index)
+ return -EINVAL;
+ if ((fintv->width != solo_dev->video_hsize >> 1 ||
+ fintv->height != solo_dev->video_vsize) &&
+ (fintv->width != solo_dev->video_hsize ||
+ fintv->height != solo_dev->video_vsize << 1))
return -EINVAL;
fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE;
- fintv->stepwise.min.numerator = solo_dev->fps;
- fintv->stepwise.min.denominator = 1;
+ fintv->stepwise.min.denominator = solo_dev->fps;
+ fintv->stepwise.min.numerator = 15;
- fintv->stepwise.max.numerator = solo_dev->fps;
- fintv->stepwise.max.denominator = 15;
+ fintv->stepwise.max.denominator = solo_dev->fps;
+ fintv->stepwise.max.numerator = 1;
fintv->stepwise.step.numerator = 1;
fintv->stepwise.step.denominator = 1;
@@ -1298,6 +1316,7 @@ static int solo_s_parm(struct file *file, void *priv,
solo_enc->interval = cp->timeperframe.numerator;
cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->readbuffers = 2;
solo_update_mode(solo_enc);
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 21b486c..a606c5c 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -34,8 +34,6 @@
#include "solo6x10.h"
#include "tw28.h"
-#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
-
/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */
#define SOLO_HW_BPL 2048
#define solo_vlines(__solo) (__solo->video_vsize * 2)
@@ -439,7 +437,7 @@ static int solo_v4l2_open(struct file *file)
videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
&solo_dev->pdev->dev, &fh->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
- SOLO_DISP_PIX_FIELD,
+ V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer),
fh, NULL);
return 0;
@@ -581,23 +579,16 @@ static int solo_try_fmt_cap(struct file *file, void *priv,
struct v4l2_pix_format *pix = &f->fmt.pix;
int image_size = solo_image_size(solo_dev);
- /* Check supported sizes */
- if (pix->width != solo_dev->video_hsize)
- pix->width = solo_dev->video_hsize;
- if (pix->height != solo_vlines(solo_dev))
- pix->height = solo_vlines(solo_dev);
- if (pix->sizeimage != image_size)
- pix->sizeimage = image_size;
-
- /* Check formats */
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = SOLO_DISP_PIX_FIELD;
-
- if (pix->pixelformat != V4L2_PIX_FMT_UYVY ||
- pix->field != SOLO_DISP_PIX_FIELD ||
- pix->colorspace != V4L2_COLORSPACE_SMPTE170M)
+ if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
return -EINVAL;
+ pix->width = solo_dev->video_hsize;
+ pix->height = solo_vlines(solo_dev);
+ pix->sizeimage = image_size;
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->priv = 0;
return 0;
}
@@ -624,10 +615,11 @@ static int solo_get_fmt_cap(struct file *file, void *priv,
pix->width = solo_dev->video_hsize;
pix->height = solo_vlines(solo_dev);
pix->pixelformat = V4L2_PIX_FMT_UYVY;
- pix->field = SOLO_DISP_PIX_FIELD;
+ pix->field = V4L2_FIELD_INTERLACED;
pix->sizeimage = solo_image_size(solo_dev);
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
pix->bytesperline = solo_bytesperline(solo_dev);
+ pix->priv = 0;
return 0;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 06/19] solo6x10: add support for prio and control event handling.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (4 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 05/19] solo6x10: fix various format-related compliancy issues Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 07/19] solo6x10: move global fields in solo_dev_fh to solo_dev Hans Verkuil
` (12 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/v4l2-enc.c | 19 +++++++++++++++++--
drivers/staging/media/solo6x10/v4l2.c | 17 ++++++++++++++++-
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index bf52a73..366f2b3 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -29,6 +29,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
#include <media/videobuf-dma-sg.h>
#include "solo6x10.h"
@@ -46,7 +47,8 @@ enum solo_enc_types {
};
struct solo_enc_fh {
- struct solo_enc_dev *enc;
+ struct v4l2_fh fh;
+ struct solo_enc_dev *enc;
u32 fmt;
u8 enc_on;
enum solo_enc_types type;
@@ -823,7 +825,11 @@ static unsigned int solo_enc_poll(struct file *file,
struct poll_table_struct *wait)
{
struct solo_enc_fh *fh = file->private_data;
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned res = v4l2_ctrl_poll(file, wait);
+ if (!(req_events & (POLLIN | POLLRDNORM)))
+ return res;
return videobuf_poll_stream(file, &fh->vidq, wait);
}
@@ -892,6 +898,7 @@ static int solo_enc_open(struct file *file)
return -ENOMEM;
}
+ v4l2_fh_init(&fh->fh, video_devdata(file));
fh->enc = solo_enc;
spin_lock_init(&fh->av_lock);
file->private_data = fh;
@@ -906,7 +913,7 @@ static int solo_enc_open(struct file *file)
V4L2_FIELD_INTERLACED,
sizeof(struct solo_videobuf),
fh, NULL);
-
+ v4l2_fh_add(&fh->fh);
return 0;
}
@@ -931,6 +938,8 @@ static int solo_enc_release(struct file *file)
struct solo_dev *solo_dev = fh->enc->solo_dev;
solo_enc_off(fh);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq);
@@ -1063,6 +1072,7 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
/* Just set these */
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
pix->sizeimage = FRAME_BUF_SIZE;
+ pix->bytesperline = 0;
pix->priv = 0;
return 0;
@@ -1417,6 +1427,10 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
/* Video capture parameters */
.vidioc_s_parm = solo_s_parm,
.vidioc_g_parm = solo_g_parm,
+ /* Logging and events */
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct video_device solo_enc_template = {
@@ -1516,6 +1530,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
*solo_enc->vfd = solo_enc_template;
solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev;
solo_enc->vfd->ctrl_handler = hdl;
+ set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags);
ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
video_device_release(solo_enc->vfd);
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index a606c5c..fe09180 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -29,6 +29,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
#include <media/videobuf-dma-contig.h>
#include "solo6x10.h"
@@ -45,6 +46,7 @@
/* Simple file handle */
struct solo_filehandle {
+ struct v4l2_fh fh;
struct solo_dev *solo_dev;
struct videobuf_queue vidq;
struct task_struct *kthread;
@@ -402,8 +404,12 @@ static unsigned int solo_v4l2_poll(struct file *file,
struct poll_table_struct *wait)
{
struct solo_filehandle *fh = file->private_data;
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned res = v4l2_ctrl_poll(file, wait);
- return videobuf_poll_stream(file, &fh->vidq, wait);
+ if (!(req_events & (POLLIN | POLLRDNORM)))
+ return res;
+ return res | videobuf_poll_stream(file, &fh->vidq, wait);
}
static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
@@ -423,6 +429,7 @@ static int solo_v4l2_open(struct file *file)
if (fh == NULL)
return -ENOMEM;
+ v4l2_fh_init(&fh->fh, video_devdata(file));
spin_lock_init(&fh->slock);
INIT_LIST_HEAD(&fh->vidq_active);
fh->solo_dev = solo_dev;
@@ -440,6 +447,7 @@ static int solo_v4l2_open(struct file *file)
V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer),
fh, NULL);
+ v4l2_fh_add(&fh->fh);
return 0;
}
@@ -461,6 +469,8 @@ static int solo_v4l2_release(struct file *file)
videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
kfree(fh);
return 0;
@@ -737,6 +747,10 @@ static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
.vidioc_dqbuf = solo_dqbuf,
.vidioc_streamon = solo_streamon,
.vidioc_streamoff = solo_streamoff,
+ /* Logging and events */
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device solo_v4l2_template = {
@@ -782,6 +796,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
if (solo_dev->disp_hdl.error)
return solo_dev->disp_hdl.error;
solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl;
+ set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags);
ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 07/19] solo6x10: move global fields in solo_dev_fh to solo_dev.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (5 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 06/19] solo6x10: add support for prio and control event handling Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 08/19] solo6x10: move global fields in solo_enc_fh to solo_enc_dev Hans Verkuil
` (11 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
All fields in solo_dev_fh do not belong there since they refer to global
properties. After moving all these fields to solo_dev the solo_dev_fh
struct can be removed completely.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/solo6x10.h | 7 +
drivers/staging/media/solo6x10/v4l2.c | 231 ++++++++++++-----------------
2 files changed, 101 insertions(+), 137 deletions(-)
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 82be88c..361f41f 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -250,6 +250,13 @@ struct solo_dev {
void *vh_buf;
dma_addr_t vh_dma;
int vh_size;
+
+ /* Buffer handling */
+ struct videobuf_queue vidq;
+ struct task_struct *kthread;
+ spinlock_t slock;
+ int old_write;
+ struct list_head vidq_active;
};
static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg)
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index fe09180..64595ef 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -44,17 +44,6 @@
#define MIN_VID_BUFFERS 2
-/* Simple file handle */
-struct solo_filehandle {
- struct v4l2_fh fh;
- struct solo_dev *solo_dev;
- struct videobuf_queue vidq;
- struct task_struct *kthread;
- spinlock_t slock;
- int old_write;
- struct list_head vidq_active;
-};
-
static inline void erase_on(struct solo_dev *solo_dev)
{
solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
@@ -202,10 +191,9 @@ static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
return 0;
}
-static void solo_fillbuf(struct solo_filehandle *fh,
+static void solo_fillbuf(struct solo_dev *solo_dev,
struct videobuf_buffer *vb)
{
- struct solo_dev *solo_dev = fh->solo_dev;
dma_addr_t vbuf;
unsigned int fdma_addr;
int error = -1;
@@ -216,7 +204,7 @@ static void solo_fillbuf(struct solo_filehandle *fh,
goto finish_buf;
if (erase_off(solo_dev)) {
- void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
+ void *p = videobuf_queue_to_vaddr(&solo_dev->vidq, vb);
int image_size = solo_image_size(solo_dev);
for (i = 0; i < image_size; i += 2) {
((u8 *)p)[i] = 0x80;
@@ -224,7 +212,7 @@ static void solo_fillbuf(struct solo_filehandle *fh,
}
error = 0;
} else {
- fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
+ fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write *
(SOLO_HW_BPL * solo_vlines(solo_dev)));
error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr,
@@ -243,7 +231,7 @@ finish_buf:
wake_up(&vb->done);
}
-static void solo_thread_try(struct solo_filehandle *fh)
+static void solo_thread_try(struct solo_dev *solo_dev)
{
struct videobuf_buffer *vb;
@@ -253,38 +241,37 @@ static void solo_thread_try(struct solo_filehandle *fh)
unsigned int cur_write;
cur_write = SOLO_VI_STATUS0_PAGE(
- solo_reg_read(fh->solo_dev, SOLO_VI_STATUS0));
- if (cur_write == fh->old_write)
+ solo_reg_read(solo_dev, SOLO_VI_STATUS0));
+ if (cur_write == solo_dev->old_write)
return;
- spin_lock(&fh->slock);
+ spin_lock(&solo_dev->slock);
- if (list_empty(&fh->vidq_active))
+ if (list_empty(&solo_dev->vidq_active))
break;
- vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer,
+ vb = list_first_entry(&solo_dev->vidq_active, struct videobuf_buffer,
queue);
if (!waitqueue_active(&vb->done))
break;
- fh->old_write = cur_write;
+ solo_dev->old_write = cur_write;
list_del(&vb->queue);
vb->state = VIDEOBUF_ACTIVE;
- spin_unlock(&fh->slock);
+ spin_unlock(&solo_dev->slock);
- solo_fillbuf(fh, vb);
+ solo_fillbuf(solo_dev, vb);
}
- assert_spin_locked(&fh->slock);
- spin_unlock(&fh->slock);
+ assert_spin_locked(&solo_dev->slock);
+ spin_unlock(&solo_dev->slock);
}
static int solo_thread(void *data)
{
- struct solo_filehandle *fh = data;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = data;
DECLARE_WAITQUEUE(wait, current);
set_freezable();
@@ -294,7 +281,7 @@ static int solo_thread(void *data)
long timeout = schedule_timeout_interruptible(HZ);
if (timeout == -ERESTARTSYS || kthread_should_stop())
break;
- solo_thread_try(fh);
+ solo_thread_try(solo_dev);
try_to_freeze();
}
@@ -303,40 +290,39 @@ static int solo_thread(void *data)
return 0;
}
-static int solo_start_thread(struct solo_filehandle *fh)
+static int solo_start_thread(struct solo_dev *solo_dev)
{
int ret = 0;
- if (atomic_inc_return(&fh->solo_dev->disp_users) == 1)
- solo_irq_on(fh->solo_dev, SOLO_IRQ_VIDEO_IN);
+ if (atomic_inc_return(&solo_dev->disp_users) == 1)
+ solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
- fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp");
+ solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp");
- if (IS_ERR(fh->kthread)) {
- ret = PTR_ERR(fh->kthread);
- fh->kthread = NULL;
+ if (IS_ERR(solo_dev->kthread)) {
+ ret = PTR_ERR(solo_dev->kthread);
+ solo_dev->kthread = NULL;
}
return ret;
}
-static void solo_stop_thread(struct solo_filehandle *fh)
+static void solo_stop_thread(struct solo_dev *solo_dev)
{
- if (!fh->kthread)
+ if (!solo_dev->kthread)
return;
- kthread_stop(fh->kthread);
- fh->kthread = NULL;
+ kthread_stop(solo_dev->kthread);
+ solo_dev->kthread = NULL;
- if (atomic_dec_return(&fh->solo_dev->disp_users) == 0)
- solo_irq_off(fh->solo_dev, SOLO_IRQ_VIDEO_IN);
+ if (atomic_dec_return(&solo_dev->disp_users) == 0)
+ solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
}
static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
- struct solo_filehandle *fh = vq->priv_data;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = vq->priv_data;
*size = solo_image_size(solo_dev);
@@ -349,8 +335,7 @@ static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
static int solo_buf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb, enum v4l2_field field)
{
- struct solo_filehandle *fh = vq->priv_data;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = vq->priv_data;
vb->size = solo_image_size(solo_dev);
if (vb->baddr != 0 && vb->bsize < vb->size)
@@ -378,11 +363,10 @@ static int solo_buf_prepare(struct videobuf_queue *vq,
static void solo_buf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
- struct solo_filehandle *fh = vq->priv_data;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = vq->priv_data;
vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &fh->vidq_active);
+ list_add_tail(&vb->queue, &solo_dev->vidq_active);
wake_up_interruptible(&solo_dev->disp_thread_wait);
}
@@ -403,84 +387,45 @@ static const struct videobuf_queue_ops solo_video_qops = {
static unsigned int solo_v4l2_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct solo_filehandle *fh = file->private_data;
+ struct solo_dev *solo_dev = video_drvdata(file);
unsigned long req_events = poll_requested_events(wait);
unsigned res = v4l2_ctrl_poll(file, wait);
if (!(req_events & (POLLIN | POLLRDNORM)))
return res;
- return res | videobuf_poll_stream(file, &fh->vidq, wait);
+ return res | videobuf_poll_stream(file, &solo_dev->vidq, wait);
}
static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct solo_filehandle *fh = file->private_data;
-
- return videobuf_mmap_mapper(&fh->vidq, vma);
-}
-
-static int solo_v4l2_open(struct file *file)
-{
struct solo_dev *solo_dev = video_drvdata(file);
- struct solo_filehandle *fh;
- int ret;
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL)
- return -ENOMEM;
-
- v4l2_fh_init(&fh->fh, video_devdata(file));
- spin_lock_init(&fh->slock);
- INIT_LIST_HEAD(&fh->vidq_active);
- fh->solo_dev = solo_dev;
- file->private_data = fh;
-
- ret = solo_start_thread(fh);
- if (ret) {
- kfree(fh);
- return ret;
- }
-
- videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
- &solo_dev->pdev->dev, &fh->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct videobuf_buffer),
- fh, NULL);
- v4l2_fh_add(&fh->fh);
- return 0;
+ return videobuf_mmap_mapper(&solo_dev->vidq, vma);
}
static ssize_t solo_v4l2_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
- struct solo_filehandle *fh = file->private_data;
+ struct solo_dev *solo_dev = video_drvdata(file);
- return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+ return videobuf_read_stream(&solo_dev->vidq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
}
static int solo_v4l2_release(struct file *file)
{
- struct solo_filehandle *fh = file->private_data;
-
- solo_stop_thread(fh);
-
- videobuf_stop(&fh->vidq);
- videobuf_mmap_free(&fh->vidq);
-
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- kfree(fh);
+ struct solo_dev *solo_dev = video_drvdata(file);
- return 0;
+ solo_stop_thread(solo_dev);
+ videobuf_stop(&solo_dev->vidq);
+ videobuf_mmap_free(&solo_dev->vidq);
+ return v4l2_fh_release(file);
}
static int solo_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct solo_filehandle *fh = priv;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = video_drvdata(file);
strcpy(cap->driver, SOLO6X10_NAME);
strcpy(cap->card, "Softlogic 6x10");
@@ -521,8 +466,7 @@ static int solo_enum_ext_input(struct solo_dev *solo_dev,
static int solo_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
- struct solo_filehandle *fh = priv;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = video_drvdata(file);
if (input->index >= solo_dev->nr_chans) {
int ret = solo_enum_ext_input(solo_dev, input);
@@ -549,11 +493,11 @@ static int solo_enum_input(struct file *file, void *priv,
static int solo_set_input(struct file *file, void *priv, unsigned int index)
{
- struct solo_filehandle *fh = priv;
- int ret = solo_v4l2_set_ch(fh->solo_dev, index);
+ struct solo_dev *solo_dev = video_drvdata(file);
+ int ret = solo_v4l2_set_ch(solo_dev, index);
if (!ret) {
- while (erase_off(fh->solo_dev))
+ while (erase_off(solo_dev))
/* Do nothing */;
}
@@ -562,9 +506,9 @@ static int solo_set_input(struct file *file, void *priv, unsigned int index)
static int solo_get_input(struct file *file, void *priv, unsigned int *index)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
- *index = fh->solo_dev->cur_disp_ch;
+ *index = solo_dev->cur_disp_ch;
return 0;
}
@@ -584,8 +528,7 @@ static int solo_enum_fmt_cap(struct file *file, void *priv,
static int solo_try_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct solo_filehandle *fh = priv;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = video_drvdata(file);
struct v4l2_pix_format *pix = &f->fmt.pix;
int image_size = solo_image_size(solo_dev);
@@ -605,9 +548,9 @@ static int solo_try_fmt_cap(struct file *file, void *priv,
static int solo_set_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
- if (videobuf_queue_is_busy(&fh->vidq))
+ if (videobuf_queue_is_busy(&solo_dev->vidq))
return -EBUSY;
/* For right now, if it doesn't match our running config,
@@ -618,8 +561,7 @@ static int solo_set_fmt_cap(struct file *file, void *priv,
static int solo_get_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct solo_filehandle *fh = priv;
- struct solo_dev *solo_dev = fh->solo_dev;
+ struct solo_dev *solo_dev = video_drvdata(file);
struct v4l2_pix_format *pix = &f->fmt.pix;
pix->width = solo_dev->video_hsize;
@@ -637,51 +579,56 @@ static int solo_get_fmt_cap(struct file *file, void *priv,
static int solo_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *req)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
- return videobuf_reqbufs(&fh->vidq, req);
+ return videobuf_reqbufs(&solo_dev->vidq, req);
}
static int solo_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
- return videobuf_querybuf(&fh->vidq, buf);
+ return videobuf_querybuf(&solo_dev->vidq, buf);
}
static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
- return videobuf_qbuf(&fh->vidq, buf);
+ return videobuf_qbuf(&solo_dev->vidq, buf);
}
static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
- return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
+ return videobuf_dqbuf(&solo_dev->vidq, buf, file->f_flags & O_NONBLOCK);
}
static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
+ int ret;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return videobuf_streamon(&fh->vidq);
+ ret = solo_start_thread(solo_dev);
+ if (ret)
+ return ret;
+
+ return videobuf_streamon(&solo_dev->vidq);
}
static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct solo_filehandle *fh = priv;
+ struct solo_dev *solo_dev = video_drvdata(file);
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return videobuf_streamoff(&fh->vidq);
+ return videobuf_streamoff(&solo_dev->vidq);
}
static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
@@ -720,7 +667,7 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
static const struct v4l2_file_operations solo_v4l2_fops = {
.owner = THIS_MODULE,
- .open = solo_v4l2_open,
+ .open = v4l2_fh_open,
.release = solo_v4l2_release,
.read = solo_v4l2_read,
.poll = solo_v4l2_poll,
@@ -798,21 +745,17 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl;
set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags);
- ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
- if (ret < 0) {
- video_device_release(solo_dev->vfd);
- solo_dev->vfd = NULL;
- return ret;
- }
-
video_set_drvdata(solo_dev->vfd, solo_dev);
- snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
- SOLO6X10_NAME, solo_dev->vfd->num);
+ spin_lock_init(&solo_dev->slock);
+ INIT_LIST_HEAD(&solo_dev->vidq_active);
- dev_info(&solo_dev->pdev->dev,
- "Display as /dev/video%d with %d inputs (%d extended)\n",
- solo_dev->vfd->num, solo_dev->nr_chans, solo_dev->nr_ext);
+ videobuf_queue_dma_contig_init(&solo_dev->vidq, &solo_video_qops,
+ &solo_dev->pdev->dev, &solo_dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct videobuf_buffer),
+ solo_dev, NULL);
/* Cycle all the channels and clear */
for (i = 0; i < solo_dev->nr_chans; i++) {
@@ -826,6 +769,20 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
while (erase_off(solo_dev))
/* Do nothing */;
+ ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
+ if (ret < 0) {
+ video_device_release(solo_dev->vfd);
+ solo_dev->vfd = NULL;
+ return ret;
+ }
+
+ snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
+ SOLO6X10_NAME, solo_dev->vfd->num);
+
+ dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with "
+ "%d inputs (%d extended)\n", solo_dev->vfd->num,
+ solo_dev->nr_chans, solo_dev->nr_ext);
+
return 0;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 08/19] solo6x10: move global fields in solo_enc_fh to solo_enc_dev.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (6 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 07/19] solo6x10: move global fields in solo_dev_fh to solo_dev Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 09/19] solo6x10: convert encoder nodes to vb2 Hans Verkuil
` (10 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
All fields in solo_enc_fh do not belong there since they refer to global
properties. After moving all these fields to solo_enc_dev the solo_dev_fh
struct can be removed completely.
Note that this also kills the 'listener' feature of this driver. This
feature (where multiple filehandles can read the video) is illegal in the
V4L2 API. Do this in userspace: it's much more efficient to copy memory
than it is to DMA to every listener.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/solo6x10.h | 17 +-
drivers/staging/media/solo6x10/v4l2-enc.c | 379 ++++++++++++-----------------
2 files changed, 175 insertions(+), 221 deletions(-)
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 361f41f..cf88db3 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -135,6 +135,11 @@ struct solo_p2m_dev {
#define OSD_TEXT_MAX 44
+enum solo_enc_types {
+ SOLO_ENC_TYPE_STD,
+ SOLO_ENC_TYPE_EXT,
+};
+
struct solo_enc_dev {
struct solo_dev *solo_dev;
/* V4L2 Items */
@@ -163,8 +168,16 @@ struct solo_enc_dev {
unsigned char jpeg_header[1024];
int jpeg_len;
- /* File handles that are listening for buffers */
- struct list_head listeners;
+ u32 fmt;
+ u8 enc_on;
+ enum solo_enc_types type;
+ struct videobuf_queue vidq;
+ struct list_head vidq_active;
+ int desc_count;
+ int desc_nelts;
+ struct solo_p2m_desc *desc_items;
+ dma_addr_t desc_dma;
+ spinlock_t av_lock;
};
/* The SOLO6x10 PCI Device */
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 366f2b3..65609c1 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -41,27 +41,6 @@
#define MP4_QS 16
#define DMA_ALIGN 4096
-enum solo_enc_types {
- SOLO_ENC_TYPE_STD,
- SOLO_ENC_TYPE_EXT,
-};
-
-struct solo_enc_fh {
- struct v4l2_fh fh;
- struct solo_enc_dev *enc;
- u32 fmt;
- u8 enc_on;
- enum solo_enc_types type;
- struct videobuf_queue vidq;
- struct list_head vidq_active;
- int desc_count;
- int desc_nelts;
- struct solo_p2m_desc *desc_items;
- dma_addr_t desc_dma;
- spinlock_t av_lock;
- struct list_head list;
-};
-
struct solo_videobuf {
struct videobuf_buffer vb;
unsigned int flags;
@@ -286,16 +265,15 @@ static void solo_update_mode(struct solo_enc_dev *solo_enc)
}
/* MUST be called with solo_enc->enable_lock held */
-static int __solo_enc_on(struct solo_enc_fh *fh)
+static int __solo_enc_on(struct solo_enc_dev *solo_enc)
{
- struct solo_enc_dev *solo_enc = fh->enc;
u8 ch = solo_enc->ch;
struct solo_dev *solo_dev = solo_enc->solo_dev;
u8 interval;
BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
- if (fh->enc_on)
+ if (solo_enc->enc_on)
return 0;
solo_update_mode(solo_enc);
@@ -308,15 +286,14 @@ static int __solo_enc_on(struct solo_enc_fh *fh)
solo_dev->enc_bw_remain -= solo_enc->bw_weight;
}
- fh->enc_on = 1;
- list_add(&fh->list, &solo_enc->listeners);
+ solo_enc->enc_on = 1;
- if (fh->type == SOLO_ENC_TYPE_EXT)
+ if (solo_enc->type == SOLO_ENC_TYPE_EXT)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1);
/* Reset the encoder if we are the first mpeg reader, else only reset
* on the first mjpeg reader. */
- if (fh->fmt == V4L2_PIX_FMT_MPEG) {
+ if (solo_enc->fmt == V4L2_PIX_FMT_MPEG) {
atomic_inc(&solo_enc->readers);
if (atomic_inc_return(&solo_enc->mpeg_readers) > 1)
return 0;
@@ -352,32 +329,29 @@ static int __solo_enc_on(struct solo_enc_fh *fh)
return 0;
}
-static int solo_enc_on(struct solo_enc_fh *fh)
+static int solo_enc_on(struct solo_enc_dev *solo_enc)
{
- struct solo_enc_dev *solo_enc = fh->enc;
int ret;
mutex_lock(&solo_enc->enable_lock);
- ret = __solo_enc_on(fh);
+ ret = __solo_enc_on(solo_enc);
mutex_unlock(&solo_enc->enable_lock);
return ret;
}
-static void __solo_enc_off(struct solo_enc_fh *fh)
+static void __solo_enc_off(struct solo_enc_dev *solo_enc)
{
- struct solo_enc_dev *solo_enc = fh->enc;
struct solo_dev *solo_dev = solo_enc->solo_dev;
BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
- if (!fh->enc_on)
+ if (!solo_enc->enc_on)
return;
- list_del(&fh->list);
- fh->enc_on = 0;
+ solo_enc->enc_on = 0;
- if (fh->fmt == V4L2_PIX_FMT_MPEG)
+ if (solo_enc->fmt == V4L2_PIX_FMT_MPEG)
atomic_dec(&solo_enc->mpeg_readers);
if (atomic_dec_return(&solo_enc->readers) > 0)
@@ -389,12 +363,10 @@ static void __solo_enc_off(struct solo_enc_fh *fh)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0);
}
-static void solo_enc_off(struct solo_enc_fh *fh)
+static void solo_enc_off(struct solo_enc_dev *solo_enc)
{
- struct solo_enc_dev *solo_enc = fh->enc;
-
mutex_lock(&solo_enc->enable_lock);
- __solo_enc_off(fh);
+ __solo_enc_off(solo_enc);
mutex_unlock(&solo_enc->enable_lock);
}
@@ -430,11 +402,11 @@ static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma,
/* Build a descriptor queue out of an SG list and send it to the P2M for
* processing. */
-static int solo_send_desc(struct solo_enc_fh *fh, int skip,
+static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
struct videobuf_dmabuf *vbuf, int off, int size,
unsigned int base, unsigned int base_size)
{
- struct solo_dev *solo_dev = fh->enc->solo_dev;
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
struct scatterlist *sg;
int i;
int ret;
@@ -442,7 +414,7 @@ static int solo_send_desc(struct solo_enc_fh *fh, int skip,
if (WARN_ON_ONCE(size > FRAME_BUF_SIZE))
return -EINVAL;
- fh->desc_count = 1;
+ solo_enc->desc_count = 1;
for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
struct solo_p2m_desc *desc;
@@ -450,7 +422,7 @@ static int solo_send_desc(struct solo_enc_fh *fh, int skip,
int len;
int left = base_size - off;
- desc = &fh->desc_items[fh->desc_count++];
+ desc = &solo_enc->desc_items[solo_enc->desc_count++];
dma = sg_dma_address(sg);
len = sg_dma_len(sg);
@@ -486,7 +458,7 @@ static int solo_send_desc(struct solo_enc_fh *fh, int skip,
if (ret)
return ret;
- fh->desc_count--;
+ solo_enc->desc_count--;
}
size -= len;
@@ -498,27 +470,26 @@ static int solo_send_desc(struct solo_enc_fh *fh, int skip,
off -= base_size;
/* Because we may use two descriptors per loop */
- if (fh->desc_count >= (fh->desc_nelts - 1)) {
- ret = solo_p2m_dma_desc(solo_dev, fh->desc_items,
- fh->desc_dma,
- fh->desc_count - 1);
+ if (solo_enc->desc_count >= (solo_enc->desc_nelts - 1)) {
+ ret = solo_p2m_dma_desc(solo_dev, solo_enc->desc_items,
+ solo_enc->desc_dma,
+ solo_enc->desc_count - 1);
if (ret)
return ret;
- fh->desc_count = 1;
+ solo_enc->desc_count = 1;
}
}
- if (fh->desc_count <= 1)
+ if (solo_enc->desc_count <= 1)
return 0;
- return solo_p2m_dma_desc(solo_dev, fh->desc_items, fh->desc_dma,
- fh->desc_count - 1);
+ return solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, solo_enc->desc_dma,
+ solo_enc->desc_count - 1);
}
-static int solo_fill_jpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
+static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, struct videobuf_buffer *vb,
struct videobuf_dmabuf *vbuf, struct vop_header *vh)
{
- struct solo_enc_dev *solo_enc = fh->enc;
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct solo_videobuf *svb = (struct solo_videobuf *)vb;
int frame_size;
@@ -539,15 +510,14 @@ static int solo_fill_jpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1))
& ~(DMA_ALIGN - 1);
- return solo_send_desc(fh, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
+ return solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
SOLO_JPEG_EXT_SIZE(solo_dev));
}
-static int solo_fill_mpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
+static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, struct videobuf_buffer *vb,
struct videobuf_dmabuf *vbuf, struct vop_header *vh)
{
- struct solo_enc_dev *solo_enc = fh->enc;
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct solo_videobuf *svb = (struct solo_videobuf *)vb;
int frame_off, frame_size;
@@ -580,16 +550,15 @@ static int solo_fill_mpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1))
& ~(DMA_ALIGN - 1);
- return solo_send_desc(fh, skip, vbuf, frame_off, frame_size,
+ return solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size,
SOLO_MP4E_EXT_ADDR(solo_dev),
SOLO_MP4E_EXT_SIZE(solo_dev));
}
-static int solo_enc_fillbuf(struct solo_enc_fh *fh,
+static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
struct videobuf_buffer *vb,
struct solo_enc_buf *enc_buf)
{
- struct solo_enc_dev *solo_enc = fh->enc;
struct solo_videobuf *svb = (struct solo_videobuf *)vb;
struct videobuf_dmabuf *vbuf = NULL;
struct vop_header *vh = enc_buf->vh;
@@ -613,10 +582,10 @@ static int solo_enc_fillbuf(struct solo_enc_fh *fh,
svb->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
}
- if (fh->fmt == V4L2_PIX_FMT_MPEG)
- ret = solo_fill_mpeg(fh, vb, vbuf, vh);
+ if (solo_enc->fmt == V4L2_PIX_FMT_MPEG)
+ ret = solo_fill_mpeg(solo_enc, vb, vbuf, vh);
else
- ret = solo_fill_jpeg(fh, vb, vbuf, vh);
+ ret = solo_fill_jpeg(solo_enc, vb, vbuf, vh);
vbuf_error:
/* On error, we push this buffer back into the queue. The
@@ -625,10 +594,10 @@ vbuf_error:
if (ret) {
unsigned long flags;
- spin_lock_irqsave(&fh->av_lock, flags);
- list_add(&vb->queue, &fh->vidq_active);
+ spin_lock_irqsave(&solo_enc->av_lock, flags);
+ list_add(&vb->queue, &solo_enc->vidq_active);
vb->state = VIDEOBUF_QUEUED;
- spin_unlock_irqrestore(&fh->av_lock, flags);
+ spin_unlock_irqrestore(&solo_enc->av_lock, flags);
} else {
vb->state = VIDEOBUF_DONE;
vb->field_count++;
@@ -644,34 +613,29 @@ vbuf_error:
static void solo_enc_handle_one(struct solo_enc_dev *solo_enc,
struct solo_enc_buf *enc_buf)
{
- struct solo_enc_fh *fh;
+ struct videobuf_buffer *vb;
+ unsigned long flags;
mutex_lock(&solo_enc->enable_lock);
- list_for_each_entry(fh, &solo_enc->listeners, list) {
- struct videobuf_buffer *vb;
- unsigned long flags;
-
- if (fh->type != enc_buf->type)
- continue;
-
-
- if (list_empty(&fh->vidq_active))
- continue;
+ if (solo_enc->type != enc_buf->type)
+ goto unlock;
- spin_lock_irqsave(&fh->av_lock, flags);
+ if (list_empty(&solo_enc->vidq_active))
+ goto unlock;
- vb = list_first_entry(&fh->vidq_active,
- struct videobuf_buffer, queue);
+ spin_lock_irqsave(&solo_enc->av_lock, flags);
- list_del(&vb->queue);
- vb->state = VIDEOBUF_ACTIVE;
+ vb = list_first_entry(&solo_enc->vidq_active,
+ struct videobuf_buffer, queue);
- spin_unlock_irqrestore(&fh->av_lock, flags);
+ list_del(&vb->queue);
+ vb->state = VIDEOBUF_ACTIVE;
- solo_enc_fillbuf(fh, vb, enc_buf);
- }
+ spin_unlock_irqrestore(&solo_enc->av_lock, flags);
+ solo_enc_fillbuf(solo_enc, vb, enc_buf);
+unlock:
mutex_unlock(&solo_enc->enable_lock);
}
@@ -799,10 +763,10 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq,
static void solo_enc_buf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
- struct solo_enc_fh *fh = vq->priv_data;
+ struct solo_enc_dev *solo_enc = vq->priv_data;
vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &fh->vidq_active);
+ list_add_tail(&vb->queue, &solo_enc->vidq_active);
}
static void solo_enc_buf_release(struct videobuf_queue *vq,
@@ -824,20 +788,20 @@ static const struct videobuf_queue_ops solo_enc_video_qops = {
static unsigned int solo_enc_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct solo_enc_fh *fh = file->private_data;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
unsigned long req_events = poll_requested_events(wait);
unsigned res = v4l2_ctrl_poll(file, wait);
if (!(req_events & (POLLIN | POLLRDNORM)))
return res;
- return videobuf_poll_stream(file, &fh->vidq, wait);
+ return videobuf_poll_stream(file, &solo_enc->vidq, wait);
}
static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct solo_enc_fh *fh = file->private_data;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
- return videobuf_mmap_mapper(&fh->vidq, vma);
+ return videobuf_mmap_mapper(&solo_enc->vidq, vma);
}
static int solo_ring_start(struct solo_dev *solo_dev)
@@ -875,91 +839,50 @@ static int solo_enc_open(struct file *file)
{
struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
- struct solo_enc_fh *fh;
- int ret;
+ int ret = v4l2_fh_open(file);
- ret = solo_ring_start(solo_dev);
if (ret)
return ret;
-
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL) {
- solo_ring_stop(solo_dev);
- return -ENOMEM;
- }
-
- fh->desc_nelts = 32;
- fh->desc_items = pci_alloc_consistent(solo_dev->pdev,
- sizeof(struct solo_p2m_desc) *
- fh->desc_nelts, &fh->desc_dma);
- if (fh->desc_items == NULL) {
- kfree(fh);
- solo_ring_stop(solo_dev);
- return -ENOMEM;
+ ret = solo_ring_start(solo_dev);
+ if (ret) {
+ v4l2_fh_release(file);
+ return ret;
}
-
- v4l2_fh_init(&fh->fh, video_devdata(file));
- fh->enc = solo_enc;
- spin_lock_init(&fh->av_lock);
- file->private_data = fh;
- INIT_LIST_HEAD(&fh->vidq_active);
- fh->fmt = V4L2_PIX_FMT_MPEG;
- fh->type = SOLO_ENC_TYPE_STD;
-
- videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
- &solo_dev->pdev->dev,
- &fh->av_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct solo_videobuf),
- fh, NULL);
- v4l2_fh_add(&fh->fh);
return 0;
}
static ssize_t solo_enc_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
- struct solo_enc_fh *fh = file->private_data;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
int ret;
/* Make sure the encoder is on */
- ret = solo_enc_on(fh);
+ ret = solo_enc_on(solo_enc);
if (ret)
return ret;
- return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+ return videobuf_read_stream(&solo_enc->vidq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
}
static int solo_enc_release(struct file *file)
{
- struct solo_enc_fh *fh = file->private_data;
- struct solo_dev *solo_dev = fh->enc->solo_dev;
-
- solo_enc_off(fh);
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
-
- videobuf_stop(&fh->vidq);
- videobuf_mmap_free(&fh->vidq);
-
- pci_free_consistent(fh->enc->solo_dev->pdev,
- sizeof(struct solo_p2m_desc) *
- fh->desc_nelts, fh->desc_items, fh->desc_dma);
-
- kfree(fh);
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
+ solo_enc_off(solo_enc);
+ videobuf_stop(&solo_enc->vidq);
+ videobuf_mmap_free(&solo_enc->vidq);
solo_ring_stop(solo_dev);
- return 0;
+ return v4l2_fh_release(file);
}
static int solo_enc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
strcpy(cap->driver, SOLO6X10_NAME);
@@ -976,8 +899,7 @@ static int solo_enc_querycap(struct file *file, void *priv,
static int solo_enc_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
if (input->index)
@@ -1039,8 +961,7 @@ static int solo_enc_enum_fmt_cap(struct file *file, void *priv,
static int solo_enc_try_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_pix_format *pix = &f->fmt.pix;
@@ -1081,8 +1002,7 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
static int solo_enc_set_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
@@ -1110,10 +1030,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
solo_enc->mode = SOLO_ENC_MODE_CIF;
/* This does not change the encoder at all */
- fh->fmt = pix->pixelformat;
+ solo_enc->fmt = pix->pixelformat;
if (pix->priv)
- fh->type = SOLO_ENC_TYPE_EXT;
+ solo_enc->type = SOLO_ENC_TYPE_EXT;
mutex_unlock(&solo_enc->enable_lock);
@@ -1123,13 +1043,12 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
static int solo_enc_get_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct v4l2_pix_format *pix = &f->fmt.pix;
pix->width = solo_enc->width;
pix->height = solo_enc->height;
- pix->pixelformat = fh->fmt;
+ pix->pixelformat = solo_enc->fmt;
pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED :
V4L2_FIELD_NONE;
pix->sizeimage = FRAME_BUF_SIZE;
@@ -1142,45 +1061,45 @@ static int solo_enc_get_fmt_cap(struct file *file, void *priv,
static int solo_enc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *req)
{
- struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
- return videobuf_reqbufs(&fh->vidq, req);
+ return videobuf_reqbufs(&solo_enc->vidq, req);
}
static int solo_enc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
- return videobuf_querybuf(&fh->vidq, buf);
+ return videobuf_querybuf(&solo_enc->vidq, buf);
}
static int solo_enc_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
- return videobuf_qbuf(&fh->vidq, buf);
+ return videobuf_qbuf(&solo_enc->vidq, buf);
}
static int solo_enc_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_videobuf *svb;
int ret;
/* Make sure the encoder is on */
- ret = solo_enc_on(fh);
+ ret = solo_enc_on(solo_enc);
if (ret)
return ret;
- ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
+ ret = videobuf_dqbuf(&solo_enc->vidq, buf, file->f_flags & O_NONBLOCK);
if (ret)
return ret;
/* Copy over the flags */
- svb = (struct solo_videobuf *)fh->vidq.bufs[buf->index];
+ svb = (struct solo_videobuf *)solo_enc->vidq.bufs[buf->index];
buf->flags |= svb->flags;
return 0;
@@ -1189,26 +1108,26 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
static int solo_enc_streamon(struct file *file, void *priv,
enum v4l2_buf_type i)
{
- struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return videobuf_streamon(&fh->vidq);
+ return videobuf_streamon(&solo_enc->vidq);
}
static int solo_enc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type i)
{
- struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
int ret;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- ret = videobuf_streamoff(&fh->vidq);
+ ret = videobuf_streamoff(&solo_enc->vidq);
if (!ret)
- solo_enc_off(fh);
+ solo_enc_off(solo_enc);
return ret;
}
@@ -1221,8 +1140,8 @@ static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i)
static int solo_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
- struct solo_enc_fh *fh = priv;
- struct solo_dev *solo_dev = fh->enc->solo_dev;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
if (fsize->pixel_format != V4L2_PIX_FMT_MPEG &&
fsize->pixel_format != V4L2_PIX_FMT_MJPEG)
@@ -1249,8 +1168,8 @@ static int solo_enum_framesizes(struct file *file, void *priv,
static int solo_enum_frameintervals(struct file *file, void *priv,
struct v4l2_frmivalenum *fintv)
{
- struct solo_enc_fh *fh = priv;
- struct solo_dev *solo_dev = fh->enc->solo_dev;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
if (fintv->pixel_format != V4L2_PIX_FMT_MPEG &&
fintv->pixel_format != V4L2_PIX_FMT_MJPEG)
@@ -1280,8 +1199,7 @@ static int solo_enum_frameintervals(struct file *file, void *priv,
static int solo_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_captureparm *cp = &sp->parm.capture;
@@ -1298,8 +1216,7 @@ static int solo_g_parm(struct file *file, void *priv,
static int solo_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_captureparm *cp = &sp->parm.capture;
@@ -1512,45 +1429,17 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL);
if (hdl->error) {
ret = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- kfree(solo_enc);
- return ERR_PTR(ret);
- }
-
- solo_enc->vfd = video_device_alloc();
- if (!solo_enc->vfd) {
- v4l2_ctrl_handler_free(hdl);
- kfree(solo_enc);
- return ERR_PTR(-ENOMEM);
+ goto hdl_free;
}
solo_enc->solo_dev = solo_dev;
solo_enc->ch = ch;
-
- *solo_enc->vfd = solo_enc_template;
- solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev;
- solo_enc->vfd->ctrl_handler = hdl;
- set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags);
- ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
- if (ret < 0) {
- video_device_release(solo_enc->vfd);
- v4l2_ctrl_handler_free(hdl);
- kfree(solo_enc);
- return ERR_PTR(ret);
- }
-
- video_set_drvdata(solo_enc->vfd, solo_enc);
-
- snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name),
- "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num,
- solo_enc->vfd->num);
-
- INIT_LIST_HEAD(&solo_enc->listeners);
- mutex_init(&solo_enc->enable_lock);
- spin_lock_init(&solo_enc->motion_lock);
+ spin_lock_init(&solo_enc->av_lock);
+ INIT_LIST_HEAD(&solo_enc->vidq_active);
+ solo_enc->fmt = V4L2_PIX_FMT_MPEG;
+ solo_enc->type = SOLO_ENC_TYPE_STD;
atomic_set(&solo_enc->readers, 0);
- atomic_set(&solo_enc->mpeg_readers, 0);
solo_enc->qp = SOLO_DEFAULT_QP;
solo_enc->gop = solo_dev->fps;
@@ -1558,15 +1447,65 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
solo_enc->mode = SOLO_ENC_MODE_CIF;
solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;
- mutex_lock(&solo_enc->enable_lock);
+ spin_lock(&solo_enc->av_lock);
solo_update_mode(solo_enc);
- mutex_unlock(&solo_enc->enable_lock);
+ spin_unlock(&solo_enc->av_lock);
+
+ mutex_init(&solo_enc->enable_lock);
+ spin_lock_init(&solo_enc->motion_lock);
+
+ atomic_set(&solo_enc->readers, 0);
+ atomic_set(&solo_enc->mpeg_readers, 0);
/* Initialize this per encoder */
solo_enc->jpeg_len = sizeof(jpeg_header);
memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len);
+ solo_enc->desc_nelts = 32;
+ solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev,
+ sizeof(struct solo_p2m_desc) *
+ solo_enc->desc_nelts, &solo_enc->desc_dma);
+ ret = -ENOMEM;
+ if (solo_enc->desc_items == NULL)
+ goto hdl_free;
+
+ videobuf_queue_sg_init(&solo_enc->vidq, &solo_enc_video_qops,
+ &solo_dev->pdev->dev,
+ &solo_enc->av_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct solo_videobuf),
+ solo_enc, NULL);
+
+ solo_enc->vfd = video_device_alloc();
+ if (!solo_enc->vfd)
+ goto pci_free;
+
+ *solo_enc->vfd = solo_enc_template;
+ solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev;
+ solo_enc->vfd->ctrl_handler = hdl;
+ set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags);
+ video_set_drvdata(solo_enc->vfd, solo_enc);
+ ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
+ if (ret < 0)
+ goto vdev_release;
+
+ snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name),
+ "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num,
+ solo_enc->vfd->num);
+
return solo_enc;
+
+vdev_release:
+ video_device_release(solo_enc->vfd);
+pci_free:
+ pci_free_consistent(solo_enc->solo_dev->pdev,
+ sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts,
+ solo_enc->desc_items, solo_enc->desc_dma);
+hdl_free:
+ v4l2_ctrl_handler_free(hdl);
+ kfree(solo_enc);
+ return ERR_PTR(ret);
}
static void solo_enc_free(struct solo_enc_dev *solo_enc)
@@ -1605,6 +1544,7 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
solo_enc_free(solo_dev->v4l2_enc[i]);
pci_free_consistent(solo_dev->pdev, solo_dev->vh_size,
solo_dev->vh_buf, solo_dev->vh_dma);
+ solo_dev->vh_buf = NULL;
return ret;
}
@@ -1627,6 +1567,7 @@ void solo_enc_v4l2_exit(struct solo_dev *solo_dev)
for (i = 0; i < solo_dev->nr_chans; i++)
solo_enc_free(solo_dev->v4l2_enc[i]);
- pci_free_consistent(solo_dev->pdev, solo_dev->vh_size,
+ if (solo_dev->vh_buf)
+ pci_free_consistent(solo_dev->pdev, solo_dev->vh_size,
solo_dev->vh_buf, solo_dev->vh_dma);
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 09/19] solo6x10: convert encoder nodes to vb2.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (7 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 08/19] solo6x10: move global fields in solo_enc_fh to solo_enc_dev Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 10/19] solo6x10: convert the display node " Hans Verkuil
` (9 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
As a consequence the ioctl op has been replaced by unlocked_ioctl.
Since we are now using the core lock the locking scheme has been
simplified as well.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/Kconfig | 1 +
drivers/staging/media/solo6x10/solo6x10.h | 13 +-
drivers/staging/media/solo6x10/v4l2-enc.c | 505 ++++++++---------------------
3 files changed, 140 insertions(+), 379 deletions(-)
diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig
index 63352de..f93b4ca 100644
--- a/drivers/staging/media/solo6x10/Kconfig
+++ b/drivers/staging/media/solo6x10/Kconfig
@@ -2,6 +2,7 @@ config SOLO6X10
tristate "Softlogic 6x10 MPEG codec cards"
depends on PCI && VIDEO_DEV && SND && I2C
select VIDEOBUF_DMA_SG
+ select VIDEOBUF2_DMA_SG
select SND_PCM
---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index cf88db3..5b21178 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -40,6 +40,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
#include "registers.h"
@@ -135,6 +136,11 @@ struct solo_p2m_dev {
#define OSD_TEXT_MAX 44
+struct solo_vb2_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
enum solo_enc_types {
SOLO_ENC_TYPE_STD,
SOLO_ENC_TYPE_EXT,
@@ -146,10 +152,8 @@ struct solo_enc_dev {
struct v4l2_ctrl_handler hdl;
struct video_device *vfd;
/* General accounting */
- struct mutex enable_lock;
+ struct mutex lock;
spinlock_t motion_lock;
- atomic_t readers;
- atomic_t mpeg_readers;
u8 ch;
u8 mode, gop, qp, interlaced, interval;
u8 bw_weight;
@@ -169,9 +173,8 @@ struct solo_enc_dev {
int jpeg_len;
u32 fmt;
- u8 enc_on;
enum solo_enc_types type;
- struct videobuf_queue vidq;
+ struct vb2_queue vidq;
struct list_head vidq_active;
int desc_count;
int desc_nelts;
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 65609c1..eb82299 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -30,7 +30,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf2-dma-sg.h>
#include "solo6x10.h"
#include "tw28.h"
@@ -181,7 +181,6 @@ static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
spin_unlock_irqrestore(&solo_enc->motion_lock, flags);
}
-/* MUST be called with solo_enc->enable_lock held */
static void solo_update_mode(struct solo_enc_dev *solo_enc)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
@@ -264,43 +263,22 @@ static void solo_update_mode(struct solo_enc_dev *solo_enc)
jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN);
}
-/* MUST be called with solo_enc->enable_lock held */
-static int __solo_enc_on(struct solo_enc_dev *solo_enc)
+static int solo_enc_on(struct solo_enc_dev *solo_enc)
{
u8 ch = solo_enc->ch;
struct solo_dev *solo_dev = solo_enc->solo_dev;
u8 interval;
- BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
-
- if (solo_enc->enc_on)
- return 0;
-
solo_update_mode(solo_enc);
- /* Make sure to bw check on first reader */
- if (!atomic_read(&solo_enc->readers)) {
- if (solo_enc->bw_weight > solo_dev->enc_bw_remain)
- return -EBUSY;
- else
- solo_dev->enc_bw_remain -= solo_enc->bw_weight;
- }
-
- solo_enc->enc_on = 1;
+ /* Make sure to do a bandwidth check */
+ if (solo_enc->bw_weight > solo_dev->enc_bw_remain)
+ return -EBUSY;
+ solo_dev->enc_bw_remain -= solo_enc->bw_weight;
if (solo_enc->type == SOLO_ENC_TYPE_EXT)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1);
- /* Reset the encoder if we are the first mpeg reader, else only reset
- * on the first mjpeg reader. */
- if (solo_enc->fmt == V4L2_PIX_FMT_MPEG) {
- atomic_inc(&solo_enc->readers);
- if (atomic_inc_return(&solo_enc->mpeg_readers) > 1)
- return 0;
- } else if (atomic_inc_return(&solo_enc->readers) > 1) {
- return 0;
- }
-
/* Disable all encoding for this channel */
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0);
@@ -329,47 +307,16 @@ static int __solo_enc_on(struct solo_enc_dev *solo_enc)
return 0;
}
-static int solo_enc_on(struct solo_enc_dev *solo_enc)
-{
- int ret;
-
- mutex_lock(&solo_enc->enable_lock);
- ret = __solo_enc_on(solo_enc);
- mutex_unlock(&solo_enc->enable_lock);
-
- return ret;
-}
-
-static void __solo_enc_off(struct solo_enc_dev *solo_enc)
+static void solo_enc_off(struct solo_enc_dev *solo_enc)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
- BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
-
- if (!solo_enc->enc_on)
- return;
-
- solo_enc->enc_on = 0;
-
- if (solo_enc->fmt == V4L2_PIX_FMT_MPEG)
- atomic_dec(&solo_enc->mpeg_readers);
-
- if (atomic_dec_return(&solo_enc->readers) > 0)
- return;
-
solo_dev->enc_bw_remain += solo_enc->bw_weight;
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0);
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0);
}
-static void solo_enc_off(struct solo_enc_dev *solo_enc)
-{
- mutex_lock(&solo_enc->enable_lock);
- __solo_enc_off(solo_enc);
- mutex_unlock(&solo_enc->enable_lock);
-}
-
static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma,
unsigned int off, unsigned int size)
{
@@ -403,7 +350,7 @@ static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma,
/* Build a descriptor queue out of an SG list and send it to the P2M for
* processing. */
static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
- struct videobuf_dmabuf *vbuf, int off, int size,
+ struct vb2_dma_sg_desc *vbuf, int off, int size,
unsigned int base, unsigned int base_size)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
@@ -416,7 +363,7 @@ static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
solo_enc->desc_count = 1;
- for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
+ for_each_sg(vbuf->sglist, sg, vbuf->num_pages, i) {
struct solo_p2m_desc *desc;
dma_addr_t dma;
int len;
@@ -487,61 +434,62 @@ static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
solo_enc->desc_count - 1);
}
-static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, struct videobuf_buffer *vb,
- struct videobuf_dmabuf *vbuf, struct vop_header *vh)
+static int solo_fill_jpeg(struct solo_enc_dev *solo_enc,
+ struct vb2_buffer *vb, struct vop_header *vh)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
- struct solo_videobuf *svb = (struct solo_videobuf *)vb;
+ struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0);
int frame_size;
+ int ret;
- svb->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
- if (vb->bsize < (vh->jpeg_size + solo_enc->jpeg_len))
+ if (vb2_plane_size(vb, 0) < vh->jpeg_size + solo_enc->jpeg_len)
return -EIO;
- vb->width = solo_enc->width;
- vb->height = solo_enc->height;
- vb->size = vh->jpeg_size + solo_enc->jpeg_len;
-
- sg_copy_from_buffer(vbuf->sglist, vbuf->sglen,
- solo_enc->jpeg_header,
- solo_enc->jpeg_len);
+ sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages,
+ solo_enc->jpeg_header,
+ solo_enc->jpeg_len);
frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1))
& ~(DMA_ALIGN - 1);
-
- return solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
- frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
- SOLO_JPEG_EXT_SIZE(solo_dev));
+ vb2_set_plane_payload(vb, 0, vh->jpeg_size + solo_enc->jpeg_len);
+
+ dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+ DMA_FROM_DEVICE);
+ ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
+ frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
+ SOLO_JPEG_EXT_SIZE(solo_dev));
+ dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+ DMA_FROM_DEVICE);
+ return ret;
}
-static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, struct videobuf_buffer *vb,
- struct videobuf_dmabuf *vbuf, struct vop_header *vh)
+static int solo_fill_mpeg(struct solo_enc_dev *solo_enc,
+ struct vb2_buffer *vb, struct vop_header *vh)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
- struct solo_videobuf *svb = (struct solo_videobuf *)vb;
+ struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0);
int frame_off, frame_size;
int skip = 0;
+ int ret;
- if (vb->bsize < vh->mpeg_size)
+ if (vb2_plane_size(vb, 0) < vh->mpeg_size)
return -EIO;
- vb->width = vh->hsize << 4;
- vb->height = vh->vsize << 4;
- vb->size = vh->mpeg_size;
-
/* If this is a key frame, add extra header */
if (!vh->vop_type) {
- sg_copy_from_buffer(vbuf->sglist, vbuf->sglen,
- solo_enc->vop,
- solo_enc->vop_len);
+ sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages,
+ solo_enc->vop,
+ solo_enc->vop_len);
skip = solo_enc->vop_len;
- vb->size += solo_enc->vop_len;
- svb->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ vb2_set_plane_payload(vb, 0, vh->mpeg_size + solo_enc->vop_len);
} else {
- svb->flags |= V4L2_BUF_FLAG_PFRAME;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ vb2_set_plane_payload(vb, 0, vh->mpeg_size);
}
/* Now get the actual mpeg payload */
@@ -550,93 +498,67 @@ static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, struct videobuf_buffer
frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1))
& ~(DMA_ALIGN - 1);
- return solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size,
- SOLO_MP4E_EXT_ADDR(solo_dev),
- SOLO_MP4E_EXT_SIZE(solo_dev));
+ dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+ DMA_FROM_DEVICE);
+ ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size,
+ SOLO_MP4E_EXT_ADDR(solo_dev),
+ SOLO_MP4E_EXT_SIZE(solo_dev));
+ dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+ DMA_FROM_DEVICE);
+ return ret;
}
static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
- struct videobuf_buffer *vb,
- struct solo_enc_buf *enc_buf)
+ struct vb2_buffer *vb, struct solo_enc_buf *enc_buf)
{
- struct solo_videobuf *svb = (struct solo_videobuf *)vb;
- struct videobuf_dmabuf *vbuf = NULL;
struct vop_header *vh = enc_buf->vh;
int ret;
- vbuf = videobuf_to_dma(vb);
- if (WARN_ON_ONCE(!vbuf)) {
- ret = -EIO;
- goto vbuf_error;
- }
-
- /* Setup some common flags for both types */
- svb->flags = V4L2_BUF_FLAG_TIMECODE;
- vb->ts.tv_sec = vh->sec;
- vb->ts.tv_usec = vh->usec;
-
/* Check for motion flags */
if (solo_is_motion_on(solo_enc)) {
- svb->flags |= V4L2_BUF_FLAG_MOTION_ON;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_ON;
if (enc_buf->motion)
- svb->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
}
if (solo_enc->fmt == V4L2_PIX_FMT_MPEG)
- ret = solo_fill_mpeg(solo_enc, vb, vbuf, vh);
+ ret = solo_fill_mpeg(solo_enc, vb, vh);
else
- ret = solo_fill_jpeg(solo_enc, vb, vbuf, vh);
-
-vbuf_error:
- /* On error, we push this buffer back into the queue. The
- * videobuf-core doesn't handle error packets very well. Plus
- * we recover nicely internally anyway. */
- if (ret) {
- unsigned long flags;
-
- spin_lock_irqsave(&solo_enc->av_lock, flags);
- list_add(&vb->queue, &solo_enc->vidq_active);
- vb->state = VIDEOBUF_QUEUED;
- spin_unlock_irqrestore(&solo_enc->av_lock, flags);
- } else {
- vb->state = VIDEOBUF_DONE;
- vb->field_count++;
- vb->width = solo_enc->width;
- vb->height = solo_enc->height;
+ ret = solo_fill_jpeg(solo_enc, vb, vh);
- wake_up(&vb->done);
+ if (!ret) {
+ vb->v4l2_buf.sequence++;
+ vb->v4l2_buf.timestamp.tv_sec = vh->sec;
+ vb->v4l2_buf.timestamp.tv_usec = vh->usec;
}
+ vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
return ret;
}
static void solo_enc_handle_one(struct solo_enc_dev *solo_enc,
struct solo_enc_buf *enc_buf)
{
- struct videobuf_buffer *vb;
+ struct solo_vb2_buf *vb;
unsigned long flags;
- mutex_lock(&solo_enc->enable_lock);
-
+ mutex_lock(&solo_enc->lock);
if (solo_enc->type != enc_buf->type)
goto unlock;
- if (list_empty(&solo_enc->vidq_active))
- goto unlock;
-
spin_lock_irqsave(&solo_enc->av_lock, flags);
-
- vb = list_first_entry(&solo_enc->vidq_active,
- struct videobuf_buffer, queue);
-
- list_del(&vb->queue);
- vb->state = VIDEOBUF_ACTIVE;
-
+ if (list_empty(&solo_enc->vidq_active)) {
+ spin_unlock_irqrestore(&solo_enc->av_lock, flags);
+ goto unlock;
+ }
+ vb = list_first_entry(&solo_enc->vidq_active, struct solo_vb2_buf, list);
+ list_del(&vb->list);
spin_unlock_irqrestore(&solo_enc->av_lock, flags);
- solo_enc_fillbuf(solo_enc, vb, enc_buf);
+ solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf);
unlock:
- mutex_unlock(&solo_enc->enable_lock);
+ mutex_unlock(&solo_enc->lock);
}
void solo_enc_v4l2_isr(struct solo_dev *solo_dev)
@@ -723,85 +645,29 @@ static int solo_ring_thread(void *data)
return 0;
}
-static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- *size = FRAME_BUF_SIZE;
-
- if (*count < MIN_VID_BUFFERS)
- *count = MIN_VID_BUFFERS;
-
- return 0;
-}
-
-static int solo_enc_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int solo_enc_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- vb->size = FRAME_BUF_SIZE;
- if (vb->baddr != 0 && vb->bsize < vb->size)
- return -EINVAL;
-
- /* This property only change when queue is idle */
- vb->field = field;
+ sizes[0] = FRAME_BUF_SIZE;
+ *num_planes = 1;
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- int rc = videobuf_iolock(vq, vb, NULL);
- if (rc < 0) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
-
- return rc;
- }
- }
- vb->state = VIDEOBUF_PREPARED;
+ if (*num_buffers < MIN_VID_BUFFERS)
+ *num_buffers = MIN_VID_BUFFERS;
return 0;
}
-static void solo_enc_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void solo_enc_buf_queue(struct vb2_buffer *vb)
{
- struct solo_enc_dev *solo_enc = vq->priv_data;
-
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &solo_enc->vidq_active);
-}
-
-static void solo_enc_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static const struct videobuf_queue_ops solo_enc_video_qops = {
- .buf_setup = solo_enc_buf_setup,
- .buf_prepare = solo_enc_buf_prepare,
- .buf_queue = solo_enc_buf_queue,
- .buf_release = solo_enc_buf_release,
-};
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq);
+ struct solo_vb2_buf *solo_vb =
+ container_of(vb, struct solo_vb2_buf, vb);
-static unsigned int solo_enc_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
- unsigned long req_events = poll_requested_events(wait);
- unsigned res = v4l2_ctrl_poll(file, wait);
-
- if (!(req_events & (POLLIN | POLLRDNORM)))
- return res;
- return videobuf_poll_stream(file, &solo_enc->vidq, wait);
-}
-
-static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
-
- return videobuf_mmap_mapper(&solo_enc->vidq, vma);
+ spin_lock(&solo_enc->av_lock);
+ list_add_tail(&solo_vb->list, &solo_enc->vidq_active);
+ spin_unlock(&solo_enc->av_lock);
}
static int solo_ring_start(struct solo_dev *solo_dev)
@@ -835,50 +701,36 @@ static void solo_ring_stop(struct solo_dev *solo_dev)
solo_irq_off(solo_dev, SOLO_IRQ_ENCODER);
}
-static int solo_enc_open(struct file *file)
+static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count)
{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
- struct solo_dev *solo_dev = solo_enc->solo_dev;
- int ret = v4l2_fh_open(file);
-
- if (ret)
- return ret;
- ret = solo_ring_start(solo_dev);
- if (ret) {
- v4l2_fh_release(file);
- return ret;
- }
- return 0;
-}
-
-static ssize_t solo_enc_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
int ret;
- /* Make sure the encoder is on */
ret = solo_enc_on(solo_enc);
if (ret)
return ret;
-
- return videobuf_read_stream(&solo_enc->vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
+ return solo_ring_start(solo_enc->solo_dev);
}
-static int solo_enc_release(struct file *file)
+static int solo_enc_stop_streaming(struct vb2_queue *q)
{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
- struct solo_dev *solo_dev = solo_enc->solo_dev;
+ struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
solo_enc_off(solo_enc);
- videobuf_stop(&solo_enc->vidq);
- videobuf_mmap_free(&solo_enc->vidq);
- solo_ring_stop(solo_dev);
-
- return v4l2_fh_release(file);
+ INIT_LIST_HEAD(&solo_enc->vidq_active);
+ solo_ring_stop(solo_enc->solo_dev);
+ return 0;
}
+static struct vb2_ops solo_enc_video_qops = {
+ .queue_setup = solo_enc_queue_setup,
+ .buf_queue = solo_enc_buf_queue,
+ .start_streaming = solo_enc_start_streaming,
+ .stop_streaming = solo_enc_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
static int solo_enc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -1007,23 +859,13 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
- mutex_lock(&solo_enc->enable_lock);
+ if (vb2_is_busy(&solo_enc->vidq))
+ return -EBUSY;
ret = solo_enc_try_fmt_cap(file, priv, f);
if (ret)
return ret;
- /* We cannot change width/height in mid read */
- if (!ret && atomic_read(&solo_enc->readers) > 0) {
- if (pix->width != solo_enc->width ||
- pix->height != solo_enc->height)
- ret = -EBUSY;
- }
- if (ret) {
- mutex_unlock(&solo_enc->enable_lock);
- return ret;
- }
-
if (pix->width == solo_dev->video_hsize)
solo_enc->mode = SOLO_ENC_MODE_D1;
else
@@ -1034,9 +876,6 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
if (pix->priv)
solo_enc->type = SOLO_ENC_TYPE_EXT;
-
- mutex_unlock(&solo_enc->enable_lock);
-
return 0;
}
@@ -1058,80 +897,6 @@ static int solo_enc_get_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int solo_enc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
-
- return videobuf_reqbufs(&solo_enc->vidq, req);
-}
-
-static int solo_enc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
-
- return videobuf_querybuf(&solo_enc->vidq, buf);
-}
-
-static int solo_enc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
-
- return videobuf_qbuf(&solo_enc->vidq, buf);
-}
-
-static int solo_enc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
- struct solo_videobuf *svb;
- int ret;
-
- /* Make sure the encoder is on */
- ret = solo_enc_on(solo_enc);
- if (ret)
- return ret;
-
- ret = videobuf_dqbuf(&solo_enc->vidq, buf, file->f_flags & O_NONBLOCK);
- if (ret)
- return ret;
-
- /* Copy over the flags */
- svb = (struct solo_videobuf *)solo_enc->vidq.bufs[buf->index];
- buf->flags |= svb->flags;
-
- return 0;
-}
-
-static int solo_enc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamon(&solo_enc->vidq);
-}
-
-static int solo_enc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
- int ret;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- ret = videobuf_streamoff(&solo_enc->vidq);
- if (!ret)
- solo_enc_off(solo_enc);
-
- return ret;
-}
-
static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
return 0;
@@ -1220,12 +985,8 @@ static int solo_s_parm(struct file *file, void *priv,
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_captureparm *cp = &sp->parm.capture;
- mutex_lock(&solo_enc->enable_lock);
-
- if (atomic_read(&solo_enc->mpeg_readers) > 0) {
- mutex_unlock(&solo_enc->enable_lock);
+ if (vb2_is_streaming(&solo_enc->vidq))
return -EBUSY;
- }
if ((cp->timeperframe.numerator == 0) ||
(cp->timeperframe.denominator == 0)) {
@@ -1246,9 +1007,6 @@ static int solo_s_parm(struct file *file, void *priv,
cp->readbuffers = 2;
solo_update_mode(solo_enc);
-
- mutex_unlock(&solo_enc->enable_lock);
-
return 0;
}
@@ -1297,10 +1055,8 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
solo_motion_toggle(solo_enc, ctrl->val);
return 0;
case V4L2_CID_OSD_TEXT:
- mutex_lock(&solo_enc->enable_lock);
strcpy(solo_enc->osd_text, ctrl->string);
err = solo_osd_print(solo_enc);
- mutex_unlock(&solo_enc->enable_lock);
return err;
default:
return -EINVAL;
@@ -1311,12 +1067,12 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
static const struct v4l2_file_operations solo_enc_fops = {
.owner = THIS_MODULE,
- .open = solo_enc_open,
- .release = solo_enc_release,
- .read = solo_enc_read,
- .poll = solo_enc_poll,
- .mmap = solo_enc_mmap,
- .ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
@@ -1332,12 +1088,12 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
.vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap,
.vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap,
/* Streaming I/O */
- .vidioc_reqbufs = solo_enc_reqbufs,
- .vidioc_querybuf = solo_enc_querybuf,
- .vidioc_qbuf = solo_enc_qbuf,
- .vidioc_dqbuf = solo_enc_dqbuf,
- .vidioc_streamon = solo_enc_streamon,
- .vidioc_streamoff = solo_enc_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
/* Frame size and interval */
.vidioc_enum_framesizes = solo_enum_framesizes,
.vidioc_enum_frameintervals = solo_enum_frameintervals,
@@ -1434,29 +1190,36 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
solo_enc->solo_dev = solo_dev;
solo_enc->ch = ch;
+ mutex_init(&solo_enc->lock);
spin_lock_init(&solo_enc->av_lock);
INIT_LIST_HEAD(&solo_enc->vidq_active);
solo_enc->fmt = V4L2_PIX_FMT_MPEG;
solo_enc->type = SOLO_ENC_TYPE_STD;
- atomic_set(&solo_enc->readers, 0);
-
solo_enc->qp = SOLO_DEFAULT_QP;
solo_enc->gop = solo_dev->fps;
solo_enc->interval = 1;
solo_enc->mode = SOLO_ENC_MODE_CIF;
solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;
+ solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ solo_enc->vidq.ops = &solo_enc_video_qops;
+ solo_enc->vidq.mem_ops = &vb2_dma_sg_memops;
+ solo_enc->vidq.drv_priv = solo_enc;
+ solo_enc->vidq.gfp_flags = __GFP_DMA32;
+ solo_enc->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf);
+ solo_enc->vidq.lock = &solo_enc->lock;
+ ret = vb2_queue_init(&solo_enc->vidq);
+ if (ret)
+ goto hdl_free;
spin_lock(&solo_enc->av_lock);
solo_update_mode(solo_enc);
spin_unlock(&solo_enc->av_lock);
- mutex_init(&solo_enc->enable_lock);
spin_lock_init(&solo_enc->motion_lock);
- atomic_set(&solo_enc->readers, 0);
- atomic_set(&solo_enc->mpeg_readers, 0);
-
/* Initialize this per encoder */
solo_enc->jpeg_len = sizeof(jpeg_header);
memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len);
@@ -1469,14 +1232,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
if (solo_enc->desc_items == NULL)
goto hdl_free;
- videobuf_queue_sg_init(&solo_enc->vidq, &solo_enc_video_qops,
- &solo_dev->pdev->dev,
- &solo_enc->av_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct solo_videobuf),
- solo_enc, NULL);
-
solo_enc->vfd = video_device_alloc();
if (!solo_enc->vfd)
goto pci_free;
@@ -1484,6 +1239,8 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
*solo_enc->vfd = solo_enc_template;
solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev;
solo_enc->vfd->ctrl_handler = hdl;
+ solo_enc->vfd->queue = &solo_enc->vidq;
+ solo_enc->vfd->lock = &solo_enc->lock;
set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags);
video_set_drvdata(solo_enc->vfd, solo_enc);
ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 10/19] solo6x10: convert the display node to vb2.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (8 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 09/19] solo6x10: convert encoder nodes to vb2 Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 11/19] solo6x10: fix 'BUG: key ffff88081a2a9b58 not in .data!' Hans Verkuil
` (8 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
As a consequence the ioctl op has been replaced by unlocked_ioctl.
Since we are now using the core lock the locking scheme has been
simplified as well.
The main reason for converting this driver to vb2 was that the locking
scheme in v4l2.c was hopeless. It was easier to just convert the driver
then to try and salvage a threading and videobuf nightmare.
The videobuf2 framework is far, far superior compared to the old videobuf.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/Kconfig | 2 +-
drivers/staging/media/solo6x10/solo6x10.h | 7 +-
drivers/staging/media/solo6x10/v4l2-enc.c | 12 --
drivers/staging/media/solo6x10/v4l2.c | 289 ++++++++++-------------------
4 files changed, 103 insertions(+), 207 deletions(-)
diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig
index f93b4ca..ec32776 100644
--- a/drivers/staging/media/solo6x10/Kconfig
+++ b/drivers/staging/media/solo6x10/Kconfig
@@ -1,8 +1,8 @@
config SOLO6X10
tristate "Softlogic 6x10 MPEG codec cards"
depends on PCI && VIDEO_DEV && SND && I2C
- select VIDEOBUF_DMA_SG
select VIDEOBUF2_DMA_SG
+ select VIDEOBUF2_DMA_CONTIG
select SND_PCM
---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 5b21178..96ac299 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -39,7 +39,6 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/videobuf-core.h>
#include <media/videobuf2-core.h>
#include "registers.h"
@@ -259,8 +258,6 @@ struct solo_dev {
/* Ring thread */
struct task_struct *ring_thread;
wait_queue_head_t ring_thread_wait;
- atomic_t enc_users;
- atomic_t disp_users;
/* VOP_HEADER handling */
void *vh_buf;
@@ -268,8 +265,10 @@ struct solo_dev {
int vh_size;
/* Buffer handling */
- struct videobuf_queue vidq;
+ struct vb2_queue vidq;
+ struct vb2_alloc_ctx *alloc_ctx;
struct task_struct *kthread;
+ struct mutex lock;
spinlock_t slock;
int old_write;
struct list_head vidq_active;
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index eb82299..dc1f8b3 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -41,11 +41,6 @@
#define MP4_QS 16
#define DMA_ALIGN 4096
-struct solo_videobuf {
- struct videobuf_buffer vb;
- unsigned int flags;
-};
-
/* 6010 M4V */
static unsigned char vop_6010_ntsc_d1[] = {
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
@@ -672,9 +667,6 @@ static void solo_enc_buf_queue(struct vb2_buffer *vb)
static int solo_ring_start(struct solo_dev *solo_dev)
{
- if (atomic_inc_return(&solo_dev->enc_users) > 1)
- return 0;
-
solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev,
SOLO6X10_NAME "_ring");
if (IS_ERR(solo_dev->ring_thread)) {
@@ -690,9 +682,6 @@ static int solo_ring_start(struct solo_dev *solo_dev)
static void solo_ring_stop(struct solo_dev *solo_dev)
{
- if (atomic_dec_return(&solo_dev->enc_users) > 0)
- return;
-
if (solo_dev->ring_thread) {
kthread_stop(solo_dev->ring_thread);
solo_dev->ring_thread = NULL;
@@ -1279,7 +1268,6 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
{
int i;
- atomic_set(&solo_dev->enc_users, 0);
init_waitqueue_head(&solo_dev->ring_thread_wait);
solo_dev->vh_size = sizeof(struct vop_header);
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 64595ef..6931950 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -30,7 +30,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
#include "solo6x10.h"
#include "tw28.h"
@@ -192,19 +192,19 @@ static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
}
static void solo_fillbuf(struct solo_dev *solo_dev,
- struct videobuf_buffer *vb)
+ struct vb2_buffer *vb)
{
dma_addr_t vbuf;
unsigned int fdma_addr;
int error = -1;
int i;
- vbuf = videobuf_to_dma_contig(vb);
+ vbuf = vb2_dma_contig_plane_dma_addr(vb, 0);
if (!vbuf)
goto finish_buf;
if (erase_off(solo_dev)) {
- void *p = videobuf_queue_to_vaddr(&solo_dev->vidq, vb);
+ void *p = vb2_plane_vaddr(vb, 0);
int image_size = solo_image_size(solo_dev);
for (i = 0; i < image_size; i += 2) {
((u8 *)p)[i] = 0x80;
@@ -221,19 +221,19 @@ static void solo_fillbuf(struct solo_dev *solo_dev,
}
finish_buf:
- if (error) {
- vb->state = VIDEOBUF_ERROR;
- } else {
- vb->state = VIDEOBUF_DONE;
- vb->field_count++;
+ if (!error) {
+ vb2_set_plane_payload(vb, 0,
+ solo_vlines(solo_dev) * solo_bytesperline(solo_dev));
+ vb->v4l2_buf.sequence++;
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
}
- wake_up(&vb->done);
+ vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
}
static void solo_thread_try(struct solo_dev *solo_dev)
{
- struct videobuf_buffer *vb;
+ struct solo_vb2_buf *vb;
/* Only "break" from this loop if slock is held, otherwise
* just return. */
@@ -250,19 +250,15 @@ static void solo_thread_try(struct solo_dev *solo_dev)
if (list_empty(&solo_dev->vidq_active))
break;
- vb = list_first_entry(&solo_dev->vidq_active, struct videobuf_buffer,
- queue);
-
- if (!waitqueue_active(&vb->done))
- break;
+ vb = list_first_entry(&solo_dev->vidq_active, struct solo_vb2_buf,
+ list);
solo_dev->old_write = cur_write;
- list_del(&vb->queue);
- vb->state = VIDEOBUF_ACTIVE;
+ list_del(&vb->list);
spin_unlock(&solo_dev->slock);
- solo_fillbuf(solo_dev, vb);
+ solo_fillbuf(solo_dev, &vb->vb);
}
assert_spin_locked(&solo_dev->slock);
@@ -294,15 +290,14 @@ static int solo_start_thread(struct solo_dev *solo_dev)
{
int ret = 0;
- if (atomic_inc_return(&solo_dev->disp_users) == 1)
- solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
-
solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp");
if (IS_ERR(solo_dev->kthread)) {
ret = PTR_ERR(solo_dev->kthread);
solo_dev->kthread = NULL;
+ return ret;
}
+ solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
return ret;
}
@@ -312,116 +307,65 @@ static void solo_stop_thread(struct solo_dev *solo_dev)
if (!solo_dev->kthread)
return;
+ solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
kthread_stop(solo_dev->kthread);
solo_dev->kthread = NULL;
-
- if (atomic_dec_return(&solo_dev->disp_users) == 0)
- solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
}
-static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct solo_dev *solo_dev = vq->priv_data;
+ struct solo_dev *solo_dev = vb2_get_drv_priv(q);
- *size = solo_image_size(solo_dev);
+ sizes[0] = solo_image_size(solo_dev);
+ alloc_ctxs[0] = solo_dev->alloc_ctx;
+ *num_planes = 1;
- if (*count < MIN_VID_BUFFERS)
- *count = MIN_VID_BUFFERS;
+ if (*num_buffers < MIN_VID_BUFFERS)
+ *num_buffers = MIN_VID_BUFFERS;
return 0;
}
-static int solo_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
+static int solo_start_streaming(struct vb2_queue *q, unsigned int count)
{
- struct solo_dev *solo_dev = vq->priv_data;
+ struct solo_dev *solo_dev = vb2_get_drv_priv(q);
- vb->size = solo_image_size(solo_dev);
- if (vb->baddr != 0 && vb->bsize < vb->size)
- return -EINVAL;
+ return solo_start_thread(solo_dev);
+}
- /* XXX: These properties only change when queue is idle */
- vb->width = solo_dev->video_hsize;
- vb->height = solo_vlines(solo_dev);
- vb->bytesperline = solo_bytesperline(solo_dev);
- vb->field = field;
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- int rc = videobuf_iolock(vq, vb, NULL);
- if (rc < 0) {
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
- return rc;
- }
- }
- vb->state = VIDEOBUF_PREPARED;
+static int solo_stop_streaming(struct vb2_queue *q)
+{
+ struct solo_dev *solo_dev = vb2_get_drv_priv(q);
+ solo_stop_thread(solo_dev);
+ INIT_LIST_HEAD(&solo_dev->vidq_active);
return 0;
}
-static void solo_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void solo_buf_queue(struct vb2_buffer *vb)
{
- struct solo_dev *solo_dev = vq->priv_data;
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct solo_dev *solo_dev = vb2_get_drv_priv(vq);
+ struct solo_vb2_buf *solo_vb =
+ container_of(vb, struct solo_vb2_buf, vb);
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &solo_dev->vidq_active);
+ spin_lock(&solo_dev->slock);
+ list_add_tail(&solo_vb->list, &solo_dev->vidq_active);
+ spin_unlock(&solo_dev->slock);
wake_up_interruptible(&solo_dev->disp_thread_wait);
}
-static void solo_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static const struct videobuf_queue_ops solo_video_qops = {
- .buf_setup = solo_buf_setup,
- .buf_prepare = solo_buf_prepare,
+static const struct vb2_ops solo_video_qops = {
+ .queue_setup = solo_queue_setup,
.buf_queue = solo_buf_queue,
- .buf_release = solo_buf_release,
+ .start_streaming = solo_start_streaming,
+ .stop_streaming = solo_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
-static unsigned int solo_v4l2_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
- unsigned long req_events = poll_requested_events(wait);
- unsigned res = v4l2_ctrl_poll(file, wait);
-
- if (!(req_events & (POLLIN | POLLRDNORM)))
- return res;
- return res | videobuf_poll_stream(file, &solo_dev->vidq, wait);
-}
-
-static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- return videobuf_mmap_mapper(&solo_dev->vidq, vma);
-}
-
-static ssize_t solo_v4l2_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- return videobuf_read_stream(&solo_dev->vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static int solo_v4l2_release(struct file *file)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- solo_stop_thread(solo_dev);
- videobuf_stop(&solo_dev->vidq);
- videobuf_mmap_free(&solo_dev->vidq);
- return v4l2_fh_release(file);
-}
-
static int solo_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -550,7 +494,7 @@ static int solo_set_fmt_cap(struct file *file, void *priv,
{
struct solo_dev *solo_dev = video_drvdata(file);
- if (videobuf_queue_is_busy(&solo_dev->vidq))
+ if (vb2_is_busy(&solo_dev->vidq))
return -EBUSY;
/* For right now, if it doesn't match our running config,
@@ -576,61 +520,6 @@ static int solo_get_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int solo_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- return videobuf_reqbufs(&solo_dev->vidq, req);
-}
-
-static int solo_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- return videobuf_querybuf(&solo_dev->vidq, buf);
-}
-
-static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- return videobuf_qbuf(&solo_dev->vidq, buf);
-}
-
-static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- return videobuf_dqbuf(&solo_dev->vidq, buf, file->f_flags & O_NONBLOCK);
-}
-
-static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
- int ret;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- ret = solo_start_thread(solo_dev);
- if (ret)
- return ret;
-
- return videobuf_streamon(&solo_dev->vidq);
-}
-
-static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct solo_dev *solo_dev = video_drvdata(file);
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamoff(&solo_dev->vidq);
-}
-
static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
return 0;
@@ -668,11 +557,11 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
static const struct v4l2_file_operations solo_v4l2_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
- .release = solo_v4l2_release,
- .read = solo_v4l2_read,
- .poll = solo_v4l2_poll,
- .mmap = solo_v4l2_mmap,
- .ioctl = video_ioctl2,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
@@ -688,12 +577,12 @@ static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
.vidioc_s_fmt_vid_cap = solo_set_fmt_cap,
.vidioc_g_fmt_vid_cap = solo_get_fmt_cap,
/* Streaming I/O */
- .vidioc_reqbufs = solo_reqbufs,
- .vidioc_querybuf = solo_querybuf,
- .vidioc_qbuf = solo_qbuf,
- .vidioc_dqbuf = solo_dqbuf,
- .vidioc_streamon = solo_streamon,
- .vidioc_streamoff = solo_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
/* Logging and events */
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
@@ -729,8 +618,10 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
int ret;
int i;
- atomic_set(&solo_dev->disp_users, 0);
init_waitqueue_head(&solo_dev->disp_thread_wait);
+ spin_lock_init(&solo_dev->slock);
+ mutex_init(&solo_dev->lock);
+ INIT_LIST_HEAD(&solo_dev->vidq_active);
solo_dev->vfd = video_device_alloc();
if (!solo_dev->vfd)
@@ -738,24 +629,37 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
*solo_dev->vfd = solo_v4l2_template;
solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev;
+ solo_dev->vfd->queue = &solo_dev->vidq;
+ solo_dev->vfd->lock = &solo_dev->lock;
v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1);
v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL);
- if (solo_dev->disp_hdl.error)
- return solo_dev->disp_hdl.error;
+ if (solo_dev->disp_hdl.error) {
+ ret = solo_dev->disp_hdl.error;
+ goto fail;
+ }
solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl;
set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags);
video_set_drvdata(solo_dev->vfd, solo_dev);
- spin_lock_init(&solo_dev->slock);
- INIT_LIST_HEAD(&solo_dev->vidq_active);
-
- videobuf_queue_dma_contig_init(&solo_dev->vidq, &solo_video_qops,
- &solo_dev->pdev->dev, &solo_dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct videobuf_buffer),
- solo_dev, NULL);
+ solo_dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ solo_dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ solo_dev->vidq.ops = &solo_video_qops;
+ solo_dev->vidq.mem_ops = &vb2_dma_contig_memops;
+ solo_dev->vidq.drv_priv = solo_dev;
+ solo_dev->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ solo_dev->vidq.gfp_flags = __GFP_DMA32;
+ solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf);
+ solo_dev->vidq.lock = &solo_dev->lock;
+ ret = vb2_queue_init(&solo_dev->vidq);
+ if (ret < 0)
+ goto fail;
+
+ solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev);
+ if (IS_ERR(solo_dev->alloc_ctx)) {
+ dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context");
+ return PTR_ERR(solo_dev->alloc_ctx);
+ }
/* Cycle all the channels and clear */
for (i = 0; i < solo_dev->nr_chans; i++) {
@@ -770,11 +674,8 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
/* Do nothing */;
ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
- if (ret < 0) {
- video_device_release(solo_dev->vfd);
- solo_dev->vfd = NULL;
- return ret;
- }
+ if (ret < 0)
+ goto fail;
snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
SOLO6X10_NAME, solo_dev->vfd->num);
@@ -784,6 +685,13 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
solo_dev->nr_chans, solo_dev->nr_ext);
return 0;
+
+fail:
+ video_device_release(solo_dev->vfd);
+ vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx);
+ v4l2_ctrl_handler_free(&solo_dev->disp_hdl);
+ solo_dev->vfd = NULL;
+ return ret;
}
void solo_v4l2_exit(struct solo_dev *solo_dev)
@@ -792,6 +700,7 @@ void solo_v4l2_exit(struct solo_dev *solo_dev)
return;
video_unregister_device(solo_dev->vfd);
+ vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx);
v4l2_ctrl_handler_free(&solo_dev->disp_hdl);
solo_dev->vfd = NULL;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 11/19] solo6x10: fix 'BUG: key ffff88081a2a9b58 not in .data!'
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (9 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 10/19] solo6x10: convert the display node " Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 12/19] solo6x10: add call to pci_dma_mapping_error Hans Verkuil
` (7 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Caused by a missing sysfs_attr_init().
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/core.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index 271759f..b7e5d5e 100644
--- a/drivers/staging/media/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
@@ -480,6 +480,7 @@ static int solo_sysfs_init(struct solo_dev *solo_dev)
}
}
+ sysfs_attr_init(&sdram_attr->attr);
sdram_attr->attr.name = "sdram";
sdram_attr->attr.mode = 0440;
sdram_attr->read = sdram_show;
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 12/19] solo6x10: add call to pci_dma_mapping_error.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (10 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 11/19] solo6x10: fix 'BUG: key ffff88081a2a9b58 not in .data!' Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 13/19] solo6x10: drop video_type and add proper s_std support Hans Verkuil
` (6 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Check the result of the dma mapping.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/p2m.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c
index 3ed4d58..2292061 100644
--- a/drivers/staging/media/solo6x10/p2m.c
+++ b/drivers/staging/media/solo6x10/p2m.c
@@ -51,6 +51,8 @@ int solo_p2m_dma(struct solo_dev *solo_dev, int wr,
dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(solo_dev->pdev, dma_addr))
+ return -ENOMEM;
ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size,
repeat, ext_size);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 13/19] solo6x10: drop video_type and add proper s_std support.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (11 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 12/19] solo6x10: add call to pci_dma_mapping_error Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 14/19] solo6x10: also stop DMA if the SOLO_PCI_ERR_P2M_DESC is raised Hans Verkuil
` (5 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/core.c | 19 ------------
drivers/staging/media/solo6x10/disp.c | 8 +----
drivers/staging/media/solo6x10/solo6x10.h | 6 +++-
drivers/staging/media/solo6x10/tw28.c | 2 ++
drivers/staging/media/solo6x10/v4l2-enc.c | 37 ++++++++++++++---------
drivers/staging/media/solo6x10/v4l2.c | 47 +++++++++++++++++++++++------
6 files changed, 67 insertions(+), 52 deletions(-)
diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index b7e5d5e..00fb35a 100644
--- a/drivers/staging/media/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
@@ -231,24 +231,6 @@ static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr,
return count;
}
-static ssize_t video_type_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- return -EPERM;
-}
-
-static ssize_t video_type_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct solo_dev *solo_dev =
- container_of(dev, struct solo_dev, dev);
-
- return sprintf(buf, "%s", solo_dev->video_type ==
- SOLO_VO_FMT_TYPE_NTSC ? "NTSC" : "PAL");
-}
-
static ssize_t p2m_timeouts_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -435,7 +417,6 @@ static ssize_t sdram_show(struct file *file, struct kobject *kobj,
static const struct device_attribute solo_dev_attrs[] = {
__ATTR(eeprom, 0640, eeprom_show, eeprom_store),
- __ATTR(video_type, 0644, video_type_show, video_type_store),
__ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store),
__ATTR_RO(p2m_timeouts),
__ATTR_RO(sdram_size),
diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c
index ddd85e7..3dcaad9 100644
--- a/drivers/staging/media/solo6x10/disp.c
+++ b/drivers/staging/media/solo6x10/disp.c
@@ -39,10 +39,6 @@
#define SOLO_MOT_FLAG_SIZE 1024
#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16)
-static unsigned video_type;
-module_param(video_type, uint, 0644);
-MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)");
-
static void solo_vin_config(struct solo_dev *solo_dev)
{
solo_dev->vin_hstart = 8;
@@ -273,12 +269,10 @@ int solo_disp_init(struct solo_dev *solo_dev)
int i;
solo_dev->video_hsize = 704;
- if (video_type == 0) {
- solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC;
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
solo_dev->video_vsize = 240;
solo_dev->fps = 30;
} else {
- solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL;
solo_dev->video_vsize = 288;
solo_dev->fps = 25;
}
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 96ac299..56789b4 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -320,7 +320,7 @@ static inline void solo_irq_off(struct solo_dev *dev, u32 mask)
solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask);
}
-/* Init/exit routeines for subsystems */
+/* Init/exit routines for subsystems */
int solo_disp_init(struct solo_dev *solo_dev);
void solo_disp_exit(struct solo_dev *solo_dev);
@@ -373,6 +373,10 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev,
struct solo_p2m_desc *desc, dma_addr_t desc_dma,
int desc_cnt);
+/* Global s_std ioctl */
+int solo_set_video_type(struct solo_dev *solo_dev, bool type);
+void solo_update_mode(struct solo_enc_dev *solo_enc);
+
/* Set the threshold for motion detection */
int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val);
int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, u16 val,
diff --git a/drivers/staging/media/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c
index 69baf82..6c77af8 100644
--- a/drivers/staging/media/solo6x10/tw28.c
+++ b/drivers/staging/media/solo6x10/tw28.c
@@ -576,6 +576,8 @@ int solo_tw28_init(struct solo_dev *solo_dev)
int i;
u8 value;
+ solo_dev->tw28_cnt = 0;
+
/* Detect techwell chip type(s) */
for (i = 0; i < solo_dev->nr_chans / 4; i++) {
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index dc1f8b3..93f0dc7 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -176,7 +176,7 @@ static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
spin_unlock_irqrestore(&solo_enc->motion_lock, flags);
}
-static void solo_update_mode(struct solo_enc_dev *solo_enc)
+void solo_update_mode(struct solo_enc_dev *solo_enc)
{
struct solo_dev *solo_dev = solo_enc->solo_dev;
int vop_len;
@@ -749,11 +749,7 @@ static int solo_enc_enum_input(struct file *file, void *priv,
snprintf(input->name, sizeof(input->name), "Encoder %d",
solo_enc->ch + 1);
input->type = V4L2_INPUT_TYPE_CAMERA;
-
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
- input->std = V4L2_STD_NTSC_M;
- else
- input->std = V4L2_STD_PAL_B;
+ input->std = solo_enc->vfd->tvnorms;
if (!tw28_get_video_status(solo_dev, solo_enc->ch))
input->status = V4L2_IN_ST_NO_SIGNAL;
@@ -886,11 +882,25 @@ static int solo_enc_get_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+static int solo_enc_g_std(struct file *file, void *priv, v4l2_std_id *i)
{
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
+
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
+ *i = V4L2_STD_NTSC_M;
+ else
+ *i = V4L2_STD_PAL;
return 0;
}
+static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+
+ return solo_set_video_type(solo_enc->solo_dev, *std & V4L2_STD_PAL);
+}
+
static int solo_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
@@ -938,14 +948,14 @@ static int solo_enum_frameintervals(struct file *file, void *priv,
fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ fintv->stepwise.min.numerator = 1;
fintv->stepwise.min.denominator = solo_dev->fps;
- fintv->stepwise.min.numerator = 15;
+ fintv->stepwise.max.numerator = 15;
fintv->stepwise.max.denominator = solo_dev->fps;
- fintv->stepwise.max.numerator = 1;
fintv->stepwise.step.numerator = 1;
- fintv->stepwise.step.denominator = 1;
+ fintv->stepwise.step.denominator = solo_dev->fps;
return 0;
}
@@ -1067,6 +1077,7 @@ static const struct v4l2_file_operations solo_enc_fops = {
static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
.vidioc_querycap = solo_enc_querycap,
.vidioc_s_std = solo_enc_s_std,
+ .vidioc_g_std = solo_enc_g_std,
/* Input callbacks */
.vidioc_enum_input = solo_enc_enum_input,
.vidioc_s_input = solo_enc_set_input,
@@ -1101,9 +1112,7 @@ static const struct video_device solo_enc_template = {
.ioctl_ops = &solo_enc_ioctl_ops,
.minor = -1,
.release = video_device_release,
-
- .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
- .current_norm = V4L2_STD_NTSC_M,
+ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
};
static const struct v4l2_ctrl_ops solo_ctrl_ops = {
@@ -1203,9 +1212,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
ret = vb2_queue_init(&solo_enc->vidq);
if (ret)
goto hdl_free;
- spin_lock(&solo_enc->av_lock);
solo_update_mode(solo_enc);
- spin_unlock(&solo_enc->av_lock);
spin_lock_init(&solo_enc->motion_lock);
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 6931950..94db4c2 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -426,12 +426,7 @@ static int solo_enum_input(struct file *file, void *priv,
}
input->type = V4L2_INPUT_TYPE_CAMERA;
-
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
- input->std = V4L2_STD_NTSC_M;
- else
- input->std = V4L2_STD_PAL_B;
-
+ input->std = solo_dev->vfd->tvnorms;
return 0;
}
@@ -520,11 +515,44 @@ static int solo_get_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
+static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i)
{
+ struct solo_dev *solo_dev = video_drvdata(file);
+
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
+ *i = V4L2_STD_NTSC_M;
+ else
+ *i = V4L2_STD_PAL;
return 0;
}
+int solo_set_video_type(struct solo_dev *solo_dev, bool type)
+{
+ int i;
+
+ /* Make sure all video nodes are idle */
+ if (vb2_is_busy(&solo_dev->vidq))
+ return -EBUSY;
+ for (i = 0; i < solo_dev->nr_chans; i++)
+ if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq))
+ return -EBUSY;
+ solo_dev->video_type = type;
+ /* Reconfigure for the new standard */
+ solo_disp_init(solo_dev);
+ solo_enc_init(solo_dev);
+ solo_tw28_init(solo_dev);
+ for (i = 0; i < solo_dev->nr_chans; i++)
+ solo_update_mode(solo_dev->v4l2_enc[i]);
+ return solo_v4l2_set_ch(solo_dev, solo_dev->cur_disp_ch);
+}
+
+static int solo_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct solo_dev *solo_dev = video_drvdata(file);
+
+ return solo_set_video_type(solo_dev, *std & V4L2_STD_PAL);
+}
+
static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct solo_dev *solo_dev =
@@ -567,6 +595,7 @@ static const struct v4l2_file_operations solo_v4l2_fops = {
static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
.vidioc_querycap = solo_querycap,
.vidioc_s_std = solo_s_std,
+ .vidioc_g_std = solo_g_std,
/* Input callbacks */
.vidioc_enum_input = solo_enum_input,
.vidioc_s_input = solo_set_input,
@@ -595,9 +624,7 @@ static struct video_device solo_v4l2_template = {
.ioctl_ops = &solo_v4l2_ioctl_ops,
.minor = -1,
.release = video_device_release,
-
- .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
- .current_norm = V4L2_STD_NTSC_M,
+ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
};
static const struct v4l2_ctrl_ops solo_ctrl_ops = {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 14/19] solo6x10: also stop DMA if the SOLO_PCI_ERR_P2M_DESC is raised.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (12 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 13/19] solo6x10: drop video_type and add proper s_std support Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 15/19] solo6x10: small big-endian fix Hans Verkuil
` (4 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Otherwise the computer will hang.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/p2m.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c
index 2292061..013f177 100644
--- a/drivers/staging/media/solo6x10/p2m.c
+++ b/drivers/staging/media/solo6x10/p2m.c
@@ -197,7 +197,7 @@ void solo_p2m_error_isr(struct solo_dev *solo_dev)
struct solo_p2m_dev *p2m_dev;
int i;
- if (!(err & SOLO_PCI_ERR_P2M))
+ if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC)))
return;
for (i = 0; i < SOLO_NR_P2M; i++) {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 15/19] solo6x10: small big-endian fix.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (13 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 14/19] solo6x10: also stop DMA if the SOLO_PCI_ERR_P2M_DESC is raised Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 16/19] solo6x10: use V4L2_PIX_FMT_MPEG4, not _FMT_MPEG Hans Verkuil
` (3 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/disp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c
index 3dcaad9..7007006 100644
--- a/drivers/staging/media/solo6x10/disp.c
+++ b/drivers/staging/media/solo6x10/disp.c
@@ -181,7 +181,7 @@ static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,
int ret = 0;
for (i = 0; i < sizeof(buf) >> 1; i++)
- buf[i] = val;
+ buf[i] = cpu_to_le16(val);
for (i = 0; i < reg_size; i += sizeof(buf))
ret |= solo_p2m_dma(solo_dev, 1, buf,
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 16/19] solo6x10: use V4L2_PIX_FMT_MPEG4, not _FMT_MPEG
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (14 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 15/19] solo6x10: small big-endian fix Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 17/19] solo6x10: fix sequence handling Hans Verkuil
` (2 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
FMT_MPEG is for multiplexed streams, not elementary streams. The same is
true for the V4L2_CID_MPEG_VIDEO_ENCODING control.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/v4l2-enc.c | 18 ++++++------------
1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 93f0dc7..ca87fb3 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -516,7 +516,7 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
}
- if (solo_enc->fmt == V4L2_PIX_FMT_MPEG)
+ if (solo_enc->fmt == V4L2_PIX_FMT_MPEG4)
ret = solo_fill_mpeg(solo_enc, vb, vh);
else
ret = solo_fill_jpeg(solo_enc, vb, vh);
@@ -779,7 +779,7 @@ static int solo_enc_enum_fmt_cap(struct file *file, void *priv,
{
switch (f->index) {
case 0:
- f->pixelformat = V4L2_PIX_FMT_MPEG;
+ f->pixelformat = V4L2_PIX_FMT_MPEG4;
strcpy(f->description, "MPEG-4 AVC");
break;
case 1:
@@ -802,7 +802,7 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_pix_format *pix = &f->fmt.pix;
- if (pix->pixelformat != V4L2_PIX_FMT_MPEG &&
+ if (pix->pixelformat != V4L2_PIX_FMT_MPEG4 &&
pix->pixelformat != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
@@ -907,7 +907,7 @@ static int solo_enum_framesizes(struct file *file, void *priv,
struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
- if (fsize->pixel_format != V4L2_PIX_FMT_MPEG &&
+ if (fsize->pixel_format != V4L2_PIX_FMT_MPEG4 &&
fsize->pixel_format != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
@@ -935,7 +935,7 @@ static int solo_enum_frameintervals(struct file *file, void *priv,
struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
- if (fintv->pixel_format != V4L2_PIX_FMT_MPEG &&
+ if (fintv->pixel_format != V4L2_PIX_FMT_MPEG4 &&
fintv->pixel_format != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
if (fintv->index)
@@ -1024,8 +1024,6 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_SHARPNESS:
return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch,
ctrl->val);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return 0;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
solo_enc->gop = ctrl->val;
return 0;
@@ -1172,10 +1170,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
if (tw28_has_sharpness(solo_dev, ch))
v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
V4L2_CID_SHARPNESS, 0, 15, 1, 0);
- v4l2_ctrl_new_std_menu(hdl, &solo_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 3,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps);
v4l2_ctrl_new_custom(hdl, &solo_motion_threshold_ctrl, NULL);
@@ -1191,7 +1185,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
mutex_init(&solo_enc->lock);
spin_lock_init(&solo_enc->av_lock);
INIT_LIST_HEAD(&solo_enc->vidq_active);
- solo_enc->fmt = V4L2_PIX_FMT_MPEG;
+ solo_enc->fmt = V4L2_PIX_FMT_MPEG4;
solo_enc->type = SOLO_ENC_TYPE_STD;
solo_enc->qp = SOLO_DEFAULT_QP;
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 17/19] solo6x10: fix sequence handling.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (15 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 16/19] solo6x10: use V4L2_PIX_FMT_MPEG4, not _FMT_MPEG Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 18/19] solo6x10: disable the 'priv' abuse Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 19/19] solo6x10: clean up motion detection handling Hans Verkuil
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/solo6x10.h | 2 ++
drivers/staging/media/solo6x10/v4l2-enc.c | 3 ++-
drivers/staging/media/solo6x10/v4l2.c | 3 ++-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 56789b4..3313257 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -173,6 +173,7 @@ struct solo_enc_dev {
u32 fmt;
enum solo_enc_types type;
+ u32 sequence;
struct vb2_queue vidq;
struct list_head vidq_active;
int desc_count;
@@ -267,6 +268,7 @@ struct solo_dev {
/* Buffer handling */
struct vb2_queue vidq;
struct vb2_alloc_ctx *alloc_ctx;
+ u32 sequence;
struct task_struct *kthread;
struct mutex lock;
spinlock_t slock;
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index ca87fb3..e53c985 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -269,6 +269,7 @@ static int solo_enc_on(struct solo_enc_dev *solo_enc)
/* Make sure to do a bandwidth check */
if (solo_enc->bw_weight > solo_dev->enc_bw_remain)
return -EBUSY;
+ solo_enc->sequence = 0;
solo_dev->enc_bw_remain -= solo_enc->bw_weight;
if (solo_enc->type == SOLO_ENC_TYPE_EXT)
@@ -522,7 +523,7 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
ret = solo_fill_jpeg(solo_enc, vb, vh);
if (!ret) {
- vb->v4l2_buf.sequence++;
+ vb->v4l2_buf.sequence = solo_enc->sequence++;
vb->v4l2_buf.timestamp.tv_sec = vh->sec;
vb->v4l2_buf.timestamp.tv_usec = vh->usec;
}
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 94db4c2..303d951 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -224,7 +224,7 @@ finish_buf:
if (!error) {
vb2_set_plane_payload(vb, 0,
solo_vlines(solo_dev) * solo_bytesperline(solo_dev));
- vb->v4l2_buf.sequence++;
+ vb->v4l2_buf.sequence = solo_dev->sequence++;
v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
}
@@ -332,6 +332,7 @@ static int solo_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct solo_dev *solo_dev = vb2_get_drv_priv(q);
+ solo_dev->sequence = 0;
return solo_start_thread(solo_dev);
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 18/19] solo6x10: disable the 'priv' abuse.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (16 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 17/19] solo6x10: fix sequence handling Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 19/19] solo6x10: clean up motion detection handling Hans Verkuil
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/v4l2-enc.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index e53c985..642ebbf 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -860,8 +860,16 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
/* This does not change the encoder at all */
solo_enc->fmt = pix->pixelformat;
+ /*
+ * More information is needed about these 'extended' types. As far
+ * as I can tell these are basically additional video streams with
+ * different MPEG encoding attributes that can run in parallel with
+ * the main stream. If so, then this should be implemented as a
+ * second video node. Abusing priv like this is certainly not the
+ * right approach.
if (pix->priv)
solo_enc->type = SOLO_ENC_TYPE_EXT;
+ */
return 0;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [REVIEW PATCH 19/19] solo6x10: clean up motion detection handling.
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
` (17 preceding siblings ...)
2013-03-18 12:32 ` [REVIEW PATCH 18/19] solo6x10: disable the 'priv' abuse Hans Verkuil
@ 2013-03-18 12:32 ` Hans Verkuil
18 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-18 12:32 UTC (permalink / raw)
To: linux-media; +Cc: Ismael Luceno, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
An earlier patch temporarily disabled regional motion detection. This patch
adds it back: the 'Motion Detection Enable' control is now a 'Motion Detection Mode'.
And to set/get the regional thresholds two new ioctls were added to get/set those
thresholds.
The BUF_FLAG constants were also updated to prevent clashing with existing buffer
flags.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/staging/media/solo6x10/disp.c | 37 +++++-------
drivers/staging/media/solo6x10/solo6x10.h | 27 +++++++--
drivers/staging/media/solo6x10/v4l2-enc.c | 87 ++++++++++++++++++++---------
3 files changed, 97 insertions(+), 54 deletions(-)
diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c
index 7007006..78070c8 100644
--- a/drivers/staging/media/solo6x10/disp.c
+++ b/drivers/staging/media/solo6x10/disp.c
@@ -201,31 +201,22 @@ int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val)
val, SOLO_MOT_THRESH_SIZE);
}
-int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, u16 val,
- u16 block)
+int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch,
+ const struct solo_motion_thresholds *thresholds)
{
- u16 buf[64];
- u32 addr;
- int re;
-
- addr = SOLO_MOTION_EXT_ADDR(solo_dev) +
- SOLO_MOT_FLAG_AREA +
- (SOLO_MOT_THRESH_SIZE * 2 * ch) +
- (block * 2);
-
- /* Read and write only on a 128-byte boundary; 4-byte writes with
- solo_p2m_dma silently failed. Bluecherry bug #908. */
- re = solo_p2m_dma(solo_dev, 0, &buf, addr & ~0x7f, sizeof(buf), 0, 0);
- if (re)
- return re;
-
- buf[(addr & 0x7f) / 2] = val;
-
- re = solo_p2m_dma(solo_dev, 1, &buf, addr & ~0x7f, sizeof(buf), 0, 0);
- if (re)
- return re;
+ u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2;
+ u16 buf[SOLO_MOTION_SZ];
+ int x, y;
+ int ret = 0;
- return 0;
+ for (y = 0; y < SOLO_MOTION_SZ; y++) {
+ for (x = 0; x < SOLO_MOTION_SZ; x++)
+ buf[x] = cpu_to_le16(thresholds->thresholds[y][x]);
+ ret |= solo_p2m_dma(solo_dev, 1, buf,
+ SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * sizeof(buf),
+ sizeof(buf), 0, 0);
+ }
+ return ret;
}
/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k
diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index 3313257..1d08dbc 100644
--- a/drivers/staging/media/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
@@ -98,16 +98,30 @@
#define SOLO_DEFAULT_QP 3
#ifndef V4L2_BUF_FLAG_MOTION_ON
-#define V4L2_BUF_FLAG_MOTION_ON 0x0400
-#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800
+#define V4L2_BUF_FLAG_MOTION_ON 0x10000
+#define V4L2_BUF_FLAG_MOTION_DETECTED 0x20000
#endif
#define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
-#define V4L2_CID_MOTION_ENABLE (SOLO_CID_CUSTOM_BASE+0)
+#define V4L2_CID_MOTION_MODE (SOLO_CID_CUSTOM_BASE+0)
#define V4L2_CID_MOTION_THRESHOLD (SOLO_CID_CUSTOM_BASE+1)
#define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2)
#define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3)
+/*
+ * Motion thresholds are in a table of 64x64 samples, with
+ * each sample representing 16x16 pixels of the source. In
+ * effect, 44x30 samples are used for NTSC, and 44x36 for PAL.
+ * The 5th sample on the 10th row is (10*64)+5 = 645.
+ */
+#define SOLO_MOTION_SZ (64)
+struct solo_motion_thresholds {
+ __u16 thresholds[SOLO_MOTION_SZ][SOLO_MOTION_SZ];
+};
+
+#define SOLO_IOC_G_MOTION_THRESHOLDS _IOR('V', BASE_VIDIOC_PRIVATE+0, struct solo_motion_thresholds)
+#define SOLO_IOC_S_MOTION_THRESHOLDS _IOW('V', BASE_VIDIOC_PRIVATE+1, struct solo_motion_thresholds)
+
enum SOLO_I2C_STATE {
IIC_STATE_IDLE,
IIC_STATE_START,
@@ -157,6 +171,9 @@ struct solo_enc_dev {
u8 mode, gop, qp, interlaced, interval;
u8 bw_weight;
u16 motion_thresh;
+ struct solo_motion_thresholds motion_thresholds;
+ bool motion_global;
+ bool motion_enabled;
u16 width;
u16 height;
@@ -381,8 +398,8 @@ void solo_update_mode(struct solo_enc_dev *solo_enc);
/* Set the threshold for motion detection */
int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val);
-int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, u16 val,
- u16 block);
+int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch,
+ const struct solo_motion_thresholds *thresholds);
#define SOLO_DEF_MOT_THRESH 0x0300
/* Write text on OSD */
diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index 642ebbf..a3332fe 100644
--- a/drivers/staging/media/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
@@ -511,6 +511,8 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
int ret;
/* Check for motion flags */
+ vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_MOTION_ON |
+ V4L2_BUF_FLAG_MOTION_DETECTED);
if (solo_is_motion_on(solo_enc)) {
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_ON;
if (enc_buf->motion)
@@ -1018,6 +1020,31 @@ static int solo_s_parm(struct file *file, void *priv,
return 0;
}
+static long solo_enc_default(struct file *file, void *fh,
+ bool valid_prio, int cmd, void *arg)
+{
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_dev *solo_dev = solo_enc->solo_dev;
+ struct solo_motion_thresholds *thresholds = arg;
+
+ switch (cmd) {
+ case SOLO_IOC_G_MOTION_THRESHOLDS:
+ *thresholds = solo_enc->motion_thresholds;
+ return 0;
+
+ case SOLO_IOC_S_MOTION_THRESHOLDS:
+ if (!valid_prio)
+ return -EBUSY;
+ solo_enc->motion_thresholds = *thresholds;
+ if (solo_enc->motion_enabled && !solo_enc->motion_global)
+ return solo_set_motion_block(solo_dev, solo_enc->ch,
+ &solo_enc->motion_thresholds);
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct solo_enc_dev *solo_enc =
@@ -1036,28 +1063,22 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
solo_enc->gop = ctrl->val;
return 0;
- case V4L2_CID_MOTION_THRESHOLD: {
- u16 block = (ctrl->val >> 16) & 0xffff;
- u16 value = ctrl->val & 0xffff;
-
- /* Motion thresholds are in a table of 64x64 samples, with
- * each sample representing 16x16 pixels of the source. In
- * effect, 44x30 samples are used for NTSC, and 44x36 for PAL.
- * The 5th sample on the 10th row is (10*64)+5 = 645.
- *
- * Block is 0 to set the threshold globally, or any positive
- * number under 2049 to set block-1 individually. */
- /* Currently we limit support to block 0 only. A later patch
- * will add a new ioctl to set all other blocks. */
- if (block == 0) {
- solo_enc->motion_thresh = value;
- return solo_set_motion_threshold(solo_dev,
- solo_enc->ch, value);
+ case V4L2_CID_MOTION_THRESHOLD:
+ solo_enc->motion_thresh = ctrl->val;
+ if (!solo_enc->motion_global || !solo_enc->motion_enabled)
+ return 0;
+ return solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->val);
+ case V4L2_CID_MOTION_MODE:
+ solo_enc->motion_global = ctrl->val == 1;
+ solo_enc->motion_enabled = ctrl->val > 0;
+ if (ctrl->val) {
+ if (solo_enc->motion_global)
+ solo_set_motion_threshold(solo_dev, solo_enc->ch,
+ solo_enc->motion_thresh);
+ else
+ solo_set_motion_block(solo_dev, solo_enc->ch,
+ &solo_enc->motion_thresholds);
}
- return solo_set_motion_block(solo_dev, solo_enc->ch,
- value, block - 1);
- }
- case V4L2_CID_MOTION_ENABLE:
solo_motion_toggle(solo_enc, ctrl->val);
return 0;
case V4L2_CID_OSD_TEXT:
@@ -1111,6 +1132,7 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_default = solo_enc_default,
};
static const struct video_device solo_enc_template = {
@@ -1137,13 +1159,20 @@ static const struct v4l2_ctrl_config solo_motion_threshold_ctrl = {
.flags = V4L2_CTRL_FLAG_SLIDER,
};
+static const char * const solo_motion_mode_menu[] = {
+ "Disabled",
+ "Global Threshold",
+ "Regional Threshold",
+ NULL
+};
+
static const struct v4l2_ctrl_config solo_motion_enable_ctrl = {
.ops = &solo_ctrl_ops,
- .id = V4L2_CID_MOTION_ENABLE,
- .name = "Motion Detection Enable",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
+ .id = V4L2_CID_MOTION_MODE,
+ .name = "Motion Detection Mode",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .qmenu = solo_motion_mode_menu,
+ .max = 2,
};
static const struct v4l2_ctrl_config solo_osd_text_ctrl = {
@@ -1161,6 +1190,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
struct solo_enc_dev *solo_enc;
struct v4l2_ctrl_handler *hdl;
int ret;
+ int x, y;
solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL);
if (!solo_enc)
@@ -1201,7 +1231,12 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
solo_enc->gop = solo_dev->fps;
solo_enc->interval = 1;
solo_enc->mode = SOLO_ENC_MODE_CIF;
+ solo_enc->motion_global = true;
solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;
+ for (y = 0; y < SOLO_MOTION_SZ; y++)
+ for (x = 0; x < SOLO_MOTION_SZ; x++)
+ solo_enc->motion_thresholds.thresholds[y][x] =
+ SOLO_DEF_MOT_THRESH;
solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
--
1.7.10.4
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo.
2013-03-18 12:32 ` [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo Hans Verkuil
@ 2013-03-24 14:27 ` Mauro Carvalho Chehab
2013-03-25 8:51 ` Hans Verkuil
0 siblings, 1 reply; 22+ messages in thread
From: Mauro Carvalho Chehab @ 2013-03-24 14:27 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ismael Luceno, Hans Verkuil
Em Mon, 18 Mar 2013 13:32:00 +0100
Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> Synced to commit e9815ac5503ae60cfbf6ff8037035de8f62e2846 from
> branch next in git repository https://github.com/bluecherrydvr/solo6x10.git
>
> Only removed some code under #if LINUX_VERSION_CODE < some-kernel-version,
> renamed the driver back to solo6x10 from solo6x10-edge and removed the
> unnecessary compat.h header.
>
> Otherwise the code is identical.
>
...
> @@ -21,29 +26,78 @@
> #include <linux/module.h>
> #include <linux/pci.h>
> #include <linux/interrupt.h>
> -#include <linux/slab.h>
You can't remove slab.h if any k*alloc function is used, or it will
break compilation, depending on the Kconfig options selected.
The same type of removal are on other files inside this patch.
Please fix.
Regards,
Mauro
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo.
2013-03-24 14:27 ` Mauro Carvalho Chehab
@ 2013-03-25 8:51 ` Hans Verkuil
0 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2013-03-25 8:51 UTC (permalink / raw)
To: Mauro Carvalho Chehab; +Cc: linux-media, Ismael Luceno, Hans Verkuil
On Sun March 24 2013 15:27:23 Mauro Carvalho Chehab wrote:
> Em Mon, 18 Mar 2013 13:32:00 +0100
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
>
> > From: Hans Verkuil <hans.verkuil@cisco.com>
> >
> > Synced to commit e9815ac5503ae60cfbf6ff8037035de8f62e2846 from
> > branch next in git repository https://github.com/bluecherrydvr/solo6x10.git
> >
> > Only removed some code under #if LINUX_VERSION_CODE < some-kernel-version,
> > renamed the driver back to solo6x10 from solo6x10-edge and removed the
> > unnecessary compat.h header.
> >
> > Otherwise the code is identical.
> >
>
> ...
>
> > @@ -21,29 +26,78 @@
> > #include <linux/module.h>
> > #include <linux/pci.h>
> > #include <linux/interrupt.h>
> > -#include <linux/slab.h>
>
> You can't remove slab.h if any k*alloc function is used, or it will
> break compilation, depending on the Kconfig options selected.
Well spotted, thanks!
>
> The same type of removal are on other files inside this patch.
>
> Please fix.
Done. New pull request posted.
Regards,
Hans
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2013-03-25 8:51 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-18 12:31 [REVIEW PATCH 00/19] solo6x10: driver overhaul Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 01/19] solo6x10: sync to latest code from Bluecherry's git repo Hans Verkuil
2013-03-24 14:27 ` Mauro Carvalho Chehab
2013-03-25 8:51 ` Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 02/19] solo6x10: fix querycap and update driver version Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 03/19] solo6x10: add v4l2_device Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 04/19] solo6x10: add control framework Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 05/19] solo6x10: fix various format-related compliancy issues Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 06/19] solo6x10: add support for prio and control event handling Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 07/19] solo6x10: move global fields in solo_dev_fh to solo_dev Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 08/19] solo6x10: move global fields in solo_enc_fh to solo_enc_dev Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 09/19] solo6x10: convert encoder nodes to vb2 Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 10/19] solo6x10: convert the display node " Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 11/19] solo6x10: fix 'BUG: key ffff88081a2a9b58 not in .data!' Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 12/19] solo6x10: add call to pci_dma_mapping_error Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 13/19] solo6x10: drop video_type and add proper s_std support Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 14/19] solo6x10: also stop DMA if the SOLO_PCI_ERR_P2M_DESC is raised Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 15/19] solo6x10: small big-endian fix Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 16/19] solo6x10: use V4L2_PIX_FMT_MPEG4, not _FMT_MPEG Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 17/19] solo6x10: fix sequence handling Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 18/19] solo6x10: disable the 'priv' abuse Hans Verkuil
2013-03-18 12:32 ` [REVIEW PATCH 19/19] solo6x10: clean up motion detection handling Hans Verkuil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).