aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2008-08-31 23:40:41 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-10-12 07:36:58 -0400
commitc641d09c60bfa36c7cf70444f24265090e51f5ce (patch)
tree2f3a9cc7d86aed3e0d5f1c7631a7354bb490fb29
parentb1526421eac9a912b2cda7e147f1da2aa31be278 (diff)
V4L/DVB (8914): cx18: Throttle mmio to/from the CX23418 so boards work in older systems
cx18: Throttle mmio to/from the CX23418 so boards work in older systems. The CX23418 couldn't reliably handle mmio at the rate at which the cx18 driver was attempting to access the chip. The PCI bridge arrangements and settings on modern motherboards still allowed the CX23418 to work OK, but it didn't work well on many older motherboards: mysterious I2C errors, firmware loading errors, etc. This patch adds a throttle to *all* mmio access to the CX23418. It defaults to a delay of 31 ns, but is adjustable by the mmio_ndelay module parm. My HVR-1600 and Raptor PAL/SECAM card now function for analog capture on a motherboard with an Intel 82810E Northbridge and 82801AA Southbridge. Signed-off-by: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/cx18/cx18-driver.c18
-rw-r--r--drivers/media/video/cx18/cx18-driver.h4
-rw-r--r--drivers/media/video/cx18/cx18-io.c115
-rw-r--r--drivers/media/video/cx18/cx18-io.h115
-rw-r--r--drivers/media/video/cx18/cx18-queue.c2
-rw-r--r--drivers/media/video/cx18/cx18-queue.h2
6 files changed, 168 insertions, 88 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index d31e1ec8d14c..202b28190148 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -4,6 +4,7 @@
4 * Derived from ivtv-driver.c 4 * Derived from ivtv-driver.c
5 * 5 *
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> 6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
7 * Copyright (C) 2008 Andy Walls <awalls@radix.net>
7 * 8 *
8 * This program is free software; you can redistribute it and/or modify 9 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
@@ -74,10 +75,14 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
74 -1, -1, -1, -1, -1, -1, -1, -1, 75 -1, -1, -1, -1, -1, -1, -1, -1,
75 -1, -1, -1, -1, -1, -1, -1, -1, 76 -1, -1, -1, -1, -1, -1, -1, -1,
76 -1, -1, -1, -1, -1, -1, -1, -1 }; 77 -1, -1, -1, -1, -1, -1, -1, -1 };
77 78static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
79 -1, -1, -1, -1, -1, -1, -1, -1,
80 -1, -1, -1, -1, -1, -1, -1, -1,
81 -1, -1, -1, -1, -1, -1, -1, -1 };
78static unsigned cardtype_c = 1; 82static unsigned cardtype_c = 1;
79static unsigned tuner_c = 1; 83static unsigned tuner_c = 1;
80static unsigned radio_c = 1; 84static unsigned radio_c = 1;
85static int mmio_ndelay_c = 1;
81static char pal[] = "--"; 86static char pal[] = "--";
82static char secam[] = "--"; 87static char secam[] = "--";
83static char ntsc[] = "-"; 88static char ntsc[] = "-";
@@ -96,6 +101,7 @@ int cx18_debug;
96module_param_array(tuner, int, &tuner_c, 0644); 101module_param_array(tuner, int, &tuner_c, 0644);
97module_param_array(radio, bool, &radio_c, 0644); 102module_param_array(radio, bool, &radio_c, 0644);
98module_param_array(cardtype, int, &cardtype_c, 0644); 103module_param_array(cardtype, int, &cardtype_c, 0644);
104module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644);
99module_param_string(pal, pal, sizeof(pal), 0644); 105module_param_string(pal, pal, sizeof(pal), 0644);
100module_param_string(secam, secam, sizeof(secam), 0644); 106module_param_string(secam, secam, sizeof(secam), 0644);
101module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); 107module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
@@ -141,6 +147,11 @@ MODULE_PARM_DESC(debug,
141MODULE_PARM_DESC(cx18_pci_latency, 147MODULE_PARM_DESC(cx18_pci_latency,
142 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" 148 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
143 "\t\t\tDefault: Yes"); 149 "\t\t\tDefault: Yes");
150MODULE_PARM_DESC(mmio_ndelay,
151 "Delay (ns) for each CX23418 memory mapped IO access.\n"
152 "\t\t\tTry larger values that are close to a multiple of the\n"
153 "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n"
154 "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY));
144MODULE_PARM_DESC(enc_mpg_buffers, 155MODULE_PARM_DESC(enc_mpg_buffers,
145 "Encoder MPG Buffers (in MB)\n" 156 "Encoder MPG Buffers (in MB)\n"
146 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); 157 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
@@ -357,6 +368,11 @@ static void cx18_process_options(struct cx18 *cx)
357 cx->options.tuner = tuner[cx->num]; 368 cx->options.tuner = tuner[cx->num];
358 cx->options.radio = radio[cx->num]; 369 cx->options.radio = radio[cx->num];
359 370
371 if (mmio_ndelay[cx->num] < 0)
372 cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY;
373 else
374 cx->options.mmio_ndelay = mmio_ndelay[cx->num];
375
360 cx->std = cx18_parse_std(cx); 376 cx->std = cx18_parse_std(cx);
361 if (cx->options.cardtype == -1) { 377 if (cx->options.cardtype == -1) {
362 CX18_INFO("Ignore card\n"); 378 CX18_INFO("Ignore card\n");
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 2cfdbe445820..cc6f288a6da8 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -63,6 +63,9 @@
63# error "This driver requires kernel PCI support." 63# error "This driver requires kernel PCI support."
64#endif 64#endif
65 65
66/* Default delay to throttle mmio access to the CX23418 so it doesn't choke */
67#define CX18_DEFAULT_MMIO_NDELAY 31 /* 30.3 ns = 1 PCI clock(s) / 33 MHz */
68
66#define CX18_MEM_OFFSET 0x00000000 69#define CX18_MEM_OFFSET 0x00000000
67#define CX18_MEM_SIZE 0x04000000 70#define CX18_MEM_SIZE 0x04000000
68#define CX18_REG_OFFSET 0x02000000 71#define CX18_REG_OFFSET 0x02000000
@@ -176,6 +179,7 @@ struct cx18_options {
176 int cardtype; /* force card type on load */ 179 int cardtype; /* force card type on load */
177 int tuner; /* set tuner on load */ 180 int tuner; /* set tuner on load */
178 int radio; /* enable/disable radio */ 181 int radio; /* enable/disable radio */
182 unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */
179}; 183};
180 184
181/* per-buffer bit flags */ 185/* per-buffer bit flags */
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
index c53d9b6e5e46..d92f627d35e4 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -21,76 +21,69 @@
21 */ 21 */
22 22
23#include "cx18-driver.h" 23#include "cx18-driver.h"
24#include "cx18-io.h"
24#include "cx18-irq.h" 25#include "cx18-irq.h"
25 26
26void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
27{
28 __raw_writel(val, addr);
29}
30
31u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
32{
33 return __raw_readl(addr);
34}
35
36u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
37{
38 writel(val, addr);
39 return readl(addr);
40}
41
42void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
43{
44 writel(val, addr);
45}
46
47u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
48{
49 return readl(addr);
50}
51
52
53/* Access "register" region of CX23418 memory mapped I/O */
54u32 cx18_read_reg(struct cx18 *cx, u32 reg)
55{
56 return readl(cx->reg_mem + reg);
57}
58
59void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
60{
61 writel(val, cx->reg_mem + reg);
62}
63
64u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg)
65{
66 return cx18_write_sync(cx, val, cx->reg_mem + reg);
67}
68
69/* Access "encoder memory" region of CX23418 memory mapped I/O */
70u32 cx18_read_enc(struct cx18 *cx, u32 addr)
71{
72 return readl(cx->enc_mem + addr);
73}
74
75void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
76{
77 writel(val, cx->enc_mem + addr);
78}
79
80u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr)
81{
82 return cx18_write_sync(cx, val, cx->enc_mem + addr);
83}
84
85void cx18_memcpy_fromio(struct cx18 *cx, void *to, 27void cx18_memcpy_fromio(struct cx18 *cx, void *to,
86 const void __iomem *from, unsigned int len) 28 const void __iomem *from, unsigned int len)
87{ 29{
88 memcpy_fromio(to, from, len); 30 /* Align reads on the CX23418's addresses */
31 if ((len > 0) && ((unsigned)from & 1)) {
32 *((u8 *)to) = cx18_readb(cx, from);
33 len--;
34 to++;
35 from++;
36 }
37 if ((len > 1) && ((unsigned)from & 2)) {
38 *((u16 *)to) = cx18_raw_readw(cx, from);
39 len -= 2;
40 to += 2;
41 from += 2;
42 }
43 while (len > 3) {
44 *((u32 *)to) = cx18_raw_readl(cx, from);
45 len -= 4;
46 to += 4;
47 from += 4;
48 }
49 if (len > 1) {
50 *((u16 *)to) = cx18_raw_readw(cx, from);
51 len -= 2;
52 to += 2;
53 from += 2;
54 }
55 if (len > 0)
56 *((u8 *)to) = cx18_readb(cx, from);
89} 57}
90 58
91void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) 59void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
92{ 60{
93 memset_io(addr, val, count); 61 u16 val2 = val | (val << 8);
62 u32 val4 = val2 | (val2 << 16);
63
64 /* Align writes on the CX23418's addresses */
65 if ((count > 0) && ((unsigned)addr & 1)) {
66 cx18_writeb(cx, (u8) val, addr);
67 count--;
68 addr++;
69 }
70 if ((count > 1) && ((unsigned)addr & 2)) {
71 cx18_writew(cx, val2, addr);
72 count -= 2;
73 addr += 2;
74 }
75 while (count > 3) {
76 cx18_writel(cx, val4, addr);
77 count -= 4;
78 addr += 4;
79 }
80 if (count > 1) {
81 cx18_writew(cx, val2, addr);
82 count -= 2;
83 addr += 2;
84 }
85 if (count > 0)
86 cx18_writeb(cx, (u8) val, addr);
94} 87}
95 88
96void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) 89void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
index 7c08c0add490..7ab7be2531ca 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -25,38 +25,109 @@
25 25
26#include "cx18-driver.h" 26#include "cx18-driver.h"
27 27
28/* This is a PCI post thing, where if the pci register is not read, then 28static inline void cx18_io_delay(struct cx18 *cx)
29 the write doesn't always take effect right away. By reading back the 29{
30 register any pending PCI writes will be performed (in order), and so 30 if (cx->options.mmio_ndelay)
31 you can be sure that the writes are guaranteed to be done. 31 ndelay(cx->options.mmio_ndelay);
32}
32 33
33 Rarely needed, only in some timing sensitive cases. 34/* Non byteswapping memory mapped IO */
34 Apparently if this is not done some motherboards seem 35static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
35 to kill the firmware and get into the broken state until computer is 36{
36 rebooted. */ 37 __raw_writel(val, addr);
37u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr); 38 cx18_io_delay(cx);
39}
38 40
39void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr); 41static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
40u32 cx18_readl(struct cx18 *cx, const void __iomem *addr); 42{
43 u32 ret = __raw_readl(addr);
44 cx18_io_delay(cx);
45 return ret;
46}
41 47
42/* No endiannes conversion calls */ 48static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr)
43void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr); 49{
44u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr); 50 u16 ret = __raw_readw(addr);
51 cx18_io_delay(cx);
52 return ret;
53}
45 54
46/* Access "register" region of CX23418 memory mapped I/O */ 55/* Normal memory mapped IO */
47u32 cx18_read_reg(struct cx18 *cx, u32 reg); 56static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
48void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg); 57{
49u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg); 58 writel(val, addr);
59 cx18_io_delay(cx);
60}
50 61
51/* Access "encoder memory" region of CX23418 memory mapped I/O */ 62static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
52u32 cx18_read_enc(struct cx18 *cx, u32 addr); 63{
53void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr); 64 writew(val, addr);
54u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr); 65 cx18_io_delay(cx);
66}
67
68static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
69{
70 writeb(val, addr);
71 cx18_io_delay(cx);
72}
73
74static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
75{
76 u32 ret = readl(addr);
77 cx18_io_delay(cx);
78 return ret;
79}
80
81static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
82{
83 u8 ret = readb(addr);
84 cx18_io_delay(cx);
85 return ret;
86}
87
88static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
89{
90 cx18_writel(cx, val, addr);
91 return cx18_readl(cx, addr);
92}
55 93
56void cx18_memcpy_fromio(struct cx18 *cx, void *to, 94void cx18_memcpy_fromio(struct cx18 *cx, void *to,
57 const void __iomem *from, unsigned int len); 95 const void __iomem *from, unsigned int len);
58void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); 96void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
59 97
98/* Access "register" region of CX23418 memory mapped I/O */
99static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
100{
101 cx18_writel(cx, val, cx->reg_mem + reg);
102}
103
104static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
105{
106 return cx18_readl(cx, cx->reg_mem + reg);
107}
108
109static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg)
110{
111 return cx18_write_sync(cx, val, cx->reg_mem + reg);
112}
113
114/* Access "encoder memory" region of CX23418 memory mapped I/O */
115static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
116{
117 cx18_writel(cx, val, cx->enc_mem + addr);
118}
119
120static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
121{
122 return cx18_readl(cx, cx->enc_mem + addr);
123}
124
125static inline u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr)
126{
127 return cx18_write_sync(cx, val, cx->enc_mem + addr);
128}
129
130
60void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); 131void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
61void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); 132void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
62void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); 133void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 48976833e238..a33ba04a2686 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -170,7 +170,6 @@ int cx18_stream_alloc(struct cx18_stream *s)
170 } 170 }
171 buf->id = cx->buffer_id++; 171 buf->id = cx->buffer_id++;
172 INIT_LIST_HEAD(&buf->list); 172 INIT_LIST_HEAD(&buf->list);
173 /* FIXME - check for mmio */
174 buf->dma_handle = pci_map_single(s->cx->dev, 173 buf->dma_handle = pci_map_single(s->cx->dev,
175 buf->buf, s->buf_size, s->dma); 174 buf->buf, s->buf_size, s->dma);
176 cx18_buf_sync_for_cpu(s, buf); 175 cx18_buf_sync_for_cpu(s, buf);
@@ -194,7 +193,6 @@ void cx18_stream_free(struct cx18_stream *s)
194 193
195 /* empty q_free */ 194 /* empty q_free */
196 while ((buf = cx18_dequeue(s, &s->q_free))) { 195 while ((buf = cx18_dequeue(s, &s->q_free))) {
197 /* FIXME - check for mmio */
198 pci_unmap_single(s->cx->dev, buf->dma_handle, 196 pci_unmap_single(s->cx->dev, buf->dma_handle,
199 s->buf_size, s->dma); 197 s->buf_size, s->dma);
200 kfree(buf->buf); 198 kfree(buf->buf);
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 0c7df932d176..7f93bb13c09f 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -28,7 +28,6 @@
28static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, 28static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
29 struct cx18_buffer *buf) 29 struct cx18_buffer *buf)
30{ 30{
31 /* FIXME check IO transfers */
32 pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle, 31 pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle,
33 s->buf_size, s->dma); 32 s->buf_size, s->dma);
34} 33}
@@ -36,7 +35,6 @@ static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
36static inline void cx18_buf_sync_for_device(struct cx18_stream *s, 35static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
37 struct cx18_buffer *buf) 36 struct cx18_buffer *buf)
38{ 37{
39 /* FIXME check IO transfers */
40 pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle, 38 pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle,
41 s->buf_size, s->dma); 39 s->buf_size, s->dma);
42} 40}