aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2008-11-15 23:38:19 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-12-30 06:38:07 -0500
commitee2d64f5ccc71b5c5191e92ea91a12b65f9ca060 (patch)
tree0f01253a1e517f9c1f705901d873ca25bb2c872f /drivers
parentba38ee8ebe4a42ce2213802152b0b86a95dc109f (diff)
V4L/DVB (9720): cx18: Major rewrite of interrupt handling for incoming 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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/cx18/cx18-driver.c8
-rw-r--r--drivers/media/video/cx18/cx18-driver.h19
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c25
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h1
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c5
-rw-r--r--drivers/media/video/cx18/cx18-io.c35
-rw-r--r--drivers/media/video/cx18/cx18-io.h7
-rw-r--r--drivers/media/video/cx18/cx18-irq.c133
-rw-r--r--drivers/media/video/cx18/cx18-irq.h2
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c318
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h16
-rw-r--r--drivers/media/video/cx18/cx18-queue.c25
-rw-r--r--drivers/media/video/cx18/cx18-queue.h2
-rw-r--r--drivers/media/video/cx18/cx18-scb.h8
-rw-r--r--drivers/media/video/cx18/cx18-streams.c18
-rw-r--r--drivers/media/video/cx18/cx18-streams.h1
16 files changed, 408 insertions, 215 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 8ef11d578b8d..434cc7fdee36 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -440,6 +440,8 @@ done:
440 */ 440 */
441static int __devinit cx18_init_struct1(struct cx18 *cx) 441static int __devinit cx18_init_struct1(struct cx18 *cx)
442{ 442{
443 int i;
444
443 cx->base_addr = pci_resource_start(cx->dev, 0); 445 cx->base_addr = pci_resource_start(cx->dev, 0);
444 446
445 mutex_init(&cx->serialize_lock); 447 mutex_init(&cx->serialize_lock);
@@ -451,7 +453,11 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
451 453
452 spin_lock_init(&cx->lock); 454 spin_lock_init(&cx->lock);
453 455
454 INIT_WORK(&cx->work, cx18_work_handler); 456 for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
457 cx->epu_work_order[i].cx = cx;
458 cx->epu_work_order[i].str = cx->epu_debug_str;
459 INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler);
460 }
455 461
456 /* start counting open_id at 1 */ 462 /* start counting open_id at 1 */
457 cx->open_id = 1; 463 cx->open_id = 1;
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index ce76806759ec..5ebf84b78ca8 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -203,8 +203,6 @@ struct cx18_options {
203#define CX18_F_I_EOS 4 /* End of encoder stream */ 203#define CX18_F_I_EOS 4 /* End of encoder stream */
204#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */ 204#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */
205#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ 205#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
206#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */
207#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */
208#define CX18_F_I_INITED 21 /* set after first open */ 206#define CX18_F_I_INITED 21 /* set after first open */
209#define CX18_F_I_FAILED 22 /* set if first open failed */ 207#define CX18_F_I_FAILED 22 /* set if first open failed */
210 208
@@ -247,6 +245,19 @@ struct cx18_dvb {
247struct cx18; /* forward reference */ 245struct cx18; /* forward reference */
248struct cx18_scb; /* forward reference */ 246struct cx18_scb; /* forward reference */
249 247
248#define CX18_MAX_MDL_ACKS 2
249#define CX18_MAX_EPU_WORK_ORDERS 70 /* CPU_DE_RELEASE_MDL bursts 63 commands */
250
251struct cx18_epu_work_order {
252 struct work_struct work;
253 atomic_t pending;
254 struct cx18 *cx;
255 int rpu;
256 struct cx18_mailbox mb;
257 struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
258 char *str;
259};
260
250#define CX18_INVALID_TASK_HANDLE 0xffffffff 261#define CX18_INVALID_TASK_HANDLE 0xffffffff
251 262
252struct cx18_stream { 263struct cx18_stream {
@@ -388,7 +399,6 @@ struct cx18 {
388 struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ 399 struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
389 struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ 400 struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
390 401
391
392 struct cx18_av_state av_state; 402 struct cx18_av_state av_state;
393 403
394 /* codec settings */ 404 /* codec settings */
@@ -441,7 +451,8 @@ struct cx18 {
441 /* when the current DMA is finished this queue is woken up */ 451 /* when the current DMA is finished this queue is woken up */
442 wait_queue_head_t dma_waitq; 452 wait_queue_head_t dma_waitq;
443 453
444 struct work_struct work; 454 struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS];
455 char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */
445 456
446 /* i2c */ 457 /* i2c */
447 struct i2c_adapter i2c_adap[2]; 458 struct i2c_adapter i2c_adap[2];
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 4845f732ef48..b74253b1377e 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -23,8 +23,6 @@
23#include "cx18-dvb.h" 23#include "cx18-dvb.h"
24#include "cx18-io.h" 24#include "cx18-io.h"
25#include "cx18-streams.h" 25#include "cx18-streams.h"
26#include "cx18-queue.h"
27#include "cx18-scb.h"
28#include "cx18-cards.h" 26#include "cx18-cards.h"
29#include "s5h1409.h" 27#include "s5h1409.h"
30#include "mxl5005s.h" 28#include "mxl5005s.h"
@@ -305,26 +303,3 @@ static int dvb_register(struct cx18_stream *stream)
305 303
306 return ret; 304 return ret;
307} 305}
308
309void cx18_dvb_work_handler(struct cx18 *cx)
310{
311 struct cx18_buffer *buf;
312 struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS];
313
314 while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) {
315 if (s->dvb.enabled)
316 dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
317 buf->bytesused);
318
319 cx18_buf_sync_for_device(s, buf);
320 cx18_enqueue(s, buf, &s->q_free);
321
322 if (s->handle == CX18_INVALID_TASK_HANDLE ||
323 !test_bit(CX18_F_S_STREAMING, &s->s_flags))
324 continue;
325
326 cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
327 (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
328 1, buf->id, s->buf_size);
329 }
330}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
index bbdcefc87f28..bf8d8f6f5455 100644
--- a/drivers/media/video/cx18/cx18-dvb.h
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -23,4 +23,3 @@
23 23
24int cx18_dvb_register(struct cx18_stream *stream); 24int cx18_dvb_register(struct cx18_stream *stream);
25void cx18_dvb_unregister(struct cx18_stream *stream); 25void cx18_dvb_unregister(struct cx18_stream *stream);
26void cx18_dvb_work_handler(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index d9c5f55ab17c..fdea4b75204d 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -121,6 +121,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
121 if (cx18_raw_readl(cx, dst) != *src) { 121 if (cx18_raw_readl(cx, dst) != *src) {
122 CX18_ERR("Mismatch at offset %x\n", i); 122 CX18_ERR("Mismatch at offset %x\n", i);
123 release_firmware(fw); 123 release_firmware(fw);
124 cx18_setup_page(cx, 0);
124 return -EIO; 125 return -EIO;
125 } 126 }
126 dst++; 127 dst++;
@@ -131,6 +132,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
131 CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); 132 CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
132 size = fw->size; 133 size = fw->size;
133 release_firmware(fw); 134 release_firmware(fw);
135 cx18_setup_page(cx, SCB_OFFSET);
134 return size; 136 return size;
135} 137}
136 138
@@ -150,6 +152,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
150 if (request_firmware(&fw, fn, &cx->dev->dev)) { 152 if (request_firmware(&fw, fn, &cx->dev->dev)) {
151 CX18_ERR("unable to open firmware %s\n", fn); 153 CX18_ERR("unable to open firmware %s\n", fn);
152 CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); 154 CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
155 cx18_setup_page(cx, 0);
153 return -ENOMEM; 156 return -ENOMEM;
154 } 157 }
155 158
@@ -185,6 +188,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
185 CX18_ERR("Mismatch at offset %x\n", 188 CX18_ERR("Mismatch at offset %x\n",
186 offset + j); 189 offset + j);
187 release_firmware(fw); 190 release_firmware(fw);
191 cx18_setup_page(cx, 0);
188 return -EIO; 192 return -EIO;
189 } 193 }
190 } 194 }
@@ -196,6 +200,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
196 fn, apu_version, fw->size); 200 fn, apu_version, fw->size);
197 size = fw->size; 201 size = fw->size;
198 release_firmware(fw); 202 release_firmware(fw);
203 cx18_setup_page(cx, 0);
199 return size; 204 return size;
200} 205}
201 206
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
index 3c6485fceea0..c67694f63d0e 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -166,41 +166,6 @@ u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr)
166 return val; 166 return val;
167} 167}
168 168
169void cx18_memcpy_fromio(struct cx18 *cx, void *to,
170 const void __iomem *from, unsigned int len)
171{
172 const u8 __iomem *src = from;
173 u8 *dst = to;
174
175 /* Align reads on the CX23418's addresses */
176 if ((len > 0) && ((unsigned long) src & 1)) {
177 *dst = cx18_readb(cx, src);
178 len--;
179 dst++;
180 src++;
181 }
182 if ((len > 1) && ((unsigned long) src & 2)) {
183 *((u16 *)dst) = cx18_raw_readw(cx, src);
184 len -= 2;
185 dst += 2;
186 src += 2;
187 }
188 while (len > 3) {
189 *((u32 *)dst) = cx18_raw_readl(cx, src);
190 len -= 4;
191 dst += 4;
192 src += 4;
193 }
194 if (len > 1) {
195 *((u16 *)dst) = cx18_raw_readw(cx, src);
196 len -= 2;
197 dst += 2;
198 src += 2;
199 }
200 if (len > 0)
201 *dst = cx18_readb(cx, src);
202}
203
204void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) 169void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
205{ 170{
206 u8 __iomem *dst = addr; 171 u8 __iomem *dst = addr;
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
index 4486b73faf5b..fdc2bcc92fca 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -249,8 +249,13 @@ static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
249} 249}
250 250
251 251
252static inline
252void cx18_memcpy_fromio(struct cx18 *cx, void *to, 253void cx18_memcpy_fromio(struct cx18 *cx, void *to,
253 const void __iomem *from, unsigned int len); 254 const void __iomem *from, unsigned int len)
255{
256 memcpy_fromio(to, from, len);
257}
258
254void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); 259void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
255 260
256 261
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index 4912b3c8e6a5..bc36a6b8f775 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -21,121 +21,9 @@
21 21
22#include "cx18-driver.h" 22#include "cx18-driver.h"
23#include "cx18-io.h" 23#include "cx18-io.h"
24#include "cx18-firmware.h"
25#include "cx18-fileops.h"
26#include "cx18-queue.h"
27#include "cx18-irq.h" 24#include "cx18-irq.h"
28#include "cx18-ioctl.h"
29#include "cx18-mailbox.h" 25#include "cx18-mailbox.h"
30#include "cx18-vbi.h"
31#include "cx18-scb.h" 26#include "cx18-scb.h"
32#include "cx18-dvb.h"
33
34void cx18_work_handler(struct work_struct *work)
35{
36 struct cx18 *cx = container_of(work, struct cx18, work);
37 if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags))
38 cx18_dvb_work_handler(cx);
39}
40
41static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb, int rpu)
42{
43 u32 handle = mb->args[0];
44 struct cx18_stream *s = NULL;
45 struct cx18_buffer *buf;
46 u32 off;
47 int i;
48 int id;
49
50 for (i = 0; i < CX18_MAX_STREAMS; i++) {
51 s = &cx->streams[i];
52 if ((handle == s->handle) && (s->dvb.enabled))
53 break;
54 if (s->v4l2dev && handle == s->handle)
55 break;
56 }
57 if (i == CX18_MAX_STREAMS) {
58 CX18_WARN("Got DMA done notification for unknown/inactive"
59 " handle %d\n", handle);
60 mb->error = CXERR_NOT_OPEN;
61 mb->cmd = 0;
62 cx18_mb_ack(cx, mb, rpu);
63 return;
64 }
65
66 off = mb->args[1];
67 if (mb->args[2] != 1)
68 CX18_WARN("Ack struct = %d for %s\n",
69 mb->args[2], s->name);
70 id = cx18_read_enc(cx, off);
71 buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4));
72 CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
73 if (buf) {
74 cx18_buf_sync_for_cpu(s, buf);
75 if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
76 CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
77 buf->bytesused);
78
79 set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags);
80 set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags);
81 } else
82 set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
83 } else {
84 CX18_WARN("Could not find buf %d for stream %s\n",
85 cx18_read_enc(cx, off), s->name);
86 }
87 mb->error = 0;
88 mb->cmd = 0;
89 cx18_mb_ack(cx, mb, rpu);
90 wake_up(&cx->dma_waitq);
91 if (s->id != -1)
92 wake_up(&s->waitq);
93}
94
95static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu)
96{
97 char str[256] = { 0 };
98 char *p;
99
100 if (mb->args[1]) {
101 cx18_setup_page(cx, mb->args[1]);
102 cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
103 str[252] = 0;
104 }
105 cx18_mb_ack(cx, mb, rpu);
106 CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
107 p = strchr(str, '.');
108 if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
109 CX18_INFO("FW version: %s\n", p - 1);
110}
111
112static void epu_cmd(struct cx18 *cx, u32 sw1)
113{
114 struct cx18_mailbox mb;
115
116 if (sw1 & IRQ_CPU_TO_EPU) {
117 cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb));
118 mb.error = 0;
119
120 switch (mb.cmd) {
121 case CX18_EPU_DMA_DONE:
122 epu_dma_done(cx, &mb, CPU);
123 break;
124 case CX18_EPU_DEBUG:
125 epu_debug(cx, &mb, CPU);
126 break;
127 default:
128 CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n",
129 mb.cmd);
130 break;
131 }
132 }
133
134 if (sw1 & IRQ_APU_TO_EPU) {
135 cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb));
136 CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd);
137 }
138}
139 27
140static void xpu_ack(struct cx18 *cx, u32 sw2) 28static void xpu_ack(struct cx18 *cx, u32 sw2)
141{ 29{
@@ -145,6 +33,14 @@ static void xpu_ack(struct cx18 *cx, u32 sw2)
145 wake_up(&cx->mb_apu_waitq); 33 wake_up(&cx->mb_apu_waitq);
146} 34}
147 35
36static void epu_cmd(struct cx18 *cx, u32 sw1)
37{
38 if (sw1 & IRQ_CPU_TO_EPU)
39 cx18_api_epu_cmd_irq(cx, CPU);
40 if (sw1 & IRQ_APU_TO_EPU)
41 cx18_api_epu_cmd_irq(cx, APU);
42}
43
148irqreturn_t cx18_irq_handler(int irq, void *dev_id) 44irqreturn_t cx18_irq_handler(int irq, void *dev_id)
149{ 45{
150 struct cx18 *cx = (struct cx18 *)dev_id; 46 struct cx18 *cx = (struct cx18 *)dev_id;
@@ -170,6 +66,13 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
170 CX18_DEBUG_HI_IRQ("received interrupts " 66 CX18_DEBUG_HI_IRQ("received interrupts "
171 "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); 67 "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
172 68
69 /*
70 * SW1 responses have to happen first. The sending XPU times out the
71 * incoming mailboxes on us rather rapidly.
72 */
73 if (sw1)
74 epu_cmd(cx, sw1);
75
173 /* To do: interrupt-based I2C handling 76 /* To do: interrupt-based I2C handling
174 if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { 77 if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
175 } 78 }
@@ -178,11 +81,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
178 if (sw2) 81 if (sw2)
179 xpu_ack(cx, sw2); 82 xpu_ack(cx, sw2);
180 83
181 if (sw1)
182 epu_cmd(cx, sw1);
183
184 if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags))
185 schedule_work(&cx->work);
186
187 return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; 84 return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
188} 85}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
index 6f3ec8963762..cd8f548b7ee8 100644
--- a/drivers/media/video/cx18/cx18-irq.h
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -32,5 +32,3 @@
32#define SW2_INT_ENABLE_PCI 0xc7315c 32#define SW2_INT_ENABLE_PCI 0xc7315c
33 33
34irqreturn_t cx18_irq_handler(int irq, void *dev_id); 34irqreturn_t cx18_irq_handler(int irq, void *dev_id);
35
36void cx18_work_handler(struct work_struct *work);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index 35f7188d4d3b..3d210d2ffdad 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -26,6 +26,10 @@
26#include "cx18-scb.h" 26#include "cx18-scb.h"
27#include "cx18-irq.h" 27#include "cx18-irq.h"
28#include "cx18-mailbox.h" 28#include "cx18-mailbox.h"
29#include "cx18-queue.h"
30#include "cx18-streams.h"
31
32static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
29 33
30#define API_FAST (1 << 2) /* Short timeout */ 34#define API_FAST (1 << 2) /* Short timeout */
31#define API_SLOW (1 << 3) /* Additional 300ms timeout */ 35#define API_SLOW (1 << 3) /* Additional 300ms timeout */
@@ -92,12 +96,149 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
92 return NULL; 96 return NULL;
93} 97}
94 98
95long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) 99static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
100{
101 char argstr[MAX_MB_ARGUMENTS*11+1];
102 char *p;
103 int i;
104
105 if (!(cx18_debug & CX18_DBGFLG_API))
106 return;
107
108 for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) {
109 /* kernel snprintf() appends '\0' always */
110 snprintf(p, 12, " %#010x", mb->args[i]);
111 }
112 CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
113 "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr);
114}
115
116
117/*
118 * Functions that run in a work_queue work handling context
119 */
120
121static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
122{
123 u32 handle, mdl_ack_count;
124 struct cx18_mailbox *mb;
125 struct cx18_mdl_ack *mdl_ack;
126 struct cx18_stream *s;
127 struct cx18_buffer *buf;
128 int i;
129
130 mb = &order->mb;
131 handle = mb->args[0];
132 s = cx18_handle_to_stream(cx, handle);
133
134 if (s == NULL) {
135 CX18_WARN("Got DMA done notification for unknown/inactive"
136 " handle %d\n", handle);
137 return;
138 }
139
140 mdl_ack_count = mb->args[2];
141 mdl_ack = order->mdl_ack;
142 for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {
143 buf = cx18_queue_get_buf(s, mdl_ack->id, mdl_ack->data_used);
144 CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name,
145 mdl_ack->id);
146 if (buf == NULL) {
147 CX18_WARN("Could not find buf %d for stream %s\n",
148 mdl_ack->id, s->name);
149 continue;
150 }
151
152 cx18_buf_sync_for_cpu(s, buf);
153 if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
154 CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
155 buf->bytesused);
156
157 dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
158 buf->bytesused);
159
160 cx18_buf_sync_for_device(s, buf);
161
162 if (s->handle != CX18_INVALID_TASK_HANDLE &&
163 test_bit(CX18_F_S_STREAMING, &s->s_flags))
164 cx18_vapi(cx,
165 CX18_CPU_DE_SET_MDL, 5, s->handle,
166 (void __iomem *)
167 &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
168 1, buf->id, s->buf_size);
169 } else
170 set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
171 }
172 wake_up(&cx->dma_waitq);
173 if (s->id != -1)
174 wake_up(&s->waitq);
175}
176
177static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order)
178{
179 char *p;
180 char *str = order->str;
181
182 CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);
183 p = strchr(str, '.');
184 if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
185 CX18_INFO("FW version: %s\n", p - 1);
186}
187
188static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order)
189{
190 switch (order->rpu) {
191 case CPU:
192 {
193 switch (order->mb.cmd) {
194 case CX18_EPU_DMA_DONE:
195 epu_dma_done(cx, order);
196 break;
197 case CX18_EPU_DEBUG:
198 epu_debug(cx, order);
199 break;
200 default:
201 CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
202 order->mb.cmd);
203 break;
204 }
205 break;
206 }
207 case APU:
208 CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
209 order->mb.cmd);
210 break;
211 default:
212 break;
213 }
214}
215
216static
217void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order)
218{
219 atomic_set(&order->pending, 0);
220}
221
222void cx18_epu_work_handler(struct work_struct *work)
223{
224 struct cx18_epu_work_order *order =
225 container_of(work, struct cx18_epu_work_order, work);
226 struct cx18 *cx = order->cx;
227 epu_cmd(cx, order);
228 free_epu_work_order(cx, order);
229}
230
231
232/*
233 * Functions that run in an interrupt handling context
234 */
235
236static void mb_ack_irq(struct cx18 *cx, const struct cx18_epu_work_order *order)
96{ 237{
97 struct cx18_mailbox __iomem *ack_mb; 238 struct cx18_mailbox __iomem *ack_mb;
98 u32 ack_irq; 239 u32 ack_irq, req;
99 240
100 switch (rpu) { 241 switch (order->rpu) {
101 case APU: 242 case APU:
102 ack_irq = IRQ_EPU_TO_APU_ACK; 243 ack_irq = IRQ_EPU_TO_APU_ACK;
103 ack_mb = &cx->scb->apu2epu_mb; 244 ack_mb = &cx->scb->apu2epu_mb;
@@ -108,16 +249,175 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu)
108 break; 249 break;
109 default: 250 default:
110 CX18_WARN("Unhandled RPU (%d) for command %x ack\n", 251 CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
111 rpu, mb->cmd); 252 order->rpu, order->mb.cmd);
112 return -EINVAL; 253 return;
113 } 254 }
114 255
115 cx18_setup_page(cx, SCB_OFFSET); 256 req = order->mb.request;
116 cx18_write_sync(cx, mb->request, &ack_mb->ack); 257 /* Don't ack if the RPU has gotten impatient and timed us out */
258 if (req != cx18_readl(cx, &ack_mb->request) ||
259 req == cx18_readl(cx, &ack_mb->ack))
260 return;
261 cx18_writel(cx, req, &ack_mb->ack);
117 cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); 262 cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
118 return 0; 263 return;
264}
265
266static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order,
267 int stale)
268{
269 u32 handle, mdl_ack_offset, mdl_ack_count;
270 struct cx18_mailbox *mb;
271
272 mb = &order->mb;
273 handle = mb->args[0];
274 mdl_ack_offset = mb->args[1];
275 mdl_ack_count = mb->args[2];
276
277 if (handle == CX18_INVALID_TASK_HANDLE ||
278 mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {
279 if (!stale)
280 mb_ack_irq(cx, order);
281 return -1;
282 }
283
284 cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset,
285 sizeof(struct cx18_mdl_ack) * mdl_ack_count);
286 if (!stale)
287 mb_ack_irq(cx, order);
288 return 1;
289}
290
291static
292int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order, int stale)
293{
294 u32 str_offset;
295 char *str = order->str;
296
297 str[0] = '\0';
298 str_offset = order->mb.args[1];
299 if (str_offset) {
300 cx18_setup_page(cx, str_offset);
301 cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);
302 str[252] = '\0';
303 cx18_setup_page(cx, SCB_OFFSET);
304 }
305
306 if (!stale)
307 mb_ack_irq(cx, order);
308
309 return str_offset ? 1 : 0;
310}
311
312static inline
313int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order, int stale)
314{
315 int ret = -1;
316
317 switch (order->rpu) {
318 case CPU:
319 {
320 switch (order->mb.cmd) {
321 case CX18_EPU_DMA_DONE:
322 ret = epu_dma_done_irq(cx, order, stale);
323 break;
324 case CX18_EPU_DEBUG:
325 ret = epu_debug_irq(cx, order, stale);
326 break;
327 default:
328 CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
329 order->mb.cmd);
330 break;
331 }
332 break;
333 }
334 case APU:
335 CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
336 order->mb.cmd);
337 break;
338 default:
339 break;
340 }
341 return ret;
342}
343
344static inline
345struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx)
346{
347 int i;
348 struct cx18_epu_work_order *order = NULL;
349
350 for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
351 /*
352 * We only need "pending" atomic to inspect its contents,
353 * and need not do a check and set because:
354 * 1. Any work handler thread only clears "pending" and only
355 * on one, particular work order at a time, per handler thread.
356 * 2. "pending" is only set here, and we're serialized because
357 * we're called in an IRQ handler context.
358 */
359 if (atomic_read(&cx->epu_work_order[i].pending) == 0) {
360 order = &cx->epu_work_order[i];
361 atomic_set(&order->pending, 1);
362 break;
363 }
364 }
365 return order;
119} 366}
120 367
368void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
369{
370 struct cx18_mailbox __iomem *mb;
371 struct cx18_mailbox *order_mb;
372 struct cx18_epu_work_order *order;
373 int stale = 0;
374 int submit;
375
376 switch (rpu) {
377 case CPU:
378 mb = &cx->scb->cpu2epu_mb;
379 break;
380 case APU:
381 mb = &cx->scb->apu2epu_mb;
382 break;
383 default:
384 return;
385 }
386
387 order = alloc_epu_work_order_irq(cx);
388 if (order == NULL) {
389 CX18_WARN("Unable to find blank work order form to schedule "
390 "incoming mailbox command processing\n");
391 return;
392 }
393
394 order->rpu = rpu;
395 order_mb = &order->mb;
396 cx18_memcpy_fromio(cx, order_mb, mb, sizeof(struct cx18_mailbox));
397
398 if (order_mb->request == order_mb->ack) {
399 CX18_WARN("Possibly falling behind: %s self-ack'ed our incoming"
400 " %s to EPU mailbox (sequence no. %u)\n",
401 rpu_str[rpu], rpu_str[rpu], order_mb->request);
402 dump_mb(cx, order_mb, "incoming");
403 stale = 1;
404 }
405
406 /*
407 * Individual EPU command processing is responsible for ack-ing
408 * a non-stale mailbox as soon as possible
409 */
410 submit = epu_cmd_irq(cx, order, stale);
411 if (submit > 0) {
412 schedule_work(&order->work);
413 }
414}
415
416
417/*
418 * Functions called from a non-interrupt, non work_queue context
419 */
420
121static void cx18_api_log_ack_delay(struct cx18 *cx, int msecs) 421static void cx18_api_log_ack_delay(struct cx18 *cx, int msecs)
122{ 422{
123 if (msecs > CX18_MAX_MB_ACK_DELAY) 423 if (msecs > CX18_MAX_MB_ACK_DELAY)
@@ -167,8 +467,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
167 } 467 }
168 468
169 mutex_lock(mb_lock); 469 mutex_lock(mb_lock);
170 cx18_setup_page(cx, SCB_OFFSET);
171
172 /* 470 /*
173 * Wait for an in-use mailbox to complete 471 * Wait for an in-use mailbox to complete
174 * 472 *
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index 54758f32db1e..002c0526afa0 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -37,6 +37,17 @@
37 37
38struct cx18; 38struct cx18;
39 39
40/*
41 * This structure is used by CPU to provide completed buffers information
42 * Its structure is dictrated by the layout of the SCB, required by the
43 * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
44 * for mailbox work order scheduling
45 */
46struct cx18_mdl_ack {
47 u32 id; /* ID of a completed MDL */
48 u32 data_used; /* Total data filled in the MDL for buffer 'id' */
49};
50
40/* The cx18_mailbox struct is the mailbox structure which is used for passing 51/* The cx18_mailbox struct is the mailbox structure which is used for passing
41 messages between processors */ 52 messages between processors */
42struct cx18_mailbox { 53struct cx18_mailbox {
@@ -73,6 +84,9 @@ int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
73int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); 84int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
74int cx18_api_func(void *priv, u32 cmd, int in, int out, 85int cx18_api_func(void *priv, u32 cmd, int in, int out,
75 u32 data[CX2341X_MBOX_MAX_DATA]); 86 u32 data[CX2341X_MBOX_MAX_DATA]);
76long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu); 87
88void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu);
89
90void cx18_epu_work_handler(struct work_struct *work);
77 91
78#endif 92#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 174682c2582f..c5a81aed61bb 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -75,30 +75,37 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
75 return buf; 75 return buf;
76} 76}
77 77
78struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, 78struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
79 u32 bytesused) 79 u32 bytesused)
80{ 80{
81 struct cx18 *cx = s->cx; 81 struct cx18 *cx = s->cx;
82 struct list_head *p; 82 struct list_head *p;
83 unsigned long flags = 0;
83 84
84 spin_lock(&s->qlock); 85 spin_lock_irqsave(&s->qlock, flags);
85 list_for_each(p, &s->q_free.list) { 86 list_for_each(p, &s->q_free.list) {
86 struct cx18_buffer *buf = 87 struct cx18_buffer *buf =
87 list_entry(p, struct cx18_buffer, list); 88 list_entry(p, struct cx18_buffer, list);
88 89
89 if (buf->id != id) 90 if (buf->id != id) {
91 CX18_DEBUG_HI_DMA("Skipping buffer %d searching for %d "
92 "in stream %s q_free\n", buf->id, id,
93 s->name);
90 continue; 94 continue;
95 }
91 96
92 buf->bytesused = bytesused; 97 buf->bytesused = bytesused;
93 atomic_dec(&s->q_free.buffers); 98 if (s->type != CX18_ENC_STREAM_TYPE_TS) {
94 atomic_inc(&s->q_full.buffers); 99 atomic_dec(&s->q_free.buffers);
95 s->q_full.bytesused += buf->bytesused; 100 atomic_inc(&s->q_full.buffers);
96 list_move_tail(&buf->list, &s->q_full.list); 101 s->q_full.bytesused += buf->bytesused;
102 list_move_tail(&buf->list, &s->q_full.list);
103 }
97 104
98 spin_unlock(&s->qlock); 105 spin_unlock_irqrestore(&s->qlock, flags);
99 return buf; 106 return buf;
100 } 107 }
101 spin_unlock(&s->qlock); 108 spin_unlock_irqrestore(&s->qlock, flags);
102 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); 109 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
103 return NULL; 110 return NULL;
104} 111}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 7f93bb13c09f..92edb5e3a627 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -46,7 +46,7 @@ void cx18_queue_init(struct cx18_queue *q);
46void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 46void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
47 struct cx18_queue *q); 47 struct cx18_queue *q);
48struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); 48struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
49struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, 49struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
50 u32 bytesused); 50 u32 bytesused);
51void cx18_flush_queues(struct cx18_stream *s); 51void cx18_flush_queues(struct cx18_stream *s);
52 52
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
index 594713bbed68..29866f02f018 100644
--- a/drivers/media/video/cx18/cx18-scb.h
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -85,12 +85,6 @@ struct cx18_mdl {
85 u32 length; /* Length of the buffer segment */ 85 u32 length; /* Length of the buffer segment */
86}; 86};
87 87
88/* This structure is used by CPU to provide completed buffers information */
89struct cx18_mdl_ack {
90 u32 id; /* ID of a completed MDL */
91 u32 data_used; /* Total data filled in the MDL for buffer 'id' */
92};
93
94struct cx18_scb { 88struct cx18_scb {
95 /* These fields form the System Control Block which is used at boot time 89 /* These fields form the System Control Block which is used at boot time
96 for localizing the IPC data as well as the code positions for all 90 for localizing the IPC data as well as the code positions for all
@@ -276,7 +270,7 @@ struct cx18_scb {
276 struct cx18_mailbox hpu2epu_mb; 270 struct cx18_mailbox hpu2epu_mb;
277 struct cx18_mailbox ppu2epu_mb; 271 struct cx18_mailbox ppu2epu_mb;
278 272
279 struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2]; 273 struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
280 struct cx18_mdl cpu_mdl[1]; 274 struct cx18_mdl cpu_mdl[1];
281}; 275};
282 276
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index c7d431f49238..e6d808f7cc8f 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -595,3 +595,21 @@ u32 cx18_find_handle(struct cx18 *cx)
595 } 595 }
596 return CX18_INVALID_TASK_HANDLE; 596 return CX18_INVALID_TASK_HANDLE;
597} 597}
598
599struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
600{
601 int i;
602 struct cx18_stream *s;
603
604 if (handle == CX18_INVALID_TASK_HANDLE)
605 return NULL;
606
607 for (i = 0; i < CX18_MAX_STREAMS; i++) {
608 s = &cx->streams[i];
609 if (s->handle != handle)
610 continue;
611 if (s->v4l2dev || s->dvb.enabled)
612 return s;
613 }
614 return NULL;
615}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index f327e947b24f..3fd578d0e6ce 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -22,6 +22,7 @@
22 */ 22 */
23 23
24u32 cx18_find_handle(struct cx18 *cx); 24u32 cx18_find_handle(struct cx18 *cx);
25struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle);
25int cx18_streams_setup(struct cx18 *cx); 26int cx18_streams_setup(struct cx18 *cx);
26int cx18_streams_register(struct cx18 *cx); 27int cx18_streams_register(struct cx18 *cx);
27void cx18_streams_cleanup(struct cx18 *cx, int unregister); 28void cx18_streams_cleanup(struct cx18 *cx, int unregister);