Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f62de9be authored by Ben Collins's avatar Ben Collins
Browse files

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
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
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.
+9 −3
Original line number Diff line number Diff line
@@ -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,}
};

+4 −4
Original line number Diff line number Diff line
@@ -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];
+121 −27
Original line number Diff line number Diff line
@@ -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));
	}
+245 −107
Original line number Diff line number Diff line
@@ -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,
};

Loading