diff options
author | Steven Toth <stoth@kernellabs.com> | 2009-05-09 20:17:28 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-18 23:14:43 -0400 |
commit | 443c1228d50518f3c550e1fef490a2c9d9246ce7 (patch) | |
tree | cde68674e62a75b616906edcea835b2ea8b19588 /drivers/media/video/saa7164 | |
parent | 9afef394308cf63ddb67b003a1c6036615456eb9 (diff) |
V4L/DVB (12923): SAA7164: Add support for the NXP SAA7164 silicon
This patch adds support for all of the known shipping Hauppauge HVR-2200
and HVR-2250 boards. Digital TV ATSC/QAM and DVB-T is enabled at this
time. Both tuners are supported.
Volatiles and typedefs need rework, the rest is coding style compliant.
Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/saa7164')
-rw-r--r-- | drivers/media/video/saa7164/Kconfig | 19 | ||||
-rw-r--r-- | drivers/media/video/saa7164/Makefile | 12 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-api.c | 619 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-buffer.c | 158 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-bus.c | 448 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-cards.c | 562 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-cmd.c | 529 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-core.c | 746 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-dvb.c | 578 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-fw.c | 615 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-i2c.c | 170 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-reg.h | 166 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-types.h | 287 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164.h | 401 |
14 files changed, 5310 insertions, 0 deletions
diff --git a/drivers/media/video/saa7164/Kconfig b/drivers/media/video/saa7164/Kconfig new file mode 100644 index 000000000000..582556792bde --- /dev/null +++ b/drivers/media/video/saa7164/Kconfig | |||
@@ -0,0 +1,19 @@ | |||
1 | config VIDEO_SAA7164 | ||
2 | tristate "NXP SAA7164 support" | ||
3 | depends on DVB_CORE && PCI && I2C | ||
4 | depends on HOTPLUG # due to FW_LOADER | ||
5 | select I2C_ALGOBIT | ||
6 | select FW_LOADER | ||
7 | select VIDEO_TUNER | ||
8 | select VIDEO_TVEEPROM | ||
9 | select VIDEOBUF_DVB | ||
10 | select DVB_TDA10048 if !DVB_FE_CUSTOMISE | ||
11 | select DVB_S5H1411 if !DVB_FE_CUSTOMISE | ||
12 | select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE | ||
13 | ---help--- | ||
14 | This is a video4linux driver for NXP SAA7164 based | ||
15 | TV cards. | ||
16 | |||
17 | To compile this driver as a module, choose M here: the | ||
18 | module will be called saa7164 | ||
19 | |||
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile new file mode 100644 index 000000000000..4b329fd42add --- /dev/null +++ b/drivers/media/video/saa7164/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ | ||
2 | saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \ | ||
3 | saa7164-buffer.o | ||
4 | |||
5 | obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o | ||
6 | |||
7 | EXTRA_CFLAGS += -Idrivers/media/video | ||
8 | EXTRA_CFLAGS += -Idrivers/media/common/tuners | ||
9 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core | ||
10 | EXTRA_CFLAGS += -Idrivers/media/dvb/frontends | ||
11 | |||
12 | EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) | ||
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c new file mode 100644 index 000000000000..8cef1df9b54f --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-api.c | |||
@@ -0,0 +1,619 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/wait.h> | ||
23 | |||
24 | #include "saa7164.h" | ||
25 | |||
26 | int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode) | ||
27 | { | ||
28 | int ret; | ||
29 | |||
30 | ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR, | ||
31 | SAA_STATE_CONTROL, sizeof(mode), &mode); | ||
32 | if (ret != SAA_OK) | ||
33 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
34 | |||
35 | return ret; | ||
36 | } | ||
37 | |||
38 | int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version) | ||
39 | { | ||
40 | int ret; | ||
41 | |||
42 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
43 | GET_FW_VERSION_CONTROL, sizeof(u32), version); | ||
44 | if (ret != SAA_OK) | ||
45 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
46 | |||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen) | ||
51 | { | ||
52 | u8 reg[] = { 0x0f, 0x00 }; | ||
53 | |||
54 | if (buflen < 128) | ||
55 | return -ENOMEM; | ||
56 | |||
57 | /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */ | ||
58 | /* TODO: Pull the details from the boards struct */ | ||
59 | return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg), | ||
60 | ®[0], 128, buf); | ||
61 | } | ||
62 | |||
63 | /* Exercise the i2c interface, saa7164_cmd()/bus() layers: | ||
64 | * 1. Read the identity byte from each of the demodulators. | ||
65 | * 2. Read the entire register set from the TDA18271. | ||
66 | * TODO: This function has no purpose other than to exercise i2c. | ||
67 | */ | ||
68 | int saa7164_api_test(struct saa7164_dev *dev) | ||
69 | { | ||
70 | /* TDA10048 identities */ | ||
71 | u8 reg[] = { 0x00 }; | ||
72 | u8 data[256]; | ||
73 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
74 | /* Read all 39 bytes from the TDA18271 tuners */ | ||
75 | saa7164_api_i2c_read(&dev->i2c_bus[1], 0xc0 >> 1, 0, | ||
76 | ®[0], 39, &data[0]); | ||
77 | saa7164_api_i2c_read(&dev->i2c_bus[2], 0xc0 >> 1, 0, | ||
78 | ®[0], 39, &data[0]); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev, | ||
84 | struct saa7164_tsport *port, | ||
85 | tmComResTSFormatDescrHeader_t *tsfmt) | ||
86 | { | ||
87 | dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex); | ||
88 | dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset); | ||
89 | dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength); | ||
90 | dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength); | ||
91 | dprintk(DBGLVL_API, " bguid = (....)\n"); | ||
92 | |||
93 | /* Cache the hardware configuration in the port */ | ||
94 | |||
95 | port->bufcounter = port->hwcfg.BARLocation; | ||
96 | port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); | ||
97 | port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); | ||
98 | port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); | ||
99 | port->bufptr32l = port->hwcfg.BARLocation + | ||
100 | (4 * sizeof(u32)) + | ||
101 | (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); | ||
102 | port->bufptr32h = port->hwcfg.BARLocation + | ||
103 | (4 * sizeof(u32)) + | ||
104 | (sizeof(u32) * port->hwcfg.buffercount); | ||
105 | port->bufptr64 = port->hwcfg.BARLocation + | ||
106 | (4 * sizeof(u32)) + | ||
107 | (sizeof(u32) * port->hwcfg.buffercount); | ||
108 | dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", | ||
109 | port->hwcfg.BARLocation); | ||
110 | |||
111 | dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n", | ||
112 | port->nr); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len) | ||
118 | { | ||
119 | struct saa7164_tsport *port = 0; | ||
120 | u32 idx, next_offset; | ||
121 | int i; | ||
122 | tmComResDescrHeader_t *hdr, *t; | ||
123 | tmComResExtDevDescrHeader_t *exthdr; | ||
124 | tmComResPathDescrHeader_t *pathhdr; | ||
125 | tmComResAntTermDescrHeader_t *anttermhdr; | ||
126 | tmComResTunerDescrHeader_t *tunerunithdr; | ||
127 | tmComResDMATermDescrHeader_t *vcoutputtermhdr; | ||
128 | tmComResTSFormatDescrHeader_t *tsfmt; | ||
129 | u32 currpath = 0; | ||
130 | |||
131 | dprintk(DBGLVL_API, | ||
132 | "%s(?,?,%d) sizeof(tmComResDescrHeader_t) = %lu bytes\n", | ||
133 | __func__, len, sizeof(tmComResDescrHeader_t)); | ||
134 | |||
135 | for (idx = 0; idx < (len - sizeof(tmComResDescrHeader_t)); ) { | ||
136 | |||
137 | hdr = (tmComResDescrHeader_t *)(buf + idx); | ||
138 | |||
139 | if (hdr->type != CS_INTERFACE) | ||
140 | return SAA_ERR_NOT_SUPPORTED; | ||
141 | |||
142 | dprintk(DBGLVL_API, "@ 0x%x = \n", idx); | ||
143 | switch (hdr->subtype) { | ||
144 | case GENERAL_REQUEST: | ||
145 | dprintk(DBGLVL_API, " GENERAL_REQUEST\n"); | ||
146 | break; | ||
147 | case VC_TUNER_PATH: | ||
148 | dprintk(DBGLVL_API, " VC_TUNER_PATH\n"); | ||
149 | pathhdr = (tmComResPathDescrHeader_t *)(buf + idx); | ||
150 | dprintk(DBGLVL_API, " pathid = 0x%x\n", | ||
151 | pathhdr->pathid); | ||
152 | currpath = pathhdr->pathid; | ||
153 | break; | ||
154 | case VC_INPUT_TERMINAL: | ||
155 | dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n"); | ||
156 | anttermhdr = | ||
157 | (tmComResAntTermDescrHeader_t *)(buf + idx); | ||
158 | dprintk(DBGLVL_API, " terminalid = 0x%x\n", | ||
159 | anttermhdr->terminalid); | ||
160 | dprintk(DBGLVL_API, " terminaltype = 0x%x\n", | ||
161 | anttermhdr->terminaltype); | ||
162 | switch (anttermhdr->terminaltype) { | ||
163 | case ITT_ANTENNA: | ||
164 | dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); | ||
165 | break; | ||
166 | case LINE_CONNECTOR: | ||
167 | dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); | ||
168 | break; | ||
169 | case SPDIF_CONNECTOR: | ||
170 | dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); | ||
171 | break; | ||
172 | case COMPOSITE_CONNECTOR: | ||
173 | dprintk(DBGLVL_API, | ||
174 | " = COMPOSITE_CONNECTOR\n"); | ||
175 | break; | ||
176 | case SVIDEO_CONNECTOR: | ||
177 | dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); | ||
178 | break; | ||
179 | case COMPONENT_CONNECTOR: | ||
180 | dprintk(DBGLVL_API, | ||
181 | " = COMPONENT_CONNECTOR\n"); | ||
182 | break; | ||
183 | case STANDARD_DMA: | ||
184 | dprintk(DBGLVL_API, " = STANDARD_DMA\n"); | ||
185 | break; | ||
186 | default: | ||
187 | dprintk(DBGLVL_API, " = undefined (0x%x)\n", | ||
188 | anttermhdr->terminaltype); | ||
189 | } | ||
190 | dprintk(DBGLVL_API, " assocterminal= 0x%x\n", | ||
191 | anttermhdr->assocterminal); | ||
192 | dprintk(DBGLVL_API, " iterminal = 0x%x\n", | ||
193 | anttermhdr->iterminal); | ||
194 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
195 | anttermhdr->controlsize); | ||
196 | break; | ||
197 | case VC_OUTPUT_TERMINAL: | ||
198 | dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n"); | ||
199 | vcoutputtermhdr = | ||
200 | (tmComResDMATermDescrHeader_t *)(buf + idx); | ||
201 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
202 | vcoutputtermhdr->unitid); | ||
203 | dprintk(DBGLVL_API, " terminaltype = 0x%x\n", | ||
204 | vcoutputtermhdr->terminaltype); | ||
205 | switch (vcoutputtermhdr->terminaltype) { | ||
206 | case ITT_ANTENNA: | ||
207 | dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); | ||
208 | break; | ||
209 | case LINE_CONNECTOR: | ||
210 | dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); | ||
211 | break; | ||
212 | case SPDIF_CONNECTOR: | ||
213 | dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); | ||
214 | break; | ||
215 | case COMPOSITE_CONNECTOR: | ||
216 | dprintk(DBGLVL_API, | ||
217 | " = COMPOSITE_CONNECTOR\n"); | ||
218 | break; | ||
219 | case SVIDEO_CONNECTOR: | ||
220 | dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); | ||
221 | break; | ||
222 | case COMPONENT_CONNECTOR: | ||
223 | dprintk(DBGLVL_API, | ||
224 | " = COMPONENT_CONNECTOR\n"); | ||
225 | break; | ||
226 | case STANDARD_DMA: | ||
227 | dprintk(DBGLVL_API, " = STANDARD_DMA\n"); | ||
228 | break; | ||
229 | default: | ||
230 | dprintk(DBGLVL_API, " = undefined (0x%x)\n", | ||
231 | vcoutputtermhdr->terminaltype); | ||
232 | } | ||
233 | dprintk(DBGLVL_API, " assocterminal= 0x%x\n", | ||
234 | vcoutputtermhdr->assocterminal); | ||
235 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
236 | vcoutputtermhdr->sourceid); | ||
237 | dprintk(DBGLVL_API, " iterminal = 0x%x\n", | ||
238 | vcoutputtermhdr->iterminal); | ||
239 | dprintk(DBGLVL_API, " BARLocation = 0x%x\n", | ||
240 | vcoutputtermhdr->BARLocation); | ||
241 | dprintk(DBGLVL_API, " flags = 0x%x\n", | ||
242 | vcoutputtermhdr->flags); | ||
243 | dprintk(DBGLVL_API, " interruptid = 0x%x\n", | ||
244 | vcoutputtermhdr->interruptid); | ||
245 | dprintk(DBGLVL_API, " buffercount = 0x%x\n", | ||
246 | vcoutputtermhdr->buffercount); | ||
247 | dprintk(DBGLVL_API, " metadatasize = 0x%x\n", | ||
248 | vcoutputtermhdr->metadatasize); | ||
249 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
250 | vcoutputtermhdr->controlsize); | ||
251 | dprintk(DBGLVL_API, " numformats = 0x%x\n", | ||
252 | vcoutputtermhdr->numformats); | ||
253 | |||
254 | t = (tmComResDescrHeader_t *) | ||
255 | ((tmComResDMATermDescrHeader_t *)(buf + idx)); | ||
256 | next_offset = idx + (vcoutputtermhdr->len); | ||
257 | for (i = 0; i < vcoutputtermhdr->numformats; i++) { | ||
258 | t = (tmComResDescrHeader_t *) | ||
259 | (buf + next_offset); | ||
260 | switch (t->subtype) { | ||
261 | case VS_FORMAT_MPEG2TS: | ||
262 | tsfmt = | ||
263 | (tmComResTSFormatDescrHeader_t *)t; | ||
264 | if (currpath == 1) | ||
265 | port = &dev->ts1; | ||
266 | else | ||
267 | port = &dev->ts2; | ||
268 | memcpy(&port->hwcfg, vcoutputtermhdr, | ||
269 | sizeof(*vcoutputtermhdr)); | ||
270 | saa7164_api_configure_port_mpeg2ts(dev, | ||
271 | port, tsfmt); | ||
272 | break; | ||
273 | case VS_FORMAT_MPEG2PS: | ||
274 | dprintk(DBGLVL_API, | ||
275 | " = VS_FORMAT_MPEG2PS\n"); | ||
276 | break; | ||
277 | case VS_FORMAT_VBI: | ||
278 | dprintk(DBGLVL_API, | ||
279 | " = VS_FORMAT_VBI\n"); | ||
280 | break; | ||
281 | case VS_FORMAT_RDS: | ||
282 | dprintk(DBGLVL_API, | ||
283 | " = VS_FORMAT_RDS\n"); | ||
284 | break; | ||
285 | case VS_FORMAT_UNCOMPRESSED: | ||
286 | dprintk(DBGLVL_API, | ||
287 | " = VS_FORMAT_UNCOMPRESSED\n"); | ||
288 | break; | ||
289 | case VS_FORMAT_TYPE: | ||
290 | dprintk(DBGLVL_API, | ||
291 | " = VS_FORMAT_TYPE\n"); | ||
292 | break; | ||
293 | default: | ||
294 | dprintk(DBGLVL_API, | ||
295 | " = undefined (0x%x)\n", | ||
296 | t->subtype); | ||
297 | } | ||
298 | next_offset += t->len; | ||
299 | } | ||
300 | |||
301 | break; | ||
302 | case TUNER_UNIT: | ||
303 | dprintk(DBGLVL_API, " TUNER_UNIT\n"); | ||
304 | tunerunithdr = | ||
305 | (tmComResTunerDescrHeader_t *)(buf + idx); | ||
306 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
307 | tunerunithdr->unitid); | ||
308 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
309 | tunerunithdr->sourceid); | ||
310 | dprintk(DBGLVL_API, " iunit = 0x%x\n", | ||
311 | tunerunithdr->iunit); | ||
312 | dprintk(DBGLVL_API, " tuningstandards = 0x%x\n", | ||
313 | tunerunithdr->tuningstandards); | ||
314 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
315 | tunerunithdr->controlsize); | ||
316 | dprintk(DBGLVL_API, " controls = 0x%x\n", | ||
317 | tunerunithdr->controls); | ||
318 | break; | ||
319 | case VC_SELECTOR_UNIT: | ||
320 | dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n"); | ||
321 | break; | ||
322 | case VC_PROCESSING_UNIT: | ||
323 | dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n"); | ||
324 | break; | ||
325 | case FEATURE_UNIT: | ||
326 | dprintk(DBGLVL_API, " FEATURE_UNIT\n"); | ||
327 | break; | ||
328 | case ENCODER_UNIT: | ||
329 | dprintk(DBGLVL_API, " ENCODER_UNIT\n"); | ||
330 | break; | ||
331 | case EXTENSION_UNIT: | ||
332 | dprintk(DBGLVL_API, " EXTENSION_UNIT\n"); | ||
333 | exthdr = (tmComResExtDevDescrHeader_t *)(buf + idx); | ||
334 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
335 | exthdr->unitid); | ||
336 | dprintk(DBGLVL_API, " deviceid = 0x%x\n", | ||
337 | exthdr->deviceid); | ||
338 | dprintk(DBGLVL_API, " devicetype = 0x%x\n", | ||
339 | exthdr->devicetype); | ||
340 | if (exthdr->devicetype & 0x1) | ||
341 | dprintk(DBGLVL_API, " = Decoder Device\n"); | ||
342 | if (exthdr->devicetype & 0x2) | ||
343 | dprintk(DBGLVL_API, " = GPIO Source\n"); | ||
344 | if (exthdr->devicetype & 0x4) | ||
345 | dprintk(DBGLVL_API, " = Video Decoder\n"); | ||
346 | if (exthdr->devicetype & 0x8) | ||
347 | dprintk(DBGLVL_API, " = Audio Decoder\n"); | ||
348 | if (exthdr->devicetype & 0x20) | ||
349 | dprintk(DBGLVL_API, " = Crossbar\n"); | ||
350 | if (exthdr->devicetype & 0x40) | ||
351 | dprintk(DBGLVL_API, " = Tuner\n"); | ||
352 | if (exthdr->devicetype & 0x80) | ||
353 | dprintk(DBGLVL_API, " = IF PLL\n"); | ||
354 | if (exthdr->devicetype & 0x100) | ||
355 | dprintk(DBGLVL_API, " = Demodulator\n"); | ||
356 | if (exthdr->devicetype & 0x200) | ||
357 | dprintk(DBGLVL_API, " = RDS Decoder\n"); | ||
358 | if (exthdr->devicetype & 0x400) | ||
359 | dprintk(DBGLVL_API, " = Encoder\n"); | ||
360 | if (exthdr->devicetype & 0x800) | ||
361 | dprintk(DBGLVL_API, " = IR Decoder\n"); | ||
362 | if (exthdr->devicetype & 0x1000) | ||
363 | dprintk(DBGLVL_API, " = EEPROM\n"); | ||
364 | if (exthdr->devicetype & 0x2000) | ||
365 | dprintk(DBGLVL_API, | ||
366 | " = VBI Decoder\n"); | ||
367 | if (exthdr->devicetype & 0x10000) | ||
368 | dprintk(DBGLVL_API, | ||
369 | " = Streaming Device\n"); | ||
370 | if (exthdr->devicetype & 0x20000) | ||
371 | dprintk(DBGLVL_API, | ||
372 | " = DRM Device\n"); | ||
373 | if (exthdr->devicetype & 0x40000000) | ||
374 | dprintk(DBGLVL_API, | ||
375 | " = Generic Device\n"); | ||
376 | if (exthdr->devicetype & 0x80000000) | ||
377 | dprintk(DBGLVL_API, | ||
378 | " = Config Space Device\n"); | ||
379 | dprintk(DBGLVL_API, " numgpiopins = 0x%x\n", | ||
380 | exthdr->numgpiopins); | ||
381 | dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n", | ||
382 | exthdr->numgpiogroups); | ||
383 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
384 | exthdr->controlsize); | ||
385 | break; | ||
386 | case PVC_INFRARED_UNIT: | ||
387 | dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n"); | ||
388 | break; | ||
389 | case DRM_UNIT: | ||
390 | dprintk(DBGLVL_API, " DRM_UNIT\n"); | ||
391 | break; | ||
392 | default: | ||
393 | dprintk(DBGLVL_API, "default %d\n", hdr->subtype); | ||
394 | } | ||
395 | |||
396 | dprintk(DBGLVL_API, " 1.%x\n", hdr->len); | ||
397 | dprintk(DBGLVL_API, " 2.%x\n", hdr->type); | ||
398 | dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype); | ||
399 | dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid); | ||
400 | |||
401 | idx += hdr->len; | ||
402 | } | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | int saa7164_api_enum_subdevs(struct saa7164_dev *dev) | ||
408 | { | ||
409 | int ret; | ||
410 | u32 buflen = 0; | ||
411 | u8 *buf; | ||
412 | |||
413 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
414 | |||
415 | /* Get the total descriptor length */ | ||
416 | ret = saa7164_cmd_send(dev, 0, GET_LEN, | ||
417 | GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen); | ||
418 | if (ret != SAA_OK) | ||
419 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
420 | |||
421 | dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n", | ||
422 | __func__, buflen); | ||
423 | |||
424 | /* Allocate enough storage for all of the descs */ | ||
425 | buf = kzalloc(buflen, GFP_KERNEL); | ||
426 | if (buf == NULL) | ||
427 | return SAA_ERR_NO_RESOURCES; | ||
428 | |||
429 | /* Retrieve them */ | ||
430 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
431 | GET_DESCRIPTORS_CONTROL, buflen, buf); | ||
432 | if (ret != SAA_OK) { | ||
433 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
434 | goto out; | ||
435 | } | ||
436 | |||
437 | if (debug & DBGLVL_API) | ||
438 | saa7164_dumphex16(dev, buf, (buflen/16)*16); | ||
439 | |||
440 | saa7164_api_dump_subdevs(dev, buf, buflen); | ||
441 | |||
442 | out: | ||
443 | kfree(buf); | ||
444 | return ret; | ||
445 | } | ||
446 | |||
447 | int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, | ||
448 | u32 datalen, u8 *data) | ||
449 | { | ||
450 | struct saa7164_dev *dev = bus->dev; | ||
451 | u16 len = 0; | ||
452 | int unitid; | ||
453 | u32 regval; | ||
454 | u8 buf[256]; | ||
455 | int ret; | ||
456 | |||
457 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
458 | |||
459 | if (reglen > 4) | ||
460 | return -EIO; | ||
461 | |||
462 | if (reglen == 1) | ||
463 | regval = *(reg); | ||
464 | else | ||
465 | if (reglen == 2) | ||
466 | regval = ((*(reg) << 8) || *(reg+1)); | ||
467 | else | ||
468 | if (reglen == 3) | ||
469 | regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2)); | ||
470 | else | ||
471 | if (reglen == 4) | ||
472 | regval = ((*(reg) << 24) | (*(reg+1) << 16) | | ||
473 | (*(reg+2) << 8) | *(reg+3)); | ||
474 | |||
475 | /* Prepare the send buffer */ | ||
476 | /* Bytes 00-03 source register length | ||
477 | * 04-07 source bytes to read | ||
478 | * 08... register address | ||
479 | */ | ||
480 | memset(buf, 0, sizeof(buf)); | ||
481 | memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen); | ||
482 | *((u32 *)(buf + 0 * sizeof(u32))) = reglen; | ||
483 | *((u32 *)(buf + 1 * sizeof(u32))) = datalen; | ||
484 | |||
485 | unitid = saa7164_i2caddr_to_unitid(bus, addr); | ||
486 | if (unitid < 0) { | ||
487 | printk(KERN_ERR | ||
488 | "%s() error, cannot translate regaddr 0x%x to unitid\n", | ||
489 | __func__, addr); | ||
490 | return -EIO; | ||
491 | } | ||
492 | |||
493 | ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, | ||
494 | EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); | ||
495 | if (ret != SAA_OK) { | ||
496 | printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); | ||
497 | return -EIO; | ||
498 | } | ||
499 | |||
500 | dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); | ||
501 | |||
502 | if (debug & DBGLVL_I2C) | ||
503 | saa7164_dumphex16(dev, buf, 2 * 16); | ||
504 | |||
505 | ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR, | ||
506 | EXU_REGISTER_ACCESS_CONTROL, len, &buf); | ||
507 | if (ret != SAA_OK) | ||
508 | printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); | ||
509 | else { | ||
510 | if (debug & DBGLVL_I2C) | ||
511 | saa7164_dumphex16(dev, buf, sizeof(buf)); | ||
512 | memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen); | ||
513 | } | ||
514 | |||
515 | return ret == SAA_OK ? 0 : -EIO; | ||
516 | } | ||
517 | |||
518 | /* For a given 8 bit i2c address device, write the buffer */ | ||
519 | int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, | ||
520 | u8 *data) | ||
521 | { | ||
522 | struct saa7164_dev *dev = bus->dev; | ||
523 | u16 len = 0; | ||
524 | int unitid; | ||
525 | int reglen; | ||
526 | u8 buf[256]; | ||
527 | int ret; | ||
528 | |||
529 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
530 | |||
531 | if ((datalen == 0) || (datalen > 232)) | ||
532 | return -EIO; | ||
533 | |||
534 | memset(buf, 0, sizeof(buf)); | ||
535 | |||
536 | unitid = saa7164_i2caddr_to_unitid(bus, addr); | ||
537 | if (unitid < 0) { | ||
538 | printk(KERN_ERR | ||
539 | "%s() error, cannot translate regaddr 0x%x to unitid\n", | ||
540 | __func__, addr); | ||
541 | return -EIO; | ||
542 | } | ||
543 | |||
544 | reglen = saa7164_i2caddr_to_reglen(bus, addr); | ||
545 | if (unitid < 0) { | ||
546 | printk(KERN_ERR | ||
547 | "%s() error, cannot translate regaddr to reglen\n", | ||
548 | __func__); | ||
549 | return -EIO; | ||
550 | } | ||
551 | |||
552 | ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, | ||
553 | EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); | ||
554 | if (ret != SAA_OK) { | ||
555 | printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); | ||
556 | return -EIO; | ||
557 | } | ||
558 | |||
559 | dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); | ||
560 | |||
561 | /* Prepare the send buffer */ | ||
562 | /* Bytes 00-03 dest register length | ||
563 | * 04-07 dest bytes to write | ||
564 | * 08... register address | ||
565 | */ | ||
566 | *((u32 *)(buf + 0 * sizeof(u32))) = reglen; | ||
567 | *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen; | ||
568 | memcpy((buf + 2 * sizeof(u32)), data, datalen); | ||
569 | |||
570 | if (debug & DBGLVL_I2C) | ||
571 | saa7164_dumphex16(dev, buf, sizeof(buf)); | ||
572 | |||
573 | ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR, | ||
574 | EXU_REGISTER_ACCESS_CONTROL, len, &buf); | ||
575 | if (ret != SAA_OK) | ||
576 | printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); | ||
577 | |||
578 | return ret == SAA_OK ? 0 : -EIO; | ||
579 | } | ||
580 | |||
581 | |||
582 | int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid, | ||
583 | u8 pin, u8 state) | ||
584 | { | ||
585 | int ret; | ||
586 | tmComResGPIO_t t; | ||
587 | |||
588 | dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n", | ||
589 | __func__, unitid, pin, state); | ||
590 | |||
591 | if ((pin > 7) || (state > 2)) | ||
592 | return SAA_ERR_BAD_PARAMETER; | ||
593 | |||
594 | t.pin = pin; | ||
595 | t.state = state; | ||
596 | |||
597 | ret = saa7164_cmd_send(dev, unitid, SET_CUR, | ||
598 | EXU_GPIO_CONTROL, sizeof(t), &t); | ||
599 | if (ret != SAA_OK) | ||
600 | printk(KERN_ERR "%s() error, ret = 0x%x\n", | ||
601 | __func__, ret); | ||
602 | |||
603 | return ret; | ||
604 | } | ||
605 | |||
606 | int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, | ||
607 | u8 pin) | ||
608 | { | ||
609 | return saa7164_api_modify_gpio(dev, unitid, pin, 1); | ||
610 | } | ||
611 | |||
612 | int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, | ||
613 | u8 pin) | ||
614 | { | ||
615 | return saa7164_api_modify_gpio(dev, unitid, pin, 0); | ||
616 | } | ||
617 | |||
618 | |||
619 | |||
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c new file mode 100644 index 000000000000..4176544ee019 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-buffer.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include "saa7164.h" | ||
23 | |||
24 | /* The PCI address space for buffer handling looks like this: | ||
25 | |||
26 | +-u32 wide-------------+ | ||
27 | | + | ||
28 | +-u64 wide------------------------------------+ | ||
29 | + + | ||
30 | +----------------------+ | ||
31 | | CurrentBufferPtr + Pointer to current PCI buffer >-+ | ||
32 | +----------------------+ | | ||
33 | | Unused + | | ||
34 | +----------------------+ | | ||
35 | | Pitch + = 188 (bytes) | | ||
36 | +----------------------+ | | ||
37 | | PCI buffer size + = pitch * number of lines (312) | | ||
38 | +----------------------+ | | ||
39 | |0| Buf0 Write Offset + | | ||
40 | +----------------------+ v | ||
41 | |1| Buf1 Write Offset + | | ||
42 | +----------------------+ | | ||
43 | |2| Buf2 Write Offset + | | ||
44 | +----------------------+ | | ||
45 | |3| Buf3 Write Offset + | | ||
46 | +----------------------+ | | ||
47 | ... More write offsets | | ||
48 | +---------------------------------------------+ | | ||
49 | +0| set of ptrs to PCI pagetables + | | ||
50 | +---------------------------------------------+ | | ||
51 | +1| set of ptrs to PCI pagetables + <--------+ | ||
52 | +---------------------------------------------+ | ||
53 | +2| set of ptrs to PCI pagetables + | ||
54 | +---------------------------------------------+ | ||
55 | +3| set of ptrs to PCI pagetables + >--+ | ||
56 | +---------------------------------------------+ | | ||
57 | ... More buffer pointers | +----------------+ | ||
58 | +->| pt[0] TS data | | ||
59 | | +----------------+ | ||
60 | | | ||
61 | | +----------------+ | ||
62 | +->| pt[1] TS data | | ||
63 | | +----------------+ | ||
64 | | etc | ||
65 | */ | ||
66 | |||
67 | /* Allocate a new buffer structure and associated PCI space in bytes. | ||
68 | * len must be a multiple of sizeof(u64) | ||
69 | */ | ||
70 | struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_tsport *port, | ||
71 | u32 len) | ||
72 | { | ||
73 | struct saa7164_buffer *buf = 0; | ||
74 | struct saa7164_dev *dev = port->dev; | ||
75 | int i; | ||
76 | |||
77 | if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) { | ||
78 | log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__); | ||
79 | goto ret; | ||
80 | } | ||
81 | |||
82 | buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL); | ||
83 | if (buf == NULL) { | ||
84 | log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__); | ||
85 | goto ret; | ||
86 | } | ||
87 | |||
88 | buf->port = port; | ||
89 | buf->flags = SAA7164_BUFFER_FREE; | ||
90 | /* TODO: arg len is being ignored */ | ||
91 | buf->pci_size = SAA7164_PT_ENTRIES * 0x1000; | ||
92 | buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; | ||
93 | |||
94 | /* Allocate contiguous memory */ | ||
95 | buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, | ||
96 | &buf->dma); | ||
97 | if (!buf->cpu) | ||
98 | goto fail1; | ||
99 | |||
100 | buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, | ||
101 | &buf->pt_dma); | ||
102 | if (!buf->pt_cpu) | ||
103 | goto fail2; | ||
104 | |||
105 | /* init the buffers to a known pattern, easier during debugging */ | ||
106 | memset(buf->cpu, 0xff, buf->pci_size); | ||
107 | memset(buf->pt_cpu, 0xff, buf->pt_size); | ||
108 | |||
109 | dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p\n", __func__, buf); | ||
110 | dprintk(DBGLVL_BUF, " pci_cpu @ 0x%llx dma @ 0x%llx len = 0x%x\n", | ||
111 | (u64)buf->cpu, (u64)buf->dma, buf->pci_size); | ||
112 | dprintk(DBGLVL_BUF, " pt_cpu @ 0x%llx pt_dma @ 0x%llx len = 0x%x\n", | ||
113 | (u64)buf->pt_cpu, (u64)buf->pt_dma, buf->pt_size); | ||
114 | |||
115 | /* Format the Page Table Entries to point into the data buffer */ | ||
116 | for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { | ||
117 | |||
118 | *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ | ||
119 | |||
120 | dprintk(DBGLVL_BUF, " pt[%02d] = 0x%llx -> 0x%llx\n", | ||
121 | i, (u64)buf->pt_cpu, (u64)*(buf->pt_cpu)); | ||
122 | |||
123 | } | ||
124 | |||
125 | goto ret; | ||
126 | |||
127 | fail2: | ||
128 | pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); | ||
129 | fail1: | ||
130 | kfree(buf); | ||
131 | |||
132 | buf = 0; | ||
133 | ret: | ||
134 | return buf; | ||
135 | } | ||
136 | |||
137 | int saa7164_buffer_dealloc(struct saa7164_tsport *port, | ||
138 | struct saa7164_buffer *buf) | ||
139 | { | ||
140 | struct saa7164_dev *dev = port->dev; | ||
141 | |||
142 | if ((buf == 0) || (port == 0)) | ||
143 | return SAA_ERR_BAD_PARAMETER; | ||
144 | |||
145 | dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", __func__, buf); | ||
146 | |||
147 | if (buf->flags != SAA7164_BUFFER_FREE) | ||
148 | log_warn(" freeing a non-free buffer\n"); | ||
149 | |||
150 | pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); | ||
151 | pci_free_consistent(port->dev->pci, buf->pt_size, buf->pt_cpu, | ||
152 | buf->pt_dma); | ||
153 | |||
154 | kfree(buf); | ||
155 | |||
156 | return SAA_OK; | ||
157 | } | ||
158 | |||
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c new file mode 100644 index 000000000000..28f630dc49c9 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-bus.c | |||
@@ -0,0 +1,448 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include "saa7164.h" | ||
23 | |||
24 | /* The message bus to/from the firmware is a ring buffer in PCI address | ||
25 | * space. Establish the defaults. | ||
26 | */ | ||
27 | int saa7164_bus_setup(struct saa7164_dev *dev) | ||
28 | { | ||
29 | tmComResBusInfo_t *b = &dev->bus; | ||
30 | |||
31 | mutex_init(&b->lock); | ||
32 | |||
33 | b->Type = TYPE_BUS_PCIe; | ||
34 | b->m_wMaxReqSize = SAA_DEVICE_MAXREQUESTSIZE; | ||
35 | |||
36 | b->m_pdwSetRing = (u8 *)(dev->bmmio + | ||
37 | ((u32)dev->busdesc.CommandRing)); | ||
38 | |||
39 | b->m_dwSizeSetRing = SAA_DEVICE_BUFFERBLOCKSIZE; | ||
40 | |||
41 | b->m_pdwGetRing = (u8 *)(dev->bmmio + | ||
42 | ((u32)dev->busdesc.ResponseRing)); | ||
43 | |||
44 | b->m_dwSizeGetRing = SAA_DEVICE_BUFFERBLOCKSIZE; | ||
45 | |||
46 | b->m_pdwSetWritePos = (u32 *)((u8 *)(dev->bmmio + | ||
47 | ((u32)dev->intfdesc.BARLocation) + (2 * sizeof(u64)))); | ||
48 | |||
49 | b->m_pdwSetReadPos = (u32 *)((u8 *)b->m_pdwSetWritePos + | ||
50 | 1 * sizeof(u32)); | ||
51 | |||
52 | b->m_pdwGetWritePos = (u32 *)((u8 *)b->m_pdwSetWritePos + | ||
53 | 2 * sizeof(u32)); | ||
54 | |||
55 | b->m_pdwGetReadPos = (u32 *)((u8 *)b->m_pdwSetWritePos + | ||
56 | 3 * sizeof(u32)); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | void saa7164_bus_dump(struct saa7164_dev *dev) | ||
62 | { | ||
63 | tmComResBusInfo_t *b = &dev->bus; | ||
64 | |||
65 | dprintk(DBGLVL_BUS, "Dumping the bus structure:\n"); | ||
66 | dprintk(DBGLVL_BUS, " .type = %d\n", b->Type); | ||
67 | dprintk(DBGLVL_BUS, " .dev->bmmio = 0x%p\n", dev->bmmio); | ||
68 | dprintk(DBGLVL_BUS, " .m_wMaxReqSize = 0x%x\n", b->m_wMaxReqSize); | ||
69 | dprintk(DBGLVL_BUS, " .m_pdwSetRing = 0x%p\n", b->m_pdwSetRing); | ||
70 | dprintk(DBGLVL_BUS, " .m_dwSizeSetRing = 0x%x\n", b->m_dwSizeSetRing); | ||
71 | dprintk(DBGLVL_BUS, " .m_pdwGetRing = 0x%p\n", b->m_pdwGetRing); | ||
72 | dprintk(DBGLVL_BUS, " .m_dwSizeGetRing = 0x%x\n", b->m_dwSizeGetRing); | ||
73 | |||
74 | dprintk(DBGLVL_BUS, " .m_pdwSetWritePos = 0x%p (0x%08x)\n", | ||
75 | b->m_pdwSetWritePos, *b->m_pdwSetWritePos); | ||
76 | |||
77 | dprintk(DBGLVL_BUS, " .m_pdwSetReadPos = 0x%p (0x%08x)\n", | ||
78 | b->m_pdwSetReadPos, *b->m_pdwSetReadPos); | ||
79 | |||
80 | dprintk(DBGLVL_BUS, " .m_pdwGetWritePos = 0x%p (0x%08x)\n", | ||
81 | b->m_pdwGetWritePos, *b->m_pdwGetWritePos); | ||
82 | |||
83 | dprintk(DBGLVL_BUS, " .m_pdwGetReadPos = 0x%p (0x%08x)\n", | ||
84 | b->m_pdwGetReadPos, *b->m_pdwGetReadPos); | ||
85 | } | ||
86 | |||
87 | void saa7164_bus_dumpmsg(struct saa7164_dev *dev, tmComResInfo_t* m, void *buf) | ||
88 | { | ||
89 | dprintk(DBGLVL_BUS, "Dumping msg structure:\n"); | ||
90 | dprintk(DBGLVL_BUS, " .id = %d\n", m->id); | ||
91 | dprintk(DBGLVL_BUS, " .flags = 0x%x\n", m->flags); | ||
92 | dprintk(DBGLVL_BUS, " .size = 0x%x\n", m->size); | ||
93 | dprintk(DBGLVL_BUS, " .command = 0x%x\n", m->command); | ||
94 | dprintk(DBGLVL_BUS, " .controlselector = 0x%x\n", m->controlselector); | ||
95 | dprintk(DBGLVL_BUS, " .seqno = %d\n", m->seqno); | ||
96 | if (buf) | ||
97 | dprintk(DBGLVL_BUS, " .buffer (ignored)\n"); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Places a command or a response on the bus. The implementation does not | ||
102 | * know if it is a command or a response it just places the data on the | ||
103 | * bus depending on the bus information given in the tmComResBusInfo_t | ||
104 | * structure. If the command or response does not fit into the bus ring | ||
105 | * buffer it will be refused. | ||
106 | * | ||
107 | * Return Value: | ||
108 | * SAA_OK The function executed successfully. | ||
109 | * < 0 One or more members are not initialized. | ||
110 | */ | ||
111 | int saa7164_bus_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf) | ||
112 | { | ||
113 | tmComResBusInfo_t *bus = &dev->bus; | ||
114 | u32 bytes_to_write, read_distance, timeout, curr_srp, curr_swp; | ||
115 | u32 new_swp, space_rem; | ||
116 | int ret = SAA_ERR_BAD_PARAMETER; | ||
117 | |||
118 | if (!msg) { | ||
119 | printk(KERN_ERR "%s() !msg\n", __func__); | ||
120 | return SAA_ERR_BAD_PARAMETER; | ||
121 | } | ||
122 | |||
123 | dprintk(DBGLVL_BUS, "%s()\n", __func__); | ||
124 | |||
125 | msg->size = cpu_to_le16(msg->size); | ||
126 | msg->command = cpu_to_le16(msg->command); | ||
127 | msg->controlselector = cpu_to_le16(msg->controlselector); | ||
128 | |||
129 | if (msg->size > dev->bus.m_wMaxReqSize) { | ||
130 | printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", | ||
131 | __func__); | ||
132 | return SAA_ERR_BAD_PARAMETER; | ||
133 | } | ||
134 | |||
135 | if ((msg->size > 0) && (buf == 0)) { | ||
136 | printk(KERN_ERR "%s() Missing message buffer\n", __func__); | ||
137 | return SAA_ERR_BAD_PARAMETER; | ||
138 | } | ||
139 | |||
140 | /* Lock the bus from any other access */ | ||
141 | mutex_lock(&bus->lock); | ||
142 | |||
143 | bytes_to_write = sizeof(*msg) + msg->size; | ||
144 | read_distance = 0; | ||
145 | timeout = SAA_BUS_TIMEOUT; | ||
146 | curr_srp = le32_to_cpu(*bus->m_pdwSetReadPos); | ||
147 | curr_swp = le32_to_cpu(*bus->m_pdwSetWritePos); | ||
148 | |||
149 | /* Deal with ring wrapping issues */ | ||
150 | if (curr_srp > curr_swp) | ||
151 | /* The ring has not wrapped yet */ | ||
152 | read_distance = curr_srp - curr_swp; | ||
153 | else | ||
154 | /* Deal with the wrapped ring */ | ||
155 | read_distance = (curr_srp + bus->m_dwSizeSetRing) - curr_swp; | ||
156 | |||
157 | dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__, | ||
158 | bytes_to_write); | ||
159 | |||
160 | dprintk(DBGLVL_BUS, "%s() read_distance = %d\n", __func__, | ||
161 | read_distance); | ||
162 | |||
163 | dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp); | ||
164 | dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp); | ||
165 | |||
166 | /* Process the msg and write the content onto the bus */ | ||
167 | while (bytes_to_write >= read_distance) { | ||
168 | |||
169 | if (timeout-- == 0) { | ||
170 | printk(KERN_ERR "%s() bus timeout\n", __func__); | ||
171 | ret = SAA_ERR_NO_RESOURCES; | ||
172 | goto out; | ||
173 | } | ||
174 | |||
175 | /* TODO: Review this delay, efficient? */ | ||
176 | /* Wait, allowing the hardware fetch time */ | ||
177 | mdelay(1); | ||
178 | |||
179 | /* Check the space usage again */ | ||
180 | curr_srp = le32_to_cpu(*bus->m_pdwSetReadPos); | ||
181 | |||
182 | /* Deal with ring wrapping issues */ | ||
183 | if (curr_srp > curr_swp) | ||
184 | /* Read didn't wrap around the buffer */ | ||
185 | read_distance = curr_srp - curr_swp; | ||
186 | else | ||
187 | /* Deal with the wrapped ring */ | ||
188 | read_distance = (curr_srp + bus->m_dwSizeSetRing) - | ||
189 | curr_swp; | ||
190 | |||
191 | } | ||
192 | |||
193 | /* Calculate the new write position */ | ||
194 | new_swp = curr_swp + bytes_to_write; | ||
195 | |||
196 | dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); | ||
197 | dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__, | ||
198 | bus->m_dwSizeSetRing); | ||
199 | |||
200 | /* Mental Note: line 462 tmmhComResBusPCIe.cpp */ | ||
201 | |||
202 | /* Check if we're going to wrap again */ | ||
203 | if (new_swp > bus->m_dwSizeSetRing) { | ||
204 | |||
205 | /* Ring wraps */ | ||
206 | new_swp -= bus->m_dwSizeSetRing; | ||
207 | |||
208 | space_rem = bus->m_dwSizeSetRing - curr_swp; | ||
209 | |||
210 | dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__, | ||
211 | space_rem); | ||
212 | |||
213 | dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %lu\n", __func__, | ||
214 | sizeof(*msg)); | ||
215 | |||
216 | if (space_rem < sizeof(*msg)) { | ||
217 | dprintk(DBGLVL_BUS, "%s() tr4\n", __func__); | ||
218 | |||
219 | /* Split the msg into pieces as the ring wraps */ | ||
220 | memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem); | ||
221 | memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem, | ||
222 | sizeof(*msg) - space_rem); | ||
223 | |||
224 | memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem, | ||
225 | buf, msg->size); | ||
226 | |||
227 | } else if (space_rem == sizeof(*msg)) { | ||
228 | dprintk(DBGLVL_BUS, "%s() tr5\n", __func__); | ||
229 | |||
230 | /* Additional data at the beginning of the ring */ | ||
231 | memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); | ||
232 | memcpy(bus->m_pdwSetRing, buf, msg->size); | ||
233 | |||
234 | } else { | ||
235 | /* Additional data wraps around the ring */ | ||
236 | memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); | ||
237 | if (msg->size > 0) { | ||
238 | memcpy(bus->m_pdwSetRing + curr_swp + | ||
239 | sizeof(*msg), buf, space_rem - | ||
240 | sizeof(*msg)); | ||
241 | memcpy(bus->m_pdwSetRing, (u8 *)buf + | ||
242 | space_rem - sizeof(*msg), | ||
243 | bytes_to_write - space_rem); | ||
244 | } | ||
245 | |||
246 | } | ||
247 | |||
248 | } /* (new_swp > bus->m_dwSizeSetRing) */ | ||
249 | else { | ||
250 | dprintk(DBGLVL_BUS, "%s() tr6\n", __func__); | ||
251 | |||
252 | /* The ring buffer doesn't wrap, two simple copies */ | ||
253 | memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); | ||
254 | memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf, | ||
255 | msg->size); | ||
256 | } | ||
257 | |||
258 | dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); | ||
259 | |||
260 | /* TODO: Convert all of the volatiles and direct PCI writes into | ||
261 | * saa7164_writel/b calls for consistency. | ||
262 | */ | ||
263 | |||
264 | /* Update the bus write position */ | ||
265 | *bus->m_pdwSetWritePos = cpu_to_le32(new_swp); | ||
266 | ret = SAA_OK; | ||
267 | |||
268 | out: | ||
269 | mutex_unlock(&bus->lock); | ||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * Receive a command or a response from the bus. The implementation does not | ||
275 | * know if it is a command or a response it simply dequeues the data, | ||
276 | * depending on the bus information given in the tmComResBusInfo_t structure. | ||
277 | * | ||
278 | * Return Value: | ||
279 | * 0 The function executed successfully. | ||
280 | * < 0 One or more members are not initialized. | ||
281 | */ | ||
282 | int saa7164_bus_get(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf, | ||
283 | int peekonly) | ||
284 | { | ||
285 | tmComResBusInfo_t *bus = &dev->bus; | ||
286 | u32 bytes_to_read, write_distance, curr_grp, curr_gwp, | ||
287 | new_grp, buf_size, space_rem; | ||
288 | tmComResInfo_t msg_tmp; | ||
289 | int ret = SAA_ERR_BAD_PARAMETER; | ||
290 | |||
291 | if (msg == 0) | ||
292 | return ret; | ||
293 | |||
294 | if (msg->size > dev->bus.m_wMaxReqSize) { | ||
295 | printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", | ||
296 | __func__); | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | if ((peekonly == 0) && (msg->size > 0) && (buf == 0)) { | ||
301 | printk(KERN_ERR | ||
302 | "%s() Missing msg buf, size should be %d bytes\n", | ||
303 | __func__, msg->size); | ||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | mutex_lock(&bus->lock); | ||
308 | |||
309 | /* Peek the bus to see if a msg exists, if it's not what we're expecting | ||
310 | * then return cleanly else read the message from the bus. | ||
311 | */ | ||
312 | curr_gwp = le32_to_cpu(*bus->m_pdwGetWritePos); | ||
313 | curr_grp = le32_to_cpu(*bus->m_pdwGetReadPos); | ||
314 | |||
315 | if (curr_gwp == curr_grp) { | ||
316 | dprintk(DBGLVL_BUS, "%s() No message on the bus\n", __func__); | ||
317 | ret = SAA_ERR_EMPTY; | ||
318 | goto out; | ||
319 | } | ||
320 | |||
321 | bytes_to_read = sizeof(*msg); | ||
322 | |||
323 | /* Calculate write distance to current read position */ | ||
324 | write_distance = 0; | ||
325 | if (curr_gwp >= curr_grp) | ||
326 | /* Write doesn't wrap around the ring */ | ||
327 | write_distance = curr_gwp - curr_grp; | ||
328 | else | ||
329 | /* Write wraps around the ring */ | ||
330 | write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; | ||
331 | |||
332 | if (bytes_to_read > write_distance) { | ||
333 | printk(KERN_ERR "%s() No message/response found\n", __func__); | ||
334 | ret = SAA_ERR_INVALID_COMMAND; | ||
335 | goto out; | ||
336 | } | ||
337 | |||
338 | /* Calculate the new read position */ | ||
339 | new_grp = curr_grp + bytes_to_read; | ||
340 | if (new_grp > bus->m_dwSizeGetRing) { | ||
341 | |||
342 | /* Ring wraps */ | ||
343 | new_grp -= bus->m_dwSizeGetRing; | ||
344 | space_rem = bus->m_dwSizeGetRing - curr_grp; | ||
345 | |||
346 | memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem); | ||
347 | memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing, | ||
348 | bytes_to_read - space_rem); | ||
349 | |||
350 | } else { | ||
351 | /* No wrapping */ | ||
352 | memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read); | ||
353 | } | ||
354 | |||
355 | /* No need to update the read positions, because this was a peek */ | ||
356 | /* If the caller specifically want to peek, return */ | ||
357 | if (peekonly) { | ||
358 | memcpy(msg, &msg_tmp, sizeof(*msg)); | ||
359 | goto peekout; | ||
360 | } | ||
361 | |||
362 | /* Check if the command/response matches what is expected */ | ||
363 | if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) || | ||
364 | (msg_tmp.controlselector != msg->controlselector) || | ||
365 | (msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) { | ||
366 | |||
367 | printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__); | ||
368 | saa7164_bus_dumpmsg(dev, msg, buf); | ||
369 | saa7164_bus_dumpmsg(dev, &msg_tmp, 0); | ||
370 | ret = SAA_ERR_INVALID_COMMAND; | ||
371 | goto out; | ||
372 | } | ||
373 | |||
374 | /* Get the actual command and response from the bus */ | ||
375 | buf_size = msg->size; | ||
376 | |||
377 | bytes_to_read = sizeof(*msg) + msg->size; | ||
378 | /* Calculate write distance to current read position */ | ||
379 | write_distance = 0; | ||
380 | if (curr_gwp >= curr_grp) | ||
381 | /* Write doesn't wrap around the ring */ | ||
382 | write_distance = curr_gwp - curr_grp; | ||
383 | else | ||
384 | /* Write wraps around the ring */ | ||
385 | write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; | ||
386 | |||
387 | if (bytes_to_read > write_distance) { | ||
388 | printk(KERN_ERR "%s() Invalid bus state, missing msg " | ||
389 | "or mangled ring, faulty H/W / bad code?\n", __func__); | ||
390 | ret = SAA_ERR_INVALID_COMMAND; | ||
391 | goto out; | ||
392 | } | ||
393 | |||
394 | /* Calculate the new read position */ | ||
395 | new_grp = curr_grp + bytes_to_read; | ||
396 | if (new_grp > bus->m_dwSizeGetRing) { | ||
397 | |||
398 | /* Ring wraps */ | ||
399 | new_grp -= bus->m_dwSizeGetRing; | ||
400 | space_rem = bus->m_dwSizeGetRing - curr_grp; | ||
401 | |||
402 | if (space_rem < sizeof(*msg)) { | ||
403 | /* msg wraps around the ring */ | ||
404 | memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem); | ||
405 | memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing, | ||
406 | sizeof(*msg) - space_rem); | ||
407 | if (buf) | ||
408 | memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) - | ||
409 | space_rem, buf_size); | ||
410 | |||
411 | } else if (space_rem == sizeof(*msg)) { | ||
412 | memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); | ||
413 | if (buf) | ||
414 | memcpy(buf, bus->m_pdwGetRing, buf_size); | ||
415 | } else { | ||
416 | /* Additional data wraps around the ring */ | ||
417 | memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); | ||
418 | if (buf) { | ||
419 | memcpy(buf, bus->m_pdwGetRing + curr_grp + | ||
420 | sizeof(*msg), space_rem - sizeof(*msg)); | ||
421 | memcpy(buf + space_rem - sizeof(*msg), | ||
422 | bus->m_pdwGetRing, bytes_to_read - | ||
423 | space_rem); | ||
424 | } | ||
425 | |||
426 | } | ||
427 | |||
428 | } else { | ||
429 | /* No wrapping */ | ||
430 | memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); | ||
431 | if (buf) | ||
432 | memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), | ||
433 | buf_size); | ||
434 | } | ||
435 | |||
436 | /* Update the read positions, adjusting the ring */ | ||
437 | *bus->m_pdwGetReadPos = cpu_to_le32(new_grp); | ||
438 | |||
439 | peekout: | ||
440 | msg->size = le16_to_cpu(msg->size); | ||
441 | msg->command = le16_to_cpu(msg->command); | ||
442 | msg->controlselector = le16_to_cpu(msg->controlselector); | ||
443 | ret = SAA_OK; | ||
444 | out: | ||
445 | mutex_unlock(&bus->lock); | ||
446 | return ret; | ||
447 | } | ||
448 | |||
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c new file mode 100644 index 000000000000..0678b5f31bdd --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-cards.c | |||
@@ -0,0 +1,562 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <linux/delay.h> | ||
26 | |||
27 | #include "saa7164.h" | ||
28 | |||
29 | /* The Bridge API needs to understand register widths (in bytes) for the | ||
30 | * attached I2C devices, so we can simplify the virtual i2c mechansms | ||
31 | * and keep the -i2c.c implementation clean. | ||
32 | */ | ||
33 | #define REGLEN_8bit 1 | ||
34 | #define REGLEN_16bit 2 | ||
35 | |||
36 | struct saa7164_board saa7164_boards[] = { | ||
37 | [SAA7164_BOARD_UNKNOWN] = { | ||
38 | /* Bridge will not load any firmware, without knowing | ||
39 | * the rev this would be fatal. */ | ||
40 | .name = "Unknown", | ||
41 | }, | ||
42 | [SAA7164_BOARD_UNKNOWN_REV2] = { | ||
43 | /* Bridge will load the v2 f/w and dump descriptors */ | ||
44 | /* Required during new board bringup */ | ||
45 | .name = "Generic Rev2", | ||
46 | .chiprev = SAA7164_CHIP_REV2, | ||
47 | }, | ||
48 | [SAA7164_BOARD_UNKNOWN_REV3] = { | ||
49 | /* Bridge will load the v2 f/w and dump descriptors */ | ||
50 | /* Required during new board bringup */ | ||
51 | .name = "Generic Rev3", | ||
52 | .chiprev = SAA7164_CHIP_REV3, | ||
53 | }, | ||
54 | [SAA7164_BOARD_HAUPPAUGE_HVR2200] = { | ||
55 | .name = "Hauppauge WinTV-HVR2200", | ||
56 | .porta = SAA7164_MPEG_DVB, | ||
57 | .portb = SAA7164_MPEG_DVB, | ||
58 | .chiprev = SAA7164_CHIP_REV3, | ||
59 | .unit = {{ | ||
60 | .id = 0x06, | ||
61 | .type = SAA7164_UNIT_EEPROM, | ||
62 | .name = "4K EEPROM", | ||
63 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
64 | .i2c_bus_addr = 0xa0 >> 1, | ||
65 | .i2c_reg_len = REGLEN_8bit, | ||
66 | }, { | ||
67 | .id = 0x04, | ||
68 | .type = SAA7164_UNIT_TUNER, | ||
69 | .name = "TDA18271-1", | ||
70 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
71 | .i2c_bus_addr = 0xc0 >> 1, | ||
72 | .i2c_reg_len = REGLEN_8bit, | ||
73 | }, { | ||
74 | .id = 0x1b, | ||
75 | .type = SAA7164_UNIT_TUNER, | ||
76 | .name = "TDA18271-2", | ||
77 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
78 | .i2c_bus_addr = 0xc0 >> 1, | ||
79 | .i2c_reg_len = REGLEN_8bit, | ||
80 | }, { | ||
81 | .id = 0x1e, | ||
82 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
83 | .name = "TDA10048-1", | ||
84 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
85 | .i2c_bus_addr = 0x10 >> 1, | ||
86 | .i2c_reg_len = REGLEN_8bit, | ||
87 | }, { | ||
88 | .id = 0x1f, | ||
89 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
90 | .name = "TDA10048-2", | ||
91 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
92 | .i2c_bus_addr = 0x12 >> 1, | ||
93 | .i2c_reg_len = REGLEN_8bit, | ||
94 | } }, | ||
95 | }, | ||
96 | [SAA7164_BOARD_HAUPPAUGE_HVR2200_2] = { | ||
97 | .name = "Hauppauge WinTV-HVR2200", | ||
98 | .porta = SAA7164_MPEG_DVB, | ||
99 | .portb = SAA7164_MPEG_DVB, | ||
100 | .chiprev = SAA7164_CHIP_REV2, | ||
101 | .unit = {{ | ||
102 | .id = 0x06, | ||
103 | .type = SAA7164_UNIT_EEPROM, | ||
104 | .name = "4K EEPROM", | ||
105 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
106 | .i2c_bus_addr = 0xa0 >> 1, | ||
107 | .i2c_reg_len = REGLEN_8bit, | ||
108 | }, { | ||
109 | .id = 0x04, | ||
110 | .type = SAA7164_UNIT_TUNER, | ||
111 | .name = "TDA18271-1", | ||
112 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
113 | .i2c_bus_addr = 0xc0 >> 1, | ||
114 | .i2c_reg_len = REGLEN_8bit, | ||
115 | }, { | ||
116 | .id = 0x05, | ||
117 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
118 | .name = "TDA10048-1", | ||
119 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
120 | .i2c_bus_addr = 0x10 >> 1, | ||
121 | .i2c_reg_len = REGLEN_8bit, | ||
122 | }, { | ||
123 | .id = 0x1e, | ||
124 | .type = SAA7164_UNIT_TUNER, | ||
125 | .name = "TDA18271-2", | ||
126 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
127 | .i2c_bus_addr = 0xc0 >> 1, | ||
128 | .i2c_reg_len = REGLEN_8bit, | ||
129 | }, { | ||
130 | .id = 0x1f, | ||
131 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
132 | .name = "TDA10048-2", | ||
133 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
134 | .i2c_bus_addr = 0x12 >> 1, | ||
135 | .i2c_reg_len = REGLEN_8bit, | ||
136 | } }, | ||
137 | }, | ||
138 | [SAA7164_BOARD_HAUPPAUGE_HVR2200_3] = { | ||
139 | .name = "Hauppauge WinTV-HVR2200", | ||
140 | .porta = SAA7164_MPEG_DVB, | ||
141 | .portb = SAA7164_MPEG_DVB, | ||
142 | .chiprev = SAA7164_CHIP_REV2, | ||
143 | .unit = {{ | ||
144 | .id = 0x06, | ||
145 | .type = SAA7164_UNIT_EEPROM, | ||
146 | .name = "4K EEPROM", | ||
147 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
148 | .i2c_bus_addr = 0xa0 >> 1, | ||
149 | .i2c_reg_len = REGLEN_8bit, | ||
150 | }, { | ||
151 | .id = 0x04, | ||
152 | .type = SAA7164_UNIT_TUNER, | ||
153 | .name = "TDA18271-1", | ||
154 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
155 | .i2c_bus_addr = 0xc0 >> 1, | ||
156 | .i2c_reg_len = REGLEN_8bit, | ||
157 | }, { | ||
158 | .id = 0x05, | ||
159 | .type = SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
160 | .name = "TDA8290-1", | ||
161 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
162 | .i2c_bus_addr = 0x84 >> 1, | ||
163 | .i2c_reg_len = REGLEN_8bit, | ||
164 | }, { | ||
165 | .id = 0x1b, | ||
166 | .type = SAA7164_UNIT_TUNER, | ||
167 | .name = "TDA18271-2", | ||
168 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
169 | .i2c_bus_addr = 0xc0 >> 1, | ||
170 | .i2c_reg_len = REGLEN_8bit, | ||
171 | }, { | ||
172 | .id = 0x1c, | ||
173 | .type = SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
174 | .name = "TDA8290-2", | ||
175 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
176 | .i2c_bus_addr = 0x84 >> 1, | ||
177 | .i2c_reg_len = REGLEN_8bit, | ||
178 | }, { | ||
179 | .id = 0x1e, | ||
180 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
181 | .name = "TDA10048-1", | ||
182 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
183 | .i2c_bus_addr = 0x10 >> 1, | ||
184 | .i2c_reg_len = REGLEN_8bit, | ||
185 | }, { | ||
186 | .id = 0x1f, | ||
187 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
188 | .name = "TDA10048-2", | ||
189 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
190 | .i2c_bus_addr = 0x12 >> 1, | ||
191 | .i2c_reg_len = REGLEN_8bit, | ||
192 | } }, | ||
193 | }, | ||
194 | [SAA7164_BOARD_HAUPPAUGE_HVR2250] = { | ||
195 | .name = "Hauppauge WinTV-HVR2250", | ||
196 | .porta = SAA7164_MPEG_DVB, | ||
197 | .portb = SAA7164_MPEG_DVB, | ||
198 | .chiprev = SAA7164_CHIP_REV3, | ||
199 | .unit = {{ | ||
200 | .id = 0x22, | ||
201 | .type = SAA7164_UNIT_EEPROM, | ||
202 | .name = "4K EEPROM", | ||
203 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
204 | .i2c_bus_addr = 0xa0 >> 1, | ||
205 | .i2c_reg_len = REGLEN_8bit, | ||
206 | }, { | ||
207 | .id = 0x04, | ||
208 | .type = SAA7164_UNIT_TUNER, | ||
209 | .name = "TDA18271-1", | ||
210 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
211 | .i2c_bus_addr = 0xc0 >> 1, | ||
212 | .i2c_reg_len = REGLEN_8bit, | ||
213 | }, { | ||
214 | .id = 0x07, | ||
215 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
216 | .name = "CX24228/S5H1411-1 (TOP)", | ||
217 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
218 | .i2c_bus_addr = 0x32 >> 1, | ||
219 | .i2c_reg_len = REGLEN_8bit, | ||
220 | }, { | ||
221 | .id = 0x08, | ||
222 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
223 | .name = "CX24228/S5H1411-1 (QAM)", | ||
224 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
225 | .i2c_bus_addr = 0x34 >> 1, | ||
226 | .i2c_reg_len = REGLEN_8bit, | ||
227 | }, { | ||
228 | .id = 0x1e, | ||
229 | .type = SAA7164_UNIT_TUNER, | ||
230 | .name = "TDA18271-2", | ||
231 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
232 | .i2c_bus_addr = 0xc0 >> 1, | ||
233 | .i2c_reg_len = REGLEN_8bit, | ||
234 | }, { | ||
235 | .id = 0x20, | ||
236 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
237 | .name = "CX24228/S5H1411-2 (TOP)", | ||
238 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
239 | .i2c_bus_addr = 0x32 >> 1, | ||
240 | .i2c_reg_len = REGLEN_8bit, | ||
241 | }, { | ||
242 | .id = 0x23, | ||
243 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
244 | .name = "CX24228/S5H1411-2 (QAM)", | ||
245 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
246 | .i2c_bus_addr = 0x34 >> 1, | ||
247 | .i2c_reg_len = REGLEN_8bit, | ||
248 | } }, | ||
249 | }, | ||
250 | [SAA7164_BOARD_HAUPPAUGE_HVR2250_2] = { | ||
251 | .name = "Hauppauge WinTV-HVR2250", | ||
252 | .porta = SAA7164_MPEG_DVB, | ||
253 | .portb = SAA7164_MPEG_DVB, | ||
254 | .chiprev = SAA7164_CHIP_REV3, | ||
255 | .unit = {{ | ||
256 | .id = 0x22, | ||
257 | .type = SAA7164_UNIT_EEPROM, | ||
258 | .name = "4K EEPROM", | ||
259 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
260 | .i2c_bus_addr = 0xa0 >> 1, | ||
261 | .i2c_reg_len = REGLEN_8bit, | ||
262 | }, { | ||
263 | .id = 0x04, | ||
264 | .type = SAA7164_UNIT_TUNER, | ||
265 | .name = "TDA18271-1", | ||
266 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
267 | .i2c_bus_addr = 0xc0 >> 1, | ||
268 | .i2c_reg_len = REGLEN_8bit, | ||
269 | }, { | ||
270 | .id = 0x07, | ||
271 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
272 | .name = "CX24228/S5H1411-1 (TOP)", | ||
273 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
274 | .i2c_bus_addr = 0x32 >> 1, | ||
275 | .i2c_reg_len = REGLEN_8bit, | ||
276 | }, { | ||
277 | .id = 0x08, | ||
278 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
279 | .name = "CX24228/S5H1411-1 (QAM)", | ||
280 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
281 | .i2c_bus_addr = 0x34 >> 1, | ||
282 | .i2c_reg_len = REGLEN_8bit, | ||
283 | }, { | ||
284 | .id = 0x24, | ||
285 | .type = SAA7164_UNIT_TUNER, | ||
286 | .name = "TDA18271-2", | ||
287 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
288 | .i2c_bus_addr = 0xc0 >> 1, | ||
289 | .i2c_reg_len = REGLEN_8bit, | ||
290 | }, { | ||
291 | .id = 0x26, | ||
292 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
293 | .name = "CX24228/S5H1411-2 (TOP)", | ||
294 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
295 | .i2c_bus_addr = 0x32 >> 1, | ||
296 | .i2c_reg_len = REGLEN_8bit, | ||
297 | }, { | ||
298 | .id = 0x29, | ||
299 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
300 | .name = "CX24228/S5H1411-2 (QAM)", | ||
301 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
302 | .i2c_bus_addr = 0x34 >> 1, | ||
303 | .i2c_reg_len = REGLEN_8bit, | ||
304 | } }, | ||
305 | }, | ||
306 | }; | ||
307 | const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); | ||
308 | |||
309 | /* ------------------------------------------------------------------ */ | ||
310 | /* PCI subsystem IDs */ | ||
311 | |||
312 | struct saa7164_subid saa7164_subids[] = { | ||
313 | { | ||
314 | .subvendor = 0x0070, | ||
315 | .subdevice = 0x8880, | ||
316 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, | ||
317 | }, { | ||
318 | .subvendor = 0x0070, | ||
319 | .subdevice = 0x8810, | ||
320 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, | ||
321 | }, { | ||
322 | .subvendor = 0x0070, | ||
323 | .subdevice = 0x8980, | ||
324 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200, | ||
325 | }, { | ||
326 | .subvendor = 0x0070, | ||
327 | .subdevice = 0x8900, | ||
328 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_2, | ||
329 | }, { | ||
330 | .subvendor = 0x0070, | ||
331 | .subdevice = 0x8901, | ||
332 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_3, | ||
333 | }, { | ||
334 | .subvendor = 0x0070, | ||
335 | .subdevice = 0x88A1, | ||
336 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, | ||
337 | }, { | ||
338 | .subvendor = 0x0070, | ||
339 | .subdevice = 0x8891, | ||
340 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, | ||
341 | }, | ||
342 | }; | ||
343 | const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); | ||
344 | |||
345 | void saa7164_card_list(struct saa7164_dev *dev) | ||
346 | { | ||
347 | int i; | ||
348 | |||
349 | if (0 == dev->pci->subsystem_vendor && | ||
350 | 0 == dev->pci->subsystem_device) { | ||
351 | printk(KERN_ERR | ||
352 | "%s: Board has no valid PCIe Subsystem ID and can't\n" | ||
353 | "%s: be autodetected. Pass card=<n> insmod option to\n" | ||
354 | "%s: workaround that. Send complaints to the vendor\n" | ||
355 | "%s: of the TV card. Best regards,\n" | ||
356 | "%s: -- tux\n", | ||
357 | dev->name, dev->name, dev->name, dev->name, dev->name); | ||
358 | } else { | ||
359 | printk(KERN_ERR | ||
360 | "%s: Your board isn't known (yet) to the driver.\n" | ||
361 | "%s: Try to pick one of the existing card configs via\n" | ||
362 | "%s: card=<n> insmod option. Updating to the latest\n" | ||
363 | "%s: version might help as well.\n", | ||
364 | dev->name, dev->name, dev->name, dev->name); | ||
365 | } | ||
366 | |||
367 | printk(KERN_ERR "%s: Here are valid choices for the card=<n> insmod " | ||
368 | "option:\n", dev->name); | ||
369 | |||
370 | for (i = 0; i < saa7164_bcount; i++) | ||
371 | printk(KERN_ERR "%s: card=%d -> %s\n", | ||
372 | dev->name, i, saa7164_boards[i].name); | ||
373 | } | ||
374 | |||
375 | /* TODO: clean this define up into the -cards.c structs */ | ||
376 | #define PCIEBRIDGE_UNITID 2 | ||
377 | |||
378 | void saa7164_gpio_setup(struct saa7164_dev *dev) | ||
379 | { | ||
380 | |||
381 | |||
382 | switch (dev->board) { | ||
383 | case SAA7164_BOARD_HAUPPAUGE_HVR2200: | ||
384 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: | ||
385 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: | ||
386 | case SAA7164_BOARD_HAUPPAUGE_HVR2250: | ||
387 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: | ||
388 | /* | ||
389 | GPIO 2: s5h1411 / tda10048-1 demod reset | ||
390 | GPIO 3: s5h1411 / tda10048-2 demod reset | ||
391 | GPIO 7: IRBlaster Zilog reset | ||
392 | */ | ||
393 | |||
394 | /* Reset parts by going in and out of reset */ | ||
395 | saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2); | ||
396 | saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3); | ||
397 | |||
398 | msleep(10); | ||
399 | |||
400 | saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 2); | ||
401 | saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3); | ||
402 | break; | ||
403 | } | ||
404 | |||
405 | } | ||
406 | |||
407 | static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data) | ||
408 | { | ||
409 | struct tveeprom tv; | ||
410 | |||
411 | /* TODO: Assumption: eeprom on bus 0 */ | ||
412 | tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv, | ||
413 | eeprom_data); | ||
414 | |||
415 | /* Make sure we support the board model */ | ||
416 | switch (tv.model) { | ||
417 | case 88001: | ||
418 | /* Development board - Limit circulation */ | ||
419 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
420 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ | ||
421 | case 88021: | ||
422 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
423 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, MCE CIR, FM */ | ||
424 | break; | ||
425 | case 88041: | ||
426 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
427 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ | ||
428 | break; | ||
429 | case 88061: | ||
430 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
431 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, FM */ | ||
432 | break; | ||
433 | case 89519: | ||
434 | case 89609: | ||
435 | /* WinTV-HVR2200 (PCIe, Retail, full-height) | ||
436 | * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ | ||
437 | break; | ||
438 | case 89619: | ||
439 | /* WinTV-HVR2200 (PCIe, Retail, half-height) | ||
440 | * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ | ||
441 | break; | ||
442 | default: | ||
443 | printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n", | ||
444 | dev->name, tv.model); | ||
445 | break; | ||
446 | } | ||
447 | |||
448 | printk(KERN_INFO "%s: Hauppauge eeprom: model=%d\n", dev->name, | ||
449 | tv.model); | ||
450 | } | ||
451 | |||
452 | void saa7164_card_setup(struct saa7164_dev *dev) | ||
453 | { | ||
454 | static u8 eeprom[256]; | ||
455 | |||
456 | if (dev->i2c_bus[0].i2c_rc == 0) { | ||
457 | if (saa7164_api_read_eeprom(dev, &eeprom[0], | ||
458 | sizeof(eeprom)) < 0) | ||
459 | return; | ||
460 | } | ||
461 | |||
462 | switch (dev->board) { | ||
463 | case SAA7164_BOARD_HAUPPAUGE_HVR2200: | ||
464 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: | ||
465 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: | ||
466 | case SAA7164_BOARD_HAUPPAUGE_HVR2250: | ||
467 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: | ||
468 | hauppauge_eeprom(dev, &eeprom[0]); | ||
469 | break; | ||
470 | } | ||
471 | } | ||
472 | |||
473 | /* With most other drivers, the kernel expects to communicate with subdrivers | ||
474 | * through i2c. This bridge does not allow that, it does not expose any direct | ||
475 | * access to I2C. Instead we have to communicate through the device f/w for | ||
476 | * register access to 'processing units'. Each unit has a unique | ||
477 | * id, regardless of how the physical implementation occurs across | ||
478 | * the three physical i2c busses. The being said if we want leverge of | ||
479 | * the existing kernel drivers for tuners and demods we have to 'speak i2c', | ||
480 | * to this bridge implements 3 virtual i2c buses. This is a helper function | ||
481 | * for those. | ||
482 | * | ||
483 | * Description: Translate the kernels notion of an i2c address and bus into | ||
484 | * the appropriate unitid. | ||
485 | */ | ||
486 | int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr) | ||
487 | { | ||
488 | /* For a given bus and i2c device address, return the saa7164 unique | ||
489 | * unitid. < 0 on error */ | ||
490 | |||
491 | struct saa7164_dev *dev = bus->dev; | ||
492 | struct saa7164_unit *unit; | ||
493 | int i; | ||
494 | |||
495 | for (i = 0; i < SAA7164_MAX_UNITS; i++) { | ||
496 | unit = &saa7164_boards[dev->board].unit[i]; | ||
497 | |||
498 | if (unit->type == SAA7164_UNIT_UNDEFINED) | ||
499 | continue; | ||
500 | if ((bus->nr == unit->i2c_bus_nr) && | ||
501 | (addr == unit->i2c_bus_addr)) | ||
502 | return unit->id; | ||
503 | } | ||
504 | |||
505 | return -1; | ||
506 | } | ||
507 | |||
508 | /* The 7164 API needs to know the i2c register length in advance. | ||
509 | * this is a helper function. Based on a specific chip addr and bus return the | ||
510 | * reg length. | ||
511 | */ | ||
512 | int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr) | ||
513 | { | ||
514 | /* For a given bus and i2c device address, return the | ||
515 | * saa7164 registry address width. < 0 on error | ||
516 | */ | ||
517 | |||
518 | struct saa7164_dev *dev = bus->dev; | ||
519 | struct saa7164_unit *unit; | ||
520 | int i; | ||
521 | |||
522 | for (i = 0; i < SAA7164_MAX_UNITS; i++) { | ||
523 | unit = &saa7164_boards[dev->board].unit[i]; | ||
524 | |||
525 | if (unit->type == SAA7164_UNIT_UNDEFINED) | ||
526 | continue; | ||
527 | |||
528 | if ((bus->nr == unit->i2c_bus_nr) && | ||
529 | (addr == unit->i2c_bus_addr)) | ||
530 | return unit->i2c_reg_len; | ||
531 | } | ||
532 | |||
533 | return -1; | ||
534 | } | ||
535 | /* TODO: implement a 'findeeprom' functio like the above and fix any other | ||
536 | * eeprom related todo's in -api.c. | ||
537 | */ | ||
538 | |||
539 | /* Translate a unitid into a x readable device name, for display purposes. */ | ||
540 | char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid) | ||
541 | { | ||
542 | char *undefed = "UNDEFINED"; | ||
543 | char *bridge = "BRIDGE"; | ||
544 | struct saa7164_unit *unit; | ||
545 | int i; | ||
546 | |||
547 | if (unitid == 0) | ||
548 | return bridge; | ||
549 | |||
550 | for (i = 0; i < SAA7164_MAX_UNITS; i++) { | ||
551 | unit = &saa7164_boards[dev->board].unit[i]; | ||
552 | |||
553 | if (unit->type == SAA7164_UNIT_UNDEFINED) | ||
554 | continue; | ||
555 | |||
556 | if (unitid == unit->id) | ||
557 | return unit->name; | ||
558 | } | ||
559 | |||
560 | return undefed; | ||
561 | } | ||
562 | |||
diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c new file mode 100644 index 000000000000..0c3585bb2321 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-cmd.c | |||
@@ -0,0 +1,529 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/wait.h> | ||
23 | |||
24 | #include "saa7164.h" | ||
25 | |||
26 | int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev) | ||
27 | { | ||
28 | int i, ret = -1; | ||
29 | |||
30 | mutex_lock(&dev->lock); | ||
31 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
32 | if (dev->cmds[i].inuse == 0) { | ||
33 | dev->cmds[i].inuse = 1; | ||
34 | dev->cmds[i].signalled = 0; | ||
35 | dev->cmds[i].timeout = 0; | ||
36 | ret = dev->cmds[i].seqno; | ||
37 | break; | ||
38 | } | ||
39 | } | ||
40 | mutex_unlock(&dev->lock); | ||
41 | |||
42 | return ret; | ||
43 | } | ||
44 | |||
45 | void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno) | ||
46 | { | ||
47 | mutex_lock(&dev->lock); | ||
48 | if ((dev->cmds[seqno].inuse == 1) && | ||
49 | (dev->cmds[seqno].seqno == seqno)) { | ||
50 | dev->cmds[seqno].inuse = 0; | ||
51 | dev->cmds[seqno].signalled = 0; | ||
52 | dev->cmds[seqno].timeout = 0; | ||
53 | } | ||
54 | mutex_unlock(&dev->lock); | ||
55 | } | ||
56 | |||
57 | void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno) | ||
58 | { | ||
59 | mutex_lock(&dev->lock); | ||
60 | if ((dev->cmds[seqno].inuse == 1) && | ||
61 | (dev->cmds[seqno].seqno == seqno)) { | ||
62 | dev->cmds[seqno].timeout = 1; | ||
63 | } | ||
64 | mutex_unlock(&dev->lock); | ||
65 | } | ||
66 | |||
67 | u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno) | ||
68 | { | ||
69 | int ret = 0; | ||
70 | |||
71 | mutex_lock(&dev->lock); | ||
72 | if ((dev->cmds[seqno].inuse == 1) && | ||
73 | (dev->cmds[seqno].seqno == seqno)) { | ||
74 | ret = dev->cmds[seqno].timeout; | ||
75 | } | ||
76 | mutex_unlock(&dev->lock); | ||
77 | |||
78 | return ret; | ||
79 | } | ||
80 | |||
81 | /* Commands to the f/w get marshelled to/from this code then onto the PCI | ||
82 | * -bus/c running buffer. */ | ||
83 | int saa7164_cmd_dequeue(struct saa7164_dev *dev) | ||
84 | { | ||
85 | int loop = 1; | ||
86 | int ret; | ||
87 | u32 timeout; | ||
88 | wait_queue_head_t *q = 0; | ||
89 | u8 tmp[512]; | ||
90 | dprintk(DBGLVL_CMD, "%s()\n", __func__); | ||
91 | |||
92 | while (loop) { | ||
93 | |||
94 | tmComResInfo_t tRsp = { 0, 0, 0, 0, 0, 0 }; | ||
95 | ret = saa7164_bus_get(dev, &tRsp, NULL, 1); | ||
96 | if (ret == SAA_ERR_EMPTY) | ||
97 | return SAA_OK; | ||
98 | |||
99 | if (ret != SAA_OK) | ||
100 | return ret; | ||
101 | |||
102 | q = &dev->cmds[tRsp.seqno].wait; | ||
103 | timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); | ||
104 | dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); | ||
105 | if (timeout) { | ||
106 | printk(KERN_ERR "found timed out command on the bus\n"); | ||
107 | |||
108 | /* Clean the bus */ | ||
109 | ret = saa7164_bus_get(dev, &tRsp, &tmp, 0); | ||
110 | printk(KERN_ERR "ret = %x\n", ret); | ||
111 | if (ret == SAA_ERR_EMPTY) | ||
112 | /* Someone else already fetched the response */ | ||
113 | return SAA_OK; | ||
114 | |||
115 | if (ret != SAA_OK) | ||
116 | return ret; | ||
117 | |||
118 | if (tRsp.flags & PVC_CMDFLAG_CONTINUE) | ||
119 | printk(KERN_ERR "split response\n"); | ||
120 | else | ||
121 | saa7164_cmd_free_seqno(dev, tRsp.seqno); | ||
122 | |||
123 | printk(KERN_ERR " timeout continue\n"); | ||
124 | continue; | ||
125 | } | ||
126 | |||
127 | dprintk(DBGLVL_CMD, "%s() signalled seqno(%d) (for dequeue)\n", | ||
128 | __func__, tRsp.seqno); | ||
129 | dev->cmds[tRsp.seqno].signalled = 1; | ||
130 | wake_up(q); | ||
131 | return SAA_OK; | ||
132 | } | ||
133 | |||
134 | return SAA_OK; | ||
135 | } | ||
136 | |||
137 | int saa7164_cmd_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf) | ||
138 | { | ||
139 | tmComResBusInfo_t *bus = &dev->bus; | ||
140 | u8 cmd_sent; | ||
141 | u16 size, idx; | ||
142 | u32 cmds; | ||
143 | void *tmp; | ||
144 | int ret = -1; | ||
145 | |||
146 | if (!msg) { | ||
147 | printk(KERN_ERR "%s() !msg\n", __func__); | ||
148 | return SAA_ERR_BAD_PARAMETER; | ||
149 | } | ||
150 | |||
151 | mutex_lock(&dev->cmds[msg->id].lock); | ||
152 | |||
153 | size = msg->size; | ||
154 | idx = 0; | ||
155 | cmds = size / bus->m_wMaxReqSize; | ||
156 | if (size % bus->m_wMaxReqSize == 0) | ||
157 | cmds -= 1; | ||
158 | |||
159 | cmd_sent = 0; | ||
160 | |||
161 | /* Split the request into smaller chunks */ | ||
162 | for (idx = 0; idx < cmds; idx++) { | ||
163 | |||
164 | msg->flags |= SAA_CMDFLAG_CONTINUE; | ||
165 | msg->size = bus->m_wMaxReqSize; | ||
166 | tmp = buf + idx * bus->m_wMaxReqSize; | ||
167 | |||
168 | ret = saa7164_bus_set(dev, msg, tmp); | ||
169 | if (ret != SAA_OK) { | ||
170 | printk(KERN_ERR "%s() set failed %d\n", __func__, ret); | ||
171 | |||
172 | if (cmd_sent) { | ||
173 | ret = SAA_ERR_BUSY; | ||
174 | goto out; | ||
175 | } | ||
176 | ret = SAA_ERR_OVERFLOW; | ||
177 | goto out; | ||
178 | } | ||
179 | cmd_sent = 1; | ||
180 | } | ||
181 | |||
182 | /* If not the last command... */ | ||
183 | if (idx != 0) | ||
184 | msg->flags &= ~SAA_CMDFLAG_CONTINUE; | ||
185 | |||
186 | msg->size = size - idx * bus->m_wMaxReqSize; | ||
187 | |||
188 | ret = saa7164_bus_set(dev, msg, buf + idx * bus->m_wMaxReqSize); | ||
189 | if (ret != SAA_OK) { | ||
190 | printk(KERN_ERR "%s() set last failed %d\n", __func__, ret); | ||
191 | |||
192 | if (cmd_sent) { | ||
193 | ret = SAA_ERR_BUSY; | ||
194 | goto out; | ||
195 | } | ||
196 | ret = SAA_ERR_OVERFLOW; | ||
197 | goto out; | ||
198 | } | ||
199 | ret = SAA_OK; | ||
200 | |||
201 | out: | ||
202 | mutex_unlock(&dev->cmds[msg->id].lock); | ||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | /* Wait for a signal event, without holding a mutex. Either return TIMEOUT if | ||
207 | * the event never occured, or SAA_OK if it was signaled during the wait. | ||
208 | */ | ||
209 | int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno) | ||
210 | { | ||
211 | wait_queue_head_t *q = 0; | ||
212 | int ret = SAA_BUS_TIMEOUT; | ||
213 | unsigned long stamp; | ||
214 | int r; | ||
215 | |||
216 | if (debug >= 4) | ||
217 | saa7164_bus_dump(dev); | ||
218 | |||
219 | dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno); | ||
220 | |||
221 | mutex_lock(&dev->lock); | ||
222 | if ((dev->cmds[seqno].inuse == 1) && | ||
223 | (dev->cmds[seqno].seqno == seqno)) { | ||
224 | q = &dev->cmds[seqno].wait; | ||
225 | } | ||
226 | mutex_unlock(&dev->lock); | ||
227 | |||
228 | if (q) { | ||
229 | /* If we haven't been signalled we need to wait */ | ||
230 | if (dev->cmds[seqno].signalled == 0) { | ||
231 | stamp = jiffies; | ||
232 | dprintk(DBGLVL_CMD, | ||
233 | "%s(seqno=%d) Waiting (signalled=%d)\n", | ||
234 | __func__, seqno, dev->cmds[seqno].signalled); | ||
235 | |||
236 | /* Wait for signalled to be flagged or timeout */ | ||
237 | wait_event_timeout(*q, dev->cmds[seqno].signalled, HZ); | ||
238 | r = time_before(jiffies, stamp + HZ); | ||
239 | if (r) | ||
240 | ret = SAA_OK; | ||
241 | else | ||
242 | saa7164_cmd_timeout_seqno(dev, seqno); | ||
243 | |||
244 | dprintk(DBGLVL_CMD, "%s(seqno=%d) Waiting res = %d " | ||
245 | "(signalled=%d)\n", __func__, seqno, r, | ||
246 | dev->cmds[seqno].signalled); | ||
247 | } else | ||
248 | ret = SAA_OK; | ||
249 | } else | ||
250 | printk(KERN_ERR "%s(seqno=%d) seqno is invalid\n", | ||
251 | __func__, seqno); | ||
252 | |||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno) | ||
257 | { | ||
258 | int i; | ||
259 | dprintk(DBGLVL_CMD, "%s()\n", __func__); | ||
260 | |||
261 | mutex_lock(&dev->lock); | ||
262 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
263 | if (dev->cmds[i].inuse == 1) { | ||
264 | dprintk(DBGLVL_CMD, | ||
265 | "seqno %d inuse, sig = %d, t/out = %d\n", | ||
266 | dev->cmds[i].seqno, | ||
267 | dev->cmds[i].signalled, | ||
268 | dev->cmds[i].timeout); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
273 | if ((dev->cmds[i].inuse == 1) && ((i == 0) || | ||
274 | (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) { | ||
275 | dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n", | ||
276 | __func__, i); | ||
277 | dev->cmds[i].signalled = 1; | ||
278 | wake_up(&dev->cmds[i].wait); | ||
279 | } | ||
280 | } | ||
281 | mutex_unlock(&dev->lock); | ||
282 | } | ||
283 | |||
284 | int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, tmComResCmd_t command, | ||
285 | u16 controlselector, u16 size, void *buf) | ||
286 | { | ||
287 | tmComResInfo_t command_t, *pcommand_t; | ||
288 | tmComResInfo_t response_t, *presponse_t; | ||
289 | u8 errdata[256]; | ||
290 | u16 resp_dsize; | ||
291 | u16 data_recd; | ||
292 | u32 loop; | ||
293 | int ret; | ||
294 | int safety = 0; | ||
295 | |||
296 | dprintk(DBGLVL_CMD, "%s(unitid = %s (%d) , command = 0x%x, " | ||
297 | "sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id, | ||
298 | command, controlselector); | ||
299 | |||
300 | if ((size == 0) || (buf == 0)) { | ||
301 | printk(KERN_ERR "%s() Invalid param\n", __func__); | ||
302 | return SAA_ERR_BAD_PARAMETER; | ||
303 | } | ||
304 | |||
305 | /* Prepare some basic command/response structures */ | ||
306 | memset(&command_t, 0, sizeof(command_t)); | ||
307 | memset(&response_t, 0, sizeof(&response_t)); | ||
308 | pcommand_t = &command_t; | ||
309 | presponse_t = &response_t; | ||
310 | command_t.id = id; | ||
311 | command_t.command = command; | ||
312 | command_t.controlselector = controlselector; | ||
313 | command_t.size = size; | ||
314 | |||
315 | /* Allocate a unique sequence number */ | ||
316 | ret = saa7164_cmd_alloc_seqno(dev); | ||
317 | if (ret < 0) { | ||
318 | printk(KERN_ERR "%s() No free sequences\n", __func__); | ||
319 | ret = SAA_ERR_NO_RESOURCES; | ||
320 | goto out; | ||
321 | } | ||
322 | |||
323 | command_t.seqno = (u8)ret; | ||
324 | |||
325 | /* Send Command */ | ||
326 | resp_dsize = size; | ||
327 | pcommand_t->size = size; | ||
328 | |||
329 | dprintk(DBGLVL_CMD, "%s() pcommand_t.seqno = %d\n", | ||
330 | __func__, pcommand_t->seqno); | ||
331 | |||
332 | dprintk(DBGLVL_CMD, "%s() pcommand_t.size = %d\n", | ||
333 | __func__, pcommand_t->size); | ||
334 | |||
335 | ret = saa7164_cmd_set(dev, pcommand_t, buf); | ||
336 | if (ret != SAA_OK) { | ||
337 | printk(KERN_ERR "%s() set command failed %d\n", __func__, ret); | ||
338 | |||
339 | if (ret != SAA_ERR_BUSY) | ||
340 | saa7164_cmd_free_seqno(dev, pcommand_t->seqno); | ||
341 | else | ||
342 | /* Flag a timeout, because at least one | ||
343 | * command was sent */ | ||
344 | saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); | ||
345 | |||
346 | goto out; | ||
347 | } | ||
348 | |||
349 | /* With split responses we have to collect the msgs piece by piece */ | ||
350 | data_recd = 0; | ||
351 | loop = 1; | ||
352 | while (loop) { | ||
353 | dprintk(DBGLVL_CMD, "%s() loop\n", __func__); | ||
354 | |||
355 | ret = saa7164_cmd_wait(dev, pcommand_t->seqno); | ||
356 | dprintk(DBGLVL_CMD, "%s() loop ret = %d\n", __func__, ret); | ||
357 | |||
358 | /* if power is down and this is not a power command ... */ | ||
359 | |||
360 | if (ret == SAA_BUS_TIMEOUT) { | ||
361 | printk(KERN_ERR "Event timed out\n"); | ||
362 | saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); | ||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | if (ret != SAA_OK) { | ||
367 | printk(KERN_ERR "spurious error\n"); | ||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | /* Peek response */ | ||
372 | ret = saa7164_bus_get(dev, presponse_t, NULL, 1); | ||
373 | if (ret == SAA_ERR_EMPTY) { | ||
374 | dprintk(4, "%s() SAA_ERR_EMPTY\n", __func__); | ||
375 | continue; | ||
376 | } | ||
377 | if (ret != SAA_OK) { | ||
378 | printk(KERN_ERR "peek failed\n"); | ||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | dprintk(DBGLVL_CMD, "%s() presponse_t->seqno = %d\n", | ||
383 | __func__, presponse_t->seqno); | ||
384 | |||
385 | dprintk(DBGLVL_CMD, "%s() presponse_t->flags = 0x%x\n", | ||
386 | __func__, presponse_t->flags); | ||
387 | |||
388 | dprintk(DBGLVL_CMD, "%s() presponse_t->size = %d\n", | ||
389 | __func__, presponse_t->size); | ||
390 | |||
391 | /* Check if the response was for our command */ | ||
392 | if (presponse_t->seqno != pcommand_t->seqno) { | ||
393 | |||
394 | dprintk(DBGLVL_CMD, | ||
395 | "wrong event: seqno = %d, " | ||
396 | "expected seqno = %d, " | ||
397 | "will dequeue regardless\n", | ||
398 | presponse_t->seqno, pcommand_t->seqno); | ||
399 | |||
400 | ret = saa7164_cmd_dequeue(dev); | ||
401 | if (ret != SAA_OK) { | ||
402 | printk(KERN_ERR "dequeue failed, ret = %d\n", | ||
403 | ret); | ||
404 | if (safety++ > 16) { | ||
405 | printk(KERN_ERR | ||
406 | "dequeue exceeded, safety exit\n"); | ||
407 | return SAA_ERR_BUSY; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | continue; | ||
412 | } | ||
413 | |||
414 | if ((presponse_t->flags & PVC_RESPONSEFLAG_ERROR) != 0) { | ||
415 | |||
416 | memset(&errdata[0], 0, sizeof(errdata)); | ||
417 | |||
418 | ret = saa7164_bus_get(dev, presponse_t, &errdata[0], 0); | ||
419 | if (ret != SAA_OK) { | ||
420 | printk(KERN_ERR "get error(2)\n"); | ||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | saa7164_cmd_free_seqno(dev, pcommand_t->seqno); | ||
425 | |||
426 | dprintk(DBGLVL_CMD, "%s() errdata %02x%02x%02x%02x\n", | ||
427 | __func__, errdata[0], errdata[1], errdata[2], | ||
428 | errdata[3]); | ||
429 | |||
430 | /* Map error codes */ | ||
431 | dprintk(DBGLVL_CMD, "%s() cmd, error code = 0x%x\n", | ||
432 | __func__, errdata[0]); | ||
433 | |||
434 | switch (errdata[0]) { | ||
435 | case PVC_ERRORCODE_INVALID_COMMAND: | ||
436 | dprintk(DBGLVL_CMD, "%s() INVALID_COMMAND\n", | ||
437 | __func__); | ||
438 | ret = SAA_ERR_INVALID_COMMAND; | ||
439 | break; | ||
440 | case PVC_ERRORCODE_INVALID_DATA: | ||
441 | dprintk(DBGLVL_CMD, "%s() INVALID_DATA\n", | ||
442 | __func__); | ||
443 | ret = SAA_ERR_BAD_PARAMETER; | ||
444 | break; | ||
445 | case PVC_ERRORCODE_TIMEOUT: | ||
446 | dprintk(DBGLVL_CMD, "%s() TIMEOUT\n", __func__); | ||
447 | ret = SAA_ERR_TIMEOUT; | ||
448 | break; | ||
449 | case PVC_ERRORCODE_NAK: | ||
450 | dprintk(DBGLVL_CMD, "%s() NAK\n", __func__); | ||
451 | ret = SAA_ERR_NULL_PACKET; | ||
452 | break; | ||
453 | case PVC_ERRORCODE_UNKNOWN: | ||
454 | case PVC_ERRORCODE_INVALID_CONTROL: | ||
455 | dprintk(DBGLVL_CMD, | ||
456 | "%s() UNKNOWN OR INVALID CONTROL\n", | ||
457 | __func__); | ||
458 | default: | ||
459 | dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__); | ||
460 | ret = SAA_ERR_NOT_SUPPORTED; | ||
461 | } | ||
462 | |||
463 | /* See of other commands are on the bus */ | ||
464 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
465 | printk(KERN_ERR "dequeue(2) failed\n"); | ||
466 | |||
467 | return ret; | ||
468 | } | ||
469 | |||
470 | /* If response is invalid */ | ||
471 | if ((presponse_t->id != pcommand_t->id) || | ||
472 | (presponse_t->command != pcommand_t->command) || | ||
473 | (presponse_t->controlselector != | ||
474 | pcommand_t->controlselector) || | ||
475 | (((resp_dsize - data_recd) != presponse_t->size) && | ||
476 | !(presponse_t->flags & PVC_CMDFLAG_CONTINUE)) || | ||
477 | ((resp_dsize - data_recd) < presponse_t->size)) { | ||
478 | |||
479 | /* Invalid */ | ||
480 | dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__); | ||
481 | ret = saa7164_bus_get(dev, presponse_t, 0, 0); | ||
482 | if (ret != SAA_OK) { | ||
483 | printk(KERN_ERR "get failed\n"); | ||
484 | return ret; | ||
485 | } | ||
486 | |||
487 | /* See of other commands are on the bus */ | ||
488 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
489 | printk(KERN_ERR "dequeue(3) failed\n"); | ||
490 | continue; | ||
491 | } | ||
492 | |||
493 | /* OK, now we're actually getting out correct response */ | ||
494 | ret = saa7164_bus_get(dev, presponse_t, buf + data_recd, 0); | ||
495 | if (ret != SAA_OK) { | ||
496 | printk(KERN_ERR "get failed\n"); | ||
497 | return ret; | ||
498 | } | ||
499 | |||
500 | data_recd = presponse_t->size + data_recd; | ||
501 | if (resp_dsize == data_recd) { | ||
502 | dprintk(DBGLVL_CMD, "%s() Resp recd\n", __func__); | ||
503 | break; | ||
504 | } | ||
505 | |||
506 | /* See of other commands are on the bus */ | ||
507 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
508 | printk(KERN_ERR "dequeue(3) failed\n"); | ||
509 | |||
510 | continue; | ||
511 | |||
512 | } /* (loop) */ | ||
513 | |||
514 | /* Release the sequence number allocation */ | ||
515 | saa7164_cmd_free_seqno(dev, pcommand_t->seqno); | ||
516 | |||
517 | /* if powerdown signal all pending commands */ | ||
518 | |||
519 | dprintk(DBGLVL_CMD, "%s() Calling dequeue then exit\n", __func__); | ||
520 | |||
521 | /* See of other commands are on the bus */ | ||
522 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
523 | printk(KERN_ERR "dequeue(4) failed\n"); | ||
524 | |||
525 | ret = SAA_OK; | ||
526 | out: | ||
527 | return ret; | ||
528 | } | ||
529 | |||
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c new file mode 100644 index 000000000000..04957090f83e --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-core.c | |||
@@ -0,0 +1,746 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/moduleparam.h> | ||
26 | #include <linux/kmod.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <asm/div64.h> | ||
32 | |||
33 | #include "saa7164.h" | ||
34 | |||
35 | MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards"); | ||
36 | MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | /* | ||
40 | 1 Basic | ||
41 | 2 | ||
42 | 4 i2c | ||
43 | 8 api | ||
44 | 16 cmd | ||
45 | 32 bus | ||
46 | */ | ||
47 | |||
48 | unsigned int debug; | ||
49 | module_param(debug, int, 0644); | ||
50 | MODULE_PARM_DESC(debug, "enable debug messages"); | ||
51 | |||
52 | static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; | ||
53 | module_param_array(card, int, NULL, 0444); | ||
54 | MODULE_PARM_DESC(card, "card type"); | ||
55 | |||
56 | static unsigned int saa7164_devcount; | ||
57 | |||
58 | static DEFINE_MUTEX(devlist); | ||
59 | LIST_HEAD(saa7164_devlist); | ||
60 | |||
61 | #define INT_SIZE 16 | ||
62 | |||
63 | static void saa7164_work_cmdhandler(struct work_struct *w) | ||
64 | { | ||
65 | struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd); | ||
66 | |||
67 | /* Wake up any complete commands */ | ||
68 | saa7164_cmd_signal(dev, 0); | ||
69 | } | ||
70 | |||
71 | static void saa7164_buffer_deliver(struct saa7164_buffer *buf) | ||
72 | { | ||
73 | struct saa7164_tsport *port = buf->port; | ||
74 | |||
75 | /* Feed the transport payload into the kernel demux */ | ||
76 | dvb_dmx_swfilter_packets(&port->dvb.demux, buf->cpu, | ||
77 | SAA7164_TS_NUMBER_OF_LINES); | ||
78 | |||
79 | } | ||
80 | |||
81 | static irqreturn_t saa7164_irq_ts(struct saa7164_tsport *port) | ||
82 | { | ||
83 | struct saa7164_dev *dev = port->dev; | ||
84 | struct saa7164_buffer *buf; | ||
85 | struct list_head *c, *n; | ||
86 | int wp, i = 0, rp; | ||
87 | |||
88 | /* Find the current write point from the hardware */ | ||
89 | wp = saa7164_readl(port->bufcounter); | ||
90 | if (wp > (port->hwcfg.buffercount - 1)) | ||
91 | BUG(); | ||
92 | |||
93 | /* Find the previous buffer to the current write point */ | ||
94 | if (wp == 0) | ||
95 | rp = 7; | ||
96 | else | ||
97 | rp = wp - 1; | ||
98 | |||
99 | /* Lookup the WP in the buffer list */ | ||
100 | /* TODO: turn this into a worker thread */ | ||
101 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
102 | buf = list_entry(c, struct saa7164_buffer, list); | ||
103 | if (i++ > port->hwcfg.buffercount) | ||
104 | BUG(); | ||
105 | |||
106 | if (buf->nr == rp) { | ||
107 | /* Found the buffer, deal with it */ | ||
108 | dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n", | ||
109 | __func__, wp, rp); | ||
110 | saa7164_buffer_deliver(buf); | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | } | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | /* Primary IRQ handler and dispatch mechanism */ | ||
119 | static irqreturn_t saa7164_irq(int irq, void *dev_id) | ||
120 | { | ||
121 | struct saa7164_dev *dev = dev_id; | ||
122 | u32 hwacc = 0, interruptid; | ||
123 | u32 intstat[INT_SIZE/4]; | ||
124 | int i, handled = 0, bit; | ||
125 | |||
126 | /* Check that the hardware is accessable. If the status bytes are | ||
127 | * 0xFF then the device is not accessable, the the IRQ belongs | ||
128 | * to another driver. | ||
129 | */ | ||
130 | for (i = 0; i < INT_SIZE/4; i++) { | ||
131 | |||
132 | /* TODO: Convert into saa7164_readl() */ | ||
133 | /* Read the 4 hardware interrupt registers */ | ||
134 | intstat[i] = *(dev->InterruptStatus + i); | ||
135 | |||
136 | if (intstat[i] != 0xffffffff) | ||
137 | hwacc = 1; | ||
138 | } | ||
139 | if (hwacc == 0) { | ||
140 | handled = 0; | ||
141 | goto out; | ||
142 | } | ||
143 | |||
144 | handled = 1; | ||
145 | |||
146 | /* For each of the HW interrupt registers */ | ||
147 | for (i = 0; i < INT_SIZE/4; i++) { | ||
148 | |||
149 | if (intstat[i]) { | ||
150 | /* Each function of the board has it's own interruptid. | ||
151 | * Find the function that triggered then call | ||
152 | * it's handler. | ||
153 | */ | ||
154 | for (bit = 0; bit < 32; bit++) { | ||
155 | |||
156 | if (((intstat[i] >> bit) & 0x00000001) == 0) | ||
157 | continue; | ||
158 | |||
159 | /* Calculate the interrupt id (0x00 to 0x7f) */ | ||
160 | |||
161 | interruptid = (i * 32) + bit; | ||
162 | if (interruptid == dev->intfdesc.bInterruptId) { | ||
163 | /* A response to an cmd/api call */ | ||
164 | schedule_work(&dev->workcmd); | ||
165 | } else if (interruptid == | ||
166 | dev->ts1.hwcfg.interruptid) { | ||
167 | |||
168 | /* Transport path 1 */ | ||
169 | saa7164_irq_ts(&dev->ts1); | ||
170 | |||
171 | } else if (interruptid == | ||
172 | dev->ts2.hwcfg.interruptid) { | ||
173 | |||
174 | /* Transport path 2 */ | ||
175 | saa7164_irq_ts(&dev->ts2); | ||
176 | |||
177 | } else { | ||
178 | /* Find the function */ | ||
179 | dprintk(DBGLVL_IRQ, | ||
180 | "%s() unhandled interrupt " | ||
181 | "reg 0x%x bit 0x%x " | ||
182 | "intid = 0x%x\n", | ||
183 | __func__, i, bit, interruptid); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* TODO: Convert into saa7164_writel() */ | ||
188 | /* Ack it */ | ||
189 | *(dev->InterruptAck + i) = intstat[i]; | ||
190 | |||
191 | } | ||
192 | } | ||
193 | out: | ||
194 | return IRQ_RETVAL(handled); | ||
195 | } | ||
196 | |||
197 | void saa7164_getfirmwarestatus(struct saa7164_dev *dev) | ||
198 | { | ||
199 | struct saa7164_fw_status *s = &dev->fw_status; | ||
200 | |||
201 | dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS); | ||
202 | dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE); | ||
203 | dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC); | ||
204 | dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST); | ||
205 | dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD); | ||
206 | dev->fw_status.remainheap = | ||
207 | saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP); | ||
208 | |||
209 | dprintk(1, "Firmware status:\n"); | ||
210 | dprintk(1, " .status = 0x%08x\n", s->status); | ||
211 | dprintk(1, " .mode = 0x%08x\n", s->mode); | ||
212 | dprintk(1, " .spec = 0x%08x\n", s->spec); | ||
213 | dprintk(1, " .inst = 0x%08x\n", s->inst); | ||
214 | dprintk(1, " .cpuload = 0x%08x\n", s->cpuload); | ||
215 | dprintk(1, " .remainheap = 0x%08x\n", s->remainheap); | ||
216 | } | ||
217 | |||
218 | u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev) | ||
219 | { | ||
220 | u32 reg; | ||
221 | |||
222 | reg = saa7164_readl(SAA_DEVICE_VERSION); | ||
223 | dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n", | ||
224 | (reg & 0x0000fc00) >> 10, | ||
225 | (reg & 0x000003e0) >> 5, | ||
226 | (reg & 0x0000001f), | ||
227 | (reg & 0xffff0000) >> 16, | ||
228 | reg); | ||
229 | |||
230 | return reg; | ||
231 | } | ||
232 | |||
233 | /* TODO: Debugging func, remove */ | ||
234 | void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len) | ||
235 | { | ||
236 | int i; | ||
237 | |||
238 | printk(KERN_INFO "--------------------> " | ||
239 | "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
240 | |||
241 | for (i = 0; i < len; i += 16) | ||
242 | printk(KERN_INFO " [0x%08x] " | ||
243 | "%02x %02x %02x %02x %02x %02x %02x %02x " | ||
244 | "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, | ||
245 | *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3), | ||
246 | *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7), | ||
247 | *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11), | ||
248 | *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15)); | ||
249 | } | ||
250 | |||
251 | /* TODO: Debugging func, remove */ | ||
252 | void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr) | ||
253 | { | ||
254 | int i; | ||
255 | |||
256 | dprintk(1, "--------------------> " | ||
257 | "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
258 | |||
259 | for (i = 0; i < 0x100; i += 16) | ||
260 | dprintk(1, "region0[0x%08x] = " | ||
261 | "%02x %02x %02x %02x %02x %02x %02x %02x" | ||
262 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", i, | ||
263 | (u8)saa7164_readb(addr + i + 0), | ||
264 | (u8)saa7164_readb(addr + i + 1), | ||
265 | (u8)saa7164_readb(addr + i + 2), | ||
266 | (u8)saa7164_readb(addr + i + 3), | ||
267 | (u8)saa7164_readb(addr + i + 4), | ||
268 | (u8)saa7164_readb(addr + i + 5), | ||
269 | (u8)saa7164_readb(addr + i + 6), | ||
270 | (u8)saa7164_readb(addr + i + 7), | ||
271 | (u8)saa7164_readb(addr + i + 8), | ||
272 | (u8)saa7164_readb(addr + i + 9), | ||
273 | (u8)saa7164_readb(addr + i + 10), | ||
274 | (u8)saa7164_readb(addr + i + 11), | ||
275 | (u8)saa7164_readb(addr + i + 12), | ||
276 | (u8)saa7164_readb(addr + i + 13), | ||
277 | (u8)saa7164_readb(addr + i + 14), | ||
278 | (u8)saa7164_readb(addr + i + 15) | ||
279 | ); | ||
280 | } | ||
281 | |||
282 | static void saa7164_dump_hwdesc(struct saa7164_dev *dev) | ||
283 | { | ||
284 | dprintk(1, "@0x%p hwdesc sizeof(tmComResHWDescr_t) = %lu bytes\n", | ||
285 | &dev->hwdesc, sizeof(tmComResHWDescr_t)); | ||
286 | |||
287 | dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength); | ||
288 | dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType); | ||
289 | dprintk(1, " .bDescriptorSubtype = 0x%x\n", | ||
290 | dev->hwdesc.bDescriptorSubtype); | ||
291 | |||
292 | dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion); | ||
293 | dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency); | ||
294 | dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes); | ||
295 | dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities); | ||
296 | dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n", | ||
297 | dev->hwdesc.dwDeviceRegistersLocation); | ||
298 | |||
299 | dprintk(1, " .dwHostMemoryRegion = 0x%x\n", | ||
300 | dev->hwdesc.dwHostMemoryRegion); | ||
301 | |||
302 | dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n", | ||
303 | dev->hwdesc.dwHostMemoryRegionSize); | ||
304 | |||
305 | dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n", | ||
306 | dev->hwdesc.dwHostHibernatMemRegion); | ||
307 | |||
308 | dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n", | ||
309 | dev->hwdesc.dwHostHibernatMemRegionSize); | ||
310 | } | ||
311 | |||
312 | static void saa7164_dump_intfdesc(struct saa7164_dev *dev) | ||
313 | { | ||
314 | dprintk(1, "@0x%p intfdesc " | ||
315 | "sizeof(tmComResInterfaceDescr_t) = %lu bytes\n", | ||
316 | &dev->intfdesc, sizeof(tmComResInterfaceDescr_t)); | ||
317 | |||
318 | dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength); | ||
319 | dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType); | ||
320 | dprintk(1, " .bDescriptorSubtype = 0x%x\n", | ||
321 | dev->intfdesc.bDescriptorSubtype); | ||
322 | |||
323 | dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags); | ||
324 | dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType); | ||
325 | dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId); | ||
326 | dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface); | ||
327 | dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId); | ||
328 | dprintk(1, " .bDebugInterruptId = 0x%x\n", | ||
329 | dev->intfdesc.bDebugInterruptId); | ||
330 | |||
331 | dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation); | ||
332 | } | ||
333 | |||
334 | static void saa7164_dump_busdesc(struct saa7164_dev *dev) | ||
335 | { | ||
336 | dprintk(1, "@0x%p busdesc sizeof(tmComResBusDescr_t) = %lu bytes\n", | ||
337 | &dev->busdesc, sizeof(tmComResBusDescr_t)); | ||
338 | |||
339 | dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing); | ||
340 | dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing); | ||
341 | dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite); | ||
342 | dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead); | ||
343 | dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite); | ||
344 | dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead); | ||
345 | } | ||
346 | |||
347 | /* Much of the hardware configuration and PCI registers are configured | ||
348 | * dynamically depending on firmware. We have to cache some initial | ||
349 | * structures then use these to locate other important structures | ||
350 | * from PCI space. | ||
351 | */ | ||
352 | static void saa7164_get_descriptors(struct saa7164_dev *dev) | ||
353 | { | ||
354 | memcpy(&dev->hwdesc, dev->bmmio, sizeof(tmComResHWDescr_t)); | ||
355 | memcpy(&dev->intfdesc, dev->bmmio + sizeof(tmComResHWDescr_t), | ||
356 | sizeof(tmComResInterfaceDescr_t)); | ||
357 | memcpy(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation, | ||
358 | sizeof(tmComResBusDescr_t)); | ||
359 | |||
360 | if (dev->hwdesc.bLength != sizeof(tmComResHWDescr_t)) { | ||
361 | printk(KERN_ERR "Structure tmComResHWDescr_t is mangled\n"); | ||
362 | printk(KERN_ERR "Need %x got %lu\n", dev->hwdesc.bLength, | ||
363 | sizeof(tmComResHWDescr_t)); | ||
364 | } else | ||
365 | saa7164_dump_hwdesc(dev); | ||
366 | |||
367 | if (dev->intfdesc.bLength != sizeof(tmComResInterfaceDescr_t)) { | ||
368 | printk(KERN_ERR "struct tmComResInterfaceDescr_t is mangled\n"); | ||
369 | printk(KERN_ERR "Need %x got %lu\n", dev->intfdesc.bLength, | ||
370 | sizeof(tmComResInterfaceDescr_t)); | ||
371 | } else | ||
372 | saa7164_dump_intfdesc(dev); | ||
373 | |||
374 | saa7164_dump_busdesc(dev); | ||
375 | } | ||
376 | |||
377 | static int saa7164_pci_quirks(struct saa7164_dev *dev) | ||
378 | { | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static int get_resources(struct saa7164_dev *dev) | ||
383 | { | ||
384 | if (request_mem_region(pci_resource_start(dev->pci, 0), | ||
385 | pci_resource_len(dev->pci, 0), dev->name)) { | ||
386 | |||
387 | if (request_mem_region(pci_resource_start(dev->pci, 2), | ||
388 | pci_resource_len(dev->pci, 2), dev->name)) | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", | ||
393 | dev->name, | ||
394 | (u64)pci_resource_start(dev->pci, 0), | ||
395 | (u64)pci_resource_start(dev->pci, 2)); | ||
396 | |||
397 | return -EBUSY; | ||
398 | } | ||
399 | |||
400 | static int saa7164_dev_setup(struct saa7164_dev *dev) | ||
401 | { | ||
402 | int i; | ||
403 | |||
404 | mutex_init(&dev->lock); | ||
405 | atomic_inc(&dev->refcount); | ||
406 | dev->nr = saa7164_devcount++; | ||
407 | |||
408 | sprintf(dev->name, "saa7164[%d]", dev->nr); | ||
409 | |||
410 | mutex_lock(&devlist); | ||
411 | list_add_tail(&dev->devlist, &saa7164_devlist); | ||
412 | mutex_unlock(&devlist); | ||
413 | |||
414 | /* board config */ | ||
415 | dev->board = UNSET; | ||
416 | if (card[dev->nr] < saa7164_bcount) | ||
417 | dev->board = card[dev->nr]; | ||
418 | |||
419 | for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++) | ||
420 | if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor && | ||
421 | dev->pci->subsystem_device == | ||
422 | saa7164_subids[i].subdevice) | ||
423 | dev->board = saa7164_subids[i].card; | ||
424 | |||
425 | if (UNSET == dev->board) { | ||
426 | dev->board = SAA7164_BOARD_UNKNOWN; | ||
427 | saa7164_card_list(dev); | ||
428 | } | ||
429 | |||
430 | dev->pci_bus = dev->pci->bus->number; | ||
431 | dev->pci_slot = PCI_SLOT(dev->pci->devfn); | ||
432 | |||
433 | /* I2C Defaults / setup */ | ||
434 | dev->i2c_bus[0].dev = dev; | ||
435 | dev->i2c_bus[0].nr = 0; | ||
436 | dev->i2c_bus[1].dev = dev; | ||
437 | dev->i2c_bus[1].nr = 1; | ||
438 | dev->i2c_bus[2].dev = dev; | ||
439 | dev->i2c_bus[2].nr = 2; | ||
440 | |||
441 | /* Transport port A Defaults / setup */ | ||
442 | dev->ts1.dev = dev; | ||
443 | dev->ts1.nr = 0; | ||
444 | mutex_init(&dev->ts1.dvb.lock); | ||
445 | INIT_LIST_HEAD(&dev->ts1.dmaqueue.list); | ||
446 | INIT_LIST_HEAD(&dev->ts1.dummy_dmaqueue.list); | ||
447 | mutex_init(&dev->ts1.dmaqueue_lock); | ||
448 | mutex_init(&dev->ts1.dummy_dmaqueue_lock); | ||
449 | |||
450 | /* Transport port B Defaults / setup */ | ||
451 | dev->ts2.dev = dev; | ||
452 | dev->ts2.nr = 1; | ||
453 | mutex_init(&dev->ts2.dvb.lock); | ||
454 | INIT_LIST_HEAD(&dev->ts2.dmaqueue.list); | ||
455 | INIT_LIST_HEAD(&dev->ts2.dummy_dmaqueue.list); | ||
456 | mutex_init(&dev->ts2.dmaqueue_lock); | ||
457 | mutex_init(&dev->ts2.dummy_dmaqueue_lock); | ||
458 | |||
459 | if (get_resources(dev) < 0) { | ||
460 | printk(KERN_ERR "CORE %s No more PCIe resources for " | ||
461 | "subsystem: %04x:%04x\n", | ||
462 | dev->name, dev->pci->subsystem_vendor, | ||
463 | dev->pci->subsystem_device); | ||
464 | |||
465 | saa7164_devcount--; | ||
466 | return -ENODEV; | ||
467 | } | ||
468 | |||
469 | /* PCI/e allocations */ | ||
470 | dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), | ||
471 | pci_resource_len(dev->pci, 0)); | ||
472 | |||
473 | dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2), | ||
474 | pci_resource_len(dev->pci, 2)); | ||
475 | |||
476 | printk(KERN_INFO "CORE %s: dev->lmmio = 0x%p\n", dev->name, | ||
477 | dev->lmmio); | ||
478 | |||
479 | printk(KERN_INFO "CORE %s: dev->lmmio2 = 0x%p\n", dev->name, | ||
480 | dev->lmmio2); | ||
481 | |||
482 | dev->bmmio = (u8 __iomem *)dev->lmmio; | ||
483 | dev->bmmio2 = (u8 __iomem *)dev->lmmio2; | ||
484 | printk(KERN_INFO "CORE %s: dev->bmmio = 0x%p\n", dev->name, | ||
485 | dev->bmmio); | ||
486 | |||
487 | printk(KERN_INFO "CORE %s: dev->bmmio2 = 0x%p\n", dev->name, | ||
488 | dev->bmmio2); | ||
489 | |||
490 | /* TODO: Magic defines used in the windows driver, define these */ | ||
491 | dev->InterruptStatus = (u32 *)(dev->bmmio + 0x183000 + 0xf80); | ||
492 | dev->InterruptAck = (u32 *)(dev->bmmio + 0x183000 + 0xf90); | ||
493 | |||
494 | printk(KERN_INFO | ||
495 | "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", | ||
496 | dev->name, dev->pci->subsystem_vendor, | ||
497 | dev->pci->subsystem_device, saa7164_boards[dev->board].name, | ||
498 | dev->board, card[dev->nr] == dev->board ? | ||
499 | "insmod option" : "autodetected"); | ||
500 | |||
501 | saa7164_pci_quirks(dev); | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static void saa7164_dev_unregister(struct saa7164_dev *dev) | ||
507 | { | ||
508 | dprintk(1, "%s()\n", __func__); | ||
509 | |||
510 | release_mem_region(pci_resource_start(dev->pci, 0), | ||
511 | pci_resource_len(dev->pci, 0)); | ||
512 | |||
513 | release_mem_region(pci_resource_start(dev->pci, 2), | ||
514 | pci_resource_len(dev->pci, 2)); | ||
515 | |||
516 | if (!atomic_dec_and_test(&dev->refcount)) | ||
517 | return; | ||
518 | |||
519 | iounmap(dev->lmmio); | ||
520 | iounmap(dev->lmmio2); | ||
521 | |||
522 | return; | ||
523 | } | ||
524 | |||
525 | static int __devinit saa7164_initdev(struct pci_dev *pci_dev, | ||
526 | const struct pci_device_id *pci_id) | ||
527 | { | ||
528 | struct saa7164_dev *dev; | ||
529 | int err, i; | ||
530 | u32 version; | ||
531 | |||
532 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
533 | if (NULL == dev) | ||
534 | return -ENOMEM; | ||
535 | |||
536 | /* pci init */ | ||
537 | dev->pci = pci_dev; | ||
538 | if (pci_enable_device(pci_dev)) { | ||
539 | err = -EIO; | ||
540 | goto fail_free; | ||
541 | } | ||
542 | |||
543 | if (saa7164_dev_setup(dev) < 0) { | ||
544 | err = -EINVAL; | ||
545 | goto fail_free; | ||
546 | } | ||
547 | |||
548 | /* print pci info */ | ||
549 | pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); | ||
550 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
551 | printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " | ||
552 | "latency: %d, mmio: 0x%llx\n", dev->name, | ||
553 | pci_name(pci_dev), dev->pci_rev, pci_dev->irq, | ||
554 | dev->pci_lat, | ||
555 | (unsigned long long)pci_resource_start(pci_dev, 0)); | ||
556 | |||
557 | pci_set_master(pci_dev); | ||
558 | /* TODO */ | ||
559 | if (!pci_dma_supported(pci_dev, 0xffffffff)) { | ||
560 | printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); | ||
561 | err = -EIO; | ||
562 | goto fail_irq; | ||
563 | } | ||
564 | |||
565 | err = request_irq(pci_dev->irq, saa7164_irq, | ||
566 | IRQF_SHARED | IRQF_DISABLED, dev->name, dev); | ||
567 | if (err < 0) { | ||
568 | printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, | ||
569 | pci_dev->irq); | ||
570 | err = -EIO; | ||
571 | goto fail_irq; | ||
572 | } | ||
573 | |||
574 | pci_set_drvdata(pci_dev, dev); | ||
575 | |||
576 | saa7164_pci_quirks(dev); | ||
577 | |||
578 | /* Init the internal command list */ | ||
579 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
580 | dev->cmds[i].seqno = i; | ||
581 | dev->cmds[i].inuse = 0; | ||
582 | mutex_init(&dev->cmds[i].lock); | ||
583 | init_waitqueue_head(&dev->cmds[i].wait); | ||
584 | } | ||
585 | |||
586 | /* We need a deferred interrupt handler for cmd handling */ | ||
587 | INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler); | ||
588 | |||
589 | /* Only load the firmware if we know the board */ | ||
590 | if (dev->board != SAA7164_BOARD_UNKNOWN) { | ||
591 | |||
592 | err = saa7164_downloadfirmware(dev); | ||
593 | if (err < 0) { | ||
594 | printk(KERN_ERR | ||
595 | "Failed to boot firmware, cannot continue\n"); | ||
596 | goto fail_irq; | ||
597 | } | ||
598 | |||
599 | saa7164_get_descriptors(dev); | ||
600 | saa7164_dumpregs(dev, 0); | ||
601 | saa7164_getcurrentfirmwareversion(dev); | ||
602 | saa7164_getfirmwarestatus(dev); | ||
603 | err = saa7164_bus_setup(dev); | ||
604 | if (err < 0) | ||
605 | printk(KERN_ERR | ||
606 | "Failed to setup the bus, will continue\n"); | ||
607 | saa7164_bus_dump(dev); | ||
608 | |||
609 | /* Ping the running firmware via the command bus and get the | ||
610 | * firmware version, this checks the bus is running OK. | ||
611 | */ | ||
612 | version = 0; | ||
613 | if (saa7164_api_get_fw_version(dev, &version) == SAA_OK) | ||
614 | dprintk(1, "Bus is operating correctly using " | ||
615 | "version %d.%d.%d.%d (0x%x)\n", | ||
616 | (version & 0x0000fc00) >> 10, | ||
617 | (version & 0x000003e0) >> 5, | ||
618 | (version & 0x0000001f), | ||
619 | (version & 0xffff0000) >> 16, | ||
620 | version); | ||
621 | else | ||
622 | printk(KERN_ERR | ||
623 | "Failed to communicate with the firmware\n"); | ||
624 | |||
625 | /* Bring up the I2C buses */ | ||
626 | saa7164_i2c_register(&dev->i2c_bus[0]); | ||
627 | saa7164_i2c_register(&dev->i2c_bus[1]); | ||
628 | saa7164_i2c_register(&dev->i2c_bus[2]); | ||
629 | saa7164_gpio_setup(dev); | ||
630 | saa7164_card_setup(dev); | ||
631 | |||
632 | |||
633 | /* Parse the dynamic device configuration, find various | ||
634 | * media endpoints (MPEG, WMV, PS, TS) and cache their | ||
635 | * configuration details into the driver, so we can | ||
636 | * reference them later during simething_register() func, | ||
637 | * interrupt handlers, deferred work handlers etc. | ||
638 | */ | ||
639 | saa7164_api_enum_subdevs(dev); | ||
640 | |||
641 | /* Try a few API commands - just for exercise purposes */ | ||
642 | saa7164_api_test(dev); | ||
643 | |||
644 | /* Begin to create the video sub-systems and register funcs */ | ||
645 | if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) { | ||
646 | if (saa7164_dvb_register(&dev->ts1) < 0) { | ||
647 | printk(KERN_ERR "%s() Failed to register " | ||
648 | "dvb adapters on porta\n", | ||
649 | __func__); | ||
650 | } | ||
651 | } | ||
652 | |||
653 | if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) { | ||
654 | if (saa7164_dvb_register(&dev->ts2) < 0) { | ||
655 | printk(KERN_ERR"%s() Failed to register " | ||
656 | "dvb adapters on portb\n", | ||
657 | __func__); | ||
658 | } | ||
659 | } | ||
660 | |||
661 | } /* != BOARD_UNKNOWN */ | ||
662 | else | ||
663 | printk(KERN_ERR "%s() Unsupported board detected, " | ||
664 | "registering without firmware\n", __func__); | ||
665 | |||
666 | return 0; | ||
667 | |||
668 | fail_irq: | ||
669 | saa7164_dev_unregister(dev); | ||
670 | fail_free: | ||
671 | kfree(dev); | ||
672 | return err; | ||
673 | } | ||
674 | |||
675 | static void saa7164_shutdown(struct saa7164_dev *dev) | ||
676 | { | ||
677 | dprintk(1, "%s()\n", __func__); | ||
678 | } | ||
679 | |||
680 | static void __devexit saa7164_finidev(struct pci_dev *pci_dev) | ||
681 | { | ||
682 | struct saa7164_dev *dev = pci_get_drvdata(pci_dev); | ||
683 | |||
684 | saa7164_shutdown(dev); | ||
685 | |||
686 | if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) | ||
687 | saa7164_dvb_unregister(&dev->ts1); | ||
688 | |||
689 | if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) | ||
690 | saa7164_dvb_unregister(&dev->ts2); | ||
691 | |||
692 | saa7164_i2c_unregister(&dev->i2c_bus[0]); | ||
693 | saa7164_i2c_unregister(&dev->i2c_bus[1]); | ||
694 | saa7164_i2c_unregister(&dev->i2c_bus[2]); | ||
695 | |||
696 | pci_disable_device(pci_dev); | ||
697 | |||
698 | /* unregister stuff */ | ||
699 | free_irq(pci_dev->irq, dev); | ||
700 | pci_set_drvdata(pci_dev, NULL); | ||
701 | |||
702 | mutex_lock(&devlist); | ||
703 | list_del(&dev->devlist); | ||
704 | mutex_unlock(&devlist); | ||
705 | |||
706 | saa7164_dev_unregister(dev); | ||
707 | kfree(dev); | ||
708 | } | ||
709 | |||
710 | static struct pci_device_id saa7164_pci_tbl[] = { | ||
711 | { | ||
712 | /* SAA7164 */ | ||
713 | .vendor = 0x1131, | ||
714 | .device = 0x7164, | ||
715 | .subvendor = PCI_ANY_ID, | ||
716 | .subdevice = PCI_ANY_ID, | ||
717 | }, { | ||
718 | /* --- end of list --- */ | ||
719 | } | ||
720 | }; | ||
721 | MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl); | ||
722 | |||
723 | static struct pci_driver saa7164_pci_driver = { | ||
724 | .name = "saa7164", | ||
725 | .id_table = saa7164_pci_tbl, | ||
726 | .probe = saa7164_initdev, | ||
727 | .remove = __devexit_p(saa7164_finidev), | ||
728 | /* TODO */ | ||
729 | .suspend = NULL, | ||
730 | .resume = NULL, | ||
731 | }; | ||
732 | |||
733 | static int saa7164_init(void) | ||
734 | { | ||
735 | printk(KERN_INFO "saa7164 driver loaded\n"); | ||
736 | return pci_register_driver(&saa7164_pci_driver); | ||
737 | } | ||
738 | |||
739 | static void saa7164_fini(void) | ||
740 | { | ||
741 | pci_unregister_driver(&saa7164_pci_driver); | ||
742 | } | ||
743 | |||
744 | module_init(saa7164_init); | ||
745 | module_exit(saa7164_fini); | ||
746 | |||
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c new file mode 100644 index 000000000000..f21520f5979e --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-dvb.c | |||
@@ -0,0 +1,578 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include "saa7164.h" | ||
23 | |||
24 | #include "tda10048.h" | ||
25 | #include "tda18271.h" | ||
26 | #include "s5h1411.h" | ||
27 | |||
28 | #define DRIVER_NAME "saa7164" | ||
29 | |||
30 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
31 | |||
32 | /* addr is in the card struct, get it from there */ | ||
33 | static struct tda10048_config hauppauge_hvr2200_1_config = { | ||
34 | .demod_address = 0x10 >> 1, | ||
35 | .output_mode = TDA10048_SERIAL_OUTPUT, | ||
36 | .fwbulkwritelen = TDA10048_BULKWRITE_200, | ||
37 | .inversion = TDA10048_INVERSION_ON | ||
38 | }; | ||
39 | static struct tda10048_config hauppauge_hvr2200_2_config = { | ||
40 | .demod_address = 0x12 >> 1, | ||
41 | .output_mode = TDA10048_SERIAL_OUTPUT, | ||
42 | .fwbulkwritelen = TDA10048_BULKWRITE_200, | ||
43 | .inversion = TDA10048_INVERSION_ON | ||
44 | }; | ||
45 | |||
46 | static struct tda18271_std_map hauppauge_tda18271_std_map = { | ||
47 | .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, | ||
48 | .if_lvl = 6, .rfagc_top = 0x37 }, | ||
49 | .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, | ||
50 | .if_lvl = 6, .rfagc_top = 0x37 }, | ||
51 | }; | ||
52 | |||
53 | static struct tda18271_config hauppauge_hvr22x0_tuner_config = { | ||
54 | .std_map = &hauppauge_tda18271_std_map, | ||
55 | .gate = TDA18271_GATE_ANALOG, | ||
56 | }; | ||
57 | |||
58 | static struct s5h1411_config hauppauge_s5h1411_config = { | ||
59 | .output_mode = S5H1411_SERIAL_OUTPUT, | ||
60 | .gpio = S5H1411_GPIO_ON, | ||
61 | .qam_if = S5H1411_IF_4000, | ||
62 | .vsb_if = S5H1411_IF_3250, | ||
63 | .inversion = S5H1411_INVERSION_ON, | ||
64 | .status_mode = S5H1411_DEMODLOCKING, | ||
65 | .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
66 | }; | ||
67 | |||
68 | static int saa7164_dvb_stop_tsport(struct saa7164_tsport *port) | ||
69 | { | ||
70 | struct saa7164_dev *dev = port->dev; | ||
71 | int ret; | ||
72 | |||
73 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
74 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
75 | printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", | ||
76 | __func__, ret); | ||
77 | ret = -EIO; | ||
78 | } else { | ||
79 | dprintk(DBGLVL_DVB, "%s() Stopped\n", __func__); | ||
80 | ret = 0; | ||
81 | } | ||
82 | |||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | static int saa7164_dvb_acquire_tsport(struct saa7164_tsport *port) | ||
87 | { | ||
88 | struct saa7164_dev *dev = port->dev; | ||
89 | int ret; | ||
90 | |||
91 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
92 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
93 | printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", | ||
94 | __func__, ret); | ||
95 | ret = -EIO; | ||
96 | } else { | ||
97 | dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); | ||
98 | ret = 0; | ||
99 | } | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int saa7164_dvb_pause_tsport(struct saa7164_tsport *port) | ||
105 | { | ||
106 | struct saa7164_dev *dev = port->dev; | ||
107 | int ret; | ||
108 | |||
109 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
110 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
111 | printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", | ||
112 | __func__, ret); | ||
113 | ret = -EIO; | ||
114 | } else { | ||
115 | dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); | ||
116 | ret = 0; | ||
117 | } | ||
118 | |||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | /* Firmware is very windows centric, meaning you have to transition | ||
123 | * the part through AVStream / KS Windows stages, forwards or backwards. | ||
124 | * States are: stopped, acquired (h/w), paused, started. | ||
125 | */ | ||
126 | static int saa7164_dvb_stop_streaming(struct saa7164_tsport *port) | ||
127 | { | ||
128 | struct saa7164_dev *dev = port->dev; | ||
129 | int ret; | ||
130 | |||
131 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
132 | |||
133 | ret = saa7164_dvb_pause_tsport(port); | ||
134 | ret = saa7164_dvb_acquire_tsport(port); | ||
135 | ret = saa7164_dvb_stop_tsport(port); | ||
136 | |||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | static int saa7164_dvb_cfg_tsport(struct saa7164_tsport *port) | ||
141 | { | ||
142 | tmHWStreamParameters_t *params = &port->hw_streamingparams; | ||
143 | struct saa7164_dev *dev = port->dev; | ||
144 | struct saa7164_buffer *buf; | ||
145 | struct list_head *c, *n; | ||
146 | int i = 0; | ||
147 | |||
148 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
149 | |||
150 | saa7164_writel(port->pitch, params->pitch); | ||
151 | saa7164_writel(port->bufsize, params->pitch * params->numberoflines); | ||
152 | |||
153 | dprintk(DBGLVL_DVB, " configured:\n"); | ||
154 | dprintk(DBGLVL_DVB, " lmmio 0x%llx\n", (u64)dev->lmmio); | ||
155 | dprintk(DBGLVL_DVB, " bufcounter 0x%x = 0x%x\n", port->bufcounter, | ||
156 | saa7164_readl(port->bufcounter)); | ||
157 | |||
158 | dprintk(DBGLVL_DVB, " pitch 0x%x = %d\n", port->pitch, | ||
159 | saa7164_readl(port->pitch)); | ||
160 | |||
161 | dprintk(DBGLVL_DVB, " bufsize 0x%x = %d\n", port->bufsize, | ||
162 | saa7164_readl(port->bufsize)); | ||
163 | |||
164 | dprintk(DBGLVL_DVB, " buffercount = %d\n", port->hwcfg.buffercount); | ||
165 | dprintk(DBGLVL_DVB, " bufoffset = 0x%x\n", port->bufoffset); | ||
166 | dprintk(DBGLVL_DVB, " bufptr32h = 0x%x\n", port->bufptr32h); | ||
167 | dprintk(DBGLVL_DVB, " bufptr32l = 0x%x\n", port->bufptr32l); | ||
168 | |||
169 | /* Poke the buffers and offsets into PCI space */ | ||
170 | mutex_lock(&port->dmaqueue_lock); | ||
171 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
172 | buf = list_entry(c, struct saa7164_buffer, list); | ||
173 | |||
174 | /* TODO: Review this in light of 32v64 assignments */ | ||
175 | saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); | ||
176 | saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), | ||
177 | buf->pt_dma); | ||
178 | saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0); | ||
179 | |||
180 | dprintk(DBGLVL_DVB, | ||
181 | " buf[%d] offset 0x%lx (0x%x) " | ||
182 | "buf 0x%lx/%lx (0x%x/%x)\n", | ||
183 | i, | ||
184 | port->bufoffset + (i * sizeof(u32)), | ||
185 | saa7164_readl(port->bufoffset + (sizeof(u32) * i)), | ||
186 | port->bufptr32h + ((sizeof(u32) * 2) * i), | ||
187 | port->bufptr32l + ((sizeof(u32) * 2) * i), | ||
188 | saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) | ||
189 | * 2)), | ||
190 | saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) | ||
191 | * 2))); | ||
192 | |||
193 | if (i++ > port->hwcfg.buffercount) | ||
194 | BUG(); | ||
195 | |||
196 | } | ||
197 | mutex_unlock(&port->dmaqueue_lock); | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int saa7164_dvb_start_tsport(struct saa7164_tsport *port) | ||
203 | { | ||
204 | struct saa7164_dev *dev = port->dev; | ||
205 | int ret = 0, result; | ||
206 | |||
207 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
208 | |||
209 | saa7164_dvb_cfg_tsport(port); | ||
210 | |||
211 | /* Acquire the hardware */ | ||
212 | result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
213 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
214 | printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", | ||
215 | __func__, result); | ||
216 | |||
217 | /* Stop the hardware, regardless */ | ||
218 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
219 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
220 | printk(KERN_ERR "%s() acquire/forced stop transition " | ||
221 | "failed, res = 0x%x\n", __func__, result); | ||
222 | } | ||
223 | ret = -EIO; | ||
224 | goto out; | ||
225 | } else | ||
226 | dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); | ||
227 | |||
228 | /* Pause the hardware */ | ||
229 | result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
230 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
231 | printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", | ||
232 | __func__, result); | ||
233 | |||
234 | /* Stop the hardware, regardless */ | ||
235 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
236 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
237 | printk(KERN_ERR "%s() pause/forced stop transition " | ||
238 | "failed, res = 0x%x\n", __func__, result); | ||
239 | } | ||
240 | |||
241 | ret = -EIO; | ||
242 | goto out; | ||
243 | } else | ||
244 | dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); | ||
245 | |||
246 | /* Start the hardware */ | ||
247 | result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); | ||
248 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
249 | printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", | ||
250 | __func__, result); | ||
251 | |||
252 | /* Stop the hardware, regardless */ | ||
253 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
254 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
255 | printk(KERN_ERR "%s() run/forced stop transition " | ||
256 | "failed, res = 0x%x\n", __func__, result); | ||
257 | } | ||
258 | |||
259 | ret = -EIO; | ||
260 | } else | ||
261 | dprintk(DBGLVL_DVB, "%s() Running\n", __func__); | ||
262 | |||
263 | out: | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) | ||
268 | { | ||
269 | struct dvb_demux *demux = feed->demux; | ||
270 | struct saa7164_tsport *port = (struct saa7164_tsport *) demux->priv; | ||
271 | struct saa7164_dvb *dvb = &port->dvb; | ||
272 | struct saa7164_dev *dev = port->dev; | ||
273 | int ret = 0; | ||
274 | |||
275 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
276 | |||
277 | if (!demux->dmx.frontend) | ||
278 | return -EINVAL; | ||
279 | |||
280 | if (dvb) { | ||
281 | mutex_lock(&dvb->lock); | ||
282 | if (dvb->feeding++ == 0) { | ||
283 | /* Start transport */ | ||
284 | ret = saa7164_dvb_start_tsport(port); | ||
285 | } | ||
286 | mutex_unlock(&dvb->lock); | ||
287 | dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", | ||
288 | __func__, port->nr, dvb->feeding); | ||
289 | } | ||
290 | |||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) | ||
295 | { | ||
296 | struct dvb_demux *demux = feed->demux; | ||
297 | struct saa7164_tsport *port = (struct saa7164_tsport *) demux->priv; | ||
298 | struct saa7164_dvb *dvb = &port->dvb; | ||
299 | struct saa7164_dev *dev = port->dev; | ||
300 | int ret = 0; | ||
301 | |||
302 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
303 | |||
304 | if (dvb) { | ||
305 | mutex_lock(&dvb->lock); | ||
306 | if (--dvb->feeding == 0) { | ||
307 | /* Stop transport */ | ||
308 | ret = saa7164_dvb_stop_streaming(port); | ||
309 | } | ||
310 | mutex_unlock(&dvb->lock); | ||
311 | dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", | ||
312 | __func__, port->nr, dvb->feeding); | ||
313 | } | ||
314 | |||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | static int dvb_register(struct saa7164_tsport *port) | ||
319 | { | ||
320 | struct saa7164_dvb *dvb = &port->dvb; | ||
321 | struct saa7164_dev *dev = port->dev; | ||
322 | struct saa7164_buffer *buf; | ||
323 | int result, i; | ||
324 | |||
325 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
326 | |||
327 | /* Sanity check that the PCI configuration space is active */ | ||
328 | if (port->hwcfg.BARLocation == 0) { | ||
329 | result = -ENOMEM; | ||
330 | printk(KERN_ERR "%s: dvb_register_adapter failed " | ||
331 | "(errno = %d), NO PCI configuration\n", | ||
332 | DRIVER_NAME, result); | ||
333 | goto fail_adapter; | ||
334 | } | ||
335 | |||
336 | /* Init and establish defaults */ | ||
337 | port->hw_streamingparams.bitspersample = 8; | ||
338 | port->hw_streamingparams.samplesperline = 188; | ||
339 | port->hw_streamingparams.numberoflines = | ||
340 | (SAA7164_TS_NUMBER_OF_LINES * 188) / 188; | ||
341 | |||
342 | port->hw_streamingparams.pitch = 188; | ||
343 | port->hw_streamingparams.linethreshold = 0; | ||
344 | port->hw_streamingparams.pagetablelistvirt = 0; | ||
345 | port->hw_streamingparams.pagetablelistphys = 0; | ||
346 | port->hw_streamingparams.numpagetables = 2 + | ||
347 | ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); | ||
348 | |||
349 | port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount; | ||
350 | |||
351 | /* Allocate the PCI resources */ | ||
352 | for (i = 0; i < port->hwcfg.buffercount; i++) { | ||
353 | buf = saa7164_buffer_alloc(port, | ||
354 | port->hw_streamingparams.numberoflines * | ||
355 | port->hw_streamingparams.pitch); | ||
356 | |||
357 | if (!buf) { | ||
358 | result = -ENOMEM; | ||
359 | printk(KERN_ERR "%s: dvb_register_adapter failed " | ||
360 | "(errno = %d), unable to allocate buffers\n", | ||
361 | DRIVER_NAME, result); | ||
362 | goto fail_adapter; | ||
363 | } | ||
364 | buf->nr = i; | ||
365 | |||
366 | mutex_lock(&port->dmaqueue_lock); | ||
367 | list_add_tail(&buf->list, &port->dmaqueue.list); | ||
368 | mutex_unlock(&port->dmaqueue_lock); | ||
369 | } | ||
370 | |||
371 | /* register adapter */ | ||
372 | result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, | ||
373 | &dev->pci->dev, adapter_nr); | ||
374 | if (result < 0) { | ||
375 | printk(KERN_ERR "%s: dvb_register_adapter failed " | ||
376 | "(errno = %d)\n", DRIVER_NAME, result); | ||
377 | goto fail_adapter; | ||
378 | } | ||
379 | dvb->adapter.priv = port; | ||
380 | |||
381 | /* register frontend */ | ||
382 | result = dvb_register_frontend(&dvb->adapter, dvb->frontend); | ||
383 | if (result < 0) { | ||
384 | printk(KERN_ERR "%s: dvb_register_frontend failed " | ||
385 | "(errno = %d)\n", DRIVER_NAME, result); | ||
386 | goto fail_frontend; | ||
387 | } | ||
388 | |||
389 | /* register demux stuff */ | ||
390 | dvb->demux.dmx.capabilities = | ||
391 | DMX_TS_FILTERING | DMX_SECTION_FILTERING | | ||
392 | DMX_MEMORY_BASED_FILTERING; | ||
393 | dvb->demux.priv = port; | ||
394 | dvb->demux.filternum = 256; | ||
395 | dvb->demux.feednum = 256; | ||
396 | dvb->demux.start_feed = saa7164_dvb_start_feed; | ||
397 | dvb->demux.stop_feed = saa7164_dvb_stop_feed; | ||
398 | result = dvb_dmx_init(&dvb->demux); | ||
399 | if (result < 0) { | ||
400 | printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", | ||
401 | DRIVER_NAME, result); | ||
402 | goto fail_dmx; | ||
403 | } | ||
404 | |||
405 | dvb->dmxdev.filternum = 256; | ||
406 | dvb->dmxdev.demux = &dvb->demux.dmx; | ||
407 | dvb->dmxdev.capabilities = 0; | ||
408 | result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); | ||
409 | if (result < 0) { | ||
410 | printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", | ||
411 | DRIVER_NAME, result); | ||
412 | goto fail_dmxdev; | ||
413 | } | ||
414 | |||
415 | dvb->fe_hw.source = DMX_FRONTEND_0; | ||
416 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
417 | if (result < 0) { | ||
418 | printk(KERN_ERR "%s: add_frontend failed " | ||
419 | "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); | ||
420 | goto fail_fe_hw; | ||
421 | } | ||
422 | |||
423 | dvb->fe_mem.source = DMX_MEMORY_FE; | ||
424 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); | ||
425 | if (result < 0) { | ||
426 | printk(KERN_ERR "%s: add_frontend failed " | ||
427 | "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); | ||
428 | goto fail_fe_mem; | ||
429 | } | ||
430 | |||
431 | result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
432 | if (result < 0) { | ||
433 | printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", | ||
434 | DRIVER_NAME, result); | ||
435 | goto fail_fe_conn; | ||
436 | } | ||
437 | |||
438 | /* register network adapter */ | ||
439 | dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); | ||
440 | return 0; | ||
441 | |||
442 | fail_fe_conn: | ||
443 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); | ||
444 | fail_fe_mem: | ||
445 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
446 | fail_fe_hw: | ||
447 | dvb_dmxdev_release(&dvb->dmxdev); | ||
448 | fail_dmxdev: | ||
449 | dvb_dmx_release(&dvb->demux); | ||
450 | fail_dmx: | ||
451 | dvb_unregister_frontend(dvb->frontend); | ||
452 | fail_frontend: | ||
453 | dvb_frontend_detach(dvb->frontend); | ||
454 | dvb_unregister_adapter(&dvb->adapter); | ||
455 | fail_adapter: | ||
456 | return result; | ||
457 | } | ||
458 | |||
459 | int saa7164_dvb_unregister(struct saa7164_tsport *port) | ||
460 | { | ||
461 | struct saa7164_dvb *dvb = &port->dvb; | ||
462 | struct saa7164_dev *dev = port->dev; | ||
463 | struct saa7164_buffer *b; | ||
464 | struct list_head *c, *n; | ||
465 | |||
466 | dprintk(DBGLVL_DVB, "%s()\n", __func__); | ||
467 | |||
468 | /* Remove any allocated buffers */ | ||
469 | mutex_lock(&port->dmaqueue_lock); | ||
470 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
471 | b = list_entry(c, struct saa7164_buffer, list); | ||
472 | list_del(c); | ||
473 | saa7164_buffer_dealloc(port, b); | ||
474 | } | ||
475 | mutex_unlock(&port->dmaqueue_lock); | ||
476 | |||
477 | if (dvb->frontend == NULL) | ||
478 | return 0; | ||
479 | |||
480 | dvb_net_release(&dvb->net); | ||
481 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); | ||
482 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
483 | dvb_dmxdev_release(&dvb->dmxdev); | ||
484 | dvb_dmx_release(&dvb->demux); | ||
485 | dvb_unregister_frontend(dvb->frontend); | ||
486 | dvb_frontend_detach(dvb->frontend); | ||
487 | dvb_unregister_adapter(&dvb->adapter); | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | /* All the DVB attach calls go here, this function get's modified | ||
492 | * for each new card. | ||
493 | */ | ||
494 | int saa7164_dvb_register(struct saa7164_tsport *port) | ||
495 | { | ||
496 | struct saa7164_dev *dev = port->dev; | ||
497 | struct saa7164_dvb *dvb = &port->dvb; | ||
498 | struct saa7164_i2c *i2c_bus = NULL; | ||
499 | int ret; | ||
500 | |||
501 | dprintk(DBGLVL_DVB, "%s()\n", __func__); | ||
502 | |||
503 | /* init frontend */ | ||
504 | switch (dev->board) { | ||
505 | case SAA7164_BOARD_HAUPPAUGE_HVR2200: | ||
506 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: | ||
507 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: | ||
508 | switch (port->nr) { | ||
509 | case 0: | ||
510 | i2c_bus = &dev->i2c_bus[1]; | ||
511 | |||
512 | port->dvb.frontend = dvb_attach(tda10048_attach, | ||
513 | &hauppauge_hvr2200_1_config, | ||
514 | &i2c_bus->i2c_adap); | ||
515 | |||
516 | if (port->dvb.frontend != NULL) { | ||
517 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
518 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
519 | &hauppauge_hvr22x0_tuner_config); | ||
520 | } | ||
521 | |||
522 | break; | ||
523 | case 1: | ||
524 | i2c_bus = &dev->i2c_bus[2]; | ||
525 | |||
526 | port->dvb.frontend = dvb_attach(tda10048_attach, | ||
527 | &hauppauge_hvr2200_2_config, | ||
528 | &i2c_bus->i2c_adap); | ||
529 | |||
530 | if (port->dvb.frontend != NULL) { | ||
531 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
532 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
533 | &hauppauge_hvr22x0_tuner_config); | ||
534 | } | ||
535 | |||
536 | break; | ||
537 | } | ||
538 | break; | ||
539 | case SAA7164_BOARD_HAUPPAUGE_HVR2250: | ||
540 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: | ||
541 | i2c_bus = &dev->i2c_bus[port->nr + 1]; | ||
542 | |||
543 | port->dvb.frontend = dvb_attach(s5h1411_attach, | ||
544 | &hauppauge_s5h1411_config, | ||
545 | &i2c_bus->i2c_adap); | ||
546 | |||
547 | if (port->dvb.frontend != NULL) { | ||
548 | /* TODO: addr is in the card struct */ | ||
549 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
550 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
551 | &hauppauge_hvr22x0_tuner_config); | ||
552 | } | ||
553 | |||
554 | break; | ||
555 | default: | ||
556 | printk(KERN_ERR "%s: The frontend isn't supported\n", | ||
557 | dev->name); | ||
558 | break; | ||
559 | } | ||
560 | if (NULL == dvb->frontend) { | ||
561 | printk(KERN_ERR "%s() Frontend initialization failed\n", | ||
562 | __func__); | ||
563 | return -1; | ||
564 | } | ||
565 | |||
566 | /* Put the analog decoder in standby to keep it quiet */ | ||
567 | |||
568 | /* register everything */ | ||
569 | ret = dvb_register(port); | ||
570 | if (ret < 0) { | ||
571 | if (dvb->frontend->ops.release) | ||
572 | dvb->frontend->ops.release(dvb->frontend); | ||
573 | return ret; | ||
574 | } | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c new file mode 100644 index 000000000000..6595dd67c5c3 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-fw.c | |||
@@ -0,0 +1,615 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/firmware.h> | ||
23 | |||
24 | #include "saa7164.h" | ||
25 | |||
26 | #define SAA7164_REV2_FIRMWARE "v4l-saa7164-1.0.2.fw" | ||
27 | #define SAA7164_REV2_FIRMWARE_SIZE 3978608 | ||
28 | |||
29 | #define SAA7164_REV3_FIRMWARE "v4l-saa7164-1.0.3.fw" | ||
30 | #define SAA7164_REV3_FIRMWARE_SIZE 3978608 | ||
31 | |||
32 | struct fw_header { | ||
33 | u32 firmwaresize; | ||
34 | u32 bslsize; | ||
35 | u32 reserved; | ||
36 | u32 version; | ||
37 | }; | ||
38 | |||
39 | int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg) | ||
40 | { | ||
41 | u32 timeout = SAA_DEVICE_TIMEOUT; | ||
42 | while ((saa7164_readl(reg) & 0x01) == 0) { | ||
43 | timeout -= 5; | ||
44 | if (timeout == 0) { | ||
45 | printk(KERN_ERR "%s() timeout (no d/l ack)\n", | ||
46 | __func__); | ||
47 | return -EBUSY; | ||
48 | } | ||
49 | /* TODO: Review this for efficiency, f/w load is slow */ | ||
50 | msleep(1); | ||
51 | } | ||
52 | |||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg) | ||
57 | { | ||
58 | u32 timeout = SAA_DEVICE_TIMEOUT; | ||
59 | while (saa7164_readl(reg) & 0x01) { | ||
60 | timeout -= 5; | ||
61 | if (timeout == 0) { | ||
62 | printk(KERN_ERR "%s() timeout (no d/l clr)\n", | ||
63 | __func__); | ||
64 | return -EBUSY; | ||
65 | } | ||
66 | /* TODO: Review this for efficiency, f/w load is slow */ | ||
67 | msleep(1); | ||
68 | } | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /* TODO: move dlflags into dev-> and change to write/readl/b */ | ||
74 | /* TODO: Excessive levels of debug */ | ||
75 | int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize, | ||
76 | u32 dlflags, u8 *dst, u32 dstsize) | ||
77 | { | ||
78 | u32 reg, timeout, offset; | ||
79 | u8 *srcbuf = NULL; | ||
80 | int ret; | ||
81 | |||
82 | u32 dlflag = dlflags; | ||
83 | u32 dlflag_ack = dlflag + 4; | ||
84 | u32 drflag = dlflag_ack + 4; | ||
85 | u32 drflag_ack = drflag + 4; | ||
86 | u32 bleflag = drflag_ack + 4; | ||
87 | |||
88 | dprintk(DBGLVL_FW, | ||
89 | "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n", | ||
90 | __func__, src, srcsize, dlflags, dst, dstsize); | ||
91 | |||
92 | if ((src == 0) || (dst == 0)) { | ||
93 | ret = -EIO; | ||
94 | goto out; | ||
95 | } | ||
96 | |||
97 | srcbuf = kzalloc(4 * 1048576, GFP_KERNEL); | ||
98 | if (NULL == srcbuf) { | ||
99 | ret = -ENOMEM; | ||
100 | goto out; | ||
101 | } | ||
102 | |||
103 | if (srcsize > (4*1048576)) { | ||
104 | ret = -ENOMEM; | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | memcpy(srcbuf, src, srcsize); | ||
109 | |||
110 | dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag); | ||
111 | dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack); | ||
112 | dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag); | ||
113 | dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack); | ||
114 | dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag); | ||
115 | |||
116 | reg = saa7164_readl(dlflag); | ||
117 | dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg); | ||
118 | if (reg == 1) | ||
119 | dprintk(DBGLVL_FW, | ||
120 | "%s() Download flag already set, please reboot\n", | ||
121 | __func__); | ||
122 | |||
123 | /* Indicate download start */ | ||
124 | saa7164_writel(dlflag, 1); | ||
125 | ret = saa7164_dl_wait_ack(dev, dlflag_ack); | ||
126 | if (ret < 0) | ||
127 | goto out; | ||
128 | |||
129 | /* Ack download start, then wait for wait */ | ||
130 | saa7164_writel(dlflag, 0); | ||
131 | ret = saa7164_dl_wait_clr(dev, dlflag_ack); | ||
132 | if (ret < 0) | ||
133 | goto out; | ||
134 | |||
135 | /* Deal with the raw firmware, in the appropriate chunk size */ | ||
136 | for (offset = 0; srcsize > dstsize; | ||
137 | srcsize -= dstsize, offset += dstsize) { | ||
138 | |||
139 | dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize); | ||
140 | memcpy(dst, srcbuf + offset, dstsize); | ||
141 | |||
142 | /* Flag the data as ready */ | ||
143 | saa7164_writel(drflag, 1); | ||
144 | ret = saa7164_dl_wait_ack(dev, drflag_ack); | ||
145 | if (ret < 0) | ||
146 | goto out; | ||
147 | |||
148 | /* Wait for indication data was received */ | ||
149 | saa7164_writel(drflag, 0); | ||
150 | ret = saa7164_dl_wait_clr(dev, drflag_ack); | ||
151 | if (ret < 0) | ||
152 | goto out; | ||
153 | |||
154 | } | ||
155 | |||
156 | dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize); | ||
157 | /* Write last block to the device */ | ||
158 | memcpy(dst, srcbuf+offset, srcsize); | ||
159 | |||
160 | /* Flag the data as ready */ | ||
161 | saa7164_writel(drflag, 1); | ||
162 | ret = saa7164_dl_wait_ack(dev, drflag_ack); | ||
163 | if (ret < 0) | ||
164 | goto out; | ||
165 | |||
166 | saa7164_writel(drflag, 0); | ||
167 | timeout = 0; | ||
168 | while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) { | ||
169 | if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) { | ||
170 | printk(KERN_ERR "%s() image corrupt\n", __func__); | ||
171 | ret = -EBUSY; | ||
172 | goto out; | ||
173 | } | ||
174 | |||
175 | if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) { | ||
176 | printk(KERN_ERR "%s() device memory corrupt\n", | ||
177 | __func__); | ||
178 | ret = -EBUSY; | ||
179 | goto out; | ||
180 | } | ||
181 | |||
182 | msleep(10); | ||
183 | if (timeout++ > 60) | ||
184 | break; | ||
185 | } | ||
186 | |||
187 | printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__); | ||
188 | |||
189 | ret = saa7164_dl_wait_clr(dev, drflag_ack); | ||
190 | if (ret < 0) | ||
191 | goto out; | ||
192 | |||
193 | printk(KERN_INFO "%s() Image booted successfully.\n", __func__); | ||
194 | ret = 0; | ||
195 | |||
196 | out: | ||
197 | kfree(srcbuf); | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | /* TODO: Excessive debug */ | ||
202 | /* Load the firmware. Optionally it can be in ROM or newer versions | ||
203 | * can be on disk, saving the expense of the ROM hardware. */ | ||
204 | int saa7164_downloadfirmware(struct saa7164_dev *dev) | ||
205 | { | ||
206 | /* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */ | ||
207 | u32 tmp, filesize, version, err_flags, first_timeout, fwlength; | ||
208 | u32 second_timeout, updatebootloader = 1, bootloadersize = 0; | ||
209 | const struct firmware *fw = NULL; | ||
210 | struct fw_header *hdr, *boothdr = NULL, *fwhdr; | ||
211 | u32 bootloaderversion = 0, fwloadersize; | ||
212 | u8 *bootloaderoffset = NULL, *fwloaderoffset; | ||
213 | char *fwname; | ||
214 | int ret; | ||
215 | |||
216 | dprintk(DBGLVL_FW, "%s()\n", __func__); | ||
217 | |||
218 | if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) { | ||
219 | fwname = SAA7164_REV2_FIRMWARE; | ||
220 | fwlength = SAA7164_REV2_FIRMWARE_SIZE; | ||
221 | } else { | ||
222 | fwname = SAA7164_REV3_FIRMWARE; | ||
223 | fwlength = SAA7164_REV3_FIRMWARE_SIZE; | ||
224 | } | ||
225 | |||
226 | version = saa7164_getcurrentfirmwareversion(dev); | ||
227 | |||
228 | if (version == 0x00) { | ||
229 | |||
230 | second_timeout = 100; | ||
231 | first_timeout = 100; | ||
232 | err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); | ||
233 | dprintk(DBGLVL_FW, "%s() err_flags = %x\n", | ||
234 | __func__, err_flags); | ||
235 | |||
236 | while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { | ||
237 | dprintk(DBGLVL_FW, "%s() err_flags = %x\n", | ||
238 | __func__, err_flags); | ||
239 | msleep(10); | ||
240 | |||
241 | if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { | ||
242 | printk(KERN_ERR "%s() firmware corrupt\n", | ||
243 | __func__); | ||
244 | break; | ||
245 | } | ||
246 | if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { | ||
247 | printk(KERN_ERR "%s() device memory corrupt\n", | ||
248 | __func__); | ||
249 | break; | ||
250 | } | ||
251 | if (err_flags & SAA_DEVICE_NO_IMAGE) { | ||
252 | printk(KERN_ERR "%s() no first image\n", | ||
253 | __func__); | ||
254 | break; | ||
255 | } | ||
256 | if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { | ||
257 | first_timeout -= 10; | ||
258 | if (first_timeout == 0) { | ||
259 | printk(KERN_ERR | ||
260 | "%s() no first image\n", | ||
261 | __func__); | ||
262 | break; | ||
263 | } | ||
264 | } else if (err_flags & SAA_DEVICE_IMAGE_LOADING) { | ||
265 | second_timeout -= 10; | ||
266 | if (second_timeout == 0) { | ||
267 | printk(KERN_ERR | ||
268 | "%s() FW load time exceeded\n", | ||
269 | __func__); | ||
270 | break; | ||
271 | } | ||
272 | } else { | ||
273 | second_timeout -= 10; | ||
274 | if (second_timeout == 0) { | ||
275 | printk(KERN_ERR | ||
276 | "%s() Unknown bootloader flags 0x%x\n", | ||
277 | __func__, err_flags); | ||
278 | break; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); | ||
283 | } /* While != Booting */ | ||
284 | |||
285 | if (err_flags == SAA_DEVICE_IMAGE_BOOTING) { | ||
286 | dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n", | ||
287 | __func__); | ||
288 | first_timeout = SAA_DEVICE_TIMEOUT; | ||
289 | second_timeout = 60 * SAA_DEVICE_TIMEOUT; | ||
290 | second_timeout = 100; | ||
291 | |||
292 | err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); | ||
293 | dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", | ||
294 | __func__, err_flags); | ||
295 | while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { | ||
296 | dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", | ||
297 | __func__, err_flags); | ||
298 | msleep(10); | ||
299 | |||
300 | if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { | ||
301 | printk(KERN_ERR | ||
302 | "%s() firmware corrupt\n", | ||
303 | __func__); | ||
304 | break; | ||
305 | } | ||
306 | if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { | ||
307 | printk(KERN_ERR | ||
308 | "%s() device memory corrupt\n", | ||
309 | __func__); | ||
310 | break; | ||
311 | } | ||
312 | if (err_flags & SAA_DEVICE_NO_IMAGE) { | ||
313 | printk(KERN_ERR "%s() no first image\n", | ||
314 | __func__); | ||
315 | break; | ||
316 | } | ||
317 | if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { | ||
318 | first_timeout -= 10; | ||
319 | if (first_timeout == 0) { | ||
320 | printk(KERN_ERR | ||
321 | "%s() no second image\n", | ||
322 | __func__); | ||
323 | break; | ||
324 | } | ||
325 | } else if (err_flags & | ||
326 | SAA_DEVICE_IMAGE_LOADING) { | ||
327 | second_timeout -= 10; | ||
328 | if (second_timeout == 0) { | ||
329 | printk(KERN_ERR | ||
330 | "%s() FW load time exceeded\n", | ||
331 | __func__); | ||
332 | break; | ||
333 | } | ||
334 | } else { | ||
335 | second_timeout -= 10; | ||
336 | if (second_timeout == 0) { | ||
337 | printk(KERN_ERR | ||
338 | "%s() Unknown bootloader flags 0x%x\n", | ||
339 | __func__, err_flags); | ||
340 | break; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | err_flags = | ||
345 | saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); | ||
346 | } /* err_flags != SAA_DEVICE_IMAGE_BOOTING */ | ||
347 | |||
348 | dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n", | ||
349 | __func__, | ||
350 | saa7164_readl(SAA_BOOTLOADERERROR_FLAGS), | ||
351 | saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS)); | ||
352 | |||
353 | } /* err_flags == SAA_DEVICE_IMAGE_BOOTING */ | ||
354 | |||
355 | /* It's possible for both firmwares to have booted, | ||
356 | * but that doesn't mean they've finished booting yet. | ||
357 | */ | ||
358 | if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == | ||
359 | SAA_DEVICE_IMAGE_BOOTING) && | ||
360 | (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == | ||
361 | SAA_DEVICE_IMAGE_BOOTING)) { | ||
362 | |||
363 | |||
364 | dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n", | ||
365 | __func__); | ||
366 | |||
367 | first_timeout = SAA_DEVICE_TIMEOUT; | ||
368 | while (first_timeout) { | ||
369 | msleep(10); | ||
370 | |||
371 | version = | ||
372 | saa7164_getcurrentfirmwareversion(dev); | ||
373 | if (version) { | ||
374 | dprintk(DBGLVL_FW, | ||
375 | "%s() All f/w loaded successfully\n", | ||
376 | __func__); | ||
377 | break; | ||
378 | } else { | ||
379 | first_timeout -= 10; | ||
380 | if (first_timeout == 0) { | ||
381 | printk(KERN_ERR | ||
382 | "%s() FW did not boot\n", | ||
383 | __func__); | ||
384 | break; | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | version = saa7164_getcurrentfirmwareversion(dev); | ||
390 | } /* version == 0 */ | ||
391 | |||
392 | /* Has the firmware really booted? */ | ||
393 | if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == | ||
394 | SAA_DEVICE_IMAGE_BOOTING) && | ||
395 | (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == | ||
396 | SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) { | ||
397 | |||
398 | printk(KERN_ERR | ||
399 | "%s() The firmware hung, probably bad firmware\n", | ||
400 | __func__); | ||
401 | |||
402 | /* Tell the second stage loader we have a deadlock */ | ||
403 | saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET, | ||
404 | SAA_DEVICE_DEADLOCK_DETECTED); | ||
405 | |||
406 | saa7164_getfirmwarestatus(dev); | ||
407 | |||
408 | return -ENOMEM; | ||
409 | } | ||
410 | |||
411 | dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n", | ||
412 | (version & 0x0000fc00) >> 10, | ||
413 | (version & 0x000003e0) >> 5, | ||
414 | (version & 0x0000001f), | ||
415 | (version & 0xffff0000) >> 16); | ||
416 | |||
417 | /* Load the firmwware from the disk if required */ | ||
418 | if (version == 0) { | ||
419 | |||
420 | printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n", | ||
421 | __func__, fwname); | ||
422 | |||
423 | ret = request_firmware(&fw, fwname, &dev->pci->dev); | ||
424 | if (ret) { | ||
425 | printk(KERN_ERR "%s() Upload failed. " | ||
426 | "(file not found?)\n", __func__); | ||
427 | return -ENOMEM; | ||
428 | } | ||
429 | |||
430 | printk(KERN_INFO "%s() firmware read %Zu bytes.\n", | ||
431 | __func__, fw->size); | ||
432 | |||
433 | if (fw->size != fwlength) { | ||
434 | printk(KERN_ERR "xc5000: firmware incorrect size\n"); | ||
435 | ret = -ENOMEM; | ||
436 | goto out; | ||
437 | } | ||
438 | |||
439 | printk(KERN_INFO "%s() firmware loaded.\n", __func__); | ||
440 | |||
441 | hdr = (struct fw_header *)fw->data; | ||
442 | printk(KERN_INFO "Firmware file header part 1:\n"); | ||
443 | printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize); | ||
444 | printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize); | ||
445 | printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved); | ||
446 | printk(KERN_INFO " .Version = 0x%x\n", hdr->version); | ||
447 | |||
448 | /* Retreive bootloader if reqd */ | ||
449 | if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) | ||
450 | /* Second bootloader in the firmware file */ | ||
451 | filesize = hdr->reserved * 16; | ||
452 | else | ||
453 | filesize = (hdr->firmwaresize + hdr->bslsize) * | ||
454 | 16 + sizeof(struct fw_header); | ||
455 | |||
456 | printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n", | ||
457 | __func__, filesize); | ||
458 | |||
459 | /* Get bootloader (if reqd) and firmware header */ | ||
460 | if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { | ||
461 | /* Second boot loader is required */ | ||
462 | |||
463 | /* Get the loader header */ | ||
464 | boothdr = (struct fw_header *)(fw->data + | ||
465 | sizeof(struct fw_header)); | ||
466 | |||
467 | bootloaderversion = | ||
468 | saa7164_readl(SAA_DEVICE_2ND_VERSION); | ||
469 | dprintk(DBGLVL_FW, "Onboard BootLoader:\n"); | ||
470 | dprintk(DBGLVL_FW, "->Flag 0x%x\n", | ||
471 | saa7164_readl(SAA_BOOTLOADERERROR_FLAGS)); | ||
472 | dprintk(DBGLVL_FW, "->Ack 0x%x\n", | ||
473 | saa7164_readl(SAA_DATAREADY_FLAG_ACK)); | ||
474 | dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version); | ||
475 | dprintk(DBGLVL_FW, "->Loader Version 0x%x\n", | ||
476 | bootloaderversion); | ||
477 | |||
478 | if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == | ||
479 | 0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK) | ||
480 | == 0x00) && (version == 0x00)) { | ||
481 | |||
482 | dprintk(DBGLVL_FW, "BootLoader version in " | ||
483 | "rom %d.%d.%d.%d\n", | ||
484 | (bootloaderversion & 0x0000fc00) >> 10, | ||
485 | (bootloaderversion & 0x000003e0) >> 5, | ||
486 | (bootloaderversion & 0x0000001f), | ||
487 | (bootloaderversion & 0xffff0000) >> 16 | ||
488 | ); | ||
489 | dprintk(DBGLVL_FW, "BootLoader version " | ||
490 | "in file %d.%d.%d.%d\n", | ||
491 | (boothdr->version & 0x0000fc00) >> 10, | ||
492 | (boothdr->version & 0x000003e0) >> 5, | ||
493 | (boothdr->version & 0x0000001f), | ||
494 | (boothdr->version & 0xffff0000) >> 16 | ||
495 | ); | ||
496 | |||
497 | if (bootloaderversion == boothdr->version) | ||
498 | updatebootloader = 0; | ||
499 | } | ||
500 | |||
501 | /* Calculate offset to firmware header */ | ||
502 | tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 + | ||
503 | (sizeof(struct fw_header) + | ||
504 | sizeof(struct fw_header)); | ||
505 | |||
506 | fwhdr = (struct fw_header *)(fw->data+tmp); | ||
507 | } else { | ||
508 | /* No second boot loader */ | ||
509 | fwhdr = hdr; | ||
510 | } | ||
511 | |||
512 | dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n", | ||
513 | (fwhdr->version & 0x0000fc00) >> 10, | ||
514 | (fwhdr->version & 0x000003e0) >> 5, | ||
515 | (fwhdr->version & 0x0000001f), | ||
516 | (fwhdr->version & 0xffff0000) >> 16 | ||
517 | ); | ||
518 | |||
519 | if (version == fwhdr->version) { | ||
520 | /* No download, firmware already on board */ | ||
521 | ret = 0; | ||
522 | goto out; | ||
523 | } | ||
524 | |||
525 | if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { | ||
526 | if (updatebootloader) { | ||
527 | /* Get ready to upload the bootloader */ | ||
528 | bootloadersize = (boothdr->firmwaresize + | ||
529 | boothdr->bslsize) * 16 + | ||
530 | sizeof(struct fw_header); | ||
531 | |||
532 | bootloaderoffset = (u8 *)(fw->data + | ||
533 | sizeof(struct fw_header)); | ||
534 | |||
535 | dprintk(DBGLVL_FW, "bootloader d/l starts.\n"); | ||
536 | printk(KERN_INFO "%s() FirmwareSize = 0x%x\n", | ||
537 | __func__, boothdr->firmwaresize); | ||
538 | printk(KERN_INFO "%s() BSLSize = 0x%x\n", | ||
539 | __func__, boothdr->bslsize); | ||
540 | printk(KERN_INFO "%s() Reserved = 0x%x\n", | ||
541 | __func__, boothdr->reserved); | ||
542 | printk(KERN_INFO "%s() Version = 0x%x\n", | ||
543 | __func__, boothdr->version); | ||
544 | ret = saa7164_downloadimage( | ||
545 | dev, | ||
546 | bootloaderoffset, | ||
547 | bootloadersize, | ||
548 | SAA_DOWNLOAD_FLAGS, | ||
549 | dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, | ||
550 | SAA_DEVICE_BUFFERBLOCKSIZE); | ||
551 | if (ret < 0) { | ||
552 | printk(KERN_ERR | ||
553 | "bootloader d/l has failed\n"); | ||
554 | goto out; | ||
555 | } | ||
556 | dprintk(DBGLVL_FW, | ||
557 | "bootloader download complete.\n"); | ||
558 | |||
559 | } | ||
560 | |||
561 | printk(KERN_ERR "starting firmware download(2)\n"); | ||
562 | bootloadersize = (boothdr->firmwaresize + | ||
563 | boothdr->bslsize) * 16 + | ||
564 | sizeof(struct fw_header); | ||
565 | |||
566 | bootloaderoffset = | ||
567 | (u8 *)(fw->data + sizeof(struct fw_header)); | ||
568 | |||
569 | fwloaderoffset = bootloaderoffset + bootloadersize; | ||
570 | |||
571 | /* TODO: fix this bounds overrun here with old f/ws */ | ||
572 | fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) * | ||
573 | 16 + sizeof(struct fw_header); | ||
574 | |||
575 | ret = saa7164_downloadimage( | ||
576 | dev, | ||
577 | fwloaderoffset, | ||
578 | fwloadersize, | ||
579 | SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET, | ||
580 | dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET, | ||
581 | SAA_DEVICE_2ND_BUFFERBLOCKSIZE); | ||
582 | if (ret < 0) { | ||
583 | printk(KERN_ERR "firmware download failed\n"); | ||
584 | goto out; | ||
585 | } | ||
586 | printk(KERN_ERR "firmware download complete.\n"); | ||
587 | |||
588 | } else { | ||
589 | |||
590 | /* No bootloader update reqd, download firmware only */ | ||
591 | printk(KERN_ERR "starting firmware download(3)\n"); | ||
592 | |||
593 | ret = saa7164_downloadimage( | ||
594 | dev, | ||
595 | (u8 *)fw->data, | ||
596 | fw->size, | ||
597 | SAA_DOWNLOAD_FLAGS, | ||
598 | dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, | ||
599 | SAA_DEVICE_BUFFERBLOCKSIZE); | ||
600 | if (ret < 0) { | ||
601 | printk(KERN_ERR "firmware download failed\n"); | ||
602 | goto out; | ||
603 | } | ||
604 | printk(KERN_ERR "firmware download complete.\n"); | ||
605 | } | ||
606 | } | ||
607 | |||
608 | ret = 0; | ||
609 | |||
610 | out: | ||
611 | if (fw) | ||
612 | release_firmware(fw); | ||
613 | |||
614 | return ret; | ||
615 | } | ||
diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/video/saa7164/saa7164-i2c.c new file mode 100644 index 000000000000..4c431b4ac8db --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-i2c.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <asm/io.h> | ||
27 | |||
28 | #include "saa7164.h" | ||
29 | |||
30 | static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) | ||
31 | { | ||
32 | struct saa7164_i2c *bus = i2c_adap->algo_data; | ||
33 | struct saa7164_dev *dev = bus->dev; | ||
34 | int i, retval = 0; | ||
35 | |||
36 | dprintk(DBGLVL_I2C, "%s(num = %d)\n", __func__, num); | ||
37 | |||
38 | for (i = 0 ; i < num; i++) { | ||
39 | dprintk(DBGLVL_I2C, "%s(num = %d) addr = 0x%02x len = 0x%x\n", | ||
40 | __func__, num, msgs[i].addr, msgs[i].len); | ||
41 | if (msgs[i].flags & I2C_M_RD) { | ||
42 | /* Unsupported - Yet*/ | ||
43 | printk(KERN_ERR "%s() Unsupported - Yet\n", __func__); | ||
44 | continue; | ||
45 | } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && | ||
46 | msgs[i].addr == msgs[i + 1].addr) { | ||
47 | /* write then read from same address */ | ||
48 | |||
49 | retval = saa7164_api_i2c_read(bus, msgs[i].addr, | ||
50 | msgs[i].len, msgs[i].buf, | ||
51 | msgs[i+1].len, msgs[i+1].buf | ||
52 | ); | ||
53 | |||
54 | i++; | ||
55 | |||
56 | if (retval < 0) | ||
57 | goto err; | ||
58 | } else { | ||
59 | /* write */ | ||
60 | retval = saa7164_api_i2c_write(bus, msgs[i].addr, | ||
61 | msgs[i].len, msgs[i].buf); | ||
62 | } | ||
63 | if (retval < 0) | ||
64 | goto err; | ||
65 | } | ||
66 | return num; | ||
67 | |||
68 | err: | ||
69 | return retval; | ||
70 | } | ||
71 | |||
72 | static int attach_inform(struct i2c_client *client) | ||
73 | { | ||
74 | struct saa7164_i2c *bus = i2c_get_adapdata(client->adapter); | ||
75 | struct saa7164_dev *dev = bus->dev; | ||
76 | |||
77 | dprintk(DBGLVL_I2C, "%s i2c attach [addr=0x%x,client=%s]\n", | ||
78 | client->driver->driver.name, client->addr, client->name); | ||
79 | |||
80 | if (!client->driver->command) | ||
81 | return 0; | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int detach_inform(struct i2c_client *client) | ||
87 | { | ||
88 | struct saa7164_dev *dev = i2c_get_adapdata(client->adapter); | ||
89 | |||
90 | dprintk(DBGLVL_I2C, "i2c detach [client=%s]\n", client->name); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd, | ||
96 | void *arg) | ||
97 | { | ||
98 | if (bus->i2c_rc != 0) | ||
99 | return; | ||
100 | |||
101 | i2c_clients_command(&bus->i2c_adap, cmd, arg); | ||
102 | } | ||
103 | |||
104 | static u32 saa7164_functionality(struct i2c_adapter *adap) | ||
105 | { | ||
106 | return I2C_FUNC_I2C; | ||
107 | } | ||
108 | |||
109 | static struct i2c_algorithm saa7164_i2c_algo_template = { | ||
110 | .master_xfer = i2c_xfer, | ||
111 | .functionality = saa7164_functionality, | ||
112 | }; | ||
113 | |||
114 | /* ----------------------------------------------------------------------- */ | ||
115 | |||
116 | static struct i2c_adapter saa7164_i2c_adap_template = { | ||
117 | .name = "saa7164", | ||
118 | .owner = THIS_MODULE, | ||
119 | .id = I2C_HW_B_SAA7164, | ||
120 | .algo = &saa7164_i2c_algo_template, | ||
121 | .client_register = attach_inform, | ||
122 | .client_unregister = detach_inform, | ||
123 | }; | ||
124 | |||
125 | static struct i2c_client saa7164_i2c_client_template = { | ||
126 | .name = "saa7164 internal", | ||
127 | }; | ||
128 | |||
129 | int saa7164_i2c_register(struct saa7164_i2c *bus) | ||
130 | { | ||
131 | struct saa7164_dev *dev = bus->dev; | ||
132 | |||
133 | dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr); | ||
134 | |||
135 | memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template, | ||
136 | sizeof(bus->i2c_adap)); | ||
137 | |||
138 | memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template, | ||
139 | sizeof(bus->i2c_algo)); | ||
140 | |||
141 | memcpy(&bus->i2c_client, &saa7164_i2c_client_template, | ||
142 | sizeof(bus->i2c_client)); | ||
143 | |||
144 | bus->i2c_adap.dev.parent = &dev->pci->dev; | ||
145 | |||
146 | strlcpy(bus->i2c_adap.name, bus->dev->name, | ||
147 | sizeof(bus->i2c_adap.name)); | ||
148 | |||
149 | bus->i2c_algo.data = bus; | ||
150 | bus->i2c_adap.algo_data = bus; | ||
151 | i2c_set_adapdata(&bus->i2c_adap, bus); | ||
152 | i2c_add_adapter(&bus->i2c_adap); | ||
153 | |||
154 | bus->i2c_client.adapter = &bus->i2c_adap; | ||
155 | |||
156 | if (0 == bus->i2c_rc) { | ||
157 | printk(KERN_ERR "%s: i2c bus %d registered\n", | ||
158 | dev->name, bus->nr); | ||
159 | } else | ||
160 | printk(KERN_ERR "%s: i2c bus %d register FAILED\n", | ||
161 | dev->name, bus->nr); | ||
162 | |||
163 | return bus->i2c_rc; | ||
164 | } | ||
165 | |||
166 | int saa7164_i2c_unregister(struct saa7164_i2c *bus) | ||
167 | { | ||
168 | i2c_del_adapter(&bus->i2c_adap); | ||
169 | return 0; | ||
170 | } | ||
diff --git a/drivers/media/video/saa7164/saa7164-reg.h b/drivers/media/video/saa7164/saa7164-reg.h new file mode 100644 index 000000000000..06be4c13d5b1 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-reg.h | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | /* TODO: Retest the driver with errors expressed as negatives */ | ||
23 | |||
24 | /* Result codes */ | ||
25 | #define SAA_OK 0 | ||
26 | #define SAA_ERR_BAD_PARAMETER 0x09 | ||
27 | #define SAA_ERR_NO_RESOURCES 0x0c | ||
28 | #define SAA_ERR_NOT_SUPPORTED 0x13 | ||
29 | #define SAA_ERR_BUSY 0x15 | ||
30 | #define SAA_ERR_READ 0x17 | ||
31 | #define SAA_ERR_TIMEOUT 0x1f | ||
32 | #define SAA_ERR_OVERFLOW 0x20 | ||
33 | #define SAA_ERR_EMPTY 0x22 | ||
34 | #define SAA_ERR_NOT_STARTED 0x23 | ||
35 | #define SAA_ERR_ALREADY_STARTED 0x24 | ||
36 | #define SAA_ERR_NOT_STOPPED 0x25 | ||
37 | #define SAA_ERR_ALREADY_STOPPED 0x26 | ||
38 | #define SAA_ERR_INVALID_COMMAND 0x3e | ||
39 | #define SAA_ERR_NULL_PACKET 0x59 | ||
40 | |||
41 | /* Errors and flags from the silicon */ | ||
42 | #define PVC_ERRORCODE_UNKNOWN 0x00 | ||
43 | #define PVC_ERRORCODE_INVALID_COMMAND 0x01 | ||
44 | #define PVC_ERRORCODE_INVALID_CONTROL 0x02 | ||
45 | #define PVC_ERRORCODE_INVALID_DATA 0x03 | ||
46 | #define PVC_ERRORCODE_TIMEOUT 0x04 | ||
47 | #define PVC_ERRORCODE_NAK 0x05 | ||
48 | #define PVC_RESPONSEFLAG_ERROR 0x01 | ||
49 | #define PVC_RESPONSEFLAG_OVERFLOW 0x02 | ||
50 | #define PVC_RESPONSEFLAG_RESET 0x04 | ||
51 | #define PVC_RESPONSEFLAG_INTERFACE 0x08 | ||
52 | #define PVC_RESPONSEFLAG_CONTINUED 0x10 | ||
53 | #define PVC_CMDFLAG_INTERRUPT 0x02 | ||
54 | #define PVC_CMDFLAG_INTERFACE 0x04 | ||
55 | #define PVC_CMDFLAG_SERIALIZE 0x08 | ||
56 | #define PVC_CMDFLAG_CONTINUE 0x10 | ||
57 | |||
58 | /* Silicon Commands */ | ||
59 | #define GET_DESCRIPTORS_CONTROL 0x01 | ||
60 | #define GET_STRING_CONTROL 0x03 | ||
61 | #define GET_LANGUAGE_CONTROL 0x05 | ||
62 | #define SET_POWER_CONTROL 0x07 | ||
63 | #define GET_FW_VERSION_CONTROL 0x09 | ||
64 | #define SET_DEBUG_LEVEL_CONTROL 0x0B | ||
65 | #define GET_DEBUG_DATA_CONTROL 0x0C | ||
66 | #define GET_PRODUCTION_INFO_CONTROL 0x0D | ||
67 | |||
68 | /* cmd defines */ | ||
69 | #define SAA_CMDFLAG_CONTINUE 0x10 | ||
70 | #define SAA_CMD_MAX_MSG_UNITS 256 | ||
71 | |||
72 | /* Some defines */ | ||
73 | #define SAA_BUS_TIMEOUT 50 | ||
74 | #define SAA_DEVICE_TIMEOUT 5000 | ||
75 | #define SAA_DEVICE_MAXREQUESTSIZE 256 | ||
76 | |||
77 | /* Register addresses */ | ||
78 | #define SAA_DEVICE_VERSION 0x30 | ||
79 | #define SAA_DOWNLOAD_FLAGS 0x34 | ||
80 | #define SAA_DOWNLOAD_FLAG 0x34 | ||
81 | #define SAA_DOWNLOAD_FLAG_ACK 0x38 | ||
82 | #define SAA_DATAREADY_FLAG 0x3C | ||
83 | #define SAA_DATAREADY_FLAG_ACK 0x40 | ||
84 | |||
85 | /* Boot loader register and bit definitions */ | ||
86 | #define SAA_BOOTLOADERERROR_FLAGS 0x44 | ||
87 | #define SAA_DEVICE_IMAGE_SEARCHING 0x01 | ||
88 | #define SAA_DEVICE_IMAGE_LOADING 0x02 | ||
89 | #define SAA_DEVICE_IMAGE_BOOTING 0x03 | ||
90 | #define SAA_DEVICE_IMAGE_CORRUPT 0x04 | ||
91 | #define SAA_DEVICE_MEMORY_CORRUPT 0x08 | ||
92 | #define SAA_DEVICE_NO_IMAGE 0x10 | ||
93 | |||
94 | /* Register addresses */ | ||
95 | #define SAA_DEVICE_2ND_VERSION 0x50 | ||
96 | #define SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET 0x54 | ||
97 | |||
98 | /* Register addresses */ | ||
99 | #define SAA_SECONDSTAGEERROR_FLAGS 0x64 | ||
100 | |||
101 | /* Bootloader regs and flags */ | ||
102 | #define SAA_DEVICE_DEADLOCK_DETECTED_OFFSET 0x6C | ||
103 | #define SAA_DEVICE_DEADLOCK_DETECTED 0xDEADDEAD | ||
104 | |||
105 | /* Basic firmware status registers */ | ||
106 | #define SAA_DEVICE_SYSINIT_STATUS_OFFSET 0x70 | ||
107 | #define SAA_DEVICE_SYSINIT_STATUS 0x70 | ||
108 | #define SAA_DEVICE_SYSINIT_MODE 0x74 | ||
109 | #define SAA_DEVICE_SYSINIT_SPEC 0x78 | ||
110 | #define SAA_DEVICE_SYSINIT_INST 0x7C | ||
111 | #define SAA_DEVICE_SYSINIT_CPULOAD 0x80 | ||
112 | #define SAA_DEVICE_SYSINIT_REMAINHEAP 0x84 | ||
113 | |||
114 | #define SAA_DEVICE_DOWNLOAD_OFFSET 0x1000 | ||
115 | #define SAA_DEVICE_BUFFERBLOCKSIZE 0x1000 | ||
116 | |||
117 | #define SAA_DEVICE_2ND_BUFFERBLOCKSIZE 0x100000 | ||
118 | #define SAA_DEVICE_2ND_DOWNLOAD_OFFSET 0x200000 | ||
119 | |||
120 | /* Descriptors */ | ||
121 | #define CS_INTERFACE 0x24 | ||
122 | |||
123 | /* Descriptor subtypes */ | ||
124 | #define VC_INPUT_TERMINAL 0x02 | ||
125 | #define VC_OUTPUT_TERMINAL 0x03 | ||
126 | #define VC_SELECTOR_UNIT 0x04 | ||
127 | #define VC_PROCESSING_UNIT 0x05 | ||
128 | #define FEATURE_UNIT 0x06 | ||
129 | #define TUNER_UNIT 0x09 | ||
130 | #define ENCODER_UNIT 0x0A | ||
131 | #define EXTENSION_UNIT 0x0B | ||
132 | #define VC_TUNER_PATH 0xF0 | ||
133 | #define PVC_HARDWARE_DESCRIPTOR 0xF1 | ||
134 | #define PVC_INTERFACE_DESCRIPTOR 0xF2 | ||
135 | #define PVC_INFRARED_UNIT 0xF3 | ||
136 | #define DRM_UNIT 0xF4 | ||
137 | #define GENERAL_REQUEST 0xF5 | ||
138 | |||
139 | /* Format Types */ | ||
140 | #define VS_FORMAT_TYPE 0x02 | ||
141 | #define VS_FORMAT_TYPE_I 0x01 | ||
142 | #define VS_FORMAT_UNCOMPRESSED 0x04 | ||
143 | #define VS_FRAME_UNCOMPRESSED 0x05 | ||
144 | #define VS_FORMAT_MPEG2PS 0x09 | ||
145 | #define VS_FORMAT_MPEG2TS 0x0A | ||
146 | #define VS_FORMAT_MPEG4SL 0x0B | ||
147 | #define VS_FORMAT_WM9 0x0C | ||
148 | #define VS_FORMAT_DIVX 0x0D | ||
149 | #define VS_FORMAT_VBI 0x0E | ||
150 | #define VS_FORMAT_RDS 0x0F | ||
151 | |||
152 | /* Device extension commands */ | ||
153 | #define EXU_REGISTER_ACCESS_CONTROL 0x00 | ||
154 | #define EXU_GPIO_CONTROL 0x01 | ||
155 | #define EXU_GPIO_GROUP_CONTROL 0x02 | ||
156 | #define EXU_INTERRUPT_CONTROL 0x03 | ||
157 | |||
158 | /* State Transition and args */ | ||
159 | #define SAA_STATE_CONTROL 0x03 | ||
160 | #define SAA_DMASTATE_STOP 0x00 | ||
161 | #define SAA_DMASTATE_ACQUIRE 0x01 | ||
162 | #define SAA_DMASTATE_PAUSE 0x02 | ||
163 | #define SAA_DMASTATE_RUN 0x03 | ||
164 | |||
165 | /* Hardware registers */ | ||
166 | |||
diff --git a/drivers/media/video/saa7164/saa7164-types.h b/drivers/media/video/saa7164/saa7164-types.h new file mode 100644 index 000000000000..99093f23aae5 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-types.h | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | /* TODO: Cleanup and shorten the namespace */ | ||
23 | |||
24 | /* Some structues are passed directly to/from the firmware and | ||
25 | * have strict alignment requirements. This is one of them. | ||
26 | */ | ||
27 | typedef struct { | ||
28 | u8 bLength; | ||
29 | u8 bDescriptorType; | ||
30 | u8 bDescriptorSubtype; | ||
31 | u16 bcdSpecVersion; | ||
32 | u32 dwClockFrequency; | ||
33 | u32 dwClockUpdateRes; | ||
34 | u8 bCapabilities; | ||
35 | u32 dwDeviceRegistersLocation; | ||
36 | u32 dwHostMemoryRegion; | ||
37 | u32 dwHostMemoryRegionSize; | ||
38 | u32 dwHostHibernatMemRegion; | ||
39 | u32 dwHostHibernatMemRegionSize; | ||
40 | } __attribute__((packed)) tmComResHWDescr_t; | ||
41 | |||
42 | /* This is DWORD aligned on windows but I can't find the right | ||
43 | * gcc syntax to match the binary data from the device. | ||
44 | * I've manually padded with Reserved[3] bytes to match the hardware, | ||
45 | * but this could break if GCC decies to pack in a different way. | ||
46 | */ | ||
47 | typedef struct { | ||
48 | u8 bLength; | ||
49 | u8 bDescriptorType; | ||
50 | u8 bDescriptorSubtype; | ||
51 | u8 bFlags; | ||
52 | u8 bInterfaceType; | ||
53 | u8 bInterfaceId; | ||
54 | u8 bBaseInterface; | ||
55 | u8 bInterruptId; | ||
56 | u8 bDebugInterruptId; | ||
57 | u8 BARLocation; | ||
58 | u8 Reserved[3]; | ||
59 | } tmComResInterfaceDescr_t; | ||
60 | |||
61 | typedef struct { | ||
62 | u64 CommandRing; | ||
63 | u64 ResponseRing; | ||
64 | u32 CommandWrite; | ||
65 | u32 CommandRead; | ||
66 | u32 ResponseWrite; | ||
67 | u32 ResponseRead; | ||
68 | } tmComResBusDescr_t; | ||
69 | |||
70 | typedef enum { | ||
71 | NONE = 0, | ||
72 | TYPE_BUS_PCI = 1, | ||
73 | TYPE_BUS_PCIe = 2, | ||
74 | TYPE_BUS_USB = 3, | ||
75 | TYPE_BUS_I2C = 4 | ||
76 | } tmBusType_t; | ||
77 | |||
78 | typedef struct { | ||
79 | tmBusType_t Type; | ||
80 | u16 m_wMaxReqSize; | ||
81 | u8 *m_pdwSetRing; | ||
82 | u32 m_dwSizeSetRing; | ||
83 | u8 *m_pdwGetRing; | ||
84 | u32 m_dwSizeGetRing; | ||
85 | u32 *m_pdwSetWritePos; | ||
86 | u32 *m_pdwSetReadPos; | ||
87 | u32 *m_pdwGetWritePos; | ||
88 | u32 *m_pdwGetReadPos; | ||
89 | |||
90 | /* All access is protected */ | ||
91 | struct mutex lock; | ||
92 | |||
93 | } tmComResBusInfo_t; | ||
94 | |||
95 | typedef struct { | ||
96 | u8 id; | ||
97 | u8 flags; | ||
98 | u16 size; | ||
99 | u32 command; | ||
100 | u16 controlselector; | ||
101 | u8 seqno; | ||
102 | } __attribute__((packed)) tmComResInfo_t; | ||
103 | |||
104 | typedef enum { | ||
105 | SET_CUR = 0x01, | ||
106 | GET_CUR = 0x81, | ||
107 | GET_MIN = 0x82, | ||
108 | GET_MAX = 0x83, | ||
109 | GET_RES = 0x84, | ||
110 | GET_LEN = 0x85, | ||
111 | GET_INFO = 0x86, | ||
112 | GET_DEF = 0x87 | ||
113 | } tmComResCmd_t; | ||
114 | |||
115 | struct cmd { | ||
116 | u8 seqno; | ||
117 | u32 inuse; | ||
118 | u32 timeout; | ||
119 | u32 signalled; | ||
120 | struct mutex lock; | ||
121 | wait_queue_head_t wait; | ||
122 | }; | ||
123 | |||
124 | typedef struct { | ||
125 | u32 pathid; | ||
126 | u32 size; | ||
127 | void *descriptor; | ||
128 | } tmDescriptor_t; | ||
129 | |||
130 | typedef struct { | ||
131 | u8 len; | ||
132 | u8 type; | ||
133 | u8 subtype; | ||
134 | u8 unitid; | ||
135 | } __attribute__((packed)) tmComResDescrHeader_t; | ||
136 | |||
137 | typedef struct { | ||
138 | u8 len; | ||
139 | u8 type; | ||
140 | u8 subtype; | ||
141 | u8 unitid; | ||
142 | u32 devicetype; | ||
143 | u16 deviceid; | ||
144 | u32 numgpiopins; | ||
145 | u8 numgpiogroups; | ||
146 | u8 controlsize; | ||
147 | } __attribute__((packed)) tmComResExtDevDescrHeader_t; | ||
148 | |||
149 | typedef struct { | ||
150 | u32 pin; | ||
151 | u8 state; | ||
152 | } __attribute__((packed)) tmComResGPIO_t; | ||
153 | |||
154 | typedef struct { | ||
155 | u8 len; | ||
156 | u8 type; | ||
157 | u8 subtype; | ||
158 | u8 pathid; | ||
159 | } __attribute__((packed)) tmComResPathDescrHeader_t; | ||
160 | |||
161 | /* terminaltype */ | ||
162 | typedef enum { | ||
163 | ITT_ANTENNA = 0x0203, | ||
164 | LINE_CONNECTOR = 0x0603, | ||
165 | SPDIF_CONNECTOR = 0x0605, | ||
166 | COMPOSITE_CONNECTOR = 0x0401, | ||
167 | SVIDEO_CONNECTOR = 0x0402, | ||
168 | COMPONENT_CONNECTOR = 0x0403, | ||
169 | STANDARD_DMA = 0xF101 | ||
170 | } tmComResTermType_t; | ||
171 | |||
172 | typedef struct { | ||
173 | u8 len; | ||
174 | u8 type; | ||
175 | u8 subtype; | ||
176 | u8 terminalid; | ||
177 | u16 terminaltype; | ||
178 | u8 assocterminal; | ||
179 | u8 iterminal; | ||
180 | u8 controlsize; | ||
181 | } __attribute__((packed)) tmComResAntTermDescrHeader_t; | ||
182 | |||
183 | typedef struct { | ||
184 | u8 len; | ||
185 | u8 type; | ||
186 | u8 subtype; | ||
187 | u8 unitid; | ||
188 | u8 sourceid; | ||
189 | u8 iunit; | ||
190 | u32 tuningstandards; | ||
191 | u8 controlsize; | ||
192 | u32 controls; | ||
193 | } __attribute__((packed)) tmComResTunerDescrHeader_t; | ||
194 | |||
195 | typedef enum { | ||
196 | /* the buffer does not contain any valid data */ | ||
197 | TM_BUFFER_FLAG_EMPTY, | ||
198 | |||
199 | /* the buffer is filled with valid data */ | ||
200 | TM_BUFFER_FLAG_DONE, | ||
201 | |||
202 | /* the buffer is the dummy buffer - TODO??? */ | ||
203 | TM_BUFFER_FLAG_DUMMY_BUFFER | ||
204 | } tmBufferFlag_t; | ||
205 | |||
206 | typedef struct { | ||
207 | u64 *pagetablevirt; | ||
208 | u64 pagetablephys; | ||
209 | u16 offset; | ||
210 | u8 *context; | ||
211 | u64 timestamp; | ||
212 | tmBufferFlag_t BufferFlag_t; | ||
213 | u32 lostbuffers; | ||
214 | u32 validbuffers; | ||
215 | u64 *dummypagevirt; | ||
216 | u64 dummypagephys; | ||
217 | u64 *addressvirt; | ||
218 | } tmBuffer_t; | ||
219 | |||
220 | typedef struct { | ||
221 | u32 bitspersample; | ||
222 | u32 samplesperline; | ||
223 | u32 numberoflines; | ||
224 | u32 pitch; | ||
225 | u32 linethreshold; | ||
226 | u64 **pagetablelistvirt; | ||
227 | u64 *pagetablelistphys; | ||
228 | u32 numpagetables; | ||
229 | u32 numpagetableentries; | ||
230 | } tmHWStreamParameters_t; | ||
231 | |||
232 | typedef struct { | ||
233 | tmHWStreamParameters_t HWStreamParameters_t; | ||
234 | u64 qwDummyPageTablePhys; | ||
235 | u64 *pDummyPageTableVirt; | ||
236 | } tmStreamParameters_t; | ||
237 | |||
238 | typedef struct { | ||
239 | u8 len; | ||
240 | u8 type; | ||
241 | u8 subtyle; | ||
242 | u8 unitid; | ||
243 | u16 terminaltype; | ||
244 | u8 assocterminal; | ||
245 | u8 sourceid; | ||
246 | u8 iterminal; | ||
247 | u32 BARLocation; | ||
248 | u8 flags; | ||
249 | u8 interruptid; | ||
250 | u8 buffercount; | ||
251 | u8 metadatasize; | ||
252 | u8 numformats; | ||
253 | u8 controlsize; | ||
254 | } __attribute__((packed)) tmComResDMATermDescrHeader_t; | ||
255 | |||
256 | /* | ||
257 | * | ||
258 | * Description: | ||
259 | * This is the transport stream format header. | ||
260 | * | ||
261 | * Settings: | ||
262 | * bLength - The size of this descriptor in bytes. | ||
263 | * bDescriptorType - CS_INTERFACE. | ||
264 | * bDescriptorSubtype - VS_FORMAT_MPEG2TS descriptor subtype. | ||
265 | * bFormatIndex - A non-zero constant that uniquely identifies the | ||
266 | * format. | ||
267 | * bDataOffset - Offset to TSP packet within MPEG-2 TS transport | ||
268 | * stride, in bytes. | ||
269 | * bPacketLength - Length of TSP packet, in bytes (typically 188). | ||
270 | * bStrideLength - Length of MPEG-2 TS transport stride. | ||
271 | * guidStrideFormat - A Globally Unique Identifier indicating the | ||
272 | * format of the stride data (if any). Set to zeros | ||
273 | * if there is no Stride Data, or if the Stride | ||
274 | * Data is to be ignored by the application. | ||
275 | * | ||
276 | */ | ||
277 | typedef struct { | ||
278 | u8 len; | ||
279 | u8 type; | ||
280 | u8 subtype; | ||
281 | u8 bFormatIndex; | ||
282 | u8 bDataOffset; | ||
283 | u8 bPacketLength; | ||
284 | u8 bStrideLength; | ||
285 | u8 guidStrideFormat[16]; | ||
286 | } __attribute__((packed)) tmComResTSFormatDescrHeader_t; | ||
287 | |||
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h new file mode 100644 index 000000000000..ed38118ffde5 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164.h | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | Driver architecture | ||
24 | ******************* | ||
25 | |||
26 | saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c | ||
27 | | : Standard Linux driver framework for creating | ||
28 | | : exposing and managing interfaces to the rest | ||
29 | | : of the kernel or userland. Also uses _fw.c to load | ||
30 | | : firmware direct into the PCIe bus, bypassing layers. | ||
31 | V | ||
32 | saa7164_api..() : Translate kernel specific functions/features | ||
33 | | : into command buffers. | ||
34 | V | ||
35 | saa7164_cmd..() : Manages the flow of command packets on/off, | ||
36 | | : the bus. Deal with bus errors, timeouts etc. | ||
37 | V | ||
38 | saa7164_bus..() : Manage a read/write memory ring buffer in the | ||
39 | | : PCIe Address space. | ||
40 | | | ||
41 | | saa7164_fw...() : Load any frimware | ||
42 | | | : direct into the device | ||
43 | V V | ||
44 | <- ----------------- PCIe address space -------------------- -> | ||
45 | */ | ||
46 | |||
47 | #include <linux/pci.h> | ||
48 | #include <linux/i2c.h> | ||
49 | #include <linux/i2c-algo-bit.h> | ||
50 | #include <linux/kdev_t.h> | ||
51 | |||
52 | #include <media/tuner.h> | ||
53 | #include <media/tveeprom.h> | ||
54 | #include <media/videobuf-dma-sg.h> | ||
55 | #include <media/videobuf-dvb.h> | ||
56 | |||
57 | #include "saa7164-reg.h" | ||
58 | #include "saa7164-types.h" | ||
59 | |||
60 | #include <linux/version.h> | ||
61 | #include <linux/mutex.h> | ||
62 | |||
63 | #define SAA7164_MAXBOARDS 8 | ||
64 | |||
65 | #define UNSET (-1U) | ||
66 | #define SAA7164_BOARD_NOAUTO UNSET | ||
67 | #define SAA7164_BOARD_UNKNOWN 0 | ||
68 | #define SAA7164_BOARD_UNKNOWN_REV2 1 | ||
69 | #define SAA7164_BOARD_UNKNOWN_REV3 2 | ||
70 | #define SAA7164_BOARD_HAUPPAUGE_HVR2250 3 | ||
71 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200 4 | ||
72 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5 | ||
73 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6 | ||
74 | #define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7 | ||
75 | |||
76 | #define SAA7164_MAX_UNITS 8 | ||
77 | #define SAA7164_TS_NUMBER_OF_LINES 312 | ||
78 | #define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */ | ||
79 | |||
80 | #define DBGLVL_FW 4 | ||
81 | #define DBGLVL_DVB 8 | ||
82 | #define DBGLVL_I2C 16 | ||
83 | #define DBGLVL_API 32 | ||
84 | #define DBGLVL_CMD 64 | ||
85 | #define DBGLVL_BUS 128 | ||
86 | #define DBGLVL_IRQ 256 | ||
87 | #define DBGLVL_BUF 512 | ||
88 | |||
89 | enum port_t { | ||
90 | SAA7164_MPEG_UNDEFINED = 0, | ||
91 | SAA7164_MPEG_DVB, | ||
92 | }; | ||
93 | |||
94 | enum saa7164_i2c_bus_nr { | ||
95 | SAA7164_I2C_BUS_0 = 0, | ||
96 | SAA7164_I2C_BUS_1, | ||
97 | SAA7164_I2C_BUS_2, | ||
98 | }; | ||
99 | |||
100 | enum saa7164_buffer_flags { | ||
101 | SAA7164_BUFFER_UNDEFINED = 0, | ||
102 | SAA7164_BUFFER_FREE, | ||
103 | SAA7164_BUFFER_BUSY, | ||
104 | SAA7164_BUFFER_FULL | ||
105 | }; | ||
106 | |||
107 | enum saa7164_unit_type { | ||
108 | SAA7164_UNIT_UNDEFINED = 0, | ||
109 | SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
110 | SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
111 | SAA7164_UNIT_TUNER, | ||
112 | SAA7164_UNIT_EEPROM, | ||
113 | SAA7164_UNIT_ZILOG_IRBLASTER, | ||
114 | SAA7164_UNIT_ENCODER, | ||
115 | }; | ||
116 | |||
117 | /* The PCIe bridge doesn't grant direct access to i2c. | ||
118 | * Instead, you address i2c devices using a uniqely | ||
119 | * allocated 'unitid' value via a messaging API. This | ||
120 | * is a problem. The kernel and existing demod/tuner | ||
121 | * drivers expect to talk 'i2c', so we have to maintain | ||
122 | * a translation layer, and a series of functions to | ||
123 | * convert i2c bus + device address into a unit id. | ||
124 | */ | ||
125 | struct saa7164_unit { | ||
126 | enum saa7164_unit_type type; | ||
127 | u8 id; | ||
128 | char *name; | ||
129 | enum saa7164_i2c_bus_nr i2c_bus_nr; | ||
130 | u8 i2c_bus_addr; | ||
131 | u8 i2c_reg_len; | ||
132 | }; | ||
133 | |||
134 | struct saa7164_board { | ||
135 | char *name; | ||
136 | enum port_t porta, portb; | ||
137 | enum { | ||
138 | SAA7164_CHIP_UNDEFINED = 0, | ||
139 | SAA7164_CHIP_REV2, | ||
140 | SAA7164_CHIP_REV3, | ||
141 | } chiprev; | ||
142 | struct saa7164_unit unit[SAA7164_MAX_UNITS]; | ||
143 | }; | ||
144 | |||
145 | struct saa7164_subid { | ||
146 | u16 subvendor; | ||
147 | u16 subdevice; | ||
148 | u32 card; | ||
149 | }; | ||
150 | |||
151 | struct saa7164_fw_status { | ||
152 | |||
153 | /* RISC Core details */ | ||
154 | u32 status; | ||
155 | u32 mode; | ||
156 | u32 spec; | ||
157 | u32 inst; | ||
158 | u32 cpuload; | ||
159 | u32 remainheap; | ||
160 | |||
161 | /* Firmware version */ | ||
162 | u32 version; | ||
163 | u32 major; | ||
164 | u32 sub; | ||
165 | u32 rel; | ||
166 | u32 buildnr; | ||
167 | }; | ||
168 | |||
169 | struct saa7164_dvb { | ||
170 | struct mutex lock; | ||
171 | struct dvb_adapter adapter; | ||
172 | struct dvb_frontend *frontend; | ||
173 | struct dvb_demux demux; | ||
174 | struct dmxdev dmxdev; | ||
175 | struct dmx_frontend fe_hw; | ||
176 | struct dmx_frontend fe_mem; | ||
177 | struct dvb_net net; | ||
178 | int feeding; | ||
179 | }; | ||
180 | |||
181 | struct saa7164_i2c { | ||
182 | struct saa7164_dev *dev; | ||
183 | |||
184 | enum saa7164_i2c_bus_nr nr; | ||
185 | |||
186 | /* I2C I/O */ | ||
187 | struct i2c_adapter i2c_adap; | ||
188 | struct i2c_algo_bit_data i2c_algo; | ||
189 | struct i2c_client i2c_client; | ||
190 | u32 i2c_rc; | ||
191 | }; | ||
192 | |||
193 | struct saa7164_tsport; | ||
194 | |||
195 | struct saa7164_buffer { | ||
196 | struct list_head list; | ||
197 | |||
198 | u32 nr; | ||
199 | |||
200 | struct saa7164_tsport *port; | ||
201 | |||
202 | /* Hardware Specific */ | ||
203 | /* PCI Memory allocations */ | ||
204 | enum saa7164_buffer_flags flags; /* Free, Busy, Full */ | ||
205 | |||
206 | /* A block of page align PCI memory */ | ||
207 | u32 pci_size; /* PCI allocation size in bytes */ | ||
208 | u64 *cpu; /* Virtual address */ | ||
209 | dma_addr_t dma; /* Physical address */ | ||
210 | |||
211 | /* A page table that splits the block into a number of entries */ | ||
212 | u32 pt_size; /* PCI allocation size in bytes */ | ||
213 | u64 *pt_cpu; /* Virtual address */ | ||
214 | dma_addr_t pt_dma; /* Physical address */ | ||
215 | }; | ||
216 | |||
217 | struct saa7164_tsport { | ||
218 | |||
219 | struct saa7164_dev *dev; | ||
220 | int nr; | ||
221 | enum port_t type; | ||
222 | |||
223 | struct saa7164_dvb dvb; | ||
224 | |||
225 | /* HW related stream parameters */ | ||
226 | tmHWStreamParameters_t hw_streamingparams; | ||
227 | |||
228 | /* DMA configuration values, is seeded during initialization */ | ||
229 | tmComResDMATermDescrHeader_t hwcfg; | ||
230 | |||
231 | /* hardware specific registers */ | ||
232 | u32 bufcounter; | ||
233 | u32 pitch; | ||
234 | u32 bufsize; | ||
235 | u32 bufoffset; | ||
236 | u32 bufptr32l; | ||
237 | u32 bufptr32h; | ||
238 | u64 bufptr64; | ||
239 | |||
240 | u32 numpte; /* Number of entries in array, only valid in head */ | ||
241 | struct mutex dmaqueue_lock; | ||
242 | struct mutex dummy_dmaqueue_lock; | ||
243 | struct saa7164_buffer dmaqueue; | ||
244 | struct saa7164_buffer dummy_dmaqueue; | ||
245 | |||
246 | }; | ||
247 | |||
248 | struct saa7164_dev { | ||
249 | struct list_head devlist; | ||
250 | atomic_t refcount; | ||
251 | |||
252 | /* pci stuff */ | ||
253 | struct pci_dev *pci; | ||
254 | unsigned char pci_rev, pci_lat; | ||
255 | int pci_bus, pci_slot; | ||
256 | u32 __iomem *lmmio; | ||
257 | u8 __iomem *bmmio; | ||
258 | u32 __iomem *lmmio2; | ||
259 | u8 __iomem *bmmio2; | ||
260 | int pci_irqmask; | ||
261 | |||
262 | /* board details */ | ||
263 | int nr; | ||
264 | int hwrevision; | ||
265 | u32 board; | ||
266 | char name[32]; | ||
267 | |||
268 | /* firmware status */ | ||
269 | struct saa7164_fw_status fw_status; | ||
270 | |||
271 | tmComResHWDescr_t hwdesc; | ||
272 | tmComResInterfaceDescr_t intfdesc; | ||
273 | tmComResBusDescr_t busdesc; | ||
274 | |||
275 | tmComResBusInfo_t bus; | ||
276 | |||
277 | /* TODO: Urgh, remove volatiles */ | ||
278 | volatile u32 *InterruptStatus; | ||
279 | volatile u32 *InterruptAck; | ||
280 | |||
281 | struct cmd cmds[SAA_CMD_MAX_MSG_UNITS]; | ||
282 | struct mutex lock; | ||
283 | |||
284 | /* I2c related */ | ||
285 | struct saa7164_i2c i2c_bus[3]; | ||
286 | |||
287 | /* Transport related */ | ||
288 | struct saa7164_tsport ts1, ts2; | ||
289 | |||
290 | /* Deferred command/api interrupts handling */ | ||
291 | struct work_struct workcmd; | ||
292 | |||
293 | }; | ||
294 | |||
295 | extern struct list_head saa7164_devlist; | ||
296 | |||
297 | /* ----------------------------------------------------------- */ | ||
298 | /* saa7164-core.c */ | ||
299 | void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr); | ||
300 | void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len); | ||
301 | void saa7164_getfirmwarestatus(struct saa7164_dev *dev); | ||
302 | u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev); | ||
303 | |||
304 | /* ----------------------------------------------------------- */ | ||
305 | /* saa7164-fw.c */ | ||
306 | int saa7164_downloadfirmware(struct saa7164_dev *dev); | ||
307 | |||
308 | /* ----------------------------------------------------------- */ | ||
309 | /* saa7164-i2c.c */ | ||
310 | extern int saa7164_i2c_register(struct saa7164_i2c *bus); | ||
311 | extern int saa7164_i2c_unregister(struct saa7164_i2c *bus); | ||
312 | extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus, | ||
313 | unsigned int cmd, void *arg); | ||
314 | |||
315 | /* ----------------------------------------------------------- */ | ||
316 | /* saa7164-bus.c */ | ||
317 | int saa7164_bus_setup(struct saa7164_dev *dev); | ||
318 | void saa7164_bus_dump(struct saa7164_dev *dev); | ||
319 | int saa7164_bus_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf); | ||
320 | int saa7164_bus_get(struct saa7164_dev *dev, tmComResInfo_t* msg, | ||
321 | void *buf, int peekonly); | ||
322 | |||
323 | /* ----------------------------------------------------------- */ | ||
324 | /* saa7164-cmd.c */ | ||
325 | int saa7164_cmd_send(struct saa7164_dev *dev, | ||
326 | u8 id, tmComResCmd_t command, u16 controlselector, | ||
327 | u16 size, void *buf); | ||
328 | void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno); | ||
329 | |||
330 | /* ----------------------------------------------------------- */ | ||
331 | /* saa7164-api.c */ | ||
332 | int saa7164_api_test(struct saa7164_dev *dev); | ||
333 | int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version); | ||
334 | int saa7164_api_enum_subdevs(struct saa7164_dev *dev); | ||
335 | int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, | ||
336 | u32 datalen, u8 *data); | ||
337 | int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, | ||
338 | u32 datalen, u8 *data); | ||
339 | int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr, | ||
340 | u32 datalen, u8 *data); | ||
341 | int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen); | ||
342 | int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); | ||
343 | int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); | ||
344 | int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode); | ||
345 | |||
346 | /* ----------------------------------------------------------- */ | ||
347 | /* saa7164-cards.c */ | ||
348 | extern struct saa7164_board saa7164_boards[]; | ||
349 | extern const unsigned int saa7164_bcount; | ||
350 | |||
351 | extern struct saa7164_subid saa7164_subids[]; | ||
352 | extern const unsigned int saa7164_idcount; | ||
353 | |||
354 | extern void saa7164_card_list(struct saa7164_dev *dev); | ||
355 | extern void saa7164_gpio_setup(struct saa7164_dev *dev); | ||
356 | extern void saa7164_card_setup(struct saa7164_dev *dev); | ||
357 | |||
358 | extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr); | ||
359 | extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr); | ||
360 | extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid); | ||
361 | |||
362 | /* ----------------------------------------------------------- */ | ||
363 | /* saa7164-dvb.c */ | ||
364 | extern int saa7164_dvb_register(struct saa7164_tsport *port); | ||
365 | extern int saa7164_dvb_unregister(struct saa7164_tsport *port); | ||
366 | |||
367 | /* ----------------------------------------------------------- */ | ||
368 | /* saa7164-buffer.c */ | ||
369 | extern struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_tsport *port, | ||
370 | u32 len); | ||
371 | extern int saa7164_buffer_dealloc(struct saa7164_tsport *port, | ||
372 | struct saa7164_buffer *buf); | ||
373 | |||
374 | /* ----------------------------------------------------------- */ | ||
375 | |||
376 | extern unsigned int debug; | ||
377 | #define dprintk(level, fmt, arg...)\ | ||
378 | do { if (debug & level)\ | ||
379 | printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ | ||
380 | } while (0) | ||
381 | |||
382 | #define log_warn(fmt, arg...)\ | ||
383 | do { \ | ||
384 | printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\ | ||
385 | } while (0) | ||
386 | |||
387 | #define log_err(fmt, arg...)\ | ||
388 | do { \ | ||
389 | printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\ | ||
390 | } while (0) | ||
391 | |||
392 | #define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2)) | ||
393 | #define saa7164_writel(reg, value) \ | ||
394 | do { \ | ||
395 | printk(KERN_ERR "writel(%x, %llx)\n", value, (u64)(dev->lmmio + ((reg) >> 2))); \ | ||
396 | writel((value), dev->lmmio + ((reg) >> 2)); \ | ||
397 | } while (0) | ||
398 | |||
399 | #define saa7164_readb(reg) readl(dev->bmmio + (reg)) | ||
400 | #define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg)) | ||
401 | |||