Commit 050afc92 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parents 391a169e 5bf68592
config SOLO6X10 config SOLO6X10
tristate "Softlogic 6x10 MPEG codec cards" tristate "Softlogic 6x10 MPEG codec cards"
depends on PCI && VIDEO_DEV && SND depends on PCI && VIDEO_DEV && SND
select VIDEOBUF_DMA_CONTIG select VIDEOBUF_DMA_SG
---help--- ---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec This driver supports the Softlogic based MPEG-4 and h.264 codec
codec cards. codec cards.
TODO (staging => main): 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 * Motion detection flags need to be moved to v4l2
* Some private CIDs need to be moved to v4l2 * Some private CIDs need to be moved to v4l2
...@@ -21,8 +19,6 @@ TODO (general): ...@@ -21,8 +19,6 @@ TODO (general):
- implement playback via external sound jack - implement playback via external sound jack
- implement loopback of external sound jack with incoming audio? - implement loopback of external sound jack with incoming audio?
- implement pause/resume - 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 Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins
<bcollins@bluecherry.net> <bcollins@bluecherry.net>
...@@ -136,6 +136,7 @@ static int __devinit solo6010_pci_probe(struct pci_dev *pdev, ...@@ -136,6 +136,7 @@ static int __devinit solo6010_pci_probe(struct pci_dev *pdev,
int ret; int ret;
int sdram; int sdram;
u8 chip_id; u8 chip_id;
solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL);
if (solo_dev == NULL) if (solo_dev == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -261,13 +262,18 @@ static void __devexit solo6010_pci_remove(struct pci_dev *pdev) ...@@ -261,13 +262,18 @@ static void __devexit solo6010_pci_remove(struct pci_dev *pdev)
} }
static struct pci_device_id solo6010_id_table[] = { 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_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_4)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, {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_BC_SOLO_4)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_9)}, {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)},
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_16)}, {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,} {0,}
}; };
......
...@@ -198,7 +198,7 @@ static void solo_motion_config(struct solo6010_dev *solo_dev) ...@@ -198,7 +198,7 @@ static void solo_motion_config(struct solo6010_dev *solo_dev)
} }
/* Default motion settings */ /* 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_MOTION_EXT_ADDR(solo_dev) >> 16));
solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL,
SOLO_VI_MOTION_FRAME_COUNT(3) | SOLO_VI_MOTION_FRAME_COUNT(3) |
......
...@@ -145,8 +145,8 @@ int solo_osd_print(struct solo_enc_dev *solo_enc) ...@@ -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_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR(solo_dev) +
(solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE);
reg |= (1 << solo_enc->ch); reg |= (1 << solo_enc->ch);
solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
kfree(buf); kfree(buf);
......
...@@ -158,7 +158,7 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss) ...@@ -158,7 +158,7 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss)
snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; snd_pcm_substream_chip(ss) = solo_pcm->solo_dev;
kfree(solo_pcm); kfree(solo_pcm);
return 0; return 0;
} }
static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) 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) ...@@ -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) 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) 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, ...@@ -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); 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, 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, ...@@ -279,15 +279,15 @@ static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol,
{ {
struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol);
u8 ch = value->id.numid - 1; 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]) if (old_val == value->value.integer.value[0])
return 0; return 0;
tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[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 = { static struct snd_kcontrol_new snd_solo_capture_volume = {
...@@ -368,7 +368,7 @@ int solo_g723_init(struct solo6010_dev *solo_dev) ...@@ -368,7 +368,7 @@ int solo_g723_init(struct solo6010_dev *solo_dev)
strcpy(card->mixername, "SOLO-6010"); strcpy(card->mixername, "SOLO-6010");
kctl = snd_solo_capture_volume; kctl = snd_solo_capture_volume;
kctl.count = solo_dev->nr_chans; kctl.count = solo_dev->nr_chans;
ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev));
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -92,8 +92,8 @@ static void solo_gpio_config(struct solo6010_dev *solo_dev) ...@@ -92,8 +92,8 @@ static void solo_gpio_config(struct solo6010_dev *solo_dev)
int solo_gpio_init(struct solo6010_dev *solo_dev) int solo_gpio_init(struct solo6010_dev *solo_dev)
{ {
solo_gpio_config(solo_dev); solo_gpio_config(solo_dev);
return 0; return 0;
} }
void solo_gpio_exit(struct solo6010_dev *solo_dev) void solo_gpio_exit(struct solo6010_dev *solo_dev)
......
...@@ -46,7 +46,7 @@ u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off) ...@@ -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); 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, 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, ...@@ -227,7 +227,7 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap,
if (i == SOLO_I2C_ADAPTERS) if (i == SOLO_I2C_ADAPTERS)
return num; // XXX Right return value for failure? 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_id = i;
solo_dev->i2c_msg = msgs; solo_dev->i2c_msg = msgs;
solo_dev->i2c_msg_num = num; solo_dev->i2c_msg_num = num;
...@@ -258,7 +258,7 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap, ...@@ -258,7 +258,7 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap,
solo_dev->i2c_state = IIC_STATE_IDLE; solo_dev->i2c_state = IIC_STATE_IDLE;
solo_dev->i2c_id = -1; solo_dev->i2c_id = -1;
up(&solo_dev->i2c_sem); mutex_unlock(&solo_dev->i2c_mutex);
return ret; return ret;
} }
...@@ -284,7 +284,7 @@ int solo_i2c_init(struct solo6010_dev *solo_dev) ...@@ -284,7 +284,7 @@ int solo_i2c_init(struct solo6010_dev *solo_dev)
solo_dev->i2c_id = -1; solo_dev->i2c_id = -1;
solo_dev->i2c_state = IIC_STATE_IDLE; solo_dev->i2c_state = IIC_STATE_IDLE;
init_waitqueue_head(&solo_dev->i2c_wait); 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++) { for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/scatterlist.h>
#include "solo6010.h" #include "solo6010.h"
...@@ -30,8 +31,9 @@ int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, ...@@ -30,8 +31,9 @@ int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
int ret; int ret;
WARN_ON(!size); WARN_ON(!size);
WARN_ON(id >= SOLO_NR_P2M); BUG_ON(id >= SOLO_NR_P2M);
if (!size || id >= SOLO_NR_P2M)
if (!size)
return -EINVAL; return -EINVAL;
dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
...@@ -47,42 +49,137 @@ int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, ...@@ -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, int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
dma_addr_t dma_addr, u32 ext_addr, u32 size) 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; 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); BUG_ON(id >= SOLO_NR_P2M);
WARN_ON(id >= SOLO_NR_P2M); BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC);
if (!size || id >= SOLO_NR_P2M)
return -EINVAL;
p2m_dev = &solo_dev->p2m_dev[id]; 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); INIT_COMPLETION(p2m_dev->completion);
p2m_dev->error = 0; 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); timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
/* XXX Really looks to me like we will get stuck here if a /* Reset back to non-descriptor mode */
* real PCI P2M error occurs */ 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) 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 #ifdef SOLO_TEST_P2M
...@@ -152,8 +249,11 @@ static void run_p2m_test(struct solo6010_dev *solo_dev) ...@@ -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) 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)); 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) 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) ...@@ -188,16 +288,14 @@ int solo_p2m_init(struct solo6010_dev *solo_dev)
for (i = 0; i < SOLO_NR_P2M; i++) { for (i = 0; i < SOLO_NR_P2M; i++) {
p2m_dev = &solo_dev->p2m_dev[i]; p2m_dev = &solo_dev->p2m_dev[i];
sema_init(&p2m_dev->sem, 1); mutex_init(&p2m_dev->mutex);
init_completion(&p2m_dev->completion); 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_CONTROL(i), 0);
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
SOLO_P2M_CSC_16BIT_565 | SOLO_P2M_CSC_16BIT_565 |
SOLO_P2M_DMA_INTERVAL(0) | SOLO_P2M_DMA_INTERVAL(3) |
SOLO_P2M_DESC_INTR_OPT |
SOLO_P2M_PCI_MASTER_MODE); SOLO_P2M_PCI_MASTER_MODE);
solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i)); solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i));
} }
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/videobuf-dma-contig.h> #include <media/videobuf-dma-sg.h>
#include "solo6010.h" #include "solo6010.h"
#include "solo6010-tw28.h" #include "solo6010-tw28.h"
...@@ -47,13 +47,14 @@ struct solo_enc_fh { ...@@ -47,13 +47,14 @@ struct solo_enc_fh {
struct videobuf_queue vidq; struct videobuf_queue vidq;
struct list_head vidq_active; struct list_head vidq_active;
struct task_struct *kthread; struct task_struct *kthread;
struct p2m_desc desc[SOLO_NR_P2M_DESC];
}; };
static unsigned char vid_vop_header[] = { static unsigned char vid_vop_header[] = {
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
0x02, 0x48, 0x05, 0xc0, 0x00, 0x40, 0x00, 0x40, 0x02, 0x48, 0x05, 0xc0, 0x00, 0x40, 0x00, 0x40,
0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, 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) ...@@ -151,6 +152,11 @@ static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
else else
solo_dev->motion_mask &= ~(1 << ch); 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_reg_write(solo_dev, SOLO_VI_MOT_ADR,
SOLO_VI_MOTION_EN(solo_dev->motion_mask) | SOLO_VI_MOTION_EN(solo_dev->motion_mask) |
(SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
...@@ -211,11 +217,6 @@ static int solo_enc_on(struct solo_enc_fh *fh) ...@@ -211,11 +217,6 @@ static int solo_enc_on(struct solo_enc_fh *fh)
solo_dev->enc_bw_remain -= solo_enc->bw_weight; 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->enc_on = 1;
fh->rd_idx = solo_enc->solo_dev->enc_wr_idx; fh->rd_idx = solo_enc->solo_dev->enc_wr_idx;
...@@ -279,6 +280,24 @@ static void solo_enc_off(struct solo_enc_fh *fh) ...@@ -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); 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) static void enc_reset_gop(struct solo6010_dev *solo_dev, u8 ch)
{ {
BUG_ON(ch >= solo_dev->nr_chans); 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) ...@@ -299,22 +318,68 @@ static int enc_gop_reset(struct solo6010_dev *solo_dev, u8 ch, u8 vop)
return 0; 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)
unsigned int off, unsigned 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; int ret;
if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
return -EINVAL; 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, return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
}
/* Buffer wrap */ /* Buffer wrap */
ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
SOLO_MP4E_EXT_ADDR(solo_dev) + off, SOLO_MP4E_EXT_ADDR(solo_dev) + off,
SOLO_MP4E_EXT_SIZE(solo_dev) - off); SOLO_MP4E_EXT_SIZE(solo_dev) - off);
ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0,
buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off, 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, ...@@ -337,70 +402,108 @@ static int enc_get_mpeg_dma(struct solo6010_dev *solo_dev, void *buf,
return ret; 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,
unsigned int off, unsigned int size) struct p2m_desc *desc,
struct scatterlist *sglist, int skip,
unsigned int off, unsigned int size)
{ {
int ret; int ret;
if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) if (off > SOLO_JPEG_EXT_SIZE(solo_dev))
return -EINVAL; return -EINVAL;
if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) {
return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_JPEG, 0, buf, return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG,
SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); desc, 0, sglist, skip,
SOLO_JPEG_EXT_ADDR(solo_dev) + off, size);
}
/* Buffer wrap */ /* Buffer wrap */
ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_JPEG, 0, buf, ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
SOLO_JPEG_EXT_ADDR(solo_dev) + off, sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off,
SOLO_JPEG_EXT_SIZE(solo_dev) - off); SOLO_JPEG_EXT_SIZE(solo_dev) - off);
ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_JPEG, 0, ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
buf + SOLO_JPEG_EXT_SIZE(solo_dev) - off, sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
SOLO_JPEG_EXT_ADDR(solo_dev), SOLO_JPEG_EXT_ADDR(solo_dev),
size + off - SOLO_JPEG_EXT_SIZE(solo_dev)); size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
return ret; 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, 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 = fh->enc->solo_dev;
struct solo6010_dev *solo_dev = solo_enc->solo_dev; int size = enc_buf->jpeg_size;
u8 *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
memcpy(p, jpeg_header, sizeof(jpeg_header)); /* Copy the header first (direct write) */
p[SOF0_START + 5] = 0xff & (solo_enc->height >> 8); solo_jpeg_header(fh->enc, vbuf);
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;
vbuf += sizeof(jpeg_header); vb->size = size + sizeof(jpeg_header);
vb->size = enc_buf->jpeg_size + sizeof(jpeg_header);
return enc_get_jpeg_dma(solo_dev, vbuf, enc_buf->jpeg_off, /* Grab the jpeg frame */
enc_buf->jpeg_size); 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, 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 solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev; struct solo6010_dev *solo_dev = solo_enc->solo_dev;
struct vop_header vh; struct vop_header vh;
int ret; int ret;
int frame_size, frame_off; int frame_size, frame_off;
int skip = 0;
if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh)))
return -1; return -EINVAL;
/* First get the hardware vop header (not real mpeg) */ /* First get the hardware vop header (not real mpeg) */
ret = enc_get_mpeg_dma(solo_dev, &vh, enc_buf->off, sizeof(vh)); ret = enc_get_mpeg_dma(solo_dev, &vh, enc_buf->off, sizeof(vh));
if (ret) if (WARN_ON_ONCE(ret))
return -1; return ret;
if (WARN_ON_ONCE(vh.size > enc_buf->size)) if (WARN_ON_ONCE(vh.size > enc_buf->size))
return -1; return -EINVAL;
vb->width = vh.hsize << 4; vb->width = vh.hsize << 4;
vb->height = vh.vsize << 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, ...@@ -410,9 +513,9 @@ static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
if (!enc_buf->vop) { if (!enc_buf->vop) {
u16 fps = solo_dev->fps * 1000; u16 fps = solo_dev->fps * 1000;
u16 interval = solo_enc->interval * 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) if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
p[10] |= ((XVID_PAR_43_NTSC << 3) & 0x78); 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, ...@@ -434,43 +537,49 @@ static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
if (vh.interlace) if (vh.interlace)
p[29] |= 0x20; p[29] |= 0x20;
enc_write_sg(vbuf->sglist, p, sizeof(p));
/* Adjust the dma buffer past this header */ /* Adjust the dma buffer past this header */
vb->size += sizeof(vid_vop_header); vb->size += sizeof(vid_vop_header);
vbuf += sizeof(vid_vop_header); skip = sizeof(vid_vop_header);
} }
/* Now get the actual mpeg payload */ /* Now get the actual mpeg payload */
frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev);
frame_size = enc_buf->size - sizeof(vh); 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 void solo_enc_fillbuf(struct solo_enc_fh *fh,
static int solo_enc_fillbuf(struct solo_enc_fh *fh,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
struct solo_enc_dev *solo_enc = fh->enc; struct solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev; struct solo6010_dev *solo_dev = solo_enc->solo_dev;
struct solo_enc_buf *enc_buf = NULL; struct solo_enc_buf *enc_buf = NULL;
dma_addr_t vbuf; struct videobuf_dmabuf *vbuf;
int ret; int ret;
int error = 1;
u16 idx = fh->rd_idx; u16 idx = fh->rd_idx;
while (idx != solo_dev->enc_wr_idx) { while (idx != solo_dev->enc_wr_idx) {
struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx]; struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx];
idx = (idx + 1) % SOLO_NR_RING_BUFS; idx = (idx + 1) % SOLO_NR_RING_BUFS;
if (ebuf->ch != solo_enc->ch)
continue;
if (fh->fmt == V4L2_PIX_FMT_MPEG) { if (fh->fmt == V4L2_PIX_FMT_MPEG) {
if (fh->type != ebuf->type) if (fh->type == ebuf->type) {
continue;
if (ebuf->ch == solo_enc->ch) {
enc_buf = ebuf; enc_buf = ebuf;
break; break;
} }
} else if (ebuf->ch == solo_enc->ch) { } else {
/* For mjpeg, keep reading to the newest frame */ /* For mjpeg, keep reading to the newest frame */
enc_buf = ebuf; enc_buf = ebuf;
} }
...@@ -478,48 +587,54 @@ static int solo_enc_fillbuf(struct solo_enc_fh *fh, ...@@ -478,48 +587,54 @@ static int solo_enc_fillbuf(struct solo_enc_fh *fh,
fh->rd_idx = idx; fh->rd_idx = idx;
if (!enc_buf) if (WARN_ON_ONCE(!enc_buf))
return -1; goto buf_err;
if ((fh->fmt == V4L2_PIX_FMT_MPEG && if ((fh->fmt == V4L2_PIX_FMT_MPEG &&
vb->bsize < enc_buf->size) || vb->bsize < enc_buf->size) ||
(fh->fmt == V4L2_PIX_FMT_MJPEG && (fh->fmt == V4L2_PIX_FMT_MJPEG &&
vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { 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))) if (WARN_ON_ONCE(!(vbuf = videobuf_to_dma(vb))))
return -1; goto buf_err;
/* Is it ok that we mess with this buffer out of lock? */
spin_unlock(&solo_enc->lock);
if (fh->fmt == V4L2_PIX_FMT_MPEG) if (fh->fmt == V4L2_PIX_FMT_MPEG)
ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf); ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf);
else else
ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf);
if (ret) // Ignore failures if (!ret)
return 0; error = 0;
list_del(&vb->queue); buf_err:
vb->field_count++; if (error) {
vb->ts = enc_buf->ts; vb->state = VIDEOBUF_ERROR;
vb->state = VIDEOBUF_DONE; } else {
vb->field_count++;
vb->ts = enc_buf->ts;
vb->state = VIDEOBUF_DONE;
}
wake_up(&vb->done); wake_up(&vb->done);
return 0; return;
} }
static void solo_enc_thread_try(struct solo_enc_fh *fh) static void solo_enc_thread_try(struct solo_enc_fh *fh)
{ {
struct solo_enc_dev *solo_enc = fh->enc; struct solo_enc_dev *solo_enc = fh->enc;
struct solo6010_dev *solo_dev = solo_enc->solo_dev;
struct videobuf_buffer *vb; struct videobuf_buffer *vb;
for (;;) { for (;;) {
spin_lock(&solo_enc->lock); spin_lock(&solo_enc->lock);
if (fh->rd_idx == solo_dev->enc_wr_idx)
break;
if (list_empty(&fh->vidq_active)) if (list_empty(&fh->vidq_active))
break; break;
...@@ -529,9 +644,11 @@ static void solo_enc_thread_try(struct solo_enc_fh *fh) ...@@ -529,9 +644,11 @@ static void solo_enc_thread_try(struct solo_enc_fh *fh)
if (!waitqueue_active(&vb->done)) if (!waitqueue_active(&vb->done))
break; break;
/* On success, returns with solo_enc->lock unlocked */ list_del(&vb->queue);
if (solo_enc_fillbuf(fh, vb))
break; spin_unlock(&solo_enc->lock);
solo_enc_fillbuf(fh, vb);
} }
assert_spin_locked(&solo_enc->lock); assert_spin_locked(&solo_enc->lock);
...@@ -557,7 +674,7 @@ static int solo_enc_thread(void *data) ...@@ -557,7 +674,7 @@ static int solo_enc_thread(void *data)
remove_wait_queue(&solo_enc->thread_wait, &wait); remove_wait_queue(&solo_enc->thread_wait, &wait);
return 0; return 0;
} }
void solo_motion_isr(struct solo6010_dev *solo_dev) void solo_motion_isr(struct solo6010_dev *solo_dev)
...@@ -669,12 +786,12 @@ void solo_enc_v4l2_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, static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size) 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; *count = MIN_VID_BUFFERS;
return 0; return 0;
} }
static int solo_enc_buf_prepare(struct videobuf_queue *vq, static int solo_enc_buf_prepare(struct videobuf_queue *vq,
...@@ -696,7 +813,9 @@ 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) { if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL); int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) { if (rc < 0) {
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; vb->state = VIDEOBUF_NEEDS_INIT;
return rc; return rc;
} }
...@@ -719,7 +838,10 @@ static void solo_enc_buf_queue(struct videobuf_queue *vq, ...@@ -719,7 +838,10 @@ static void solo_enc_buf_queue(struct videobuf_queue *vq,
static void solo_enc_buf_release(struct videobuf_queue *vq, static void solo_enc_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
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; vb->state = VIDEOBUF_NEEDS_INIT;
} }
...@@ -753,22 +875,18 @@ static int solo_enc_open(struct file *file) ...@@ -753,22 +875,18 @@ static int solo_enc_open(struct file *file)
if ((fh = kzalloc(sizeof(*fh), GFP_KERNEL)) == NULL) if ((fh = kzalloc(sizeof(*fh), GFP_KERNEL)) == NULL)
return -ENOMEM; return -ENOMEM;
spin_lock(&solo_enc->lock);
fh->enc = solo_enc; fh->enc = solo_enc;
file->private_data = fh; file->private_data = fh;
INIT_LIST_HEAD(&fh->vidq_active); INIT_LIST_HEAD(&fh->vidq_active);
fh->fmt = V4L2_PIX_FMT_MPEG; fh->fmt = V4L2_PIX_FMT_MPEG;
fh->type = SOLO_ENC_TYPE_STD; fh->type = SOLO_ENC_TYPE_STD;
videobuf_queue_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->solo_dev->pdev->dev,
&solo_enc->lock, &solo_enc->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED, V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), fh, NULL); sizeof(struct videobuf_buffer), fh, NULL);
spin_unlock(&solo_enc->lock);
return 0; return 0;
} }
...@@ -785,7 +903,11 @@ static ssize_t solo_enc_read(struct file *file, char __user *data, ...@@ -785,7 +903,11 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
spin_lock(&solo_enc->lock); spin_lock(&solo_enc->lock);
ret = solo_enc_on(fh); 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) if (ret)
return ret; return ret;
} }
...@@ -797,10 +919,15 @@ static ssize_t solo_enc_read(struct file *file, char __user *data, ...@@ -797,10 +919,15 @@ static ssize_t solo_enc_read(struct file *file, char __user *data,
static int solo_enc_release(struct file *file) static int solo_enc_release(struct file *file)
{ {
struct solo_enc_fh *fh = file->private_data; struct solo_enc_fh *fh = file->private_data;
struct solo_enc_dev *solo_enc = fh->enc;
videobuf_stop(&fh->vidq); videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq); videobuf_mmap_free(&fh->vidq);
spin_lock(&solo_enc->lock);
solo_enc_off(fh); solo_enc_off(fh);
spin_unlock(&solo_enc->lock);
kfree(fh); kfree(fh);
return 0; return 0;
...@@ -842,7 +969,7 @@ static int solo_enc_enum_input(struct file *file, void *priv, ...@@ -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) if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
input->std = V4L2_STD_NTSC_M; input->std = V4L2_STD_NTSC_M;
else else
input->std = V4L2_STD_PAL_M; input->std = V4L2_STD_PAL_B;
if (!tw28_get_video_status(solo_dev, solo_enc->ch)) if (!tw28_get_video_status(solo_dev, solo_enc->ch))
input->status = V4L2_IN_ST_NO_SIGNAL; input->status = V4L2_IN_ST_NO_SIGNAL;
...@@ -956,7 +1083,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv, ...@@ -956,7 +1083,10 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv,
spin_unlock(&solo_enc->lock); 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, 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, ...@@ -1014,6 +1144,10 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
spin_unlock(&solo_enc->lock); spin_unlock(&solo_enc->lock);
if (ret) if (ret)
return ret; return ret;
ret = solo_start_fh_thread(fh);
if (ret)
return ret;
} }
ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
...@@ -1033,12 +1167,16 @@ static int solo_enc_dqbuf(struct file *file, void *priv, ...@@ -1033,12 +1167,16 @@ static int solo_enc_dqbuf(struct file *file, void *priv,
/* Check for key frame on mpeg data */ /* Check for key frame on mpeg data */
if (fh->fmt == V4L2_PIX_FMT_MPEG) { if (fh->fmt == V4L2_PIX_FMT_MPEG) {
struct videobuf_buffer *vb = fh->vidq.bufs[buf->index]; struct videobuf_dmabuf *vbuf =
u8 *p = videobuf_queue_to_vaddr(&fh->vidq, vb); videobuf_to_dma(fh->vidq.bufs[buf->index]);
if (p[3] == 0x00)
buf->flags |= V4L2_BUF_FLAG_KEYFRAME; if (vbuf) {
else u8 *p = sg_virt(vbuf->sglist);
buf->flags |= V4L2_BUF_FLAG_PFRAME; if (p[3] == 0x00)
buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
else
buf->flags |= V4L2_BUF_FLAG_PFRAME;
}
} }
return 0; return 0;
...@@ -1136,7 +1274,7 @@ static int solo_g_parm(struct file *file, void *priv, ...@@ -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? */ /* XXX: Shouldn't we be able to get/set this from videobuf? */
cp->readbuffers = 2; cp->readbuffers = 2;
return 0; return 0;
} }
static int solo_s_parm(struct file *file, void *priv, static int solo_s_parm(struct file *file, void *priv,
...@@ -1176,7 +1314,7 @@ 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); spin_unlock(&solo_enc->lock);
return 0; return 0;
} }
static int solo_queryctrl(struct file *file, void *priv, static int solo_queryctrl(struct file *file, void *priv,
...@@ -1240,7 +1378,7 @@ 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 0;
} }
return -EINVAL; return -EINVAL;
} }
static int solo_querymenu(struct file *file, void *priv, static int solo_querymenu(struct file *file, void *priv,
...@@ -1350,9 +1488,9 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, ...@@ -1350,9 +1488,9 @@ static int solo_s_ext_ctrls(struct file *file, void *priv,
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_RDS_TX_RADIO_TEXT: case V4L2_CID_RDS_TX_RADIO_TEXT:
if (ctrl->size - 1 > OSD_TEXT_MAX) if (ctrl->size - 1 > OSD_TEXT_MAX)
err = -ERANGE; err = -ERANGE;
else { else {
err = copy_from_user(solo_enc->osd_text, err = copy_from_user(solo_enc->osd_text,
ctrl->string, ctrl->string,
OSD_TEXT_MAX); OSD_TEXT_MAX);
solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; solo_enc->osd_text[OSD_TEXT_MAX] = '\0';
...@@ -1459,7 +1597,7 @@ static struct video_device solo_enc_template = { ...@@ -1459,7 +1597,7 @@ static struct video_device solo_enc_template = {
.minor = -1, .minor = -1,
.release = video_device_release, .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, .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) ...@@ -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); atomic_set(&solo_enc->readers, 0);
solo_enc->qp = SOLO_DEFAULT_QP; solo_enc->qp = SOLO_DEFAULT_QP;
solo_enc->gop = solo_dev->fps; solo_enc->gop = solo_dev->fps;
solo_enc->interval = 1; solo_enc->interval = 1;
solo_enc->mode = SOLO_ENC_MODE_CIF; solo_enc->mode = SOLO_ENC_MODE_CIF;
solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH;
......
...@@ -24,14 +24,13 @@ ...@@ -24,14 +24,13 @@
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/videobuf-dma-contig.h> #include <media/videobuf-dma-sg.h>
#include "solo6010.h" #include "solo6010.h"
#include "solo6010-tw28.h" #include "solo6010-tw28.h"
#define SOLO_HW_BPL 2048 #define SOLO_HW_BPL 2048
#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED #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 */ /* Image size is two fields, SOLO_HW_BPL is one horizontal line */
#define solo_vlines(__solo) (__solo->video_vsize * 2) #define solo_vlines(__solo) (__solo->video_vsize * 2)
...@@ -49,6 +48,8 @@ struct solo_filehandle { ...@@ -49,6 +48,8 @@ struct solo_filehandle {
spinlock_t slock; spinlock_t slock;
int old_write; int old_write;
struct list_head vidq_active; struct list_head vidq_active;
struct p2m_desc desc[SOLO_NR_P2M_DESC];
int desc_idx;
}; };
unsigned video_nr = -1; unsigned video_nr = -1;
...@@ -96,7 +97,7 @@ static void solo_win_setup(struct solo6010_dev *solo_dev, u8 ch, ...@@ -96,7 +97,7 @@ static void solo_win_setup(struct solo6010_dev *solo_dev, u8 ch,
SOLO_VI_WIN_EX(ex) | SOLO_VI_WIN_EX(ex) |
SOLO_VI_WIN_SCALE(scale)); 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_SY(sy) |
SOLO_VI_WIN_EY(ey)); SOLO_VI_WIN_EY(ey));
} }
...@@ -203,50 +204,148 @@ static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch) ...@@ -203,50 +204,148 @@ static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch)
return 0; return 0;
} }
static void disp_reset_desc(struct solo_filehandle *fh)
{
/* We use desc mode, which ignores desc 0 */
memset(fh->desc, 0, sizeof(*fh->desc));
fh->desc_idx = 1;
}
static int disp_flush_descs(struct solo_filehandle *fh)
{
int ret;
if (!fh->desc_idx)
return 0;
ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
fh->desc, fh->desc_idx);
disp_reset_desc(fh);
return ret;
}
static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
u32 ext_addr, int size, int repeat, int ext_size)
{
if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
int ret = disp_flush_descs(fh);
if (ret)
return ret;
}
solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
size, repeat, ext_size);
fh->desc_idx++;
return 0;
}
static void solo_fillbuf(struct solo_filehandle *fh, static void solo_fillbuf(struct solo_filehandle *fh,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
struct solo6010_dev *solo_dev = fh->solo_dev; struct solo6010_dev *solo_dev = fh->solo_dev;
dma_addr_t vbuf; struct videobuf_dmabuf* vbuf;
unsigned int fdma_addr; unsigned int fdma_addr;
int frame_size;
int error = 1; int error = 1;
int i; 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; goto finish_buf;
if (erase_off(solo_dev)) { if (erase_off(solo_dev)) {
void *p = videobuf_queue_to_vaddr(&fh->vidq, vb); int i;
int image_size = solo_image_size(solo_dev);
for (i = 0; i < image_size; i += 2) { /* Just blit to the entire sg list, ignoring size */
((u8 *)p)[i] = 0x80; for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
((u8 *)p)[i + 1] = 0x00; 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; error = 0;
goto finish_buf; goto finish_buf;
} }
frame_size = SOLO_HW_BPL * solo_vlines(solo_dev); disp_reset_desc(fh);
fdma_addr = SOLO_DISP_EXT_ADDR(solo_dev) + (fh->old_write * frame_size); 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++) { for (i = 0; i < solo_vlines(solo_dev); i++) {
int j; int line_len = solo_bytesperline(solo_dev);
for (j = 0; j < (SOLO_DISP_BUF_SIZE / SOLO_HW_BPL); j++) { int lines;
if (solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_DISP, 0,
vbuf, fdma_addr + (j * SOLO_HW_BPL), if (!sg_size_left) {
solo_bytesperline(solo_dev))) sg = sg_next(sg);
if (sg == NULL)
goto finish_buf; 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: finish_buf:
if (error) { if (error) {
vb->state = VIDEOBUF_ERROR; vb->state = VIDEOBUF_ERROR;
} else { } else {
vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
vb->state = VIDEOBUF_DONE; vb->state = VIDEOBUF_DONE;
vb->field_count++; vb->field_count++;
do_gettimeofday(&vb->ts); do_gettimeofday(&vb->ts);
...@@ -275,7 +374,7 @@ static void solo_thread_try(struct solo_filehandle *fh) ...@@ -275,7 +374,7 @@ static void solo_thread_try(struct solo_filehandle *fh)
break; break;
cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev, cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
SOLO_VI_STATUS0)); SOLO_VI_STATUS0));
if (cur_write == fh->old_write) if (cur_write == fh->old_write)
break; break;
...@@ -310,7 +409,7 @@ static int solo_thread(void *data) ...@@ -310,7 +409,7 @@ static int solo_thread(void *data)
remove_wait_queue(&solo_dev->disp_thread_wait, &wait); remove_wait_queue(&solo_dev->disp_thread_wait, &wait);
return 0; return 0;
} }
static int solo_start_thread(struct solo_filehandle *fh) 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, ...@@ -337,12 +436,12 @@ static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
struct solo_filehandle *fh = vq->priv_data; struct solo_filehandle *fh = vq->priv_data;
struct solo6010_dev *solo_dev = fh->solo_dev; 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; *count = MIN_VID_BUFFERS;
return 0; return 0;
} }
static int solo_buf_prepare(struct videobuf_queue *vq, static int solo_buf_prepare(struct videobuf_queue *vq,
...@@ -364,7 +463,9 @@ 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) { if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL); int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) { if (rc < 0) {
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; vb->state = VIDEOBUF_NEEDS_INIT;
return rc; return rc;
} }
...@@ -388,7 +489,10 @@ static void solo_buf_queue(struct videobuf_queue *vq, ...@@ -388,7 +489,10 @@ static void solo_buf_queue(struct videobuf_queue *vq,
static void solo_buf_release(struct videobuf_queue *vq, static void solo_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb) struct videobuf_buffer *vb)
{ {
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; vb->state = VIDEOBUF_NEEDS_INIT;
} }
...@@ -404,7 +508,7 @@ static unsigned int solo_v4l2_poll(struct file *file, ...@@ -404,7 +508,7 @@ static unsigned int solo_v4l2_poll(struct file *file,
{ {
struct solo_filehandle *fh = file->private_data; 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) 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) ...@@ -433,11 +537,11 @@ static int solo_v4l2_open(struct file *file)
return ret; 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, &solo_dev->pdev->dev, &fh->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
SOLO_DISP_PIX_FIELD, SOLO_DISP_PIX_FIELD,
sizeof(struct videobuf_buffer), fh, NULL); sizeof(struct videobuf_buffer), fh, NULL);
return 0; return 0;
} }
...@@ -530,7 +634,7 @@ static int solo_enum_input(struct file *file, void *priv, ...@@ -530,7 +634,7 @@ static int solo_enum_input(struct file *file, void *priv,
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
input->std = V4L2_STD_NTSC_M; input->std = V4L2_STD_NTSC_M;
else else
input->std = V4L2_STD_PAL_M; input->std = V4L2_STD_PAL_B;
return 0; return 0;
} }
...@@ -781,11 +885,11 @@ static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { ...@@ -781,11 +885,11 @@ static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
.vidioc_qbuf = solo_qbuf, .vidioc_qbuf = solo_qbuf,
.vidioc_dqbuf = solo_dqbuf, .vidioc_dqbuf = solo_dqbuf,
.vidioc_streamon = solo_streamon, .vidioc_streamon = solo_streamon,
.vidioc_streamoff = solo_streamoff, .vidioc_streamoff = solo_streamoff,
/* Controls */ /* Controls */
.vidioc_queryctrl = solo_disp_queryctrl, .vidioc_queryctrl = solo_disp_queryctrl,
.vidioc_g_ctrl = solo_disp_g_ctrl, .vidioc_g_ctrl = solo_disp_g_ctrl,
.vidioc_s_ctrl = solo_disp_s_ctrl, .vidioc_s_ctrl = solo_disp_s_ctrl,
}; };
static struct video_device solo_v4l2_template = { static struct video_device solo_v4l2_template = {
...@@ -795,7 +899,7 @@ static struct video_device solo_v4l2_template = { ...@@ -795,7 +899,7 @@ static struct video_device solo_v4l2_template = {
.minor = -1, .minor = -1,
.release = video_device_release, .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, .current_norm = V4L2_STD_NTSC_M,
}; };
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
#include <linux/semaphore.h> #include <linux/semaphore.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/delay.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/delay.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/atomic.h> #include <asm/atomic.h>
...@@ -48,10 +48,14 @@ ...@@ -48,10 +48,14 @@
#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 #define PCI_DEVICE_ID_NEUSOLO_4 0x4304
#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 #define PCI_DEVICE_ID_NEUSOLO_9 0x4309
#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 #define PCI_DEVICE_ID_NEUSOLO_16 0x4310
/* Commell Softlogic 6010 based cards */ /* Bluecherry Softlogic 6010 based cards */
#define PCI_DEVICE_ID_COMMSOLO_4 0x4E04 #define PCI_DEVICE_ID_BC_SOLO_4 0x4E04
#define PCI_DEVICE_ID_COMMSOLO_9 0x4E09 #define PCI_DEVICE_ID_BC_SOLO_9 0x4E09
#define PCI_DEVICE_ID_COMMSOLO_16 0x4E10 #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 */ #endif /* Bluecherry */
#define SOLO6010_NAME "solo6010" #define SOLO6010_NAME "solo6010"
...@@ -78,7 +82,6 @@ ...@@ -78,7 +82,6 @@
/* DMA Engine setup */ /* DMA Engine setup */
#define SOLO_NR_P2M 4 #define SOLO_NR_P2M 4
#define SOLO_NR_P2M_DESC 256 #define SOLO_NR_P2M_DESC 256
#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16)
/* MPEG and JPEG share the same interrupt and locks so they must be together /* MPEG and JPEG share the same interrupt and locks so they must be together
* in the same dma channel. */ * in the same dma channel. */
#define SOLO_P2M_DMA_ID_MP4E 0 #define SOLO_P2M_DMA_ID_MP4E 0
...@@ -123,11 +126,17 @@ enum SOLO_I2C_STATE { ...@@ -123,11 +126,17 @@ enum SOLO_I2C_STATE {
IIC_STATE_STOP IIC_STATE_STOP
}; };
struct p2m_desc {
u32 ctrl;
u32 ext;
u32 ta;
u32 fa;
};
struct solo_p2m_dev { struct solo_p2m_dev {
struct semaphore sem; struct mutex mutex;
struct completion completion; struct completion completion;
int error; int error;
u8 desc[SOLO_P2M_DESC_SIZE];
}; };
#define OSD_TEXT_MAX 30 #define OSD_TEXT_MAX 30
...@@ -185,7 +194,7 @@ struct solo6010_dev { ...@@ -185,7 +194,7 @@ struct solo6010_dev {
/* i2c related items */ /* i2c related items */
struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS];
enum SOLO_I2C_STATE i2c_state; enum SOLO_I2C_STATE i2c_state;
struct semaphore i2c_sem; struct mutex i2c_mutex;
int i2c_id; int i2c_id;
wait_queue_head_t i2c_wait; wait_queue_head_t i2c_wait;
struct i2c_msg *i2c_msg; struct i2c_msg *i2c_msg;
...@@ -306,6 +315,14 @@ int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, ...@@ -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); dma_addr_t dma_addr, u32 ext_addr, u32 size);
int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
void *sys_addr, u32 ext_addr, u32 size); 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 */ /* Set the threshold for motion detection */
void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val); 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