Commit dcae5dac authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] solo6x10: sync to latest code from Bluecherry's git repo

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, removed the
unnecessary compat.h header and kept the slab.h includes.
Otherwise the code is identical.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarIsmael Luceno <ismael.luceno@corp.bluecherry.net>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent c535cc6c
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 obj-$(CONFIG_SOLO6X10) += solo6x10.o
TODO (staging => main): - batch up desc requests for more efficient use of p2m?
- encoder on/off controls
* Motion detection flags need to be moved to v4l2 - mpeg cid bitrate mode (vbr/cbr)
* Some private CIDs need to be moved to v4l2 - mpeg cid bitrate/bitrate-peak
- mpeg encode of user data
TODO (general): - mpeg decode of user data
- implement CID controls for mozaic areas
* encoder on/off controls
* mpeg cid bitrate mode (vbr/cbr) - sound
* mpeg cid bitrate/bitrate-peak - implement playback via external sound jack
* mpeg encode of user data - implement loopback of external sound jack with incoming audio?
* mpeg decode of user data - implement pause/resume (make use of in bc-server)
* 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
Please send patches to the linux media list <linux-media@vger.kernel.org> and Please send patches to the linux media list <linux-media@vger.kernel.org> and
Cc Ismael Luceno <ismael.luceno@corp.bluecherry.net>. Cc Ismael Luceno <ismael.luceno@corp.bluecherry.net>.
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -21,29 +26,79 @@ ...@@ -21,29 +26,79 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include "solo6x10.h" #include "solo6x10.h"
#include "tw28.h" #include "tw28.h"
MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver"); MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver");
MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>"); MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>");
MODULE_VERSION(SOLO6X10_VERSION); MODULE_VERSION(SOLO6X10_VERSION);
MODULE_LICENSE("GPL"); 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; struct timespec ts;
solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
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; u32 sec, usec;
solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); 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) static irqreturn_t solo_isr(int irq, void *data)
{ {
struct solo_dev *solo_dev = data; struct solo_dev *solo_dev = data;
...@@ -60,11 +115,8 @@ static irqreturn_t solo_isr(int irq, void *data) ...@@ -60,11 +115,8 @@ static irqreturn_t solo_isr(int irq, void *data)
status &= solo_dev->irq_mask; status &= solo_dev->irq_mask;
} }
if (status & SOLO_IRQ_PCI_ERR) { if (status & SOLO_IRQ_PCI_ERR)
u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); solo_p2m_error_isr(solo_dev);
solo_p2m_error_isr(solo_dev, err);
solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR);
}
for (i = 0; i < SOLO_NR_P2M; i++) for (i = 0; i < SOLO_NR_P2M; i++)
if (status & SOLO_IRQ_P2M(i)) if (status & SOLO_IRQ_P2M(i))
...@@ -73,12 +125,10 @@ static irqreturn_t solo_isr(int irq, void *data) ...@@ -73,12 +125,10 @@ static irqreturn_t solo_isr(int irq, void *data)
if (status & SOLO_IRQ_IIC) if (status & SOLO_IRQ_IIC)
solo_i2c_isr(solo_dev); solo_i2c_isr(solo_dev);
if (status & SOLO_IRQ_VIDEO_IN) if (status & SOLO_IRQ_VIDEO_IN) {
solo_video_in_isr(solo_dev); solo_video_in_isr(solo_dev);
solo_timer_sync(solo_dev);
/* Call this first so enc gets detected flag set */ }
if (status & SOLO_IRQ_MOTION)
solo_motion_isr(solo_dev);
if (status & SOLO_IRQ_ENCODER) if (status & SOLO_IRQ_ENCODER)
solo_enc_v4l2_isr(solo_dev); solo_enc_v4l2_isr(solo_dev);
...@@ -86,6 +136,9 @@ static irqreturn_t solo_isr(int irq, void *data) ...@@ -86,6 +136,9 @@ static irqreturn_t solo_isr(int irq, void *data)
if (status & SOLO_IRQ_G723) if (status & SOLO_IRQ_G723)
solo_g723_isr(solo_dev); solo_g723_isr(solo_dev);
/* Clear all interrupts handled */
solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -96,6 +149,9 @@ static void free_solo_dev(struct solo_dev *solo_dev) ...@@ -96,6 +149,9 @@ static void free_solo_dev(struct solo_dev *solo_dev)
if (!solo_dev) if (!solo_dev)
return; return;
if (solo_dev->dev.parent)
device_unregister(&solo_dev->dev);
pdev = solo_dev->pdev; pdev = solo_dev->pdev;
/* If we never initialized the PCI device, then nothing else /* If we never initialized the PCI device, then nothing else
...@@ -105,21 +161,22 @@ static void free_solo_dev(struct solo_dev *solo_dev) ...@@ -105,21 +161,22 @@ static void free_solo_dev(struct solo_dev *solo_dev)
return; 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) { 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); solo_irq_off(solo_dev, ~0);
pci_iounmap(pdev, solo_dev->reg_base); 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); pci_release_regions(pdev);
...@@ -129,29 +186,346 @@ static void free_solo_dev(struct solo_dev *solo_dev) ...@@ -129,29 +186,346 @@ static void free_solo_dev(struct solo_dev *solo_dev)
kfree(solo_dev); kfree(solo_dev);
} }
static int solo_pci_probe(struct pci_dev *pdev, static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr,
const struct pci_device_id *id) 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; struct solo_dev *solo_dev;
int ret; int ret;
int sdram;
u8 chip_id; u8 chip_id;
u32 reg;
solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL);
if (solo_dev == NULL) if (solo_dev == NULL)
return -ENOMEM; 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; solo_dev->pdev = pdev;
spin_lock_init(&solo_dev->reg_io_lock); spin_lock_init(&solo_dev->reg_io_lock);
pci_set_drvdata(pdev, solo_dev); pci_set_drvdata(pdev, solo_dev);
/* Only for during init */
solo_dev->p2m_jiffies = msecs_to_jiffies(100);
ret = pci_enable_device(pdev); ret = pci_enable_device(pdev);
if (ret) if (ret)
goto fail_probe; goto fail_probe;
pci_set_master(pdev); 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); ret = pci_request_regions(pdev, SOLO6X10_NAME);
if (ret) if (ret)
goto fail_probe; goto fail_probe;
...@@ -163,7 +537,7 @@ static int solo_pci_probe(struct pci_dev *pdev, ...@@ -163,7 +537,7 @@ static int solo_pci_probe(struct pci_dev *pdev,
} }
chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) &
SOLO_CHIP_ID_MASK; SOLO_CHIP_ID_MASK;
switch (chip_id) { switch (chip_id) {
case 7: case 7:
solo_dev->nr_chans = 16; solo_dev->nr_chans = 16;
...@@ -174,52 +548,50 @@ static int solo_pci_probe(struct pci_dev *pdev, ...@@ -174,52 +548,50 @@ static int solo_pci_probe(struct pci_dev *pdev,
solo_dev->nr_ext = 2; solo_dev->nr_ext = 2;
break; break;
default: default:
dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n",
"defaulting to 4 channels\n",
chip_id); chip_id);
case 5: case 5:
solo_dev->nr_chans = 4; solo_dev->nr_chans = 4;
solo_dev->nr_ext = 1; solo_dev->nr_ext = 1;
} }
solo_dev->flags = id->driver_data;
/* Disable all interrupts to start */ /* Disable all interrupts to start */
solo_irq_off(solo_dev, ~0); solo_irq_off(solo_dev, ~0);
reg = SOLO_SYS_CFG_SDRAM64BIT;
/* Initial global settings */ /* Initial global settings */
if (!(solo_dev->flags & FLAGS_6110)) if (solo_dev->type == SOLO_DEV_6010) {
reg |= SOLO6010_SYS_CFG_INPUTDIV(25) | solo_dev->clock_mhz = 108;
SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT
SOLO6010_SYS_CFG_OUTDIV(3); | SOLO_SYS_CFG_INPUTDIV(25)
solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2)
| SOLO_SYS_CFG_OUTDIV(3);
if (solo_dev->flags & FLAGS_6110) { solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config);
u32 sys_clock_MHz = SOLO_CLOCK_MHZ; } else {
u32 pll_DIVQ; u32 divq, divf;
u32 pll_DIVF;
solo_dev->clock_mhz = 135;
if (sys_clock_MHz < 125) {
pll_DIVQ = 3; if (solo_dev->clock_mhz < 125) {
pll_DIVF = (sys_clock_MHz * 4) / 3; divq = 3;
divf = (solo_dev->clock_mhz * 4) / 3 - 1;
} else { } else {
pll_DIVQ = 2; divq = 2;
pll_DIVF = (sys_clock_MHz * 2) / 3; divf = (solo_dev->clock_mhz * 2) / 3 - 1;
} }
solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, solo_reg_write(solo_dev, SOLO_PLL_CONFIG,
SOLO6110_PLL_RANGE_5_10MHZ | (1 << 20) | /* PLL_RANGE */
SOLO6110_PLL_DIVR(9) | (8 << 15) | /* PLL_DIVR */
SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | (divq << 12) |
SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); (divf << 4) |
mdelay(1); /* PLL Locking time (1ms) */ (1 << 1) /* PLL_FSEN */);
solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT;
} else }
solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */
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 */ /* PLL locking time of 1ms */
mdelay(1); mdelay(1);
...@@ -237,14 +609,27 @@ static int solo_pci_probe(struct pci_dev *pdev, ...@@ -237,14 +609,27 @@ static int solo_pci_probe(struct pci_dev *pdev,
goto fail_probe; goto fail_probe;
/* Setup the DMA engine */ /* Setup the DMA engine */
sdram = (solo_dev->nr_chans >= 8) ? 2 : 1;
solo_reg_write(solo_dev, SOLO_DMA_CTRL, solo_reg_write(solo_dev, SOLO_DMA_CTRL,
SOLO_DMA_CTRL_REFRESH_CYCLE(1) | 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_SDRAM_CLK_INVERT |
SOLO_DMA_CTRL_READ_CLK_SELECT | SOLO_DMA_CTRL_READ_CLK_SELECT |
SOLO_DMA_CTRL_LATENCY(1)); 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); ret = solo_p2m_init(solo_dev);
if (ret) if (ret)
goto fail_probe; goto fail_probe;
...@@ -261,7 +646,7 @@ static int solo_pci_probe(struct pci_dev *pdev, ...@@ -261,7 +646,7 @@ static int solo_pci_probe(struct pci_dev *pdev,
if (ret) if (ret)
goto fail_probe; goto fail_probe;
ret = solo_v4l2_init(solo_dev); ret = solo_v4l2_init(solo_dev, video_nr);
if (ret) if (ret)
goto fail_probe; goto fail_probe;
...@@ -269,7 +654,7 @@ static int solo_pci_probe(struct pci_dev *pdev, ...@@ -269,7 +654,7 @@ static int solo_pci_probe(struct pci_dev *pdev,
if (ret) if (ret)
goto fail_probe; goto fail_probe;
ret = solo_enc_v4l2_init(solo_dev); ret = solo_enc_v4l2_init(solo_dev, video_nr);
if (ret) if (ret)
goto fail_probe; goto fail_probe;
...@@ -277,6 +662,13 @@ static int solo_pci_probe(struct pci_dev *pdev, ...@@ -277,6 +662,13 @@ static int solo_pci_probe(struct pci_dev *pdev,
if (ret) if (ret)
goto fail_probe; 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; return 0;
fail_probe: fail_probe:
...@@ -291,21 +683,31 @@ static void solo_pci_remove(struct pci_dev *pdev) ...@@ -291,21 +683,31 @@ static void solo_pci_remove(struct pci_dev *pdev)
free_solo_dev(solo_dev); free_solo_dev(solo_dev);
} }
static struct pci_device_id solo_id_table[] = { static DEFINE_PCI_DEVICE_TABLE(solo_id_table) = {
/* 6010 based cards */ /* 6010 based cards */
{PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010),
{PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), .driver_data = SOLO_DEV_6010 },
.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_4)}, .driver_data = SOLO_DEV_6010 },
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9),
{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)}, { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16),
{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)}, { 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 */ /* 6110 based cards */
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)}, { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110),
{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)}, { 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,} {0,}
}; };
......
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -21,6 +26,7 @@ ...@@ -21,6 +26,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include "solo6x10.h" #include "solo6x10.h"
#define SOLO_VCLK_DELAY 3 #define SOLO_VCLK_DELAY 3
...@@ -30,8 +36,8 @@ ...@@ -30,8 +36,8 @@
#define SOLO_MOT_THRESH_H 64 #define SOLO_MOT_THRESH_H 64
#define SOLO_MOT_THRESH_SIZE 8192 #define SOLO_MOT_THRESH_SIZE 8192
#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) #define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H)
#define SOLO_MOT_FLAG_SIZE 512 #define SOLO_MOT_FLAG_SIZE 1024
#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32) #define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16)
static unsigned video_type; static unsigned video_type;
module_param(video_type, uint, 0644); module_param(video_type, uint, 0644);
...@@ -73,7 +79,12 @@ static void solo_vin_config(struct solo_dev *solo_dev) ...@@ -73,7 +79,12 @@ static void solo_vin_config(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT,
SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); 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); solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2);
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
...@@ -97,21 +108,30 @@ static void solo_vin_config(struct solo_dev *solo_dev) ...@@ -97,21 +108,30 @@ static void solo_vin_config(struct solo_dev *solo_dev)
SOLO_VI_PB_HSTOP(16 + 720)); 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_hstart = 6;
solo_dev->vout_vstart = 8; 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_reg_write(solo_dev, SOLO_VO_FMT_ENC,
solo_dev->video_type | solo_dev->video_type |
SOLO_VO_USER_COLOR_SET_NAV | SOLO_VO_USER_COLOR_SET_NAV |
SOLO_VO_USER_COLOR_SET_NAH |
SOLO_VO_NA_COLOR_Y(0) | SOLO_VO_NA_COLOR_Y(0) |
SOLO_VO_NA_COLOR_CB(0) | SOLO_VO_NA_COLOR_CB(0) |
SOLO_VO_NA_COLOR_CR(0)); SOLO_VO_NA_COLOR_CR(0));
...@@ -130,19 +150,31 @@ static void solo_disp_config(struct solo_dev *solo_dev) ...@@ -130,19 +150,31 @@ static void solo_disp_config(struct solo_dev *solo_dev)
SOLO_VO_H_LEN(solo_dev->video_hsize) | SOLO_VO_H_LEN(solo_dev->video_hsize) |
SOLO_VO_V_LEN(solo_dev->video_vsize)); 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_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON |
SOLO_VO_DISP_ERASE_COUNT(8) | SOLO_VO_DISP_ERASE_COUNT(8) |
SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); 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_vout_config_cursor(solo_dev);
solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1);
/* Disable the watchdog */ /* Enable channels we support */
solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); 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, 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, ...@@ -156,26 +188,53 @@ static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,
buf[i] = val; buf[i] = val;
for (i = 0; i < reg_size; i += sizeof(buf)) 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, SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,
sizeof(buf)); sizeof(buf), 0, 0);
return ret; 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) if (ch > solo_dev->nr_chans)
return; return -EINVAL;
solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
(ch * SOLO_MOT_THRESH_SIZE * 2), (ch * SOLO_MOT_THRESH_SIZE * 2),
val, SOLO_MOT_THRESH_REAL); 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 /* 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 * 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. */ * block right after the flag area, before the first thresh table. */
static void solo_motion_config(struct solo_dev *solo_dev) static void solo_motion_config(struct solo_dev *solo_dev)
{ {
...@@ -188,9 +247,9 @@ 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 */ /* Clear working cache table */
solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
SOLO_MOT_THRESH_SIZE + (i * SOLO_MOT_THRESH_SIZE * 2) +
(i * SOLO_MOT_THRESH_SIZE * 2), SOLO_MOT_THRESH_SIZE, 0x0000,
0x0000, SOLO_MOT_THRESH_REAL); SOLO_MOT_THRESH_SIZE);
/* Set default threshold table */ /* Set default threshold table */
solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); 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) ...@@ -202,8 +261,8 @@ static void solo_motion_config(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL,
SOLO_VI_MOTION_FRAME_COUNT(3) | SOLO_VI_MOTION_FRAME_COUNT(3) |
SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16)
| /* SOLO_VI_MOTION_INTR_START_STOP | */ /* | SOLO_VI_MOTION_INTR_START_STOP */
SOLO_VI_MOTION_SAMPLE_COUNT(10)); | SOLO_VI_MOTION_SAMPLE_COUNT(10));
solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0);
solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0);
...@@ -226,7 +285,7 @@ int solo_disp_init(struct solo_dev *solo_dev) ...@@ -226,7 +285,7 @@ int solo_disp_init(struct solo_dev *solo_dev)
solo_vin_config(solo_dev); solo_vin_config(solo_dev);
solo_motion_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++) for (i = 0; i < solo_dev->nr_chans; i++)
solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1);
...@@ -238,8 +297,6 @@ void solo_disp_exit(struct solo_dev *solo_dev) ...@@ -238,8 +297,6 @@ void solo_disp_exit(struct solo_dev *solo_dev)
{ {
int i; 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_DISP_CTRL, 0);
solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 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_FREEZE_CTRL, 0);
......
/*
* 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;
}
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -18,30 +23,41 @@ ...@@ -18,30 +23,41 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/font.h>
#include <linux/bitrev.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "solo6x10.h"
#include "osd-font.h"
#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */ #include "solo6x10.h"
#define OSG_BUFFER_SIZE 1024
#define VI_PROG_HSIZE (1280 - 16) #define VI_PROG_HSIZE (1280 - 16)
#define VI_PROG_VSIZE (1024 - 16) #define VI_PROG_VSIZE (1024 - 16)
#define IRQ_LEVEL 2
static void solo_capture_config(struct solo_dev *solo_dev) static void solo_capture_config(struct solo_dev *solo_dev)
{ {
int i, j;
unsigned long height; unsigned long height;
unsigned long width; unsigned long width;
unsigned char *buf; void *buf;
int i;
solo_reg_write(solo_dev, SOLO_CAP_BASE, solo_reg_write(solo_dev, SOLO_CAP_BASE,
SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE * SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev)
solo_dev->nr_chans) | - SOLO_CAP_PAGE_SIZE) >> 16)
SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); | 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) | /* XXX: Undocumented bits at b17 and b24 */
SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH)); 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 */ /* Set scale 1, 9 dimension */
width = solo_dev->video_hsize; width = solo_dev->video_hsize;
...@@ -96,115 +112,212 @@ static void solo_capture_config(struct solo_dev *solo_dev) ...@@ -96,115 +112,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_BASE, SOLO_EOSD_EXT_ADDR >> 16);
solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, solo_reg_write(solo_dev, SOLO_VE_OSD_CLR,
0xF0 << 16 | 0x80 << 8 | 0x80); 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 */ /* Clear OSG buffer */
buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL); buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL);
if (!buf) if (!buf)
return; return;
for (i = 0; i < solo_dev->nr_chans; i++) { 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, 1, buf,
solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf, SOLO_EOSD_EXT_ADDR +
SOLO_EOSD_EXT_ADDR + (SOLO_EOSD_EXT_SIZE(solo_dev) * i),
(i * SOLO_EOSD_EXT_SIZE) + j, SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0);
OSG_BUFFER_SIZE);
}
} }
kfree(buf); kfree(buf);
} }
/* Should be called with enable_lock held */
int solo_osd_print(struct solo_enc_dev *solo_enc) int solo_osd_print(struct solo_enc_dev *solo_enc)
{ {
struct solo_dev *solo_dev = solo_enc->solo_dev; struct solo_dev *solo_dev = solo_enc->solo_dev;
char *str = solo_enc->osd_text; unsigned char *str = solo_enc->osd_text;
u8 *buf; u8 *buf = solo_enc->osd_buf;
u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); 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 i, j;
int x = 1, y = 1;
if (WARN_ON_ONCE(!vga))
return -ENODEV;
len = strlen(str);
if (len == 0) { if (len == 0) {
/* Disable OSD on this channel */
reg &= ~(1 << solo_enc->ch); reg &= ~(1 << solo_enc->ch);
solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
return 0; return 0;
} }
buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL); memset(buf, 0, SOLO_EOSD_EXT_SIZE_MAX);
if (!buf) vga_data = (const unsigned char *)vga->data;
return -ENOMEM;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
unsigned char c = str[i];
for (j = 0; j < 16; j++) { for (j = 0; j < 16; j++) {
buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] = buf[(j * 2) + (i % 2) + (i / 2 * 32)] =
(solo_osd_font[(str[i] * 4) + (j / 4)] bitrev8(vga_data[(c * 16) + j]);
>> ((3 - (j % 4)) * 8)) & 0xff;
} }
} }
solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR + solo_p2m_dma(solo_dev, 1, buf,
(solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); 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); reg |= (1 << solo_enc->ch);
solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
kfree(buf);
return 0; 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) static void solo_jpeg_config(struct solo_dev *solo_dev)
{ {
u32 reg; if (solo_dev->type == SOLO_DEV_6010) {
if (solo_dev->flags & FLAGS_6110) solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL,
reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0); (2 << 24) | (2 << 16) | (2 << 8) | 2);
else } else {
reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0); solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL,
solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg); (4 << 24) | (3 << 16) | (2 << 8) | 1);
solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0); }
solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0);
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_reg_write(solo_dev, SOLO_VE_JPEG_CFG,
(SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) |
((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff));
solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff);
/* que limit, samp limit, pos limit */ if (solo_dev->type == SOLO_DEV_6110) {
solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60); solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1,
(0 << 16) | (30 << 8) | 60);
}
} }
static void solo_mp4e_config(struct solo_dev *solo_dev) static void solo_mp4e_config(struct solo_dev *solo_dev)
{ {
int i; 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_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_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) |
SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(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_WMRK_POLY, 0);
solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0);
solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 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_POLY, 0);
solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0);
reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) | solo_reg_write(solo_dev, SOLO_VE_ATTR,
SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15); SOLO_VE_LITTLE_ENDIAN |
if (solo_dev->flags & FLAGS_6110) SOLO_COMP_ATTR_FCODE(1) |
reg |= SOLO_DCT_INTERVAL(10); SOLO_COMP_TIME_INC(0) |
else SOLO_COMP_TIME_WIDTH(15) |
reg |= SOLO_DCT_INTERVAL(36 / 4); SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10));
solo_reg_write(solo_dev, SOLO_VE_ATTR, reg);
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_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i),
(SOLO_EREF_EXT_ADDR(solo_dev) + (SOLO_EREF_EXT_ADDR(solo_dev) +
(i * SOLO_EREF_EXT_SIZE)) >> 16); (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) if (solo_dev->type == SOLO_DEV_6110) {
solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */ 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) int solo_enc_init(struct solo_dev *solo_dev)
...@@ -220,8 +333,6 @@ int solo_enc_init(struct solo_dev *solo_dev) ...@@ -220,8 +333,6 @@ int solo_enc_init(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0);
} }
solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
return 0; return 0;
} }
...@@ -229,8 +340,6 @@ void solo_enc_exit(struct solo_dev *solo_dev) ...@@ -229,8 +340,6 @@ void solo_enc_exit(struct solo_dev *solo_dev)
{ {
int i; int i;
solo_irq_off(solo_dev, SOLO_IRQ_ENCODER);
for (i = 0; i < solo_dev->nr_chans; i++) { 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_SCALE(i), 0);
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0);
......
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -21,17 +26,18 @@ ...@@ -21,17 +26,18 @@
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/export.h> #include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/control.h> #include <sound/control.h>
#include "solo6x10.h" #include "solo6x10.h"
#include "tw28.h" #include "tw28.h"
#define G723_INTR_ORDER 0
#define G723_FDMA_PAGES 32 #define G723_FDMA_PAGES 32
#define G723_PERIOD_BYTES 48 #define G723_PERIOD_BYTES 48
#define G723_PERIOD_BLOCK 1024 #define G723_PERIOD_BLOCK 1024
...@@ -46,36 +52,40 @@ ...@@ -46,36 +52,40 @@
/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page /* 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) * is broken down to 20 * 48 byte regions (one for each channel possible)
* with the rest of the page being dummy data. */ * with the rest of the page being dummy data. */
#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) #define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX)
#define IRQ_PAGES 4 /* 0 - 4 */ #define G723_INTR_ORDER 4 /* 0 - 4 */
#define PERIODS_MIN (1 << IRQ_PAGES) #define PERIODS_MIN (1 << G723_INTR_ORDER)
#define PERIODS_MAX G723_FDMA_PAGES #define PERIODS_MAX G723_FDMA_PAGES
struct solo_snd_pcm { struct solo_snd_pcm {
int on; int on;
spinlock_t lock; spinlock_t lock;
struct solo_dev *solo_dev; struct solo_dev *solo_dev;
unsigned char g723_buf[G723_PERIOD_BYTES]; unsigned char *g723_buf;
dma_addr_t g723_dma;
}; };
static void solo_g723_config(struct solo_dev *solo_dev) static void solo_g723_config(struct solo_dev *solo_dev)
{ {
int clk_div; 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_reg_write(solo_dev, SOLO_AUDIO_SAMPLE,
SOLO_AUDIO_BITRATE(BITRATE) | SOLO_AUDIO_BITRATE(BITRATE)
SOLO_AUDIO_CLK_DIV(clk_div)); | SOLO_AUDIO_CLK_DIV(clk_div));
solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR,
SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | SOLO_AUDIO_FDMA_INTERVAL(1)
SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER)
SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16));
solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL,
SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | SOLO_AUDIO_ENABLE
SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); | SOLO_AUDIO_I2S_MODE
| SOLO_AUDIO_I2S_MULTI(3)
| SOLO_AUDIO_MODE(OUTMODE_MASK));
} }
void solo_g723_isr(struct solo_dev *solo_dev) void solo_g723_isr(struct solo_dev *solo_dev)
...@@ -85,8 +95,6 @@ void solo_g723_isr(struct solo_dev *solo_dev) ...@@ -85,8 +95,6 @@ void solo_g723_isr(struct solo_dev *solo_dev)
struct snd_pcm_substream *ss; struct snd_pcm_substream *ss;
struct solo_snd_pcm *solo_pcm; 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) { for (ss = pstr->substream; ss != NULL; ss = ss->next) {
if (snd_pcm_substream_chip(ss) == NULL) if (snd_pcm_substream_chip(ss) == NULL)
continue; continue;
...@@ -115,18 +123,18 @@ static int snd_solo_hw_free(struct snd_pcm_substream *ss) ...@@ -115,18 +123,18 @@ static int snd_solo_hw_free(struct snd_pcm_substream *ss)
return snd_pcm_lib_free_pages(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 | .info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID), SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8, .formats = SNDRV_PCM_FMTBIT_U8,
.rates = SNDRV_PCM_RATE_8000, .rates = SNDRV_PCM_RATE_8000,
.rate_min = 8000, .rate_min = SAMPLERATE,
.rate_max = 8000, .rate_max = SAMPLERATE,
.channels_min = 1, .channels_min = 1,
.channels_max = 1, .channels_max = 1,
.buffer_bytes_max = MAX_BUFFER, .buffer_bytes_max = G723_MAX_BUFFER,
.period_bytes_min = G723_PERIOD_BYTES, .period_bytes_min = G723_PERIOD_BYTES,
.period_bytes_max = G723_PERIOD_BYTES, .period_bytes_max = G723_PERIOD_BYTES,
.periods_min = PERIODS_MIN, .periods_min = PERIODS_MIN,
...@@ -140,7 +148,13 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss) ...@@ -140,7 +148,13 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss)
solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL);
if (solo_pcm == NULL) 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); spin_lock_init(&solo_pcm->lock);
solo_pcm->solo_dev = solo_dev; solo_pcm->solo_dev = solo_dev;
...@@ -149,6 +163,10 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss) ...@@ -149,6 +163,10 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss)
snd_pcm_substream_chip(ss) = solo_pcm; snd_pcm_substream_chip(ss) = solo_pcm;
return 0; return 0;
oom:
kfree(solo_pcm);
return -ENOMEM;
} }
static int snd_solo_pcm_close(struct snd_pcm_substream *ss) static int snd_solo_pcm_close(struct snd_pcm_substream *ss)
...@@ -156,6 +174,8 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss) ...@@ -156,6 +174,8 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss)
struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; 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); kfree(solo_pcm);
return 0; return 0;
...@@ -220,12 +240,11 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, ...@@ -220,12 +240,11 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) {
int page = (pos / 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, err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma,
solo_pcm->g723_buf, SOLO_G723_EXT_ADDR(solo_dev) +
SOLO_G723_EXT_ADDR(solo_dev) + (page * G723_PERIOD_BLOCK) +
(page * G723_PERIOD_BLOCK) + (ss->number * G723_PERIOD_BYTES),
(ss->number * G723_PERIOD_BYTES), G723_PERIOD_BYTES, 0, 0);
G723_PERIOD_BYTES);
if (err) if (err)
return err; return err;
...@@ -325,7 +344,7 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev) ...@@ -325,7 +344,7 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev)
ret = snd_pcm_lib_preallocate_pages_for_all(pcm, ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), snd_dma_continuous_data(GFP_KERNEL),
MAX_BUFFER, MAX_BUFFER); G723_MAX_BUFFER, G723_MAX_BUFFER);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -368,6 +387,7 @@ int solo_g723_init(struct solo_dev *solo_dev) ...@@ -368,6 +387,7 @@ int solo_g723_init(struct solo_dev *solo_dev)
strcpy(card->mixername, "SOLO-6x10"); strcpy(card->mixername, "SOLO-6x10");
kctl = snd_solo_capture_volume; kctl = snd_solo_capture_volume;
kctl.count = solo_dev->nr_chans; kctl.count = solo_dev->nr_chans;
ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev));
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -393,8 +413,12 @@ int solo_g723_init(struct solo_dev *solo_dev) ...@@ -393,8 +413,12 @@ int solo_g723_init(struct solo_dev *solo_dev)
void solo_g723_exit(struct solo_dev *solo_dev) 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_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0);
solo_irq_off(solo_dev, SOLO_IRQ_G723); solo_irq_off(solo_dev, SOLO_IRQ_G723);
snd_card_free(solo_dev->snd_card); snd_card_free(solo_dev->snd_card);
solo_dev->snd_card = NULL;
} }
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -19,7 +24,9 @@ ...@@ -19,7 +24,9 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <asm/uaccess.h> #include <linux/delay.h>
#include <linux/uaccess.h>
#include "solo6x10.h" #include "solo6x10.h"
static void solo_gpio_mode(struct solo_dev *solo_dev, static void solo_gpio_mode(struct solo_dev *solo_dev,
......
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -26,6 +31,7 @@ ...@@ -26,6 +31,7 @@
* thread context, ACK the interrupt, and move on. -- BenC */ * thread context, ACK the interrupt, and move on. -- BenC */
#include <linux/kernel.h> #include <linux/kernel.h>
#include "solo6x10.h" #include "solo6x10.h"
u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) 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) ...@@ -173,10 +179,9 @@ int solo_i2c_isr(struct solo_dev *solo_dev)
u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
int ret = -EINVAL; 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) || if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR)
solo_dev->i2c_id < 0) { || solo_dev->i2c_id < 0) {
solo_i2c_stop(solo_dev); solo_i2c_stop(solo_dev);
return -ENXIO; return -ENXIO;
} }
...@@ -239,7 +244,8 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap, ...@@ -239,7 +244,8 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap,
timeout = HZ / 2; timeout = HZ / 2;
for (;;) { 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) if (solo_dev->i2c_state == IIC_STATE_STOP)
break; break;
...@@ -267,7 +273,7 @@ static u32 solo_i2c_functionality(struct i2c_adapter *adap) ...@@ -267,7 +273,7 @@ static u32 solo_i2c_functionality(struct i2c_adapter *adap)
return I2C_FUNC_I2C; 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, .master_xfer = solo_i2c_master_xfer,
.functionality = solo_i2c_functionality, .functionality = solo_i2c_functionality,
}; };
...@@ -288,7 +294,8 @@ int solo_i2c_init(struct solo_dev *solo_dev) ...@@ -288,7 +294,8 @@ int solo_i2c_init(struct solo_dev *solo_dev)
for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
struct i2c_adapter *adap = &solo_dev->i2c_adap[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 = &solo_i2c_algo;
adap->algo_data = solo_dev; adap->algo_data = solo_dev;
adap->retries = 1; adap->retries = 1;
...@@ -311,9 +318,6 @@ int solo_i2c_init(struct solo_dev *solo_dev) ...@@ -311,9 +318,6 @@ int solo_i2c_init(struct solo_dev *solo_dev)
return ret; return ret;
} }
dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n",
SOLO_I2C_ADAPTERS);
return 0; return 0;
} }
......
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -20,55 +25,61 @@ ...@@ -20,55 +25,61 @@
#ifndef __SOLO6X10_OFFSETS_H #ifndef __SOLO6X10_OFFSETS_H
#define __SOLO6X10_OFFSETS_H #define __SOLO6X10_OFFSETS_H
/* Offsets and sizes of the external address */
#define SOLO_DISP_EXT_ADDR 0x00000000 #define SOLO_DISP_EXT_ADDR 0x00000000
#define SOLO_DISP_EXT_SIZE 0x00480000 #define SOLO_DISP_EXT_SIZE 0x00480000
#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) #define SOLO_EOSD_EXT_ADDR \
#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000 (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE)
#define SOLO_EOSD_EXT_SIZE(__solo) \
#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE) (__solo->type == SOLO_DEV_6010 ? 0x10000 : 0x20000)
#define SOLO_OSG_EXT_SIZE 0x00120000 #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_MOTION_EXT_ADDR(__solo) \
#define SOLO_EOSD_EXT_SIZE 0x00010000 (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_AREA(__solo))
#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \
(SOLO_EOSD_EXT_SIZE * __solo->nr_chans))
#define SOLO_MOTION_EXT_SIZE 0x00080000 #define SOLO_MOTION_EXT_SIZE 0x00080000
#define SOLO_G723_EXT_ADDR(__solo) \ #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_G723_EXT_SIZE 0x00010000
#define SOLO_CAP_EXT_ADDR(__solo) \ #define SOLO_CAP_EXT_ADDR(__solo) \
(SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) (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) /* 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) \ #define SOLO_EREF_EXT_ADDR(__solo) \
(SOLO_CAP_EXT_ADDR(__solo) + \ (SOLO_CAP_EXT_ADDR(__solo) + SOLO_CAP_EXT_SIZE(__solo))
(SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1)))
#define SOLO_EREF_EXT_SIZE 0x00140000 #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) \ #define SOLO_MP4E_EXT_ADDR(__solo) \
(SOLO_EREF_EXT_ADDR(__solo) + \ (SOLO_EREF_EXT_ADDR(__solo) + SOLO_EREF_EXT_AREA(__solo))
(SOLO_EREF_EXT_SIZE * __solo->nr_chans)) #define SOLO_MP4E_EXT_SIZE(__solo) \
#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) 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)) (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) \ #define SOLO_SDRAM_END(__solo) \
(SOLO_DREF_EXT_ADDR(__solo) + \ (SOLO_JPEG_EXT_ADDR(__solo) + SOLO_JPEG_EXT_SIZE(__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)
#endif /* __SOLO6X10_OFFSETS_H */ #endif /* __SOLO6X10_OFFSETS_H */
/*
* 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 */
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -18,28 +23,38 @@ ...@@ -18,28 +23,38 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/scatterlist.h>
#include "solo6x10.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, static int desc_mode;
void *sys_addr, u32 ext_addr, u32 size) 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; dma_addr_t dma_addr;
int ret; int ret;
WARN_ON(!size); if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03))
BUG_ON(id >= SOLO_NR_P2M); return -EINVAL;
if (WARN_ON_ONCE(!size))
if (!size)
return -EINVAL; return -EINVAL;
dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); 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, pci_unmap_single(solo_dev->pdev, dma_addr, size,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
...@@ -47,221 +62,141 @@ int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, ...@@ -47,221 +62,141 @@ int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr,
return ret; return ret;
} }
int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, /* Mutex must be held for p2m_id before calling this!! */
dma_addr_t dma_addr, u32 ext_addr, u32 size) int solo_p2m_dma_desc(struct solo_dev *solo_dev,
{ struct solo_p2m_desc *desc, dma_addr_t desc_dma,
struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA); int desc_cnt)
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)
{ {
struct solo_p2m_dev *p2m_dev; struct solo_p2m_dev *p2m_dev;
unsigned int timeout; unsigned int timeout;
unsigned int config = 0;
int ret = 0; int ret = 0;
u32 config = 0; int p2m_id = 0;
dma_addr_t desc_dma = 0;
BUG_ON(id >= SOLO_NR_P2M); /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */
BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC); 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); if (mutex_lock_interruptible(&p2m_dev->mutex))
return -EINTR;
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
INIT_COMPLETION(p2m_dev->completion); INIT_COMPLETION(p2m_dev->completion);
p2m_dev->error = 0; p2m_dev->error = 0;
/* Enable the descriptors */ if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) {
config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id)); /* For 6010 with more than one desc, we can do a one-shot */
desc_dma = pci_map_single(solo_dev->pdev, desc, p2m_dev->desc_count = p2m_dev->desc_idx = 0;
desc_count * sizeof(*desc), config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id));
PCI_DMA_TODEVICE);
solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma); solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma);
solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1); solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt);
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config | solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config |
SOLO_P2M_DESC_MODE); SOLO_P2M_DESC_MODE);
} else {
/* Should have all descriptors completed from one interrupt */ /* For single descriptors and 6110, we need to run each desc */
timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); p2m_dev->desc_count = desc_cnt;
p2m_dev->desc_idx = 1;
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); 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 */ timeout = wait_for_completion_timeout(&p2m_dev->completion,
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config); solo_dev->p2m_jiffies);
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);
if (p2m_dev->error) if (WARN_ON_ONCE(p2m_dev->error))
ret = -EIO; ret = -EIO;
else if (timeout == 0) else if (timeout == 0) {
solo_dev->p2m_timeouts++;
ret = -EAGAIN; 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, void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr,
u32 base, int size) dma_addr_t dma_addr, u32 ext_addr, u32 size,
int repeat, u32 ext_size)
{ {
u8 *wr_buf; WARN_ON_ONCE(dma_addr & 0x03);
u8 *rd_buf; WARN_ON_ONCE(!size);
int i;
unsigned long long err_cnt = 0;
wr_buf = kmalloc(size, GFP_KERNEL); desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2);
if (!wr_buf) { desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON;
return size;
}
rd_buf = kmalloc(size, GFP_KERNEL); if (repeat) {
if (!rd_buf) { desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2);
printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) |
kfree(wr_buf); SOLO_P2M_REPEAT(repeat);
return size;
} }
memset(wr_buf, P2M_TEST_CHAR, size); desc->dma_addr = dma_addr;
memset(rd_buf, P2M_TEST_CHAR + 1, size); desc->ext_addr = ext_addr;
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;
} }
#define TEST_CHUNK_SIZE (8 * 1024) int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr,
dma_addr_t dma_addr, u32 ext_addr, u32 size,
static void run_p2m_test(struct solo_dev *solo_dev) int repeat, u32 ext_size)
{ {
unsigned long long errs = 0; struct solo_p2m_desc desc[2];
u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev);
int i, d;
dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n", solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat,
size); ext_size);
for (i = 0; i < size; i += TEST_CHUNK_SIZE) /* No need for desc_dma since we know it is a single-shot */
for (d = 0; d < 4; d++) return solo_p2m_dma_desc(solo_dev, desc, 0, 1);
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;
} }
#else
#define run_p2m_test(__solo) do {} while (0)
#endif
void solo_p2m_isr(struct solo_dev *solo_dev, int id) 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_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; struct solo_p2m_dev *p2m_dev;
int i; int i;
if (!(status & SOLO_PCI_ERR_P2M)) if (!(err & SOLO_PCI_ERR_P2M))
return; return;
for (i = 0; i < SOLO_NR_P2M; i++) { for (i = 0; i < SOLO_NR_P2M; i++) {
...@@ -280,6 +215,52 @@ void solo_p2m_exit(struct solo_dev *solo_dev) ...@@ -280,6 +215,52 @@ void solo_p2m_exit(struct solo_dev *solo_dev)
solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); 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) int solo_p2m_init(struct solo_dev *solo_dev)
{ {
struct solo_p2m_dev *p2m_dev; struct solo_p2m_dev *p2m_dev;
...@@ -294,13 +275,57 @@ int solo_p2m_init(struct solo_dev *solo_dev) ...@@ -294,13 +275,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_CONTROL(i), 0);
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
SOLO_P2M_CSC_16BIT_565 | SOLO_P2M_CSC_16BIT_565 |
SOLO_P2M_DMA_INTERVAL(3) |
SOLO_P2M_DESC_INTR_OPT | SOLO_P2M_DESC_INTR_OPT |
SOLO_P2M_DMA_INTERVAL(0) |
SOLO_P2M_PCI_MASTER_MODE); SOLO_P2M_PCI_MASTER_MODE);
solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); 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; return 0;
} }
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -22,18 +27,18 @@ ...@@ -22,18 +27,18 @@
#include "offsets.h" #include "offsets.h"
/* Global 6X10 system configuration */ /* Global 6010 system configuration */
#define SOLO_SYS_CFG 0x0000 #define SOLO_SYS_CFG 0x0000
#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */ #define SOLO_SYS_CFG_FOUT_EN 0x00000001
#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */ #define SOLO_SYS_CFG_PLL_BYPASS 0x00000002
#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */ #define SOLO_SYS_CFG_PLL_PWDN 0x00000004
#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */ #define SOLO_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3)
#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */ #define SOLO_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5)
#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */ #define SOLO_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14)
#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 #define SOLO_SYS_CFG_CLOCK_DIV 0x00080000
#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) #define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24)
#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) #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_SYS_CFG_RESET 0x80000000
#define SOLO_DMA_CTRL 0x0004 #define SOLO_DMA_CTRL 0x0004
...@@ -45,7 +50,9 @@ ...@@ -45,7 +50,9 @@
#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) #define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3)
#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) #define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2)
#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) #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_SYS_VCLK 0x000C
#define SOLO_VCLK_INVERT (1<<22) #define SOLO_VCLK_INVERT (1<<22)
...@@ -61,7 +68,7 @@ ...@@ -61,7 +68,7 @@
#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) #define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0)
#define SOLO_IRQ_STAT 0x0010 #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_P2M(n) (1<<((n)+17))
#define SOLO_IRQ_GPIO (1<<16) #define SOLO_IRQ_GPIO (1<<16)
#define SOLO_IRQ_VIDEO_LOSS (1<<15) #define SOLO_IRQ_VIDEO_LOSS (1<<15)
...@@ -82,22 +89,7 @@ ...@@ -82,22 +89,7 @@
#define SOLO_CHIP_OPTION 0x001C #define SOLO_CHIP_OPTION 0x001C
#define SOLO_CHIP_ID_MASK 0x00000007 #define SOLO_CHIP_ID_MASK 0x00000007
#define SOLO6110_PLL_CONFIG 0x0020 #define SOLO_PLL_CONFIG 0x0020 /* 6110 Only */
#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_EEPROM_CTRL 0x0060 #define SOLO_EEPROM_CTRL 0x0060
#define SOLO_EEPROM_ACCESS_EN (1<<7) #define SOLO_EEPROM_ACCESS_EN (1<<7)
...@@ -105,7 +97,7 @@ ...@@ -105,7 +97,7 @@
#define SOLO_EEPROM_CLK (1<<2) #define SOLO_EEPROM_CLK (1<<2)
#define SOLO_EEPROM_DO (1<<1) #define SOLO_EEPROM_DO (1<<1)
#define SOLO_EEPROM_DI (1<<0) #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 0x0070
#define SOLO_PCI_ERR_FATAL 0x00000001 #define SOLO_PCI_ERR_FATAL 0x00000001
...@@ -274,8 +266,8 @@ ...@@ -274,8 +266,8 @@
#define SOLO_VO_FI_CHANGE (1<<20) #define SOLO_VO_FI_CHANGE (1<<20)
#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) #define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19)
#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) #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<<17)
#define SOLO_VO_USER_COLOR_SET_NAH (1<<16) #define SOLO_VO_USER_COLOR_SET_NAV (1<<16)
#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) #define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8)
#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) #define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4)
#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) #define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0)
...@@ -401,12 +393,13 @@ ...@@ -401,12 +393,13 @@
#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) #define SOLO_VE_BLOCK_BASE(n) ((n)<<0)
#define SOLO_VE_CFG1 0x0614 #define SOLO_VE_CFG1 0x0614
#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */ #define SOLO_VE_BYTE_ALIGN(n) ((n)<<24)
#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */
#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */
#define SOLO_VE_INSERT_INDEX (1<<18) #define SOLO_VE_INSERT_INDEX (1<<18)
#define SOLO_VE_MOTION_MODE(n) ((n)<<16) #define SOLO_VE_MOTION_MODE(n) ((n)<<16)
#define SOLO_VE_MOTION_BASE(n) ((n)<<0) #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_WMRK_POLY 0x061C
#define SOLO_VE_VMRK_INIT_KEY 0x0620 #define SOLO_VE_VMRK_INIT_KEY 0x0620
...@@ -420,6 +413,7 @@ ...@@ -420,6 +413,7 @@
#define SOLO_COMP_TIME_INC(n) ((n)<<25) #define SOLO_COMP_TIME_INC(n) ((n)<<25)
#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) #define SOLO_COMP_TIME_WIDTH(n) ((n)<<21)
#define SOLO_DCT_INTERVAL(n) ((n)<<16) #define SOLO_DCT_INTERVAL(n) ((n)<<16)
#define SOLO_VE_COMPT_MOT 0x0634 /* 6110 Only */
#define SOLO_VE_STATE(n) (0x0640+((n)*4)) #define SOLO_VE_STATE(n) (0x0640+((n)*4))
...@@ -428,14 +422,21 @@ ...@@ -428,14 +422,21 @@
#define SOLO_VE_JPEG_QP_CH_H 0x0678 #define SOLO_VE_JPEG_QP_CH_H 0x0678
#define SOLO_VE_JPEG_CFG 0x067C #define SOLO_VE_JPEG_CFG 0x067C
#define SOLO_VE_JPEG_CTRL 0x0680 #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_CH 0x0690
#define SOLO_VE_OSD_BASE 0x0694 #define SOLO_VE_OSD_BASE 0x0694
#define SOLO_VE_OSD_CLR 0x0698 #define SOLO_VE_OSD_CLR 0x0698
#define SOLO_VE_OSD_OPT 0x069C #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 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(ch) (0x0780+((ch)*4))
#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) #define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4))
#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) #define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4))
...@@ -447,7 +448,7 @@ ...@@ -447,7 +448,7 @@
#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) #define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8))
#define SOLO_VD_CFG0 0x0900 #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_CODE (1<<23)
#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) #define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22)
#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) #define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21)
...@@ -599,9 +600,9 @@ ...@@ -599,9 +600,9 @@
#define SOLO_UART_RX_DATA_POP (1<<8) #define SOLO_UART_RX_DATA_POP (1<<8)
#define SOLO_TIMER_CLOCK_NUM 0x0be0 #define SOLO_TIMER_CLOCK_NUM 0x0be0
#define SOLO_TIMER_WATCHDOG 0x0be4
#define SOLO_TIMER_USEC 0x0be8 #define SOLO_TIMER_USEC 0x0be8
#define SOLO_TIMER_SEC 0x0bec #define SOLO_TIMER_SEC 0x0bec
#define SOLO_TIMER_USEC_LSB 0x0d20 /* 6110 Only */
#define SOLO_AUDIO_CONTROL 0x0D00 #define SOLO_AUDIO_CONTROL 0x0D00
#define SOLO_AUDIO_ENABLE (1<<31) #define SOLO_AUDIO_ENABLE (1<<31)
...@@ -629,9 +630,10 @@ ...@@ -629,9 +630,10 @@
#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) #define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10))
#define SOLO_AUDIO_STA 0x0D14 #define SOLO_AUDIO_STA 0x0D14
/*
#define SOLO_WATCHDOG 0x0BE4 * Watchdog configuration
#define WATCHDOG_STAT(status) (status<<8) */
#define WATCHDOG_TIME(sec) (sec&0xff) #define SOLO_WATCHDOG 0x0be4
#define SOLO_WATCHDOG_SET(status, sec) (status << 8 | (sec & 0xff))
#endif /* __SOLO6X10_REGISTERS_H */ #endif /* __SOLO6X10_REGISTERS_H */
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -20,7 +25,7 @@ ...@@ -20,7 +25,7 @@
#ifndef __SOLO6X10_JPEG_H #ifndef __SOLO6X10_JPEG_H
#define __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, 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c,
0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79,
0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16,
...@@ -102,4 +107,87 @@ static unsigned char jpeg_header[] = { ...@@ -102,4 +107,87 @@ static unsigned char jpeg_header[] = {
/* This is the byte marker for the start of SOF0: 0xffc0 marker */ /* This is the byte marker for the start of SOF0: 0xffc0 marker */
#define SOF0_START 575 #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 */ #endif /* __SOLO6X10_JPEG_H */
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -20,20 +25,20 @@ ...@@ -20,20 +25,20 @@
#ifndef __SOLO6X10_H #ifndef __SOLO6X10_H
#define __SOLO6X10_H #define __SOLO6X10_H
#include <linux/version.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/semaphore.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/delay.h> #include <linux/stringify.h>
#include <linux/slab.h> #include <linux/io.h>
#include <asm/io.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/slab.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <media/v4l2-dev.h> #include <media/v4l2-dev.h>
#include <media/videobuf-core.h> #include <media/videobuf-core.h>
#include "registers.h" #include "registers.h"
#ifndef PCI_VENDOR_ID_SOFTLOGIC #ifndef PCI_VENDOR_ID_SOFTLOGIC
...@@ -58,19 +63,24 @@ ...@@ -58,19 +63,24 @@
#define PCI_DEVICE_ID_BC_6110_16 0x5310 #define PCI_DEVICE_ID_BC_6110_16 0x5310
#endif /* Bluecherry */ #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 SOLO6X10_NAME "solo6x10"
#define SOLO_MAX_CHANNELS 16 #define SOLO_MAX_CHANNELS 16
/* Make sure these two match */ /* Make sure these two match */
#define SOLO6X10_VERSION "2.1.0"
#define SOLO6X10_VER_MAJOR 2 #define SOLO6X10_VER_MAJOR 2
#define SOLO6X10_VER_MINOR 0 #define SOLO6X10_VER_MINOR 4
#define SOLO6X10_VER_SUB 0 #define SOLO6X10_VER_SUB 4
#define SOLO6X10_VER_NUM \ #define SOLO6X10_VER_NUM \
KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB) KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB)
#define SOLO6X10_VERSION \
#define FLAGS_6110 1 __stringify(SOLO6X10_VER_MAJOR) "." \
__stringify(SOLO6X10_VER_MINOR) "." \
__stringify(SOLO6X10_VER_SUB)
/* /*
* The SOLO6x10 actually has 8 i2c channels, but we only use 2. * The SOLO6x10 actually has 8 i2c channels, but we only use 2.
...@@ -84,16 +94,7 @@ ...@@ -84,16 +94,7 @@
/* DMA Engine setup */ /* DMA Engine setup */
#define SOLO_NR_P2M 4 #define SOLO_NR_P2M 4
#define SOLO_NR_P2M_DESC 256 #define SOLO_NR_P2M_DESC 256
/* MPEG and JPEG share the same interrupt and locks so they must be together #define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16)
* 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
/* Encoder standard modes */ /* Encoder standard modes */
#define SOLO_ENC_MODE_CIF 2 #define SOLO_ENC_MODE_CIF 2
...@@ -103,12 +104,6 @@ ...@@ -103,12 +104,6 @@
#define SOLO_DEFAULT_GOP 30 #define SOLO_DEFAULT_GOP 30
#define SOLO_DEFAULT_QP 3 #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 #ifndef V4L2_BUF_FLAG_MOTION_ON
#define V4L2_BUF_FLAG_MOTION_ON 0x0400 #define V4L2_BUF_FLAG_MOTION_ON 0x0400
#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800 #define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800
...@@ -128,64 +123,67 @@ enum SOLO_I2C_STATE { ...@@ -128,64 +123,67 @@ enum SOLO_I2C_STATE {
IIC_STATE_STOP IIC_STATE_STOP
}; };
struct p2m_desc { /* Defined in Table 4-16, Page 68-69 of the 6010 Datasheet */
u32 ctrl; struct solo_p2m_desc {
u32 ext; u32 ctrl;
u32 ta; u32 cfg;
u32 fa; u32 dma_addr;
u32 ext_addr;
}; };
struct solo_p2m_dev { struct solo_p2m_dev {
struct mutex mutex; struct mutex mutex;
struct completion completion; struct completion completion;
int desc_count;
int desc_idx;
struct solo_p2m_desc *descs;
int error; int error;
}; };
#define OSD_TEXT_MAX 30 #define OSD_TEXT_MAX 44
enum solo_enc_types {
SOLO_ENC_TYPE_STD,
SOLO_ENC_TYPE_EXT,
};
struct solo_enc_dev { struct solo_enc_dev {
struct solo_dev *solo_dev; struct solo_dev *solo_dev;
/* V4L2 Items */ /* V4L2 Items */
struct video_device *vfd; struct video_device *vfd;
/* General accounting */ /* General accounting */
wait_queue_head_t thread_wait; struct mutex enable_lock;
spinlock_t lock; spinlock_t motion_lock;
atomic_t readers; atomic_t readers;
atomic_t mpeg_readers;
u8 ch; u8 ch;
u8 mode, gop, qp, interlaced, interval; u8 mode, gop, qp, interlaced, interval;
u8 reset_gop;
u8 bw_weight; u8 bw_weight;
u8 motion_detected;
u16 motion_thresh; u16 motion_thresh;
u16 width; u16 width;
u16 height; u16 height;
/* OSD buffers */
char osd_text[OSD_TEXT_MAX + 1]; char osd_text[OSD_TEXT_MAX + 1];
}; u8 osd_buf[SOLO_EOSD_EXT_SIZE_MAX]
__aligned(4);
struct solo_enc_buf { /* VOP stuff */
u8 vop; unsigned char vop[64];
u8 ch; int vop_len;
enum solo_enc_types type; unsigned char jpeg_header[1024];
u32 off; int jpeg_len;
u32 size;
u32 jpeg_off; /* File handles that are listening for buffers */
u32 jpeg_size; struct list_head listeners;
struct timeval ts;
}; };
/* The SOLO6x10 PCI Device */ /* The SOLO6x10 PCI Device */
struct solo_dev { struct solo_dev {
/* General stuff */ /* General stuff */
struct pci_dev *pdev; struct pci_dev *pdev;
int type;
unsigned int time_sync;
unsigned int usec_lsb;
unsigned int clock_mhz;
u8 __iomem *reg_base; u8 __iomem *reg_base;
int nr_chans; int nr_chans;
int nr_ext; int nr_ext;
u32 flags;
u32 irq_mask; u32 irq_mask;
u32 motion_mask; u32 motion_mask;
spinlock_t reg_io_lock; spinlock_t reg_io_lock;
...@@ -206,6 +204,9 @@ struct solo_dev { ...@@ -206,6 +204,9 @@ struct solo_dev {
/* P2M DMA Engine */ /* P2M DMA Engine */
struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; struct solo_p2m_dev p2m_dev[SOLO_NR_P2M];
atomic_t p2m_count;
int p2m_jiffies;
unsigned int p2m_timeouts;
/* V4L2 Display items */ /* V4L2 Display items */
struct video_device *vfd; struct video_device *vfd;
...@@ -219,9 +220,6 @@ struct solo_dev { ...@@ -219,9 +220,6 @@ struct solo_dev {
u16 enc_bw_remain; u16 enc_bw_remain;
/* IDX into hw mp4 encoder */ /* IDX into hw mp4 encoder */
u8 enc_idx; 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 */ /* Current video settings */
u32 video_type; u32 video_type;
...@@ -230,11 +228,32 @@ struct solo_dev { ...@@ -230,11 +228,32 @@ struct solo_dev {
u16 vin_hstart, vin_vstart; u16 vin_hstart, vin_vstart;
u8 fps; u8 fps;
/* JPEG Qp setting */
spinlock_t jpeg_qp_lock;
u32 jpeg_qp[2];
/* Audio components */ /* Audio components */
struct snd_card *snd_card; struct snd_card *snd_card;
struct snd_pcm *snd_pcm; struct snd_pcm *snd_pcm;
atomic_t snd_users; atomic_t snd_users;
int g723_hw_idx; 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) 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) ...@@ -255,7 +274,8 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg)
return ret; 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; unsigned long flags;
u16 val; u16 val;
...@@ -270,8 +290,17 @@ static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) ...@@ -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); spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags);
} }
void solo_irq_on(struct solo_dev *solo_dev, u32 mask); static inline void solo_irq_on(struct solo_dev *dev, u32 mask)
void solo_irq_off(struct solo_dev *solo_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 */ /* Init/exit routeines for subsystems */
int solo_disp_init(struct solo_dev *solo_dev); int solo_disp_init(struct solo_dev *solo_dev);
...@@ -286,13 +315,13 @@ void solo_i2c_exit(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); int solo_p2m_init(struct solo_dev *solo_dev);
void solo_p2m_exit(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); void solo_v4l2_exit(struct solo_dev *solo_dev);
int solo_enc_init(struct solo_dev *solo_dev); int solo_enc_init(struct solo_dev *solo_dev);
void solo_enc_exit(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); void solo_enc_v4l2_exit(struct solo_dev *solo_dev);
int solo_g723_init(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); ...@@ -301,7 +330,7 @@ void solo_g723_exit(struct solo_dev *solo_dev);
/* ISR's */ /* ISR's */
int solo_i2c_isr(struct solo_dev *solo_dev); int solo_i2c_isr(struct solo_dev *solo_dev);
void solo_p2m_isr(struct solo_dev *solo_dev, int id); 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_enc_v4l2_isr(struct solo_dev *solo_dev);
void solo_g723_isr(struct solo_dev *solo_dev); void solo_g723_isr(struct solo_dev *solo_dev);
void solo_motion_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, ...@@ -313,24 +342,39 @@ void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off,
u8 data); u8 data);
/* P2M DMA */ /* P2M DMA */
int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr,
dma_addr_t dma_addr, u32 ext_addr, u32 size); dma_addr_t dma_addr, u32 ext_addr, u32 size,
int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, int repeat, u32 ext_size);
void *sys_addr, u32 ext_addr, u32 size); int solo_p2m_dma(struct solo_dev *solo_dev, int wr,
int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, void *sys_addr, u32 ext_addr, u32 size,
struct p2m_desc *pdesc, int wr, int repeat, u32 ext_size);
struct scatterlist *sglist, u32 sg_off, void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr,
u32 ext_addr, u32 size); dma_addr_t dma_addr, u32 ext_addr, u32 size,
void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, int repeat, u32 ext_size);
u32 ext_addr, u32 size, int repeat, u32 ext_size); int solo_p2m_dma_desc(struct solo_dev *solo_dev,
int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, struct solo_p2m_desc *desc, dma_addr_t desc_dma,
struct p2m_desc *desc, int desc_count); int desc_cnt);
/* Set the threshold for motion detection */ /* 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 #define SOLO_DEF_MOT_THRESH 0x0300
/* Write text on OSD */ /* Write text on OSD */
int solo_osd_print(struct solo_enc_dev *solo_enc); 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 */ #endif /* __SOLO6X10_H */
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -18,12 +23,12 @@ ...@@ -18,12 +23,12 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h>
#include "solo6x10.h" #include "solo6x10.h"
#include "tw28.h" #include "tw28.h"
/* XXX: Some of these values are masked into an 8-bit regs, and shifted #define DEFAULT_HDELAY_NTSC (32 - 8)
* around for other 8-bit regs. What are the magic bits in these values? */
#define DEFAULT_HDELAY_NTSC (32 - 4)
#define DEFAULT_HACTIVE_NTSC (720 + 16) #define DEFAULT_HACTIVE_NTSC (720 + 16)
#define DEFAULT_VDELAY_NTSC (7 - 2) #define DEFAULT_VDELAY_NTSC (7 - 2)
#define DEFAULT_VACTIVE_NTSC (240 + 4) #define DEFAULT_VACTIVE_NTSC (240 + 4)
...@@ -33,15 +38,16 @@ ...@@ -33,15 +38,16 @@
#define DEFAULT_VDELAY_PAL (6) #define DEFAULT_VDELAY_PAL (6)
#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) #define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL)
static u8 tbl_tw2864_template[] = {
0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ static const u8 tbl_tw2864_ntsc_template[] = {
0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0x12, 0xf5, 0x09, 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, /* 0x40 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, /* 0x50 */
...@@ -61,14 +67,49 @@ static u8 tbl_tw2864_template[] = { ...@@ -61,14 +67,49 @@ static u8 tbl_tw2864_template[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, 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 */ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
...@@ -103,7 +144,7 @@ static u8 tbl_tw2865_ntsc_template[] = { ...@@ -103,7 +144,7 @@ static u8 tbl_tw2865_ntsc_template[] = {
0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, 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 */ 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ 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, ...@@ -180,8 +221,8 @@ static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
msleep_interruptible(1); msleep_interruptible(1);
} }
/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
addr, off, val); */ /* addr, off, val); */
} }
static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) 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) ...@@ -216,16 +257,17 @@ static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
for (i = 0; i < 0xff; i++) { for (i = 0; i < 0xff; i++) {
/* Skip read only registers */ /* Skip read only registers */
if (i >= 0xb8 && i <= 0xc1) switch (i) {
continue; case 0xb8 ... 0xc1:
if ((i & ~0x30) == 0x00 || case 0xc4 ... 0xc7:
(i & ~0x30) == 0x0c || case 0xfd:
(i & ~0x30) == 0x0d)
continue;
if (i >= 0xc4 && i <= 0xc7)
continue; continue;
if (i == 0xfd) }
switch (i & ~0x30) {
case 0x00:
case 0x0c ... 0x0d:
continue; continue;
}
tw_write_and_verify(solo_dev, dev_addr, i, tw_write_and_verify(solo_dev, dev_addr, i,
tbl_tw2865_common[i]); tbl_tw2865_common[i]);
...@@ -236,11 +278,15 @@ static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) ...@@ -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) 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; int i;
memcpy(tbl_tw2864_common, tbl_tw2864_template, if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
sizeof(tbl_tw2864_common)); 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) { if (solo_dev->tw2865 == 0) {
/* IRQ Mode */ /* IRQ Mode */
...@@ -285,33 +331,19 @@ static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) ...@@ -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++) { for (i = 0; i < 0xff; i++) {
/* Skip read only registers */ /* Skip read only registers */
if (i >= 0xb8 && i <= 0xc1) switch (i) {
continue; case 0xb8 ... 0xc1:
if ((i & ~0x30) == 0x00 || case 0xfd:
(i & ~0x30) == 0x0c ||
(i & ~0x30) == 0x0d)
continue;
if (i == 0x74 || i == 0x77 || i == 0x78 ||
i == 0x79 || i == 0x7a)
continue; continue;
if (i == 0xfd) }
switch (i & ~0x30) {
case 0x00:
case 0x0c:
case 0x0d:
continue; continue;
}
tw_write_and_verify(solo_dev, dev_addr, i, tw_write_and_verify(solo_dev, dev_addr, i,
tbl_tw2864_common[i]); tbl_tw2864_common[i]);
...@@ -544,8 +576,8 @@ int solo_tw28_init(struct solo_dev *solo_dev) ...@@ -544,8 +576,8 @@ int solo_tw28_init(struct solo_dev *solo_dev)
int i; int i;
u8 value; u8 value;
/* Detect techwell chip type */ /* Detect techwell chip type(s) */
for (i = 0; i < TW_NUM_CHIP; i++) { for (i = 0; i < solo_dev->nr_chans / 4; i++) {
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(i), 0xFF); TW_CHIP_OFFSET_ADDR(i), 0xFF);
...@@ -560,7 +592,8 @@ int solo_tw28_init(struct solo_dev *solo_dev) ...@@ -560,7 +592,8 @@ int solo_tw28_init(struct solo_dev *solo_dev)
break; break;
default: default:
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, 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) { if ((value >> 3) == 0x04) {
solo_dev->tw2815 |= 1 << i; solo_dev->tw2815 |= 1 << i;
solo_dev->tw28_cnt++; solo_dev->tw28_cnt++;
...@@ -568,8 +601,11 @@ int solo_tw28_init(struct solo_dev *solo_dev) ...@@ -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; return -EINVAL;
}
saa7128_setup(solo_dev); saa7128_setup(solo_dev);
...@@ -582,17 +618,6 @@ int solo_tw28_init(struct solo_dev *solo_dev) ...@@ -582,17 +618,6 @@ int solo_tw28_init(struct solo_dev *solo_dev)
tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); 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; return 0;
} }
...@@ -610,7 +635,7 @@ int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) ...@@ -610,7 +635,7 @@ int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
chip_num = ch / 4; chip_num = ch / 4;
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; TW_AV_STAT_ADDR) & 0x0f;
return val & (1 << ch) ? 1 : 0; return val & (1 << ch) ? 1 : 0;
...@@ -626,7 +651,7 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev) ...@@ -626,7 +651,7 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev)
int i; int i;
for (i = 0; i < solo_dev->tw28_cnt; 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; TW_AV_STAT_ADDR) & 0xf0) >> 4;
status |= val << (i * 4); status |= val << (i * 4);
} }
...@@ -635,7 +660,8 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev) ...@@ -635,7 +660,8 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev)
} }
#endif #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; char sval;
u8 chip_num; u8 chip_num;
...@@ -676,6 +702,7 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) ...@@ -676,6 +702,7 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val)
break; break;
case V4L2_CID_SATURATION: case V4L2_CID_SATURATION:
/* 286x chips have a U and V component for saturation */
if (is_tw286x(solo_dev, chip_num)) { if (is_tw286x(solo_dev, chip_num)) {
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num), TW_CHIP_OFFSET_ADDR(chip_num),
......
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -36,7 +41,7 @@ ...@@ -36,7 +41,7 @@
#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) #define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0))
/* tw286x */ /* tw286x */
#define TW286X_AV_STAT_ADDR 0xfd #define TW286x_AV_STAT_ADDR 0xfd
#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) #define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4))
#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) #define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4))
#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) #define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4))
......
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -21,33 +26,100 @@ ...@@ -21,33 +26,100 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/videobuf-dma-sg.h> #include <media/videobuf-dma-sg.h>
#include "solo6x10.h" #include "solo6x10.h"
#include "tw28.h" #include "tw28.h"
#include "solo6x10-jpeg.h" #include "solo6x10-jpeg.h"
#define MIN_VID_BUFFERS 4 #define MIN_VID_BUFFERS 2
#define FRAME_BUF_SIZE (128 * 1024) #define FRAME_BUF_SIZE (196 * 1024)
#define MP4_QS 16 #define MP4_QS 16
#define DMA_ALIGN 4096
static int solo_enc_thread(void *data); enum solo_enc_types {
SOLO_ENC_TYPE_STD,
extern unsigned video_nr; SOLO_ENC_TYPE_EXT,
};
struct solo_enc_fh { struct solo_enc_fh {
struct solo_enc_dev *enc; struct solo_enc_dev *enc;
u32 fmt; u32 fmt;
u16 rd_idx;
u8 enc_on; u8 enc_on;
enum solo_enc_types type; enum solo_enc_types type;
struct videobuf_queue vidq; struct videobuf_queue vidq;
struct list_head vidq_active; struct list_head vidq_active;
struct task_struct *kthread; int desc_count;
struct p2m_desc desc[SOLO_NR_P2M_DESC]; 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[] = { static const u32 solo_user_ctrls[] = {
V4L2_CID_BRIGHTNESS, V4L2_CID_BRIGHTNESS,
V4L2_CID_CONTRAST, V4L2_CID_CONTRAST,
...@@ -82,79 +154,179 @@ static const u32 *solo_ctrl_classes[] = { ...@@ -82,79 +154,179 @@ static const u32 *solo_ctrl_classes[] = {
NULL 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) static int solo_is_motion_on(struct solo_enc_dev *solo_enc)
{ {
struct solo_dev *solo_dev = solo_enc->solo_dev; struct solo_dev *solo_dev = solo_enc->solo_dev;
u8 ch = solo_enc->ch;
if (solo_dev->motion_mask & (1 << ch)) return (solo_dev->motion_mask >> solo_enc->ch) & 1;
return 1; }
return 0;
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) static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
{ {
struct solo_dev *solo_dev = solo_enc->solo_dev; 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) if (on)
solo_dev->motion_mask |= (1 << ch); solo_dev->motion_mask |= mask;
else 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_dev, SOLO_VI_MOT_CLEAR, mask);
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_ADR, solo_reg_write(solo_dev, SOLO_VI_MOT_ADR,
SOLO_VI_MOTION_EN(solo_dev->motion_mask) | SOLO_VI_MOTION_EN(solo_dev->motion_mask) |
(SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
if (solo_dev->motion_mask) spin_unlock_irqrestore(&solo_enc->motion_lock, flags);
solo_irq_on(solo_dev, SOLO_IRQ_MOTION);
else
solo_irq_off(solo_dev, SOLO_IRQ_MOTION);
spin_unlock(&solo_enc->lock);
} }
/* 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) static void solo_update_mode(struct solo_enc_dev *solo_enc)
{ {
struct solo_dev *solo_dev = solo_enc->solo_dev; struct solo_dev *solo_dev = solo_enc->solo_dev;
int vop_len;
assert_spin_locked(&solo_enc->lock); unsigned char *vop;
solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0;
solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1);
switch (solo_enc->mode) { if (solo_enc->mode == SOLO_ENC_MODE_CIF) {
case SOLO_ENC_MODE_CIF:
solo_enc->width = solo_dev->video_hsize >> 1; solo_enc->width = solo_dev->video_hsize >> 1;
solo_enc->height = solo_dev->video_vsize; solo_enc->height = solo_dev->video_vsize;
break; if (solo_dev->type == SOLO_DEV_6110) {
case SOLO_ENC_MODE_D1: 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->width = solo_dev->video_hsize;
solo_enc->height = solo_dev->video_vsize << 1; solo_enc->height = solo_dev->video_vsize << 1;
solo_enc->bw_weight <<= 2; solo_enc->bw_weight <<= 2;
break; if (solo_dev->type == SOLO_DEV_6110) {
default: if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {
WARN(1, "mode is unknown\n"); 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 */ /* 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_fh *fh)
{ {
struct solo_enc_dev *solo_enc = fh->enc; struct solo_enc_dev *solo_enc = fh->enc;
u8 ch = solo_enc->ch; u8 ch = solo_enc->ch;
struct solo_dev *solo_dev = solo_enc->solo_dev; struct solo_dev *solo_dev = solo_enc->solo_dev;
u8 interval; u8 interval;
assert_spin_locked(&solo_enc->lock); BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
if (fh->enc_on) if (fh->enc_on)
return 0; return 0;
...@@ -170,13 +342,20 @@ static int solo_enc_on(struct solo_enc_fh *fh) ...@@ -170,13 +342,20 @@ static int solo_enc_on(struct solo_enc_fh *fh)
} }
fh->enc_on = 1; 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) if (fh->type == SOLO_ENC_TYPE_EXT)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); 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; return 0;
}
/* Disable all encoding for this channel */ /* Disable all encoding for this channel */
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); 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) ...@@ -203,701 +382,414 @@ static int solo_enc_on(struct solo_enc_fh *fh)
/* Enables the standard encoder */ /* Enables the standard encoder */
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode);
/* Settle down Beavis... */
mdelay(10);
return 0; 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_enc_dev *solo_enc = fh->enc;
struct solo_dev *solo_dev = solo_enc->solo_dev; int ret;
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;
if (atomic_dec_return(&solo_enc->readers) > 0) mutex_lock(&solo_enc->enable_lock);
return; ret = __solo_enc_on(fh);
mutex_unlock(&solo_enc->enable_lock);
solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); return ret;
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0);
} }
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_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"); BUG_ON(!mutex_is_locked(&solo_enc->enable_lock));
/* 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);
}
return 0; if (!fh->enc_on)
} return;
static void enc_reset_gop(struct solo_dev *solo_dev, u8 ch) list_del(&fh->list);
{ fh->enc_on = 0;
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;
}
static int enc_gop_reset(struct solo_dev *solo_dev, u8 ch, u8 vop) if (fh->fmt == V4L2_PIX_FMT_MPEG)
{ atomic_dec(&solo_enc->mpeg_readers);
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;
}
static void enc_write_sg(struct scatterlist *sglist, void *buf, int size) if (atomic_dec_return(&solo_enc->readers) > 0)
{ return;
struct scatterlist *sg;
u8 *src = buf;
for (sg = sglist; sg && size > 0; sg = sg_next(sg)) { solo_dev->enc_bw_remain += solo_enc->bw_weight;
u8 *p = sg_virt(sg);
size_t len = sg_dma_len(sg);
int i;
for (i = 0; i < len && size; i++) solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0);
p[i] = *(src++); 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, static void solo_enc_off(struct solo_enc_fh *fh)
struct p2m_desc *desc,
struct scatterlist *sglist, int skip,
unsigned int off, unsigned int size)
{ {
int ret; struct solo_enc_dev *solo_enc = fh->enc;
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));
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, static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma,
dma_addr_t buf, unsigned int off, unsigned int off, unsigned int size)
unsigned int size)
{ {
int ret; int ret;
if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
return -EINVAL; return -EINVAL;
/* Single shot */
if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, return solo_p2m_dma_t(solo_dev, 0, dma,
SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); SOLO_MP4E_EXT_ADDR(solo_dev) + off, size,
0, 0);
} }
/* Buffer wrap */ /* 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_ADDR(solo_dev) + off,
SOLO_MP4E_EXT_SIZE(solo_dev) - off); SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0);
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;
}
static int enc_get_mpeg_dma(struct solo_dev *solo_dev, void *buf, if (!ret) {
unsigned int off, unsigned int size) ret = solo_p2m_dma_t(solo_dev, 0,
{ dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
int ret; SOLO_MP4E_EXT_ADDR(solo_dev),
size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0);
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);
return ret; return ret;
} }
static int enc_get_jpeg_dma_sg(struct solo_dev *solo_dev, /* Build a descriptor queue out of an SG list and send it to the P2M for
struct p2m_desc *desc, * processing. */
struct scatterlist *sglist, int skip, static int solo_send_desc(struct solo_enc_fh *fh, int skip,
unsigned int off, unsigned int size) 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; int ret;
if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) if (WARN_ON_ONCE(size > FRAME_BUF_SIZE))
return -EINVAL; return -EINVAL;
if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) { fh->desc_count = 1;
return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG,
desc, 0, sglist, skip, for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); struct solo_p2m_desc *desc;
} dma_addr_t dma;
int len;
/* Buffer wrap */ int left = base_size - off;
ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off, desc = &fh->desc_items[fh->desc_count++];
SOLO_JPEG_EXT_SIZE(solo_dev) - off); 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, len = min(len, size);
sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
SOLO_JPEG_EXT_ADDR(solo_dev),
size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
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 */ size -= len;
#define OFF_IN_RANGE(__off, __range, __chk) \ if (size <= 0)
((__off <= __chk) && ((__off + __range) >= __chk)) break;
static void solo_jpeg_header(struct solo_enc_dev *solo_enc, off += len;
struct videobuf_dmabuf *vbuf) if (off >= base_size)
{ off -= base_size;
struct scatterlist *sg;
void *src = jpeg_header; /* Because we may use two descriptors per loop */
size_t copied = 0; if (fh->desc_count >= (fh->desc_nelts - 1)) {
size_t to_copy = sizeof(jpeg_header); ret = solo_p2m_dma_desc(solo_dev, fh->desc_items,
fh->desc_dma,
for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) { fh->desc_count - 1);
size_t this_copy = min(sg_dma_len(sg), if (ret)
(unsigned int)(to_copy - copied)); return ret;
u8 *p = sg_virt(sg); fh->desc_count = 1;
}
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;
} }
}
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) if (fh->desc_count <= 1)
{ return 0;
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
while (count--) { return solo_p2m_dma_desc(solo_dev, fh->desc_items, fh->desc_dma,
**out <<= 1; fh->desc_count - 1);
**out |= !!(value & (1 << 31)); /* MSB */
value <<= 1;
if (++(*bits) % 8 == 0)
(*out)++;
}
} }
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) { svb->flags |= V4L2_BUF_FLAG_KEYFRAME;
max = (max + 2) * 2 - 2;
cnt++;
}
write_bits(out, bits, 1, cnt + 1);
write_bits(out, bits, ~(max - value), cnt);
}
static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */ if (vb->bsize < (vh->jpeg_size + solo_enc->jpeg_len))
{ return -EIO;
if (value <= 0)
write_ue(out, bits, -value * 2);
else
write_ue(out, bits, value * 2 - 1);
}
static void write_mpeg4_end(u8 **out, unsigned *bits) vb->width = solo_enc->width;
{ vb->height = solo_enc->height;
write_bits(out, bits, 0, 1); vb->size = vh->jpeg_size + solo_enc->jpeg_len;
/* align on 32-bit boundary */
if (*bits % 32)
write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32);
}
static void write_h264_end(u8 **out, unsigned *bits, int align) sg_copy_from_buffer(vbuf->sglist, vbuf->sglen,
{ solo_enc->jpeg_header,
write_bits(out, bits, 1, 1); solo_enc->jpeg_len);
while ((*bits) % 8)
write_bits(out, bits, 0, 1);
if (align)
while ((*bits) % 32)
write_bits(out, bits, 0, 1);
}
static void mpeg4_write_vol(u8 **out, struct solo_dev *solo_dev, frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1))
__le32 *vh, unsigned fps, unsigned interval) & ~(DMA_ALIGN - 1);
{
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);
}
static void h264_write_vol(u8 **out, struct solo_dev *solo_dev, __le32 *vh) return solo_send_desc(fh, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
{ frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
static const u8 sps[] = { SOLO_JPEG_EXT_SIZE(solo_dev));
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);
} }
static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, static int solo_fill_mpeg(struct solo_enc_fh *fh, struct videobuf_buffer *vb,
struct videobuf_buffer *vb, struct videobuf_dmabuf *vbuf, struct vop_header *vh)
struct videobuf_dmabuf *vbuf)
{ {
struct solo_enc_dev *solo_enc = fh->enc; struct solo_enc_dev *solo_enc = fh->enc;
struct solo_dev *solo_dev = solo_enc->solo_dev; struct solo_dev *solo_dev = solo_enc->solo_dev;
struct solo_videobuf *svb = (struct solo_videobuf *)vb;
#define VH_WORDS 16 int frame_off, frame_size;
#define MAX_VOL_HEADER_LENGTH 64
__le32 vh[VH_WORDS];
int ret;
int frame_size, frame_off;
int skip = 0; int skip = 0;
if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) if (vb->bsize < vh->mpeg_size)
return -EINVAL; return -EIO;
/* 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 (WARN_ON_ONCE(vop_size(vh) > enc_buf->size)) vb->width = vh->hsize << 4;
return -EINVAL; vb->height = vh->vsize << 4;
vb->size = vh->mpeg_size;
vb->width = vop_hsize(vh) << 4; /* If this is a key frame, add extra header */
vb->height = vop_vsize(vh) << 4; if (!vh->vop_type) {
vb->size = vop_size(vh); 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 */ skip = solo_enc->vop_len;
if (!enc_buf->vop) { vb->size += solo_enc->vop_len;
u8 header[MAX_VOL_HEADER_LENGTH], *out = header;
if (solo_dev->flags & FLAGS_6110) svb->flags |= V4L2_BUF_FLAG_KEYFRAME;
h264_write_vol(&out, solo_dev, vh); } else {
else svb->flags |= V4L2_BUF_FLAG_PFRAME;
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;
} }
/* Now get the actual mpeg payload */ /* Now get the actual mpeg payload */
frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); frame_off = (vh->mpeg_off + sizeof(*vh))
frame_size = enc_buf->size - sizeof(vh); % SOLO_MP4E_EXT_SIZE(solo_dev);
frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1))
ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, & ~(DMA_ALIGN - 1);
skip, frame_off, frame_size);
WARN_ON_ONCE(ret);
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, static int solo_enc_fillbuf(struct solo_enc_fh *fh,
struct videobuf_buffer *vb) struct videobuf_buffer *vb,
struct solo_enc_buf *enc_buf)
{ {
struct solo_enc_dev *solo_enc = fh->enc; 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;
struct solo_enc_buf *enc_buf = NULL; struct videobuf_dmabuf *vbuf = NULL;
struct videobuf_dmabuf *vbuf; struct vop_header *vh = enc_buf->vh;
int ret; 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; vbuf = videobuf_to_dma(vb);
if (WARN_ON_ONCE(!vbuf)) {
if (ebuf->ch != solo_enc->ch) ret = -EIO;
continue; goto vbuf_error;
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;
}
} }
fh->rd_idx = idx; /* Setup some common flags for both types */
svb->flags = V4L2_BUF_FLAG_TIMECODE;
if (WARN_ON_ONCE(!enc_buf)) vb->ts.tv_sec = vh->sec;
goto buf_err; vb->ts.tv_usec = vh->usec;
if ((fh->fmt == V4L2_PIX_FMT_MPEG && /* Check for motion flags */
vb->bsize < enc_buf->size) || if (solo_is_motion_on(solo_enc)) {
(fh->fmt == V4L2_PIX_FMT_MJPEG && svb->flags |= V4L2_BUF_FLAG_MOTION_ON;
vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { if (enc_buf->motion)
WARN_ON_ONCE(1); svb->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
goto buf_err;
} }
vbuf = videobuf_to_dma(vb);
if (WARN_ON_ONCE(!vbuf))
goto buf_err;
if (fh->fmt == V4L2_PIX_FMT_MPEG) 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 else
ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); ret = solo_fill_jpeg(fh, vb, vbuf, vh);
if (!ret) vbuf_error:
error = 0; /* 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: spin_lock_irqsave(&fh->av_lock, flags);
if (error) { list_add(&vb->queue, &fh->vidq_active);
vb->state = VIDEOBUF_ERROR; vb->state = VIDEOBUF_QUEUED;
spin_unlock_irqrestore(&fh->av_lock, flags);
} else { } else {
vb->field_count++;
vb->ts = enc_buf->ts;
vb->state = VIDEOBUF_DONE; 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_enc_fh *fh;
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct videobuf_buffer *vb;
for (;;) { mutex_lock(&solo_enc->enable_lock);
spin_lock(&solo_enc->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)) if (list_empty(&fh->vidq_active))
break; continue;
spin_lock_irqsave(&fh->av_lock, flags);
vb = list_first_entry(&fh->vidq_active, vb = list_first_entry(&fh->vidq_active,
struct videobuf_buffer, queue); struct videobuf_buffer, queue);
if (!waitqueue_active(&vb->done))
break;
list_del(&vb->queue); list_del(&vb->queue);
vb->state = VIDEOBUF_ACTIVE;
spin_unlock(&solo_enc->lock); spin_unlock_irqrestore(&fh->av_lock, flags);
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);
set_freezable(); solo_enc_fillbuf(fh, vb, enc_buf);
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();
} }
remove_wait_queue(&solo_enc->thread_wait, &wait); mutex_unlock(&solo_enc->enable_lock);
return 0;
} }
void solo_motion_isr(struct solo_dev *solo_dev) void solo_enc_v4l2_isr(struct solo_dev *solo_dev)
{ {
u32 status; wake_up_interruptible_all(&solo_dev->ring_thread_wait);
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;
}
} }
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; for (;;) {
u32 mpeg_current, mpeg_next, mpeg_size; struct solo_enc_dev *solo_enc;
u32 jpeg_current, jpeg_next, jpeg_size; struct solo_enc_buf enc_buf;
u32 reg_mpeg_size; u32 mpeg_current, off;
u8 cur_q, vop_type; u8 ch;
u8 ch; u8 cur_q;
enum solo_enc_types enc_type;
/* Check if the hardware has any new ones in the queue */
solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER); cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff;
if (cur_q == solo_dev->enc_idx)
cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS; break;
reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7;
while (solo_dev->enc_idx != cur_q) {
mpeg_current = solo_reg_read(solo_dev, mpeg_current = solo_reg_read(solo_dev,
SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); 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; 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; ch = (mpeg_current >> 24) & 0x1f;
off = mpeg_current & 0x00ffffff;
if (ch >= SOLO_MAX_CHANNELS) { if (ch >= SOLO_MAX_CHANNELS) {
ch -= SOLO_MAX_CHANNELS; ch -= SOLO_MAX_CHANNELS;
enc_type = SOLO_ENC_TYPE_EXT; enc_buf.type = SOLO_ENC_TYPE_EXT;
} else } else
enc_type = SOLO_ENC_TYPE_STD; enc_buf.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);
/* XXX I think this means we had a ring overflow? */ solo_enc = solo_dev->v4l2_enc[ch];
if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) { if (solo_enc == NULL) {
enc_reset_gop(solo_dev, ch); dev_err(&solo_dev->pdev->dev,
"Got spurious packet for channel %d\n", ch);
continue; continue;
} }
/* When resetting the GOP, skip frames until I-frame */ /* FAIL... */
if (enc_gop_reset(solo_dev, ch, vop_type)) if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off,
sizeof(struct vop_header)))
continue; 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; /* Sanity check */
enc_buf->ch = ch; if (enc_buf.vh->mpeg_off != off)
enc_buf->off = mpeg_current; continue;
enc_buf->size = mpeg_size;
enc_buf->jpeg_off = jpeg_current; if (solo_motion_detected(solo_enc))
enc_buf->jpeg_size = jpeg_size; enc_buf.motion = 1;
enc_buf->type = enc_type; 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) % set_freezable();
SOLO_NR_RING_BUFS; 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, 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, ...@@ -915,17 +807,12 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb, struct videobuf_buffer *vb,
enum v4l2_field field) 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; vb->size = FRAME_BUF_SIZE;
if (vb->baddr != 0 && vb->bsize < vb->size) if (vb->baddr != 0 && vb->bsize < vb->size)
return -EINVAL; return -EINVAL;
/* These properties only change when queue is idle */ /* This property only change when queue is idle */
vb->width = solo_enc->width; vb->field = field;
vb->height = solo_enc->height;
vb->field = field;
if (vb->state == VIDEOBUF_NEEDS_INIT) { if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL); int rc = videobuf_iolock(vq, vb, NULL);
...@@ -933,7 +820,7 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq, ...@@ -933,7 +820,7 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq,
struct videobuf_dmabuf *dma = videobuf_to_dma(vb); struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq->dev, dma); videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma); videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
return rc; return rc;
} }
} }
...@@ -949,20 +836,18 @@ static void solo_enc_buf_queue(struct videobuf_queue *vq, ...@@ -949,20 +836,18 @@ static void solo_enc_buf_queue(struct videobuf_queue *vq,
vb->state = VIDEOBUF_QUEUED; vb->state = VIDEOBUF_QUEUED;
list_add_tail(&vb->queue, &fh->vidq_active); 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, static void solo_enc_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
struct videobuf_dmabuf *dma = videobuf_to_dma(vb); struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq->dev, dma); videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma); videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT; 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_setup = solo_enc_buf_setup,
.buf_prepare = solo_enc_buf_prepare, .buf_prepare = solo_enc_buf_prepare,
.buf_queue = solo_enc_buf_queue, .buf_queue = solo_enc_buf_queue,
...@@ -984,27 +869,78 @@ static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -984,27 +869,78 @@ static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma)
return videobuf_mmap_mapper(&fh->vidq, 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) static int solo_enc_open(struct file *file)
{ {
struct solo_enc_dev *solo_enc = video_drvdata(file); struct solo_enc_dev *solo_enc = video_drvdata(file);
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct solo_enc_fh *fh; struct solo_enc_fh *fh;
int ret;
ret = solo_ring_start(solo_dev);
if (ret)
return ret;
fh = kzalloc(sizeof(*fh), GFP_KERNEL); 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; return -ENOMEM;
}
fh->enc = solo_enc; fh->enc = solo_enc;
spin_lock_init(&fh->av_lock);
file->private_data = fh; file->private_data = fh;
INIT_LIST_HEAD(&fh->vidq_active); INIT_LIST_HEAD(&fh->vidq_active);
fh->fmt = V4L2_PIX_FMT_MPEG; fh->fmt = V4L2_PIX_FMT_MPEG;
fh->type = SOLO_ENC_TYPE_STD; fh->type = SOLO_ENC_TYPE_STD;
videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops, videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
&solo_enc->solo_dev->pdev->dev, &solo_dev->pdev->dev,
&solo_enc->lock, &fh->av_lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED, V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), fh, NULL); sizeof(struct solo_videobuf),
fh, NULL);
return 0; return 0;
} }
...@@ -1013,22 +949,12 @@ static ssize_t solo_enc_read(struct file *file, char __user *data, ...@@ -1013,22 +949,12 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct solo_enc_fh *fh = file->private_data; struct solo_enc_fh *fh = file->private_data;
struct solo_enc_dev *solo_enc = fh->enc; int ret;
/* Make sure the encoder is on */ /* Make sure the encoder is on */
if (!fh->enc_on) { ret = solo_enc_on(fh);
int ret; if (ret)
return 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;
}
return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK); file->f_flags & O_NONBLOCK);
...@@ -1037,17 +963,21 @@ static ssize_t solo_enc_read(struct file *file, char __user *data, ...@@ -1037,17 +963,21 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
static int solo_enc_release(struct file *file) static int solo_enc_release(struct file *file)
{ {
struct solo_enc_fh *fh = file->private_data; 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_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq); videobuf_mmap_free(&fh->vidq);
spin_lock(&solo_enc->lock); pci_free_consistent(fh->enc->solo_dev->pdev,
solo_enc_off(fh); sizeof(struct solo_p2m_desc) *
spin_unlock(&solo_enc->lock); fh->desc_nelts, fh->desc_items, fh->desc_dma);
kfree(fh); kfree(fh);
solo_ring_stop(solo_dev);
return 0; return 0;
} }
...@@ -1095,7 +1025,8 @@ static int solo_enc_enum_input(struct file *file, void *priv, ...@@ -1095,7 +1025,8 @@ static int solo_enc_enum_input(struct file *file, void *priv,
return 0; 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) if (index)
return -EINVAL; return -EINVAL;
...@@ -1144,8 +1075,8 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv, ...@@ -1144,8 +1075,8 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv,
pix->pixelformat != V4L2_PIX_FMT_MJPEG) pix->pixelformat != V4L2_PIX_FMT_MJPEG)
return -EINVAL; return -EINVAL;
/* We cannot change width/height in mid read */ /* We cannot change width/height in mid mpeg */
if (atomic_read(&solo_enc->readers) > 0) { if (atomic_read(&solo_enc->mpeg_readers) > 0) {
if (pix->width != solo_enc->width || if (pix->width != solo_enc->width ||
pix->height != solo_enc->height) pix->height != solo_enc->height)
return -EBUSY; return -EBUSY;
...@@ -1183,11 +1114,11 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv, ...@@ -1183,11 +1114,11 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_pix_format *pix = &f->fmt.pix;
int ret; int ret;
spin_lock(&solo_enc->lock); mutex_lock(&solo_enc->enable_lock);
ret = solo_enc_try_fmt_cap(file, priv, f); ret = solo_enc_try_fmt_cap(file, priv, f);
if (ret) { if (ret) {
spin_unlock(&solo_enc->lock); mutex_unlock(&solo_enc->enable_lock);
return ret; return ret;
} }
...@@ -1201,14 +1132,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv, ...@@ -1201,14 +1132,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
if (pix->priv) if (pix->priv)
fh->type = SOLO_ENC_TYPE_EXT; fh->type = SOLO_ENC_TYPE_EXT;
ret = solo_enc_on(fh);
spin_unlock(&solo_enc->lock);
if (ret) mutex_unlock(&solo_enc->enable_lock);
return ret;
return solo_start_fh_thread(fh); return 0;
} }
static int solo_enc_get_fmt_cap(struct file *file, void *priv, 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, ...@@ -1245,7 +1172,8 @@ static int solo_enc_querybuf(struct file *file, void *priv,
return videobuf_querybuf(&fh->vidq, buf); 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; struct solo_enc_fh *fh = priv;
...@@ -1256,50 +1184,21 @@ static int solo_enc_dqbuf(struct file *file, void *priv, ...@@ -1256,50 +1184,21 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf) struct v4l2_buffer *buf)
{ {
struct solo_enc_fh *fh = priv; struct solo_enc_fh *fh = priv;
struct solo_enc_dev *solo_enc = fh->enc; struct solo_videobuf *svb;
int ret; int ret;
/* Make sure the encoder is on */ /* Make sure the encoder is on */
if (!fh->enc_on) { ret = solo_enc_on(fh);
spin_lock(&solo_enc->lock); if (ret)
ret = solo_enc_on(fh); return ret;
spin_unlock(&solo_enc->lock);
if (ret)
return ret;
ret = solo_start_fh_thread(fh);
if (ret)
return ret;
}
ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
if (ret) if (ret)
return ret; return ret;
/* Signal motion detection */ /* Copy over the flags */
if (solo_is_motion_on(solo_enc)) { svb = (struct solo_videobuf *)fh->vidq.bufs[buf->index];
buf->flags |= V4L2_BUF_FLAG_MOTION_ON; buf->flags |= svb->flags;
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;
}
}
return 0; return 0;
} }
...@@ -1319,11 +1218,16 @@ static int solo_enc_streamoff(struct file *file, void *priv, ...@@ -1319,11 +1218,16 @@ static int solo_enc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type i) enum v4l2_buf_type i)
{ {
struct solo_enc_fh *fh = priv; struct solo_enc_fh *fh = priv;
int ret;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL; 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) 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, ...@@ -1407,10 +1311,10 @@ static int solo_s_parm(struct file *file, void *priv,
struct solo_dev *solo_dev = solo_enc->solo_dev; struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_captureparm *cp = &sp->parm.capture; 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) { if (atomic_read(&solo_enc->mpeg_readers) > 0) {
spin_unlock(&solo_enc->lock); mutex_unlock(&solo_enc->enable_lock);
return -EBUSY; return -EBUSY;
} }
...@@ -1431,10 +1335,9 @@ static int solo_s_parm(struct file *file, void *priv, ...@@ -1431,10 +1335,9 @@ static int solo_s_parm(struct file *file, void *priv,
cp->capability = V4L2_CAP_TIMEPERFRAME; cp->capability = V4L2_CAP_TIMEPERFRAME;
solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1);
solo_update_mode(solo_enc); solo_update_mode(solo_enc);
spin_unlock(&solo_enc->lock); mutex_unlock(&solo_enc->enable_lock);
return 0; return 0;
} }
...@@ -1510,6 +1413,7 @@ static int solo_querymenu(struct file *file, void *priv, ...@@ -1510,6 +1413,7 @@ static int solo_querymenu(struct file *file, void *priv,
int err; int err;
qctrl.id = qmenu->id; qctrl.id = qmenu->id;
err = solo_queryctrl(file, priv, &qctrl); err = solo_queryctrl(file, priv, &qctrl);
if (err) if (err)
return err; return err;
...@@ -1536,6 +1440,8 @@ static int solo_g_ctrl(struct file *file, void *priv, ...@@ -1536,6 +1440,8 @@ static int solo_g_ctrl(struct file *file, void *priv,
ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
break; break;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE: case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
if (atomic_read(&solo_enc->readers) > 0)
return -EBUSY;
ctrl->value = solo_enc->gop; ctrl->value = solo_enc->gop;
break; break;
case V4L2_CID_MOTION_THRESHOLD: case V4L2_CID_MOTION_THRESHOLD:
...@@ -1574,19 +1480,32 @@ static int solo_s_ctrl(struct file *file, void *priv, ...@@ -1574,19 +1480,32 @@ static int solo_s_ctrl(struct file *file, void *priv,
if (ctrl->value < 1 || ctrl->value > 255) if (ctrl->value < 1 || ctrl->value > 255)
return -ERANGE; return -ERANGE;
solo_enc->gop = ctrl->value; 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; break;
case V4L2_CID_MOTION_THRESHOLD: 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 */ u16 block = (ctrl->value >> 16) & 0xffff;
if (ctrl->value < 0 || ctrl->value > 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; 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; break;
}
case V4L2_CID_MOTION_ENABLE: case V4L2_CID_MOTION_ENABLE:
solo_motion_toggle(solo_enc, ctrl->value); solo_motion_toggle(solo_enc, ctrl->value);
break; break;
...@@ -1613,6 +1532,7 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, ...@@ -1613,6 +1532,7 @@ static int solo_s_ext_ctrls(struct file *file, void *priv,
if (ctrl->size - 1 > OSD_TEXT_MAX) if (ctrl->size - 1 > OSD_TEXT_MAX)
err = -ERANGE; err = -ERANGE;
else { else {
mutex_lock(&solo_enc->enable_lock);
err = copy_from_user(solo_enc->osd_text, err = copy_from_user(solo_enc->osd_text,
ctrl->string, ctrl->string,
OSD_TEXT_MAX); OSD_TEXT_MAX);
...@@ -1621,6 +1541,7 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, ...@@ -1621,6 +1541,7 @@ static int solo_s_ext_ctrls(struct file *file, void *priv,
err = solo_osd_print(solo_enc); err = solo_osd_print(solo_enc);
else else
err = -EFAULT; err = -EFAULT;
mutex_unlock(&solo_enc->enable_lock);
} }
break; break;
default: default:
...@@ -1653,11 +1574,13 @@ static int solo_g_ext_ctrls(struct file *file, void *priv, ...@@ -1653,11 +1574,13 @@ static int solo_g_ext_ctrls(struct file *file, void *priv,
ctrl->size = OSD_TEXT_MAX; ctrl->size = OSD_TEXT_MAX;
err = -ENOSPC; err = -ENOSPC;
} else { } else {
mutex_lock(&solo_enc->enable_lock);
err = copy_to_user(ctrl->string, err = copy_to_user(ctrl->string,
solo_enc->osd_text, solo_enc->osd_text,
OSD_TEXT_MAX); OSD_TEXT_MAX);
if (err) if (err)
err = -EFAULT; err = -EFAULT;
mutex_unlock(&solo_enc->enable_lock);
} }
break; break;
default: default:
...@@ -1717,7 +1640,7 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { ...@@ -1717,7 +1640,7 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = {
.vidioc_s_ext_ctrls = solo_s_ext_ctrls, .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, .name = SOLO6X10_NAME,
.fops = &solo_enc_fops, .fops = &solo_enc_fops,
.ioctl_ops = &solo_enc_ioctl_ops, .ioctl_ops = &solo_enc_ioctl_ops,
...@@ -1728,7 +1651,8 @@ static struct video_device solo_enc_template = { ...@@ -1728,7 +1651,8 @@ static struct video_device solo_enc_template = {
.current_norm = V4L2_STD_NTSC_M, .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; struct solo_enc_dev *solo_enc;
int ret; int ret;
...@@ -1748,8 +1672,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch) ...@@ -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 = solo_enc_template;
solo_enc->vfd->parent = &solo_dev->pdev->dev; solo_enc->vfd->parent = &solo_dev->pdev->dev;
ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr);
video_nr);
if (ret < 0) { if (ret < 0) {
video_device_release(solo_enc->vfd); video_device_release(solo_enc->vfd);
kfree(solo_enc); kfree(solo_enc);
...@@ -1762,12 +1685,12 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch) ...@@ -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, "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num,
solo_enc->vfd->num); solo_enc->vfd->num);
if (video_nr != -1) INIT_LIST_HEAD(&solo_enc->listeners);
video_nr++; 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->readers, 0);
atomic_set(&solo_enc->mpeg_readers, 0);
solo_enc->qp = SOLO_DEFAULT_QP; solo_enc->qp = SOLO_DEFAULT_QP;
solo_enc->gop = solo_dev->fps; 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) ...@@ -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->mode = SOLO_ENC_MODE_CIF;
solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;
spin_lock(&solo_enc->lock); mutex_lock(&solo_enc->enable_lock);
solo_update_mode(solo_enc); 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; return solo_enc;
} }
...@@ -1791,12 +1718,22 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc) ...@@ -1791,12 +1718,22 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc)
kfree(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; 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++) { 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])) if (IS_ERR(solo_dev->v4l2_enc[i]))
break; break;
} }
...@@ -1805,11 +1742,15 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev) ...@@ -1805,11 +1742,15 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev)
int ret = PTR_ERR(solo_dev->v4l2_enc[i]); int ret = PTR_ERR(solo_dev->v4l2_enc[i]);
while (i--) while (i--)
solo_enc_free(solo_dev->v4l2_enc[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; return ret;
} }
/* D1@MAX-FPS * 4 */ if (solo_dev->type == SOLO_DEV_6010)
solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; 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", dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n",
solo_dev->v4l2_enc[0]->vfd->num, solo_dev->v4l2_enc[0]->vfd->num,
...@@ -1822,8 +1763,9 @@ void solo_enc_v4l2_exit(struct solo_dev *solo_dev) ...@@ -1822,8 +1763,9 @@ void solo_enc_v4l2_exit(struct solo_dev *solo_dev)
{ {
int i; int i;
solo_irq_off(solo_dev, SOLO_IRQ_MOTION);
for (i = 0; i < solo_dev->nr_chans; i++) for (i = 0; i < solo_dev->nr_chans; i++)
solo_enc_free(solo_dev->v4l2_enc[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);
} }
/* /*
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> *
* 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -21,47 +26,43 @@ ...@@ -21,47 +26,43 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/videobuf-dma-sg.h> #include <media/videobuf-dma-contig.h>
#include "solo6x10.h" #include "solo6x10.h"
#include "tw28.h" #include "tw28.h"
#define SOLO_HW_BPL 2048
#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED #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_vlines(__solo) (__solo->video_vsize * 2)
#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ #define solo_image_size(__solo) (solo_bytesperline(__solo) * \
solo_vlines(__solo)) solo_vlines(__solo))
#define solo_bytesperline(__solo) (__solo->video_hsize * 2) #define solo_bytesperline(__solo) (__solo->video_hsize * 2)
#define MIN_VID_BUFFERS 4 #define MIN_VID_BUFFERS 2
/* Simple file handle */ /* Simple file handle */
struct solo_filehandle { struct solo_filehandle {
struct solo_dev *solo_dev; struct solo_dev *solo_dev;
struct videobuf_queue vidq; struct videobuf_queue vidq;
struct task_struct *kthread; struct task_struct *kthread;
spinlock_t slock; spinlock_t slock;
int old_write; int old_write;
struct list_head vidq_active; struct list_head vidq_active;
struct p2m_desc desc[SOLO_NR_P2M_DESC];
int desc_idx;
}; };
unsigned video_nr = -1; static inline void erase_on(struct solo_dev *solo_dev)
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)
{ {
solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
solo_dev->erasing = 1; solo_dev->erasing = 1;
solo_dev->frame_blank = 0; 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) if (!solo_dev->erasing)
return 0; return 0;
...@@ -78,8 +79,7 @@ static int erase_off(struct solo_dev *solo_dev) ...@@ -78,8 +79,7 @@ static int erase_off(struct solo_dev *solo_dev)
void solo_video_in_isr(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_all(&solo_dev->disp_thread_wait);
wake_up_interruptible(&solo_dev->disp_thread_wait);
} }
static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, 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) ...@@ -202,165 +202,61 @@ static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
return 0; 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, static void solo_fillbuf(struct solo_filehandle *fh,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
struct solo_dev *solo_dev = fh->solo_dev; struct solo_dev *solo_dev = fh->solo_dev;
struct videobuf_dmabuf *vbuf; dma_addr_t vbuf;
unsigned int fdma_addr; unsigned int fdma_addr;
int error = 1; int error = -1;
int i; 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) if (!vbuf)
goto finish_buf; goto finish_buf;
if (erase_off(solo_dev)) { if (erase_off(solo_dev)) {
int i; void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
int image_size = solo_image_size(solo_dev);
/* Just blit to the entire sg list, ignoring size */ for (i = 0; i < image_size; i += 2) {
for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) { ((u8 *)p)[i] = 0x80;
void *p = sg_virt(sg); ((u8 *)p)[i + 1] = 0x00;
size_t len = sg_dma_len(sg);
for (i = 0; i < len; i += 2) {
((u8 *)p)[i] = 0x80;
((u8 *)p)[i + 1] = 0x00;
}
} }
error = 0; error = 0;
goto finish_buf; } else {
} fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
(SOLO_HW_BPL * solo_vlines(solo_dev)));
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;
i += lines - 1; error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr,
fdma_addr += SOLO_HW_BPL * lines; solo_bytesperline(solo_dev),
sg_dma += lines * line_len; solo_vlines(solo_dev), SOLO_HW_BPL);
sg_size_left -= lines * line_len;
} }
error = disp_flush_descs(fh);
finish_buf: finish_buf:
if (error) { if (error) {
vb->state = VIDEOBUF_ERROR; vb->state = VIDEOBUF_ERROR;
} else { } else {
vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
vb->state = VIDEOBUF_DONE; vb->state = VIDEOBUF_DONE;
vb->field_count++; vb->field_count++;
do_gettimeofday(&vb->ts);
} }
wake_up(&vb->done); wake_up(&vb->done);
return;
} }
static void solo_thread_try(struct solo_filehandle *fh) static void solo_thread_try(struct solo_filehandle *fh)
{ {
struct videobuf_buffer *vb; struct videobuf_buffer *vb;
unsigned int cur_write;
/* Only "break" from this loop if slock is held, otherwise
* just return. */
for (;;) { 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); spin_lock(&fh->slock);
if (list_empty(&fh->vidq_active)) if (list_empty(&fh->vidq_active))
...@@ -372,13 +268,9 @@ static void solo_thread_try(struct solo_filehandle *fh) ...@@ -372,13 +268,9 @@ static void solo_thread_try(struct solo_filehandle *fh)
if (!waitqueue_active(&vb->done)) if (!waitqueue_active(&vb->done))
break; 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; fh->old_write = cur_write;
list_del(&vb->queue); list_del(&vb->queue);
vb->state = VIDEOBUF_ACTIVE;
spin_unlock(&fh->slock); spin_unlock(&fh->slock);
...@@ -413,17 +305,31 @@ static int solo_thread(void *data) ...@@ -413,17 +305,31 @@ static int solo_thread(void *data)
static int solo_start_thread(struct solo_filehandle *fh) 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"); 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) static void solo_stop_thread(struct solo_filehandle *fh)
{ {
if (fh->kthread) { if (!fh->kthread)
kthread_stop(fh->kthread); return;
fh->kthread = NULL;
} 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, 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, ...@@ -459,9 +365,7 @@ static int solo_buf_prepare(struct videobuf_queue *vq,
if (vb->state == VIDEOBUF_NEEDS_INIT) { if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL); int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) { if (rc < 0) {
struct videobuf_dmabuf *dma = videobuf_to_dma(vb); videobuf_dma_contig_free(vq, vb);
videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT; vb->state = VIDEOBUF_NEEDS_INIT;
return rc; return rc;
} }
...@@ -485,14 +389,11 @@ static void solo_buf_queue(struct videobuf_queue *vq, ...@@ -485,14 +389,11 @@ static void solo_buf_queue(struct videobuf_queue *vq,
static void solo_buf_release(struct videobuf_queue *vq, static void solo_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
struct videobuf_dmabuf *dma = videobuf_to_dma(vb); videobuf_dma_contig_free(vq, vb);
videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT; 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_setup = solo_buf_setup,
.buf_prepare = solo_buf_prepare, .buf_prepare = solo_buf_prepare,
.buf_queue = solo_buf_queue, .buf_queue = solo_buf_queue,
...@@ -535,12 +436,12 @@ static int solo_v4l2_open(struct file *file) ...@@ -535,12 +436,12 @@ static int solo_v4l2_open(struct file *file)
return ret; return ret;
} }
videobuf_queue_sg_init(&fh->vidq, &solo_video_qops, videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
&solo_dev->pdev->dev, &fh->slock, &solo_dev->pdev->dev, &fh->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
SOLO_DISP_PIX_FIELD, SOLO_DISP_PIX_FIELD,
sizeof(struct videobuf_buffer), fh, NULL); sizeof(struct videobuf_buffer),
fh, NULL);
return 0; return 0;
} }
...@@ -557,9 +458,11 @@ static int solo_v4l2_release(struct file *file) ...@@ -557,9 +458,11 @@ static int solo_v4l2_release(struct file *file)
{ {
struct solo_filehandle *fh = file->private_data; struct solo_filehandle *fh = file->private_data;
solo_stop_thread(fh);
videobuf_stop(&fh->vidq); videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq); videobuf_mmap_free(&fh->vidq);
solo_stop_thread(fh);
kfree(fh); kfree(fh);
return 0; return 0;
...@@ -585,12 +488,12 @@ static int solo_querycap(struct file *file, void *priv, ...@@ -585,12 +488,12 @@ static int solo_querycap(struct file *file, void *priv,
static int solo_enum_ext_input(struct solo_dev *solo_dev, static int solo_enum_ext_input(struct solo_dev *solo_dev,
struct v4l2_input *input) struct v4l2_input *input)
{ {
static const char *dispnames_1[] = { "4UP" }; static const char * const dispnames_1[] = { "4UP" };
static const char *dispnames_2[] = { "4UP-1", "4UP-2" }; static const char * const dispnames_2[] = { "4UP-1", "4UP-2" };
static const char *dispnames_5[] = { static const char * const dispnames_5[] = {
"4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" "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)) if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext))
return -EINVAL; return -EINVAL;
...@@ -640,8 +543,14 @@ static int solo_enum_input(struct file *file, void *priv, ...@@ -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) static int solo_set_input(struct file *file, void *priv, unsigned int index)
{ {
struct solo_filehandle *fh = priv; 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) 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, ...@@ -732,7 +641,8 @@ static int solo_reqbufs(struct file *file, void *priv,
return videobuf_reqbufs(&fh->vidq, req); 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; struct solo_filehandle *fh = priv;
...@@ -901,11 +811,12 @@ static struct video_device solo_v4l2_template = { ...@@ -901,11 +811,12 @@ static struct video_device solo_v4l2_template = {
.current_norm = V4L2_STD_NTSC_M, .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 ret;
int i; int i;
atomic_set(&solo_dev->disp_users, 0);
init_waitqueue_head(&solo_dev->disp_thread_wait); init_waitqueue_head(&solo_dev->disp_thread_wait);
solo_dev->vfd = video_device_alloc(); solo_dev->vfd = video_device_alloc();
...@@ -915,7 +826,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev) ...@@ -915,7 +826,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev)
*solo_dev->vfd = solo_v4l2_template; *solo_dev->vfd = solo_v4l2_template;
solo_dev->vfd->parent = &solo_dev->pdev->dev; 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) { if (ret < 0) {
video_device_release(solo_dev->vfd); video_device_release(solo_dev->vfd);
solo_dev->vfd = NULL; solo_dev->vfd = NULL;
...@@ -927,35 +838,30 @@ int solo_v4l2_init(struct solo_dev *solo_dev) ...@@ -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)", snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
SOLO6X10_NAME, solo_dev->vfd->num); SOLO6X10_NAME, solo_dev->vfd->num);
if (video_nr != -1) dev_info(&solo_dev->pdev->dev,
video_nr++; "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 */ /* Cycle all the channels and clear */
for (i = 0; i < solo_dev->nr_chans; i++) { for (i = 0; i < solo_dev->nr_chans; i++) {
solo_v4l2_set_ch(solo_dev, i); solo_v4l2_set_ch(solo_dev, i);
while (erase_off(solo_dev)) while (erase_off(solo_dev))
;/* Do nothing */ /* Do nothing */;
} }
/* Set the default display channel */ /* Set the default display channel */
solo_v4l2_set_ch(solo_dev, 0); solo_v4l2_set_ch(solo_dev, 0);
while (erase_off(solo_dev)) while (erase_off(solo_dev))
;/* Do nothing */ /* Do nothing */;
solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
return 0; return 0;
} }
void solo_v4l2_exit(struct solo_dev *solo_dev) void solo_v4l2_exit(struct solo_dev *solo_dev)
{ {
solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); if (solo_dev->vfd == NULL)
if (solo_dev->vfd) { return;
video_unregister_device(solo_dev->vfd);
solo_dev->vfd = NULL; video_unregister_device(solo_dev->vfd);
} solo_dev->vfd = NULL;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment