Commit f62de9be authored by Ben Collins's avatar Ben Collins

solo6x10: Conversion to videobuf-dma-sg (from dma-cont)

Full rewrite of the P2M DMA Engine to support scatter gather and take
advantage of some of the features of the hardware. This includes using
repeat DMA operations and line-mode transfers (for copying OSG and
video display buffers).

What isn't working: For some reason, descriptor mode is not working. I've
implemented a psuedo version (still has one-interrupt per DMA operation),
but we would get huge improvements if we could hand off a ring of
descriptors to the P2M and get back one interrupt when it was done with
all of them.

Documentation is very vague on this, and even the ODM example code
half attempts to get it right, but comments it out of the driver
because it just doesn't work *sigh*

Converts all v4l2 to dma-sg. So long slow dma-contiguous, but hello
more interrupts :(
Signed-off-by: default avatarBen Collins <bcollins@bluecherry.net>
parent 1194cf43
config SOLO6X10
tristate "Softlogic 6x10 MPEG codec cards"
depends on PCI && VIDEO_DEV && SND
select VIDEOBUF_DMA_CONTIG
select VIDEOBUF_DMA_SG
---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec
codec cards.
......@@ -136,6 +136,7 @@ static int __devinit solo6010_pci_probe(struct pci_dev *pdev,
int ret;
int sdram;
u8 chip_id;
solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL);
if (solo_dev == NULL)
return -ENOMEM;
......@@ -261,13 +262,18 @@ static void __devexit solo6010_pci_remove(struct pci_dev *pdev)
}
static struct pci_device_id solo6010_id_table[] = {
/* 6010 based cards */
{PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_4)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_9)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_16)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)},
/* 6110 based cards */
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)},
{0,}
};
......
......@@ -227,7 +227,7 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap,
if (i == SOLO_I2C_ADAPTERS)
return num; // XXX Right return value for failure?
down(&solo_dev->i2c_sem);
mutex_lock(&solo_dev->i2c_mutex);
solo_dev->i2c_id = i;
solo_dev->i2c_msg = msgs;
solo_dev->i2c_msg_num = num;
......@@ -258,7 +258,7 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap,
solo_dev->i2c_state = IIC_STATE_IDLE;
solo_dev->i2c_id = -1;
up(&solo_dev->i2c_sem);
mutex_unlock(&solo_dev->i2c_mutex);
return ret;
}
......@@ -284,7 +284,7 @@ int solo_i2c_init(struct solo6010_dev *solo_dev)
solo_dev->i2c_id = -1;
solo_dev->i2c_state = IIC_STATE_IDLE;
init_waitqueue_head(&solo_dev->i2c_wait);
sema_init(&solo_dev->i2c_sem, 1);
mutex_init(&solo_dev->i2c_mutex);
for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
......
......@@ -18,6 +18,7 @@
*/
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include "solo6010.h"
......@@ -30,8 +31,9 @@ int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
int ret;
WARN_ON(!size);
WARN_ON(id >= SOLO_NR_P2M);
if (!size || id >= SOLO_NR_P2M)
BUG_ON(id >= SOLO_NR_P2M);
if (!size)
return -EINVAL;
dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
......@@ -47,42 +49,118 @@ int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
dma_addr_t dma_addr, u32 ext_addr, u32 size)
{
struct p2m_desc desc;
solo_p2m_push_desc(&desc, wr, dma_addr, ext_addr, size, 0, 0);
return solo_p2m_dma_desc(solo_dev, id, &desc, 1);
}
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 = dma_addr;
desc->fa = ext_addr;
desc->ext = SOLO_P2M_COPY_SIZE(size >> 2);
desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
(wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON;
/* Ext size only matters when we're repeating */
if (repeat) {
desc->ext |= SOLO_P2M_EXT_INC(ext_size >> 2);
desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) |
SOLO_P2M_REPEAT(repeat);
}
}
int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id,
struct p2m_desc *desc, int desc_count)
{
struct solo_p2m_dev *p2m_dev;
unsigned int timeout = 0;
unsigned int timeout;
int ret = 0;
WARN_ON(!size);
WARN_ON(id >= SOLO_NR_P2M);
if (!size || id >= SOLO_NR_P2M)
return -EINVAL;
BUG_ON(id >= SOLO_NR_P2M);
BUG_ON(desc_count > SOLO_NR_P2M_DESC);
p2m_dev = &solo_dev->p2m_dev[id];
down(&p2m_dev->sem);
mutex_lock(&p2m_dev->mutex);
start_dma:
INIT_COMPLETION(p2m_dev->completion);
p2m_dev->error = 0;
solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), dma_addr);
solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), ext_addr);
solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id),
SOLO_P2M_COPY_SIZE(size >> 2));
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id),
SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
(wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON);
/* Setup the descriptor count and base address */
p2m_dev->num_descs = desc_count;
p2m_dev->descs = desc;
p2m_dev->desc_idx = 0;
/* We plug in the first descriptor here. The isr will take
* over from desc[1] after this. */
solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc[0].ta);
solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc[0].fa);
solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc[0].ext);
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc[0].ctrl);
/* Should have all descriptors completed from one interrupt */
timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
/* XXX Really looks to me like we will get stuck here if a
* real PCI P2M error occurs */
if (p2m_dev->error)
goto start_dma;
ret = -EIO;
else if (timeout == 0)
ret = -EAGAIN;
mutex_unlock(&p2m_dev->mutex);
WARN_ON_ONCE(ret);
return ret;
}
int solo_p2m_dma_sg(struct solo6010_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;
up(&p2m_dev->sem);
BUG_ON(id >= SOLO_NR_P2M);
return (timeout == 0) ? -EAGAIN : 0;
if (WARN_ON_ONCE(!size))
return -EINVAL;
for (i = idx = 0; i < SOLO_NR_P2M_DESC && sg && size > 0;
i++, sg = sg_next(sg)) {
struct p2m_desc *desc = &pdesc[i];
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);
return solo_p2m_dma_desc(solo_dev, id, pdesc, idx);
}
#ifdef SOLO_TEST_P2M
......@@ -152,8 +230,27 @@ static void run_p2m_test(struct solo6010_dev *solo_dev)
void solo_p2m_isr(struct solo6010_dev *solo_dev, int id)
{
struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id];
struct p2m_desc *desc;
solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id));
complete(&solo_dev->p2m_dev[id].completion);
p2m_dev->desc_idx++;
if (p2m_dev->desc_idx >= p2m_dev->num_descs) {
complete(&p2m_dev->completion);
return;
}
/* Reset the p2m and start the next one */
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
desc = &p2m_dev->descs[p2m_dev->desc_idx];
solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->ta);
solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->fa);
solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->ext);
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl);
}
void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status)
......@@ -188,16 +285,13 @@ int solo_p2m_init(struct solo6010_dev *solo_dev)
for (i = 0; i < SOLO_NR_P2M; i++) {
p2m_dev = &solo_dev->p2m_dev[i];
sema_init(&p2m_dev->sem, 1);
mutex_init(&p2m_dev->mutex);
init_completion(&p2m_dev->completion);
solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(i),
__pa(p2m_dev->desc));
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0);
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
SOLO_P2M_CSC_16BIT_565 |
SOLO_P2M_DMA_INTERVAL(0) |
SOLO_P2M_DMA_INTERVAL(3) |
SOLO_P2M_PCI_MASTER_MODE);
solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i));
}
......
......@@ -24,7 +24,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/videobuf-dma-contig.h>
#include <media/videobuf-dma-sg.h>
#include "solo6010.h"
#include "solo6010-tw28.h"
......@@ -47,13 +47,14 @@ struct solo_enc_fh {
struct videobuf_queue vidq;
struct list_head vidq_active;
struct task_struct *kthread;
struct p2m_desc desc[SOLO_NR_P2M_DESC];
};
static unsigned char vid_vop_header[] = {
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
0x02, 0x48, 0x05, 0xc0, 0x00, 0x40, 0x00, 0x40,
0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
0x1f, 0x4c, 0x58, 0x10, 0x78, 0x51, 0x18, 0x3e,
0x1f, 0x4c, 0x58, 0x10, 0x78, 0x51, 0x18, 0x3f,
};
/*
......@@ -151,6 +152,11 @@ static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
else
solo_dev->motion_mask &= ~(1 << ch);
/* Do this regardless of if we are turning on or off */
solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
1 << solo_enc->ch);
solo_enc->motion_detected = 0;
solo_reg_write(solo_dev, SOLO_VI_MOT_ADR,
SOLO_VI_MOTION_EN(solo_dev->motion_mask) |
(SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
......@@ -184,7 +190,7 @@ static void solo_update_mode(struct solo_enc_dev *solo_enc)
solo_enc->bw_weight <<= 2;
break;
default:
WARN(1, "mode is unknown");
WARN(1, "mode is unknown\n");
}
}
......@@ -211,11 +217,6 @@ static int solo_enc_on(struct solo_enc_fh *fh)
solo_dev->enc_bw_remain -= solo_enc->bw_weight;
}
fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6010_NAME "_enc");
if (IS_ERR(fh->kthread))
return PTR_ERR(fh->kthread);
fh->enc_on = 1;
fh->rd_idx = solo_enc->solo_dev->enc_wr_idx;
......@@ -279,6 +280,24 @@ static void solo_enc_off(struct solo_enc_fh *fh)
solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0);
}
static int solo_start_fh_thread(struct solo_enc_fh *fh)
{
struct solo_enc_dev *solo_enc = fh->enc;
fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6010_NAME "_enc");
/* Oops, we had a problem */
if (IS_ERR(fh->kthread)) {
spin_lock(&solo_enc->lock);
solo_enc_off(fh);
spin_unlock(&solo_enc->lock);
return PTR_ERR(fh->kthread);
}
return 0;
}
static void enc_reset_gop(struct solo6010_dev *solo_dev, u8 ch)
{
BUG_ON(ch >= solo_dev->nr_chans);
......@@ -299,7 +318,24 @@ static int enc_gop_reset(struct solo6010_dev *solo_dev, u8 ch, u8 vop)
return 0;
}
static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev, dma_addr_t buf,
static void enc_write_sg(struct scatterlist *sglist, void *buf, int size)
{
struct scatterlist *sg;
u8 *src = buf;
for (sg = sglist; sg && size > 0; sg = sg_next(sg)) {
u8 *p = sg_virt(sg);
size_t len = sg_dma_len(sg);
int i;
for (i = 0; i < len && size; i++)
p[i] = *(src++);
}
}
static int enc_get_mpeg_dma_sg(struct solo6010_dev *solo_dev,
struct p2m_desc *desc,
struct scatterlist *sglist, int skip,
unsigned int off, unsigned int size)
{
int ret;
......@@ -307,9 +343,38 @@ static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev, dma_addr_t buf,
if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
return -EINVAL;
if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev))
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;
}
static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev,
dma_addr_t buf, unsigned int off,
unsigned int size)
{
int ret;
if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
return -EINVAL;
if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
}
/* Buffer wrap */
ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
......@@ -337,7 +402,9 @@ static int enc_get_mpeg_dma(struct solo6010_dev *solo_dev, void *buf,
return ret;
}
static int enc_get_jpeg_dma(struct solo6010_dev *solo_dev, dma_addr_t buf,
static int enc_get_jpeg_dma_sg(struct solo6010_dev *solo_dev,
struct p2m_desc *desc,
struct scatterlist *sglist, int skip,
unsigned int off, unsigned int size)
{
int ret;
......@@ -345,62 +412,98 @@ static int enc_get_jpeg_dma(struct solo6010_dev *solo_dev, dma_addr_t buf,
if (off > SOLO_JPEG_EXT_SIZE(solo_dev))
return -EINVAL;
if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev))
return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_JPEG, 0, buf,
if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) {
return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG,
desc, 0, sglist, skip,
SOLO_JPEG_EXT_ADDR(solo_dev) + off, size);
}
/* Buffer wrap */
ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_JPEG, 0, buf,
SOLO_JPEG_EXT_ADDR(solo_dev) + off,
ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off,
SOLO_JPEG_EXT_SIZE(solo_dev) - off);
ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_JPEG, 0,
buf + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
SOLO_JPEG_EXT_ADDR(solo_dev),
size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
return ret;
}
/* Returns true of __chk is within the first __range bytes of __off */
#define OFF_IN_RANGE(__off, __range, __chk) \
((__off <= __chk) && ((__off + __range) >= __chk))
static void solo_jpeg_header(struct solo_enc_dev *solo_enc,
struct videobuf_dmabuf *vbuf)
{
struct scatterlist *sg;
void *src = jpeg_header;
size_t copied = 0;
size_t to_copy = sizeof(jpeg_header);
for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) {
size_t this_copy = min(sg_dma_len(sg),
(unsigned int)(to_copy - copied));
u8 *p = sg_virt(sg);
memcpy(p, src + copied, this_copy);
if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5))
p[(SOF0_START + 5) - copied] =
0xff & (solo_enc->height >> 8);
if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6))
p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height;
if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7))
p[(SOF0_START + 7) - copied] =
0xff & (solo_enc->width >> 8);
if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8))
p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width;
copied += this_copy;
}
}
static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
struct videobuf_buffer *vb, dma_addr_t vbuf)
struct videobuf_buffer *vb,
struct videobuf_dmabuf *vbuf)
{
struct solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev;
u8 *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
struct solo6010_dev *solo_dev = fh->enc->solo_dev;
int size = enc_buf->jpeg_size;
memcpy(p, jpeg_header, sizeof(jpeg_header));
p[SOF0_START + 5] = 0xff & (solo_enc->height >> 8);
p[SOF0_START + 6] = 0xff & solo_enc->height;
p[SOF0_START + 7] = 0xff & (solo_enc->width >> 8);
p[SOF0_START + 8] = 0xff & solo_enc->width;
/* Copy the header first (direct write) */
solo_jpeg_header(fh->enc, vbuf);
vbuf += sizeof(jpeg_header);
vb->size = enc_buf->jpeg_size + sizeof(jpeg_header);
vb->size = size + sizeof(jpeg_header);
return enc_get_jpeg_dma(solo_dev, vbuf, enc_buf->jpeg_off,
enc_buf->jpeg_size);
/* 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 int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
struct videobuf_buffer *vb, dma_addr_t vbuf)
struct videobuf_buffer *vb,
struct videobuf_dmabuf *vbuf)
{
struct solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev;
struct vop_header vh;
int ret;
int frame_size, frame_off;
int skip = 0;
if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh)))
return -1;
return -EINVAL;
/* First get the hardware vop header (not real mpeg) */
ret = enc_get_mpeg_dma(solo_dev, &vh, enc_buf->off, sizeof(vh));
if (ret)
return -1;
if (WARN_ON_ONCE(ret))
return ret;
if (WARN_ON_ONCE(vh.size > enc_buf->size))
return -1;
return -EINVAL;
vb->width = vh.hsize << 4;
vb->height = vh.vsize << 4;
......@@ -410,9 +513,9 @@ static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
if (!enc_buf->vop) {
u16 fps = solo_dev->fps * 1000;
u16 interval = solo_enc->interval * 1000;
u8 *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
u8 p[sizeof(vid_vop_header)];
memcpy(p, vid_vop_header, sizeof(vid_vop_header));
memcpy(p, vid_vop_header, sizeof(p));
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
p[10] |= ((XVID_PAR_43_NTSC << 3) & 0x78);
......@@ -434,43 +537,49 @@ static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
if (vh.interlace)
p[29] |= 0x20;
enc_write_sg(vbuf->sglist, p, sizeof(p));
/* Adjust the dma buffer past this header */
vb->size += sizeof(vid_vop_header);
vbuf += sizeof(vid_vop_header);
skip = sizeof(vid_vop_header);
}
/* Now get the actual mpeg payload */
frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev);
frame_size = enc_buf->size - sizeof(vh);
ret = enc_get_mpeg_dma_t(solo_dev, vbuf, frame_off, frame_size);
if (WARN_ON_ONCE(ret))
return -1;
return 0;
ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
skip, frame_off, frame_size);
WARN_ON_ONCE(ret);
return ret;
}
/* On successful return (0), leaves solo_enc->lock unlocked */
static int solo_enc_fillbuf(struct solo_enc_fh *fh,
static void solo_enc_fillbuf(struct solo_enc_fh *fh,
struct videobuf_buffer *vb)
{
struct solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev;
struct solo_enc_buf *enc_buf = NULL;
dma_addr_t vbuf;
struct videobuf_dmabuf *vbuf;
int ret;
int error = 1;
u16 idx = fh->rd_idx;
while (idx != solo_dev->enc_wr_idx) {
struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx];
idx = (idx + 1) % SOLO_NR_RING_BUFS;
if (fh->fmt == V4L2_PIX_FMT_MPEG) {
if (fh->type != ebuf->type)
if (ebuf->ch != solo_enc->ch)
continue;
if (ebuf->ch == solo_enc->ch) {
if (fh->fmt == V4L2_PIX_FMT_MPEG) {
if (fh->type == ebuf->type) {
enc_buf = ebuf;
break;
}
} else if (ebuf->ch == solo_enc->ch) {
} else {
/* For mjpeg, keep reading to the newest frame */
enc_buf = ebuf;
}
......@@ -478,48 +587,54 @@ static int solo_enc_fillbuf(struct solo_enc_fh *fh,
fh->rd_idx = idx;
if (!enc_buf)
return -1;
if (WARN_ON_ONCE(!enc_buf))
goto buf_err;
if ((fh->fmt == V4L2_PIX_FMT_MPEG &&
vb->bsize < enc_buf->size) ||
(fh->fmt == V4L2_PIX_FMT_MJPEG &&
vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) {
return -1;
WARN_ON_ONCE(1);
goto buf_err;
}
if (!(vbuf = videobuf_to_dma_contig(vb)))
return -1;
/* Is it ok that we mess with this buffer out of lock? */
spin_unlock(&solo_enc->lock);
if (WARN_ON_ONCE(!(vbuf = videobuf_to_dma(vb))))
goto buf_err;
if (fh->fmt == V4L2_PIX_FMT_MPEG)
ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf);
else
ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf);
if (ret) // Ignore failures
return 0;
if (!ret)
error = 0;
list_del(&vb->queue);
buf_err:
if (error) {
vb->state = VIDEOBUF_ERROR;
} else {
vb->field_count++;
vb->ts = enc_buf->ts;
vb->state = VIDEOBUF_DONE;
}
wake_up(&vb->done);
return 0;
return;
}
static void solo_enc_thread_try(struct solo_enc_fh *fh)
{
struct solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev;
struct videobuf_buffer *vb;
for (;;) {
spin_lock(&solo_enc->lock);
if (fh->rd_idx == solo_dev->enc_wr_idx)
break;
if (list_empty(&fh->vidq_active))
break;
......@@ -529,9 +644,11 @@ static void solo_enc_thread_try(struct solo_enc_fh *fh)
if (!waitqueue_active(&vb->done))
break;
/* On success, returns with solo_enc->lock unlocked */
if (solo_enc_fillbuf(fh, vb))
break;
list_del(&vb->queue);
spin_unlock(&solo_enc->lock);
solo_enc_fillbuf(fh, vb);
}
assert_spin_locked(&solo_enc->lock);
......@@ -696,7 +813,9 @@ static int solo_enc_buf_prepare(struct videobuf_queue *vq,
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) {
videobuf_dma_contig_free(vq, vb);
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
return rc;
}
......@@ -719,7 +838,10 @@ static void solo_enc_buf_queue(struct videobuf_queue *vq,
static void solo_enc_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
videobuf_dma_contig_free(vq, vb);
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
}
......@@ -753,23 +875,19 @@ static int solo_enc_open(struct file *file)
if ((fh = kzalloc(sizeof(*fh), GFP_KERNEL)) == NULL)
return -ENOMEM;
spin_lock(&solo_enc->lock);
fh->enc = solo_enc;
file->private_data = fh;
INIT_LIST_HEAD(&fh->vidq_active);
fh->fmt = V4L2_PIX_FMT_MPEG;
fh->type = SOLO_ENC_TYPE_STD;
videobuf_queue_dma_contig_init(&fh->vidq, &solo_enc_video_qops,
videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
&solo_enc->solo_dev->pdev->dev,
&solo_enc->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), fh);
spin_unlock(&solo_enc->lock);
return 0;
}
......@@ -788,6 +906,10 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
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,
......@@ -797,10 +919,15 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
static int solo_enc_release(struct file *file)
{
struct solo_enc_fh *fh = file->private_data;
struct solo_enc_dev *solo_enc = fh->enc;
videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq);
spin_lock(&solo_enc->lock);
solo_enc_off(fh);
spin_unlock(&solo_enc->lock);
kfree(fh);
return 0;
......@@ -842,7 +969,7 @@ static int solo_enc_enum_input(struct file *file, void *priv,
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
input->std = V4L2_STD_NTSC_M;
else
input->std = V4L2_STD_PAL_M;
input->std = V4L2_STD_PAL_B;
if (!tw28_get_video_status(solo_dev, solo_enc->ch))
input->status = V4L2_IN_ST_NO_SIGNAL;
......@@ -956,7 +1083,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
spin_unlock(&solo_enc->lock);
if (ret)
return ret;
return solo_start_fh_thread(fh);
}
static int solo_enc_get_fmt_cap(struct file *file, void *priv,
......@@ -1014,6 +1144,10 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
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);
......@@ -1033,13 +1167,17 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
/* Check for key frame on mpeg data */
if (fh->fmt == V4L2_PIX_FMT_MPEG) {
struct videobuf_buffer *vb = fh->vidq.bufs[buf->index];
u8 *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
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;
}
......@@ -1459,7 +1597,7 @@ static struct video_device solo_enc_template = {
.minor = -1,
.release = video_device_release,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_M,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
.current_norm = V4L2_STD_NTSC_M,
};
......
......@@ -24,14 +24,13 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/videobuf-dma-contig.h>
#include <media/videobuf-dma-sg.h>
#include "solo6010.h"
#include "solo6010-tw28.h"
#define SOLO_HW_BPL 2048
#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
#define SOLO_DISP_BUF_SIZE (64 * 1024) // 64k
/* Image size is two fields, SOLO_HW_BPL is one horizontal line */
#define solo_vlines(__solo) (__solo->video_vsize * 2)
......@@ -49,6 +48,8 @@ struct solo_filehandle {
spinlock_t slock;
int old_write;
struct list_head vidq_active;
struct p2m_desc desc[SOLO_NR_P2M_DESC];
int desc_idx;
};
unsigned video_nr = -1;
......@@ -203,50 +204,146 @@ static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch)
return 0;
}
static void disp_reset_desc(struct solo_filehandle *fh)
{
fh->desc_idx = 0;
}
static int disp_flush_descs(struct solo_filehandle *fh)
{
int ret;
if (!fh->desc_idx)
return 0;
ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
fh->desc, fh->desc_idx);
disp_reset_desc(fh);
return ret;
}
static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
u32 ext_addr, int size, int repeat, int ext_size)
{
if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
int ret = disp_flush_descs(fh);
if (ret)
return ret;
}
solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
size, repeat, ext_size);
fh->desc_idx++;
return 0;
}
static void solo_fillbuf(struct solo_filehandle *fh,
struct videobuf_buffer *vb)
{
struct solo6010_dev *solo_dev = fh->solo_dev;
dma_addr_t vbuf;
struct videobuf_dmabuf* vbuf;
unsigned int fdma_addr;
int frame_size;
int error = 1;
int i;
struct scatterlist* sg;
dma_addr_t sg_dma;
int sg_size_left;
if (!(vbuf = videobuf_to_dma_contig(vb)))
if (!(vbuf = videobuf_to_dma(vb)))
goto finish_buf;
if (erase_off(solo_dev)) {
void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
int image_size = solo_image_size(solo_dev);
for (i = 0; i < image_size; i += 2) {
int i;
/* Just blit to the entire sg list, ignoring size */
for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
void *p = sg_virt(sg);
size_t len = sg_dma_len(sg);
for (i = 0; i < len; i += 2) {
((u8 *)p)[i] = 0x80;
((u8 *)p)[i + 1] = 0x00;
}
}
error = 0;
goto finish_buf;
}
frame_size = SOLO_HW_BPL * solo_vlines(solo_dev);
fdma_addr = SOLO_DISP_EXT_ADDR(solo_dev) + (fh->old_write * frame_size);
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(solo_dev) + (fh->old_write *
(SOLO_HW_BPL * solo_vlines(solo_dev)));
for (i = 0; i < frame_size / SOLO_DISP_BUF_SIZE; i++) {
int j;
for (j = 0; j < (SOLO_DISP_BUF_SIZE / SOLO_HW_BPL); j++) {
if (solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_DISP, 0,
vbuf, fdma_addr + (j * SOLO_HW_BPL),
solo_bytesperline(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;
vbuf += solo_bytesperline(solo_dev);
sg_dma = sg_dma_address(sg);
sg_size_left = sg_dma_len(sg);
}
fdma_addr += SOLO_DISP_BUF_SIZE;
/* 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);
}
error = 0;
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;
fdma_addr += SOLO_HW_BPL * lines;
sg_dma += lines * line_len;
sg_size_left -= lines * line_len;
}
error = disp_flush_descs(fh);
finish_buf:
if (error) {
vb->state = VIDEOBUF_ERROR;
} else {
vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
vb->state = VIDEOBUF_DONE;
vb->field_count++;
do_gettimeofday(&vb->ts);
......@@ -364,7 +461,9 @@ static int solo_buf_prepare(struct videobuf_queue *vq,
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) {
videobuf_dma_contig_free(vq, vb);
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
return rc;
}
......@@ -388,7 +487,10 @@ static void solo_buf_queue(struct videobuf_queue *vq,
static void solo_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
videobuf_dma_contig_free(vq, vb);
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
}
......@@ -433,7 +535,7 @@ static int solo_v4l2_open(struct file *file)
return ret;
}
videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
videobuf_queue_sg_init(&fh->vidq, &solo_video_qops,
&solo_dev->pdev->dev, &fh->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
SOLO_DISP_PIX_FIELD,
......@@ -530,7 +632,7 @@ static int solo_enum_input(struct file *file, void *priv,
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
input->std = V4L2_STD_NTSC_M;
else
input->std = V4L2_STD_PAL_M;
input->std = V4L2_STD_PAL_B;
return 0;
}
......@@ -795,7 +897,7 @@ static struct video_device solo_v4l2_template = {
.minor = -1,
.release = video_device_release,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_M,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
.current_norm = V4L2_STD_NTSC_M,
};
......
......@@ -26,8 +26,8 @@
#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/atomic.h>
......@@ -48,10 +48,14 @@
#define PCI_DEVICE_ID_NEUSOLO_4 0x4304
#define PCI_DEVICE_ID_NEUSOLO_9 0x4309
#define PCI_DEVICE_ID_NEUSOLO_16 0x4310
/* Commell Softlogic 6010 based cards */
#define PCI_DEVICE_ID_COMMSOLO_4 0x4E04
#define PCI_DEVICE_ID_COMMSOLO_9 0x4E09
#define PCI_DEVICE_ID_COMMSOLO_16 0x4E10
/* Bluecherry Softlogic 6010 based cards */
#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04
#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09
#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10
/* Bluecherry Softlogic 6110 based cards */
#define PCI_DEVICE_ID_BC_6110_4 0x5304
#define PCI_DEVICE_ID_BC_6110_8 0x5308
#define PCI_DEVICE_ID_BC_6110_16 0x5310
#endif /* Bluecherry */
#define SOLO6010_NAME "solo6010"
......@@ -78,7 +82,6 @@
/* DMA Engine setup */
#define SOLO_NR_P2M 4
#define SOLO_NR_P2M_DESC 256
#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16)
/* MPEG and JPEG share the same interrupt and locks so they must be together
* in the same dma channel. */
#define SOLO_P2M_DMA_ID_MP4E 0
......@@ -123,11 +126,20 @@ enum SOLO_I2C_STATE {
IIC_STATE_STOP
};
struct p2m_desc {
u32 ctrl;
u32 ext;
u32 ta;
u32 fa;
};
struct solo_p2m_dev {
struct semaphore sem;
struct mutex mutex;
struct completion completion;
int error;
u8 desc[SOLO_P2M_DESC_SIZE];
int num_descs;
int desc_idx;
struct p2m_desc *descs;
};
#define OSD_TEXT_MAX 30
......@@ -185,7 +197,7 @@ struct solo6010_dev {
/* i2c related items */
struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS];
enum SOLO_I2C_STATE i2c_state;
struct semaphore i2c_sem;
struct mutex i2c_mutex;
int i2c_id;
wait_queue_head_t i2c_wait;
struct i2c_msg *i2c_msg;
......@@ -306,6 +318,14 @@ int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
dma_addr_t dma_addr, u32 ext_addr, u32 size);
int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
void *sys_addr, u32 ext_addr, u32 size);
int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id,
struct p2m_desc *pdesc, int wr,
struct scatterlist *sglist, u32 sg_off,
u32 ext_addr, u32 size);
void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
u32 ext_addr, u32 size, int repeat, u32 ext_size);
int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id,
struct p2m_desc *desc, int desc_count);
/* Set the threshold for motion detection */
void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val);
......
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