Ver Fonte

Staging: Merge Ben Collins solo6x10 tree with upstream

There were some duplicate changes that needed to be hand-merged due to
fixes needed to keep .37 building and working properly.

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Greg Kroah-Hartman há 14 anos atrás
pai
commit
050afc92ee

+ 1 - 1
drivers/staging/solo6x10/Kconfig

@@ -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.

+ 0 - 4
drivers/staging/solo6x10/TODO

@@ -1,7 +1,5 @@
 TODO (staging => main):
 
-	* checkpatch.pl (haven't run it yet)
-	* Lindent (should be clean, but check)
 	* Motion detection flags need to be moved to v4l2
 	* Some private CIDs need to be moved to v4l2
 
@@ -21,8 +19,6 @@ TODO (general):
 	  - implement playback via external sound jack
 	  - implement loopback of external sound jack with incoming audio?
 	  - implement pause/resume
-	  - check into jacking sound from tx28xx chips directly (to avoid
-	    g.723/8khz limitations)
 
 Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins
 <bcollins@bluecherry.net>

+ 9 - 3
drivers/staging/solo6x10/solo6010-core.c

@@ -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,}
 };
 

+ 1 - 1
drivers/staging/solo6x10/solo6010-disp.c

@@ -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) |

+ 2 - 2
drivers/staging/solo6x10/solo6010-enc.c

@@ -145,8 +145,8 @@ int solo_osd_print(struct solo_enc_dev *solo_enc)
 
 	solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR(solo_dev) +
 		     (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE);
-        reg |= (1 << solo_enc->ch);
-        solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
+	reg |= (1 << solo_enc->ch);
+	solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
 
 	kfree(buf);
 

+ 7 - 7
drivers/staging/solo6x10/solo6010-g723.c

@@ -158,7 +158,7 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss)
 	snd_pcm_substream_chip(ss) = solo_pcm->solo_dev;
 	kfree(solo_pcm);
 
-        return 0;
+	return 0;
 }
 
 static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
@@ -197,7 +197,7 @@ static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
 
 static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss)
 {
-        return 0;
+	return 0;
 }
 
 static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss)
@@ -271,7 +271,7 @@ static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol,
 
 	value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch);
 
-        return 0;
+	return 0;
 }
 
 static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol,
@@ -279,15 +279,15 @@ static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol,
 {
 	struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol);
 	u8 ch = value->id.numid - 1;
-        u8 old_val;
+	u8 old_val;
 
-        old_val = tw28_get_audio_gain(solo_dev, ch);
+	old_val = tw28_get_audio_gain(solo_dev, ch);
 	if (old_val == value->value.integer.value[0])
 		return 0;
 
 	tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]);
 
-        return 1;
+	return 1;
 }
 
 static struct snd_kcontrol_new snd_solo_capture_volume = {
@@ -368,7 +368,7 @@ int solo_g723_init(struct solo6010_dev *solo_dev)
 	strcpy(card->mixername, "SOLO-6010");
 	kctl = snd_solo_capture_volume;
 	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)
 		return ret;
 

+ 2 - 2
drivers/staging/solo6x10/solo6010-gpio.c

@@ -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)

+ 4 - 4
drivers/staging/solo6x10/solo6010-i2c.c

@@ -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,
@@ -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];

+ 125 - 27
drivers/staging/solo6x10/solo6010-p2m.c

@@ -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,137 @@ 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 = kzalloc(sizeof(*desc) * 2, GFP_DMA);
+	int ret;
+
+	if (desc == NULL)
+		return -ENOMEM;
+
+	solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0);
+	ret = solo_p2m_dma_desc(solo_dev, id, desc, 2);
+	kfree(desc);
+
+	return ret;
+}
+
+void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
+			u32 ext_addr, u32 size, int repeat, u32 ext_size)
+{
+	desc->ta = 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;
+	u32 config = 0;
+	dma_addr_t desc_dma = 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 || desc_count > SOLO_NR_P2M_DESC);
 
 	p2m_dev = &solo_dev->p2m_dev[id];
 
-	down(&p2m_dev->sem);
+	mutex_lock(&p2m_dev->mutex);
+
+	solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
 
-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);
 
+	/* Enable the descriptors */
+	config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id));
+	desc_dma = pci_map_single(solo_dev->pdev, desc,
+				  desc_count * sizeof(*desc),
+				  PCI_DMA_TODEVICE);
+	solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma);
+	solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1);
+	solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config |
+		       SOLO_P2M_DESC_MODE);
+
+	/* Should have all descriptors completed from one interrupt */
 	timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
 
 	solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
 
-	/* XXX Really looks to me like we will get stuck here if a
-	 * real PCI P2M error occurs */
+	/* Reset back to non-descriptor mode */
+	solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config);
+	solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0);
+	solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0);
+	pci_unmap_single(solo_dev->pdev, desc_dma,
+			 desc_count * sizeof(*desc),
+			 PCI_DMA_TODEVICE);
+
 	if (p2m_dev->error)
-		goto start_dma;
+		ret = -EIO;
+	else if (timeout == 0)
+		ret = -EAGAIN;
+
+	mutex_unlock(&p2m_dev->mutex);
+
+	WARN_ON_ONCE(ret);
 
-	up(&p2m_dev->sem);
+	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;
+
+	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;
 
-	return (timeout == 0) ? -EAGAIN : 0;
+		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 +249,11 @@ 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];
+
 	solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id));
-	complete(&solo_dev->p2m_dev[id].completion);
+
+	complete(&p2m_dev->completion);
 }
 
 void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status)
@@ -188,16 +288,14 @@ 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_DESC_INTR_OPT |
 			       SOLO_P2M_PCI_MASTER_MODE);
 		solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i));
 	}

+ 244 - 106
drivers/staging/solo6x10/solo6010-v4l2-enc.c

@@ -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));
@@ -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,22 +318,68 @@ 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,
-			      unsigned int off, unsigned int size)
+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;
+
+	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;
+}
+
+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))
+	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,
-			    SOLO_MP4E_EXT_ADDR(solo_dev) + off,
-			    SOLO_MP4E_EXT_SIZE(solo_dev) - off);
+			     SOLO_MP4E_EXT_ADDR(solo_dev) + off,
+			     SOLO_MP4E_EXT_SIZE(solo_dev) - off);
 
 	ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0,
 			      buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
@@ -337,70 +402,108 @@ 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,
-			    unsigned int off, unsigned int size)
+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;
 
 	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,
-				      SOLO_JPEG_EXT_ADDR(solo_dev) + off, size);
+	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,
-			     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_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,
-			      SOLO_JPEG_EXT_ADDR(solo_dev),
-			      size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
+	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 (ebuf->ch != solo_enc->ch)
+			continue;
+
 		if (fh->fmt == V4L2_PIX_FMT_MPEG) {
-			if (fh->type != ebuf->type)
-				continue;
-			if (ebuf->ch == solo_enc->ch) {
+			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);
-	vb->field_count++;
-	vb->ts = enc_buf->ts;
-	vb->state = VIDEOBUF_DONE;
+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);
@@ -557,7 +674,7 @@ static int solo_enc_thread(void *data)
 
 	remove_wait_queue(&solo_enc->thread_wait, &wait);
 
-        return 0;
+	return 0;
 }
 
 void solo_motion_isr(struct solo6010_dev *solo_dev)
@@ -669,12 +786,12 @@ void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev)
 static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
 			      unsigned int *size)
 {
-        *size = FRAME_BUF_SIZE;
+	*size = FRAME_BUF_SIZE;
 
-        if (*count < MIN_VID_BUFFERS)
+	if (*count < MIN_VID_BUFFERS)
 		*count = MIN_VID_BUFFERS;
 
-        return 0;
+	return 0;
 }
 
 static int solo_enc_buf_prepare(struct videobuf_queue *vq,
@@ -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->dev, 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->dev, dma);
+	videobuf_dma_free(dma);
 	vb->state = VIDEOBUF_NEEDS_INIT;
 }
 
@@ -753,22 +875,18 @@ 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,
-				    &solo_enc->solo_dev->pdev->dev,
-				    &solo_enc->lock,
-				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
-				    V4L2_FIELD_INTERLACED,
-				    sizeof(struct videobuf_buffer), fh, NULL);
-
-	spin_unlock(&solo_enc->lock);
+	videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
+			       &solo_enc->solo_dev->pdev->dev,
+			       &solo_enc->lock,
+			       V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			       V4L2_FIELD_INTERLACED,
+			       sizeof(struct videobuf_buffer), fh, NULL);
 
 	return 0;
 }
@@ -785,7 +903,11 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
 
 		spin_lock(&solo_enc->lock);
 		ret = solo_enc_on(fh);
-	        spin_unlock(&solo_enc->lock);
+		spin_unlock(&solo_enc->lock);
+		if (ret)
+			return ret;
+
+		ret = solo_start_fh_thread(fh);
 		if (ret)
 			return ret;
 	}
@@ -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);
 
-	return ret;
+	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,12 +1167,16 @@ 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);
-		if (p[3] == 0x00)
-			buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
-		else
-			buf->flags |= V4L2_BUF_FLAG_PFRAME;
+		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;
@@ -1136,7 +1274,7 @@ static int solo_g_parm(struct file *file, void *priv,
 	/* XXX: Shouldn't we be able to get/set this from videobuf? */
 	cp->readbuffers = 2;
 
-        return 0;
+	return 0;
 }
 
 static int solo_s_parm(struct file *file, void *priv,
@@ -1176,7 +1314,7 @@ static int solo_s_parm(struct file *file, void *priv,
 
 	spin_unlock(&solo_enc->lock);
 
-        return 0;
+	return 0;
 }
 
 static int solo_queryctrl(struct file *file, void *priv,
@@ -1240,7 +1378,7 @@ static int solo_queryctrl(struct file *file, void *priv,
 		return 0;
 	}
 
-        return -EINVAL;
+	return -EINVAL;
 }
 
 static int solo_querymenu(struct file *file, void *priv,
@@ -1350,9 +1488,9 @@ static int solo_s_ext_ctrls(struct file *file, void *priv,
 		switch (ctrl->id) {
 		case V4L2_CID_RDS_TX_RADIO_TEXT:
 			if (ctrl->size - 1 > OSD_TEXT_MAX)
-                                err = -ERANGE;
+				err = -ERANGE;
 			else {
-                        	err = copy_from_user(solo_enc->osd_text,
+				err = copy_from_user(solo_enc->osd_text,
 						     ctrl->string,
 						     OSD_TEXT_MAX);
 				solo_enc->osd_text[OSD_TEXT_MAX] = '\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,
 };
 
@@ -1505,7 +1643,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo6010_dev *solo_dev, u8 ch)
 	atomic_set(&solo_enc->readers, 0);
 
 	solo_enc->qp = SOLO_DEFAULT_QP;
-        solo_enc->gop = solo_dev->fps;
+	solo_enc->gop = solo_dev->fps;
 	solo_enc->interval = 1;
 	solo_enc->mode = SOLO_ENC_MODE_CIF;
 	solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;

+ 144 - 40
drivers/staging/solo6x10/solo6010-v4l2.c

@@ -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;
@@ -96,7 +97,7 @@ static void solo_win_setup(struct solo6010_dev *solo_dev, u8 ch,
 		       SOLO_VI_WIN_EX(ex) |
 		       SOLO_VI_WIN_SCALE(scale));
 
-        solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch),
+	solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch),
 		       SOLO_VI_WIN_SY(sy) |
 		       SOLO_VI_WIN_EY(ey));
 }
@@ -203,50 +204,148 @@ static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch)
 	return 0;
 }
 
+static void disp_reset_desc(struct solo_filehandle *fh)
+{
+	/* We use desc mode, which ignores desc 0 */
+	memset(fh->desc, 0, sizeof(*fh->desc));
+	fh->desc_idx = 1;
+}
+
+static int disp_flush_descs(struct solo_filehandle *fh)
+{
+	int ret;
+
+	if (!fh->desc_idx)
+		return 0;
+
+	ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
+				fh->desc, fh->desc_idx);
+	disp_reset_desc(fh);
+
+	return ret;
+}
+
+static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
+		      u32 ext_addr, int size, int repeat, int ext_size)
+{
+	if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
+		int ret = disp_flush_descs(fh);
+		if (ret)
+			return ret;
+	}
+
+	solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
+			   size, repeat, ext_size);
+	fh->desc_idx++;
+
+	return 0;
+}
+
 static void solo_fillbuf(struct solo_filehandle *fh,
 			 struct videobuf_buffer *vb)
 {
 	struct 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) {
-			((u8 *)p)[i] = 0x80;
-			((u8 *)p)[i + 1] = 0x00;
+		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);
+				}
+
+				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 = 0;
+
+	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);
@@ -275,7 +374,7 @@ static void solo_thread_try(struct solo_filehandle *fh)
 			break;
 
 		cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
-							SOLO_VI_STATUS0));
+						 SOLO_VI_STATUS0));
 		if (cur_write == fh->old_write)
 			break;
 
@@ -310,7 +409,7 @@ static int solo_thread(void *data)
 
 	remove_wait_queue(&solo_dev->disp_thread_wait, &wait);
 
-        return 0;
+	return 0;
 }
 
 static int solo_start_thread(struct solo_filehandle *fh)
@@ -337,12 +436,12 @@ static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
 	struct solo_filehandle *fh = vq->priv_data;
 	struct solo6010_dev *solo_dev  = fh->solo_dev;
 
-        *size = solo_image_size(solo_dev);
+	*size = solo_image_size(solo_dev);
 
-        if (*count < MIN_VID_BUFFERS)
+	if (*count < MIN_VID_BUFFERS)
 		*count = MIN_VID_BUFFERS;
 
-        return 0;
+	return 0;
 }
 
 static int solo_buf_prepare(struct videobuf_queue *vq,
@@ -364,7 +463,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->dev, dma);
+			videobuf_dma_free(dma);
 			vb->state = VIDEOBUF_NEEDS_INIT;
 			return rc;
 		}
@@ -388,7 +489,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->dev, dma);
+	videobuf_dma_free(dma);
 	vb->state = VIDEOBUF_NEEDS_INIT;
 }
 
@@ -404,7 +508,7 @@ static unsigned int solo_v4l2_poll(struct file *file,
 {
 	struct solo_filehandle *fh = file->private_data;
 
-        return videobuf_poll_stream(file, &fh->vidq, wait);
+	return videobuf_poll_stream(file, &fh->vidq, wait);
 }
 
 static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
@@ -433,11 +537,11 @@ static int solo_v4l2_open(struct file *file)
 		return ret;
 	}
 
-	videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
-				    &solo_dev->pdev->dev, &fh->slock,
-				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
-				    SOLO_DISP_PIX_FIELD,
-				    sizeof(struct videobuf_buffer), fh, NULL);
+	videobuf_queue_sg_init(&fh->vidq, &solo_video_qops,
+			       &solo_dev->pdev->dev, &fh->slock,
+			       V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			       SOLO_DISP_PIX_FIELD,
+			       sizeof(struct videobuf_buffer), fh, NULL);
 
 	return 0;
 }
@@ -530,7 +634,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;
 }
@@ -781,11 +885,11 @@ static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
 	.vidioc_qbuf			= solo_qbuf,
 	.vidioc_dqbuf			= solo_dqbuf,
 	.vidioc_streamon		= solo_streamon,
-        .vidioc_streamoff		= solo_streamoff,
+	.vidioc_streamoff		= solo_streamoff,
 	/* Controls */
 	.vidioc_queryctrl		= solo_disp_queryctrl,
-        .vidioc_g_ctrl			= solo_disp_g_ctrl,
-        .vidioc_s_ctrl			= solo_disp_s_ctrl,
+	.vidioc_g_ctrl			= solo_disp_g_ctrl,
+	.vidioc_s_ctrl			= solo_disp_s_ctrl,
 };
 
 static struct video_device solo_v4l2_template = {
@@ -795,7 +899,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 - 9
drivers/staging/solo6x10/solo6010.h

@@ -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,17 @@ 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];
 };
 
 #define OSD_TEXT_MAX		30
@@ -185,7 +194,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 +315,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);