Commit dc02d50a authored by Hans Verkuil's avatar Hans Verkuil Committed by 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: default avatarHans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 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 */
......
This diff is collapsed.
...@@ -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%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment