diff options
Diffstat (limited to 'linux/drivers/media')
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-driver.c | 16 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-driver.h | 16 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-dvb.c | 37 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-firmware.c | 35 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-io.c | 11 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-io.h | 1 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-irq.c | 32 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-irq.h | 1 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-mailbox.c | 209 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-mailbox.h | 7 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-streams.c | 6 | ||||
-rw-r--r-- | linux/drivers/media/video/cx18/cx18-version.h | 2 |
12 files changed, 204 insertions, 169 deletions
diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 542beb333..54f0fde42 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -187,7 +187,7 @@ MODULE_VERSION(CX18_VERSION); /* Generic utility functions */ int cx18_msleep_timeout(unsigned int msecs, int intr) { - int timeout = msecs_to_jiffies(msecs); + long int timeout = msecs_to_jiffies(msecs); int sig; do { @@ -446,15 +446,11 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->i2c_bus_lock[0]); mutex_init(&cx->i2c_bus_lock[1]); mutex_init(&cx->gpio_lock); + mutex_init(&cx->epu2apu_mb_lock); + mutex_init(&cx->epu2cpu_mb_lock); spin_lock_init(&cx->lock); - cx->work_queue = create_singlethread_workqueue(cx->name); - if (cx->work_queue == NULL) { - CX18_ERR("Could not create work queue\n"); - return -1; - } - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) INIT_WORK(&cx->work, cx18_work_handler); #else @@ -476,8 +472,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq); - init_waitqueue_head(&cx->mb_epu_waitq); - init_waitqueue_head(&cx->mb_hpu_waitq); init_waitqueue_head(&cx->dma_waitq); /* VBI */ @@ -841,7 +835,6 @@ free_map: free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); free_workqueue: - destroy_workqueue(cx->work_queue); err: if (retval == 0) retval = -ENODEV; @@ -942,8 +935,7 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); - flush_workqueue(cx->work_queue); - destroy_workqueue(cx->work_queue); + flush_scheduled_work(); cx18_streams_cleanup(cx, 1); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index fe9e5bb97..ce7680675 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -207,7 +207,6 @@ struct cx18_options { #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 */ -#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */ /* These are the VBI types as they appear in the embedded VBI private packets. */ #define CX18_SLICED_TYPE_TELETEXT_B (1) @@ -361,6 +360,12 @@ struct cx18_mmio_stats { atomic_t retried_read[CX18_MAX_MMIO_RD_RETRIES+1]; }; +#define CX18_MAX_MB_ACK_DELAY 100 + +struct cx18_mbox_stats { + atomic_t mb_ack_delay[CX18_MAX_MB_ACK_DELAY+1]; +}; + /* Struct to hold info about cx18 cards */ struct cx18 { int num; /* board number, -1 during init! */ @@ -379,7 +384,10 @@ struct cx18 { u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* Hardware description of the board */ unsigned mdl_offset; - struct cx18_scb __iomem *scb; /* pointer to SCB */ + struct cx18_scb __iomem *scb; /* pointer to SCB */ + 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; @@ -429,13 +437,10 @@ struct cx18 { wait_queue_head_t mb_apu_waitq; wait_queue_head_t mb_cpu_waitq; - wait_queue_head_t mb_epu_waitq; - wait_queue_head_t mb_hpu_waitq; wait_queue_head_t cap_w; /* when the current DMA is finished this queue is woken up */ wait_queue_head_t dma_waitq; - struct workqueue_struct *work_queue; struct work_struct work; /* i2c */ @@ -453,6 +458,7 @@ struct cx18 { /* Statistics */ struct cx18_mmio_stats mmio_stats; + struct cx18_mbox_stats mbox_stats; /* v4l2 and User settings */ diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index 4542e2e5e..4845f732e 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -109,20 +109,23 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - if (stream) { - mutex_lock(&stream->dvb.feedlock); - if (stream->dvb.feeding++ == 0) { - CX18_DEBUG_INFO("Starting Transport DMA\n"); - ret = cx18_start_v4l2_encode_stream(stream); - if (ret < 0) { - CX18_DEBUG_INFO( - "Failed to start Transport DMA\n"); - stream->dvb.feeding--; - } - } else - ret = 0; - mutex_unlock(&stream->dvb.feedlock); - } + if (!stream) + return -EINVAL; + + mutex_lock(&stream->dvb.feedlock); + if (stream->dvb.feeding++ == 0) { + CX18_DEBUG_INFO("Starting Transport DMA\n"); + set_bit(CX18_F_S_STREAMING, &stream->s_flags); + ret = cx18_start_v4l2_encode_stream(stream); + if (ret < 0) { + CX18_DEBUG_INFO("Failed to start Transport DMA\n"); + stream->dvb.feeding--; + if (stream->dvb.feeding == 0) + clear_bit(CX18_F_S_STREAMING, &stream->s_flags); + } + } else + ret = 0; + mutex_unlock(&stream->dvb.feedlock); return ret; } @@ -313,9 +316,11 @@ void cx18_dvb_work_handler(struct cx18 *cx) dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused); - cx18_enqueue(s, buf, &s->q_free); cx18_buf_sync_for_device(s, buf); - if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */ + 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, diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 7a7c89032..d9c5f55ab 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-firmware.c @@ -134,7 +134,8 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) return size; } -static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) +static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, + u32 *entry_addr) { const struct firmware *fw = NULL; int i, j; @@ -152,6 +153,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) return -ENOMEM; } + *entry_addr = 0; src = (const u32 *)fw->data; vers = fw->data + sizeof(seghdr); sz = fw->size; @@ -168,10 +170,12 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) } CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, seghdr.addr + seghdr.size - 1); + if (*entry_addr == 0) + *entry_addr = seghdr.addr; if (offset + seghdr.size > sz) break; for (i = 0; i < seghdr.size; i += 4096) { - cx18_setup_page(cx, offset + i); + cx18_setup_page(cx, seghdr.addr + i); for (j = i; j < seghdr.size && j < i + 4096; j += 4) { /* no need for endianness conversion on the ppc */ cx18_raw_writel(cx, src[(offset + j) / 4], @@ -192,8 +196,6 @@ 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); - /* Clear bit0 for APU to start from 0 */ - cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); return size; } @@ -338,11 +340,19 @@ int cx18_firmware_init(struct cx18 *cx) /* Only if the processor is not running */ if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { + u32 fw_entry_addr = 0; int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", - cx->enc_mem, cx); + cx->enc_mem, cx, &fw_entry_addr); + + if (sz <= 0) + return sz; + + /* Clear bit0 for APU to start from 0 */ + cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); + + cx18_write_enc(cx, 0xE51FF004, 0); /* ldr pc, [pc, #-4] */ + cx18_write_enc(cx, fw_entry_addr, 4); - cx18_write_enc(cx, 0xE51FF004, 0); - cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ /* Start APU */ cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET, 0x00000000, 0x00010001); @@ -373,6 +383,17 @@ int cx18_firmware_init(struct cx18 *cx) if (sz <= 0) return -EIO; } + + /* + * The CPU firmware apparently sets up to receive an interrupt for it's + * outgoing IRQ_CPU_TO_EPU_ACK to us (*boggle*). We get an interrupt + * when it sends us an ack, but by the time we process it, that flag in + * the SW2 status register has been cleared by the CPU firmware. + * We'll prevent that not so useful behavior by clearing the CPU's + * interrupt enables for Ack IRQ's we want to process. + */ + cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + /* initialize GPIO */ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); return 0; diff --git a/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 0ad8dea3e..3c6485fce 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/drivers/media/video/cx18/cx18-io.c @@ -37,6 +37,10 @@ void cx18_log_statistics(struct cx18 *cx) for (i = 0; i <= CX18_MAX_MMIO_RD_RETRIES; i++) CX18_DEBUG_INFO("retried_read[%d] = %d\n", i, atomic_read(&cx->mmio_stats.retried_read[i])); + for (i = 0; i <= CX18_MAX_MB_ACK_DELAY; i++) + if (atomic_read(&cx->mbox_stats.mb_ack_delay[i])) + CX18_DEBUG_INFO("mb_ack_delay[%d] = %d\n", i, + atomic_read(&cx->mbox_stats.mb_ack_delay[i])); return; } @@ -258,6 +262,13 @@ void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI); } +void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val) +{ + u32 r; + r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU); + cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU); +} + void cx18_setup_page(struct cx18 *cx, u32 addr) { u32 val; diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index cb695a596..4486b73fa 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/drivers/media/video/cx18/cx18-io.h @@ -390,6 +390,7 @@ void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); +void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val); void cx18_setup_page(struct cx18 *cx, u32 addr); #endif /* CX18_IO_H */ diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index aff5a3abd..3e23fc350 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -40,17 +40,11 @@ void cx18_work_handler(void *arg) { struct cx18 *cx = arg; #endif - if (test_and_clear_bit(CX18_F_I_WORK_INITED, &cx->i_flags)) { - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - /* This thread must use the FIFO scheduler as it - * is realtime sensitive. */ - sched_setscheduler(current, SCHED_FIFO, ¶m); - } 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) +static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) { u32 handle = mb->args[0]; struct cx18_stream *s = NULL; @@ -71,7 +65,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) " handle %d\n", handle); mb->error = CXERR_NOT_OPEN; mb->cmd = 0; - cx18_mb_ack(cx, mb); + cx18_mb_ack(cx, mb, rpu); return; } @@ -98,13 +92,13 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) } mb->error = 0; mb->cmd = 0; - cx18_mb_ack(cx, mb); + 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) +static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) { char str[256] = { 0 }; char *p; @@ -114,7 +108,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); str[252] = 0; } - cx18_mb_ack(cx, mb); + 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) @@ -131,10 +125,10 @@ static void epu_cmd(struct cx18 *cx, u32 sw1) switch (mb.cmd) { case CX18_EPU_DMA_DONE: - epu_dma_done(cx, &mb); + epu_dma_done(cx, &mb, CPU); break; case CX18_EPU_DEBUG: - epu_debug(cx, &mb); + epu_debug(cx, &mb, CPU); break; default: CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n", @@ -147,11 +141,6 @@ static void epu_cmd(struct cx18 *cx, u32 sw1) cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb)); CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd); } - - if (sw1 & IRQ_HPU_TO_EPU) { - cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb)); - CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd); - } } static void xpu_ack(struct cx18 *cx, u32 sw2) @@ -160,8 +149,6 @@ static void xpu_ack(struct cx18 *cx, u32 sw2) wake_up(&cx->mb_cpu_waitq); if (sw2 & IRQ_APU_TO_EPU_ACK) wake_up(&cx->mb_apu_waitq); - if (sw2 & IRQ_HPU_TO_EPU_ACK) - wake_up(&cx->mb_hpu_waitq); } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) @@ -190,7 +177,8 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); if (sw1 || sw2 || hw2) - CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); + CX18_DEBUG_HI_IRQ("received interrupts " + "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); /* To do: interrupt-based I2C handling if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { @@ -204,7 +192,7 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) epu_cmd(cx, sw1); if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags)) - queue_work(cx->work_queue, &cx->work); + schedule_work(&cx->work); return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h index d1d9d1967..6472e42c6 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.h +++ b/linux/drivers/media/video/cx18/cx18-irq.h @@ -28,6 +28,7 @@ #define SW1_INT_ENABLE_PCI 0xc7311c #define SW2_INT_SET 0xc73140 #define SW2_INT_STATUS 0xc73144 +#define SW2_INT_ENABLE_CPU 0xc73158 #define SW2_INT_ENABLE_PCI 0xc7315c #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index acff7dfb6..35f7188d4 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -30,11 +30,6 @@ #define API_FAST (1 << 2) /* Short timeout */ #define API_SLOW (1 << 3) /* Additional 300ms timeout */ -#define APU 0 -#define CPU 1 -#define EPU 2 -#define HPU 3 - struct cx18_api_info { u32 cmd; u8 flags; /* Flags, see above */ @@ -97,70 +92,12 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, - u32 *state, u32 *irq, u32 *req) +long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) { - struct cx18_mailbox __iomem *mb = NULL; - int wait_count = 0; - u32 ack; - - switch (rpu) { - case APU: - mb = &cx->scb->epu2apu_mb; - *state = cx18_readl(cx, &cx->scb->apu_state); - *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); - break; - - case CPU: - mb = &cx->scb->epu2cpu_mb; - *state = cx18_readl(cx, &cx->scb->cpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); - break; - - case HPU: - mb = &cx->scb->epu2hpu_mb; - *state = cx18_readl(cx, &cx->scb->hpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); - break; - } - - if (mb == NULL) - return mb; - - do { - *req = cx18_readl(cx, &mb->request); - ack = cx18_readl(cx, &mb->ack); - wait_count++; - } while (*req != ack && wait_count < 600); - - if (*req == ack) { - (*req)++; - if (*req == 0 || *req == 0xffffffff) - *req = 1; - return mb; - } - return NULL; -} - -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) -{ - const struct cx18_api_info *info = find_api_info(mb->cmd); struct cx18_mailbox __iomem *ack_mb; u32 ack_irq; - u8 rpu = CPU; - - if (info == NULL && mb->cmd) { - CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); - return -EINVAL; - } - if (info) - rpu = info->rpu; switch (rpu) { - case HPU: - ack_irq = IRQ_EPU_TO_HPU_ACK; - ack_mb = &cx->scb->hpu2epu_mb; - break; case APU: ack_irq = IRQ_EPU_TO_APU_ACK; ack_mb = &cx->scb->apu2epu_mb; @@ -170,7 +107,8 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) ack_mb = &cx->scb->cpu2epu_mb; break; default: - CX18_WARN("Unknown RPU for command %x\n", mb->cmd); + CX18_WARN("Unhandled RPU (%d) for command %x ack\n", + rpu, mb->cmd); return -EINVAL; } @@ -180,16 +118,22 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) return 0; } +static void cx18_api_log_ack_delay(struct cx18 *cx, int msecs) +{ + if (msecs > CX18_MAX_MB_ACK_DELAY) + msecs = CX18_MAX_MB_ACK_DELAY; + atomic_inc(&cx->mbox_stats.mb_ack_delay[msecs]); +} static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); - u32 state = 0, irq = 0, req, oldreq, err; + u32 state, irq, req, ack, err; struct cx18_mailbox __iomem *mb; + u32 __iomem *xpu_state; wait_queue_head_t *waitq; - int timeout = 100; - int cnt = 0; - int sig = 0; + struct mutex *mb_lock; + long int timeout, ret; int i; if (info == NULL) { @@ -201,50 +145,116 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) CX18_DEBUG_HI_API("%s\n", info->name); else CX18_DEBUG_API("%s\n", info->name); - cx18_setup_page(cx, SCB_OFFSET); - mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); - if (mb == NULL) { - CX18_ERR("mb %s busy\n", info->name); - return -EBUSY; + switch (info->rpu) { + case APU: + waitq = &cx->mb_apu_waitq; + mb_lock = &cx->epu2apu_mb_lock; + irq = IRQ_EPU_TO_APU; + mb = &cx->scb->epu2apu_mb; + xpu_state = &cx->scb->apu_state; + break; + case CPU: + waitq = &cx->mb_cpu_waitq; + mb_lock = &cx->epu2cpu_mb_lock; + irq = IRQ_EPU_TO_CPU; + mb = &cx->scb->epu2cpu_mb; + xpu_state = &cx->scb->cpu_state; + break; + default: + CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); + return -EINVAL; } - oldreq = req - 1; + mutex_lock(mb_lock); + cx18_setup_page(cx, SCB_OFFSET); + + /* + * Wait for an in-use mailbox to complete + * + * If the XPU is responding with Ack's, the mailbox shouldn't be in + * a busy state, since we serialize access to it on our end. + * + * If the wait for ack after sending a previous command was interrupted + * by a signal, we may get here and find a busy mailbox. After waiting, + * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. + */ + state = cx18_readl(cx, xpu_state); + req = cx18_readl(cx, &mb->request); + timeout = msecs_to_jiffies(20); /* 1 field at 50 Hz vertical refresh */ + ret = wait_event_timeout(*waitq, + (ack = cx18_readl(cx, &mb->ack)) == req, + timeout); + if (req != ack) { + /* waited long enough, make the mbox "not busy" from our end */ + cx18_writel(cx, req, &mb->ack); + CX18_ERR("mbox was found stuck busy when setting up for %s; " + "clearing busy and trying to proceed\n", info->name); + } else if (ret != timeout) + CX18_DEBUG_API("waited %u usecs for busy mbox to be acked\n", + jiffies_to_usecs(timeout-ret)); + + /* Build the outgoing mailbox */ + req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; + cx18_writel(cx, cmd, &mb->cmd); for (i = 0; i < args; i++) cx18_writel(cx, data[i], &mb->args[i]); cx18_writel(cx, 0, &mb->error); cx18_writel(cx, req, &mb->request); + cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ - switch (info->rpu) { - case APU: waitq = &cx->mb_apu_waitq; break; - case CPU: waitq = &cx->mb_cpu_waitq; break; - case EPU: waitq = &cx->mb_epu_waitq; break; - case HPU: waitq = &cx->mb_hpu_waitq; break; - default: return -EINVAL; - } - if (info->flags & API_FAST) - timeout /= 2; + /* + * Notify the XPU and wait for it to send an Ack back + * 21 ms = ~ 0.5 frames at a frame rate of 24 fps + * 42 ms = ~ 1 frame at a frame rate of 24 fps + */ + timeout = msecs_to_jiffies((info->flags & API_FAST) ? 21 : 42); + + CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", + irq, info->name); cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); - while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) - && cnt < 660) { - if (cnt > 200 && !in_atomic()) - sig = cx18_msleep_timeout(10, 1); - cnt++; - } - if (sig) - return -EINTR; - if (cnt == 660) { - cx18_writel(cx, oldreq, &mb->request); - CX18_ERR("mb %s failed\n", info->name); + ret = wait_event_timeout( + *waitq, + cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), + timeout); + if (ret == 0) { + /* Timed out */ + mutex_unlock(mb_lock); + i = jiffies_to_msecs(timeout); + cx18_api_log_ack_delay(cx, i); + CX18_WARN("sending %s timed out waiting %d msecs for RPU " + "acknowledgement\n", info->name, i); return -EINVAL; + } else if (ret < 0) { + /* Interrupted */ + mutex_unlock(mb_lock); + CX18_WARN("sending %s was interrupted waiting for RPU" + "acknowledgement\n", info->name); + return -EINTR; } + + i = jiffies_to_msecs(timeout-ret); + cx18_api_log_ack_delay(cx, i); + if (ret != timeout) + CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", + i, info->name); + + /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) data[i] = cx18_readl(cx, &mb->args[i]); err = cx18_readl(cx, &mb->error); - if (!in_atomic() && (info->flags & API_SLOW)) + mutex_unlock(mb_lock); + + /* + * Wait for XPU to perform extra actions for the caller in some cases. + * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers + * back in a burst shortly thereafter + */ + if (info->flags & API_SLOW) cx18_msleep_timeout(300, 0); + if (err) CX18_DEBUG_API("mailbox error %08x for command %s\n", err, info->name); @@ -253,12 +263,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) { - int res = cx18_api_call(cx, cmd, args, data); - - /* Allow a single retry, probably already too late though. - If there is no free mailbox then that is usually an indication - of a more serious problem. */ - return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; + return cx18_api_call(cx, cmd, args, data); } static int cx18_set_filter_param(struct cx18_stream *s) diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index d99564153..54758f32d 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/drivers/media/video/cx18/cx18-mailbox.h @@ -30,6 +30,11 @@ #define MB_RESERVED_HANDLE_0 0 #define MB_RESERVED_HANDLE_1 0xFFFFFFFF +#define APU 0 +#define CPU 1 +#define EPU 2 +#define HPU 3 + struct cx18; /* The cx18_mailbox struct is the mailbox structure which is used for passing @@ -68,6 +73,6 @@ int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); int cx18_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb); +long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu); #endif diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 8ead4025e..d29a0b61b 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -587,9 +587,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) #endif } - /* Tell the CX23418 it can't use our buffers anymore */ - cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); - if (s->type != CX18_ENC_STREAM_TYPE_TS) atomic_dec(&cx->ana_capturing); atomic_dec(&cx->tot_capturing); @@ -597,6 +594,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) /* Clear capture and no-read bits */ clear_bit(CX18_F_S_STREAMING, &s->s_flags); + /* Tell the CX23418 it can't use our buffers anymore */ + cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); + cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h index 9f6be2d45..366cc1472 100644 --- a/linux/drivers/media/video/cx18/cx18-version.h +++ b/linux/drivers/media/video/cx18/cx18-version.h @@ -25,7 +25,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 #define CX18_DRIVER_VERSION_MINOR 0 -#define CX18_DRIVER_VERSION_PATCHLEVEL 1 +#define CX18_DRIVER_VERSION_PATCHLEVEL 2 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) #define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \ |