Skip to content

Commit

Permalink
V4L/DVB (9720): cx18: Major rewrite of interrupt handling for incomin…
Browse files Browse the repository at this point in the history
…g mailbox processing

A major rewrite of interrupt handling for incoming mailbox processing, to split
the timing critical steps from the the deferrable steps as the sending XPU on
the CX23418 will time out and overwrite our incoming mailboxes rather quickly.
Setup a pool of work "order forms" for the irq handler to send jobs to the new
work handler routine which uses the kernel default work queue to do the
deferrable work.  Started optimizing some of the cx18-io calls as they are now
the low hanging fruit for recoving microseconds back from the timeline.
Future optimizations will get rid of mmio read retries, mmio stats logging, and
combine smaller functions in the irq path into the larger ones to save ~2 us
each.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Andy Walls authored and Mauro Carvalho Chehab committed Dec 30, 2008
1 parent ba38ee8 commit ee2d64f
Show file tree
Hide file tree
Showing 16 changed files with 408 additions and 215 deletions.
8 changes: 7 additions & 1 deletion drivers/media/video/cx18/cx18-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ static void cx18_process_options(struct cx18 *cx)
*/
static int __devinit cx18_init_struct1(struct cx18 *cx)
{
int i;

cx->base_addr = pci_resource_start(cx->dev, 0);

mutex_init(&cx->serialize_lock);
Expand All @@ -451,7 +453,11 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)

spin_lock_init(&cx->lock);

INIT_WORK(&cx->work, cx18_work_handler);
for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
cx->epu_work_order[i].cx = cx;
cx->epu_work_order[i].str = cx->epu_debug_str;
INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler);
}

/* start counting open_id at 1 */
cx->open_id = 1;
Expand Down
19 changes: 15 additions & 4 deletions drivers/media/video/cx18/cx18-driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,6 @@ struct cx18_options {
#define CX18_F_I_EOS 4 /* End of encoder stream */
#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */
#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */
#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */
#define CX18_F_I_INITED 21 /* set after first open */
#define CX18_F_I_FAILED 22 /* set if first open failed */

Expand Down Expand Up @@ -247,6 +245,19 @@ struct cx18_dvb {
struct cx18; /* forward reference */
struct cx18_scb; /* forward reference */

#define CX18_MAX_MDL_ACKS 2
#define CX18_MAX_EPU_WORK_ORDERS 70 /* CPU_DE_RELEASE_MDL bursts 63 commands */

struct cx18_epu_work_order {
struct work_struct work;
atomic_t pending;
struct cx18 *cx;
int rpu;
struct cx18_mailbox mb;
struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
char *str;
};

#define CX18_INVALID_TASK_HANDLE 0xffffffff

struct cx18_stream {
Expand Down Expand Up @@ -388,7 +399,6 @@ struct cx18 {
struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/


struct cx18_av_state av_state;

/* codec settings */
Expand Down Expand Up @@ -441,7 +451,8 @@ struct cx18 {
/* when the current DMA is finished this queue is woken up */
wait_queue_head_t dma_waitq;

struct work_struct work;
struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS];
char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */

/* i2c */
struct i2c_adapter i2c_adap[2];
Expand Down
25 changes: 0 additions & 25 deletions drivers/media/video/cx18/cx18-dvb.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
#include "cx18-dvb.h"
#include "cx18-io.h"
#include "cx18-streams.h"
#include "cx18-queue.h"
#include "cx18-scb.h"
#include "cx18-cards.h"
#include "s5h1409.h"
#include "mxl5005s.h"
Expand Down Expand Up @@ -305,26 +303,3 @@ static int dvb_register(struct cx18_stream *stream)

return ret;
}

void cx18_dvb_work_handler(struct cx18 *cx)
{
struct cx18_buffer *buf;
struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS];

while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) {
if (s->dvb.enabled)
dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
buf->bytesused);

cx18_buf_sync_for_device(s, buf);
cx18_enqueue(s, buf, &s->q_free);

if (s->handle == CX18_INVALID_TASK_HANDLE ||
!test_bit(CX18_F_S_STREAMING, &s->s_flags))
continue;

cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
(void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
1, buf->id, s->buf_size);
}
}
1 change: 0 additions & 1 deletion drivers/media/video/cx18/cx18-dvb.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@

int cx18_dvb_register(struct cx18_stream *stream);
void cx18_dvb_unregister(struct cx18_stream *stream);
void cx18_dvb_work_handler(struct cx18 *cx);
5 changes: 5 additions & 0 deletions drivers/media/video/cx18/cx18-firmware.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
if (cx18_raw_readl(cx, dst) != *src) {
CX18_ERR("Mismatch at offset %x\n", i);
release_firmware(fw);
cx18_setup_page(cx, 0);
return -EIO;
}
dst++;
Expand All @@ -131,6 +132,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
size = fw->size;
release_firmware(fw);
cx18_setup_page(cx, SCB_OFFSET);
return size;
}

Expand All @@ -150,6 +152,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
if (request_firmware(&fw, fn, &cx->dev->dev)) {
CX18_ERR("unable to open firmware %s\n", fn);
CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
cx18_setup_page(cx, 0);
return -ENOMEM;
}

Expand Down Expand Up @@ -185,6 +188,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
CX18_ERR("Mismatch at offset %x\n",
offset + j);
release_firmware(fw);
cx18_setup_page(cx, 0);
return -EIO;
}
}
Expand All @@ -196,6 +200,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
fn, apu_version, fw->size);
size = fw->size;
release_firmware(fw);
cx18_setup_page(cx, 0);
return size;
}

Expand Down
35 changes: 0 additions & 35 deletions drivers/media/video/cx18/cx18-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,41 +166,6 @@ u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr)
return val;
}

void cx18_memcpy_fromio(struct cx18 *cx, void *to,
const void __iomem *from, unsigned int len)
{
const u8 __iomem *src = from;
u8 *dst = to;

/* Align reads on the CX23418's addresses */
if ((len > 0) && ((unsigned long) src & 1)) {
*dst = cx18_readb(cx, src);
len--;
dst++;
src++;
}
if ((len > 1) && ((unsigned long) src & 2)) {
*((u16 *)dst) = cx18_raw_readw(cx, src);
len -= 2;
dst += 2;
src += 2;
}
while (len > 3) {
*((u32 *)dst) = cx18_raw_readl(cx, src);
len -= 4;
dst += 4;
src += 4;
}
if (len > 1) {
*((u16 *)dst) = cx18_raw_readw(cx, src);
len -= 2;
dst += 2;
src += 2;
}
if (len > 0)
*dst = cx18_readb(cx, src);
}

void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
{
u8 __iomem *dst = addr;
Expand Down
7 changes: 6 additions & 1 deletion drivers/media/video/cx18/cx18-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,13 @@ static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
}


static inline
void cx18_memcpy_fromio(struct cx18 *cx, void *to,
const void __iomem *from, unsigned int len);
const void __iomem *from, unsigned int len)
{
memcpy_fromio(to, from, len);
}

void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);


Expand Down
133 changes: 15 additions & 118 deletions drivers/media/video/cx18/cx18-irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,121 +21,9 @@

#include "cx18-driver.h"
#include "cx18-io.h"
#include "cx18-firmware.h"
#include "cx18-fileops.h"
#include "cx18-queue.h"
#include "cx18-irq.h"
#include "cx18-ioctl.h"
#include "cx18-mailbox.h"
#include "cx18-vbi.h"
#include "cx18-scb.h"
#include "cx18-dvb.h"

void cx18_work_handler(struct work_struct *work)
{
struct cx18 *cx = container_of(work, struct cx18, work);
if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags))
cx18_dvb_work_handler(cx);
}

static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb, int rpu)
{
u32 handle = mb->args[0];
struct cx18_stream *s = NULL;
struct cx18_buffer *buf;
u32 off;
int i;
int id;

for (i = 0; i < CX18_MAX_STREAMS; i++) {
s = &cx->streams[i];
if ((handle == s->handle) && (s->dvb.enabled))
break;
if (s->v4l2dev && handle == s->handle)
break;
}
if (i == CX18_MAX_STREAMS) {
CX18_WARN("Got DMA done notification for unknown/inactive"
" handle %d\n", handle);
mb->error = CXERR_NOT_OPEN;
mb->cmd = 0;
cx18_mb_ack(cx, mb, rpu);
return;
}

off = mb->args[1];
if (mb->args[2] != 1)
CX18_WARN("Ack struct = %d for %s\n",
mb->args[2], s->name);
id = cx18_read_enc(cx, off);
buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4));
CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
if (buf) {
cx18_buf_sync_for_cpu(s, buf);
if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
buf->bytesused);

set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags);
set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags);
} else
set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
} else {
CX18_WARN("Could not find buf %d for stream %s\n",
cx18_read_enc(cx, off), s->name);
}
mb->error = 0;
mb->cmd = 0;
cx18_mb_ack(cx, mb, rpu);
wake_up(&cx->dma_waitq);
if (s->id != -1)
wake_up(&s->waitq);
}

static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu)
{
char str[256] = { 0 };
char *p;

if (mb->args[1]) {
cx18_setup_page(cx, mb->args[1]);
cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
str[252] = 0;
}
cx18_mb_ack(cx, mb, rpu);
CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
p = strchr(str, '.');
if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
CX18_INFO("FW version: %s\n", p - 1);
}

static void epu_cmd(struct cx18 *cx, u32 sw1)
{
struct cx18_mailbox mb;

if (sw1 & IRQ_CPU_TO_EPU) {
cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb));
mb.error = 0;

switch (mb.cmd) {
case CX18_EPU_DMA_DONE:
epu_dma_done(cx, &mb, CPU);
break;
case CX18_EPU_DEBUG:
epu_debug(cx, &mb, CPU);
break;
default:
CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n",
mb.cmd);
break;
}
}

if (sw1 & IRQ_APU_TO_EPU) {
cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb));
CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd);
}
}

static void xpu_ack(struct cx18 *cx, u32 sw2)
{
Expand All @@ -145,6 +33,14 @@ static void xpu_ack(struct cx18 *cx, u32 sw2)
wake_up(&cx->mb_apu_waitq);
}

static void epu_cmd(struct cx18 *cx, u32 sw1)
{
if (sw1 & IRQ_CPU_TO_EPU)
cx18_api_epu_cmd_irq(cx, CPU);
if (sw1 & IRQ_APU_TO_EPU)
cx18_api_epu_cmd_irq(cx, APU);
}

irqreturn_t cx18_irq_handler(int irq, void *dev_id)
{
struct cx18 *cx = (struct cx18 *)dev_id;
Expand All @@ -170,6 +66,13 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
CX18_DEBUG_HI_IRQ("received interrupts "
"SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);

/*
* SW1 responses have to happen first. The sending XPU times out the
* incoming mailboxes on us rather rapidly.
*/
if (sw1)
epu_cmd(cx, sw1);

/* To do: interrupt-based I2C handling
if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
}
Expand All @@ -178,11 +81,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
if (sw2)
xpu_ack(cx, sw2);

if (sw1)
epu_cmd(cx, sw1);

if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags))
schedule_work(&cx->work);

return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
}
2 changes: 0 additions & 2 deletions drivers/media/video/cx18/cx18-irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,3 @@
#define SW2_INT_ENABLE_PCI 0xc7315c

irqreturn_t cx18_irq_handler(int irq, void *dev_id);

void cx18_work_handler(struct work_struct *work);
Loading

0 comments on commit ee2d64f

Please sign in to comment.