Skip to content

Commit

Permalink
solo6x10: Conversion to videobuf-dma-sg (from dma-cont)
Browse files Browse the repository at this point in the history
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: Ben Collins <bcollins@bluecherry.net>
  • Loading branch information
Ben Collins committed Nov 5, 2010
1 parent 1194cf4 commit f62de9b
Show file tree
Hide file tree
Showing 9 changed files with 554 additions and 194 deletions.
2 changes: 1 addition & 1 deletion drivers/staging/solo6x10/Kconfig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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.
12 changes: 9 additions & 3 deletions drivers/staging/solo6x10/solo6010-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,}
};

Expand Down
2 changes: 1 addition & 1 deletion drivers/staging/solo6x10/solo6010-disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ static void solo_motion_config(struct solo6010_dev *solo_dev)
}

/* Default motion settings */
solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) |
solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) |
(SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL,
SOLO_VI_MOTION_FRAME_COUNT(3) |
Expand Down
4 changes: 2 additions & 2 deletions drivers/staging/solo6x10/solo6010-gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ static void solo_gpio_config(struct solo6010_dev *solo_dev)

int solo_gpio_init(struct solo6010_dev *solo_dev)
{
solo_gpio_config(solo_dev);
return 0;
solo_gpio_config(solo_dev);
return 0;
}

void solo_gpio_exit(struct solo6010_dev *solo_dev)
Expand Down
8 changes: 4 additions & 4 deletions drivers/staging/solo6x10/solo6010-i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off)

i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2);

return data;
return data;
}

void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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];
Expand Down
148 changes: 121 additions & 27 deletions drivers/staging/solo6x10/solo6010-p2m.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

#include <linux/kernel.h>
#include <linux/scatterlist.h>

#include "solo6010.h"

Expand All @@ -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,
Expand All @@ -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;

up(&p2m_dev->sem);
mutex_unlock(&p2m_dev->mutex);

return (timeout == 0) ? -EAGAIN : 0;
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;

BUG_ON(id >= SOLO_NR_P2M);

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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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));
}
Expand Down
Loading

0 comments on commit f62de9b

Please sign in to comment.