提交 dc02d50a 编写于 作者: H Hans Verkuil 提交者: Mauro Carvalho Chehab

V4L/DVB (5675): Move big PIO accesses from the interrupt handler to a workhandler

Sliced VBI transfers use PIO instead of DMA. This was done inside the
interrupt handler, but since PIO accesses are very slow this meant that
a lot of time was spent inside the interrupt handler. All PIO copies are
now moved to a workqueue. This should fix various issues with missing time
ticks and remote key hits.
Signed-off-by: NHans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: NMauro Carvalho Chehab <mchehab@infradead.org>
上级 ffeb9ec7
...@@ -652,6 +652,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) ...@@ -652,6 +652,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
itv->dma_timer.data = (unsigned long)itv; itv->dma_timer.data = (unsigned long)itv;
itv->cur_dma_stream = -1; itv->cur_dma_stream = -1;
itv->cur_pio_stream = -1;
itv->audio_stereo_mode = AUDIO_STEREO; itv->audio_stereo_mode = AUDIO_STEREO;
itv->audio_bilingual_mode = AUDIO_MONO_LEFT; itv->audio_bilingual_mode = AUDIO_MONO_LEFT;
......
...@@ -237,6 +237,7 @@ extern const u32 yuv_offset[4]; ...@@ -237,6 +237,7 @@ extern const u32 yuv_offset[4];
#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29) #define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29)
#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28) #define IVTV_IRQ_ENC_VIM_RST (0x1 << 28)
#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27) #define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27)
#define IVTV_IRQ_ENC_PIO_COMPLETE (0x1 << 25)
#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24) #define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24)
#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) #define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22)
#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) #define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20)
...@@ -247,7 +248,8 @@ extern const u32 yuv_offset[4]; ...@@ -247,7 +248,8 @@ extern const u32 yuv_offset[4];
#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) #define IVTV_IRQ_DEC_VSYNC (0x1 << 10)
/* IRQ Masks */ /* IRQ Masks */
#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ) #define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|\
IVTV_IRQ_DMA_READ|IVTV_IRQ_ENC_PIO_COMPLETE)
#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS) #define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG) #define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
...@@ -374,6 +376,9 @@ struct ivtv_mailbox_data { ...@@ -374,6 +376,9 @@ struct ivtv_mailbox_data {
#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */ #define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */
#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */ #define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */
#define IVTV_F_S_PIO_PENDING 9 /* this stream has pending PIO */
#define IVTV_F_S_PIO_HAS_VBI 1 /* the current PIO request also requests VBI data */
/* per-ivtv, i_flags */ /* per-ivtv, i_flags */
#define IVTV_F_I_DMA 0 /* DMA in progress */ #define IVTV_F_I_DMA 0 /* DMA in progress */
#define IVTV_F_I_UDMA 1 /* UDMA in progress */ #define IVTV_F_I_UDMA 1 /* UDMA in progress */
...@@ -390,8 +395,11 @@ struct ivtv_mailbox_data { ...@@ -390,8 +395,11 @@ struct ivtv_mailbox_data {
#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */ #define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */
#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */ #define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */
#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */ #define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */
#define IVTV_F_I_WORK_HANDLER_VBI 15 /* there is work to be done for VBI */ #define IVTV_F_I_HAVE_WORK 15 /* Used in the interrupt handler: there is work to be done */
#define IVTV_F_I_WORK_HANDLER_YUV 16 /* there is work to be done for YUV */ #define IVTV_F_I_WORK_HANDLER_VBI 16 /* there is work to be done for VBI */
#define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */
#define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */
#define IVTV_F_I_PIO 19 /* PIO in progress */
/* Event notifications */ /* Event notifications */
#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ #define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */
...@@ -484,6 +492,7 @@ struct ivtv_stream { ...@@ -484,6 +492,7 @@ struct ivtv_stream {
/* Base Dev SG Array for cx23415/6 */ /* Base Dev SG Array for cx23415/6 */
struct ivtv_SG_element *SGarray; struct ivtv_SG_element *SGarray;
struct ivtv_SG_element *PIOarray;
dma_addr_t SG_handle; dma_addr_t SG_handle;
int SG_length; int SG_length;
...@@ -706,6 +715,7 @@ struct ivtv { ...@@ -706,6 +715,7 @@ struct ivtv {
atomic_t decoding; /* count number of active decoding streams */ atomic_t decoding; /* count number of active decoding streams */
u32 irq_rr_idx; /* Round-robin stream index */ u32 irq_rr_idx; /* Round-robin stream index */
int cur_dma_stream; /* index of stream doing DMA */ int cur_dma_stream; /* index of stream doing DMA */
int cur_pio_stream; /* index of stream doing PIO */
u32 dma_data_req_offset; u32 dma_data_req_offset;
u32 dma_data_req_size; u32 dma_data_req_size;
int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */ int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */
......
...@@ -31,8 +31,6 @@ ...@@ -31,8 +31,6 @@
#define DMA_MAGIC_COOKIE 0x000001fe #define DMA_MAGIC_COOKIE 0x000001fe
#define SLICED_VBI_PIO 1
static void ivtv_dma_dec_start(struct ivtv_stream *s); static void ivtv_dma_dec_start(struct ivtv_stream *s);
static const int ivtv_stream_map[] = { static const int ivtv_stream_map[] = {
...@@ -42,12 +40,40 @@ static const int ivtv_stream_map[] = { ...@@ -42,12 +40,40 @@ static const int ivtv_stream_map[] = {
IVTV_ENC_STREAM_TYPE_VBI, IVTV_ENC_STREAM_TYPE_VBI,
}; };
static inline int ivtv_use_pio(struct ivtv_stream *s)
static void ivtv_pio_work_handler(struct ivtv *itv)
{ {
struct ivtv *itv = s->itv; struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream];
struct ivtv_buffer *buf;
struct list_head *p;
int i = 0;
IVTV_DEBUG_DMA("ivtv_pio_work_handler\n");
if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
s->v4l2dev == NULL || !ivtv_use_pio(s)) {
itv->cur_pio_stream = -1;
/* trigger PIO complete user interrupt */
write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
return;
}
IVTV_DEBUG_DMA("Process PIO %s\n", s->name);
buf = list_entry(s->q_dma.list.next, struct ivtv_buffer, list);
list_for_each(p, &s->q_dma.list) {
struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
u32 size = s->PIOarray[i].size & 0x3ffff;
return s->dma == PCI_DMA_NONE || /* Copy the data from the card to the buffer */
(SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set); if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
memcpy_fromio(buf->buf, itv->dec_mem + s->PIOarray[i].src - IVTV_DECODER_OFFSET, size);
}
else {
memcpy_fromio(buf->buf, itv->enc_mem + s->PIOarray[i].src, size);
}
if (s->PIOarray[i].size & 0x80000000)
break;
i++;
}
write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
} }
void ivtv_irq_work_handler(struct work_struct *work) void ivtv_irq_work_handler(struct work_struct *work)
...@@ -56,8 +82,11 @@ void ivtv_irq_work_handler(struct work_struct *work) ...@@ -56,8 +82,11 @@ void ivtv_irq_work_handler(struct work_struct *work)
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags))
ivtv_pio_work_handler(itv);
if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags)) if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
vbi_work_handler(itv); ivtv_vbi_work_handler(itv);
if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags)) if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
ivtv_yuv_work_handler(itv); ivtv_yuv_work_handler(itv);
...@@ -173,8 +202,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA ...@@ -173,8 +202,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
} }
s->buffers_stolen = rc; s->buffers_stolen = rc;
/* got the buffers, now fill in SGarray (DMA) or copy the data from the card /* got the buffers, now fill in SGarray (DMA) */
to the buffers (PIO). */
buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
memset(buf->buf, 0, 128); memset(buf->buf, 0, 128);
list_for_each(p, &s->q_predma.list) { list_for_each(p, &s->q_predma.list) {
...@@ -182,21 +210,11 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA ...@@ -182,21 +210,11 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
if (skip_bufs-- > 0) if (skip_bufs-- > 0)
continue; continue;
if (!ivtv_use_pio(s)) { s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle);
s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle); s->SGarray[idx].src = cpu_to_le32(offset);
s->SGarray[idx].src = cpu_to_le32(offset); s->SGarray[idx].size = cpu_to_le32(s->buf_size);
s->SGarray[idx].size = cpu_to_le32(s->buf_size);
}
buf->bytesused = (size < s->buf_size) ? size : s->buf_size; buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
/* If PIO, then copy the data from the card to the buffer */
if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused);
}
else if (ivtv_use_pio(s)) {
memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused);
}
s->q_predma.bytesused += buf->bytesused; s->q_predma.bytesused += buf->bytesused;
size -= buf->bytesused; size -= buf->bytesused;
offset += s->buf_size; offset += s->buf_size;
...@@ -224,11 +242,6 @@ static void dma_post(struct ivtv_stream *s) ...@@ -224,11 +242,6 @@ static void dma_post(struct ivtv_stream *s)
u32 *u32buf; u32 *u32buf;
int x = 0; int x = 0;
if (ivtv_use_pio(s)) {
if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
s->SG_length = 0;
}
IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
s->name, s->dma_offset); s->name, s->dma_offset);
list_for_each(p, &s->q_dma.list) { list_for_each(p, &s->q_dma.list) {
...@@ -278,10 +291,14 @@ static void dma_post(struct ivtv_stream *s) ...@@ -278,10 +291,14 @@ static void dma_post(struct ivtv_stream *s)
if (buf) if (buf)
buf->bytesused += s->dma_last_offset; buf->bytesused += s->dma_last_offset;
if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) { if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
/* Parse and Groom VBI Data */ list_for_each(p, &s->q_dma.list) {
s->q_dma.bytesused -= buf->bytesused; buf = list_entry(p, struct ivtv_buffer, list);
ivtv_process_vbi_data(itv, buf, 0, s->type);
s->q_dma.bytesused += buf->bytesused; /* Parse and Groom VBI Data */
s->q_dma.bytesused -= buf->bytesused;
ivtv_process_vbi_data(itv, buf, 0, s->type);
s->q_dma.bytesused += buf->bytesused;
}
if (s->id == -1) { if (s->id == -1) {
ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
return; return;
...@@ -351,10 +368,14 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) ...@@ -351,10 +368,14 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
int i; int i;
IVTV_DEBUG_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);
if (s->q_predma.bytesused) if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256); if (ivtv_use_dma(s))
s->SGarray[s->SG_length - 1].size =
cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256);
/* If this is an MPEG stream, and VBI data is also pending, then append the /* If this is an MPEG stream, and VBI data is also pending, then append the
VBI DMA to the MPEG DMA and transfer both sets of data at once. VBI DMA to the MPEG DMA and transfer both sets of data at once.
...@@ -368,7 +389,8 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) ...@@ -368,7 +389,8 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length && if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length &&
s->SG_length + s_vbi->SG_length <= s->buffers) { s->SG_length + s_vbi->SG_length <= s->buffers) {
ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused); ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256); if (ivtv_use_dma(s_vbi))
s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256);
for (i = 0; i < s_vbi->SG_length; i++) { for (i = 0; i < s_vbi->SG_length; i++) {
s->SGarray[s->SG_length++] = s_vbi->SGarray[i]; s->SGarray[s->SG_length++] = s_vbi->SGarray[i];
} }
...@@ -381,14 +403,26 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) ...@@ -381,14 +403,26 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
/* Mark last buffer size for Interrupt flag */ /* Mark last buffer size for Interrupt flag */
s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
/* Sync Hardware SG List of buffers */ if (ivtv_use_pio(s)) {
ivtv_stream_sync_for_device(s); for (i = 0; i < s->SG_length; i++) {
write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR); s->PIOarray[i].src = le32_to_cpu(s->SGarray[i].src);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); s->PIOarray[i].size = le32_to_cpu(s->SGarray[i].size);
set_bit(IVTV_F_I_DMA, &itv->i_flags); }
itv->cur_dma_stream = s->type; set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);
itv->dma_timer.expires = jiffies + HZ / 10; set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
add_timer(&itv->dma_timer); set_bit(IVTV_F_I_PIO, &itv->i_flags);
itv->cur_pio_stream = s->type;
}
else {
/* Sync Hardware SG List of buffers */
ivtv_stream_sync_for_device(s);
write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
set_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = s->type;
itv->dma_timer.expires = jiffies + HZ / 10;
add_timer(&itv->dma_timer);
}
} }
static void ivtv_dma_dec_start(struct ivtv_stream *s) static void ivtv_dma_dec_start(struct ivtv_stream *s)
...@@ -489,6 +523,40 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv) ...@@ -489,6 +523,40 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
wake_up(&itv->dma_waitq); wake_up(&itv->dma_waitq);
} }
static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
{
struct ivtv_stream *s;
if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {
itv->cur_pio_stream = -1;
return;
}
s = &itv->streams[itv->cur_pio_stream];
IVTV_DEBUG_IRQ("ENC PIO COMPLETE %s\n", s->name);
s->SG_length = 0;
clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
clear_bit(IVTV_F_I_PIO, &itv->i_flags);
itv->cur_pio_stream = -1;
dma_post(s);
if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);
else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);
else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)
ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);
clear_bit(IVTV_F_I_PIO, &itv->i_flags);
if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
u32 tmp;
s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
tmp = s->dma_offset;
s->dma_offset = itv->vbi.dma_offset;
dma_post(s);
s->dma_offset = tmp;
}
wake_up(&itv->dma_waitq);
}
static void ivtv_irq_dma_err(struct ivtv *itv) static void ivtv_irq_dma_err(struct ivtv *itv)
{ {
u32 data[CX2341X_MBOX_MAX_DATA]; u32 data[CX2341X_MBOX_MAX_DATA];
...@@ -532,13 +600,7 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv) ...@@ -532,13 +600,7 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv)
clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
s = &itv->streams[ivtv_stream_map[data[0]]]; s = &itv->streams[ivtv_stream_map[data[0]]];
if (!stream_enc_dma_append(s, data)) { if (!stream_enc_dma_append(s, data)) {
if (ivtv_use_pio(s)) { set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
dma_post(s);
ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]);
}
else {
set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
}
} }
} }
...@@ -551,15 +613,6 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) ...@@ -551,15 +613,6 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
IVTV_DEBUG_IRQ("ENC START VBI CAP\n"); IVTV_DEBUG_IRQ("ENC START VBI CAP\n");
s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
if (ivtv_use_pio(s)) {
if (stream_enc_dma_append(s, data))
return;
if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
s->SG_length = 0;
dma_post(s);
return;
}
/* If more than two VBI buffers are pending, then /* If more than two VBI buffers are pending, then
clear the old ones and start with this new one. clear the old ones and start with this new one.
This can happen during transition stages when MPEG capturing is This can happen during transition stages when MPEG capturing is
...@@ -582,11 +635,11 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) ...@@ -582,11 +635,11 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
if (!stream_enc_dma_append(s, data) && if (!stream_enc_dma_append(s, data) &&
!test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) { !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) {
set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
} }
} }
static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv) static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv)
{ {
u32 data[CX2341X_MBOX_MAX_DATA]; u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
...@@ -594,7 +647,7 @@ static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv) ...@@ -594,7 +647,7 @@ static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv)
IVTV_DEBUG_IRQ("DEC VBI REINSERT\n"); IVTV_DEBUG_IRQ("DEC VBI REINSERT\n");
if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) && if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
!stream_enc_dma_append(s, data)) { !stream_enc_dma_append(s, data)) {
dma_post(s); set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags);
} }
} }
...@@ -657,7 +710,6 @@ static void ivtv_irq_vsync(struct ivtv *itv) ...@@ -657,7 +710,6 @@ static void ivtv_irq_vsync(struct ivtv *itv)
} }
if (frame != (itv->lastVsyncFrame & 1)) { if (frame != (itv->lastVsyncFrame & 1)) {
struct ivtv_stream *s = ivtv_get_output_stream(itv); struct ivtv_stream *s = ivtv_get_output_stream(itv);
int work = 0;
itv->lastVsyncFrame += 1; itv->lastVsyncFrame += 1;
if (frame == 0) { if (frame == 0) {
...@@ -678,7 +730,7 @@ static void ivtv_irq_vsync(struct ivtv *itv) ...@@ -678,7 +730,7 @@ static void ivtv_irq_vsync(struct ivtv *itv)
/* Send VBI to saa7127 */ /* Send VBI to saa7127 */
if (frame) { if (frame) {
set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags); set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
work = 1; set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
} }
/* Check if we need to update the yuv registers */ /* Check if we need to update the yuv registers */
...@@ -691,11 +743,9 @@ static void ivtv_irq_vsync(struct ivtv *itv) ...@@ -691,11 +743,9 @@ static void ivtv_irq_vsync(struct ivtv *itv)
itv->yuv_info.new_frame_info[last_dma_frame].update = 0; itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
itv->yuv_info.yuv_forced_update = 0; itv->yuv_info.yuv_forced_update = 0;
set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags); set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
work = 1; set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
} }
} }
if (work)
queue_work(itv->irq_work_queues, &itv->irq_work_queue);
} }
} }
...@@ -755,6 +805,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) ...@@ -755,6 +805,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
ivtv_irq_enc_dma_complete(itv); ivtv_irq_enc_dma_complete(itv);
} }
if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) {
ivtv_irq_enc_pio_complete(itv);
}
if (combo & IVTV_IRQ_DMA_ERR) { if (combo & IVTV_IRQ_DMA_ERR) {
ivtv_irq_dma_err(itv); ivtv_irq_dma_err(itv);
} }
...@@ -768,7 +822,7 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) ...@@ -768,7 +822,7 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
} }
if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) { if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
ivtv_irq_dev_vbi_reinsert(itv); ivtv_irq_dec_vbi_reinsert(itv);
} }
if (combo & IVTV_IRQ_ENC_EOS) { if (combo & IVTV_IRQ_ENC_EOS) {
...@@ -813,6 +867,22 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) ...@@ -813,6 +867,22 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
} }
} }
if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS;
struct ivtv_stream *s = &itv->streams[idx];
if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags))
continue;
if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG)
ivtv_dma_enc_start(s);
break;
}
}
if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags))
queue_work(itv->irq_work_queues, &itv->irq_work_queue);
spin_unlock(&itv->dma_reg_lock); spin_unlock(&itv->dma_reg_lock);
/* If we've just handled a 'forced' vsync, it's safest to say it /* If we've just handled a 'forced' vsync, it's safest to say it
......
...@@ -195,14 +195,26 @@ int ivtv_stream_alloc(struct ivtv_stream *s) ...@@ -195,14 +195,26 @@ int ivtv_stream_alloc(struct ivtv_stream *s)
s->dma != PCI_DMA_NONE ? "DMA " : "", s->dma != PCI_DMA_NONE ? "DMA " : "",
s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
/* Allocate DMA SG Arrays */ if (ivtv_might_use_pio(s)) {
if (s->dma != PCI_DMA_NONE) { s->PIOarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL); if (s->PIOarray == NULL) {
if (s->SGarray == NULL) { IVTV_ERR("Could not allocate PIOarray for %s stream\n", s->name);
IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
return -ENOMEM; return -ENOMEM;
} }
s->SG_length = 0; }
/* Allocate DMA SG Arrays */
s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
if (s->SGarray == NULL) {
IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
if (ivtv_might_use_pio(s)) {
kfree(s->PIOarray);
s->PIOarray = NULL;
}
return -ENOMEM;
}
s->SG_length = 0;
if (ivtv_might_use_dma(s)) {
s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma); s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma);
ivtv_stream_sync_for_cpu(s); ivtv_stream_sync_for_cpu(s);
} }
...@@ -219,7 +231,7 @@ int ivtv_stream_alloc(struct ivtv_stream *s) ...@@ -219,7 +231,7 @@ int ivtv_stream_alloc(struct ivtv_stream *s)
break; break;
} }
INIT_LIST_HEAD(&buf->list); INIT_LIST_HEAD(&buf->list);
if (s->dma != PCI_DMA_NONE) { if (ivtv_might_use_dma(s)) {
buf->dma_handle = pci_map_single(s->itv->dev, buf->dma_handle = pci_map_single(s->itv->dev,
buf->buf, s->buf_size + 256, s->dma); buf->buf, s->buf_size + 256, s->dma);
ivtv_buf_sync_for_cpu(s, buf); ivtv_buf_sync_for_cpu(s, buf);
...@@ -242,7 +254,7 @@ void ivtv_stream_free(struct ivtv_stream *s) ...@@ -242,7 +254,7 @@ void ivtv_stream_free(struct ivtv_stream *s)
/* empty q_free */ /* empty q_free */
while ((buf = ivtv_dequeue(s, &s->q_free))) { while ((buf = ivtv_dequeue(s, &s->q_free))) {
if (s->dma != PCI_DMA_NONE) if (ivtv_might_use_dma(s))
pci_unmap_single(s->itv->dev, buf->dma_handle, pci_unmap_single(s->itv->dev, buf->dma_handle,
s->buf_size + 256, s->dma); s->buf_size + 256, s->dma);
kfree(buf->buf); kfree(buf->buf);
...@@ -256,6 +268,9 @@ void ivtv_stream_free(struct ivtv_stream *s) ...@@ -256,6 +268,9 @@ void ivtv_stream_free(struct ivtv_stream *s)
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
s->SG_handle = IVTV_DMA_UNMAPPED; s->SG_handle = IVTV_DMA_UNMAPPED;
} }
kfree(s->SGarray);
kfree(s->PIOarray);
s->PIOarray = NULL;
s->SGarray = NULL; s->SGarray = NULL;
s->SG_length = 0; s->SG_length = 0;
} }
......
...@@ -20,18 +20,43 @@ ...@@ -20,18 +20,43 @@
*/ */
#define IVTV_DMA_UNMAPPED ((u32) -1) #define IVTV_DMA_UNMAPPED ((u32) -1)
#define SLICED_VBI_PIO 1
/* ivtv_buffer utility functions */ /* ivtv_buffer utility functions */
static inline int ivtv_might_use_pio(struct ivtv_stream *s)
{
return s->dma == PCI_DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI);
}
static inline int ivtv_use_pio(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
return s->dma == PCI_DMA_NONE ||
(SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
}
static inline int ivtv_might_use_dma(struct ivtv_stream *s)
{
return s->dma != PCI_DMA_NONE;
}
static inline int ivtv_use_dma(struct ivtv_stream *s)
{
return !ivtv_use_pio(s);
}
static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
{ {
if (s->dma != PCI_DMA_NONE) if (ivtv_use_dma(s))
pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle, pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle,
s->buf_size + 256, s->dma); s->buf_size + 256, s->dma);
} }
static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
{ {
if (s->dma != PCI_DMA_NONE) if (ivtv_use_dma(s))
pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle, pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle,
s->buf_size + 256, s->dma); s->buf_size + 256, s->dma);
} }
...@@ -53,12 +78,14 @@ void ivtv_stream_free(struct ivtv_stream *s); ...@@ -53,12 +78,14 @@ void ivtv_stream_free(struct ivtv_stream *s);
static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
{ {
pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle, if (ivtv_use_dma(s))
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle,
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
} }
static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
{ {
pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle, if (ivtv_use_dma(s))
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle,
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
} }
...@@ -450,7 +450,7 @@ void ivtv_disable_vbi(struct ivtv *itv) ...@@ -450,7 +450,7 @@ void ivtv_disable_vbi(struct ivtv *itv)
} }
void vbi_work_handler(struct ivtv *itv) void ivtv_vbi_work_handler(struct ivtv *itv)
{ {
struct v4l2_sliced_vbi_data data; struct v4l2_sliced_vbi_data data;
......
...@@ -23,4 +23,4 @@ void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, ...@@ -23,4 +23,4 @@ void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
int ivtv_used_line(struct ivtv *itv, int line, int field); int ivtv_used_line(struct ivtv *itv, int line, int field);
void ivtv_disable_vbi(struct ivtv *itv); void ivtv_disable_vbi(struct ivtv *itv);
void ivtv_set_vbi(unsigned long arg); void ivtv_set_vbi(unsigned long arg);
void vbi_work_handler(struct ivtv *itv); void ivtv_vbi_work_handler(struct ivtv *itv);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册