diff options
Diffstat (limited to 'drivers/media/video/saa7164')
-rw-r--r-- | drivers/media/video/saa7164/Kconfig | 18 | ||||
-rw-r--r-- | drivers/media/video/saa7164/Makefile | 12 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-api.c | 1533 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-buffer.c | 322 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-bus.c | 475 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-cards.c | 715 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-cmd.c | 589 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-core.c | 1526 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-dvb.c | 555 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-encoder.c | 1506 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-fw.c | 613 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-i2c.c | 141 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-reg.h | 219 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-types.h | 442 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164-vbi.c | 1380 | ||||
-rw-r--r-- | drivers/media/video/saa7164/saa7164.h | 623 |
16 files changed, 10669 insertions, 0 deletions
diff --git a/drivers/media/video/saa7164/Kconfig b/drivers/media/video/saa7164/Kconfig new file mode 100644 index 00000000000..35326372517 --- /dev/null +++ b/drivers/media/video/saa7164/Kconfig | |||
@@ -0,0 +1,18 @@ | |||
1 | config VIDEO_SAA7164 | ||
2 | tristate "NXP SAA7164 support" | ||
3 | depends on DVB_CORE && PCI && I2C | ||
4 | select I2C_ALGOBIT | ||
5 | select FW_LOADER | ||
6 | select VIDEO_TUNER | ||
7 | select VIDEO_TVEEPROM | ||
8 | select VIDEOBUF_DVB | ||
9 | select DVB_TDA10048 if !DVB_FE_CUSTOMISE | ||
10 | select DVB_S5H1411 if !DVB_FE_CUSTOMISE | ||
11 | select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE | ||
12 | ---help--- | ||
13 | This is a video4linux driver for NXP SAA7164 based | ||
14 | TV cards. | ||
15 | |||
16 | To compile this driver as a module, choose M here: the | ||
17 | module will be called saa7164 | ||
18 | |||
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile new file mode 100644 index 00000000000..6303a8e60ea --- /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 saa7164-encoder.o saa7164-vbi.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 00000000000..8a98ab68239 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-api.c | |||
@@ -0,0 +1,1533 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | #include <linux/slab.h> | ||
24 | |||
25 | #include "saa7164.h" | ||
26 | |||
27 | int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i) | ||
28 | { | ||
29 | int ret; | ||
30 | |||
31 | if (!(saa_debug & DBGLVL_CPU)) | ||
32 | return 0; | ||
33 | |||
34 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
35 | |||
36 | i->deviceinst = 0; | ||
37 | i->devicespec = 0; | ||
38 | i->mode = 0; | ||
39 | i->status = 0; | ||
40 | |||
41 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
42 | GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i); | ||
43 | if (ret != SAA_OK) | ||
44 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
45 | |||
46 | printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad); | ||
47 | |||
48 | return ret; | ||
49 | } | ||
50 | |||
51 | int saa7164_api_collect_debug(struct saa7164_dev *dev) | ||
52 | { | ||
53 | struct tmComResDebugGetData d; | ||
54 | u8 more = 255; | ||
55 | int ret; | ||
56 | |||
57 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
58 | |||
59 | while (more--) { | ||
60 | |||
61 | memset(&d, 0, sizeof(d)); | ||
62 | |||
63 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
64 | GET_DEBUG_DATA_CONTROL, sizeof(d), &d); | ||
65 | if (ret != SAA_OK) | ||
66 | printk(KERN_ERR "%s() error, ret = 0x%x\n", | ||
67 | __func__, ret); | ||
68 | |||
69 | if (d.dwResult != SAA_OK) | ||
70 | break; | ||
71 | |||
72 | printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr, | ||
73 | d.ucDebugData); | ||
74 | } | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level) | ||
80 | { | ||
81 | struct tmComResDebugSetLevel lvl; | ||
82 | int ret; | ||
83 | |||
84 | dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level); | ||
85 | |||
86 | /* Retrieve current state */ | ||
87 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
88 | SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl); | ||
89 | if (ret != SAA_OK) | ||
90 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
91 | |||
92 | dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel); | ||
93 | |||
94 | lvl.dwDebugLevel = level; | ||
95 | |||
96 | /* set new state */ | ||
97 | ret = saa7164_cmd_send(dev, 0, SET_CUR, | ||
98 | SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl); | ||
99 | if (ret != SAA_OK) | ||
100 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
101 | |||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | int saa7164_api_set_vbi_format(struct saa7164_port *port) | ||
106 | { | ||
107 | struct saa7164_dev *dev = port->dev; | ||
108 | struct tmComResProbeCommit fmt, rsp; | ||
109 | int ret; | ||
110 | |||
111 | dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__, | ||
112 | port->nr, port->hwcfg.unitid); | ||
113 | |||
114 | fmt.bmHint = 0; | ||
115 | fmt.bFormatIndex = 1; | ||
116 | fmt.bFrameIndex = 1; | ||
117 | |||
118 | /* Probe, see if it can support this format */ | ||
119 | ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, | ||
120 | SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt); | ||
121 | if (ret != SAA_OK) | ||
122 | printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret); | ||
123 | |||
124 | /* See of the format change was successful */ | ||
125 | ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, | ||
126 | GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp); | ||
127 | if (ret != SAA_OK) { | ||
128 | printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret); | ||
129 | } else { | ||
130 | /* Compare requested vs received, should be same */ | ||
131 | if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) { | ||
132 | dprintk(DBGLVL_API, "SET/PROBE Verified\n"); | ||
133 | |||
134 | /* Ask the device to select the negotiated format */ | ||
135 | ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, | ||
136 | SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt); | ||
137 | if (ret != SAA_OK) | ||
138 | printk(KERN_ERR "%s() commit error, ret = 0x%x\n", | ||
139 | __func__, ret); | ||
140 | |||
141 | ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, | ||
142 | GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp); | ||
143 | if (ret != SAA_OK) | ||
144 | printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n", | ||
145 | __func__, ret); | ||
146 | |||
147 | if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) { | ||
148 | printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n", | ||
149 | __func__, ret); | ||
150 | } else | ||
151 | dprintk(DBGLVL_API, "SET/COMMIT Verified\n"); | ||
152 | |||
153 | dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint); | ||
154 | dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n", | ||
155 | rsp.bFormatIndex); | ||
156 | dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n", | ||
157 | rsp.bFrameIndex); | ||
158 | } else | ||
159 | printk(KERN_ERR "%s() compare failed\n", __func__); | ||
160 | } | ||
161 | |||
162 | if (ret == SAA_OK) | ||
163 | dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr); | ||
164 | |||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | int saa7164_api_set_gop_size(struct saa7164_port *port) | ||
169 | { | ||
170 | struct saa7164_dev *dev = port->dev; | ||
171 | struct tmComResEncVideoGopStructure gs; | ||
172 | int ret; | ||
173 | |||
174 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
175 | |||
176 | gs.ucRefFrameDist = port->encoder_params.refdist; | ||
177 | gs.ucGOPSize = port->encoder_params.gop_size; | ||
178 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, | ||
179 | EU_VIDEO_GOP_STRUCTURE_CONTROL, | ||
180 | sizeof(gs), &gs); | ||
181 | if (ret != SAA_OK) | ||
182 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
183 | |||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | int saa7164_api_set_encoder(struct saa7164_port *port) | ||
188 | { | ||
189 | struct saa7164_dev *dev = port->dev; | ||
190 | struct tmComResEncVideoBitRate vb; | ||
191 | struct tmComResEncAudioBitRate ab; | ||
192 | int ret; | ||
193 | |||
194 | dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__, | ||
195 | port->hwcfg.sourceid); | ||
196 | |||
197 | if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) | ||
198 | port->encoder_profile = EU_PROFILE_PS_DVD; | ||
199 | else | ||
200 | port->encoder_profile = EU_PROFILE_TS_HQ; | ||
201 | |||
202 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, | ||
203 | EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile); | ||
204 | if (ret != SAA_OK) | ||
205 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
206 | |||
207 | /* Resolution */ | ||
208 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, | ||
209 | EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile); | ||
210 | if (ret != SAA_OK) | ||
211 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
212 | |||
213 | /* Establish video bitrates */ | ||
214 | if (port->encoder_params.bitrate_mode == | ||
215 | V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) | ||
216 | vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT; | ||
217 | else | ||
218 | vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK; | ||
219 | vb.dwVideoBitRate = port->encoder_params.bitrate; | ||
220 | vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak; | ||
221 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, | ||
222 | EU_VIDEO_BIT_RATE_CONTROL, | ||
223 | sizeof(struct tmComResEncVideoBitRate), | ||
224 | &vb); | ||
225 | if (ret != SAA_OK) | ||
226 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
227 | |||
228 | /* Establish audio bitrates */ | ||
229 | ab.ucAudioBitRateMode = 0; | ||
230 | ab.dwAudioBitRate = 384000; | ||
231 | ab.dwAudioBitRatePeak = ab.dwAudioBitRate; | ||
232 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, | ||
233 | EU_AUDIO_BIT_RATE_CONTROL, | ||
234 | sizeof(struct tmComResEncAudioBitRate), | ||
235 | &ab); | ||
236 | if (ret != SAA_OK) | ||
237 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, | ||
238 | ret); | ||
239 | |||
240 | saa7164_api_set_aspect_ratio(port); | ||
241 | saa7164_api_set_gop_size(port); | ||
242 | |||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | int saa7164_api_get_encoder(struct saa7164_port *port) | ||
247 | { | ||
248 | struct saa7164_dev *dev = port->dev; | ||
249 | struct tmComResEncVideoBitRate v; | ||
250 | struct tmComResEncAudioBitRate a; | ||
251 | struct tmComResEncVideoInputAspectRatio ar; | ||
252 | int ret; | ||
253 | |||
254 | dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__, | ||
255 | port->hwcfg.sourceid); | ||
256 | |||
257 | port->encoder_profile = 0; | ||
258 | port->video_format = 0; | ||
259 | port->video_resolution = 0; | ||
260 | port->audio_format = 0; | ||
261 | |||
262 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
263 | EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile); | ||
264 | if (ret != SAA_OK) | ||
265 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
266 | |||
267 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
268 | EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8), | ||
269 | &port->video_resolution); | ||
270 | if (ret != SAA_OK) | ||
271 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
272 | |||
273 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
274 | EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format); | ||
275 | if (ret != SAA_OK) | ||
276 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
277 | |||
278 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
279 | EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v); | ||
280 | if (ret != SAA_OK) | ||
281 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
282 | |||
283 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
284 | EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format); | ||
285 | if (ret != SAA_OK) | ||
286 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
287 | |||
288 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
289 | EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a); | ||
290 | if (ret != SAA_OK) | ||
291 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
292 | |||
293 | /* Aspect Ratio */ | ||
294 | ar.width = 0; | ||
295 | ar.height = 0; | ||
296 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, | ||
297 | EU_VIDEO_INPUT_ASPECT_CONTROL, | ||
298 | sizeof(struct tmComResEncVideoInputAspectRatio), &ar); | ||
299 | if (ret != SAA_OK) | ||
300 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
301 | |||
302 | dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile); | ||
303 | dprintk(DBGLVL_ENC, "video_format = %d\n", port->video_format); | ||
304 | dprintk(DBGLVL_ENC, "audio_format = %d\n", port->audio_format); | ||
305 | dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution); | ||
306 | dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n", | ||
307 | v.ucVideoBitRateMode); | ||
308 | dprintk(DBGLVL_ENC, "v.dwVideoBitRate = %d\n", | ||
309 | v.dwVideoBitRate); | ||
310 | dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n", | ||
311 | v.dwVideoBitRatePeak); | ||
312 | dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n", | ||
313 | a.ucAudioBitRateMode); | ||
314 | dprintk(DBGLVL_ENC, "a.dwVideoBitRate = %d\n", | ||
315 | a.dwAudioBitRate); | ||
316 | dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n", | ||
317 | a.dwAudioBitRatePeak); | ||
318 | dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n", | ||
319 | ar.width, ar.height); | ||
320 | |||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | int saa7164_api_set_aspect_ratio(struct saa7164_port *port) | ||
325 | { | ||
326 | struct saa7164_dev *dev = port->dev; | ||
327 | struct tmComResEncVideoInputAspectRatio ar; | ||
328 | int ret; | ||
329 | |||
330 | dprintk(DBGLVL_ENC, "%s(%d)\n", __func__, | ||
331 | port->encoder_params.ctl_aspect); | ||
332 | |||
333 | switch (port->encoder_params.ctl_aspect) { | ||
334 | case V4L2_MPEG_VIDEO_ASPECT_1x1: | ||
335 | ar.width = 1; | ||
336 | ar.height = 1; | ||
337 | break; | ||
338 | case V4L2_MPEG_VIDEO_ASPECT_4x3: | ||
339 | ar.width = 4; | ||
340 | ar.height = 3; | ||
341 | break; | ||
342 | case V4L2_MPEG_VIDEO_ASPECT_16x9: | ||
343 | ar.width = 16; | ||
344 | ar.height = 9; | ||
345 | break; | ||
346 | case V4L2_MPEG_VIDEO_ASPECT_221x100: | ||
347 | ar.width = 221; | ||
348 | ar.height = 100; | ||
349 | break; | ||
350 | default: | ||
351 | BUG(); | ||
352 | } | ||
353 | |||
354 | dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__, | ||
355 | port->encoder_params.ctl_aspect, | ||
356 | ar.width, ar.height); | ||
357 | |||
358 | /* Aspect Ratio */ | ||
359 | ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, | ||
360 | EU_VIDEO_INPUT_ASPECT_CONTROL, | ||
361 | sizeof(struct tmComResEncVideoInputAspectRatio), &ar); | ||
362 | if (ret != SAA_OK) | ||
363 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
364 | |||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl) | ||
369 | { | ||
370 | struct saa7164_dev *dev = port->dev; | ||
371 | int ret; | ||
372 | u16 val; | ||
373 | |||
374 | if (ctl == PU_BRIGHTNESS_CONTROL) | ||
375 | val = port->ctl_brightness; | ||
376 | else | ||
377 | if (ctl == PU_CONTRAST_CONTROL) | ||
378 | val = port->ctl_contrast; | ||
379 | else | ||
380 | if (ctl == PU_HUE_CONTROL) | ||
381 | val = port->ctl_hue; | ||
382 | else | ||
383 | if (ctl == PU_SATURATION_CONTROL) | ||
384 | val = port->ctl_saturation; | ||
385 | else | ||
386 | if (ctl == PU_SHARPNESS_CONTROL) | ||
387 | val = port->ctl_sharpness; | ||
388 | else | ||
389 | return -EINVAL; | ||
390 | |||
391 | dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n", | ||
392 | __func__, port->encunit.vsourceid, ctl, val); | ||
393 | |||
394 | ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR, | ||
395 | ctl, sizeof(u16), &val); | ||
396 | if (ret != SAA_OK) | ||
397 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
398 | |||
399 | return ret; | ||
400 | } | ||
401 | |||
402 | int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl) | ||
403 | { | ||
404 | struct saa7164_dev *dev = port->dev; | ||
405 | int ret; | ||
406 | u16 val; | ||
407 | |||
408 | ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR, | ||
409 | ctl, sizeof(u16), &val); | ||
410 | if (ret != SAA_OK) { | ||
411 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
412 | return ret; | ||
413 | } | ||
414 | |||
415 | dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n", | ||
416 | __func__, ctl, val); | ||
417 | |||
418 | if (ctl == PU_BRIGHTNESS_CONTROL) | ||
419 | port->ctl_brightness = val; | ||
420 | else | ||
421 | if (ctl == PU_CONTRAST_CONTROL) | ||
422 | port->ctl_contrast = val; | ||
423 | else | ||
424 | if (ctl == PU_HUE_CONTROL) | ||
425 | port->ctl_hue = val; | ||
426 | else | ||
427 | if (ctl == PU_SATURATION_CONTROL) | ||
428 | port->ctl_saturation = val; | ||
429 | else | ||
430 | if (ctl == PU_SHARPNESS_CONTROL) | ||
431 | port->ctl_sharpness = val; | ||
432 | |||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | int saa7164_api_set_videomux(struct saa7164_port *port) | ||
437 | { | ||
438 | struct saa7164_dev *dev = port->dev; | ||
439 | u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 }; | ||
440 | int ret; | ||
441 | |||
442 | dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n", | ||
443 | __func__, port->mux_input, inputs[port->mux_input - 1]); | ||
444 | |||
445 | /* Audio Mute */ | ||
446 | ret = saa7164_api_audio_mute(port, 1); | ||
447 | if (ret != SAA_OK) | ||
448 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
449 | |||
450 | /* Video Mux */ | ||
451 | ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR, | ||
452 | SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input); | ||
453 | if (ret != SAA_OK) | ||
454 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
455 | |||
456 | /* Audio Mux */ | ||
457 | ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR, | ||
458 | SU_INPUT_SELECT_CONTROL, sizeof(u8), | ||
459 | &inputs[port->mux_input - 1]); | ||
460 | if (ret != SAA_OK) | ||
461 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
462 | |||
463 | /* Audio UnMute */ | ||
464 | ret = saa7164_api_audio_mute(port, 0); | ||
465 | if (ret != SAA_OK) | ||
466 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
467 | |||
468 | return ret; | ||
469 | } | ||
470 | |||
471 | int saa7164_api_audio_mute(struct saa7164_port *port, int mute) | ||
472 | { | ||
473 | struct saa7164_dev *dev = port->dev; | ||
474 | u8 v = mute; | ||
475 | int ret; | ||
476 | |||
477 | dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute); | ||
478 | |||
479 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, | ||
480 | MUTE_CONTROL, sizeof(u8), &v); | ||
481 | if (ret != SAA_OK) | ||
482 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
483 | |||
484 | return ret; | ||
485 | } | ||
486 | |||
487 | /* 0 = silence, 0xff = full */ | ||
488 | int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level) | ||
489 | { | ||
490 | struct saa7164_dev *dev = port->dev; | ||
491 | s16 v, min, max; | ||
492 | int ret; | ||
493 | |||
494 | dprintk(DBGLVL_API, "%s(%d)\n", __func__, level); | ||
495 | |||
496 | /* Obtain the min/max ranges */ | ||
497 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN, | ||
498 | VOLUME_CONTROL, sizeof(u16), &min); | ||
499 | if (ret != SAA_OK) | ||
500 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
501 | |||
502 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX, | ||
503 | VOLUME_CONTROL, sizeof(u16), &max); | ||
504 | if (ret != SAA_OK) | ||
505 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
506 | |||
507 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR, | ||
508 | (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v); | ||
509 | if (ret != SAA_OK) | ||
510 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
511 | |||
512 | dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__, | ||
513 | level, min, max, v); | ||
514 | |||
515 | v = level; | ||
516 | if (v < min) | ||
517 | v = min; | ||
518 | if (v > max) | ||
519 | v = max; | ||
520 | |||
521 | /* Left */ | ||
522 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, | ||
523 | (0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v); | ||
524 | if (ret != SAA_OK) | ||
525 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
526 | |||
527 | /* Right */ | ||
528 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, | ||
529 | (0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v); | ||
530 | if (ret != SAA_OK) | ||
531 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
532 | |||
533 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR, | ||
534 | (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v); | ||
535 | if (ret != SAA_OK) | ||
536 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
537 | |||
538 | dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__, | ||
539 | level, min, max, v); | ||
540 | |||
541 | return ret; | ||
542 | } | ||
543 | |||
544 | int saa7164_api_set_audio_std(struct saa7164_port *port) | ||
545 | { | ||
546 | struct saa7164_dev *dev = port->dev; | ||
547 | struct tmComResAudioDefaults lvl; | ||
548 | struct tmComResTunerStandard tvaudio; | ||
549 | int ret; | ||
550 | |||
551 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
552 | |||
553 | /* Establish default levels */ | ||
554 | lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT; | ||
555 | lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT; | ||
556 | lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT; | ||
557 | lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT; | ||
558 | lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT; | ||
559 | lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT; | ||
560 | ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, | ||
561 | AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults), | ||
562 | &lvl); | ||
563 | if (ret != SAA_OK) | ||
564 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
565 | |||
566 | /* Manually select the appropriate TV audio standard */ | ||
567 | if (port->encodernorm.id & V4L2_STD_NTSC) { | ||
568 | tvaudio.std = TU_STANDARD_NTSC_M; | ||
569 | tvaudio.country = 1; | ||
570 | } else { | ||
571 | tvaudio.std = TU_STANDARD_PAL_I; | ||
572 | tvaudio.country = 44; | ||
573 | } | ||
574 | |||
575 | ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR, | ||
576 | TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio); | ||
577 | if (ret != SAA_OK) | ||
578 | printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n", | ||
579 | __func__, ret); | ||
580 | return ret; | ||
581 | } | ||
582 | |||
583 | int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect) | ||
584 | { | ||
585 | struct saa7164_dev *dev = port->dev; | ||
586 | struct tmComResTunerStandardAuto p; | ||
587 | int ret; | ||
588 | |||
589 | dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect); | ||
590 | |||
591 | /* Disable TV Audio autodetect if not already set (buggy) */ | ||
592 | if (autodetect) | ||
593 | p.mode = TU_STANDARD_AUTO; | ||
594 | else | ||
595 | p.mode = TU_STANDARD_MANUAL; | ||
596 | ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR, | ||
597 | TU_STANDARD_AUTO_CONTROL, sizeof(p), &p); | ||
598 | if (ret != SAA_OK) | ||
599 | printk(KERN_ERR | ||
600 | "%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n", | ||
601 | __func__, ret); | ||
602 | |||
603 | return ret; | ||
604 | } | ||
605 | |||
606 | int saa7164_api_get_videomux(struct saa7164_port *port) | ||
607 | { | ||
608 | struct saa7164_dev *dev = port->dev; | ||
609 | int ret; | ||
610 | |||
611 | ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR, | ||
612 | SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input); | ||
613 | if (ret != SAA_OK) | ||
614 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
615 | |||
616 | dprintk(DBGLVL_ENC, "%s() v_mux=%d\n", | ||
617 | __func__, port->mux_input); | ||
618 | |||
619 | return ret; | ||
620 | } | ||
621 | |||
622 | int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val) | ||
623 | { | ||
624 | struct saa7164_dev *dev = port->dev; | ||
625 | |||
626 | u16 len = 0; | ||
627 | u8 buf[256]; | ||
628 | int ret; | ||
629 | u8 mas; | ||
630 | |||
631 | dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__, | ||
632 | port->nr, port->type, val); | ||
633 | |||
634 | if (port->nr == 0) | ||
635 | mas = 0xd0; | ||
636 | else | ||
637 | mas = 0xe0; | ||
638 | |||
639 | memset(buf, 0, sizeof(buf)); | ||
640 | |||
641 | buf[0x00] = 0x04; | ||
642 | buf[0x01] = 0x00; | ||
643 | buf[0x02] = 0x00; | ||
644 | buf[0x03] = 0x00; | ||
645 | |||
646 | buf[0x04] = 0x04; | ||
647 | buf[0x05] = 0x00; | ||
648 | buf[0x06] = 0x00; | ||
649 | buf[0x07] = 0x00; | ||
650 | |||
651 | buf[0x08] = reg; | ||
652 | buf[0x09] = 0x26; | ||
653 | buf[0x0a] = mas; | ||
654 | buf[0x0b] = 0xb0; | ||
655 | |||
656 | buf[0x0c] = val; | ||
657 | buf[0x0d] = 0x00; | ||
658 | buf[0x0e] = 0x00; | ||
659 | buf[0x0f] = 0x00; | ||
660 | |||
661 | ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN, | ||
662 | EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); | ||
663 | if (ret != SAA_OK) { | ||
664 | printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); | ||
665 | return -EIO; | ||
666 | } | ||
667 | |||
668 | ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR, | ||
669 | EXU_REGISTER_ACCESS_CONTROL, len, &buf); | ||
670 | if (ret != SAA_OK) | ||
671 | printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); | ||
672 | #if 0 | ||
673 | saa7164_dumphex16(dev, buf, 16); | ||
674 | #endif | ||
675 | return ret == SAA_OK ? 0 : -EIO; | ||
676 | } | ||
677 | |||
678 | /* Disable the IF block AGC controls */ | ||
679 | int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) | ||
680 | { | ||
681 | struct saa7164_dev *dev = port->dev; | ||
682 | int ret = 0; | ||
683 | u8 agc_disable; | ||
684 | |||
685 | dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std); | ||
686 | |||
687 | if (std & V4L2_STD_NTSC) { | ||
688 | dprintk(DBGLVL_API, " NTSC\n"); | ||
689 | saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ | ||
690 | agc_disable = 0; | ||
691 | } else if (std & V4L2_STD_PAL_I) { | ||
692 | dprintk(DBGLVL_API, " PAL-I\n"); | ||
693 | saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */ | ||
694 | agc_disable = 0; | ||
695 | } else if (std & V4L2_STD_PAL_M) { | ||
696 | dprintk(DBGLVL_API, " PAL-M\n"); | ||
697 | saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ | ||
698 | agc_disable = 0; | ||
699 | } else if (std & V4L2_STD_PAL_N) { | ||
700 | dprintk(DBGLVL_API, " PAL-N\n"); | ||
701 | saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ | ||
702 | agc_disable = 0; | ||
703 | } else if (std & V4L2_STD_PAL_Nc) { | ||
704 | dprintk(DBGLVL_API, " PAL-Nc\n"); | ||
705 | saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ | ||
706 | agc_disable = 0; | ||
707 | } else if (std & V4L2_STD_PAL_B) { | ||
708 | dprintk(DBGLVL_API, " PAL-B\n"); | ||
709 | saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */ | ||
710 | agc_disable = 0; | ||
711 | } else if (std & V4L2_STD_PAL_DK) { | ||
712 | dprintk(DBGLVL_API, " PAL-DK\n"); | ||
713 | saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */ | ||
714 | agc_disable = 0; | ||
715 | } else if (std & V4L2_STD_SECAM_L) { | ||
716 | dprintk(DBGLVL_API, " SECAM-L\n"); | ||
717 | saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */ | ||
718 | agc_disable = 0; | ||
719 | } else { | ||
720 | /* Unknown standard, assume DTV */ | ||
721 | dprintk(DBGLVL_API, " Unknown (assuming DTV)\n"); | ||
722 | /* Undefinded Video Standard */ | ||
723 | saa7164_api_set_dif(port, 0x00, 0x80); | ||
724 | agc_disable = 1; | ||
725 | } | ||
726 | |||
727 | saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */ | ||
728 | saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */ | ||
729 | saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */ | ||
730 | saa7164_api_set_dif(port, 0x04, 0x01); /* Active */ | ||
731 | msleep(100); | ||
732 | saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */ | ||
733 | msleep(100); | ||
734 | |||
735 | return ret; | ||
736 | } | ||
737 | |||
738 | /* Ensure the dif is in the correct state for the operating mode | ||
739 | * (analog / dtv). We only configure the diff through the analog encoder | ||
740 | * so when we're in digital mode we need to find the appropriate encoder | ||
741 | * and use it to configure the DIF. | ||
742 | */ | ||
743 | int saa7164_api_initialize_dif(struct saa7164_port *port) | ||
744 | { | ||
745 | struct saa7164_dev *dev = port->dev; | ||
746 | struct saa7164_port *p = NULL; | ||
747 | int ret = -EINVAL; | ||
748 | u32 std = 0; | ||
749 | |||
750 | dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__, | ||
751 | port->nr, port->type); | ||
752 | |||
753 | if (port->type == SAA7164_MPEG_ENCODER) { | ||
754 | /* Pick any analog standard to init the diff. | ||
755 | * we'll come back during encoder_init' | ||
756 | * and set the correct standard if requried. | ||
757 | */ | ||
758 | std = V4L2_STD_NTSC; | ||
759 | } else | ||
760 | if (port->type == SAA7164_MPEG_DVB) { | ||
761 | if (port->nr == SAA7164_PORT_TS1) | ||
762 | p = &dev->ports[SAA7164_PORT_ENC1]; | ||
763 | else | ||
764 | p = &dev->ports[SAA7164_PORT_ENC2]; | ||
765 | } else | ||
766 | if (port->type == SAA7164_MPEG_VBI) { | ||
767 | std = V4L2_STD_NTSC; | ||
768 | if (port->nr == SAA7164_PORT_VBI1) | ||
769 | p = &dev->ports[SAA7164_PORT_ENC1]; | ||
770 | else | ||
771 | p = &dev->ports[SAA7164_PORT_ENC2]; | ||
772 | } else | ||
773 | BUG(); | ||
774 | |||
775 | if (p) | ||
776 | ret = saa7164_api_configure_dif(p, std); | ||
777 | |||
778 | return ret; | ||
779 | } | ||
780 | |||
781 | int saa7164_api_transition_port(struct saa7164_port *port, u8 mode) | ||
782 | { | ||
783 | struct saa7164_dev *dev = port->dev; | ||
784 | |||
785 | int ret; | ||
786 | |||
787 | dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n", | ||
788 | __func__, port->nr, port->hwcfg.unitid, mode); | ||
789 | |||
790 | ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR, | ||
791 | SAA_STATE_CONTROL, sizeof(mode), &mode); | ||
792 | if (ret != SAA_OK) | ||
793 | printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n", | ||
794 | __func__, port->nr, port->hwcfg.unitid, ret); | ||
795 | |||
796 | return ret; | ||
797 | } | ||
798 | |||
799 | int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version) | ||
800 | { | ||
801 | int ret; | ||
802 | |||
803 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
804 | GET_FW_VERSION_CONTROL, sizeof(u32), version); | ||
805 | if (ret != SAA_OK) | ||
806 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
807 | |||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen) | ||
812 | { | ||
813 | u8 reg[] = { 0x0f, 0x00 }; | ||
814 | |||
815 | if (buflen < 128) | ||
816 | return -ENOMEM; | ||
817 | |||
818 | /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */ | ||
819 | /* TODO: Pull the details from the boards struct */ | ||
820 | return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg), | ||
821 | ®[0], 128, buf); | ||
822 | } | ||
823 | |||
824 | int saa7164_api_configure_port_vbi(struct saa7164_dev *dev, | ||
825 | struct saa7164_port *port) | ||
826 | { | ||
827 | struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc; | ||
828 | |||
829 | dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex); | ||
830 | dprintk(DBGLVL_API, " VideoStandard = 0x%x\n", fmt->VideoStandard); | ||
831 | dprintk(DBGLVL_API, " StartLine = %d\n", fmt->StartLine); | ||
832 | dprintk(DBGLVL_API, " EndLine = %d\n", fmt->EndLine); | ||
833 | dprintk(DBGLVL_API, " FieldRate = %d\n", fmt->FieldRate); | ||
834 | dprintk(DBGLVL_API, " bNumLines = %d\n", fmt->bNumLines); | ||
835 | |||
836 | /* Cache the hardware configuration in the port */ | ||
837 | |||
838 | port->bufcounter = port->hwcfg.BARLocation; | ||
839 | port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); | ||
840 | port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); | ||
841 | port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); | ||
842 | port->bufptr32l = port->hwcfg.BARLocation + | ||
843 | (4 * sizeof(u32)) + | ||
844 | (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); | ||
845 | port->bufptr32h = port->hwcfg.BARLocation + | ||
846 | (4 * sizeof(u32)) + | ||
847 | (sizeof(u32) * port->hwcfg.buffercount); | ||
848 | port->bufptr64 = port->hwcfg.BARLocation + | ||
849 | (4 * sizeof(u32)) + | ||
850 | (sizeof(u32) * port->hwcfg.buffercount); | ||
851 | dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", | ||
852 | port->hwcfg.BARLocation); | ||
853 | |||
854 | dprintk(DBGLVL_API, " = VS_FORMAT_VBI (becomes dev->en[%d])\n", | ||
855 | port->nr); | ||
856 | |||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev, | ||
861 | struct saa7164_port *port, | ||
862 | struct tmComResTSFormatDescrHeader *tsfmt) | ||
863 | { | ||
864 | dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex); | ||
865 | dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset); | ||
866 | dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength); | ||
867 | dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength); | ||
868 | dprintk(DBGLVL_API, " bguid = (....)\n"); | ||
869 | |||
870 | /* Cache the hardware configuration in the port */ | ||
871 | |||
872 | port->bufcounter = port->hwcfg.BARLocation; | ||
873 | port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); | ||
874 | port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); | ||
875 | port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); | ||
876 | port->bufptr32l = port->hwcfg.BARLocation + | ||
877 | (4 * sizeof(u32)) + | ||
878 | (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); | ||
879 | port->bufptr32h = port->hwcfg.BARLocation + | ||
880 | (4 * sizeof(u32)) + | ||
881 | (sizeof(u32) * port->hwcfg.buffercount); | ||
882 | port->bufptr64 = port->hwcfg.BARLocation + | ||
883 | (4 * sizeof(u32)) + | ||
884 | (sizeof(u32) * port->hwcfg.buffercount); | ||
885 | dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", | ||
886 | port->hwcfg.BARLocation); | ||
887 | |||
888 | dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n", | ||
889 | port->nr); | ||
890 | |||
891 | return 0; | ||
892 | } | ||
893 | |||
894 | int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev, | ||
895 | struct saa7164_port *port, | ||
896 | struct tmComResPSFormatDescrHeader *fmt) | ||
897 | { | ||
898 | dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex); | ||
899 | dprintk(DBGLVL_API, " wPacketLength= 0x%x\n", fmt->wPacketLength); | ||
900 | dprintk(DBGLVL_API, " wPackLength= 0x%x\n", fmt->wPackLength); | ||
901 | dprintk(DBGLVL_API, " bPackDataType= 0x%x\n", fmt->bPackDataType); | ||
902 | |||
903 | /* Cache the hardware configuration in the port */ | ||
904 | /* TODO: CHECK THIS in the port config */ | ||
905 | port->bufcounter = port->hwcfg.BARLocation; | ||
906 | port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); | ||
907 | port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); | ||
908 | port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); | ||
909 | port->bufptr32l = port->hwcfg.BARLocation + | ||
910 | (4 * sizeof(u32)) + | ||
911 | (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); | ||
912 | port->bufptr32h = port->hwcfg.BARLocation + | ||
913 | (4 * sizeof(u32)) + | ||
914 | (sizeof(u32) * port->hwcfg.buffercount); | ||
915 | port->bufptr64 = port->hwcfg.BARLocation + | ||
916 | (4 * sizeof(u32)) + | ||
917 | (sizeof(u32) * port->hwcfg.buffercount); | ||
918 | dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", | ||
919 | port->hwcfg.BARLocation); | ||
920 | |||
921 | dprintk(DBGLVL_API, " = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n", | ||
922 | port->nr); | ||
923 | |||
924 | return 0; | ||
925 | } | ||
926 | |||
927 | int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len) | ||
928 | { | ||
929 | struct saa7164_port *tsport = NULL; | ||
930 | struct saa7164_port *encport = NULL; | ||
931 | struct saa7164_port *vbiport = NULL; | ||
932 | u32 idx, next_offset; | ||
933 | int i; | ||
934 | struct tmComResDescrHeader *hdr, *t; | ||
935 | struct tmComResExtDevDescrHeader *exthdr; | ||
936 | struct tmComResPathDescrHeader *pathhdr; | ||
937 | struct tmComResAntTermDescrHeader *anttermhdr; | ||
938 | struct tmComResTunerDescrHeader *tunerunithdr; | ||
939 | struct tmComResDMATermDescrHeader *vcoutputtermhdr; | ||
940 | struct tmComResTSFormatDescrHeader *tsfmt; | ||
941 | struct tmComResPSFormatDescrHeader *psfmt; | ||
942 | struct tmComResSelDescrHeader *psel; | ||
943 | struct tmComResProcDescrHeader *pdh; | ||
944 | struct tmComResAFeatureDescrHeader *afd; | ||
945 | struct tmComResEncoderDescrHeader *edh; | ||
946 | struct tmComResVBIFormatDescrHeader *vbifmt; | ||
947 | u32 currpath = 0; | ||
948 | |||
949 | dprintk(DBGLVL_API, | ||
950 | "%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n", | ||
951 | __func__, len, (u32)sizeof(struct tmComResDescrHeader)); | ||
952 | |||
953 | for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) { | ||
954 | |||
955 | hdr = (struct tmComResDescrHeader *)(buf + idx); | ||
956 | |||
957 | if (hdr->type != CS_INTERFACE) | ||
958 | return SAA_ERR_NOT_SUPPORTED; | ||
959 | |||
960 | dprintk(DBGLVL_API, "@ 0x%x =\n", idx); | ||
961 | switch (hdr->subtype) { | ||
962 | case GENERAL_REQUEST: | ||
963 | dprintk(DBGLVL_API, " GENERAL_REQUEST\n"); | ||
964 | break; | ||
965 | case VC_TUNER_PATH: | ||
966 | dprintk(DBGLVL_API, " VC_TUNER_PATH\n"); | ||
967 | pathhdr = (struct tmComResPathDescrHeader *)(buf + idx); | ||
968 | dprintk(DBGLVL_API, " pathid = 0x%x\n", | ||
969 | pathhdr->pathid); | ||
970 | currpath = pathhdr->pathid; | ||
971 | break; | ||
972 | case VC_INPUT_TERMINAL: | ||
973 | dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n"); | ||
974 | anttermhdr = | ||
975 | (struct tmComResAntTermDescrHeader *)(buf + idx); | ||
976 | dprintk(DBGLVL_API, " terminalid = 0x%x\n", | ||
977 | anttermhdr->terminalid); | ||
978 | dprintk(DBGLVL_API, " terminaltype = 0x%x\n", | ||
979 | anttermhdr->terminaltype); | ||
980 | switch (anttermhdr->terminaltype) { | ||
981 | case ITT_ANTENNA: | ||
982 | dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); | ||
983 | break; | ||
984 | case LINE_CONNECTOR: | ||
985 | dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); | ||
986 | break; | ||
987 | case SPDIF_CONNECTOR: | ||
988 | dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); | ||
989 | break; | ||
990 | case COMPOSITE_CONNECTOR: | ||
991 | dprintk(DBGLVL_API, | ||
992 | " = COMPOSITE_CONNECTOR\n"); | ||
993 | break; | ||
994 | case SVIDEO_CONNECTOR: | ||
995 | dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); | ||
996 | break; | ||
997 | case COMPONENT_CONNECTOR: | ||
998 | dprintk(DBGLVL_API, | ||
999 | " = COMPONENT_CONNECTOR\n"); | ||
1000 | break; | ||
1001 | case STANDARD_DMA: | ||
1002 | dprintk(DBGLVL_API, " = STANDARD_DMA\n"); | ||
1003 | break; | ||
1004 | default: | ||
1005 | dprintk(DBGLVL_API, " = undefined (0x%x)\n", | ||
1006 | anttermhdr->terminaltype); | ||
1007 | } | ||
1008 | dprintk(DBGLVL_API, " assocterminal= 0x%x\n", | ||
1009 | anttermhdr->assocterminal); | ||
1010 | dprintk(DBGLVL_API, " iterminal = 0x%x\n", | ||
1011 | anttermhdr->iterminal); | ||
1012 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
1013 | anttermhdr->controlsize); | ||
1014 | break; | ||
1015 | case VC_OUTPUT_TERMINAL: | ||
1016 | dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n"); | ||
1017 | vcoutputtermhdr = | ||
1018 | (struct tmComResDMATermDescrHeader *)(buf + idx); | ||
1019 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
1020 | vcoutputtermhdr->unitid); | ||
1021 | dprintk(DBGLVL_API, " terminaltype = 0x%x\n", | ||
1022 | vcoutputtermhdr->terminaltype); | ||
1023 | switch (vcoutputtermhdr->terminaltype) { | ||
1024 | case ITT_ANTENNA: | ||
1025 | dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); | ||
1026 | break; | ||
1027 | case LINE_CONNECTOR: | ||
1028 | dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); | ||
1029 | break; | ||
1030 | case SPDIF_CONNECTOR: | ||
1031 | dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); | ||
1032 | break; | ||
1033 | case COMPOSITE_CONNECTOR: | ||
1034 | dprintk(DBGLVL_API, | ||
1035 | " = COMPOSITE_CONNECTOR\n"); | ||
1036 | break; | ||
1037 | case SVIDEO_CONNECTOR: | ||
1038 | dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); | ||
1039 | break; | ||
1040 | case COMPONENT_CONNECTOR: | ||
1041 | dprintk(DBGLVL_API, | ||
1042 | " = COMPONENT_CONNECTOR\n"); | ||
1043 | break; | ||
1044 | case STANDARD_DMA: | ||
1045 | dprintk(DBGLVL_API, " = STANDARD_DMA\n"); | ||
1046 | break; | ||
1047 | default: | ||
1048 | dprintk(DBGLVL_API, " = undefined (0x%x)\n", | ||
1049 | vcoutputtermhdr->terminaltype); | ||
1050 | } | ||
1051 | dprintk(DBGLVL_API, " assocterminal= 0x%x\n", | ||
1052 | vcoutputtermhdr->assocterminal); | ||
1053 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
1054 | vcoutputtermhdr->sourceid); | ||
1055 | dprintk(DBGLVL_API, " iterminal = 0x%x\n", | ||
1056 | vcoutputtermhdr->iterminal); | ||
1057 | dprintk(DBGLVL_API, " BARLocation = 0x%x\n", | ||
1058 | vcoutputtermhdr->BARLocation); | ||
1059 | dprintk(DBGLVL_API, " flags = 0x%x\n", | ||
1060 | vcoutputtermhdr->flags); | ||
1061 | dprintk(DBGLVL_API, " interruptid = 0x%x\n", | ||
1062 | vcoutputtermhdr->interruptid); | ||
1063 | dprintk(DBGLVL_API, " buffercount = 0x%x\n", | ||
1064 | vcoutputtermhdr->buffercount); | ||
1065 | dprintk(DBGLVL_API, " metadatasize = 0x%x\n", | ||
1066 | vcoutputtermhdr->metadatasize); | ||
1067 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
1068 | vcoutputtermhdr->controlsize); | ||
1069 | dprintk(DBGLVL_API, " numformats = 0x%x\n", | ||
1070 | vcoutputtermhdr->numformats); | ||
1071 | |||
1072 | t = (struct tmComResDescrHeader *) | ||
1073 | ((struct tmComResDMATermDescrHeader *)(buf + idx)); | ||
1074 | next_offset = idx + (vcoutputtermhdr->len); | ||
1075 | for (i = 0; i < vcoutputtermhdr->numformats; i++) { | ||
1076 | t = (struct tmComResDescrHeader *) | ||
1077 | (buf + next_offset); | ||
1078 | switch (t->subtype) { | ||
1079 | case VS_FORMAT_MPEG2TS: | ||
1080 | tsfmt = | ||
1081 | (struct tmComResTSFormatDescrHeader *)t; | ||
1082 | if (currpath == 1) | ||
1083 | tsport = &dev->ports[SAA7164_PORT_TS1]; | ||
1084 | else | ||
1085 | tsport = &dev->ports[SAA7164_PORT_TS2]; | ||
1086 | memcpy(&tsport->hwcfg, vcoutputtermhdr, | ||
1087 | sizeof(*vcoutputtermhdr)); | ||
1088 | saa7164_api_configure_port_mpeg2ts(dev, | ||
1089 | tsport, tsfmt); | ||
1090 | break; | ||
1091 | case VS_FORMAT_MPEG2PS: | ||
1092 | psfmt = | ||
1093 | (struct tmComResPSFormatDescrHeader *)t; | ||
1094 | if (currpath == 1) | ||
1095 | encport = &dev->ports[SAA7164_PORT_ENC1]; | ||
1096 | else | ||
1097 | encport = &dev->ports[SAA7164_PORT_ENC2]; | ||
1098 | memcpy(&encport->hwcfg, vcoutputtermhdr, | ||
1099 | sizeof(*vcoutputtermhdr)); | ||
1100 | saa7164_api_configure_port_mpeg2ps(dev, | ||
1101 | encport, psfmt); | ||
1102 | break; | ||
1103 | case VS_FORMAT_VBI: | ||
1104 | vbifmt = | ||
1105 | (struct tmComResVBIFormatDescrHeader *)t; | ||
1106 | if (currpath == 1) | ||
1107 | vbiport = &dev->ports[SAA7164_PORT_VBI1]; | ||
1108 | else | ||
1109 | vbiport = &dev->ports[SAA7164_PORT_VBI2]; | ||
1110 | memcpy(&vbiport->hwcfg, vcoutputtermhdr, | ||
1111 | sizeof(*vcoutputtermhdr)); | ||
1112 | memcpy(&vbiport->vbi_fmt_ntsc, vbifmt, | ||
1113 | sizeof(*vbifmt)); | ||
1114 | saa7164_api_configure_port_vbi(dev, | ||
1115 | vbiport); | ||
1116 | break; | ||
1117 | case VS_FORMAT_RDS: | ||
1118 | dprintk(DBGLVL_API, | ||
1119 | " = VS_FORMAT_RDS\n"); | ||
1120 | break; | ||
1121 | case VS_FORMAT_UNCOMPRESSED: | ||
1122 | dprintk(DBGLVL_API, | ||
1123 | " = VS_FORMAT_UNCOMPRESSED\n"); | ||
1124 | break; | ||
1125 | case VS_FORMAT_TYPE: | ||
1126 | dprintk(DBGLVL_API, | ||
1127 | " = VS_FORMAT_TYPE\n"); | ||
1128 | break; | ||
1129 | default: | ||
1130 | dprintk(DBGLVL_API, | ||
1131 | " = undefined (0x%x)\n", | ||
1132 | t->subtype); | ||
1133 | } | ||
1134 | next_offset += t->len; | ||
1135 | } | ||
1136 | |||
1137 | break; | ||
1138 | case TUNER_UNIT: | ||
1139 | dprintk(DBGLVL_API, " TUNER_UNIT\n"); | ||
1140 | tunerunithdr = | ||
1141 | (struct tmComResTunerDescrHeader *)(buf + idx); | ||
1142 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
1143 | tunerunithdr->unitid); | ||
1144 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
1145 | tunerunithdr->sourceid); | ||
1146 | dprintk(DBGLVL_API, " iunit = 0x%x\n", | ||
1147 | tunerunithdr->iunit); | ||
1148 | dprintk(DBGLVL_API, " tuningstandards = 0x%x\n", | ||
1149 | tunerunithdr->tuningstandards); | ||
1150 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
1151 | tunerunithdr->controlsize); | ||
1152 | dprintk(DBGLVL_API, " controls = 0x%x\n", | ||
1153 | tunerunithdr->controls); | ||
1154 | |||
1155 | if (tunerunithdr->unitid == tunerunithdr->iunit) { | ||
1156 | if (currpath == 1) | ||
1157 | encport = &dev->ports[SAA7164_PORT_ENC1]; | ||
1158 | else | ||
1159 | encport = &dev->ports[SAA7164_PORT_ENC2]; | ||
1160 | memcpy(&encport->tunerunit, tunerunithdr, | ||
1161 | sizeof(struct tmComResTunerDescrHeader)); | ||
1162 | dprintk(DBGLVL_API, | ||
1163 | " (becomes dev->enc[%d] tuner)\n", | ||
1164 | encport->nr); | ||
1165 | } | ||
1166 | break; | ||
1167 | case VC_SELECTOR_UNIT: | ||
1168 | psel = (struct tmComResSelDescrHeader *)(buf + idx); | ||
1169 | dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n"); | ||
1170 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
1171 | psel->unitid); | ||
1172 | dprintk(DBGLVL_API, " nrinpins = 0x%x\n", | ||
1173 | psel->nrinpins); | ||
1174 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
1175 | psel->sourceid); | ||
1176 | break; | ||
1177 | case VC_PROCESSING_UNIT: | ||
1178 | pdh = (struct tmComResProcDescrHeader *)(buf + idx); | ||
1179 | dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n"); | ||
1180 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
1181 | pdh->unitid); | ||
1182 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
1183 | pdh->sourceid); | ||
1184 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
1185 | pdh->controlsize); | ||
1186 | if (pdh->controlsize == 0x04) { | ||
1187 | if (currpath == 1) | ||
1188 | encport = &dev->ports[SAA7164_PORT_ENC1]; | ||
1189 | else | ||
1190 | encport = &dev->ports[SAA7164_PORT_ENC2]; | ||
1191 | memcpy(&encport->vidproc, pdh, | ||
1192 | sizeof(struct tmComResProcDescrHeader)); | ||
1193 | dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n", | ||
1194 | encport->nr); | ||
1195 | } | ||
1196 | break; | ||
1197 | case FEATURE_UNIT: | ||
1198 | afd = (struct tmComResAFeatureDescrHeader *)(buf + idx); | ||
1199 | dprintk(DBGLVL_API, " FEATURE_UNIT\n"); | ||
1200 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
1201 | afd->unitid); | ||
1202 | dprintk(DBGLVL_API, " sourceid = 0x%x\n", | ||
1203 | afd->sourceid); | ||
1204 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
1205 | afd->controlsize); | ||
1206 | if (currpath == 1) | ||
1207 | encport = &dev->ports[SAA7164_PORT_ENC1]; | ||
1208 | else | ||
1209 | encport = &dev->ports[SAA7164_PORT_ENC2]; | ||
1210 | memcpy(&encport->audfeat, afd, | ||
1211 | sizeof(struct tmComResAFeatureDescrHeader)); | ||
1212 | dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n", | ||
1213 | encport->nr); | ||
1214 | break; | ||
1215 | case ENCODER_UNIT: | ||
1216 | edh = (struct tmComResEncoderDescrHeader *)(buf + idx); | ||
1217 | dprintk(DBGLVL_API, " ENCODER_UNIT\n"); | ||
1218 | dprintk(DBGLVL_API, " subtype = 0x%x\n", edh->subtype); | ||
1219 | dprintk(DBGLVL_API, " unitid = 0x%x\n", edh->unitid); | ||
1220 | dprintk(DBGLVL_API, " vsourceid = 0x%x\n", | ||
1221 | edh->vsourceid); | ||
1222 | dprintk(DBGLVL_API, " asourceid = 0x%x\n", | ||
1223 | edh->asourceid); | ||
1224 | dprintk(DBGLVL_API, " iunit = 0x%x\n", edh->iunit); | ||
1225 | if (edh->iunit == edh->unitid) { | ||
1226 | if (currpath == 1) | ||
1227 | encport = &dev->ports[SAA7164_PORT_ENC1]; | ||
1228 | else | ||
1229 | encport = &dev->ports[SAA7164_PORT_ENC2]; | ||
1230 | memcpy(&encport->encunit, edh, | ||
1231 | sizeof(struct tmComResEncoderDescrHeader)); | ||
1232 | dprintk(DBGLVL_API, | ||
1233 | " (becomes dev->enc[%d])\n", | ||
1234 | encport->nr); | ||
1235 | } | ||
1236 | break; | ||
1237 | case EXTENSION_UNIT: | ||
1238 | dprintk(DBGLVL_API, " EXTENSION_UNIT\n"); | ||
1239 | exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx); | ||
1240 | dprintk(DBGLVL_API, " unitid = 0x%x\n", | ||
1241 | exthdr->unitid); | ||
1242 | dprintk(DBGLVL_API, " deviceid = 0x%x\n", | ||
1243 | exthdr->deviceid); | ||
1244 | dprintk(DBGLVL_API, " devicetype = 0x%x\n", | ||
1245 | exthdr->devicetype); | ||
1246 | if (exthdr->devicetype & 0x1) | ||
1247 | dprintk(DBGLVL_API, " = Decoder Device\n"); | ||
1248 | if (exthdr->devicetype & 0x2) | ||
1249 | dprintk(DBGLVL_API, " = GPIO Source\n"); | ||
1250 | if (exthdr->devicetype & 0x4) | ||
1251 | dprintk(DBGLVL_API, " = Video Decoder\n"); | ||
1252 | if (exthdr->devicetype & 0x8) | ||
1253 | dprintk(DBGLVL_API, " = Audio Decoder\n"); | ||
1254 | if (exthdr->devicetype & 0x20) | ||
1255 | dprintk(DBGLVL_API, " = Crossbar\n"); | ||
1256 | if (exthdr->devicetype & 0x40) | ||
1257 | dprintk(DBGLVL_API, " = Tuner\n"); | ||
1258 | if (exthdr->devicetype & 0x80) | ||
1259 | dprintk(DBGLVL_API, " = IF PLL\n"); | ||
1260 | if (exthdr->devicetype & 0x100) | ||
1261 | dprintk(DBGLVL_API, " = Demodulator\n"); | ||
1262 | if (exthdr->devicetype & 0x200) | ||
1263 | dprintk(DBGLVL_API, " = RDS Decoder\n"); | ||
1264 | if (exthdr->devicetype & 0x400) | ||
1265 | dprintk(DBGLVL_API, " = Encoder\n"); | ||
1266 | if (exthdr->devicetype & 0x800) | ||
1267 | dprintk(DBGLVL_API, " = IR Decoder\n"); | ||
1268 | if (exthdr->devicetype & 0x1000) | ||
1269 | dprintk(DBGLVL_API, " = EEPROM\n"); | ||
1270 | if (exthdr->devicetype & 0x2000) | ||
1271 | dprintk(DBGLVL_API, | ||
1272 | " = VBI Decoder\n"); | ||
1273 | if (exthdr->devicetype & 0x10000) | ||
1274 | dprintk(DBGLVL_API, | ||
1275 | " = Streaming Device\n"); | ||
1276 | if (exthdr->devicetype & 0x20000) | ||
1277 | dprintk(DBGLVL_API, | ||
1278 | " = DRM Device\n"); | ||
1279 | if (exthdr->devicetype & 0x40000000) | ||
1280 | dprintk(DBGLVL_API, | ||
1281 | " = Generic Device\n"); | ||
1282 | if (exthdr->devicetype & 0x80000000) | ||
1283 | dprintk(DBGLVL_API, | ||
1284 | " = Config Space Device\n"); | ||
1285 | dprintk(DBGLVL_API, " numgpiopins = 0x%x\n", | ||
1286 | exthdr->numgpiopins); | ||
1287 | dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n", | ||
1288 | exthdr->numgpiogroups); | ||
1289 | dprintk(DBGLVL_API, " controlsize = 0x%x\n", | ||
1290 | exthdr->controlsize); | ||
1291 | if (exthdr->devicetype & 0x80) { | ||
1292 | if (currpath == 1) | ||
1293 | encport = &dev->ports[SAA7164_PORT_ENC1]; | ||
1294 | else | ||
1295 | encport = &dev->ports[SAA7164_PORT_ENC2]; | ||
1296 | memcpy(&encport->ifunit, exthdr, | ||
1297 | sizeof(struct tmComResExtDevDescrHeader)); | ||
1298 | dprintk(DBGLVL_API, | ||
1299 | " (becomes dev->enc[%d])\n", | ||
1300 | encport->nr); | ||
1301 | } | ||
1302 | break; | ||
1303 | case PVC_INFRARED_UNIT: | ||
1304 | dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n"); | ||
1305 | break; | ||
1306 | case DRM_UNIT: | ||
1307 | dprintk(DBGLVL_API, " DRM_UNIT\n"); | ||
1308 | break; | ||
1309 | default: | ||
1310 | dprintk(DBGLVL_API, "default %d\n", hdr->subtype); | ||
1311 | } | ||
1312 | |||
1313 | dprintk(DBGLVL_API, " 1.%x\n", hdr->len); | ||
1314 | dprintk(DBGLVL_API, " 2.%x\n", hdr->type); | ||
1315 | dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype); | ||
1316 | dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid); | ||
1317 | |||
1318 | idx += hdr->len; | ||
1319 | } | ||
1320 | |||
1321 | return 0; | ||
1322 | } | ||
1323 | |||
1324 | int saa7164_api_enum_subdevs(struct saa7164_dev *dev) | ||
1325 | { | ||
1326 | int ret; | ||
1327 | u32 buflen = 0; | ||
1328 | u8 *buf; | ||
1329 | |||
1330 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
1331 | |||
1332 | /* Get the total descriptor length */ | ||
1333 | ret = saa7164_cmd_send(dev, 0, GET_LEN, | ||
1334 | GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen); | ||
1335 | if (ret != SAA_OK) | ||
1336 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
1337 | |||
1338 | dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n", | ||
1339 | __func__, buflen); | ||
1340 | |||
1341 | /* Allocate enough storage for all of the descs */ | ||
1342 | buf = kzalloc(buflen, GFP_KERNEL); | ||
1343 | if (!buf) | ||
1344 | return SAA_ERR_NO_RESOURCES; | ||
1345 | |||
1346 | /* Retrieve them */ | ||
1347 | ret = saa7164_cmd_send(dev, 0, GET_CUR, | ||
1348 | GET_DESCRIPTORS_CONTROL, buflen, buf); | ||
1349 | if (ret != SAA_OK) { | ||
1350 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); | ||
1351 | goto out; | ||
1352 | } | ||
1353 | |||
1354 | if (saa_debug & DBGLVL_API) | ||
1355 | saa7164_dumphex16(dev, buf, (buflen/16)*16); | ||
1356 | |||
1357 | saa7164_api_dump_subdevs(dev, buf, buflen); | ||
1358 | |||
1359 | out: | ||
1360 | kfree(buf); | ||
1361 | return ret; | ||
1362 | } | ||
1363 | |||
1364 | int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, | ||
1365 | u32 datalen, u8 *data) | ||
1366 | { | ||
1367 | struct saa7164_dev *dev = bus->dev; | ||
1368 | u16 len = 0; | ||
1369 | int unitid; | ||
1370 | u32 regval; | ||
1371 | u8 buf[256]; | ||
1372 | int ret; | ||
1373 | |||
1374 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
1375 | |||
1376 | if (reglen > 4) | ||
1377 | return -EIO; | ||
1378 | |||
1379 | if (reglen == 1) | ||
1380 | regval = *(reg); | ||
1381 | else | ||
1382 | if (reglen == 2) | ||
1383 | regval = ((*(reg) << 8) || *(reg+1)); | ||
1384 | else | ||
1385 | if (reglen == 3) | ||
1386 | regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2)); | ||
1387 | else | ||
1388 | if (reglen == 4) | ||
1389 | regval = ((*(reg) << 24) | (*(reg+1) << 16) | | ||
1390 | (*(reg+2) << 8) | *(reg+3)); | ||
1391 | |||
1392 | /* Prepare the send buffer */ | ||
1393 | /* Bytes 00-03 source register length | ||
1394 | * 04-07 source bytes to read | ||
1395 | * 08... register address | ||
1396 | */ | ||
1397 | memset(buf, 0, sizeof(buf)); | ||
1398 | memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen); | ||
1399 | *((u32 *)(buf + 0 * sizeof(u32))) = reglen; | ||
1400 | *((u32 *)(buf + 1 * sizeof(u32))) = datalen; | ||
1401 | |||
1402 | unitid = saa7164_i2caddr_to_unitid(bus, addr); | ||
1403 | if (unitid < 0) { | ||
1404 | printk(KERN_ERR | ||
1405 | "%s() error, cannot translate regaddr 0x%x to unitid\n", | ||
1406 | __func__, addr); | ||
1407 | return -EIO; | ||
1408 | } | ||
1409 | |||
1410 | ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, | ||
1411 | EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); | ||
1412 | if (ret != SAA_OK) { | ||
1413 | printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); | ||
1414 | return -EIO; | ||
1415 | } | ||
1416 | |||
1417 | dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); | ||
1418 | |||
1419 | if (saa_debug & DBGLVL_I2C) | ||
1420 | saa7164_dumphex16(dev, buf, 2 * 16); | ||
1421 | |||
1422 | ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR, | ||
1423 | EXU_REGISTER_ACCESS_CONTROL, len, &buf); | ||
1424 | if (ret != SAA_OK) | ||
1425 | printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); | ||
1426 | else { | ||
1427 | if (saa_debug & DBGLVL_I2C) | ||
1428 | saa7164_dumphex16(dev, buf, sizeof(buf)); | ||
1429 | memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen); | ||
1430 | } | ||
1431 | |||
1432 | return ret == SAA_OK ? 0 : -EIO; | ||
1433 | } | ||
1434 | |||
1435 | /* For a given 8 bit i2c address device, write the buffer */ | ||
1436 | int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, | ||
1437 | u8 *data) | ||
1438 | { | ||
1439 | struct saa7164_dev *dev = bus->dev; | ||
1440 | u16 len = 0; | ||
1441 | int unitid; | ||
1442 | int reglen; | ||
1443 | u8 buf[256]; | ||
1444 | int ret; | ||
1445 | |||
1446 | dprintk(DBGLVL_API, "%s()\n", __func__); | ||
1447 | |||
1448 | if ((datalen == 0) || (datalen > 232)) | ||
1449 | return -EIO; | ||
1450 | |||
1451 | memset(buf, 0, sizeof(buf)); | ||
1452 | |||
1453 | unitid = saa7164_i2caddr_to_unitid(bus, addr); | ||
1454 | if (unitid < 0) { | ||
1455 | printk(KERN_ERR | ||
1456 | "%s() error, cannot translate regaddr 0x%x to unitid\n", | ||
1457 | __func__, addr); | ||
1458 | return -EIO; | ||
1459 | } | ||
1460 | |||
1461 | reglen = saa7164_i2caddr_to_reglen(bus, addr); | ||
1462 | if (reglen < 0) { | ||
1463 | printk(KERN_ERR | ||
1464 | "%s() error, cannot translate regaddr to reglen\n", | ||
1465 | __func__); | ||
1466 | return -EIO; | ||
1467 | } | ||
1468 | |||
1469 | ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, | ||
1470 | EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); | ||
1471 | if (ret != SAA_OK) { | ||
1472 | printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); | ||
1473 | return -EIO; | ||
1474 | } | ||
1475 | |||
1476 | dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); | ||
1477 | |||
1478 | /* Prepare the send buffer */ | ||
1479 | /* Bytes 00-03 dest register length | ||
1480 | * 04-07 dest bytes to write | ||
1481 | * 08... register address | ||
1482 | */ | ||
1483 | *((u32 *)(buf + 0 * sizeof(u32))) = reglen; | ||
1484 | *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen; | ||
1485 | memcpy((buf + 2 * sizeof(u32)), data, datalen); | ||
1486 | |||
1487 | if (saa_debug & DBGLVL_I2C) | ||
1488 | saa7164_dumphex16(dev, buf, sizeof(buf)); | ||
1489 | |||
1490 | ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR, | ||
1491 | EXU_REGISTER_ACCESS_CONTROL, len, &buf); | ||
1492 | if (ret != SAA_OK) | ||
1493 | printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); | ||
1494 | |||
1495 | return ret == SAA_OK ? 0 : -EIO; | ||
1496 | } | ||
1497 | |||
1498 | int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid, | ||
1499 | u8 pin, u8 state) | ||
1500 | { | ||
1501 | int ret; | ||
1502 | struct tmComResGPIO t; | ||
1503 | |||
1504 | dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n", | ||
1505 | __func__, unitid, pin, state); | ||
1506 | |||
1507 | if ((pin > 7) || (state > 2)) | ||
1508 | return SAA_ERR_BAD_PARAMETER; | ||
1509 | |||
1510 | t.pin = pin; | ||
1511 | t.state = state; | ||
1512 | |||
1513 | ret = saa7164_cmd_send(dev, unitid, SET_CUR, | ||
1514 | EXU_GPIO_CONTROL, sizeof(t), &t); | ||
1515 | if (ret != SAA_OK) | ||
1516 | printk(KERN_ERR "%s() error, ret = 0x%x\n", | ||
1517 | __func__, ret); | ||
1518 | |||
1519 | return ret; | ||
1520 | } | ||
1521 | |||
1522 | int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, | ||
1523 | u8 pin) | ||
1524 | { | ||
1525 | return saa7164_api_modify_gpio(dev, unitid, pin, 1); | ||
1526 | } | ||
1527 | |||
1528 | int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, | ||
1529 | u8 pin) | ||
1530 | { | ||
1531 | return saa7164_api_modify_gpio(dev, unitid, pin, 0); | ||
1532 | } | ||
1533 | |||
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c new file mode 100644 index 00000000000..66696fa8341 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-buffer.c | |||
@@ -0,0 +1,322 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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/slab.h> | ||
23 | |||
24 | #include "saa7164.h" | ||
25 | |||
26 | /* The PCI address space for buffer handling looks like this: | ||
27 | * | ||
28 | * +-u32 wide-------------+ | ||
29 | * | + | ||
30 | * +-u64 wide------------------------------------+ | ||
31 | * + + | ||
32 | * +----------------------+ | ||
33 | * | CurrentBufferPtr + Pointer to current PCI buffer >-+ | ||
34 | * +----------------------+ | | ||
35 | * | Unused + | | ||
36 | * +----------------------+ | | ||
37 | * | Pitch + = 188 (bytes) | | ||
38 | * +----------------------+ | | ||
39 | * | PCI buffer size + = pitch * number of lines (312) | | ||
40 | * +----------------------+ | | ||
41 | * |0| Buf0 Write Offset + | | ||
42 | * +----------------------+ v | ||
43 | * |1| Buf1 Write Offset + | | ||
44 | * +----------------------+ | | ||
45 | * |2| Buf2 Write Offset + | | ||
46 | * +----------------------+ | | ||
47 | * |3| Buf3 Write Offset + | | ||
48 | * +----------------------+ | | ||
49 | * ... More write offsets | | ||
50 | * +---------------------------------------------+ | | ||
51 | * +0| set of ptrs to PCI pagetables + | | ||
52 | * +---------------------------------------------+ | | ||
53 | * +1| set of ptrs to PCI pagetables + <--------+ | ||
54 | * +---------------------------------------------+ | ||
55 | * +2| set of ptrs to PCI pagetables + | ||
56 | * +---------------------------------------------+ | ||
57 | * +3| set of ptrs to PCI pagetables + >--+ | ||
58 | * +---------------------------------------------+ | | ||
59 | * ... More buffer pointers | +----------------+ | ||
60 | * +->| pt[0] TS data | | ||
61 | * | +----------------+ | ||
62 | * | | ||
63 | * | +----------------+ | ||
64 | * +->| pt[1] TS data | | ||
65 | * | +----------------+ | ||
66 | * | etc | ||
67 | */ | ||
68 | |||
69 | void saa7164_buffer_display(struct saa7164_buffer *buf) | ||
70 | { | ||
71 | struct saa7164_dev *dev = buf->port->dev; | ||
72 | int i; | ||
73 | |||
74 | dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n", | ||
75 | __func__, buf, buf->idx); | ||
76 | dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n", | ||
77 | buf->cpu, (long long)buf->dma, buf->pci_size); | ||
78 | dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n", | ||
79 | buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size); | ||
80 | |||
81 | /* Format the Page Table Entries to point into the data buffer */ | ||
82 | for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { | ||
83 | |||
84 | dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", | ||
85 | i, buf->pt_cpu, (u64)*(buf->pt_cpu)); | ||
86 | |||
87 | } | ||
88 | } | ||
89 | /* Allocate a new buffer structure and associated PCI space in bytes. | ||
90 | * len must be a multiple of sizeof(u64) | ||
91 | */ | ||
92 | struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, | ||
93 | u32 len) | ||
94 | { | ||
95 | struct tmHWStreamParameters *params = &port->hw_streamingparams; | ||
96 | struct saa7164_buffer *buf = NULL; | ||
97 | struct saa7164_dev *dev = port->dev; | ||
98 | int i; | ||
99 | |||
100 | if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) { | ||
101 | log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__); | ||
102 | goto ret; | ||
103 | } | ||
104 | |||
105 | buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL); | ||
106 | if (!buf) { | ||
107 | log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__); | ||
108 | goto ret; | ||
109 | } | ||
110 | |||
111 | buf->idx = -1; | ||
112 | buf->port = port; | ||
113 | buf->flags = SAA7164_BUFFER_FREE; | ||
114 | buf->pos = 0; | ||
115 | buf->actual_size = params->pitch * params->numberoflines; | ||
116 | buf->crc = 0; | ||
117 | /* TODO: arg len is being ignored */ | ||
118 | buf->pci_size = SAA7164_PT_ENTRIES * 0x1000; | ||
119 | buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; | ||
120 | |||
121 | /* Allocate contiguous memory */ | ||
122 | buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, | ||
123 | &buf->dma); | ||
124 | if (!buf->cpu) | ||
125 | goto fail1; | ||
126 | |||
127 | buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, | ||
128 | &buf->pt_dma); | ||
129 | if (!buf->pt_cpu) | ||
130 | goto fail2; | ||
131 | |||
132 | /* init the buffers to a known pattern, easier during debugging */ | ||
133 | memset_io(buf->cpu, 0xff, buf->pci_size); | ||
134 | buf->crc = crc32(0, buf->cpu, buf->actual_size); | ||
135 | memset_io(buf->pt_cpu, 0xff, buf->pt_size); | ||
136 | |||
137 | dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n", | ||
138 | __func__, buf, params->numpagetables); | ||
139 | dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n", | ||
140 | buf->cpu, (long)buf->dma, buf->pci_size); | ||
141 | dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n", | ||
142 | buf->pt_cpu, (long)buf->pt_dma, buf->pt_size); | ||
143 | |||
144 | /* Format the Page Table Entries to point into the data buffer */ | ||
145 | for (i = 0 ; i < params->numpagetables; i++) { | ||
146 | |||
147 | *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ | ||
148 | dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", | ||
149 | i, buf->pt_cpu, (u64)*(buf->pt_cpu)); | ||
150 | |||
151 | } | ||
152 | |||
153 | goto ret; | ||
154 | |||
155 | fail2: | ||
156 | pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); | ||
157 | fail1: | ||
158 | kfree(buf); | ||
159 | |||
160 | buf = NULL; | ||
161 | ret: | ||
162 | return buf; | ||
163 | } | ||
164 | |||
165 | int saa7164_buffer_dealloc(struct saa7164_buffer *buf) | ||
166 | { | ||
167 | struct saa7164_dev *dev; | ||
168 | |||
169 | if (!buf || !buf->port) | ||
170 | return SAA_ERR_BAD_PARAMETER; | ||
171 | dev = buf->port->dev; | ||
172 | |||
173 | dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", | ||
174 | __func__, buf); | ||
175 | |||
176 | if (buf->flags != SAA7164_BUFFER_FREE) | ||
177 | log_warn(" freeing a non-free buffer\n"); | ||
178 | |||
179 | pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma); | ||
180 | pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma); | ||
181 | |||
182 | kfree(buf); | ||
183 | |||
184 | return SAA_OK; | ||
185 | } | ||
186 | |||
187 | int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i) | ||
188 | { | ||
189 | struct saa7164_dev *dev = port->dev; | ||
190 | |||
191 | if ((i < 0) || (i >= port->hwcfg.buffercount)) | ||
192 | return -EINVAL; | ||
193 | |||
194 | dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); | ||
195 | |||
196 | saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | /* Write a buffer into the hardware */ | ||
202 | int saa7164_buffer_activate(struct saa7164_buffer *buf, int i) | ||
203 | { | ||
204 | struct saa7164_port *port = buf->port; | ||
205 | struct saa7164_dev *dev = port->dev; | ||
206 | |||
207 | if ((i < 0) || (i >= port->hwcfg.buffercount)) | ||
208 | return -EINVAL; | ||
209 | |||
210 | dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); | ||
211 | |||
212 | buf->idx = i; /* Note of which buffer list index position we occupy */ | ||
213 | buf->flags = SAA7164_BUFFER_BUSY; | ||
214 | buf->pos = 0; | ||
215 | |||
216 | /* TODO: Review this in light of 32v64 assignments */ | ||
217 | saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); | ||
218 | saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma); | ||
219 | saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0); | ||
220 | |||
221 | dprintk(DBGLVL_BUF, " buf[%d] offset 0x%llx (0x%x) " | ||
222 | "buf 0x%llx/%llx (0x%x/%x) nr=%d\n", | ||
223 | buf->idx, | ||
224 | (u64)port->bufoffset + (i * sizeof(u32)), | ||
225 | saa7164_readl(port->bufoffset + (sizeof(u32) * i)), | ||
226 | (u64)port->bufptr32h + ((sizeof(u32) * 2) * i), | ||
227 | (u64)port->bufptr32l + ((sizeof(u32) * 2) * i), | ||
228 | saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)), | ||
229 | saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)), | ||
230 | buf->idx); | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | int saa7164_buffer_cfg_port(struct saa7164_port *port) | ||
236 | { | ||
237 | struct tmHWStreamParameters *params = &port->hw_streamingparams; | ||
238 | struct saa7164_dev *dev = port->dev; | ||
239 | struct saa7164_buffer *buf; | ||
240 | struct list_head *c, *n; | ||
241 | int i = 0; | ||
242 | |||
243 | dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr); | ||
244 | |||
245 | saa7164_writel(port->bufcounter, 0); | ||
246 | saa7164_writel(port->pitch, params->pitch); | ||
247 | saa7164_writel(port->bufsize, params->pitch * params->numberoflines); | ||
248 | |||
249 | dprintk(DBGLVL_BUF, " configured:\n"); | ||
250 | dprintk(DBGLVL_BUF, " lmmio 0x%p\n", dev->lmmio); | ||
251 | dprintk(DBGLVL_BUF, " bufcounter 0x%x = 0x%x\n", port->bufcounter, | ||
252 | saa7164_readl(port->bufcounter)); | ||
253 | |||
254 | dprintk(DBGLVL_BUF, " pitch 0x%x = %d\n", port->pitch, | ||
255 | saa7164_readl(port->pitch)); | ||
256 | |||
257 | dprintk(DBGLVL_BUF, " bufsize 0x%x = %d\n", port->bufsize, | ||
258 | saa7164_readl(port->bufsize)); | ||
259 | |||
260 | dprintk(DBGLVL_BUF, " buffercount = %d\n", port->hwcfg.buffercount); | ||
261 | dprintk(DBGLVL_BUF, " bufoffset = 0x%x\n", port->bufoffset); | ||
262 | dprintk(DBGLVL_BUF, " bufptr32h = 0x%x\n", port->bufptr32h); | ||
263 | dprintk(DBGLVL_BUF, " bufptr32l = 0x%x\n", port->bufptr32l); | ||
264 | |||
265 | /* Poke the buffers and offsets into PCI space */ | ||
266 | mutex_lock(&port->dmaqueue_lock); | ||
267 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
268 | buf = list_entry(c, struct saa7164_buffer, list); | ||
269 | |||
270 | if (buf->flags != SAA7164_BUFFER_FREE) | ||
271 | BUG(); | ||
272 | |||
273 | /* Place the buffer in the h/w queue */ | ||
274 | saa7164_buffer_activate(buf, i); | ||
275 | |||
276 | /* Don't exceed the device maximum # bufs */ | ||
277 | if (i++ > port->hwcfg.buffercount) | ||
278 | BUG(); | ||
279 | |||
280 | } | ||
281 | mutex_unlock(&port->dmaqueue_lock); | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev, | ||
287 | u32 len) | ||
288 | { | ||
289 | struct saa7164_user_buffer *buf; | ||
290 | |||
291 | buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL); | ||
292 | if (!buf) | ||
293 | return NULL; | ||
294 | |||
295 | buf->data = kzalloc(len, GFP_KERNEL); | ||
296 | |||
297 | if (!buf->data) { | ||
298 | kfree(buf); | ||
299 | return NULL; | ||
300 | } | ||
301 | |||
302 | buf->actual_size = len; | ||
303 | buf->pos = 0; | ||
304 | buf->crc = 0; | ||
305 | |||
306 | dprintk(DBGLVL_BUF, "%s() allocated user buffer @ 0x%p\n", | ||
307 | __func__, buf); | ||
308 | |||
309 | return buf; | ||
310 | } | ||
311 | |||
312 | void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf) | ||
313 | { | ||
314 | if (!buf) | ||
315 | return; | ||
316 | |||
317 | kfree(buf->data); | ||
318 | buf->data = NULL; | ||
319 | |||
320 | kfree(buf); | ||
321 | } | ||
322 | |||
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c new file mode 100644 index 00000000000..466e1b02f91 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-bus.c | |||
@@ -0,0 +1,475 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | struct tmComResBusInfo *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_dwSetWritePos = ((u32)dev->intfdesc.BARLocation) + | ||
47 | (2 * sizeof(u64)); | ||
48 | b->m_dwSetReadPos = b->m_dwSetWritePos + (1 * sizeof(u32)); | ||
49 | |||
50 | b->m_dwGetWritePos = b->m_dwSetWritePos + (2 * sizeof(u32)); | ||
51 | b->m_dwGetReadPos = b->m_dwSetWritePos + (3 * sizeof(u32)); | ||
52 | |||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | void saa7164_bus_dump(struct saa7164_dev *dev) | ||
57 | { | ||
58 | struct tmComResBusInfo *b = &dev->bus; | ||
59 | |||
60 | dprintk(DBGLVL_BUS, "Dumping the bus structure:\n"); | ||
61 | dprintk(DBGLVL_BUS, " .type = %d\n", b->Type); | ||
62 | dprintk(DBGLVL_BUS, " .dev->bmmio = 0x%p\n", dev->bmmio); | ||
63 | dprintk(DBGLVL_BUS, " .m_wMaxReqSize = 0x%x\n", b->m_wMaxReqSize); | ||
64 | dprintk(DBGLVL_BUS, " .m_pdwSetRing = 0x%p\n", b->m_pdwSetRing); | ||
65 | dprintk(DBGLVL_BUS, " .m_dwSizeSetRing = 0x%x\n", b->m_dwSizeSetRing); | ||
66 | dprintk(DBGLVL_BUS, " .m_pdwGetRing = 0x%p\n", b->m_pdwGetRing); | ||
67 | dprintk(DBGLVL_BUS, " .m_dwSizeGetRing = 0x%x\n", b->m_dwSizeGetRing); | ||
68 | |||
69 | dprintk(DBGLVL_BUS, " .m_dwSetReadPos = 0x%x (0x%08x)\n", | ||
70 | b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); | ||
71 | |||
72 | dprintk(DBGLVL_BUS, " .m_dwSetWritePos = 0x%x (0x%08x)\n", | ||
73 | b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); | ||
74 | |||
75 | dprintk(DBGLVL_BUS, " .m_dwGetReadPos = 0x%x (0x%08x)\n", | ||
76 | b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); | ||
77 | |||
78 | dprintk(DBGLVL_BUS, " .m_dwGetWritePos = 0x%x (0x%08x)\n", | ||
79 | b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); | ||
80 | |||
81 | } | ||
82 | |||
83 | /* Intensionally throw a BUG() if the state of the message bus looks corrupt */ | ||
84 | void saa7164_bus_verify(struct saa7164_dev *dev) | ||
85 | { | ||
86 | struct tmComResBusInfo *b = &dev->bus; | ||
87 | int bug = 0; | ||
88 | |||
89 | if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing) | ||
90 | bug++; | ||
91 | |||
92 | if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing) | ||
93 | bug++; | ||
94 | |||
95 | if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing) | ||
96 | bug++; | ||
97 | |||
98 | if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing) | ||
99 | bug++; | ||
100 | |||
101 | if (bug) { | ||
102 | saa_debug = 0xffff; /* Ensure we get the bus dump */ | ||
103 | saa7164_bus_dump(dev); | ||
104 | saa_debug = 1024; /* Ensure we get the bus dump */ | ||
105 | BUG(); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo* m, | ||
110 | void *buf) | ||
111 | { | ||
112 | dprintk(DBGLVL_BUS, "Dumping msg structure:\n"); | ||
113 | dprintk(DBGLVL_BUS, " .id = %d\n", m->id); | ||
114 | dprintk(DBGLVL_BUS, " .flags = 0x%x\n", m->flags); | ||
115 | dprintk(DBGLVL_BUS, " .size = 0x%x\n", m->size); | ||
116 | dprintk(DBGLVL_BUS, " .command = 0x%x\n", m->command); | ||
117 | dprintk(DBGLVL_BUS, " .controlselector = 0x%x\n", m->controlselector); | ||
118 | dprintk(DBGLVL_BUS, " .seqno = %d\n", m->seqno); | ||
119 | if (buf) | ||
120 | dprintk(DBGLVL_BUS, " .buffer (ignored)\n"); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Places a command or a response on the bus. The implementation does not | ||
125 | * know if it is a command or a response it just places the data on the | ||
126 | * bus depending on the bus information given in the struct tmComResBusInfo | ||
127 | * structure. If the command or response does not fit into the bus ring | ||
128 | * buffer it will be refused. | ||
129 | * | ||
130 | * Return Value: | ||
131 | * SAA_OK The function executed successfully. | ||
132 | * < 0 One or more members are not initialized. | ||
133 | */ | ||
134 | int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, | ||
135 | void *buf) | ||
136 | { | ||
137 | struct tmComResBusInfo *bus = &dev->bus; | ||
138 | u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp; | ||
139 | u32 new_swp, space_rem; | ||
140 | int ret = SAA_ERR_BAD_PARAMETER; | ||
141 | |||
142 | if (!msg) { | ||
143 | printk(KERN_ERR "%s() !msg\n", __func__); | ||
144 | return SAA_ERR_BAD_PARAMETER; | ||
145 | } | ||
146 | |||
147 | dprintk(DBGLVL_BUS, "%s()\n", __func__); | ||
148 | |||
149 | saa7164_bus_verify(dev); | ||
150 | |||
151 | msg->size = cpu_to_le16(msg->size); | ||
152 | msg->command = cpu_to_le16(msg->command); | ||
153 | msg->controlselector = cpu_to_le16(msg->controlselector); | ||
154 | |||
155 | if (msg->size > dev->bus.m_wMaxReqSize) { | ||
156 | printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", | ||
157 | __func__); | ||
158 | return SAA_ERR_BAD_PARAMETER; | ||
159 | } | ||
160 | |||
161 | if ((msg->size > 0) && (buf == NULL)) { | ||
162 | printk(KERN_ERR "%s() Missing message buffer\n", __func__); | ||
163 | return SAA_ERR_BAD_PARAMETER; | ||
164 | } | ||
165 | |||
166 | /* Lock the bus from any other access */ | ||
167 | mutex_lock(&bus->lock); | ||
168 | |||
169 | bytes_to_write = sizeof(*msg) + msg->size; | ||
170 | free_write_space = 0; | ||
171 | timeout = SAA_BUS_TIMEOUT; | ||
172 | curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos)); | ||
173 | curr_swp = le32_to_cpu(saa7164_readl(bus->m_dwSetWritePos)); | ||
174 | |||
175 | /* Deal with ring wrapping issues */ | ||
176 | if (curr_srp > curr_swp) | ||
177 | /* Deal with the wrapped ring */ | ||
178 | free_write_space = curr_srp - curr_swp; | ||
179 | else | ||
180 | /* The ring has not wrapped yet */ | ||
181 | free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp; | ||
182 | |||
183 | dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__, | ||
184 | bytes_to_write); | ||
185 | |||
186 | dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__, | ||
187 | free_write_space); | ||
188 | |||
189 | dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp); | ||
190 | dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp); | ||
191 | |||
192 | /* Process the msg and write the content onto the bus */ | ||
193 | while (bytes_to_write >= free_write_space) { | ||
194 | |||
195 | if (timeout-- == 0) { | ||
196 | printk(KERN_ERR "%s() bus timeout\n", __func__); | ||
197 | ret = SAA_ERR_NO_RESOURCES; | ||
198 | goto out; | ||
199 | } | ||
200 | |||
201 | /* TODO: Review this delay, efficient? */ | ||
202 | /* Wait, allowing the hardware fetch time */ | ||
203 | mdelay(1); | ||
204 | |||
205 | /* Check the space usage again */ | ||
206 | curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos)); | ||
207 | |||
208 | /* Deal with ring wrapping issues */ | ||
209 | if (curr_srp > curr_swp) | ||
210 | /* Deal with the wrapped ring */ | ||
211 | free_write_space = curr_srp - curr_swp; | ||
212 | else | ||
213 | /* Read didn't wrap around the buffer */ | ||
214 | free_write_space = (curr_srp + bus->m_dwSizeSetRing) - | ||
215 | curr_swp; | ||
216 | |||
217 | } | ||
218 | |||
219 | /* Calculate the new write position */ | ||
220 | new_swp = curr_swp + bytes_to_write; | ||
221 | |||
222 | dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); | ||
223 | dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__, | ||
224 | bus->m_dwSizeSetRing); | ||
225 | |||
226 | /* Mental Note: line 462 tmmhComResBusPCIe.cpp */ | ||
227 | |||
228 | /* Check if we're going to wrap again */ | ||
229 | if (new_swp > bus->m_dwSizeSetRing) { | ||
230 | |||
231 | /* Ring wraps */ | ||
232 | new_swp -= bus->m_dwSizeSetRing; | ||
233 | |||
234 | space_rem = bus->m_dwSizeSetRing - curr_swp; | ||
235 | |||
236 | dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__, | ||
237 | space_rem); | ||
238 | |||
239 | dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__, | ||
240 | (u32)sizeof(*msg)); | ||
241 | |||
242 | if (space_rem < sizeof(*msg)) { | ||
243 | dprintk(DBGLVL_BUS, "%s() tr4\n", __func__); | ||
244 | |||
245 | /* Split the msg into pieces as the ring wraps */ | ||
246 | memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem); | ||
247 | memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem, | ||
248 | sizeof(*msg) - space_rem); | ||
249 | |||
250 | memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem, | ||
251 | buf, msg->size); | ||
252 | |||
253 | } else if (space_rem == sizeof(*msg)) { | ||
254 | dprintk(DBGLVL_BUS, "%s() tr5\n", __func__); | ||
255 | |||
256 | /* Additional data at the beginning of the ring */ | ||
257 | memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); | ||
258 | memcpy(bus->m_pdwSetRing, buf, msg->size); | ||
259 | |||
260 | } else { | ||
261 | /* Additional data wraps around the ring */ | ||
262 | memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); | ||
263 | if (msg->size > 0) { | ||
264 | memcpy(bus->m_pdwSetRing + curr_swp + | ||
265 | sizeof(*msg), buf, space_rem - | ||
266 | sizeof(*msg)); | ||
267 | memcpy(bus->m_pdwSetRing, (u8 *)buf + | ||
268 | space_rem - sizeof(*msg), | ||
269 | bytes_to_write - space_rem); | ||
270 | } | ||
271 | |||
272 | } | ||
273 | |||
274 | } /* (new_swp > bus->m_dwSizeSetRing) */ | ||
275 | else { | ||
276 | dprintk(DBGLVL_BUS, "%s() tr6\n", __func__); | ||
277 | |||
278 | /* The ring buffer doesn't wrap, two simple copies */ | ||
279 | memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); | ||
280 | memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf, | ||
281 | msg->size); | ||
282 | } | ||
283 | |||
284 | dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); | ||
285 | |||
286 | /* Update the bus write position */ | ||
287 | saa7164_writel(bus->m_dwSetWritePos, cpu_to_le32(new_swp)); | ||
288 | ret = SAA_OK; | ||
289 | |||
290 | out: | ||
291 | saa7164_bus_dump(dev); | ||
292 | mutex_unlock(&bus->lock); | ||
293 | saa7164_bus_verify(dev); | ||
294 | return ret; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * Receive a command or a response from the bus. The implementation does not | ||
299 | * know if it is a command or a response it simply dequeues the data, | ||
300 | * depending on the bus information given in the struct tmComResBusInfo | ||
301 | * structure. | ||
302 | * | ||
303 | * Return Value: | ||
304 | * 0 The function executed successfully. | ||
305 | * < 0 One or more members are not initialized. | ||
306 | */ | ||
307 | int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, | ||
308 | void *buf, int peekonly) | ||
309 | { | ||
310 | struct tmComResBusInfo *bus = &dev->bus; | ||
311 | u32 bytes_to_read, write_distance, curr_grp, curr_gwp, | ||
312 | new_grp, buf_size, space_rem; | ||
313 | struct tmComResInfo msg_tmp; | ||
314 | int ret = SAA_ERR_BAD_PARAMETER; | ||
315 | |||
316 | saa7164_bus_verify(dev); | ||
317 | |||
318 | if (msg == NULL) | ||
319 | return ret; | ||
320 | |||
321 | if (msg->size > dev->bus.m_wMaxReqSize) { | ||
322 | printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", | ||
323 | __func__); | ||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) { | ||
328 | printk(KERN_ERR | ||
329 | "%s() Missing msg buf, size should be %d bytes\n", | ||
330 | __func__, msg->size); | ||
331 | return ret; | ||
332 | } | ||
333 | |||
334 | mutex_lock(&bus->lock); | ||
335 | |||
336 | /* Peek the bus to see if a msg exists, if it's not what we're expecting | ||
337 | * then return cleanly else read the message from the bus. | ||
338 | */ | ||
339 | curr_gwp = le32_to_cpu(saa7164_readl(bus->m_dwGetWritePos)); | ||
340 | curr_grp = le32_to_cpu(saa7164_readl(bus->m_dwGetReadPos)); | ||
341 | |||
342 | if (curr_gwp == curr_grp) { | ||
343 | ret = SAA_ERR_EMPTY; | ||
344 | goto out; | ||
345 | } | ||
346 | |||
347 | bytes_to_read = sizeof(*msg); | ||
348 | |||
349 | /* Calculate write distance to current read position */ | ||
350 | write_distance = 0; | ||
351 | if (curr_gwp >= curr_grp) | ||
352 | /* Write doesn't wrap around the ring */ | ||
353 | write_distance = curr_gwp - curr_grp; | ||
354 | else | ||
355 | /* Write wraps around the ring */ | ||
356 | write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; | ||
357 | |||
358 | if (bytes_to_read > write_distance) { | ||
359 | printk(KERN_ERR "%s() No message/response found\n", __func__); | ||
360 | ret = SAA_ERR_INVALID_COMMAND; | ||
361 | goto out; | ||
362 | } | ||
363 | |||
364 | /* Calculate the new read position */ | ||
365 | new_grp = curr_grp + bytes_to_read; | ||
366 | if (new_grp > bus->m_dwSizeGetRing) { | ||
367 | |||
368 | /* Ring wraps */ | ||
369 | new_grp -= bus->m_dwSizeGetRing; | ||
370 | space_rem = bus->m_dwSizeGetRing - curr_grp; | ||
371 | |||
372 | memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem); | ||
373 | memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing, | ||
374 | bytes_to_read - space_rem); | ||
375 | |||
376 | } else { | ||
377 | /* No wrapping */ | ||
378 | memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read); | ||
379 | } | ||
380 | |||
381 | /* No need to update the read positions, because this was a peek */ | ||
382 | /* If the caller specifically want to peek, return */ | ||
383 | if (peekonly) { | ||
384 | memcpy(msg, &msg_tmp, sizeof(*msg)); | ||
385 | goto peekout; | ||
386 | } | ||
387 | |||
388 | /* Check if the command/response matches what is expected */ | ||
389 | if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) || | ||
390 | (msg_tmp.controlselector != msg->controlselector) || | ||
391 | (msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) { | ||
392 | |||
393 | printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__); | ||
394 | saa7164_bus_dumpmsg(dev, msg, buf); | ||
395 | saa7164_bus_dumpmsg(dev, &msg_tmp, NULL); | ||
396 | ret = SAA_ERR_INVALID_COMMAND; | ||
397 | goto out; | ||
398 | } | ||
399 | |||
400 | /* Get the actual command and response from the bus */ | ||
401 | buf_size = msg->size; | ||
402 | |||
403 | bytes_to_read = sizeof(*msg) + msg->size; | ||
404 | /* Calculate write distance to current read position */ | ||
405 | write_distance = 0; | ||
406 | if (curr_gwp >= curr_grp) | ||
407 | /* Write doesn't wrap around the ring */ | ||
408 | write_distance = curr_gwp - curr_grp; | ||
409 | else | ||
410 | /* Write wraps around the ring */ | ||
411 | write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; | ||
412 | |||
413 | if (bytes_to_read > write_distance) { | ||
414 | printk(KERN_ERR "%s() Invalid bus state, missing msg " | ||
415 | "or mangled ring, faulty H/W / bad code?\n", __func__); | ||
416 | ret = SAA_ERR_INVALID_COMMAND; | ||
417 | goto out; | ||
418 | } | ||
419 | |||
420 | /* Calculate the new read position */ | ||
421 | new_grp = curr_grp + bytes_to_read; | ||
422 | if (new_grp > bus->m_dwSizeGetRing) { | ||
423 | |||
424 | /* Ring wraps */ | ||
425 | new_grp -= bus->m_dwSizeGetRing; | ||
426 | space_rem = bus->m_dwSizeGetRing - curr_grp; | ||
427 | |||
428 | if (space_rem < sizeof(*msg)) { | ||
429 | /* msg wraps around the ring */ | ||
430 | memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem); | ||
431 | memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing, | ||
432 | sizeof(*msg) - space_rem); | ||
433 | if (buf) | ||
434 | memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) - | ||
435 | space_rem, buf_size); | ||
436 | |||
437 | } else if (space_rem == sizeof(*msg)) { | ||
438 | memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); | ||
439 | if (buf) | ||
440 | memcpy(buf, bus->m_pdwGetRing, buf_size); | ||
441 | } else { | ||
442 | /* Additional data wraps around the ring */ | ||
443 | memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); | ||
444 | if (buf) { | ||
445 | memcpy(buf, bus->m_pdwGetRing + curr_grp + | ||
446 | sizeof(*msg), space_rem - sizeof(*msg)); | ||
447 | memcpy(buf + space_rem - sizeof(*msg), | ||
448 | bus->m_pdwGetRing, bytes_to_read - | ||
449 | space_rem); | ||
450 | } | ||
451 | |||
452 | } | ||
453 | |||
454 | } else { | ||
455 | /* No wrapping */ | ||
456 | memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); | ||
457 | if (buf) | ||
458 | memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), | ||
459 | buf_size); | ||
460 | } | ||
461 | |||
462 | /* Update the read positions, adjusting the ring */ | ||
463 | saa7164_writel(bus->m_dwGetReadPos, cpu_to_le32(new_grp)); | ||
464 | |||
465 | peekout: | ||
466 | msg->size = le16_to_cpu(msg->size); | ||
467 | msg->command = le16_to_cpu(msg->command); | ||
468 | msg->controlselector = le16_to_cpu(msg->controlselector); | ||
469 | ret = SAA_OK; | ||
470 | out: | ||
471 | mutex_unlock(&bus->lock); | ||
472 | saa7164_bus_verify(dev); | ||
473 | return ret; | ||
474 | } | ||
475 | |||
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c new file mode 100644 index 00000000000..c71369173fa --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-cards.c | |||
@@ -0,0 +1,715 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | .portc = SAA7164_MPEG_ENCODER, | ||
59 | .portd = SAA7164_MPEG_ENCODER, | ||
60 | .porte = SAA7164_MPEG_VBI, | ||
61 | .portf = SAA7164_MPEG_VBI, | ||
62 | .chiprev = SAA7164_CHIP_REV3, | ||
63 | .unit = {{ | ||
64 | .id = 0x1d, | ||
65 | .type = SAA7164_UNIT_EEPROM, | ||
66 | .name = "4K EEPROM", | ||
67 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
68 | .i2c_bus_addr = 0xa0 >> 1, | ||
69 | .i2c_reg_len = REGLEN_8bit, | ||
70 | }, { | ||
71 | .id = 0x04, | ||
72 | .type = SAA7164_UNIT_TUNER, | ||
73 | .name = "TDA18271-1", | ||
74 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
75 | .i2c_bus_addr = 0xc0 >> 1, | ||
76 | .i2c_reg_len = REGLEN_8bit, | ||
77 | }, { | ||
78 | .id = 0x1b, | ||
79 | .type = SAA7164_UNIT_TUNER, | ||
80 | .name = "TDA18271-2", | ||
81 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
82 | .i2c_bus_addr = 0xc0 >> 1, | ||
83 | .i2c_reg_len = REGLEN_8bit, | ||
84 | }, { | ||
85 | .id = 0x1e, | ||
86 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
87 | .name = "TDA10048-1", | ||
88 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
89 | .i2c_bus_addr = 0x10 >> 1, | ||
90 | .i2c_reg_len = REGLEN_8bit, | ||
91 | }, { | ||
92 | .id = 0x1f, | ||
93 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
94 | .name = "TDA10048-2", | ||
95 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
96 | .i2c_bus_addr = 0x12 >> 1, | ||
97 | .i2c_reg_len = REGLEN_8bit, | ||
98 | } }, | ||
99 | }, | ||
100 | [SAA7164_BOARD_HAUPPAUGE_HVR2200_2] = { | ||
101 | .name = "Hauppauge WinTV-HVR2200", | ||
102 | .porta = SAA7164_MPEG_DVB, | ||
103 | .portb = SAA7164_MPEG_DVB, | ||
104 | .portc = SAA7164_MPEG_ENCODER, | ||
105 | .portd = SAA7164_MPEG_ENCODER, | ||
106 | .porte = SAA7164_MPEG_VBI, | ||
107 | .portf = SAA7164_MPEG_VBI, | ||
108 | .chiprev = SAA7164_CHIP_REV2, | ||
109 | .unit = {{ | ||
110 | .id = 0x06, | ||
111 | .type = SAA7164_UNIT_EEPROM, | ||
112 | .name = "4K EEPROM", | ||
113 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
114 | .i2c_bus_addr = 0xa0 >> 1, | ||
115 | .i2c_reg_len = REGLEN_8bit, | ||
116 | }, { | ||
117 | .id = 0x04, | ||
118 | .type = SAA7164_UNIT_TUNER, | ||
119 | .name = "TDA18271-1", | ||
120 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
121 | .i2c_bus_addr = 0xc0 >> 1, | ||
122 | .i2c_reg_len = REGLEN_8bit, | ||
123 | }, { | ||
124 | .id = 0x05, | ||
125 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
126 | .name = "TDA10048-1", | ||
127 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
128 | .i2c_bus_addr = 0x10 >> 1, | ||
129 | .i2c_reg_len = REGLEN_8bit, | ||
130 | }, { | ||
131 | .id = 0x1e, | ||
132 | .type = SAA7164_UNIT_TUNER, | ||
133 | .name = "TDA18271-2", | ||
134 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
135 | .i2c_bus_addr = 0xc0 >> 1, | ||
136 | .i2c_reg_len = REGLEN_8bit, | ||
137 | }, { | ||
138 | .id = 0x1f, | ||
139 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
140 | .name = "TDA10048-2", | ||
141 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
142 | .i2c_bus_addr = 0x12 >> 1, | ||
143 | .i2c_reg_len = REGLEN_8bit, | ||
144 | } }, | ||
145 | }, | ||
146 | [SAA7164_BOARD_HAUPPAUGE_HVR2200_3] = { | ||
147 | .name = "Hauppauge WinTV-HVR2200", | ||
148 | .porta = SAA7164_MPEG_DVB, | ||
149 | .portb = SAA7164_MPEG_DVB, | ||
150 | .portc = SAA7164_MPEG_ENCODER, | ||
151 | .portd = SAA7164_MPEG_ENCODER, | ||
152 | .porte = SAA7164_MPEG_VBI, | ||
153 | .portf = SAA7164_MPEG_VBI, | ||
154 | .chiprev = SAA7164_CHIP_REV2, | ||
155 | .unit = {{ | ||
156 | .id = 0x1d, | ||
157 | .type = SAA7164_UNIT_EEPROM, | ||
158 | .name = "4K EEPROM", | ||
159 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
160 | .i2c_bus_addr = 0xa0 >> 1, | ||
161 | .i2c_reg_len = REGLEN_8bit, | ||
162 | }, { | ||
163 | .id = 0x04, | ||
164 | .type = SAA7164_UNIT_TUNER, | ||
165 | .name = "TDA18271-1", | ||
166 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
167 | .i2c_bus_addr = 0xc0 >> 1, | ||
168 | .i2c_reg_len = REGLEN_8bit, | ||
169 | }, { | ||
170 | .id = 0x05, | ||
171 | .type = SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
172 | .name = "TDA8290-1", | ||
173 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
174 | .i2c_bus_addr = 0x84 >> 1, | ||
175 | .i2c_reg_len = REGLEN_8bit, | ||
176 | }, { | ||
177 | .id = 0x1b, | ||
178 | .type = SAA7164_UNIT_TUNER, | ||
179 | .name = "TDA18271-2", | ||
180 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
181 | .i2c_bus_addr = 0xc0 >> 1, | ||
182 | .i2c_reg_len = REGLEN_8bit, | ||
183 | }, { | ||
184 | .id = 0x1c, | ||
185 | .type = SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
186 | .name = "TDA8290-2", | ||
187 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
188 | .i2c_bus_addr = 0x84 >> 1, | ||
189 | .i2c_reg_len = REGLEN_8bit, | ||
190 | }, { | ||
191 | .id = 0x1e, | ||
192 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
193 | .name = "TDA10048-1", | ||
194 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
195 | .i2c_bus_addr = 0x10 >> 1, | ||
196 | .i2c_reg_len = REGLEN_8bit, | ||
197 | }, { | ||
198 | .id = 0x1f, | ||
199 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
200 | .name = "TDA10048-2", | ||
201 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
202 | .i2c_bus_addr = 0x12 >> 1, | ||
203 | .i2c_reg_len = REGLEN_8bit, | ||
204 | } }, | ||
205 | }, | ||
206 | [SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = { | ||
207 | .name = "Hauppauge WinTV-HVR2200", | ||
208 | .porta = SAA7164_MPEG_DVB, | ||
209 | .portb = SAA7164_MPEG_DVB, | ||
210 | .portc = SAA7164_MPEG_ENCODER, | ||
211 | .portd = SAA7164_MPEG_ENCODER, | ||
212 | .porte = SAA7164_MPEG_VBI, | ||
213 | .portf = SAA7164_MPEG_VBI, | ||
214 | .chiprev = SAA7164_CHIP_REV3, | ||
215 | .unit = {{ | ||
216 | .id = 0x1d, | ||
217 | .type = SAA7164_UNIT_EEPROM, | ||
218 | .name = "4K EEPROM", | ||
219 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
220 | .i2c_bus_addr = 0xa0 >> 1, | ||
221 | .i2c_reg_len = REGLEN_8bit, | ||
222 | }, { | ||
223 | .id = 0x04, | ||
224 | .type = SAA7164_UNIT_TUNER, | ||
225 | .name = "TDA18271-1", | ||
226 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
227 | .i2c_bus_addr = 0xc0 >> 1, | ||
228 | .i2c_reg_len = REGLEN_8bit, | ||
229 | }, { | ||
230 | .id = 0x05, | ||
231 | .type = SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
232 | .name = "TDA8290-1", | ||
233 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
234 | .i2c_bus_addr = 0x84 >> 1, | ||
235 | .i2c_reg_len = REGLEN_8bit, | ||
236 | }, { | ||
237 | .id = 0x1b, | ||
238 | .type = SAA7164_UNIT_TUNER, | ||
239 | .name = "TDA18271-2", | ||
240 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
241 | .i2c_bus_addr = 0xc0 >> 1, | ||
242 | .i2c_reg_len = REGLEN_8bit, | ||
243 | }, { | ||
244 | .id = 0x1c, | ||
245 | .type = SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
246 | .name = "TDA8290-2", | ||
247 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
248 | .i2c_bus_addr = 0x84 >> 1, | ||
249 | .i2c_reg_len = REGLEN_8bit, | ||
250 | }, { | ||
251 | .id = 0x1e, | ||
252 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
253 | .name = "TDA10048-1", | ||
254 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
255 | .i2c_bus_addr = 0x10 >> 1, | ||
256 | .i2c_reg_len = REGLEN_8bit, | ||
257 | }, { | ||
258 | .id = 0x1f, | ||
259 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
260 | .name = "TDA10048-2", | ||
261 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
262 | .i2c_bus_addr = 0x12 >> 1, | ||
263 | .i2c_reg_len = REGLEN_8bit, | ||
264 | } }, | ||
265 | }, | ||
266 | [SAA7164_BOARD_HAUPPAUGE_HVR2250] = { | ||
267 | .name = "Hauppauge WinTV-HVR2250", | ||
268 | .porta = SAA7164_MPEG_DVB, | ||
269 | .portb = SAA7164_MPEG_DVB, | ||
270 | .portc = SAA7164_MPEG_ENCODER, | ||
271 | .portd = SAA7164_MPEG_ENCODER, | ||
272 | .portc = SAA7164_MPEG_ENCODER, | ||
273 | .portd = SAA7164_MPEG_ENCODER, | ||
274 | .porte = SAA7164_MPEG_VBI, | ||
275 | .portf = SAA7164_MPEG_VBI, | ||
276 | .chiprev = SAA7164_CHIP_REV3, | ||
277 | .unit = {{ | ||
278 | .id = 0x22, | ||
279 | .type = SAA7164_UNIT_EEPROM, | ||
280 | .name = "4K EEPROM", | ||
281 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
282 | .i2c_bus_addr = 0xa0 >> 1, | ||
283 | .i2c_reg_len = REGLEN_8bit, | ||
284 | }, { | ||
285 | .id = 0x04, | ||
286 | .type = SAA7164_UNIT_TUNER, | ||
287 | .name = "TDA18271-1", | ||
288 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
289 | .i2c_bus_addr = 0xc0 >> 1, | ||
290 | .i2c_reg_len = REGLEN_8bit, | ||
291 | }, { | ||
292 | .id = 0x07, | ||
293 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
294 | .name = "CX24228/S5H1411-1 (TOP)", | ||
295 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
296 | .i2c_bus_addr = 0x32 >> 1, | ||
297 | .i2c_reg_len = REGLEN_8bit, | ||
298 | }, { | ||
299 | .id = 0x08, | ||
300 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
301 | .name = "CX24228/S5H1411-1 (QAM)", | ||
302 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
303 | .i2c_bus_addr = 0x34 >> 1, | ||
304 | .i2c_reg_len = REGLEN_8bit, | ||
305 | }, { | ||
306 | .id = 0x1e, | ||
307 | .type = SAA7164_UNIT_TUNER, | ||
308 | .name = "TDA18271-2", | ||
309 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
310 | .i2c_bus_addr = 0xc0 >> 1, | ||
311 | .i2c_reg_len = REGLEN_8bit, | ||
312 | }, { | ||
313 | .id = 0x20, | ||
314 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
315 | .name = "CX24228/S5H1411-2 (TOP)", | ||
316 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
317 | .i2c_bus_addr = 0x32 >> 1, | ||
318 | .i2c_reg_len = REGLEN_8bit, | ||
319 | }, { | ||
320 | .id = 0x23, | ||
321 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
322 | .name = "CX24228/S5H1411-2 (QAM)", | ||
323 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
324 | .i2c_bus_addr = 0x34 >> 1, | ||
325 | .i2c_reg_len = REGLEN_8bit, | ||
326 | } }, | ||
327 | }, | ||
328 | [SAA7164_BOARD_HAUPPAUGE_HVR2250_2] = { | ||
329 | .name = "Hauppauge WinTV-HVR2250", | ||
330 | .porta = SAA7164_MPEG_DVB, | ||
331 | .portb = SAA7164_MPEG_DVB, | ||
332 | .portc = SAA7164_MPEG_ENCODER, | ||
333 | .portd = SAA7164_MPEG_ENCODER, | ||
334 | .porte = SAA7164_MPEG_VBI, | ||
335 | .portf = SAA7164_MPEG_VBI, | ||
336 | .porte = SAA7164_MPEG_VBI, | ||
337 | .portf = SAA7164_MPEG_VBI, | ||
338 | .chiprev = SAA7164_CHIP_REV3, | ||
339 | .unit = {{ | ||
340 | .id = 0x28, | ||
341 | .type = SAA7164_UNIT_EEPROM, | ||
342 | .name = "4K EEPROM", | ||
343 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
344 | .i2c_bus_addr = 0xa0 >> 1, | ||
345 | .i2c_reg_len = REGLEN_8bit, | ||
346 | }, { | ||
347 | .id = 0x04, | ||
348 | .type = SAA7164_UNIT_TUNER, | ||
349 | .name = "TDA18271-1", | ||
350 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
351 | .i2c_bus_addr = 0xc0 >> 1, | ||
352 | .i2c_reg_len = REGLEN_8bit, | ||
353 | }, { | ||
354 | .id = 0x07, | ||
355 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
356 | .name = "CX24228/S5H1411-1 (TOP)", | ||
357 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
358 | .i2c_bus_addr = 0x32 >> 1, | ||
359 | .i2c_reg_len = REGLEN_8bit, | ||
360 | }, { | ||
361 | .id = 0x08, | ||
362 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
363 | .name = "CX24228/S5H1411-1 (QAM)", | ||
364 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
365 | .i2c_bus_addr = 0x34 >> 1, | ||
366 | .i2c_reg_len = REGLEN_8bit, | ||
367 | }, { | ||
368 | .id = 0x24, | ||
369 | .type = SAA7164_UNIT_TUNER, | ||
370 | .name = "TDA18271-2", | ||
371 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
372 | .i2c_bus_addr = 0xc0 >> 1, | ||
373 | .i2c_reg_len = REGLEN_8bit, | ||
374 | }, { | ||
375 | .id = 0x26, | ||
376 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
377 | .name = "CX24228/S5H1411-2 (TOP)", | ||
378 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
379 | .i2c_bus_addr = 0x32 >> 1, | ||
380 | .i2c_reg_len = REGLEN_8bit, | ||
381 | }, { | ||
382 | .id = 0x29, | ||
383 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
384 | .name = "CX24228/S5H1411-2 (QAM)", | ||
385 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
386 | .i2c_bus_addr = 0x34 >> 1, | ||
387 | .i2c_reg_len = REGLEN_8bit, | ||
388 | } }, | ||
389 | }, | ||
390 | [SAA7164_BOARD_HAUPPAUGE_HVR2250_3] = { | ||
391 | .name = "Hauppauge WinTV-HVR2250", | ||
392 | .porta = SAA7164_MPEG_DVB, | ||
393 | .portb = SAA7164_MPEG_DVB, | ||
394 | .portc = SAA7164_MPEG_ENCODER, | ||
395 | .portd = SAA7164_MPEG_ENCODER, | ||
396 | .porte = SAA7164_MPEG_VBI, | ||
397 | .portf = SAA7164_MPEG_VBI, | ||
398 | .chiprev = SAA7164_CHIP_REV3, | ||
399 | .unit = {{ | ||
400 | .id = 0x26, | ||
401 | .type = SAA7164_UNIT_EEPROM, | ||
402 | .name = "4K EEPROM", | ||
403 | .i2c_bus_nr = SAA7164_I2C_BUS_0, | ||
404 | .i2c_bus_addr = 0xa0 >> 1, | ||
405 | .i2c_reg_len = REGLEN_8bit, | ||
406 | }, { | ||
407 | .id = 0x04, | ||
408 | .type = SAA7164_UNIT_TUNER, | ||
409 | .name = "TDA18271-1", | ||
410 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
411 | .i2c_bus_addr = 0xc0 >> 1, | ||
412 | .i2c_reg_len = REGLEN_8bit, | ||
413 | }, { | ||
414 | .id = 0x07, | ||
415 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
416 | .name = "CX24228/S5H1411-1 (TOP)", | ||
417 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
418 | .i2c_bus_addr = 0x32 >> 1, | ||
419 | .i2c_reg_len = REGLEN_8bit, | ||
420 | }, { | ||
421 | .id = 0x08, | ||
422 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
423 | .name = "CX24228/S5H1411-1 (QAM)", | ||
424 | .i2c_bus_nr = SAA7164_I2C_BUS_1, | ||
425 | .i2c_bus_addr = 0x34 >> 1, | ||
426 | .i2c_reg_len = REGLEN_8bit, | ||
427 | }, { | ||
428 | .id = 0x22, | ||
429 | .type = SAA7164_UNIT_TUNER, | ||
430 | .name = "TDA18271-2", | ||
431 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
432 | .i2c_bus_addr = 0xc0 >> 1, | ||
433 | .i2c_reg_len = REGLEN_8bit, | ||
434 | }, { | ||
435 | .id = 0x24, | ||
436 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
437 | .name = "CX24228/S5H1411-2 (TOP)", | ||
438 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
439 | .i2c_bus_addr = 0x32 >> 1, | ||
440 | .i2c_reg_len = REGLEN_8bit, | ||
441 | }, { | ||
442 | .id = 0x27, | ||
443 | .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
444 | .name = "CX24228/S5H1411-2 (QAM)", | ||
445 | .i2c_bus_nr = SAA7164_I2C_BUS_2, | ||
446 | .i2c_bus_addr = 0x34 >> 1, | ||
447 | .i2c_reg_len = REGLEN_8bit, | ||
448 | } }, | ||
449 | }, | ||
450 | }; | ||
451 | const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); | ||
452 | |||
453 | /* ------------------------------------------------------------------ */ | ||
454 | /* PCI subsystem IDs */ | ||
455 | |||
456 | struct saa7164_subid saa7164_subids[] = { | ||
457 | { | ||
458 | .subvendor = 0x0070, | ||
459 | .subdevice = 0x8880, | ||
460 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, | ||
461 | }, { | ||
462 | .subvendor = 0x0070, | ||
463 | .subdevice = 0x8810, | ||
464 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, | ||
465 | }, { | ||
466 | .subvendor = 0x0070, | ||
467 | .subdevice = 0x8980, | ||
468 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200, | ||
469 | }, { | ||
470 | .subvendor = 0x0070, | ||
471 | .subdevice = 0x8900, | ||
472 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_2, | ||
473 | }, { | ||
474 | .subvendor = 0x0070, | ||
475 | .subdevice = 0x8901, | ||
476 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_3, | ||
477 | }, { | ||
478 | .subvendor = 0x0070, | ||
479 | .subdevice = 0x88A1, | ||
480 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_3, | ||
481 | }, { | ||
482 | .subvendor = 0x0070, | ||
483 | .subdevice = 0x8891, | ||
484 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, | ||
485 | }, { | ||
486 | .subvendor = 0x0070, | ||
487 | .subdevice = 0x8851, | ||
488 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, | ||
489 | }, { | ||
490 | .subvendor = 0x0070, | ||
491 | .subdevice = 0x8940, | ||
492 | .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_4, | ||
493 | }, | ||
494 | }; | ||
495 | const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); | ||
496 | |||
497 | void saa7164_card_list(struct saa7164_dev *dev) | ||
498 | { | ||
499 | int i; | ||
500 | |||
501 | if (0 == dev->pci->subsystem_vendor && | ||
502 | 0 == dev->pci->subsystem_device) { | ||
503 | printk(KERN_ERR | ||
504 | "%s: Board has no valid PCIe Subsystem ID and can't\n" | ||
505 | "%s: be autodetected. Pass card=<n> insmod option to\n" | ||
506 | "%s: workaround that. Send complaints to the vendor\n" | ||
507 | "%s: of the TV card. Best regards,\n" | ||
508 | "%s: -- tux\n", | ||
509 | dev->name, dev->name, dev->name, dev->name, dev->name); | ||
510 | } else { | ||
511 | printk(KERN_ERR | ||
512 | "%s: Your board isn't known (yet) to the driver.\n" | ||
513 | "%s: Try to pick one of the existing card configs via\n" | ||
514 | "%s: card=<n> insmod option. Updating to the latest\n" | ||
515 | "%s: version might help as well.\n", | ||
516 | dev->name, dev->name, dev->name, dev->name); | ||
517 | } | ||
518 | |||
519 | printk(KERN_ERR "%s: Here are valid choices for the card=<n> insmod " | ||
520 | "option:\n", dev->name); | ||
521 | |||
522 | for (i = 0; i < saa7164_bcount; i++) | ||
523 | printk(KERN_ERR "%s: card=%d -> %s\n", | ||
524 | dev->name, i, saa7164_boards[i].name); | ||
525 | } | ||
526 | |||
527 | /* TODO: clean this define up into the -cards.c structs */ | ||
528 | #define PCIEBRIDGE_UNITID 2 | ||
529 | |||
530 | void saa7164_gpio_setup(struct saa7164_dev *dev) | ||
531 | { | ||
532 | switch (dev->board) { | ||
533 | case SAA7164_BOARD_HAUPPAUGE_HVR2200: | ||
534 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: | ||
535 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: | ||
536 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: | ||
537 | case SAA7164_BOARD_HAUPPAUGE_HVR2250: | ||
538 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: | ||
539 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: | ||
540 | /* | ||
541 | GPIO 2: s5h1411 / tda10048-1 demod reset | ||
542 | GPIO 3: s5h1411 / tda10048-2 demod reset | ||
543 | GPIO 7: IRBlaster Zilog reset | ||
544 | */ | ||
545 | |||
546 | /* Reset parts by going in and out of reset */ | ||
547 | saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2); | ||
548 | saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3); | ||
549 | |||
550 | msleep(20); | ||
551 | |||
552 | saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 2); | ||
553 | saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3); | ||
554 | break; | ||
555 | } | ||
556 | } | ||
557 | |||
558 | static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data) | ||
559 | { | ||
560 | struct tveeprom tv; | ||
561 | |||
562 | /* TODO: Assumption: eeprom on bus 0 */ | ||
563 | tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv, | ||
564 | eeprom_data); | ||
565 | |||
566 | /* Make sure we support the board model */ | ||
567 | switch (tv.model) { | ||
568 | case 88001: | ||
569 | /* Development board - Limit circulation */ | ||
570 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
571 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ | ||
572 | case 88021: | ||
573 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
574 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, MCE CIR, FM */ | ||
575 | break; | ||
576 | case 88041: | ||
577 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
578 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ | ||
579 | break; | ||
580 | case 88061: | ||
581 | /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) | ||
582 | * ATSC/QAM (TDA18271/S5H1411) and basic analog, FM */ | ||
583 | break; | ||
584 | case 89519: | ||
585 | case 89609: | ||
586 | /* WinTV-HVR2200 (PCIe, Retail, full-height) | ||
587 | * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ | ||
588 | break; | ||
589 | case 89619: | ||
590 | /* WinTV-HVR2200 (PCIe, Retail, half-height) | ||
591 | * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ | ||
592 | break; | ||
593 | default: | ||
594 | printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n", | ||
595 | dev->name, tv.model); | ||
596 | break; | ||
597 | } | ||
598 | |||
599 | printk(KERN_INFO "%s: Hauppauge eeprom: model=%d\n", dev->name, | ||
600 | tv.model); | ||
601 | } | ||
602 | |||
603 | void saa7164_card_setup(struct saa7164_dev *dev) | ||
604 | { | ||
605 | static u8 eeprom[256]; | ||
606 | |||
607 | if (dev->i2c_bus[0].i2c_rc == 0) { | ||
608 | if (saa7164_api_read_eeprom(dev, &eeprom[0], | ||
609 | sizeof(eeprom)) < 0) | ||
610 | return; | ||
611 | } | ||
612 | |||
613 | switch (dev->board) { | ||
614 | case SAA7164_BOARD_HAUPPAUGE_HVR2200: | ||
615 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: | ||
616 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: | ||
617 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: | ||
618 | case SAA7164_BOARD_HAUPPAUGE_HVR2250: | ||
619 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: | ||
620 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: | ||
621 | hauppauge_eeprom(dev, &eeprom[0]); | ||
622 | break; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | /* With most other drivers, the kernel expects to communicate with subdrivers | ||
627 | * through i2c. This bridge does not allow that, it does not expose any direct | ||
628 | * access to I2C. Instead we have to communicate through the device f/w for | ||
629 | * register access to 'processing units'. Each unit has a unique | ||
630 | * id, regardless of how the physical implementation occurs across | ||
631 | * the three physical i2c busses. The being said if we want leverge of | ||
632 | * the existing kernel drivers for tuners and demods we have to 'speak i2c', | ||
633 | * to this bridge implements 3 virtual i2c buses. This is a helper function | ||
634 | * for those. | ||
635 | * | ||
636 | * Description: Translate the kernels notion of an i2c address and bus into | ||
637 | * the appropriate unitid. | ||
638 | */ | ||
639 | int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr) | ||
640 | { | ||
641 | /* For a given bus and i2c device address, return the saa7164 unique | ||
642 | * unitid. < 0 on error */ | ||
643 | |||
644 | struct saa7164_dev *dev = bus->dev; | ||
645 | struct saa7164_unit *unit; | ||
646 | int i; | ||
647 | |||
648 | for (i = 0; i < SAA7164_MAX_UNITS; i++) { | ||
649 | unit = &saa7164_boards[dev->board].unit[i]; | ||
650 | |||
651 | if (unit->type == SAA7164_UNIT_UNDEFINED) | ||
652 | continue; | ||
653 | if ((bus->nr == unit->i2c_bus_nr) && | ||
654 | (addr == unit->i2c_bus_addr)) | ||
655 | return unit->id; | ||
656 | } | ||
657 | |||
658 | return -1; | ||
659 | } | ||
660 | |||
661 | /* The 7164 API needs to know the i2c register length in advance. | ||
662 | * this is a helper function. Based on a specific chip addr and bus return the | ||
663 | * reg length. | ||
664 | */ | ||
665 | int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr) | ||
666 | { | ||
667 | /* For a given bus and i2c device address, return the | ||
668 | * saa7164 registry address width. < 0 on error | ||
669 | */ | ||
670 | |||
671 | struct saa7164_dev *dev = bus->dev; | ||
672 | struct saa7164_unit *unit; | ||
673 | int i; | ||
674 | |||
675 | for (i = 0; i < SAA7164_MAX_UNITS; i++) { | ||
676 | unit = &saa7164_boards[dev->board].unit[i]; | ||
677 | |||
678 | if (unit->type == SAA7164_UNIT_UNDEFINED) | ||
679 | continue; | ||
680 | |||
681 | if ((bus->nr == unit->i2c_bus_nr) && | ||
682 | (addr == unit->i2c_bus_addr)) | ||
683 | return unit->i2c_reg_len; | ||
684 | } | ||
685 | |||
686 | return -1; | ||
687 | } | ||
688 | /* TODO: implement a 'findeeprom' functio like the above and fix any other | ||
689 | * eeprom related todo's in -api.c. | ||
690 | */ | ||
691 | |||
692 | /* Translate a unitid into a x readable device name, for display purposes. */ | ||
693 | char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid) | ||
694 | { | ||
695 | char *undefed = "UNDEFINED"; | ||
696 | char *bridge = "BRIDGE"; | ||
697 | struct saa7164_unit *unit; | ||
698 | int i; | ||
699 | |||
700 | if (unitid == 0) | ||
701 | return bridge; | ||
702 | |||
703 | for (i = 0; i < SAA7164_MAX_UNITS; i++) { | ||
704 | unit = &saa7164_boards[dev->board].unit[i]; | ||
705 | |||
706 | if (unit->type == SAA7164_UNIT_UNDEFINED) | ||
707 | continue; | ||
708 | |||
709 | if (unitid == unit->id) | ||
710 | return unit->name; | ||
711 | } | ||
712 | |||
713 | return undefed; | ||
714 | } | ||
715 | |||
diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c new file mode 100644 index 00000000000..62fac7f9d04 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-cmd.c | |||
@@ -0,0 +1,589 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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_irq_dequeue(struct saa7164_dev *dev) | ||
84 | { | ||
85 | int ret = SAA_OK, i = 0; | ||
86 | u32 timeout; | ||
87 | wait_queue_head_t *q = NULL; | ||
88 | u8 tmp[512]; | ||
89 | dprintk(DBGLVL_CMD, "%s()\n", __func__); | ||
90 | |||
91 | /* While any outstand message on the bus exists... */ | ||
92 | do { | ||
93 | |||
94 | /* Peek the msg bus */ | ||
95 | struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 }; | ||
96 | ret = saa7164_bus_get(dev, &tRsp, NULL, 1); | ||
97 | if (ret != SAA_OK) | ||
98 | break; | ||
99 | |||
100 | q = &dev->cmds[tRsp.seqno].wait; | ||
101 | timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); | ||
102 | dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); | ||
103 | if (!timeout) { | ||
104 | dprintk(DBGLVL_CMD, | ||
105 | "%s() signalled seqno(%d) (for dequeue)\n", | ||
106 | __func__, tRsp.seqno); | ||
107 | dev->cmds[tRsp.seqno].signalled = 1; | ||
108 | wake_up(q); | ||
109 | } else { | ||
110 | printk(KERN_ERR | ||
111 | "%s() found timed out command on the bus\n", | ||
112 | __func__); | ||
113 | |||
114 | /* Clean the bus */ | ||
115 | ret = saa7164_bus_get(dev, &tRsp, &tmp, 0); | ||
116 | printk(KERN_ERR "%s() ret = %x\n", __func__, ret); | ||
117 | if (ret == SAA_ERR_EMPTY) | ||
118 | /* Someone else already fetched the response */ | ||
119 | return SAA_OK; | ||
120 | |||
121 | if (ret != SAA_OK) | ||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | /* It's unlikely to have more than 4 or 5 pending messages, | ||
126 | * ensure we exit at some point regardless. | ||
127 | */ | ||
128 | } while (i++ < 32); | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | /* Commands to the f/w get marshelled to/from this code then onto the PCI | ||
134 | * -bus/c running buffer. */ | ||
135 | int saa7164_cmd_dequeue(struct saa7164_dev *dev) | ||
136 | { | ||
137 | int loop = 1; | ||
138 | int ret; | ||
139 | u32 timeout; | ||
140 | wait_queue_head_t *q = NULL; | ||
141 | u8 tmp[512]; | ||
142 | dprintk(DBGLVL_CMD, "%s()\n", __func__); | ||
143 | |||
144 | while (loop) { | ||
145 | |||
146 | struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 }; | ||
147 | ret = saa7164_bus_get(dev, &tRsp, NULL, 1); | ||
148 | if (ret == SAA_ERR_EMPTY) | ||
149 | return SAA_OK; | ||
150 | |||
151 | if (ret != SAA_OK) | ||
152 | return ret; | ||
153 | |||
154 | q = &dev->cmds[tRsp.seqno].wait; | ||
155 | timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); | ||
156 | dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); | ||
157 | if (timeout) { | ||
158 | printk(KERN_ERR "found timed out command on the bus\n"); | ||
159 | |||
160 | /* Clean the bus */ | ||
161 | ret = saa7164_bus_get(dev, &tRsp, &tmp, 0); | ||
162 | printk(KERN_ERR "ret = %x\n", ret); | ||
163 | if (ret == SAA_ERR_EMPTY) | ||
164 | /* Someone else already fetched the response */ | ||
165 | return SAA_OK; | ||
166 | |||
167 | if (ret != SAA_OK) | ||
168 | return ret; | ||
169 | |||
170 | if (tRsp.flags & PVC_CMDFLAG_CONTINUE) | ||
171 | printk(KERN_ERR "split response\n"); | ||
172 | else | ||
173 | saa7164_cmd_free_seqno(dev, tRsp.seqno); | ||
174 | |||
175 | printk(KERN_ERR " timeout continue\n"); | ||
176 | continue; | ||
177 | } | ||
178 | |||
179 | dprintk(DBGLVL_CMD, "%s() signalled seqno(%d) (for dequeue)\n", | ||
180 | __func__, tRsp.seqno); | ||
181 | dev->cmds[tRsp.seqno].signalled = 1; | ||
182 | wake_up(q); | ||
183 | return SAA_OK; | ||
184 | } | ||
185 | |||
186 | return SAA_OK; | ||
187 | } | ||
188 | |||
189 | int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg, | ||
190 | void *buf) | ||
191 | { | ||
192 | struct tmComResBusInfo *bus = &dev->bus; | ||
193 | u8 cmd_sent; | ||
194 | u16 size, idx; | ||
195 | u32 cmds; | ||
196 | void *tmp; | ||
197 | int ret = -1; | ||
198 | |||
199 | if (!msg) { | ||
200 | printk(KERN_ERR "%s() !msg\n", __func__); | ||
201 | return SAA_ERR_BAD_PARAMETER; | ||
202 | } | ||
203 | |||
204 | mutex_lock(&dev->cmds[msg->id].lock); | ||
205 | |||
206 | size = msg->size; | ||
207 | idx = 0; | ||
208 | cmds = size / bus->m_wMaxReqSize; | ||
209 | if (size % bus->m_wMaxReqSize == 0) | ||
210 | cmds -= 1; | ||
211 | |||
212 | cmd_sent = 0; | ||
213 | |||
214 | /* Split the request into smaller chunks */ | ||
215 | for (idx = 0; idx < cmds; idx++) { | ||
216 | |||
217 | msg->flags |= SAA_CMDFLAG_CONTINUE; | ||
218 | msg->size = bus->m_wMaxReqSize; | ||
219 | tmp = buf + idx * bus->m_wMaxReqSize; | ||
220 | |||
221 | ret = saa7164_bus_set(dev, msg, tmp); | ||
222 | if (ret != SAA_OK) { | ||
223 | printk(KERN_ERR "%s() set failed %d\n", __func__, ret); | ||
224 | |||
225 | if (cmd_sent) { | ||
226 | ret = SAA_ERR_BUSY; | ||
227 | goto out; | ||
228 | } | ||
229 | ret = SAA_ERR_OVERFLOW; | ||
230 | goto out; | ||
231 | } | ||
232 | cmd_sent = 1; | ||
233 | } | ||
234 | |||
235 | /* If not the last command... */ | ||
236 | if (idx != 0) | ||
237 | msg->flags &= ~SAA_CMDFLAG_CONTINUE; | ||
238 | |||
239 | msg->size = size - idx * bus->m_wMaxReqSize; | ||
240 | |||
241 | ret = saa7164_bus_set(dev, msg, buf + idx * bus->m_wMaxReqSize); | ||
242 | if (ret != SAA_OK) { | ||
243 | printk(KERN_ERR "%s() set last failed %d\n", __func__, ret); | ||
244 | |||
245 | if (cmd_sent) { | ||
246 | ret = SAA_ERR_BUSY; | ||
247 | goto out; | ||
248 | } | ||
249 | ret = SAA_ERR_OVERFLOW; | ||
250 | goto out; | ||
251 | } | ||
252 | ret = SAA_OK; | ||
253 | |||
254 | out: | ||
255 | mutex_unlock(&dev->cmds[msg->id].lock); | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | /* Wait for a signal event, without holding a mutex. Either return TIMEOUT if | ||
260 | * the event never occurred, or SAA_OK if it was signaled during the wait. | ||
261 | */ | ||
262 | int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno) | ||
263 | { | ||
264 | wait_queue_head_t *q = NULL; | ||
265 | int ret = SAA_BUS_TIMEOUT; | ||
266 | unsigned long stamp; | ||
267 | int r; | ||
268 | |||
269 | if (saa_debug >= 4) | ||
270 | saa7164_bus_dump(dev); | ||
271 | |||
272 | dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno); | ||
273 | |||
274 | mutex_lock(&dev->lock); | ||
275 | if ((dev->cmds[seqno].inuse == 1) && | ||
276 | (dev->cmds[seqno].seqno == seqno)) { | ||
277 | q = &dev->cmds[seqno].wait; | ||
278 | } | ||
279 | mutex_unlock(&dev->lock); | ||
280 | |||
281 | if (q) { | ||
282 | /* If we haven't been signalled we need to wait */ | ||
283 | if (dev->cmds[seqno].signalled == 0) { | ||
284 | stamp = jiffies; | ||
285 | dprintk(DBGLVL_CMD, | ||
286 | "%s(seqno=%d) Waiting (signalled=%d)\n", | ||
287 | __func__, seqno, dev->cmds[seqno].signalled); | ||
288 | |||
289 | /* Wait for signalled to be flagged or timeout */ | ||
290 | /* In a highly stressed system this can easily extend | ||
291 | * into multiple seconds before the deferred worker | ||
292 | * is scheduled, and we're woken up via signal. | ||
293 | * We typically are signalled in < 50ms but it can | ||
294 | * take MUCH longer. | ||
295 | */ | ||
296 | wait_event_timeout(*q, dev->cmds[seqno].signalled, | ||
297 | (HZ * waitsecs)); | ||
298 | r = time_before(jiffies, stamp + (HZ * waitsecs)); | ||
299 | if (r) | ||
300 | ret = SAA_OK; | ||
301 | else | ||
302 | saa7164_cmd_timeout_seqno(dev, seqno); | ||
303 | |||
304 | dprintk(DBGLVL_CMD, "%s(seqno=%d) Waiting res = %d " | ||
305 | "(signalled=%d)\n", __func__, seqno, r, | ||
306 | dev->cmds[seqno].signalled); | ||
307 | } else | ||
308 | ret = SAA_OK; | ||
309 | } else | ||
310 | printk(KERN_ERR "%s(seqno=%d) seqno is invalid\n", | ||
311 | __func__, seqno); | ||
312 | |||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno) | ||
317 | { | ||
318 | int i; | ||
319 | dprintk(DBGLVL_CMD, "%s()\n", __func__); | ||
320 | |||
321 | mutex_lock(&dev->lock); | ||
322 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
323 | if (dev->cmds[i].inuse == 1) { | ||
324 | dprintk(DBGLVL_CMD, | ||
325 | "seqno %d inuse, sig = %d, t/out = %d\n", | ||
326 | dev->cmds[i].seqno, | ||
327 | dev->cmds[i].signalled, | ||
328 | dev->cmds[i].timeout); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
333 | if ((dev->cmds[i].inuse == 1) && ((i == 0) || | ||
334 | (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) { | ||
335 | dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n", | ||
336 | __func__, i); | ||
337 | dev->cmds[i].signalled = 1; | ||
338 | wake_up(&dev->cmds[i].wait); | ||
339 | } | ||
340 | } | ||
341 | mutex_unlock(&dev->lock); | ||
342 | } | ||
343 | |||
344 | int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command, | ||
345 | u16 controlselector, u16 size, void *buf) | ||
346 | { | ||
347 | struct tmComResInfo command_t, *pcommand_t; | ||
348 | struct tmComResInfo response_t, *presponse_t; | ||
349 | u8 errdata[256]; | ||
350 | u16 resp_dsize; | ||
351 | u16 data_recd; | ||
352 | u32 loop; | ||
353 | int ret; | ||
354 | int safety = 0; | ||
355 | |||
356 | dprintk(DBGLVL_CMD, "%s(unitid = %s (%d) , command = 0x%x, " | ||
357 | "sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id, | ||
358 | command, controlselector); | ||
359 | |||
360 | if ((size == 0) || (buf == NULL)) { | ||
361 | printk(KERN_ERR "%s() Invalid param\n", __func__); | ||
362 | return SAA_ERR_BAD_PARAMETER; | ||
363 | } | ||
364 | |||
365 | /* Prepare some basic command/response structures */ | ||
366 | memset(&command_t, 0, sizeof(command_t)); | ||
367 | memset(&response_t, 0, sizeof(response_t)); | ||
368 | pcommand_t = &command_t; | ||
369 | presponse_t = &response_t; | ||
370 | command_t.id = id; | ||
371 | command_t.command = command; | ||
372 | command_t.controlselector = controlselector; | ||
373 | command_t.size = size; | ||
374 | |||
375 | /* Allocate a unique sequence number */ | ||
376 | ret = saa7164_cmd_alloc_seqno(dev); | ||
377 | if (ret < 0) { | ||
378 | printk(KERN_ERR "%s() No free sequences\n", __func__); | ||
379 | ret = SAA_ERR_NO_RESOURCES; | ||
380 | goto out; | ||
381 | } | ||
382 | |||
383 | command_t.seqno = (u8)ret; | ||
384 | |||
385 | /* Send Command */ | ||
386 | resp_dsize = size; | ||
387 | pcommand_t->size = size; | ||
388 | |||
389 | dprintk(DBGLVL_CMD, "%s() pcommand_t.seqno = %d\n", | ||
390 | __func__, pcommand_t->seqno); | ||
391 | |||
392 | dprintk(DBGLVL_CMD, "%s() pcommand_t.size = %d\n", | ||
393 | __func__, pcommand_t->size); | ||
394 | |||
395 | ret = saa7164_cmd_set(dev, pcommand_t, buf); | ||
396 | if (ret != SAA_OK) { | ||
397 | printk(KERN_ERR "%s() set command failed %d\n", __func__, ret); | ||
398 | |||
399 | if (ret != SAA_ERR_BUSY) | ||
400 | saa7164_cmd_free_seqno(dev, pcommand_t->seqno); | ||
401 | else | ||
402 | /* Flag a timeout, because at least one | ||
403 | * command was sent */ | ||
404 | saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); | ||
405 | |||
406 | goto out; | ||
407 | } | ||
408 | |||
409 | /* With split responses we have to collect the msgs piece by piece */ | ||
410 | data_recd = 0; | ||
411 | loop = 1; | ||
412 | while (loop) { | ||
413 | dprintk(DBGLVL_CMD, "%s() loop\n", __func__); | ||
414 | |||
415 | ret = saa7164_cmd_wait(dev, pcommand_t->seqno); | ||
416 | dprintk(DBGLVL_CMD, "%s() loop ret = %d\n", __func__, ret); | ||
417 | |||
418 | /* if power is down and this is not a power command ... */ | ||
419 | |||
420 | if (ret == SAA_BUS_TIMEOUT) { | ||
421 | printk(KERN_ERR "Event timed out\n"); | ||
422 | saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); | ||
423 | return ret; | ||
424 | } | ||
425 | |||
426 | if (ret != SAA_OK) { | ||
427 | printk(KERN_ERR "spurious error\n"); | ||
428 | return ret; | ||
429 | } | ||
430 | |||
431 | /* Peek response */ | ||
432 | ret = saa7164_bus_get(dev, presponse_t, NULL, 1); | ||
433 | if (ret == SAA_ERR_EMPTY) { | ||
434 | dprintk(4, "%s() SAA_ERR_EMPTY\n", __func__); | ||
435 | continue; | ||
436 | } | ||
437 | if (ret != SAA_OK) { | ||
438 | printk(KERN_ERR "peek failed\n"); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | dprintk(DBGLVL_CMD, "%s() presponse_t->seqno = %d\n", | ||
443 | __func__, presponse_t->seqno); | ||
444 | |||
445 | dprintk(DBGLVL_CMD, "%s() presponse_t->flags = 0x%x\n", | ||
446 | __func__, presponse_t->flags); | ||
447 | |||
448 | dprintk(DBGLVL_CMD, "%s() presponse_t->size = %d\n", | ||
449 | __func__, presponse_t->size); | ||
450 | |||
451 | /* Check if the response was for our command */ | ||
452 | if (presponse_t->seqno != pcommand_t->seqno) { | ||
453 | |||
454 | dprintk(DBGLVL_CMD, | ||
455 | "wrong event: seqno = %d, " | ||
456 | "expected seqno = %d, " | ||
457 | "will dequeue regardless\n", | ||
458 | presponse_t->seqno, pcommand_t->seqno); | ||
459 | |||
460 | ret = saa7164_cmd_dequeue(dev); | ||
461 | if (ret != SAA_OK) { | ||
462 | printk(KERN_ERR "dequeue failed, ret = %d\n", | ||
463 | ret); | ||
464 | if (safety++ > 16) { | ||
465 | printk(KERN_ERR | ||
466 | "dequeue exceeded, safety exit\n"); | ||
467 | return SAA_ERR_BUSY; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | continue; | ||
472 | } | ||
473 | |||
474 | if ((presponse_t->flags & PVC_RESPONSEFLAG_ERROR) != 0) { | ||
475 | |||
476 | memset(&errdata[0], 0, sizeof(errdata)); | ||
477 | |||
478 | ret = saa7164_bus_get(dev, presponse_t, &errdata[0], 0); | ||
479 | if (ret != SAA_OK) { | ||
480 | printk(KERN_ERR "get error(2)\n"); | ||
481 | return ret; | ||
482 | } | ||
483 | |||
484 | saa7164_cmd_free_seqno(dev, pcommand_t->seqno); | ||
485 | |||
486 | dprintk(DBGLVL_CMD, "%s() errdata %02x%02x%02x%02x\n", | ||
487 | __func__, errdata[0], errdata[1], errdata[2], | ||
488 | errdata[3]); | ||
489 | |||
490 | /* Map error codes */ | ||
491 | dprintk(DBGLVL_CMD, "%s() cmd, error code = 0x%x\n", | ||
492 | __func__, errdata[0]); | ||
493 | |||
494 | switch (errdata[0]) { | ||
495 | case PVC_ERRORCODE_INVALID_COMMAND: | ||
496 | dprintk(DBGLVL_CMD, "%s() INVALID_COMMAND\n", | ||
497 | __func__); | ||
498 | ret = SAA_ERR_INVALID_COMMAND; | ||
499 | break; | ||
500 | case PVC_ERRORCODE_INVALID_DATA: | ||
501 | dprintk(DBGLVL_CMD, "%s() INVALID_DATA\n", | ||
502 | __func__); | ||
503 | ret = SAA_ERR_BAD_PARAMETER; | ||
504 | break; | ||
505 | case PVC_ERRORCODE_TIMEOUT: | ||
506 | dprintk(DBGLVL_CMD, "%s() TIMEOUT\n", __func__); | ||
507 | ret = SAA_ERR_TIMEOUT; | ||
508 | break; | ||
509 | case PVC_ERRORCODE_NAK: | ||
510 | dprintk(DBGLVL_CMD, "%s() NAK\n", __func__); | ||
511 | ret = SAA_ERR_NULL_PACKET; | ||
512 | break; | ||
513 | case PVC_ERRORCODE_UNKNOWN: | ||
514 | case PVC_ERRORCODE_INVALID_CONTROL: | ||
515 | dprintk(DBGLVL_CMD, | ||
516 | "%s() UNKNOWN OR INVALID CONTROL\n", | ||
517 | __func__); | ||
518 | default: | ||
519 | dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__); | ||
520 | ret = SAA_ERR_NOT_SUPPORTED; | ||
521 | } | ||
522 | |||
523 | /* See of other commands are on the bus */ | ||
524 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
525 | printk(KERN_ERR "dequeue(2) failed\n"); | ||
526 | |||
527 | return ret; | ||
528 | } | ||
529 | |||
530 | /* If response is invalid */ | ||
531 | if ((presponse_t->id != pcommand_t->id) || | ||
532 | (presponse_t->command != pcommand_t->command) || | ||
533 | (presponse_t->controlselector != | ||
534 | pcommand_t->controlselector) || | ||
535 | (((resp_dsize - data_recd) != presponse_t->size) && | ||
536 | !(presponse_t->flags & PVC_CMDFLAG_CONTINUE)) || | ||
537 | ((resp_dsize - data_recd) < presponse_t->size)) { | ||
538 | |||
539 | /* Invalid */ | ||
540 | dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__); | ||
541 | ret = saa7164_bus_get(dev, presponse_t, NULL, 0); | ||
542 | if (ret != SAA_OK) { | ||
543 | printk(KERN_ERR "get failed\n"); | ||
544 | return ret; | ||
545 | } | ||
546 | |||
547 | /* See of other commands are on the bus */ | ||
548 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
549 | printk(KERN_ERR "dequeue(3) failed\n"); | ||
550 | continue; | ||
551 | } | ||
552 | |||
553 | /* OK, now we're actually getting out correct response */ | ||
554 | ret = saa7164_bus_get(dev, presponse_t, buf + data_recd, 0); | ||
555 | if (ret != SAA_OK) { | ||
556 | printk(KERN_ERR "get failed\n"); | ||
557 | return ret; | ||
558 | } | ||
559 | |||
560 | data_recd = presponse_t->size + data_recd; | ||
561 | if (resp_dsize == data_recd) { | ||
562 | dprintk(DBGLVL_CMD, "%s() Resp recd\n", __func__); | ||
563 | break; | ||
564 | } | ||
565 | |||
566 | /* See of other commands are on the bus */ | ||
567 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
568 | printk(KERN_ERR "dequeue(3) failed\n"); | ||
569 | |||
570 | continue; | ||
571 | |||
572 | } /* (loop) */ | ||
573 | |||
574 | /* Release the sequence number allocation */ | ||
575 | saa7164_cmd_free_seqno(dev, pcommand_t->seqno); | ||
576 | |||
577 | /* if powerdown signal all pending commands */ | ||
578 | |||
579 | dprintk(DBGLVL_CMD, "%s() Calling dequeue then exit\n", __func__); | ||
580 | |||
581 | /* See of other commands are on the bus */ | ||
582 | if (saa7164_cmd_dequeue(dev) != SAA_OK) | ||
583 | printk(KERN_ERR "dequeue(4) failed\n"); | ||
584 | |||
585 | ret = SAA_OK; | ||
586 | out: | ||
587 | return ret; | ||
588 | } | ||
589 | |||
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c new file mode 100644 index 00000000000..3b7d7b4e303 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-core.c | |||
@@ -0,0 +1,1526 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | #ifdef CONFIG_PROC_FS | ||
34 | #include <linux/proc_fs.h> | ||
35 | #endif | ||
36 | #include "saa7164.h" | ||
37 | |||
38 | MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards"); | ||
39 | MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | /* | ||
43 | * 1 Basic | ||
44 | * 2 | ||
45 | * 4 i2c | ||
46 | * 8 api | ||
47 | * 16 cmd | ||
48 | * 32 bus | ||
49 | */ | ||
50 | |||
51 | unsigned int saa_debug; | ||
52 | module_param_named(debug, saa_debug, int, 0644); | ||
53 | MODULE_PARM_DESC(debug, "enable debug messages"); | ||
54 | |||
55 | unsigned int fw_debug; | ||
56 | module_param(fw_debug, int, 0644); | ||
57 | MODULE_PARM_DESC(fw_debug, "Firware debug level def:2"); | ||
58 | |||
59 | unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS; | ||
60 | module_param(encoder_buffers, int, 0644); | ||
61 | MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64"); | ||
62 | |||
63 | unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS; | ||
64 | module_param(vbi_buffers, int, 0644); | ||
65 | MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64"); | ||
66 | |||
67 | unsigned int waitsecs = 10; | ||
68 | module_param(waitsecs, int, 0644); | ||
69 | MODULE_PARM_DESC(waitsecs, "timeout on firmware messages"); | ||
70 | |||
71 | static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; | ||
72 | module_param_array(card, int, NULL, 0444); | ||
73 | MODULE_PARM_DESC(card, "card type"); | ||
74 | |||
75 | unsigned int print_histogram = 64; | ||
76 | module_param(print_histogram, int, 0644); | ||
77 | MODULE_PARM_DESC(print_histogram, "print histogram values once"); | ||
78 | |||
79 | unsigned int crc_checking = 1; | ||
80 | module_param(crc_checking, int, 0644); | ||
81 | MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers"); | ||
82 | |||
83 | unsigned int guard_checking = 1; | ||
84 | module_param(guard_checking, int, 0644); | ||
85 | MODULE_PARM_DESC(guard_checking, | ||
86 | "enable dma sanity checking for buffer overruns"); | ||
87 | |||
88 | static unsigned int saa7164_devcount; | ||
89 | |||
90 | static DEFINE_MUTEX(devlist); | ||
91 | LIST_HEAD(saa7164_devlist); | ||
92 | |||
93 | #define INT_SIZE 16 | ||
94 | |||
95 | void saa7164_dumphex16FF(struct saa7164_dev *dev, u8 *buf, int len) | ||
96 | { | ||
97 | int i; | ||
98 | u8 tmp[16]; | ||
99 | memset(&tmp[0], 0xff, sizeof(tmp)); | ||
100 | |||
101 | printk(KERN_INFO "--------------------> " | ||
102 | "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
103 | |||
104 | for (i = 0; i < len; i += 16) { | ||
105 | if (memcmp(&tmp, buf + i, sizeof(tmp)) != 0) { | ||
106 | printk(KERN_INFO " [0x%08x] " | ||
107 | "%02x %02x %02x %02x %02x %02x %02x %02x " | ||
108 | "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, | ||
109 | *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3), | ||
110 | *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7), | ||
111 | *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11), | ||
112 | *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15)); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | static void saa7164_pack_verifier(struct saa7164_buffer *buf) | ||
118 | { | ||
119 | u8 *p = (u8 *)buf->cpu; | ||
120 | int i; | ||
121 | |||
122 | for (i = 0; i < buf->actual_size; i += 2048) { | ||
123 | |||
124 | if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) || | ||
125 | (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) { | ||
126 | printk(KERN_ERR "No pack at 0x%x\n", i); | ||
127 | #if 0 | ||
128 | saa7164_dumphex16FF(buf->port->dev, (p + i), 32); | ||
129 | #endif | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | #define FIXED_VIDEO_PID 0xf1 | ||
135 | #define FIXED_AUDIO_PID 0xf2 | ||
136 | |||
137 | static void saa7164_ts_verifier(struct saa7164_buffer *buf) | ||
138 | { | ||
139 | struct saa7164_port *port = buf->port; | ||
140 | u32 i; | ||
141 | u8 cc, a; | ||
142 | u16 pid; | ||
143 | u8 __iomem *bufcpu = (u8 *)buf->cpu; | ||
144 | |||
145 | port->sync_errors = 0; | ||
146 | port->v_cc_errors = 0; | ||
147 | port->a_cc_errors = 0; | ||
148 | |||
149 | for (i = 0; i < buf->actual_size; i += 188) { | ||
150 | if (*(bufcpu + i) != 0x47) | ||
151 | port->sync_errors++; | ||
152 | |||
153 | /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */ | ||
154 | pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2); | ||
155 | cc = *(bufcpu + i + 3) & 0x0f; | ||
156 | |||
157 | if (pid == FIXED_VIDEO_PID) { | ||
158 | a = ((port->last_v_cc + 1) & 0x0f); | ||
159 | if (a != cc) { | ||
160 | printk(KERN_ERR "video cc last = %x current = %x i = %d\n", | ||
161 | port->last_v_cc, cc, i); | ||
162 | port->v_cc_errors++; | ||
163 | } | ||
164 | |||
165 | port->last_v_cc = cc; | ||
166 | } else | ||
167 | if (pid == FIXED_AUDIO_PID) { | ||
168 | a = ((port->last_a_cc + 1) & 0x0f); | ||
169 | if (a != cc) { | ||
170 | printk(KERN_ERR "audio cc last = %x current = %x i = %d\n", | ||
171 | port->last_a_cc, cc, i); | ||
172 | port->a_cc_errors++; | ||
173 | } | ||
174 | |||
175 | port->last_a_cc = cc; | ||
176 | } | ||
177 | |||
178 | } | ||
179 | |||
180 | /* Only report errors if we've been through this function atleast | ||
181 | * once already and the cached cc values are primed. First time through | ||
182 | * always generates errors. | ||
183 | */ | ||
184 | if (port->v_cc_errors && (port->done_first_interrupt > 1)) | ||
185 | printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors); | ||
186 | |||
187 | if (port->a_cc_errors && (port->done_first_interrupt > 1)) | ||
188 | printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors); | ||
189 | |||
190 | if (port->sync_errors && (port->done_first_interrupt > 1)) | ||
191 | printk(KERN_ERR "sync_errors = %d\n", port->sync_errors); | ||
192 | |||
193 | if (port->done_first_interrupt == 1) | ||
194 | port->done_first_interrupt++; | ||
195 | } | ||
196 | |||
197 | static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name) | ||
198 | { | ||
199 | int i; | ||
200 | |||
201 | memset(hg, 0, sizeof(struct saa7164_histogram)); | ||
202 | strcpy(hg->name, name); | ||
203 | |||
204 | /* First 30ms x 1ms */ | ||
205 | for (i = 0; i < 30; i++) | ||
206 | hg->counter1[0 + i].val = i; | ||
207 | |||
208 | /* 30 - 200ms x 10ms */ | ||
209 | for (i = 0; i < 18; i++) | ||
210 | hg->counter1[30 + i].val = 30 + (i * 10); | ||
211 | |||
212 | /* 200 - 2000ms x 100ms */ | ||
213 | for (i = 0; i < 15; i++) | ||
214 | hg->counter1[48 + i].val = 200 + (i * 200); | ||
215 | |||
216 | /* Catch all massive value (2secs) */ | ||
217 | hg->counter1[55].val = 2000; | ||
218 | |||
219 | /* Catch all massive value (4secs) */ | ||
220 | hg->counter1[56].val = 4000; | ||
221 | |||
222 | /* Catch all massive value (8secs) */ | ||
223 | hg->counter1[57].val = 8000; | ||
224 | |||
225 | /* Catch all massive value (15secs) */ | ||
226 | hg->counter1[58].val = 15000; | ||
227 | |||
228 | /* Catch all massive value (30secs) */ | ||
229 | hg->counter1[59].val = 30000; | ||
230 | |||
231 | /* Catch all massive value (60secs) */ | ||
232 | hg->counter1[60].val = 60000; | ||
233 | |||
234 | /* Catch all massive value (5mins) */ | ||
235 | hg->counter1[61].val = 300000; | ||
236 | |||
237 | /* Catch all massive value (15mins) */ | ||
238 | hg->counter1[62].val = 900000; | ||
239 | |||
240 | /* Catch all massive values (1hr) */ | ||
241 | hg->counter1[63].val = 3600000; | ||
242 | } | ||
243 | |||
244 | void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val) | ||
245 | { | ||
246 | int i; | ||
247 | for (i = 0; i < 64; i++) { | ||
248 | if (val <= hg->counter1[i].val) { | ||
249 | hg->counter1[i].count++; | ||
250 | hg->counter1[i].update_time = jiffies; | ||
251 | break; | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | static void saa7164_histogram_print(struct saa7164_port *port, | ||
257 | struct saa7164_histogram *hg) | ||
258 | { | ||
259 | u32 entries = 0; | ||
260 | int i; | ||
261 | |||
262 | printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name); | ||
263 | for (i = 0; i < 64; i++) { | ||
264 | if (hg->counter1[i].count == 0) | ||
265 | continue; | ||
266 | |||
267 | printk(KERN_ERR " %4d %12d %Ld\n", | ||
268 | hg->counter1[i].val, | ||
269 | hg->counter1[i].count, | ||
270 | hg->counter1[i].update_time); | ||
271 | |||
272 | entries++; | ||
273 | } | ||
274 | printk(KERN_ERR "Total: %d\n", entries); | ||
275 | } | ||
276 | |||
277 | static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) | ||
278 | { | ||
279 | struct saa7164_dev *dev = port->dev; | ||
280 | struct saa7164_buffer *buf = NULL; | ||
281 | struct saa7164_user_buffer *ubuf = NULL; | ||
282 | struct list_head *c, *n; | ||
283 | int i = 0; | ||
284 | u8 __iomem *p; | ||
285 | |||
286 | mutex_lock(&port->dmaqueue_lock); | ||
287 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
288 | |||
289 | buf = list_entry(c, struct saa7164_buffer, list); | ||
290 | if (i++ > port->hwcfg.buffercount) { | ||
291 | printk(KERN_ERR "%s() illegal i count %d\n", | ||
292 | __func__, i); | ||
293 | break; | ||
294 | } | ||
295 | |||
296 | if (buf->idx == bufnr) { | ||
297 | |||
298 | /* Found the buffer, deal with it */ | ||
299 | dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr); | ||
300 | |||
301 | if (crc_checking) { | ||
302 | /* Throw a new checksum on the dma buffer */ | ||
303 | buf->crc = crc32(0, buf->cpu, buf->actual_size); | ||
304 | } | ||
305 | |||
306 | if (guard_checking) { | ||
307 | p = (u8 *)buf->cpu; | ||
308 | if ((*(p + buf->actual_size + 0) != 0xff) || | ||
309 | (*(p + buf->actual_size + 1) != 0xff) || | ||
310 | (*(p + buf->actual_size + 2) != 0xff) || | ||
311 | (*(p + buf->actual_size + 3) != 0xff) || | ||
312 | (*(p + buf->actual_size + 0x10) != 0xff) || | ||
313 | (*(p + buf->actual_size + 0x11) != 0xff) || | ||
314 | (*(p + buf->actual_size + 0x12) != 0xff) || | ||
315 | (*(p + buf->actual_size + 0x13) != 0xff)) { | ||
316 | printk(KERN_ERR "%s() buf %p guard buffer breach\n", | ||
317 | __func__, buf); | ||
318 | #if 0 | ||
319 | saa7164_dumphex16FF(dev, (p + buf->actual_size) - 32 , 64); | ||
320 | #endif | ||
321 | } | ||
322 | } | ||
323 | |||
324 | if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) { | ||
325 | /* Validate the incoming buffer content */ | ||
326 | if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) | ||
327 | saa7164_ts_verifier(buf); | ||
328 | else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) | ||
329 | saa7164_pack_verifier(buf); | ||
330 | } | ||
331 | |||
332 | /* find a free user buffer and clone to it */ | ||
333 | if (!list_empty(&port->list_buf_free.list)) { | ||
334 | |||
335 | /* Pull the first buffer from the used list */ | ||
336 | ubuf = list_first_entry(&port->list_buf_free.list, | ||
337 | struct saa7164_user_buffer, list); | ||
338 | |||
339 | if (buf->actual_size <= ubuf->actual_size) { | ||
340 | |||
341 | memcpy_fromio(ubuf->data, buf->cpu, | ||
342 | ubuf->actual_size); | ||
343 | |||
344 | if (crc_checking) { | ||
345 | /* Throw a new checksum on the read buffer */ | ||
346 | ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size); | ||
347 | } | ||
348 | |||
349 | /* Requeue the buffer on the free list */ | ||
350 | ubuf->pos = 0; | ||
351 | |||
352 | list_move_tail(&ubuf->list, | ||
353 | &port->list_buf_used.list); | ||
354 | |||
355 | /* Flag any userland waiters */ | ||
356 | wake_up_interruptible(&port->wait_read); | ||
357 | |||
358 | } else { | ||
359 | printk(KERN_ERR "buf %p bufsize fails match\n", buf); | ||
360 | } | ||
361 | |||
362 | } else | ||
363 | printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n"); | ||
364 | |||
365 | /* Ensure offset into buffer remains 0, fill buffer | ||
366 | * with known bad data. We check for this data at a later point | ||
367 | * in time. */ | ||
368 | saa7164_buffer_zero_offsets(port, bufnr); | ||
369 | memset_io(buf->cpu, 0xff, buf->pci_size); | ||
370 | if (crc_checking) { | ||
371 | /* Throw yet aanother new checksum on the dma buffer */ | ||
372 | buf->crc = crc32(0, buf->cpu, buf->actual_size); | ||
373 | } | ||
374 | |||
375 | break; | ||
376 | } | ||
377 | } | ||
378 | mutex_unlock(&port->dmaqueue_lock); | ||
379 | } | ||
380 | |||
381 | static void saa7164_work_enchandler(struct work_struct *w) | ||
382 | { | ||
383 | struct saa7164_port *port = | ||
384 | container_of(w, struct saa7164_port, workenc); | ||
385 | struct saa7164_dev *dev = port->dev; | ||
386 | |||
387 | u32 wp, mcb, rp, cnt = 0; | ||
388 | |||
389 | port->last_svc_msecs_diff = port->last_svc_msecs; | ||
390 | port->last_svc_msecs = jiffies_to_msecs(jiffies); | ||
391 | |||
392 | port->last_svc_msecs_diff = port->last_svc_msecs - | ||
393 | port->last_svc_msecs_diff; | ||
394 | |||
395 | saa7164_histogram_update(&port->svc_interval, | ||
396 | port->last_svc_msecs_diff); | ||
397 | |||
398 | port->last_irq_svc_msecs_diff = port->last_svc_msecs - | ||
399 | port->last_irq_msecs; | ||
400 | |||
401 | saa7164_histogram_update(&port->irq_svc_interval, | ||
402 | port->last_irq_svc_msecs_diff); | ||
403 | |||
404 | dprintk(DBGLVL_IRQ, | ||
405 | "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n", | ||
406 | __func__, | ||
407 | port->last_svc_msecs_diff, | ||
408 | port->last_irq_svc_msecs_diff, | ||
409 | port->last_svc_wp, | ||
410 | port->last_svc_rp | ||
411 | ); | ||
412 | |||
413 | /* Current write position */ | ||
414 | wp = saa7164_readl(port->bufcounter); | ||
415 | if (wp > (port->hwcfg.buffercount - 1)) { | ||
416 | printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp); | ||
417 | return; | ||
418 | } | ||
419 | |||
420 | /* Most current complete buffer */ | ||
421 | if (wp == 0) | ||
422 | mcb = (port->hwcfg.buffercount - 1); | ||
423 | else | ||
424 | mcb = wp - 1; | ||
425 | |||
426 | while (1) { | ||
427 | if (port->done_first_interrupt == 0) { | ||
428 | port->done_first_interrupt++; | ||
429 | rp = mcb; | ||
430 | } else | ||
431 | rp = (port->last_svc_rp + 1) % 8; | ||
432 | |||
433 | if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) { | ||
434 | printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); | ||
435 | break; | ||
436 | } | ||
437 | |||
438 | saa7164_work_enchandler_helper(port, rp); | ||
439 | port->last_svc_rp = rp; | ||
440 | cnt++; | ||
441 | |||
442 | if (rp == mcb) | ||
443 | break; | ||
444 | } | ||
445 | |||
446 | /* TODO: Convert this into a /proc/saa7164 style readable file */ | ||
447 | if (print_histogram == port->nr) { | ||
448 | saa7164_histogram_print(port, &port->irq_interval); | ||
449 | saa7164_histogram_print(port, &port->svc_interval); | ||
450 | saa7164_histogram_print(port, &port->irq_svc_interval); | ||
451 | saa7164_histogram_print(port, &port->read_interval); | ||
452 | saa7164_histogram_print(port, &port->poll_interval); | ||
453 | /* TODO: fix this to preserve any previous state */ | ||
454 | print_histogram = 64 + port->nr; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | static void saa7164_work_vbihandler(struct work_struct *w) | ||
459 | { | ||
460 | struct saa7164_port *port = | ||
461 | container_of(w, struct saa7164_port, workenc); | ||
462 | struct saa7164_dev *dev = port->dev; | ||
463 | |||
464 | u32 wp, mcb, rp, cnt = 0; | ||
465 | |||
466 | port->last_svc_msecs_diff = port->last_svc_msecs; | ||
467 | port->last_svc_msecs = jiffies_to_msecs(jiffies); | ||
468 | port->last_svc_msecs_diff = port->last_svc_msecs - | ||
469 | port->last_svc_msecs_diff; | ||
470 | |||
471 | saa7164_histogram_update(&port->svc_interval, | ||
472 | port->last_svc_msecs_diff); | ||
473 | |||
474 | port->last_irq_svc_msecs_diff = port->last_svc_msecs - | ||
475 | port->last_irq_msecs; | ||
476 | |||
477 | saa7164_histogram_update(&port->irq_svc_interval, | ||
478 | port->last_irq_svc_msecs_diff); | ||
479 | |||
480 | dprintk(DBGLVL_IRQ, | ||
481 | "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n", | ||
482 | __func__, | ||
483 | port->last_svc_msecs_diff, | ||
484 | port->last_irq_svc_msecs_diff, | ||
485 | port->last_svc_wp, | ||
486 | port->last_svc_rp | ||
487 | ); | ||
488 | |||
489 | /* Current write position */ | ||
490 | wp = saa7164_readl(port->bufcounter); | ||
491 | if (wp > (port->hwcfg.buffercount - 1)) { | ||
492 | printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp); | ||
493 | return; | ||
494 | } | ||
495 | |||
496 | /* Most current complete buffer */ | ||
497 | if (wp == 0) | ||
498 | mcb = (port->hwcfg.buffercount - 1); | ||
499 | else | ||
500 | mcb = wp - 1; | ||
501 | |||
502 | while (1) { | ||
503 | if (port->done_first_interrupt == 0) { | ||
504 | port->done_first_interrupt++; | ||
505 | rp = mcb; | ||
506 | } else | ||
507 | rp = (port->last_svc_rp + 1) % 8; | ||
508 | |||
509 | if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) { | ||
510 | printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); | ||
511 | break; | ||
512 | } | ||
513 | |||
514 | saa7164_work_enchandler_helper(port, rp); | ||
515 | port->last_svc_rp = rp; | ||
516 | cnt++; | ||
517 | |||
518 | if (rp == mcb) | ||
519 | break; | ||
520 | } | ||
521 | |||
522 | /* TODO: Convert this into a /proc/saa7164 style readable file */ | ||
523 | if (print_histogram == port->nr) { | ||
524 | saa7164_histogram_print(port, &port->irq_interval); | ||
525 | saa7164_histogram_print(port, &port->svc_interval); | ||
526 | saa7164_histogram_print(port, &port->irq_svc_interval); | ||
527 | saa7164_histogram_print(port, &port->read_interval); | ||
528 | saa7164_histogram_print(port, &port->poll_interval); | ||
529 | /* TODO: fix this to preserve any previous state */ | ||
530 | print_histogram = 64 + port->nr; | ||
531 | } | ||
532 | } | ||
533 | |||
534 | static void saa7164_work_cmdhandler(struct work_struct *w) | ||
535 | { | ||
536 | struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd); | ||
537 | |||
538 | /* Wake up any complete commands */ | ||
539 | saa7164_irq_dequeue(dev); | ||
540 | } | ||
541 | |||
542 | static void saa7164_buffer_deliver(struct saa7164_buffer *buf) | ||
543 | { | ||
544 | struct saa7164_port *port = buf->port; | ||
545 | |||
546 | /* Feed the transport payload into the kernel demux */ | ||
547 | dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu, | ||
548 | SAA7164_TS_NUMBER_OF_LINES); | ||
549 | |||
550 | } | ||
551 | |||
552 | static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port) | ||
553 | { | ||
554 | struct saa7164_dev *dev = port->dev; | ||
555 | |||
556 | /* Store old time */ | ||
557 | port->last_irq_msecs_diff = port->last_irq_msecs; | ||
558 | |||
559 | /* Collect new stats */ | ||
560 | port->last_irq_msecs = jiffies_to_msecs(jiffies); | ||
561 | |||
562 | /* Calculate stats */ | ||
563 | port->last_irq_msecs_diff = port->last_irq_msecs - | ||
564 | port->last_irq_msecs_diff; | ||
565 | |||
566 | saa7164_histogram_update(&port->irq_interval, | ||
567 | port->last_irq_msecs_diff); | ||
568 | |||
569 | dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__, | ||
570 | port->last_irq_msecs_diff); | ||
571 | |||
572 | /* Tis calls the vbi irq handler */ | ||
573 | schedule_work(&port->workenc); | ||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port) | ||
578 | { | ||
579 | struct saa7164_dev *dev = port->dev; | ||
580 | |||
581 | /* Store old time */ | ||
582 | port->last_irq_msecs_diff = port->last_irq_msecs; | ||
583 | |||
584 | /* Collect new stats */ | ||
585 | port->last_irq_msecs = jiffies_to_msecs(jiffies); | ||
586 | |||
587 | /* Calculate stats */ | ||
588 | port->last_irq_msecs_diff = port->last_irq_msecs - | ||
589 | port->last_irq_msecs_diff; | ||
590 | |||
591 | saa7164_histogram_update(&port->irq_interval, | ||
592 | port->last_irq_msecs_diff); | ||
593 | |||
594 | dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__, | ||
595 | port->last_irq_msecs_diff); | ||
596 | |||
597 | schedule_work(&port->workenc); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) | ||
602 | { | ||
603 | struct saa7164_dev *dev = port->dev; | ||
604 | struct saa7164_buffer *buf; | ||
605 | struct list_head *c, *n; | ||
606 | int wp, i = 0, rp; | ||
607 | |||
608 | /* Find the current write point from the hardware */ | ||
609 | wp = saa7164_readl(port->bufcounter); | ||
610 | if (wp > (port->hwcfg.buffercount - 1)) | ||
611 | BUG(); | ||
612 | |||
613 | /* Find the previous buffer to the current write point */ | ||
614 | if (wp == 0) | ||
615 | rp = (port->hwcfg.buffercount - 1); | ||
616 | else | ||
617 | rp = wp - 1; | ||
618 | |||
619 | /* Lookup the WP in the buffer list */ | ||
620 | /* TODO: turn this into a worker thread */ | ||
621 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
622 | buf = list_entry(c, struct saa7164_buffer, list); | ||
623 | if (i++ > port->hwcfg.buffercount) | ||
624 | BUG(); | ||
625 | |||
626 | if (buf->idx == rp) { | ||
627 | /* Found the buffer, deal with it */ | ||
628 | dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n", | ||
629 | __func__, wp, rp); | ||
630 | saa7164_buffer_deliver(buf); | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | } | ||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | /* Primary IRQ handler and dispatch mechanism */ | ||
639 | static irqreturn_t saa7164_irq(int irq, void *dev_id) | ||
640 | { | ||
641 | struct saa7164_dev *dev = dev_id; | ||
642 | struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1]; | ||
643 | struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2]; | ||
644 | struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1]; | ||
645 | struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2]; | ||
646 | struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1]; | ||
647 | struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2]; | ||
648 | |||
649 | u32 intid, intstat[INT_SIZE/4]; | ||
650 | int i, handled = 0, bit; | ||
651 | |||
652 | if (dev == NULL) { | ||
653 | printk(KERN_ERR "%s() No device specified\n", __func__); | ||
654 | handled = 0; | ||
655 | goto out; | ||
656 | } | ||
657 | |||
658 | /* Check that the hardware is accessible. If the status bytes are | ||
659 | * 0xFF then the device is not accessible, the the IRQ belongs | ||
660 | * to another driver. | ||
661 | * 4 x u32 interrupt registers. | ||
662 | */ | ||
663 | for (i = 0; i < INT_SIZE/4; i++) { | ||
664 | |||
665 | /* TODO: Convert into saa7164_readl() */ | ||
666 | /* Read the 4 hardware interrupt registers */ | ||
667 | intstat[i] = saa7164_readl(dev->int_status + (i * 4)); | ||
668 | |||
669 | if (intstat[i]) | ||
670 | handled = 1; | ||
671 | } | ||
672 | if (handled == 0) | ||
673 | goto out; | ||
674 | |||
675 | /* For each of the HW interrupt registers */ | ||
676 | for (i = 0; i < INT_SIZE/4; i++) { | ||
677 | |||
678 | if (intstat[i]) { | ||
679 | /* Each function of the board has it's own interruptid. | ||
680 | * Find the function that triggered then call | ||
681 | * it's handler. | ||
682 | */ | ||
683 | for (bit = 0; bit < 32; bit++) { | ||
684 | |||
685 | if (((intstat[i] >> bit) & 0x00000001) == 0) | ||
686 | continue; | ||
687 | |||
688 | /* Calculate the interrupt id (0x00 to 0x7f) */ | ||
689 | |||
690 | intid = (i * 32) + bit; | ||
691 | if (intid == dev->intfdesc.bInterruptId) { | ||
692 | /* A response to an cmd/api call */ | ||
693 | schedule_work(&dev->workcmd); | ||
694 | } else if (intid == porta->hwcfg.interruptid) { | ||
695 | |||
696 | /* Transport path 1 */ | ||
697 | saa7164_irq_ts(porta); | ||
698 | |||
699 | } else if (intid == portb->hwcfg.interruptid) { | ||
700 | |||
701 | /* Transport path 2 */ | ||
702 | saa7164_irq_ts(portb); | ||
703 | |||
704 | } else if (intid == portc->hwcfg.interruptid) { | ||
705 | |||
706 | /* Encoder path 1 */ | ||
707 | saa7164_irq_encoder(portc); | ||
708 | |||
709 | } else if (intid == portd->hwcfg.interruptid) { | ||
710 | |||
711 | /* Encoder path 2 */ | ||
712 | saa7164_irq_encoder(portd); | ||
713 | |||
714 | } else if (intid == porte->hwcfg.interruptid) { | ||
715 | |||
716 | /* VBI path 1 */ | ||
717 | saa7164_irq_vbi(porte); | ||
718 | |||
719 | } else if (intid == portf->hwcfg.interruptid) { | ||
720 | |||
721 | /* VBI path 2 */ | ||
722 | saa7164_irq_vbi(portf); | ||
723 | |||
724 | } else { | ||
725 | /* Find the function */ | ||
726 | dprintk(DBGLVL_IRQ, | ||
727 | "%s() unhandled interrupt " | ||
728 | "reg 0x%x bit 0x%x " | ||
729 | "intid = 0x%x\n", | ||
730 | __func__, i, bit, intid); | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /* Ack it */ | ||
735 | saa7164_writel(dev->int_ack + (i * 4), intstat[i]); | ||
736 | |||
737 | } | ||
738 | } | ||
739 | out: | ||
740 | return IRQ_RETVAL(handled); | ||
741 | } | ||
742 | |||
743 | void saa7164_getfirmwarestatus(struct saa7164_dev *dev) | ||
744 | { | ||
745 | struct saa7164_fw_status *s = &dev->fw_status; | ||
746 | |||
747 | dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS); | ||
748 | dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE); | ||
749 | dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC); | ||
750 | dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST); | ||
751 | dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD); | ||
752 | dev->fw_status.remainheap = | ||
753 | saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP); | ||
754 | |||
755 | dprintk(1, "Firmware status:\n"); | ||
756 | dprintk(1, " .status = 0x%08x\n", s->status); | ||
757 | dprintk(1, " .mode = 0x%08x\n", s->mode); | ||
758 | dprintk(1, " .spec = 0x%08x\n", s->spec); | ||
759 | dprintk(1, " .inst = 0x%08x\n", s->inst); | ||
760 | dprintk(1, " .cpuload = 0x%08x\n", s->cpuload); | ||
761 | dprintk(1, " .remainheap = 0x%08x\n", s->remainheap); | ||
762 | } | ||
763 | |||
764 | u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev) | ||
765 | { | ||
766 | u32 reg; | ||
767 | |||
768 | reg = saa7164_readl(SAA_DEVICE_VERSION); | ||
769 | dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n", | ||
770 | (reg & 0x0000fc00) >> 10, | ||
771 | (reg & 0x000003e0) >> 5, | ||
772 | (reg & 0x0000001f), | ||
773 | (reg & 0xffff0000) >> 16, | ||
774 | reg); | ||
775 | |||
776 | return reg; | ||
777 | } | ||
778 | |||
779 | /* TODO: Debugging func, remove */ | ||
780 | void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len) | ||
781 | { | ||
782 | int i; | ||
783 | |||
784 | printk(KERN_INFO "--------------------> " | ||
785 | "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
786 | |||
787 | for (i = 0; i < len; i += 16) | ||
788 | printk(KERN_INFO " [0x%08x] " | ||
789 | "%02x %02x %02x %02x %02x %02x %02x %02x " | ||
790 | "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, | ||
791 | *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3), | ||
792 | *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7), | ||
793 | *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11), | ||
794 | *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15)); | ||
795 | } | ||
796 | |||
797 | /* TODO: Debugging func, remove */ | ||
798 | void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr) | ||
799 | { | ||
800 | int i; | ||
801 | |||
802 | dprintk(1, "--------------------> " | ||
803 | "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
804 | |||
805 | for (i = 0; i < 0x100; i += 16) | ||
806 | dprintk(1, "region0[0x%08x] = " | ||
807 | "%02x %02x %02x %02x %02x %02x %02x %02x" | ||
808 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", i, | ||
809 | (u8)saa7164_readb(addr + i + 0), | ||
810 | (u8)saa7164_readb(addr + i + 1), | ||
811 | (u8)saa7164_readb(addr + i + 2), | ||
812 | (u8)saa7164_readb(addr + i + 3), | ||
813 | (u8)saa7164_readb(addr + i + 4), | ||
814 | (u8)saa7164_readb(addr + i + 5), | ||
815 | (u8)saa7164_readb(addr + i + 6), | ||
816 | (u8)saa7164_readb(addr + i + 7), | ||
817 | (u8)saa7164_readb(addr + i + 8), | ||
818 | (u8)saa7164_readb(addr + i + 9), | ||
819 | (u8)saa7164_readb(addr + i + 10), | ||
820 | (u8)saa7164_readb(addr + i + 11), | ||
821 | (u8)saa7164_readb(addr + i + 12), | ||
822 | (u8)saa7164_readb(addr + i + 13), | ||
823 | (u8)saa7164_readb(addr + i + 14), | ||
824 | (u8)saa7164_readb(addr + i + 15) | ||
825 | ); | ||
826 | } | ||
827 | |||
828 | static void saa7164_dump_hwdesc(struct saa7164_dev *dev) | ||
829 | { | ||
830 | dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n", | ||
831 | &dev->hwdesc, (u32)sizeof(struct tmComResHWDescr)); | ||
832 | |||
833 | dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength); | ||
834 | dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType); | ||
835 | dprintk(1, " .bDescriptorSubtype = 0x%x\n", | ||
836 | dev->hwdesc.bDescriptorSubtype); | ||
837 | |||
838 | dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion); | ||
839 | dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency); | ||
840 | dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes); | ||
841 | dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities); | ||
842 | dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n", | ||
843 | dev->hwdesc.dwDeviceRegistersLocation); | ||
844 | |||
845 | dprintk(1, " .dwHostMemoryRegion = 0x%x\n", | ||
846 | dev->hwdesc.dwHostMemoryRegion); | ||
847 | |||
848 | dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n", | ||
849 | dev->hwdesc.dwHostMemoryRegionSize); | ||
850 | |||
851 | dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n", | ||
852 | dev->hwdesc.dwHostHibernatMemRegion); | ||
853 | |||
854 | dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n", | ||
855 | dev->hwdesc.dwHostHibernatMemRegionSize); | ||
856 | } | ||
857 | |||
858 | static void saa7164_dump_intfdesc(struct saa7164_dev *dev) | ||
859 | { | ||
860 | dprintk(1, "@0x%p intfdesc " | ||
861 | "sizeof(struct tmComResInterfaceDescr) = %d bytes\n", | ||
862 | &dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr)); | ||
863 | |||
864 | dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength); | ||
865 | dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType); | ||
866 | dprintk(1, " .bDescriptorSubtype = 0x%x\n", | ||
867 | dev->intfdesc.bDescriptorSubtype); | ||
868 | |||
869 | dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags); | ||
870 | dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType); | ||
871 | dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId); | ||
872 | dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface); | ||
873 | dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId); | ||
874 | dprintk(1, " .bDebugInterruptId = 0x%x\n", | ||
875 | dev->intfdesc.bDebugInterruptId); | ||
876 | |||
877 | dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation); | ||
878 | } | ||
879 | |||
880 | static void saa7164_dump_busdesc(struct saa7164_dev *dev) | ||
881 | { | ||
882 | dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n", | ||
883 | &dev->busdesc, (u32)sizeof(struct tmComResBusDescr)); | ||
884 | |||
885 | dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing); | ||
886 | dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing); | ||
887 | dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite); | ||
888 | dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead); | ||
889 | dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite); | ||
890 | dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead); | ||
891 | } | ||
892 | |||
893 | /* Much of the hardware configuration and PCI registers are configured | ||
894 | * dynamically depending on firmware. We have to cache some initial | ||
895 | * structures then use these to locate other important structures | ||
896 | * from PCI space. | ||
897 | */ | ||
898 | static void saa7164_get_descriptors(struct saa7164_dev *dev) | ||
899 | { | ||
900 | memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr)); | ||
901 | memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr), | ||
902 | sizeof(struct tmComResInterfaceDescr)); | ||
903 | memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation, | ||
904 | sizeof(struct tmComResBusDescr)); | ||
905 | |||
906 | if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) { | ||
907 | printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n"); | ||
908 | printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength, | ||
909 | (u32)sizeof(struct tmComResHWDescr)); | ||
910 | } else | ||
911 | saa7164_dump_hwdesc(dev); | ||
912 | |||
913 | if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) { | ||
914 | printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n"); | ||
915 | printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength, | ||
916 | (u32)sizeof(struct tmComResInterfaceDescr)); | ||
917 | } else | ||
918 | saa7164_dump_intfdesc(dev); | ||
919 | |||
920 | saa7164_dump_busdesc(dev); | ||
921 | } | ||
922 | |||
923 | static int saa7164_pci_quirks(struct saa7164_dev *dev) | ||
924 | { | ||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | static int get_resources(struct saa7164_dev *dev) | ||
929 | { | ||
930 | if (request_mem_region(pci_resource_start(dev->pci, 0), | ||
931 | pci_resource_len(dev->pci, 0), dev->name)) { | ||
932 | |||
933 | if (request_mem_region(pci_resource_start(dev->pci, 2), | ||
934 | pci_resource_len(dev->pci, 2), dev->name)) | ||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", | ||
939 | dev->name, | ||
940 | (u64)pci_resource_start(dev->pci, 0), | ||
941 | (u64)pci_resource_start(dev->pci, 2)); | ||
942 | |||
943 | return -EBUSY; | ||
944 | } | ||
945 | |||
946 | static int saa7164_port_init(struct saa7164_dev *dev, int portnr) | ||
947 | { | ||
948 | struct saa7164_port *port = NULL; | ||
949 | |||
950 | if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS)) | ||
951 | BUG(); | ||
952 | |||
953 | port = &dev->ports[portnr]; | ||
954 | |||
955 | port->dev = dev; | ||
956 | port->nr = portnr; | ||
957 | |||
958 | if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2)) | ||
959 | port->type = SAA7164_MPEG_DVB; | ||
960 | else | ||
961 | if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) { | ||
962 | port->type = SAA7164_MPEG_ENCODER; | ||
963 | |||
964 | /* We need a deferred interrupt handler for cmd handling */ | ||
965 | INIT_WORK(&port->workenc, saa7164_work_enchandler); | ||
966 | } else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) { | ||
967 | port->type = SAA7164_MPEG_VBI; | ||
968 | |||
969 | /* We need a deferred interrupt handler for cmd handling */ | ||
970 | INIT_WORK(&port->workenc, saa7164_work_vbihandler); | ||
971 | } else | ||
972 | BUG(); | ||
973 | |||
974 | /* Init all the critical resources */ | ||
975 | mutex_init(&port->dvb.lock); | ||
976 | INIT_LIST_HEAD(&port->dmaqueue.list); | ||
977 | mutex_init(&port->dmaqueue_lock); | ||
978 | |||
979 | INIT_LIST_HEAD(&port->list_buf_used.list); | ||
980 | INIT_LIST_HEAD(&port->list_buf_free.list); | ||
981 | init_waitqueue_head(&port->wait_read); | ||
982 | |||
983 | |||
984 | saa7164_histogram_reset(&port->irq_interval, "irq intervals"); | ||
985 | saa7164_histogram_reset(&port->svc_interval, "deferred intervals"); | ||
986 | saa7164_histogram_reset(&port->irq_svc_interval, | ||
987 | "irq to deferred intervals"); | ||
988 | saa7164_histogram_reset(&port->read_interval, | ||
989 | "encoder/vbi read() intervals"); | ||
990 | saa7164_histogram_reset(&port->poll_interval, | ||
991 | "encoder/vbi poll() intervals"); | ||
992 | |||
993 | return 0; | ||
994 | } | ||
995 | |||
996 | static int saa7164_dev_setup(struct saa7164_dev *dev) | ||
997 | { | ||
998 | int i; | ||
999 | |||
1000 | mutex_init(&dev->lock); | ||
1001 | atomic_inc(&dev->refcount); | ||
1002 | dev->nr = saa7164_devcount++; | ||
1003 | |||
1004 | snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr); | ||
1005 | |||
1006 | mutex_lock(&devlist); | ||
1007 | list_add_tail(&dev->devlist, &saa7164_devlist); | ||
1008 | mutex_unlock(&devlist); | ||
1009 | |||
1010 | /* board config */ | ||
1011 | dev->board = UNSET; | ||
1012 | if (card[dev->nr] < saa7164_bcount) | ||
1013 | dev->board = card[dev->nr]; | ||
1014 | |||
1015 | for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++) | ||
1016 | if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor && | ||
1017 | dev->pci->subsystem_device == | ||
1018 | saa7164_subids[i].subdevice) | ||
1019 | dev->board = saa7164_subids[i].card; | ||
1020 | |||
1021 | if (UNSET == dev->board) { | ||
1022 | dev->board = SAA7164_BOARD_UNKNOWN; | ||
1023 | saa7164_card_list(dev); | ||
1024 | } | ||
1025 | |||
1026 | dev->pci_bus = dev->pci->bus->number; | ||
1027 | dev->pci_slot = PCI_SLOT(dev->pci->devfn); | ||
1028 | |||
1029 | /* I2C Defaults / setup */ | ||
1030 | dev->i2c_bus[0].dev = dev; | ||
1031 | dev->i2c_bus[0].nr = 0; | ||
1032 | dev->i2c_bus[1].dev = dev; | ||
1033 | dev->i2c_bus[1].nr = 1; | ||
1034 | dev->i2c_bus[2].dev = dev; | ||
1035 | dev->i2c_bus[2].nr = 2; | ||
1036 | |||
1037 | /* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */ | ||
1038 | saa7164_port_init(dev, SAA7164_PORT_TS1); | ||
1039 | saa7164_port_init(dev, SAA7164_PORT_TS2); | ||
1040 | saa7164_port_init(dev, SAA7164_PORT_ENC1); | ||
1041 | saa7164_port_init(dev, SAA7164_PORT_ENC2); | ||
1042 | saa7164_port_init(dev, SAA7164_PORT_VBI1); | ||
1043 | saa7164_port_init(dev, SAA7164_PORT_VBI2); | ||
1044 | |||
1045 | if (get_resources(dev) < 0) { | ||
1046 | printk(KERN_ERR "CORE %s No more PCIe resources for " | ||
1047 | "subsystem: %04x:%04x\n", | ||
1048 | dev->name, dev->pci->subsystem_vendor, | ||
1049 | dev->pci->subsystem_device); | ||
1050 | |||
1051 | saa7164_devcount--; | ||
1052 | return -ENODEV; | ||
1053 | } | ||
1054 | |||
1055 | /* PCI/e allocations */ | ||
1056 | dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), | ||
1057 | pci_resource_len(dev->pci, 0)); | ||
1058 | |||
1059 | dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2), | ||
1060 | pci_resource_len(dev->pci, 2)); | ||
1061 | |||
1062 | dev->bmmio = (u8 __iomem *)dev->lmmio; | ||
1063 | dev->bmmio2 = (u8 __iomem *)dev->lmmio2; | ||
1064 | |||
1065 | /* Inerrupt and ack register locations offset of bmmio */ | ||
1066 | dev->int_status = 0x183000 + 0xf80; | ||
1067 | dev->int_ack = 0x183000 + 0xf90; | ||
1068 | |||
1069 | printk(KERN_INFO | ||
1070 | "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", | ||
1071 | dev->name, dev->pci->subsystem_vendor, | ||
1072 | dev->pci->subsystem_device, saa7164_boards[dev->board].name, | ||
1073 | dev->board, card[dev->nr] == dev->board ? | ||
1074 | "insmod option" : "autodetected"); | ||
1075 | |||
1076 | saa7164_pci_quirks(dev); | ||
1077 | |||
1078 | return 0; | ||
1079 | } | ||
1080 | |||
1081 | static void saa7164_dev_unregister(struct saa7164_dev *dev) | ||
1082 | { | ||
1083 | dprintk(1, "%s()\n", __func__); | ||
1084 | |||
1085 | release_mem_region(pci_resource_start(dev->pci, 0), | ||
1086 | pci_resource_len(dev->pci, 0)); | ||
1087 | |||
1088 | release_mem_region(pci_resource_start(dev->pci, 2), | ||
1089 | pci_resource_len(dev->pci, 2)); | ||
1090 | |||
1091 | if (!atomic_dec_and_test(&dev->refcount)) | ||
1092 | return; | ||
1093 | |||
1094 | iounmap(dev->lmmio); | ||
1095 | iounmap(dev->lmmio2); | ||
1096 | |||
1097 | return; | ||
1098 | } | ||
1099 | |||
1100 | #ifdef CONFIG_PROC_FS | ||
1101 | static int saa7164_proc_show(struct seq_file *m, void *v) | ||
1102 | { | ||
1103 | struct saa7164_dev *dev; | ||
1104 | struct tmComResBusInfo *b; | ||
1105 | struct list_head *list; | ||
1106 | int i, c; | ||
1107 | |||
1108 | if (saa7164_devcount == 0) | ||
1109 | return 0; | ||
1110 | |||
1111 | list_for_each(list, &saa7164_devlist) { | ||
1112 | dev = list_entry(list, struct saa7164_dev, devlist); | ||
1113 | seq_printf(m, "%s = %p\n", dev->name, dev); | ||
1114 | |||
1115 | /* Lock the bus from any other access */ | ||
1116 | b = &dev->bus; | ||
1117 | mutex_lock(&b->lock); | ||
1118 | |||
1119 | seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n", | ||
1120 | b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); | ||
1121 | |||
1122 | seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n", | ||
1123 | b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); | ||
1124 | |||
1125 | seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n", | ||
1126 | b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); | ||
1127 | |||
1128 | seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n", | ||
1129 | b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); | ||
1130 | c = 0; | ||
1131 | seq_printf(m, "\n Set Ring:\n"); | ||
1132 | seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
1133 | for (i = 0; i < b->m_dwSizeSetRing; i++) { | ||
1134 | if (c == 0) | ||
1135 | seq_printf(m, " %04x:", i); | ||
1136 | |||
1137 | seq_printf(m, " %02x", *(b->m_pdwSetRing + i)); | ||
1138 | |||
1139 | if (++c == 16) { | ||
1140 | seq_printf(m, "\n"); | ||
1141 | c = 0; | ||
1142 | } | ||
1143 | } | ||
1144 | |||
1145 | c = 0; | ||
1146 | seq_printf(m, "\n Get Ring:\n"); | ||
1147 | seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | ||
1148 | for (i = 0; i < b->m_dwSizeGetRing; i++) { | ||
1149 | if (c == 0) | ||
1150 | seq_printf(m, " %04x:", i); | ||
1151 | |||
1152 | seq_printf(m, " %02x", *(b->m_pdwGetRing + i)); | ||
1153 | |||
1154 | if (++c == 16) { | ||
1155 | seq_printf(m, "\n"); | ||
1156 | c = 0; | ||
1157 | } | ||
1158 | } | ||
1159 | |||
1160 | mutex_unlock(&b->lock); | ||
1161 | |||
1162 | } | ||
1163 | |||
1164 | return 0; | ||
1165 | } | ||
1166 | |||
1167 | static int saa7164_proc_open(struct inode *inode, struct file *filp) | ||
1168 | { | ||
1169 | return single_open(filp, saa7164_proc_show, NULL); | ||
1170 | } | ||
1171 | |||
1172 | static const struct file_operations saa7164_proc_fops = { | ||
1173 | .open = saa7164_proc_open, | ||
1174 | .read = seq_read, | ||
1175 | .llseek = seq_lseek, | ||
1176 | .release = single_release, | ||
1177 | }; | ||
1178 | |||
1179 | static int saa7164_proc_create(void) | ||
1180 | { | ||
1181 | struct proc_dir_entry *pe; | ||
1182 | |||
1183 | pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops); | ||
1184 | if (!pe) | ||
1185 | return -ENOMEM; | ||
1186 | |||
1187 | return 0; | ||
1188 | } | ||
1189 | #endif | ||
1190 | |||
1191 | static int saa7164_thread_function(void *data) | ||
1192 | { | ||
1193 | struct saa7164_dev *dev = data; | ||
1194 | struct tmFwInfoStruct fwinfo; | ||
1195 | u64 last_poll_time = 0; | ||
1196 | |||
1197 | dprintk(DBGLVL_THR, "thread started\n"); | ||
1198 | |||
1199 | set_freezable(); | ||
1200 | |||
1201 | while (1) { | ||
1202 | msleep_interruptible(100); | ||
1203 | if (kthread_should_stop()) | ||
1204 | break; | ||
1205 | try_to_freeze(); | ||
1206 | |||
1207 | dprintk(DBGLVL_THR, "thread running\n"); | ||
1208 | |||
1209 | /* Dump the firmware debug message to console */ | ||
1210 | /* Polling this costs us 1-2% of the arm CPU */ | ||
1211 | /* convert this into a respnde to interrupt 0x7a */ | ||
1212 | saa7164_api_collect_debug(dev); | ||
1213 | |||
1214 | /* Monitor CPU load every 1 second */ | ||
1215 | if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) { | ||
1216 | saa7164_api_get_load_info(dev, &fwinfo); | ||
1217 | last_poll_time = jiffies_to_msecs(jiffies); | ||
1218 | } | ||
1219 | |||
1220 | } | ||
1221 | |||
1222 | dprintk(DBGLVL_THR, "thread exiting\n"); | ||
1223 | return 0; | ||
1224 | } | ||
1225 | |||
1226 | static int __devinit saa7164_initdev(struct pci_dev *pci_dev, | ||
1227 | const struct pci_device_id *pci_id) | ||
1228 | { | ||
1229 | struct saa7164_dev *dev; | ||
1230 | int err, i; | ||
1231 | u32 version; | ||
1232 | |||
1233 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
1234 | if (NULL == dev) | ||
1235 | return -ENOMEM; | ||
1236 | |||
1237 | /* pci init */ | ||
1238 | dev->pci = pci_dev; | ||
1239 | if (pci_enable_device(pci_dev)) { | ||
1240 | err = -EIO; | ||
1241 | goto fail_free; | ||
1242 | } | ||
1243 | |||
1244 | if (saa7164_dev_setup(dev) < 0) { | ||
1245 | err = -EINVAL; | ||
1246 | goto fail_free; | ||
1247 | } | ||
1248 | |||
1249 | /* print pci info */ | ||
1250 | dev->pci_rev = pci_dev->revision; | ||
1251 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
1252 | printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " | ||
1253 | "latency: %d, mmio: 0x%llx\n", dev->name, | ||
1254 | pci_name(pci_dev), dev->pci_rev, pci_dev->irq, | ||
1255 | dev->pci_lat, | ||
1256 | (unsigned long long)pci_resource_start(pci_dev, 0)); | ||
1257 | |||
1258 | pci_set_master(pci_dev); | ||
1259 | /* TODO */ | ||
1260 | if (!pci_dma_supported(pci_dev, 0xffffffff)) { | ||
1261 | printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); | ||
1262 | err = -EIO; | ||
1263 | goto fail_irq; | ||
1264 | } | ||
1265 | |||
1266 | err = request_irq(pci_dev->irq, saa7164_irq, | ||
1267 | IRQF_SHARED | IRQF_DISABLED, dev->name, dev); | ||
1268 | if (err < 0) { | ||
1269 | printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, | ||
1270 | pci_dev->irq); | ||
1271 | err = -EIO; | ||
1272 | goto fail_irq; | ||
1273 | } | ||
1274 | |||
1275 | pci_set_drvdata(pci_dev, dev); | ||
1276 | |||
1277 | /* Init the internal command list */ | ||
1278 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | ||
1279 | dev->cmds[i].seqno = i; | ||
1280 | dev->cmds[i].inuse = 0; | ||
1281 | mutex_init(&dev->cmds[i].lock); | ||
1282 | init_waitqueue_head(&dev->cmds[i].wait); | ||
1283 | } | ||
1284 | |||
1285 | /* We need a deferred interrupt handler for cmd handling */ | ||
1286 | INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler); | ||
1287 | |||
1288 | /* Only load the firmware if we know the board */ | ||
1289 | if (dev->board != SAA7164_BOARD_UNKNOWN) { | ||
1290 | |||
1291 | err = saa7164_downloadfirmware(dev); | ||
1292 | if (err < 0) { | ||
1293 | printk(KERN_ERR | ||
1294 | "Failed to boot firmware, no features " | ||
1295 | "registered\n"); | ||
1296 | goto fail_fw; | ||
1297 | } | ||
1298 | |||
1299 | saa7164_get_descriptors(dev); | ||
1300 | saa7164_dumpregs(dev, 0); | ||
1301 | saa7164_getcurrentfirmwareversion(dev); | ||
1302 | saa7164_getfirmwarestatus(dev); | ||
1303 | err = saa7164_bus_setup(dev); | ||
1304 | if (err < 0) | ||
1305 | printk(KERN_ERR | ||
1306 | "Failed to setup the bus, will continue\n"); | ||
1307 | saa7164_bus_dump(dev); | ||
1308 | |||
1309 | /* Ping the running firmware via the command bus and get the | ||
1310 | * firmware version, this checks the bus is running OK. | ||
1311 | */ | ||
1312 | version = 0; | ||
1313 | if (saa7164_api_get_fw_version(dev, &version) == SAA_OK) | ||
1314 | dprintk(1, "Bus is operating correctly using " | ||
1315 | "version %d.%d.%d.%d (0x%x)\n", | ||
1316 | (version & 0x0000fc00) >> 10, | ||
1317 | (version & 0x000003e0) >> 5, | ||
1318 | (version & 0x0000001f), | ||
1319 | (version & 0xffff0000) >> 16, | ||
1320 | version); | ||
1321 | else | ||
1322 | printk(KERN_ERR | ||
1323 | "Failed to communicate with the firmware\n"); | ||
1324 | |||
1325 | /* Bring up the I2C buses */ | ||
1326 | saa7164_i2c_register(&dev->i2c_bus[0]); | ||
1327 | saa7164_i2c_register(&dev->i2c_bus[1]); | ||
1328 | saa7164_i2c_register(&dev->i2c_bus[2]); | ||
1329 | saa7164_gpio_setup(dev); | ||
1330 | saa7164_card_setup(dev); | ||
1331 | |||
1332 | /* Parse the dynamic device configuration, find various | ||
1333 | * media endpoints (MPEG, WMV, PS, TS) and cache their | ||
1334 | * configuration details into the driver, so we can | ||
1335 | * reference them later during simething_register() func, | ||
1336 | * interrupt handlers, deferred work handlers etc. | ||
1337 | */ | ||
1338 | saa7164_api_enum_subdevs(dev); | ||
1339 | |||
1340 | /* Begin to create the video sub-systems and register funcs */ | ||
1341 | if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) { | ||
1342 | if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) { | ||
1343 | printk(KERN_ERR "%s() Failed to register " | ||
1344 | "dvb adapters on porta\n", | ||
1345 | __func__); | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) { | ||
1350 | if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) { | ||
1351 | printk(KERN_ERR"%s() Failed to register " | ||
1352 | "dvb adapters on portb\n", | ||
1353 | __func__); | ||
1354 | } | ||
1355 | } | ||
1356 | |||
1357 | if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) { | ||
1358 | if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) { | ||
1359 | printk(KERN_ERR"%s() Failed to register " | ||
1360 | "mpeg encoder\n", __func__); | ||
1361 | } | ||
1362 | } | ||
1363 | |||
1364 | if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) { | ||
1365 | if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) { | ||
1366 | printk(KERN_ERR"%s() Failed to register " | ||
1367 | "mpeg encoder\n", __func__); | ||
1368 | } | ||
1369 | } | ||
1370 | |||
1371 | if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) { | ||
1372 | if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) { | ||
1373 | printk(KERN_ERR"%s() Failed to register " | ||
1374 | "vbi device\n", __func__); | ||
1375 | } | ||
1376 | } | ||
1377 | |||
1378 | if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) { | ||
1379 | if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) { | ||
1380 | printk(KERN_ERR"%s() Failed to register " | ||
1381 | "vbi device\n", __func__); | ||
1382 | } | ||
1383 | } | ||
1384 | saa7164_api_set_debug(dev, fw_debug); | ||
1385 | |||
1386 | if (fw_debug) { | ||
1387 | dev->kthread = kthread_run(saa7164_thread_function, dev, | ||
1388 | "saa7164 debug"); | ||
1389 | if (!dev->kthread) | ||
1390 | printk(KERN_ERR "%s() Failed to create " | ||
1391 | "debug kernel thread\n", __func__); | ||
1392 | } | ||
1393 | |||
1394 | } /* != BOARD_UNKNOWN */ | ||
1395 | else | ||
1396 | printk(KERN_ERR "%s() Unsupported board detected, " | ||
1397 | "registering without firmware\n", __func__); | ||
1398 | |||
1399 | dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug); | ||
1400 | dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs); | ||
1401 | |||
1402 | fail_fw: | ||
1403 | return 0; | ||
1404 | |||
1405 | fail_irq: | ||
1406 | saa7164_dev_unregister(dev); | ||
1407 | fail_free: | ||
1408 | kfree(dev); | ||
1409 | return err; | ||
1410 | } | ||
1411 | |||
1412 | static void saa7164_shutdown(struct saa7164_dev *dev) | ||
1413 | { | ||
1414 | dprintk(1, "%s()\n", __func__); | ||
1415 | } | ||
1416 | |||
1417 | static void __devexit saa7164_finidev(struct pci_dev *pci_dev) | ||
1418 | { | ||
1419 | struct saa7164_dev *dev = pci_get_drvdata(pci_dev); | ||
1420 | |||
1421 | if (dev->board != SAA7164_BOARD_UNKNOWN) { | ||
1422 | if (fw_debug && dev->kthread) { | ||
1423 | kthread_stop(dev->kthread); | ||
1424 | dev->kthread = NULL; | ||
1425 | } | ||
1426 | if (dev->firmwareloaded) | ||
1427 | saa7164_api_set_debug(dev, 0x00); | ||
1428 | } | ||
1429 | |||
1430 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | ||
1431 | &dev->ports[SAA7164_PORT_ENC1].irq_interval); | ||
1432 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | ||
1433 | &dev->ports[SAA7164_PORT_ENC1].svc_interval); | ||
1434 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | ||
1435 | &dev->ports[SAA7164_PORT_ENC1].irq_svc_interval); | ||
1436 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | ||
1437 | &dev->ports[SAA7164_PORT_ENC1].read_interval); | ||
1438 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | ||
1439 | &dev->ports[SAA7164_PORT_ENC1].poll_interval); | ||
1440 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1], | ||
1441 | &dev->ports[SAA7164_PORT_VBI1].read_interval); | ||
1442 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2], | ||
1443 | &dev->ports[SAA7164_PORT_VBI2].poll_interval); | ||
1444 | |||
1445 | saa7164_shutdown(dev); | ||
1446 | |||
1447 | if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) | ||
1448 | saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]); | ||
1449 | |||
1450 | if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) | ||
1451 | saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]); | ||
1452 | |||
1453 | if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) | ||
1454 | saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]); | ||
1455 | |||
1456 | if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) | ||
1457 | saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]); | ||
1458 | |||
1459 | if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) | ||
1460 | saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]); | ||
1461 | |||
1462 | if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) | ||
1463 | saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]); | ||
1464 | |||
1465 | saa7164_i2c_unregister(&dev->i2c_bus[0]); | ||
1466 | saa7164_i2c_unregister(&dev->i2c_bus[1]); | ||
1467 | saa7164_i2c_unregister(&dev->i2c_bus[2]); | ||
1468 | |||
1469 | pci_disable_device(pci_dev); | ||
1470 | |||
1471 | /* unregister stuff */ | ||
1472 | free_irq(pci_dev->irq, dev); | ||
1473 | pci_set_drvdata(pci_dev, NULL); | ||
1474 | |||
1475 | mutex_lock(&devlist); | ||
1476 | list_del(&dev->devlist); | ||
1477 | mutex_unlock(&devlist); | ||
1478 | |||
1479 | saa7164_dev_unregister(dev); | ||
1480 | kfree(dev); | ||
1481 | } | ||
1482 | |||
1483 | static struct pci_device_id saa7164_pci_tbl[] = { | ||
1484 | { | ||
1485 | /* SAA7164 */ | ||
1486 | .vendor = 0x1131, | ||
1487 | .device = 0x7164, | ||
1488 | .subvendor = PCI_ANY_ID, | ||
1489 | .subdevice = PCI_ANY_ID, | ||
1490 | }, { | ||
1491 | /* --- end of list --- */ | ||
1492 | } | ||
1493 | }; | ||
1494 | MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl); | ||
1495 | |||
1496 | static struct pci_driver saa7164_pci_driver = { | ||
1497 | .name = "saa7164", | ||
1498 | .id_table = saa7164_pci_tbl, | ||
1499 | .probe = saa7164_initdev, | ||
1500 | .remove = __devexit_p(saa7164_finidev), | ||
1501 | /* TODO */ | ||
1502 | .suspend = NULL, | ||
1503 | .resume = NULL, | ||
1504 | }; | ||
1505 | |||
1506 | static int __init saa7164_init(void) | ||
1507 | { | ||
1508 | printk(KERN_INFO "saa7164 driver loaded\n"); | ||
1509 | |||
1510 | #ifdef CONFIG_PROC_FS | ||
1511 | saa7164_proc_create(); | ||
1512 | #endif | ||
1513 | return pci_register_driver(&saa7164_pci_driver); | ||
1514 | } | ||
1515 | |||
1516 | static void __exit saa7164_fini(void) | ||
1517 | { | ||
1518 | #ifdef CONFIG_PROC_FS | ||
1519 | remove_proc_entry("saa7164", NULL); | ||
1520 | #endif | ||
1521 | pci_unregister_driver(&saa7164_pci_driver); | ||
1522 | } | ||
1523 | |||
1524 | module_init(saa7164_init); | ||
1525 | module_exit(saa7164_fini); | ||
1526 | |||
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c new file mode 100644 index 00000000000..d3779379197 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-dvb.c | |||
@@ -0,0 +1,555 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | .dtv6_if_freq_khz = TDA10048_IF_3300, | ||
39 | .dtv7_if_freq_khz = TDA10048_IF_3500, | ||
40 | .dtv8_if_freq_khz = TDA10048_IF_4000, | ||
41 | .clk_freq_khz = TDA10048_CLK_16000, | ||
42 | }; | ||
43 | static struct tda10048_config hauppauge_hvr2200_2_config = { | ||
44 | .demod_address = 0x12 >> 1, | ||
45 | .output_mode = TDA10048_SERIAL_OUTPUT, | ||
46 | .fwbulkwritelen = TDA10048_BULKWRITE_200, | ||
47 | .inversion = TDA10048_INVERSION_ON, | ||
48 | .dtv6_if_freq_khz = TDA10048_IF_3300, | ||
49 | .dtv7_if_freq_khz = TDA10048_IF_3500, | ||
50 | .dtv8_if_freq_khz = TDA10048_IF_4000, | ||
51 | .clk_freq_khz = TDA10048_CLK_16000, | ||
52 | }; | ||
53 | |||
54 | static struct tda18271_std_map hauppauge_tda18271_std_map = { | ||
55 | .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, | ||
56 | .if_lvl = 6, .rfagc_top = 0x37 }, | ||
57 | .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, | ||
58 | .if_lvl = 6, .rfagc_top = 0x37 }, | ||
59 | }; | ||
60 | |||
61 | static struct tda18271_config hauppauge_hvr22x0_tuner_config = { | ||
62 | .std_map = &hauppauge_tda18271_std_map, | ||
63 | .gate = TDA18271_GATE_ANALOG, | ||
64 | .role = TDA18271_MASTER, | ||
65 | }; | ||
66 | |||
67 | static struct tda18271_config hauppauge_hvr22x0s_tuner_config = { | ||
68 | .std_map = &hauppauge_tda18271_std_map, | ||
69 | .gate = TDA18271_GATE_ANALOG, | ||
70 | .role = TDA18271_SLAVE, | ||
71 | .output_opt = TDA18271_OUTPUT_LT_OFF, | ||
72 | .rf_cal_on_startup = 1 | ||
73 | }; | ||
74 | |||
75 | static struct s5h1411_config hauppauge_s5h1411_config = { | ||
76 | .output_mode = S5H1411_SERIAL_OUTPUT, | ||
77 | .gpio = S5H1411_GPIO_ON, | ||
78 | .qam_if = S5H1411_IF_4000, | ||
79 | .vsb_if = S5H1411_IF_3250, | ||
80 | .inversion = S5H1411_INVERSION_ON, | ||
81 | .status_mode = S5H1411_DEMODLOCKING, | ||
82 | .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
83 | }; | ||
84 | |||
85 | static int saa7164_dvb_stop_port(struct saa7164_port *port) | ||
86 | { | ||
87 | struct saa7164_dev *dev = port->dev; | ||
88 | int ret; | ||
89 | |||
90 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
91 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
92 | printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", | ||
93 | __func__, ret); | ||
94 | ret = -EIO; | ||
95 | } else { | ||
96 | dprintk(DBGLVL_DVB, "%s() Stopped\n", __func__); | ||
97 | ret = 0; | ||
98 | } | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static int saa7164_dvb_acquire_port(struct saa7164_port *port) | ||
104 | { | ||
105 | struct saa7164_dev *dev = port->dev; | ||
106 | int ret; | ||
107 | |||
108 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
109 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
110 | printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", | ||
111 | __func__, ret); | ||
112 | ret = -EIO; | ||
113 | } else { | ||
114 | dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); | ||
115 | ret = 0; | ||
116 | } | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static int saa7164_dvb_pause_port(struct saa7164_port *port) | ||
122 | { | ||
123 | struct saa7164_dev *dev = port->dev; | ||
124 | int ret; | ||
125 | |||
126 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
127 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
128 | printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", | ||
129 | __func__, ret); | ||
130 | ret = -EIO; | ||
131 | } else { | ||
132 | dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); | ||
133 | ret = 0; | ||
134 | } | ||
135 | |||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | /* Firmware is very windows centric, meaning you have to transition | ||
140 | * the part through AVStream / KS Windows stages, forwards or backwards. | ||
141 | * States are: stopped, acquired (h/w), paused, started. | ||
142 | */ | ||
143 | static int saa7164_dvb_stop_streaming(struct saa7164_port *port) | ||
144 | { | ||
145 | struct saa7164_dev *dev = port->dev; | ||
146 | struct saa7164_buffer *buf; | ||
147 | struct list_head *p, *q; | ||
148 | int ret; | ||
149 | |||
150 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
151 | |||
152 | ret = saa7164_dvb_pause_port(port); | ||
153 | ret = saa7164_dvb_acquire_port(port); | ||
154 | ret = saa7164_dvb_stop_port(port); | ||
155 | |||
156 | /* Mark the hardware buffers as free */ | ||
157 | mutex_lock(&port->dmaqueue_lock); | ||
158 | list_for_each_safe(p, q, &port->dmaqueue.list) { | ||
159 | buf = list_entry(p, struct saa7164_buffer, list); | ||
160 | buf->flags = SAA7164_BUFFER_FREE; | ||
161 | } | ||
162 | mutex_unlock(&port->dmaqueue_lock); | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int saa7164_dvb_start_port(struct saa7164_port *port) | ||
168 | { | ||
169 | struct saa7164_dev *dev = port->dev; | ||
170 | int ret = 0, result; | ||
171 | |||
172 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
173 | |||
174 | saa7164_buffer_cfg_port(port); | ||
175 | |||
176 | /* Acquire the hardware */ | ||
177 | result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
178 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
179 | printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", | ||
180 | __func__, result); | ||
181 | |||
182 | /* Stop the hardware, regardless */ | ||
183 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
184 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
185 | printk(KERN_ERR "%s() acquire/forced stop transition " | ||
186 | "failed, res = 0x%x\n", __func__, result); | ||
187 | } | ||
188 | ret = -EIO; | ||
189 | goto out; | ||
190 | } else | ||
191 | dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); | ||
192 | |||
193 | /* Pause the hardware */ | ||
194 | result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
195 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
196 | printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", | ||
197 | __func__, result); | ||
198 | |||
199 | /* Stop the hardware, regardless */ | ||
200 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
201 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
202 | printk(KERN_ERR "%s() pause/forced stop transition " | ||
203 | "failed, res = 0x%x\n", __func__, result); | ||
204 | } | ||
205 | |||
206 | ret = -EIO; | ||
207 | goto out; | ||
208 | } else | ||
209 | dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); | ||
210 | |||
211 | /* Start the hardware */ | ||
212 | result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); | ||
213 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
214 | printk(KERN_ERR "%s() run transition failed, result = 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() run/forced stop transition " | ||
221 | "failed, res = 0x%x\n", __func__, result); | ||
222 | } | ||
223 | |||
224 | ret = -EIO; | ||
225 | } else | ||
226 | dprintk(DBGLVL_DVB, "%s() Running\n", __func__); | ||
227 | |||
228 | out: | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) | ||
233 | { | ||
234 | struct dvb_demux *demux = feed->demux; | ||
235 | struct saa7164_port *port = (struct saa7164_port *) demux->priv; | ||
236 | struct saa7164_dvb *dvb = &port->dvb; | ||
237 | struct saa7164_dev *dev = port->dev; | ||
238 | int ret = 0; | ||
239 | |||
240 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
241 | |||
242 | if (!demux->dmx.frontend) | ||
243 | return -EINVAL; | ||
244 | |||
245 | if (dvb) { | ||
246 | mutex_lock(&dvb->lock); | ||
247 | if (dvb->feeding++ == 0) { | ||
248 | /* Start transport */ | ||
249 | ret = saa7164_dvb_start_port(port); | ||
250 | } | ||
251 | mutex_unlock(&dvb->lock); | ||
252 | dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", | ||
253 | __func__, port->nr, dvb->feeding); | ||
254 | } | ||
255 | |||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) | ||
260 | { | ||
261 | struct dvb_demux *demux = feed->demux; | ||
262 | struct saa7164_port *port = (struct saa7164_port *) demux->priv; | ||
263 | struct saa7164_dvb *dvb = &port->dvb; | ||
264 | struct saa7164_dev *dev = port->dev; | ||
265 | int ret = 0; | ||
266 | |||
267 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
268 | |||
269 | if (dvb) { | ||
270 | mutex_lock(&dvb->lock); | ||
271 | if (--dvb->feeding == 0) { | ||
272 | /* Stop transport */ | ||
273 | ret = saa7164_dvb_stop_streaming(port); | ||
274 | } | ||
275 | mutex_unlock(&dvb->lock); | ||
276 | dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", | ||
277 | __func__, port->nr, dvb->feeding); | ||
278 | } | ||
279 | |||
280 | return ret; | ||
281 | } | ||
282 | |||
283 | static int dvb_register(struct saa7164_port *port) | ||
284 | { | ||
285 | struct saa7164_dvb *dvb = &port->dvb; | ||
286 | struct saa7164_dev *dev = port->dev; | ||
287 | struct saa7164_buffer *buf; | ||
288 | int result, i; | ||
289 | |||
290 | dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); | ||
291 | |||
292 | if (port->type != SAA7164_MPEG_DVB) | ||
293 | BUG(); | ||
294 | |||
295 | /* Sanity check that the PCI configuration space is active */ | ||
296 | if (port->hwcfg.BARLocation == 0) { | ||
297 | result = -ENOMEM; | ||
298 | printk(KERN_ERR "%s: dvb_register_adapter failed " | ||
299 | "(errno = %d), NO PCI configuration\n", | ||
300 | DRIVER_NAME, result); | ||
301 | goto fail_adapter; | ||
302 | } | ||
303 | |||
304 | /* Init and establish defaults */ | ||
305 | port->hw_streamingparams.bitspersample = 8; | ||
306 | port->hw_streamingparams.samplesperline = 188; | ||
307 | port->hw_streamingparams.numberoflines = | ||
308 | (SAA7164_TS_NUMBER_OF_LINES * 188) / 188; | ||
309 | |||
310 | port->hw_streamingparams.pitch = 188; | ||
311 | port->hw_streamingparams.linethreshold = 0; | ||
312 | port->hw_streamingparams.pagetablelistvirt = NULL; | ||
313 | port->hw_streamingparams.pagetablelistphys = NULL; | ||
314 | port->hw_streamingparams.numpagetables = 2 + | ||
315 | ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); | ||
316 | |||
317 | port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount; | ||
318 | |||
319 | /* Allocate the PCI resources */ | ||
320 | for (i = 0; i < port->hwcfg.buffercount; i++) { | ||
321 | buf = saa7164_buffer_alloc(port, | ||
322 | port->hw_streamingparams.numberoflines * | ||
323 | port->hw_streamingparams.pitch); | ||
324 | |||
325 | if (!buf) { | ||
326 | result = -ENOMEM; | ||
327 | printk(KERN_ERR "%s: dvb_register_adapter failed " | ||
328 | "(errno = %d), unable to allocate buffers\n", | ||
329 | DRIVER_NAME, result); | ||
330 | goto fail_adapter; | ||
331 | } | ||
332 | |||
333 | mutex_lock(&port->dmaqueue_lock); | ||
334 | list_add_tail(&buf->list, &port->dmaqueue.list); | ||
335 | mutex_unlock(&port->dmaqueue_lock); | ||
336 | } | ||
337 | |||
338 | /* register adapter */ | ||
339 | result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, | ||
340 | &dev->pci->dev, adapter_nr); | ||
341 | if (result < 0) { | ||
342 | printk(KERN_ERR "%s: dvb_register_adapter failed " | ||
343 | "(errno = %d)\n", DRIVER_NAME, result); | ||
344 | goto fail_adapter; | ||
345 | } | ||
346 | dvb->adapter.priv = port; | ||
347 | |||
348 | /* register frontend */ | ||
349 | result = dvb_register_frontend(&dvb->adapter, dvb->frontend); | ||
350 | if (result < 0) { | ||
351 | printk(KERN_ERR "%s: dvb_register_frontend failed " | ||
352 | "(errno = %d)\n", DRIVER_NAME, result); | ||
353 | goto fail_frontend; | ||
354 | } | ||
355 | |||
356 | /* register demux stuff */ | ||
357 | dvb->demux.dmx.capabilities = | ||
358 | DMX_TS_FILTERING | DMX_SECTION_FILTERING | | ||
359 | DMX_MEMORY_BASED_FILTERING; | ||
360 | dvb->demux.priv = port; | ||
361 | dvb->demux.filternum = 256; | ||
362 | dvb->demux.feednum = 256; | ||
363 | dvb->demux.start_feed = saa7164_dvb_start_feed; | ||
364 | dvb->demux.stop_feed = saa7164_dvb_stop_feed; | ||
365 | result = dvb_dmx_init(&dvb->demux); | ||
366 | if (result < 0) { | ||
367 | printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", | ||
368 | DRIVER_NAME, result); | ||
369 | goto fail_dmx; | ||
370 | } | ||
371 | |||
372 | dvb->dmxdev.filternum = 256; | ||
373 | dvb->dmxdev.demux = &dvb->demux.dmx; | ||
374 | dvb->dmxdev.capabilities = 0; | ||
375 | result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); | ||
376 | if (result < 0) { | ||
377 | printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", | ||
378 | DRIVER_NAME, result); | ||
379 | goto fail_dmxdev; | ||
380 | } | ||
381 | |||
382 | dvb->fe_hw.source = DMX_FRONTEND_0; | ||
383 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
384 | if (result < 0) { | ||
385 | printk(KERN_ERR "%s: add_frontend failed " | ||
386 | "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); | ||
387 | goto fail_fe_hw; | ||
388 | } | ||
389 | |||
390 | dvb->fe_mem.source = DMX_MEMORY_FE; | ||
391 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); | ||
392 | if (result < 0) { | ||
393 | printk(KERN_ERR "%s: add_frontend failed " | ||
394 | "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); | ||
395 | goto fail_fe_mem; | ||
396 | } | ||
397 | |||
398 | result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
399 | if (result < 0) { | ||
400 | printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", | ||
401 | DRIVER_NAME, result); | ||
402 | goto fail_fe_conn; | ||
403 | } | ||
404 | |||
405 | /* register network adapter */ | ||
406 | dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); | ||
407 | return 0; | ||
408 | |||
409 | fail_fe_conn: | ||
410 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); | ||
411 | fail_fe_mem: | ||
412 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
413 | fail_fe_hw: | ||
414 | dvb_dmxdev_release(&dvb->dmxdev); | ||
415 | fail_dmxdev: | ||
416 | dvb_dmx_release(&dvb->demux); | ||
417 | fail_dmx: | ||
418 | dvb_unregister_frontend(dvb->frontend); | ||
419 | fail_frontend: | ||
420 | dvb_frontend_detach(dvb->frontend); | ||
421 | dvb_unregister_adapter(&dvb->adapter); | ||
422 | fail_adapter: | ||
423 | return result; | ||
424 | } | ||
425 | |||
426 | int saa7164_dvb_unregister(struct saa7164_port *port) | ||
427 | { | ||
428 | struct saa7164_dvb *dvb = &port->dvb; | ||
429 | struct saa7164_dev *dev = port->dev; | ||
430 | struct saa7164_buffer *b; | ||
431 | struct list_head *c, *n; | ||
432 | |||
433 | dprintk(DBGLVL_DVB, "%s()\n", __func__); | ||
434 | |||
435 | if (port->type != SAA7164_MPEG_DVB) | ||
436 | BUG(); | ||
437 | |||
438 | /* Remove any allocated buffers */ | ||
439 | mutex_lock(&port->dmaqueue_lock); | ||
440 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
441 | b = list_entry(c, struct saa7164_buffer, list); | ||
442 | list_del(c); | ||
443 | saa7164_buffer_dealloc(b); | ||
444 | } | ||
445 | mutex_unlock(&port->dmaqueue_lock); | ||
446 | |||
447 | if (dvb->frontend == NULL) | ||
448 | return 0; | ||
449 | |||
450 | dvb_net_release(&dvb->net); | ||
451 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); | ||
452 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); | ||
453 | dvb_dmxdev_release(&dvb->dmxdev); | ||
454 | dvb_dmx_release(&dvb->demux); | ||
455 | dvb_unregister_frontend(dvb->frontend); | ||
456 | dvb_frontend_detach(dvb->frontend); | ||
457 | dvb_unregister_adapter(&dvb->adapter); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /* All the DVB attach calls go here, this function get's modified | ||
462 | * for each new card. | ||
463 | */ | ||
464 | int saa7164_dvb_register(struct saa7164_port *port) | ||
465 | { | ||
466 | struct saa7164_dev *dev = port->dev; | ||
467 | struct saa7164_dvb *dvb = &port->dvb; | ||
468 | struct saa7164_i2c *i2c_bus = NULL; | ||
469 | int ret; | ||
470 | |||
471 | dprintk(DBGLVL_DVB, "%s()\n", __func__); | ||
472 | |||
473 | /* init frontend */ | ||
474 | switch (dev->board) { | ||
475 | case SAA7164_BOARD_HAUPPAUGE_HVR2200: | ||
476 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: | ||
477 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: | ||
478 | case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: | ||
479 | i2c_bus = &dev->i2c_bus[port->nr + 1]; | ||
480 | switch (port->nr) { | ||
481 | case 0: | ||
482 | port->dvb.frontend = dvb_attach(tda10048_attach, | ||
483 | &hauppauge_hvr2200_1_config, | ||
484 | &i2c_bus->i2c_adap); | ||
485 | |||
486 | if (port->dvb.frontend != NULL) { | ||
487 | /* TODO: addr is in the card struct */ | ||
488 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
489 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
490 | &hauppauge_hvr22x0_tuner_config); | ||
491 | } | ||
492 | |||
493 | break; | ||
494 | case 1: | ||
495 | port->dvb.frontend = dvb_attach(tda10048_attach, | ||
496 | &hauppauge_hvr2200_2_config, | ||
497 | &i2c_bus->i2c_adap); | ||
498 | |||
499 | if (port->dvb.frontend != NULL) { | ||
500 | /* TODO: addr is in the card struct */ | ||
501 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
502 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
503 | &hauppauge_hvr22x0s_tuner_config); | ||
504 | } | ||
505 | |||
506 | break; | ||
507 | } | ||
508 | break; | ||
509 | case SAA7164_BOARD_HAUPPAUGE_HVR2250: | ||
510 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: | ||
511 | case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: | ||
512 | i2c_bus = &dev->i2c_bus[port->nr + 1]; | ||
513 | |||
514 | port->dvb.frontend = dvb_attach(s5h1411_attach, | ||
515 | &hauppauge_s5h1411_config, | ||
516 | &i2c_bus->i2c_adap); | ||
517 | |||
518 | if (port->dvb.frontend != NULL) { | ||
519 | if (port->nr == 0) { | ||
520 | /* Master TDA18271 */ | ||
521 | /* TODO: addr is in the card struct */ | ||
522 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
523 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
524 | &hauppauge_hvr22x0_tuner_config); | ||
525 | } else { | ||
526 | /* Slave TDA18271 */ | ||
527 | dvb_attach(tda18271_attach, port->dvb.frontend, | ||
528 | 0xc0 >> 1, &i2c_bus->i2c_adap, | ||
529 | &hauppauge_hvr22x0s_tuner_config); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | break; | ||
534 | default: | ||
535 | printk(KERN_ERR "%s: The frontend isn't supported\n", | ||
536 | dev->name); | ||
537 | break; | ||
538 | } | ||
539 | if (NULL == dvb->frontend) { | ||
540 | printk(KERN_ERR "%s() Frontend initialization failed\n", | ||
541 | __func__); | ||
542 | return -1; | ||
543 | } | ||
544 | |||
545 | /* register everything */ | ||
546 | ret = dvb_register(port); | ||
547 | if (ret < 0) { | ||
548 | if (dvb->frontend->ops.release) | ||
549 | dvb->frontend->ops.release(dvb->frontend); | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | return 0; | ||
554 | } | ||
555 | |||
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c new file mode 100644 index 00000000000..2fd38a01887 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-encoder.c | |||
@@ -0,0 +1,1506 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | #define ENCODER_MAX_BITRATE 6500000 | ||
25 | #define ENCODER_MIN_BITRATE 1000000 | ||
26 | #define ENCODER_DEF_BITRATE 5000000 | ||
27 | |||
28 | static struct saa7164_tvnorm saa7164_tvnorms[] = { | ||
29 | { | ||
30 | .name = "NTSC-M", | ||
31 | .id = V4L2_STD_NTSC_M, | ||
32 | }, { | ||
33 | .name = "NTSC-JP", | ||
34 | .id = V4L2_STD_NTSC_M_JP, | ||
35 | } | ||
36 | }; | ||
37 | |||
38 | static const u32 saa7164_v4l2_ctrls[] = { | ||
39 | V4L2_CID_BRIGHTNESS, | ||
40 | V4L2_CID_CONTRAST, | ||
41 | V4L2_CID_SATURATION, | ||
42 | V4L2_CID_HUE, | ||
43 | V4L2_CID_AUDIO_VOLUME, | ||
44 | V4L2_CID_SHARPNESS, | ||
45 | V4L2_CID_MPEG_STREAM_TYPE, | ||
46 | V4L2_CID_MPEG_VIDEO_ASPECT, | ||
47 | V4L2_CID_MPEG_VIDEO_B_FRAMES, | ||
48 | V4L2_CID_MPEG_VIDEO_GOP_SIZE, | ||
49 | V4L2_CID_MPEG_AUDIO_MUTE, | ||
50 | V4L2_CID_MPEG_VIDEO_BITRATE_MODE, | ||
51 | V4L2_CID_MPEG_VIDEO_BITRATE, | ||
52 | V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, | ||
53 | 0 | ||
54 | }; | ||
55 | |||
56 | /* Take the encoder configuration form the port struct and | ||
57 | * flush it to the hardware. | ||
58 | */ | ||
59 | static void saa7164_encoder_configure(struct saa7164_port *port) | ||
60 | { | ||
61 | struct saa7164_dev *dev = port->dev; | ||
62 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
63 | |||
64 | port->encoder_params.width = port->width; | ||
65 | port->encoder_params.height = port->height; | ||
66 | port->encoder_params.is_50hz = | ||
67 | (port->encodernorm.id & V4L2_STD_625_50) != 0; | ||
68 | |||
69 | /* Set up the DIF (enable it) for analog mode by default */ | ||
70 | saa7164_api_initialize_dif(port); | ||
71 | |||
72 | /* Configure the correct video standard */ | ||
73 | saa7164_api_configure_dif(port, port->encodernorm.id); | ||
74 | |||
75 | /* Ensure the audio decoder is correct configured */ | ||
76 | saa7164_api_set_audio_std(port); | ||
77 | } | ||
78 | |||
79 | static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port) | ||
80 | { | ||
81 | struct list_head *c, *n, *p, *q, *l, *v; | ||
82 | struct saa7164_dev *dev = port->dev; | ||
83 | struct saa7164_buffer *buf; | ||
84 | struct saa7164_user_buffer *ubuf; | ||
85 | |||
86 | /* Remove any allocated buffers */ | ||
87 | mutex_lock(&port->dmaqueue_lock); | ||
88 | |||
89 | dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr); | ||
90 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
91 | buf = list_entry(c, struct saa7164_buffer, list); | ||
92 | list_del(c); | ||
93 | saa7164_buffer_dealloc(buf); | ||
94 | } | ||
95 | |||
96 | dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr); | ||
97 | list_for_each_safe(p, q, &port->list_buf_used.list) { | ||
98 | ubuf = list_entry(p, struct saa7164_user_buffer, list); | ||
99 | list_del(p); | ||
100 | saa7164_buffer_dealloc_user(ubuf); | ||
101 | } | ||
102 | |||
103 | dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr); | ||
104 | list_for_each_safe(l, v, &port->list_buf_free.list) { | ||
105 | ubuf = list_entry(l, struct saa7164_user_buffer, list); | ||
106 | list_del(l); | ||
107 | saa7164_buffer_dealloc_user(ubuf); | ||
108 | } | ||
109 | |||
110 | mutex_unlock(&port->dmaqueue_lock); | ||
111 | dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | /* Dynamic buffer switch at encoder start time */ | ||
117 | static int saa7164_encoder_buffers_alloc(struct saa7164_port *port) | ||
118 | { | ||
119 | struct saa7164_dev *dev = port->dev; | ||
120 | struct saa7164_buffer *buf; | ||
121 | struct saa7164_user_buffer *ubuf; | ||
122 | struct tmHWStreamParameters *params = &port->hw_streamingparams; | ||
123 | int result = -ENODEV, i; | ||
124 | int len = 0; | ||
125 | |||
126 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
127 | |||
128 | if (port->encoder_params.stream_type == | ||
129 | V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { | ||
130 | dprintk(DBGLVL_ENC, | ||
131 | "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n", | ||
132 | __func__); | ||
133 | params->samplesperline = 128; | ||
134 | params->numberoflines = 256; | ||
135 | params->pitch = 128; | ||
136 | params->numpagetables = 2 + | ||
137 | ((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE); | ||
138 | } else | ||
139 | if (port->encoder_params.stream_type == | ||
140 | V4L2_MPEG_STREAM_TYPE_MPEG2_TS) { | ||
141 | dprintk(DBGLVL_ENC, | ||
142 | "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n", | ||
143 | __func__); | ||
144 | params->samplesperline = 188; | ||
145 | params->numberoflines = 312; | ||
146 | params->pitch = 188; | ||
147 | params->numpagetables = 2 + | ||
148 | ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); | ||
149 | } else | ||
150 | BUG(); | ||
151 | |||
152 | /* Init and establish defaults */ | ||
153 | params->bitspersample = 8; | ||
154 | params->linethreshold = 0; | ||
155 | params->pagetablelistvirt = NULL; | ||
156 | params->pagetablelistphys = NULL; | ||
157 | params->numpagetableentries = port->hwcfg.buffercount; | ||
158 | |||
159 | /* Allocate the PCI resources, buffers (hard) */ | ||
160 | for (i = 0; i < port->hwcfg.buffercount; i++) { | ||
161 | buf = saa7164_buffer_alloc(port, | ||
162 | params->numberoflines * | ||
163 | params->pitch); | ||
164 | |||
165 | if (!buf) { | ||
166 | printk(KERN_ERR "%s() failed " | ||
167 | "(errno = %d), unable to allocate buffer\n", | ||
168 | __func__, result); | ||
169 | result = -ENOMEM; | ||
170 | goto failed; | ||
171 | } else { | ||
172 | |||
173 | mutex_lock(&port->dmaqueue_lock); | ||
174 | list_add_tail(&buf->list, &port->dmaqueue.list); | ||
175 | mutex_unlock(&port->dmaqueue_lock); | ||
176 | |||
177 | } | ||
178 | } | ||
179 | |||
180 | /* Allocate some kernel buffers for copying | ||
181 | * to userpsace. | ||
182 | */ | ||
183 | len = params->numberoflines * params->pitch; | ||
184 | |||
185 | if (encoder_buffers < 16) | ||
186 | encoder_buffers = 16; | ||
187 | if (encoder_buffers > 512) | ||
188 | encoder_buffers = 512; | ||
189 | |||
190 | for (i = 0; i < encoder_buffers; i++) { | ||
191 | |||
192 | ubuf = saa7164_buffer_alloc_user(dev, len); | ||
193 | if (ubuf) { | ||
194 | mutex_lock(&port->dmaqueue_lock); | ||
195 | list_add_tail(&ubuf->list, &port->list_buf_free.list); | ||
196 | mutex_unlock(&port->dmaqueue_lock); | ||
197 | } | ||
198 | |||
199 | } | ||
200 | |||
201 | result = 0; | ||
202 | |||
203 | failed: | ||
204 | return result; | ||
205 | } | ||
206 | |||
207 | static int saa7164_encoder_initialize(struct saa7164_port *port) | ||
208 | { | ||
209 | saa7164_encoder_configure(port); | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | /* -- V4L2 --------------------------------------------------------- */ | ||
214 | static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) | ||
215 | { | ||
216 | struct saa7164_encoder_fh *fh = file->private_data; | ||
217 | struct saa7164_port *port = fh->port; | ||
218 | struct saa7164_dev *dev = port->dev; | ||
219 | unsigned int i; | ||
220 | |||
221 | dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)*id); | ||
222 | |||
223 | for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { | ||
224 | if (*id & saa7164_tvnorms[i].id) | ||
225 | break; | ||
226 | } | ||
227 | if (i == ARRAY_SIZE(saa7164_tvnorms)) | ||
228 | return -EINVAL; | ||
229 | |||
230 | port->encodernorm = saa7164_tvnorms[i]; | ||
231 | |||
232 | /* Update the audio decoder while is not running in | ||
233 | * auto detect mode. | ||
234 | */ | ||
235 | saa7164_api_set_audio_std(port); | ||
236 | |||
237 | dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)*id); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static int vidioc_enum_input(struct file *file, void *priv, | ||
243 | struct v4l2_input *i) | ||
244 | { | ||
245 | int n; | ||
246 | |||
247 | char *inputs[] = { "tuner", "composite", "svideo", "aux", | ||
248 | "composite 2", "svideo 2", "aux 2" }; | ||
249 | |||
250 | if (i->index >= 7) | ||
251 | return -EINVAL; | ||
252 | |||
253 | strcpy(i->name, inputs[i->index]); | ||
254 | |||
255 | if (i->index == 0) | ||
256 | i->type = V4L2_INPUT_TYPE_TUNER; | ||
257 | else | ||
258 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
259 | |||
260 | for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++) | ||
261 | i->std |= saa7164_tvnorms[n].id; | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) | ||
267 | { | ||
268 | struct saa7164_encoder_fh *fh = file->private_data; | ||
269 | struct saa7164_port *port = fh->port; | ||
270 | struct saa7164_dev *dev = port->dev; | ||
271 | |||
272 | if (saa7164_api_get_videomux(port) != SAA_OK) | ||
273 | return -EIO; | ||
274 | |||
275 | *i = (port->mux_input - 1); | ||
276 | |||
277 | dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i); | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static int vidioc_s_input(struct file *file, void *priv, unsigned int i) | ||
283 | { | ||
284 | struct saa7164_encoder_fh *fh = file->private_data; | ||
285 | struct saa7164_port *port = fh->port; | ||
286 | struct saa7164_dev *dev = port->dev; | ||
287 | |||
288 | dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i); | ||
289 | |||
290 | if (i >= 7) | ||
291 | return -EINVAL; | ||
292 | |||
293 | port->mux_input = i + 1; | ||
294 | |||
295 | if (saa7164_api_set_videomux(port) != SAA_OK) | ||
296 | return -EIO; | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
302 | struct v4l2_tuner *t) | ||
303 | { | ||
304 | struct saa7164_encoder_fh *fh = file->private_data; | ||
305 | struct saa7164_port *port = fh->port; | ||
306 | struct saa7164_dev *dev = port->dev; | ||
307 | |||
308 | if (0 != t->index) | ||
309 | return -EINVAL; | ||
310 | |||
311 | strcpy(t->name, "tuner"); | ||
312 | t->type = V4L2_TUNER_ANALOG_TV; | ||
313 | t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; | ||
314 | |||
315 | dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
321 | struct v4l2_tuner *t) | ||
322 | { | ||
323 | /* Update the A/V core */ | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
328 | struct v4l2_frequency *f) | ||
329 | { | ||
330 | struct saa7164_encoder_fh *fh = file->private_data; | ||
331 | struct saa7164_port *port = fh->port; | ||
332 | |||
333 | f->type = V4L2_TUNER_ANALOG_TV; | ||
334 | f->frequency = port->freq; | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
340 | struct v4l2_frequency *f) | ||
341 | { | ||
342 | struct saa7164_encoder_fh *fh = file->private_data; | ||
343 | struct saa7164_port *port = fh->port; | ||
344 | struct saa7164_dev *dev = port->dev; | ||
345 | struct saa7164_port *tsport; | ||
346 | struct dvb_frontend *fe; | ||
347 | |||
348 | /* TODO: Pull this for the std */ | ||
349 | struct analog_parameters params = { | ||
350 | .mode = V4L2_TUNER_ANALOG_TV, | ||
351 | .audmode = V4L2_TUNER_MODE_STEREO, | ||
352 | .std = port->encodernorm.id, | ||
353 | .frequency = f->frequency | ||
354 | }; | ||
355 | |||
356 | /* Stop the encoder */ | ||
357 | dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__, | ||
358 | f->frequency, f->tuner); | ||
359 | |||
360 | if (f->tuner != 0) | ||
361 | return -EINVAL; | ||
362 | |||
363 | if (f->type != V4L2_TUNER_ANALOG_TV) | ||
364 | return -EINVAL; | ||
365 | |||
366 | port->freq = f->frequency; | ||
367 | |||
368 | /* Update the hardware */ | ||
369 | if (port->nr == SAA7164_PORT_ENC1) | ||
370 | tsport = &dev->ports[SAA7164_PORT_TS1]; | ||
371 | else | ||
372 | if (port->nr == SAA7164_PORT_ENC2) | ||
373 | tsport = &dev->ports[SAA7164_PORT_TS2]; | ||
374 | else | ||
375 | BUG(); | ||
376 | |||
377 | fe = tsport->dvb.frontend; | ||
378 | |||
379 | if (fe && fe->ops.tuner_ops.set_analog_params) | ||
380 | fe->ops.tuner_ops.set_analog_params(fe, ¶ms); | ||
381 | else | ||
382 | printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__); | ||
383 | |||
384 | saa7164_encoder_initialize(port); | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int vidioc_g_ctrl(struct file *file, void *priv, | ||
390 | struct v4l2_control *ctl) | ||
391 | { | ||
392 | struct saa7164_encoder_fh *fh = file->private_data; | ||
393 | struct saa7164_port *port = fh->port; | ||
394 | struct saa7164_dev *dev = port->dev; | ||
395 | |||
396 | dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__, | ||
397 | ctl->id, ctl->value); | ||
398 | |||
399 | switch (ctl->id) { | ||
400 | case V4L2_CID_BRIGHTNESS: | ||
401 | ctl->value = port->ctl_brightness; | ||
402 | break; | ||
403 | case V4L2_CID_CONTRAST: | ||
404 | ctl->value = port->ctl_contrast; | ||
405 | break; | ||
406 | case V4L2_CID_SATURATION: | ||
407 | ctl->value = port->ctl_saturation; | ||
408 | break; | ||
409 | case V4L2_CID_HUE: | ||
410 | ctl->value = port->ctl_hue; | ||
411 | break; | ||
412 | case V4L2_CID_SHARPNESS: | ||
413 | ctl->value = port->ctl_sharpness; | ||
414 | break; | ||
415 | case V4L2_CID_AUDIO_VOLUME: | ||
416 | ctl->value = port->ctl_volume; | ||
417 | break; | ||
418 | default: | ||
419 | return -EINVAL; | ||
420 | } | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
426 | struct v4l2_control *ctl) | ||
427 | { | ||
428 | struct saa7164_encoder_fh *fh = file->private_data; | ||
429 | struct saa7164_port *port = fh->port; | ||
430 | struct saa7164_dev *dev = port->dev; | ||
431 | int ret = 0; | ||
432 | |||
433 | dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__, | ||
434 | ctl->id, ctl->value); | ||
435 | |||
436 | switch (ctl->id) { | ||
437 | case V4L2_CID_BRIGHTNESS: | ||
438 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
439 | port->ctl_brightness = ctl->value; | ||
440 | saa7164_api_set_usercontrol(port, | ||
441 | PU_BRIGHTNESS_CONTROL); | ||
442 | } else | ||
443 | ret = -EINVAL; | ||
444 | break; | ||
445 | case V4L2_CID_CONTRAST: | ||
446 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
447 | port->ctl_contrast = ctl->value; | ||
448 | saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); | ||
449 | } else | ||
450 | ret = -EINVAL; | ||
451 | break; | ||
452 | case V4L2_CID_SATURATION: | ||
453 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
454 | port->ctl_saturation = ctl->value; | ||
455 | saa7164_api_set_usercontrol(port, | ||
456 | PU_SATURATION_CONTROL); | ||
457 | } else | ||
458 | ret = -EINVAL; | ||
459 | break; | ||
460 | case V4L2_CID_HUE: | ||
461 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
462 | port->ctl_hue = ctl->value; | ||
463 | saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); | ||
464 | } else | ||
465 | ret = -EINVAL; | ||
466 | break; | ||
467 | case V4L2_CID_SHARPNESS: | ||
468 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
469 | port->ctl_sharpness = ctl->value; | ||
470 | saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); | ||
471 | } else | ||
472 | ret = -EINVAL; | ||
473 | break; | ||
474 | case V4L2_CID_AUDIO_VOLUME: | ||
475 | if ((ctl->value >= -83) && (ctl->value <= 24)) { | ||
476 | port->ctl_volume = ctl->value; | ||
477 | saa7164_api_set_audio_volume(port, port->ctl_volume); | ||
478 | } else | ||
479 | ret = -EINVAL; | ||
480 | break; | ||
481 | default: | ||
482 | ret = -EINVAL; | ||
483 | } | ||
484 | |||
485 | return ret; | ||
486 | } | ||
487 | |||
488 | static int saa7164_get_ctrl(struct saa7164_port *port, | ||
489 | struct v4l2_ext_control *ctrl) | ||
490 | { | ||
491 | struct saa7164_encoder_params *params = &port->encoder_params; | ||
492 | |||
493 | switch (ctrl->id) { | ||
494 | case V4L2_CID_MPEG_VIDEO_BITRATE: | ||
495 | ctrl->value = params->bitrate; | ||
496 | break; | ||
497 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
498 | ctrl->value = params->stream_type; | ||
499 | break; | ||
500 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
501 | ctrl->value = params->ctl_mute; | ||
502 | break; | ||
503 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
504 | ctrl->value = params->ctl_aspect; | ||
505 | break; | ||
506 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: | ||
507 | ctrl->value = params->bitrate_mode; | ||
508 | break; | ||
509 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
510 | ctrl->value = params->refdist; | ||
511 | break; | ||
512 | case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: | ||
513 | ctrl->value = params->bitrate_peak; | ||
514 | break; | ||
515 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
516 | ctrl->value = params->gop_size; | ||
517 | break; | ||
518 | default: | ||
519 | return -EINVAL; | ||
520 | } | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int vidioc_g_ext_ctrls(struct file *file, void *priv, | ||
525 | struct v4l2_ext_controls *ctrls) | ||
526 | { | ||
527 | struct saa7164_encoder_fh *fh = file->private_data; | ||
528 | struct saa7164_port *port = fh->port; | ||
529 | int i, err = 0; | ||
530 | |||
531 | if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { | ||
532 | for (i = 0; i < ctrls->count; i++) { | ||
533 | struct v4l2_ext_control *ctrl = ctrls->controls + i; | ||
534 | |||
535 | err = saa7164_get_ctrl(port, ctrl); | ||
536 | if (err) { | ||
537 | ctrls->error_idx = i; | ||
538 | break; | ||
539 | } | ||
540 | } | ||
541 | return err; | ||
542 | |||
543 | } | ||
544 | |||
545 | return -EINVAL; | ||
546 | } | ||
547 | |||
548 | static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) | ||
549 | { | ||
550 | int ret = -EINVAL; | ||
551 | |||
552 | switch (ctrl->id) { | ||
553 | case V4L2_CID_MPEG_VIDEO_BITRATE: | ||
554 | if ((ctrl->value >= ENCODER_MIN_BITRATE) && | ||
555 | (ctrl->value <= ENCODER_MAX_BITRATE)) | ||
556 | ret = 0; | ||
557 | break; | ||
558 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
559 | if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) || | ||
560 | (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)) | ||
561 | ret = 0; | ||
562 | break; | ||
563 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
564 | if ((ctrl->value >= 0) && | ||
565 | (ctrl->value <= 1)) | ||
566 | ret = 0; | ||
567 | break; | ||
568 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
569 | if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) && | ||
570 | (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100)) | ||
571 | ret = 0; | ||
572 | break; | ||
573 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
574 | if ((ctrl->value >= 0) && | ||
575 | (ctrl->value <= 255)) | ||
576 | ret = 0; | ||
577 | break; | ||
578 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: | ||
579 | if ((ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) || | ||
580 | (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)) | ||
581 | ret = 0; | ||
582 | break; | ||
583 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
584 | if ((ctrl->value >= 1) && | ||
585 | (ctrl->value <= 3)) | ||
586 | ret = 0; | ||
587 | break; | ||
588 | case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: | ||
589 | if ((ctrl->value >= ENCODER_MIN_BITRATE) && | ||
590 | (ctrl->value <= ENCODER_MAX_BITRATE)) | ||
591 | ret = 0; | ||
592 | break; | ||
593 | default: | ||
594 | ret = -EINVAL; | ||
595 | } | ||
596 | |||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | static int vidioc_try_ext_ctrls(struct file *file, void *priv, | ||
601 | struct v4l2_ext_controls *ctrls) | ||
602 | { | ||
603 | int i, err = 0; | ||
604 | |||
605 | if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { | ||
606 | for (i = 0; i < ctrls->count; i++) { | ||
607 | struct v4l2_ext_control *ctrl = ctrls->controls + i; | ||
608 | |||
609 | err = saa7164_try_ctrl(ctrl, 0); | ||
610 | if (err) { | ||
611 | ctrls->error_idx = i; | ||
612 | break; | ||
613 | } | ||
614 | } | ||
615 | return err; | ||
616 | } | ||
617 | |||
618 | return -EINVAL; | ||
619 | } | ||
620 | |||
621 | static int saa7164_set_ctrl(struct saa7164_port *port, | ||
622 | struct v4l2_ext_control *ctrl) | ||
623 | { | ||
624 | struct saa7164_encoder_params *params = &port->encoder_params; | ||
625 | int ret = 0; | ||
626 | |||
627 | switch (ctrl->id) { | ||
628 | case V4L2_CID_MPEG_VIDEO_BITRATE: | ||
629 | params->bitrate = ctrl->value; | ||
630 | break; | ||
631 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
632 | params->stream_type = ctrl->value; | ||
633 | break; | ||
634 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
635 | params->ctl_mute = ctrl->value; | ||
636 | ret = saa7164_api_audio_mute(port, params->ctl_mute); | ||
637 | if (ret != SAA_OK) { | ||
638 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, | ||
639 | ret); | ||
640 | ret = -EIO; | ||
641 | } | ||
642 | break; | ||
643 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
644 | params->ctl_aspect = ctrl->value; | ||
645 | ret = saa7164_api_set_aspect_ratio(port); | ||
646 | if (ret != SAA_OK) { | ||
647 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, | ||
648 | ret); | ||
649 | ret = -EIO; | ||
650 | } | ||
651 | break; | ||
652 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: | ||
653 | params->bitrate_mode = ctrl->value; | ||
654 | break; | ||
655 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
656 | params->refdist = ctrl->value; | ||
657 | break; | ||
658 | case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: | ||
659 | params->bitrate_peak = ctrl->value; | ||
660 | break; | ||
661 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
662 | params->gop_size = ctrl->value; | ||
663 | break; | ||
664 | default: | ||
665 | return -EINVAL; | ||
666 | } | ||
667 | |||
668 | /* TODO: Update the hardware */ | ||
669 | |||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | static int vidioc_s_ext_ctrls(struct file *file, void *priv, | ||
674 | struct v4l2_ext_controls *ctrls) | ||
675 | { | ||
676 | struct saa7164_encoder_fh *fh = file->private_data; | ||
677 | struct saa7164_port *port = fh->port; | ||
678 | int i, err = 0; | ||
679 | |||
680 | if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { | ||
681 | for (i = 0; i < ctrls->count; i++) { | ||
682 | struct v4l2_ext_control *ctrl = ctrls->controls + i; | ||
683 | |||
684 | err = saa7164_try_ctrl(ctrl, 0); | ||
685 | if (err) { | ||
686 | ctrls->error_idx = i; | ||
687 | break; | ||
688 | } | ||
689 | err = saa7164_set_ctrl(port, ctrl); | ||
690 | if (err) { | ||
691 | ctrls->error_idx = i; | ||
692 | break; | ||
693 | } | ||
694 | } | ||
695 | return err; | ||
696 | |||
697 | } | ||
698 | |||
699 | return -EINVAL; | ||
700 | } | ||
701 | |||
702 | static int vidioc_querycap(struct file *file, void *priv, | ||
703 | struct v4l2_capability *cap) | ||
704 | { | ||
705 | struct saa7164_encoder_fh *fh = file->private_data; | ||
706 | struct saa7164_port *port = fh->port; | ||
707 | struct saa7164_dev *dev = port->dev; | ||
708 | |||
709 | strcpy(cap->driver, dev->name); | ||
710 | strlcpy(cap->card, saa7164_boards[dev->board].name, | ||
711 | sizeof(cap->card)); | ||
712 | sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); | ||
713 | |||
714 | cap->capabilities = | ||
715 | V4L2_CAP_VIDEO_CAPTURE | | ||
716 | V4L2_CAP_READWRITE | | ||
717 | 0; | ||
718 | |||
719 | cap->capabilities |= V4L2_CAP_TUNER; | ||
720 | cap->version = 0; | ||
721 | |||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, | ||
726 | struct v4l2_fmtdesc *f) | ||
727 | { | ||
728 | if (f->index != 0) | ||
729 | return -EINVAL; | ||
730 | |||
731 | strlcpy(f->description, "MPEG", sizeof(f->description)); | ||
732 | f->pixelformat = V4L2_PIX_FMT_MPEG; | ||
733 | |||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, | ||
738 | struct v4l2_format *f) | ||
739 | { | ||
740 | struct saa7164_encoder_fh *fh = file->private_data; | ||
741 | struct saa7164_port *port = fh->port; | ||
742 | struct saa7164_dev *dev = port->dev; | ||
743 | |||
744 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
745 | f->fmt.pix.bytesperline = 0; | ||
746 | f->fmt.pix.sizeimage = | ||
747 | port->ts_packet_size * port->ts_packet_count; | ||
748 | f->fmt.pix.colorspace = 0; | ||
749 | f->fmt.pix.width = port->width; | ||
750 | f->fmt.pix.height = port->height; | ||
751 | |||
752 | dprintk(DBGLVL_ENC, "VIDIOC_G_FMT: w: %d, h: %d\n", | ||
753 | port->width, port->height); | ||
754 | |||
755 | return 0; | ||
756 | } | ||
757 | |||
758 | static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, | ||
759 | struct v4l2_format *f) | ||
760 | { | ||
761 | struct saa7164_encoder_fh *fh = file->private_data; | ||
762 | struct saa7164_port *port = fh->port; | ||
763 | struct saa7164_dev *dev = port->dev; | ||
764 | |||
765 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
766 | f->fmt.pix.bytesperline = 0; | ||
767 | f->fmt.pix.sizeimage = | ||
768 | port->ts_packet_size * port->ts_packet_count; | ||
769 | f->fmt.pix.colorspace = 0; | ||
770 | dprintk(DBGLVL_ENC, "VIDIOC_TRY_FMT: w: %d, h: %d\n", | ||
771 | port->width, port->height); | ||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, | ||
776 | struct v4l2_format *f) | ||
777 | { | ||
778 | struct saa7164_encoder_fh *fh = file->private_data; | ||
779 | struct saa7164_port *port = fh->port; | ||
780 | struct saa7164_dev *dev = port->dev; | ||
781 | |||
782 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
783 | f->fmt.pix.bytesperline = 0; | ||
784 | f->fmt.pix.sizeimage = | ||
785 | port->ts_packet_size * port->ts_packet_count; | ||
786 | f->fmt.pix.colorspace = 0; | ||
787 | |||
788 | dprintk(DBGLVL_ENC, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", | ||
789 | f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); | ||
790 | |||
791 | return 0; | ||
792 | } | ||
793 | |||
794 | static int vidioc_log_status(struct file *file, void *priv) | ||
795 | { | ||
796 | return 0; | ||
797 | } | ||
798 | |||
799 | static int fill_queryctrl(struct saa7164_encoder_params *params, | ||
800 | struct v4l2_queryctrl *c) | ||
801 | { | ||
802 | switch (c->id) { | ||
803 | case V4L2_CID_BRIGHTNESS: | ||
804 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127); | ||
805 | case V4L2_CID_CONTRAST: | ||
806 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66); | ||
807 | case V4L2_CID_SATURATION: | ||
808 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62); | ||
809 | case V4L2_CID_HUE: | ||
810 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128); | ||
811 | case V4L2_CID_SHARPNESS: | ||
812 | return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8); | ||
813 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
814 | return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0); | ||
815 | case V4L2_CID_AUDIO_VOLUME: | ||
816 | return v4l2_ctrl_query_fill(c, -83, 24, 1, 20); | ||
817 | case V4L2_CID_MPEG_VIDEO_BITRATE: | ||
818 | return v4l2_ctrl_query_fill(c, | ||
819 | ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, | ||
820 | 100000, ENCODER_DEF_BITRATE); | ||
821 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
822 | return v4l2_ctrl_query_fill(c, | ||
823 | V4L2_MPEG_STREAM_TYPE_MPEG2_PS, | ||
824 | V4L2_MPEG_STREAM_TYPE_MPEG2_TS, | ||
825 | 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS); | ||
826 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
827 | return v4l2_ctrl_query_fill(c, | ||
828 | V4L2_MPEG_VIDEO_ASPECT_1x1, | ||
829 | V4L2_MPEG_VIDEO_ASPECT_221x100, | ||
830 | 1, V4L2_MPEG_VIDEO_ASPECT_4x3); | ||
831 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
832 | return v4l2_ctrl_query_fill(c, 1, 255, 1, 15); | ||
833 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: | ||
834 | return v4l2_ctrl_query_fill(c, | ||
835 | V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, | ||
836 | V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, | ||
837 | 1, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); | ||
838 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
839 | return v4l2_ctrl_query_fill(c, | ||
840 | 1, 3, 1, 1); | ||
841 | case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: | ||
842 | return v4l2_ctrl_query_fill(c, | ||
843 | ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, | ||
844 | 100000, ENCODER_DEF_BITRATE); | ||
845 | default: | ||
846 | return -EINVAL; | ||
847 | } | ||
848 | } | ||
849 | |||
850 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
851 | struct v4l2_queryctrl *c) | ||
852 | { | ||
853 | struct saa7164_encoder_fh *fh = priv; | ||
854 | struct saa7164_port *port = fh->port; | ||
855 | int i, next; | ||
856 | u32 id = c->id; | ||
857 | |||
858 | memset(c, 0, sizeof(*c)); | ||
859 | |||
860 | next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); | ||
861 | c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; | ||
862 | |||
863 | for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) { | ||
864 | if (next) { | ||
865 | if (c->id < saa7164_v4l2_ctrls[i]) | ||
866 | c->id = saa7164_v4l2_ctrls[i]; | ||
867 | else | ||
868 | continue; | ||
869 | } | ||
870 | |||
871 | if (c->id == saa7164_v4l2_ctrls[i]) | ||
872 | return fill_queryctrl(&port->encoder_params, c); | ||
873 | |||
874 | if (c->id < saa7164_v4l2_ctrls[i]) | ||
875 | break; | ||
876 | } | ||
877 | |||
878 | return -EINVAL; | ||
879 | } | ||
880 | |||
881 | static int saa7164_encoder_stop_port(struct saa7164_port *port) | ||
882 | { | ||
883 | struct saa7164_dev *dev = port->dev; | ||
884 | int ret; | ||
885 | |||
886 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
887 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
888 | printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", | ||
889 | __func__, ret); | ||
890 | ret = -EIO; | ||
891 | } else { | ||
892 | dprintk(DBGLVL_ENC, "%s() Stopped\n", __func__); | ||
893 | ret = 0; | ||
894 | } | ||
895 | |||
896 | return ret; | ||
897 | } | ||
898 | |||
899 | static int saa7164_encoder_acquire_port(struct saa7164_port *port) | ||
900 | { | ||
901 | struct saa7164_dev *dev = port->dev; | ||
902 | int ret; | ||
903 | |||
904 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
905 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
906 | printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", | ||
907 | __func__, ret); | ||
908 | ret = -EIO; | ||
909 | } else { | ||
910 | dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__); | ||
911 | ret = 0; | ||
912 | } | ||
913 | |||
914 | return ret; | ||
915 | } | ||
916 | |||
917 | static int saa7164_encoder_pause_port(struct saa7164_port *port) | ||
918 | { | ||
919 | struct saa7164_dev *dev = port->dev; | ||
920 | int ret; | ||
921 | |||
922 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
923 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
924 | printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", | ||
925 | __func__, ret); | ||
926 | ret = -EIO; | ||
927 | } else { | ||
928 | dprintk(DBGLVL_ENC, "%s() Paused\n", __func__); | ||
929 | ret = 0; | ||
930 | } | ||
931 | |||
932 | return ret; | ||
933 | } | ||
934 | |||
935 | /* Firmware is very windows centric, meaning you have to transition | ||
936 | * the part through AVStream / KS Windows stages, forwards or backwards. | ||
937 | * States are: stopped, acquired (h/w), paused, started. | ||
938 | * We have to leave here will all of the soft buffers on the free list, | ||
939 | * else the cfg_post() func won't have soft buffers to correctly configure. | ||
940 | */ | ||
941 | static int saa7164_encoder_stop_streaming(struct saa7164_port *port) | ||
942 | { | ||
943 | struct saa7164_dev *dev = port->dev; | ||
944 | struct saa7164_buffer *buf; | ||
945 | struct saa7164_user_buffer *ubuf; | ||
946 | struct list_head *c, *n; | ||
947 | int ret; | ||
948 | |||
949 | dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); | ||
950 | |||
951 | ret = saa7164_encoder_pause_port(port); | ||
952 | ret = saa7164_encoder_acquire_port(port); | ||
953 | ret = saa7164_encoder_stop_port(port); | ||
954 | |||
955 | dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__, | ||
956 | port->nr); | ||
957 | |||
958 | /* Reset the state of any allocated buffer resources */ | ||
959 | mutex_lock(&port->dmaqueue_lock); | ||
960 | |||
961 | /* Reset the hard and soft buffer state */ | ||
962 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
963 | buf = list_entry(c, struct saa7164_buffer, list); | ||
964 | buf->flags = SAA7164_BUFFER_FREE; | ||
965 | buf->pos = 0; | ||
966 | } | ||
967 | |||
968 | list_for_each_safe(c, n, &port->list_buf_used.list) { | ||
969 | ubuf = list_entry(c, struct saa7164_user_buffer, list); | ||
970 | ubuf->pos = 0; | ||
971 | list_move_tail(&ubuf->list, &port->list_buf_free.list); | ||
972 | } | ||
973 | |||
974 | mutex_unlock(&port->dmaqueue_lock); | ||
975 | |||
976 | /* Free any allocated resources */ | ||
977 | saa7164_encoder_buffers_dealloc(port); | ||
978 | |||
979 | dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr); | ||
980 | |||
981 | return ret; | ||
982 | } | ||
983 | |||
984 | static int saa7164_encoder_start_streaming(struct saa7164_port *port) | ||
985 | { | ||
986 | struct saa7164_dev *dev = port->dev; | ||
987 | int result, ret = 0; | ||
988 | |||
989 | dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); | ||
990 | |||
991 | port->done_first_interrupt = 0; | ||
992 | |||
993 | /* allocate all of the PCIe DMA buffer resources on the fly, | ||
994 | * allowing switching between TS and PS payloads without | ||
995 | * requiring a complete driver reload. | ||
996 | */ | ||
997 | saa7164_encoder_buffers_alloc(port); | ||
998 | |||
999 | /* Configure the encoder with any cache values */ | ||
1000 | saa7164_api_set_encoder(port); | ||
1001 | saa7164_api_get_encoder(port); | ||
1002 | |||
1003 | /* Place the empty buffers on the hardware */ | ||
1004 | saa7164_buffer_cfg_port(port); | ||
1005 | |||
1006 | /* Acquire the hardware */ | ||
1007 | result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
1008 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
1009 | printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", | ||
1010 | __func__, result); | ||
1011 | |||
1012 | /* Stop the hardware, regardless */ | ||
1013 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
1014 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
1015 | printk(KERN_ERR "%s() acquire/forced stop transition " | ||
1016 | "failed, res = 0x%x\n", __func__, result); | ||
1017 | } | ||
1018 | ret = -EIO; | ||
1019 | goto out; | ||
1020 | } else | ||
1021 | dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__); | ||
1022 | |||
1023 | /* Pause the hardware */ | ||
1024 | result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
1025 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
1026 | printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", | ||
1027 | __func__, result); | ||
1028 | |||
1029 | /* Stop the hardware, regardless */ | ||
1030 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
1031 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
1032 | printk(KERN_ERR "%s() pause/forced stop transition " | ||
1033 | "failed, res = 0x%x\n", __func__, result); | ||
1034 | } | ||
1035 | |||
1036 | ret = -EIO; | ||
1037 | goto out; | ||
1038 | } else | ||
1039 | dprintk(DBGLVL_ENC, "%s() Paused\n", __func__); | ||
1040 | |||
1041 | /* Start the hardware */ | ||
1042 | result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); | ||
1043 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
1044 | printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", | ||
1045 | __func__, result); | ||
1046 | |||
1047 | /* Stop the hardware, regardless */ | ||
1048 | result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
1049 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
1050 | printk(KERN_ERR "%s() run/forced stop transition " | ||
1051 | "failed, res = 0x%x\n", __func__, result); | ||
1052 | } | ||
1053 | |||
1054 | ret = -EIO; | ||
1055 | } else | ||
1056 | dprintk(DBGLVL_ENC, "%s() Running\n", __func__); | ||
1057 | |||
1058 | out: | ||
1059 | return ret; | ||
1060 | } | ||
1061 | |||
1062 | static int fops_open(struct file *file) | ||
1063 | { | ||
1064 | struct saa7164_dev *dev; | ||
1065 | struct saa7164_port *port; | ||
1066 | struct saa7164_encoder_fh *fh; | ||
1067 | |||
1068 | port = (struct saa7164_port *)video_get_drvdata(video_devdata(file)); | ||
1069 | if (!port) | ||
1070 | return -ENODEV; | ||
1071 | |||
1072 | dev = port->dev; | ||
1073 | |||
1074 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1075 | |||
1076 | /* allocate + initialize per filehandle data */ | ||
1077 | fh = kzalloc(sizeof(*fh), GFP_KERNEL); | ||
1078 | if (NULL == fh) | ||
1079 | return -ENOMEM; | ||
1080 | |||
1081 | file->private_data = fh; | ||
1082 | fh->port = port; | ||
1083 | |||
1084 | return 0; | ||
1085 | } | ||
1086 | |||
1087 | static int fops_release(struct file *file) | ||
1088 | { | ||
1089 | struct saa7164_encoder_fh *fh = file->private_data; | ||
1090 | struct saa7164_port *port = fh->port; | ||
1091 | struct saa7164_dev *dev = port->dev; | ||
1092 | |||
1093 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1094 | |||
1095 | /* Shut device down on last close */ | ||
1096 | if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { | ||
1097 | if (atomic_dec_return(&port->v4l_reader_count) == 0) { | ||
1098 | /* stop mpeg capture then cancel buffers */ | ||
1099 | saa7164_encoder_stop_streaming(port); | ||
1100 | } | ||
1101 | } | ||
1102 | |||
1103 | file->private_data = NULL; | ||
1104 | kfree(fh); | ||
1105 | |||
1106 | return 0; | ||
1107 | } | ||
1108 | |||
1109 | struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port) | ||
1110 | { | ||
1111 | struct saa7164_user_buffer *ubuf = NULL; | ||
1112 | struct saa7164_dev *dev = port->dev; | ||
1113 | u32 crc; | ||
1114 | |||
1115 | mutex_lock(&port->dmaqueue_lock); | ||
1116 | if (!list_empty(&port->list_buf_used.list)) { | ||
1117 | ubuf = list_first_entry(&port->list_buf_used.list, | ||
1118 | struct saa7164_user_buffer, list); | ||
1119 | |||
1120 | if (crc_checking) { | ||
1121 | crc = crc32(0, ubuf->data, ubuf->actual_size); | ||
1122 | if (crc != ubuf->crc) { | ||
1123 | printk(KERN_ERR | ||
1124 | "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", | ||
1125 | __func__, | ||
1126 | ubuf, ubuf->crc, crc); | ||
1127 | } | ||
1128 | } | ||
1129 | |||
1130 | } | ||
1131 | mutex_unlock(&port->dmaqueue_lock); | ||
1132 | |||
1133 | dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf); | ||
1134 | |||
1135 | return ubuf; | ||
1136 | } | ||
1137 | |||
1138 | static ssize_t fops_read(struct file *file, char __user *buffer, | ||
1139 | size_t count, loff_t *pos) | ||
1140 | { | ||
1141 | struct saa7164_encoder_fh *fh = file->private_data; | ||
1142 | struct saa7164_port *port = fh->port; | ||
1143 | struct saa7164_user_buffer *ubuf = NULL; | ||
1144 | struct saa7164_dev *dev = port->dev; | ||
1145 | int ret = 0; | ||
1146 | int rem, cnt; | ||
1147 | u8 *p; | ||
1148 | |||
1149 | port->last_read_msecs_diff = port->last_read_msecs; | ||
1150 | port->last_read_msecs = jiffies_to_msecs(jiffies); | ||
1151 | port->last_read_msecs_diff = port->last_read_msecs - | ||
1152 | port->last_read_msecs_diff; | ||
1153 | |||
1154 | saa7164_histogram_update(&port->read_interval, | ||
1155 | port->last_read_msecs_diff); | ||
1156 | |||
1157 | if (*pos) { | ||
1158 | printk(KERN_ERR "%s() ESPIPE\n", __func__); | ||
1159 | return -ESPIPE; | ||
1160 | } | ||
1161 | |||
1162 | if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { | ||
1163 | if (atomic_inc_return(&port->v4l_reader_count) == 1) { | ||
1164 | |||
1165 | if (saa7164_encoder_initialize(port) < 0) { | ||
1166 | printk(KERN_ERR "%s() EINVAL\n", __func__); | ||
1167 | return -EINVAL; | ||
1168 | } | ||
1169 | |||
1170 | saa7164_encoder_start_streaming(port); | ||
1171 | msleep(200); | ||
1172 | } | ||
1173 | } | ||
1174 | |||
1175 | /* blocking wait for buffer */ | ||
1176 | if ((file->f_flags & O_NONBLOCK) == 0) { | ||
1177 | if (wait_event_interruptible(port->wait_read, | ||
1178 | saa7164_enc_next_buf(port))) { | ||
1179 | printk(KERN_ERR "%s() ERESTARTSYS\n", __func__); | ||
1180 | return -ERESTARTSYS; | ||
1181 | } | ||
1182 | } | ||
1183 | |||
1184 | /* Pull the first buffer from the used list */ | ||
1185 | ubuf = saa7164_enc_next_buf(port); | ||
1186 | |||
1187 | while ((count > 0) && ubuf) { | ||
1188 | |||
1189 | /* set remaining bytes to copy */ | ||
1190 | rem = ubuf->actual_size - ubuf->pos; | ||
1191 | cnt = rem > count ? count : rem; | ||
1192 | |||
1193 | p = ubuf->data + ubuf->pos; | ||
1194 | |||
1195 | dprintk(DBGLVL_ENC, | ||
1196 | "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n", | ||
1197 | __func__, (int)count, cnt, rem, ubuf, ubuf->pos); | ||
1198 | |||
1199 | if (copy_to_user(buffer, p, cnt)) { | ||
1200 | printk(KERN_ERR "%s() copy_to_user failed\n", __func__); | ||
1201 | if (!ret) { | ||
1202 | printk(KERN_ERR "%s() EFAULT\n", __func__); | ||
1203 | ret = -EFAULT; | ||
1204 | } | ||
1205 | goto err; | ||
1206 | } | ||
1207 | |||
1208 | ubuf->pos += cnt; | ||
1209 | count -= cnt; | ||
1210 | buffer += cnt; | ||
1211 | ret += cnt; | ||
1212 | |||
1213 | if (ubuf->pos > ubuf->actual_size) | ||
1214 | printk(KERN_ERR "read() pos > actual, huh?\n"); | ||
1215 | |||
1216 | if (ubuf->pos == ubuf->actual_size) { | ||
1217 | |||
1218 | /* finished with current buffer, take next buffer */ | ||
1219 | |||
1220 | /* Requeue the buffer on the free list */ | ||
1221 | ubuf->pos = 0; | ||
1222 | |||
1223 | mutex_lock(&port->dmaqueue_lock); | ||
1224 | list_move_tail(&ubuf->list, &port->list_buf_free.list); | ||
1225 | mutex_unlock(&port->dmaqueue_lock); | ||
1226 | |||
1227 | /* Dequeue next */ | ||
1228 | if ((file->f_flags & O_NONBLOCK) == 0) { | ||
1229 | if (wait_event_interruptible(port->wait_read, | ||
1230 | saa7164_enc_next_buf(port))) { | ||
1231 | break; | ||
1232 | } | ||
1233 | } | ||
1234 | ubuf = saa7164_enc_next_buf(port); | ||
1235 | } | ||
1236 | } | ||
1237 | err: | ||
1238 | if (!ret && !ubuf) | ||
1239 | ret = -EAGAIN; | ||
1240 | |||
1241 | return ret; | ||
1242 | } | ||
1243 | |||
1244 | static unsigned int fops_poll(struct file *file, poll_table *wait) | ||
1245 | { | ||
1246 | struct saa7164_encoder_fh *fh = | ||
1247 | (struct saa7164_encoder_fh *)file->private_data; | ||
1248 | struct saa7164_port *port = fh->port; | ||
1249 | unsigned int mask = 0; | ||
1250 | |||
1251 | port->last_poll_msecs_diff = port->last_poll_msecs; | ||
1252 | port->last_poll_msecs = jiffies_to_msecs(jiffies); | ||
1253 | port->last_poll_msecs_diff = port->last_poll_msecs - | ||
1254 | port->last_poll_msecs_diff; | ||
1255 | |||
1256 | saa7164_histogram_update(&port->poll_interval, | ||
1257 | port->last_poll_msecs_diff); | ||
1258 | |||
1259 | if (!video_is_registered(port->v4l_device)) | ||
1260 | return -EIO; | ||
1261 | |||
1262 | if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { | ||
1263 | if (atomic_inc_return(&port->v4l_reader_count) == 1) { | ||
1264 | if (saa7164_encoder_initialize(port) < 0) | ||
1265 | return -EINVAL; | ||
1266 | saa7164_encoder_start_streaming(port); | ||
1267 | msleep(200); | ||
1268 | } | ||
1269 | } | ||
1270 | |||
1271 | /* blocking wait for buffer */ | ||
1272 | if ((file->f_flags & O_NONBLOCK) == 0) { | ||
1273 | if (wait_event_interruptible(port->wait_read, | ||
1274 | saa7164_enc_next_buf(port))) { | ||
1275 | return -ERESTARTSYS; | ||
1276 | } | ||
1277 | } | ||
1278 | |||
1279 | /* Pull the first buffer from the used list */ | ||
1280 | if (!list_empty(&port->list_buf_used.list)) | ||
1281 | mask |= POLLIN | POLLRDNORM; | ||
1282 | |||
1283 | return mask; | ||
1284 | } | ||
1285 | |||
1286 | static const struct v4l2_file_operations mpeg_fops = { | ||
1287 | .owner = THIS_MODULE, | ||
1288 | .open = fops_open, | ||
1289 | .release = fops_release, | ||
1290 | .read = fops_read, | ||
1291 | .poll = fops_poll, | ||
1292 | .unlocked_ioctl = video_ioctl2, | ||
1293 | }; | ||
1294 | |||
1295 | int saa7164_g_chip_ident(struct file *file, void *fh, | ||
1296 | struct v4l2_dbg_chip_ident *chip) | ||
1297 | { | ||
1298 | struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; | ||
1299 | struct saa7164_dev *dev = port->dev; | ||
1300 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1301 | |||
1302 | return 0; | ||
1303 | } | ||
1304 | |||
1305 | int saa7164_g_register(struct file *file, void *fh, | ||
1306 | struct v4l2_dbg_register *reg) | ||
1307 | { | ||
1308 | struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; | ||
1309 | struct saa7164_dev *dev = port->dev; | ||
1310 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1311 | |||
1312 | if (!capable(CAP_SYS_ADMIN)) | ||
1313 | return -EPERM; | ||
1314 | |||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1318 | int saa7164_s_register(struct file *file, void *fh, | ||
1319 | struct v4l2_dbg_register *reg) | ||
1320 | { | ||
1321 | struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; | ||
1322 | struct saa7164_dev *dev = port->dev; | ||
1323 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1324 | |||
1325 | if (!capable(CAP_SYS_ADMIN)) | ||
1326 | return -EPERM; | ||
1327 | |||
1328 | return 0; | ||
1329 | } | ||
1330 | |||
1331 | static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { | ||
1332 | .vidioc_s_std = vidioc_s_std, | ||
1333 | .vidioc_enum_input = vidioc_enum_input, | ||
1334 | .vidioc_g_input = vidioc_g_input, | ||
1335 | .vidioc_s_input = vidioc_s_input, | ||
1336 | .vidioc_g_tuner = vidioc_g_tuner, | ||
1337 | .vidioc_s_tuner = vidioc_s_tuner, | ||
1338 | .vidioc_g_frequency = vidioc_g_frequency, | ||
1339 | .vidioc_s_frequency = vidioc_s_frequency, | ||
1340 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
1341 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
1342 | .vidioc_querycap = vidioc_querycap, | ||
1343 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | ||
1344 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, | ||
1345 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | ||
1346 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | ||
1347 | .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, | ||
1348 | .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, | ||
1349 | .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, | ||
1350 | .vidioc_log_status = vidioc_log_status, | ||
1351 | .vidioc_queryctrl = vidioc_queryctrl, | ||
1352 | .vidioc_g_chip_ident = saa7164_g_chip_ident, | ||
1353 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1354 | .vidioc_g_register = saa7164_g_register, | ||
1355 | .vidioc_s_register = saa7164_s_register, | ||
1356 | #endif | ||
1357 | }; | ||
1358 | |||
1359 | static struct video_device saa7164_mpeg_template = { | ||
1360 | .name = "saa7164", | ||
1361 | .fops = &mpeg_fops, | ||
1362 | .ioctl_ops = &mpeg_ioctl_ops, | ||
1363 | .minor = -1, | ||
1364 | .tvnorms = SAA7164_NORMS, | ||
1365 | .current_norm = V4L2_STD_NTSC_M, | ||
1366 | }; | ||
1367 | |||
1368 | static struct video_device *saa7164_encoder_alloc( | ||
1369 | struct saa7164_port *port, | ||
1370 | struct pci_dev *pci, | ||
1371 | struct video_device *template, | ||
1372 | char *type) | ||
1373 | { | ||
1374 | struct video_device *vfd; | ||
1375 | struct saa7164_dev *dev = port->dev; | ||
1376 | |||
1377 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1378 | |||
1379 | vfd = video_device_alloc(); | ||
1380 | if (NULL == vfd) | ||
1381 | return NULL; | ||
1382 | |||
1383 | *vfd = *template; | ||
1384 | snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, | ||
1385 | type, saa7164_boards[dev->board].name); | ||
1386 | |||
1387 | vfd->parent = &pci->dev; | ||
1388 | vfd->release = video_device_release; | ||
1389 | return vfd; | ||
1390 | } | ||
1391 | |||
1392 | int saa7164_encoder_register(struct saa7164_port *port) | ||
1393 | { | ||
1394 | struct saa7164_dev *dev = port->dev; | ||
1395 | int result = -ENODEV; | ||
1396 | |||
1397 | dprintk(DBGLVL_ENC, "%s()\n", __func__); | ||
1398 | |||
1399 | if (port->type != SAA7164_MPEG_ENCODER) | ||
1400 | BUG(); | ||
1401 | |||
1402 | /* Sanity check that the PCI configuration space is active */ | ||
1403 | if (port->hwcfg.BARLocation == 0) { | ||
1404 | printk(KERN_ERR "%s() failed " | ||
1405 | "(errno = %d), NO PCI configuration\n", | ||
1406 | __func__, result); | ||
1407 | result = -ENOMEM; | ||
1408 | goto failed; | ||
1409 | } | ||
1410 | |||
1411 | /* Establish encoder defaults here */ | ||
1412 | /* Set default TV standard */ | ||
1413 | port->encodernorm = saa7164_tvnorms[0]; | ||
1414 | port->width = 720; | ||
1415 | port->mux_input = 1; /* Composite */ | ||
1416 | port->video_format = EU_VIDEO_FORMAT_MPEG_2; | ||
1417 | port->audio_format = 0; | ||
1418 | port->video_resolution = 0; | ||
1419 | port->ctl_brightness = 127; | ||
1420 | port->ctl_contrast = 66; | ||
1421 | port->ctl_hue = 128; | ||
1422 | port->ctl_saturation = 62; | ||
1423 | port->ctl_sharpness = 8; | ||
1424 | port->encoder_params.bitrate = ENCODER_DEF_BITRATE; | ||
1425 | port->encoder_params.bitrate_peak = ENCODER_DEF_BITRATE; | ||
1426 | port->encoder_params.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; | ||
1427 | port->encoder_params.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS; | ||
1428 | port->encoder_params.ctl_mute = 0; | ||
1429 | port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3; | ||
1430 | port->encoder_params.refdist = 1; | ||
1431 | port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE; | ||
1432 | |||
1433 | if (port->encodernorm.id & V4L2_STD_525_60) | ||
1434 | port->height = 480; | ||
1435 | else | ||
1436 | port->height = 576; | ||
1437 | |||
1438 | /* Allocate and register the video device node */ | ||
1439 | port->v4l_device = saa7164_encoder_alloc(port, | ||
1440 | dev->pci, &saa7164_mpeg_template, "mpeg"); | ||
1441 | |||
1442 | if (!port->v4l_device) { | ||
1443 | printk(KERN_INFO "%s: can't allocate mpeg device\n", | ||
1444 | dev->name); | ||
1445 | result = -ENOMEM; | ||
1446 | goto failed; | ||
1447 | } | ||
1448 | |||
1449 | video_set_drvdata(port->v4l_device, port); | ||
1450 | result = video_register_device(port->v4l_device, | ||
1451 | VFL_TYPE_GRABBER, -1); | ||
1452 | if (result < 0) { | ||
1453 | printk(KERN_INFO "%s: can't register mpeg device\n", | ||
1454 | dev->name); | ||
1455 | /* TODO: We're going to leak here if we don't dealloc | ||
1456 | The buffers above. The unreg function can't deal wit it. | ||
1457 | */ | ||
1458 | goto failed; | ||
1459 | } | ||
1460 | |||
1461 | printk(KERN_INFO "%s: registered device video%d [mpeg]\n", | ||
1462 | dev->name, port->v4l_device->num); | ||
1463 | |||
1464 | /* Configure the hardware defaults */ | ||
1465 | saa7164_api_set_videomux(port); | ||
1466 | saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL); | ||
1467 | saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); | ||
1468 | saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); | ||
1469 | saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL); | ||
1470 | saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); | ||
1471 | saa7164_api_audio_mute(port, 0); | ||
1472 | saa7164_api_set_audio_volume(port, 20); | ||
1473 | saa7164_api_set_aspect_ratio(port); | ||
1474 | |||
1475 | /* Disable audio standard detection, it's buggy */ | ||
1476 | saa7164_api_set_audio_detection(port, 0); | ||
1477 | |||
1478 | saa7164_api_set_encoder(port); | ||
1479 | saa7164_api_get_encoder(port); | ||
1480 | |||
1481 | result = 0; | ||
1482 | failed: | ||
1483 | return result; | ||
1484 | } | ||
1485 | |||
1486 | void saa7164_encoder_unregister(struct saa7164_port *port) | ||
1487 | { | ||
1488 | struct saa7164_dev *dev = port->dev; | ||
1489 | |||
1490 | dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); | ||
1491 | |||
1492 | if (port->type != SAA7164_MPEG_ENCODER) | ||
1493 | BUG(); | ||
1494 | |||
1495 | if (port->v4l_device) { | ||
1496 | if (port->v4l_device->minor != -1) | ||
1497 | video_unregister_device(port->v4l_device); | ||
1498 | else | ||
1499 | video_device_release(port->v4l_device); | ||
1500 | |||
1501 | port->v4l_device = NULL; | ||
1502 | } | ||
1503 | |||
1504 | dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); | ||
1505 | } | ||
1506 | |||
diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c new file mode 100644 index 00000000000..a266bf0169e --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-fw.c | |||
@@ -0,0 +1,613 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | #include <linux/slab.h> | ||
24 | |||
25 | #include "saa7164.h" | ||
26 | |||
27 | #define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw" | ||
28 | #define SAA7164_REV2_FIRMWARE_SIZE 4019072 | ||
29 | |||
30 | #define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw" | ||
31 | #define SAA7164_REV3_FIRMWARE_SIZE 4019072 | ||
32 | |||
33 | struct fw_header { | ||
34 | u32 firmwaresize; | ||
35 | u32 bslsize; | ||
36 | u32 reserved; | ||
37 | u32 version; | ||
38 | }; | ||
39 | |||
40 | int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg) | ||
41 | { | ||
42 | u32 timeout = SAA_DEVICE_TIMEOUT; | ||
43 | while ((saa7164_readl(reg) & 0x01) == 0) { | ||
44 | timeout -= 10; | ||
45 | if (timeout == 0) { | ||
46 | printk(KERN_ERR "%s() timeout (no d/l ack)\n", | ||
47 | __func__); | ||
48 | return -EBUSY; | ||
49 | } | ||
50 | msleep(100); | ||
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 -= 10; | ||
61 | if (timeout == 0) { | ||
62 | printk(KERN_ERR "%s() timeout (no d/l clr)\n", | ||
63 | __func__); | ||
64 | return -EBUSY; | ||
65 | } | ||
66 | msleep(100); | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | /* TODO: move dlflags into dev-> and change to write/readl/b */ | ||
73 | /* TODO: Excessive levels of debug */ | ||
74 | int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize, | ||
75 | u32 dlflags, u8 *dst, u32 dstsize) | ||
76 | { | ||
77 | u32 reg, timeout, offset; | ||
78 | u8 *srcbuf = NULL; | ||
79 | int ret; | ||
80 | |||
81 | u32 dlflag = dlflags; | ||
82 | u32 dlflag_ack = dlflag + 4; | ||
83 | u32 drflag = dlflag_ack + 4; | ||
84 | u32 drflag_ack = drflag + 4; | ||
85 | u32 bleflag = drflag_ack + 4; | ||
86 | |||
87 | dprintk(DBGLVL_FW, | ||
88 | "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n", | ||
89 | __func__, src, srcsize, dlflags, dst, dstsize); | ||
90 | |||
91 | if ((src == NULL) || (dst == NULL)) { | ||
92 | ret = -EIO; | ||
93 | goto out; | ||
94 | } | ||
95 | |||
96 | srcbuf = kzalloc(4 * 1048576, GFP_KERNEL); | ||
97 | if (NULL == srcbuf) { | ||
98 | ret = -ENOMEM; | ||
99 | goto out; | ||
100 | } | ||
101 | |||
102 | if (srcsize > (4*1048576)) { | ||
103 | ret = -ENOMEM; | ||
104 | goto out; | ||
105 | } | ||
106 | |||
107 | memcpy(srcbuf, src, srcsize); | ||
108 | |||
109 | dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag); | ||
110 | dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack); | ||
111 | dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag); | ||
112 | dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack); | ||
113 | dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag); | ||
114 | |||
115 | reg = saa7164_readl(dlflag); | ||
116 | dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg); | ||
117 | if (reg == 1) | ||
118 | dprintk(DBGLVL_FW, | ||
119 | "%s() Download flag already set, please reboot\n", | ||
120 | __func__); | ||
121 | |||
122 | /* Indicate download start */ | ||
123 | saa7164_writel(dlflag, 1); | ||
124 | ret = saa7164_dl_wait_ack(dev, dlflag_ack); | ||
125 | if (ret < 0) | ||
126 | goto out; | ||
127 | |||
128 | /* Ack download start, then wait for wait */ | ||
129 | saa7164_writel(dlflag, 0); | ||
130 | ret = saa7164_dl_wait_clr(dev, dlflag_ack); | ||
131 | if (ret < 0) | ||
132 | goto out; | ||
133 | |||
134 | /* Deal with the raw firmware, in the appropriate chunk size */ | ||
135 | for (offset = 0; srcsize > dstsize; | ||
136 | srcsize -= dstsize, offset += dstsize) { | ||
137 | |||
138 | dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize); | ||
139 | memcpy(dst, srcbuf + offset, dstsize); | ||
140 | |||
141 | /* Flag the data as ready */ | ||
142 | saa7164_writel(drflag, 1); | ||
143 | ret = saa7164_dl_wait_ack(dev, drflag_ack); | ||
144 | if (ret < 0) | ||
145 | goto out; | ||
146 | |||
147 | /* Wait for indication data was received */ | ||
148 | saa7164_writel(drflag, 0); | ||
149 | ret = saa7164_dl_wait_clr(dev, drflag_ack); | ||
150 | if (ret < 0) | ||
151 | goto out; | ||
152 | |||
153 | } | ||
154 | |||
155 | dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize); | ||
156 | /* Write last block to the device */ | ||
157 | memcpy(dst, srcbuf+offset, srcsize); | ||
158 | |||
159 | /* Flag the data as ready */ | ||
160 | saa7164_writel(drflag, 1); | ||
161 | ret = saa7164_dl_wait_ack(dev, drflag_ack); | ||
162 | if (ret < 0) | ||
163 | goto out; | ||
164 | |||
165 | saa7164_writel(drflag, 0); | ||
166 | timeout = 0; | ||
167 | while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) { | ||
168 | if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) { | ||
169 | printk(KERN_ERR "%s() image corrupt\n", __func__); | ||
170 | ret = -EBUSY; | ||
171 | goto out; | ||
172 | } | ||
173 | |||
174 | if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) { | ||
175 | printk(KERN_ERR "%s() device memory corrupt\n", | ||
176 | __func__); | ||
177 | ret = -EBUSY; | ||
178 | goto out; | ||
179 | } | ||
180 | |||
181 | msleep(10); /* Checkpatch throws a < 20ms warning */ | ||
182 | if (timeout++ > 60) | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__); | ||
187 | |||
188 | ret = saa7164_dl_wait_clr(dev, drflag_ack); | ||
189 | if (ret < 0) | ||
190 | goto out; | ||
191 | |||
192 | printk(KERN_INFO "%s() Image booted successfully.\n", __func__); | ||
193 | ret = 0; | ||
194 | |||
195 | out: | ||
196 | kfree(srcbuf); | ||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | /* TODO: Excessive debug */ | ||
201 | /* Load the firmware. Optionally it can be in ROM or newer versions | ||
202 | * can be on disk, saving the expense of the ROM hardware. */ | ||
203 | int saa7164_downloadfirmware(struct saa7164_dev *dev) | ||
204 | { | ||
205 | /* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */ | ||
206 | u32 tmp, filesize, version, err_flags, first_timeout, fwlength; | ||
207 | u32 second_timeout, updatebootloader = 1, bootloadersize = 0; | ||
208 | const struct firmware *fw = NULL; | ||
209 | struct fw_header *hdr, *boothdr = NULL, *fwhdr; | ||
210 | u32 bootloaderversion = 0, fwloadersize; | ||
211 | u8 *bootloaderoffset = NULL, *fwloaderoffset; | ||
212 | char *fwname; | ||
213 | int ret; | ||
214 | |||
215 | dprintk(DBGLVL_FW, "%s()\n", __func__); | ||
216 | |||
217 | if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) { | ||
218 | fwname = SAA7164_REV2_FIRMWARE; | ||
219 | fwlength = SAA7164_REV2_FIRMWARE_SIZE; | ||
220 | } else { | ||
221 | fwname = SAA7164_REV3_FIRMWARE; | ||
222 | fwlength = SAA7164_REV3_FIRMWARE_SIZE; | ||
223 | } | ||
224 | |||
225 | version = saa7164_getcurrentfirmwareversion(dev); | ||
226 | |||
227 | if (version == 0x00) { | ||
228 | |||
229 | second_timeout = 100; | ||
230 | first_timeout = 100; | ||
231 | err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); | ||
232 | dprintk(DBGLVL_FW, "%s() err_flags = %x\n", | ||
233 | __func__, err_flags); | ||
234 | |||
235 | while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { | ||
236 | dprintk(DBGLVL_FW, "%s() err_flags = %x\n", | ||
237 | __func__, err_flags); | ||
238 | msleep(10); /* Checkpatch throws a < 20ms warning */ | ||
239 | |||
240 | if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { | ||
241 | printk(KERN_ERR "%s() firmware corrupt\n", | ||
242 | __func__); | ||
243 | break; | ||
244 | } | ||
245 | if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { | ||
246 | printk(KERN_ERR "%s() device memory corrupt\n", | ||
247 | __func__); | ||
248 | break; | ||
249 | } | ||
250 | if (err_flags & SAA_DEVICE_NO_IMAGE) { | ||
251 | printk(KERN_ERR "%s() no first image\n", | ||
252 | __func__); | ||
253 | break; | ||
254 | } | ||
255 | if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { | ||
256 | first_timeout -= 10; | ||
257 | if (first_timeout == 0) { | ||
258 | printk(KERN_ERR | ||
259 | "%s() no first image\n", | ||
260 | __func__); | ||
261 | break; | ||
262 | } | ||
263 | } else if (err_flags & SAA_DEVICE_IMAGE_LOADING) { | ||
264 | second_timeout -= 10; | ||
265 | if (second_timeout == 0) { | ||
266 | printk(KERN_ERR | ||
267 | "%s() FW load time exceeded\n", | ||
268 | __func__); | ||
269 | break; | ||
270 | } | ||
271 | } else { | ||
272 | second_timeout -= 10; | ||
273 | if (second_timeout == 0) { | ||
274 | printk(KERN_ERR | ||
275 | "%s() Unknown bootloader flags 0x%x\n", | ||
276 | __func__, err_flags); | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); | ||
282 | } /* While != Booting */ | ||
283 | |||
284 | if (err_flags == SAA_DEVICE_IMAGE_BOOTING) { | ||
285 | dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n", | ||
286 | __func__); | ||
287 | first_timeout = SAA_DEVICE_TIMEOUT; | ||
288 | second_timeout = 60 * SAA_DEVICE_TIMEOUT; | ||
289 | second_timeout = 100; | ||
290 | |||
291 | err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); | ||
292 | dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", | ||
293 | __func__, err_flags); | ||
294 | while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { | ||
295 | dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", | ||
296 | __func__, err_flags); | ||
297 | msleep(10); /* Checkpatch throws a < 20ms warning */ | ||
298 | |||
299 | if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { | ||
300 | printk(KERN_ERR | ||
301 | "%s() firmware corrupt\n", | ||
302 | __func__); | ||
303 | break; | ||
304 | } | ||
305 | if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { | ||
306 | printk(KERN_ERR | ||
307 | "%s() device memory corrupt\n", | ||
308 | __func__); | ||
309 | break; | ||
310 | } | ||
311 | if (err_flags & SAA_DEVICE_NO_IMAGE) { | ||
312 | printk(KERN_ERR "%s() no first image\n", | ||
313 | __func__); | ||
314 | break; | ||
315 | } | ||
316 | if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { | ||
317 | first_timeout -= 10; | ||
318 | if (first_timeout == 0) { | ||
319 | printk(KERN_ERR | ||
320 | "%s() no second image\n", | ||
321 | __func__); | ||
322 | break; | ||
323 | } | ||
324 | } else if (err_flags & | ||
325 | SAA_DEVICE_IMAGE_LOADING) { | ||
326 | second_timeout -= 10; | ||
327 | if (second_timeout == 0) { | ||
328 | printk(KERN_ERR | ||
329 | "%s() FW load time exceeded\n", | ||
330 | __func__); | ||
331 | break; | ||
332 | } | ||
333 | } else { | ||
334 | second_timeout -= 10; | ||
335 | if (second_timeout == 0) { | ||
336 | printk(KERN_ERR | ||
337 | "%s() Unknown bootloader flags 0x%x\n", | ||
338 | __func__, err_flags); | ||
339 | break; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | err_flags = | ||
344 | saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); | ||
345 | } /* err_flags != SAA_DEVICE_IMAGE_BOOTING */ | ||
346 | |||
347 | dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n", | ||
348 | __func__, | ||
349 | saa7164_readl(SAA_BOOTLOADERERROR_FLAGS), | ||
350 | saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS)); | ||
351 | |||
352 | } /* err_flags == SAA_DEVICE_IMAGE_BOOTING */ | ||
353 | |||
354 | /* It's possible for both firmwares to have booted, | ||
355 | * but that doesn't mean they've finished booting yet. | ||
356 | */ | ||
357 | if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == | ||
358 | SAA_DEVICE_IMAGE_BOOTING) && | ||
359 | (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == | ||
360 | SAA_DEVICE_IMAGE_BOOTING)) { | ||
361 | |||
362 | |||
363 | dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n", | ||
364 | __func__); | ||
365 | |||
366 | first_timeout = SAA_DEVICE_TIMEOUT; | ||
367 | while (first_timeout) { | ||
368 | msleep(10); /* Checkpatch throws a < 20ms warning */ | ||
369 | |||
370 | version = | ||
371 | saa7164_getcurrentfirmwareversion(dev); | ||
372 | if (version) { | ||
373 | dprintk(DBGLVL_FW, | ||
374 | "%s() All f/w loaded successfully\n", | ||
375 | __func__); | ||
376 | break; | ||
377 | } else { | ||
378 | first_timeout -= 10; | ||
379 | if (first_timeout == 0) { | ||
380 | printk(KERN_ERR | ||
381 | "%s() FW did not boot\n", | ||
382 | __func__); | ||
383 | break; | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | version = saa7164_getcurrentfirmwareversion(dev); | ||
389 | } /* version == 0 */ | ||
390 | |||
391 | /* Has the firmware really booted? */ | ||
392 | if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == | ||
393 | SAA_DEVICE_IMAGE_BOOTING) && | ||
394 | (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == | ||
395 | SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) { | ||
396 | |||
397 | printk(KERN_ERR | ||
398 | "%s() The firmware hung, probably bad firmware\n", | ||
399 | __func__); | ||
400 | |||
401 | /* Tell the second stage loader we have a deadlock */ | ||
402 | saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET, | ||
403 | SAA_DEVICE_DEADLOCK_DETECTED); | ||
404 | |||
405 | saa7164_getfirmwarestatus(dev); | ||
406 | |||
407 | return -ENOMEM; | ||
408 | } | ||
409 | |||
410 | dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n", | ||
411 | (version & 0x0000fc00) >> 10, | ||
412 | (version & 0x000003e0) >> 5, | ||
413 | (version & 0x0000001f), | ||
414 | (version & 0xffff0000) >> 16); | ||
415 | |||
416 | /* Load the firmwware from the disk if required */ | ||
417 | if (version == 0) { | ||
418 | |||
419 | printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n", | ||
420 | __func__, fwname); | ||
421 | |||
422 | ret = request_firmware(&fw, fwname, &dev->pci->dev); | ||
423 | if (ret) { | ||
424 | printk(KERN_ERR "%s() Upload failed. " | ||
425 | "(file not found?)\n", __func__); | ||
426 | return -ENOMEM; | ||
427 | } | ||
428 | |||
429 | printk(KERN_INFO "%s() firmware read %Zu bytes.\n", | ||
430 | __func__, fw->size); | ||
431 | |||
432 | if (fw->size != fwlength) { | ||
433 | printk(KERN_ERR "xc5000: firmware incorrect size\n"); | ||
434 | ret = -ENOMEM; | ||
435 | goto out; | ||
436 | } | ||
437 | |||
438 | printk(KERN_INFO "%s() firmware loaded.\n", __func__); | ||
439 | |||
440 | hdr = (struct fw_header *)fw->data; | ||
441 | printk(KERN_INFO "Firmware file header part 1:\n"); | ||
442 | printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize); | ||
443 | printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize); | ||
444 | printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved); | ||
445 | printk(KERN_INFO " .Version = 0x%x\n", hdr->version); | ||
446 | |||
447 | /* Retrieve bootloader if reqd */ | ||
448 | if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) | ||
449 | /* Second bootloader in the firmware file */ | ||
450 | filesize = hdr->reserved * 16; | ||
451 | else | ||
452 | filesize = (hdr->firmwaresize + hdr->bslsize) * | ||
453 | 16 + sizeof(struct fw_header); | ||
454 | |||
455 | printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n", | ||
456 | __func__, filesize); | ||
457 | |||
458 | /* Get bootloader (if reqd) and firmware header */ | ||
459 | if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { | ||
460 | /* Second boot loader is required */ | ||
461 | |||
462 | /* Get the loader header */ | ||
463 | boothdr = (struct fw_header *)(fw->data + | ||
464 | sizeof(struct fw_header)); | ||
465 | |||
466 | bootloaderversion = | ||
467 | saa7164_readl(SAA_DEVICE_2ND_VERSION); | ||
468 | dprintk(DBGLVL_FW, "Onboard BootLoader:\n"); | ||
469 | dprintk(DBGLVL_FW, "->Flag 0x%x\n", | ||
470 | saa7164_readl(SAA_BOOTLOADERERROR_FLAGS)); | ||
471 | dprintk(DBGLVL_FW, "->Ack 0x%x\n", | ||
472 | saa7164_readl(SAA_DATAREADY_FLAG_ACK)); | ||
473 | dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version); | ||
474 | dprintk(DBGLVL_FW, "->Loader Version 0x%x\n", | ||
475 | bootloaderversion); | ||
476 | |||
477 | if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == | ||
478 | 0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK) | ||
479 | == 0x00) && (version == 0x00)) { | ||
480 | |||
481 | dprintk(DBGLVL_FW, "BootLoader version in " | ||
482 | "rom %d.%d.%d.%d\n", | ||
483 | (bootloaderversion & 0x0000fc00) >> 10, | ||
484 | (bootloaderversion & 0x000003e0) >> 5, | ||
485 | (bootloaderversion & 0x0000001f), | ||
486 | (bootloaderversion & 0xffff0000) >> 16 | ||
487 | ); | ||
488 | dprintk(DBGLVL_FW, "BootLoader version " | ||
489 | "in file %d.%d.%d.%d\n", | ||
490 | (boothdr->version & 0x0000fc00) >> 10, | ||
491 | (boothdr->version & 0x000003e0) >> 5, | ||
492 | (boothdr->version & 0x0000001f), | ||
493 | (boothdr->version & 0xffff0000) >> 16 | ||
494 | ); | ||
495 | |||
496 | if (bootloaderversion == boothdr->version) | ||
497 | updatebootloader = 0; | ||
498 | } | ||
499 | |||
500 | /* Calculate offset to firmware header */ | ||
501 | tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 + | ||
502 | (sizeof(struct fw_header) + | ||
503 | sizeof(struct fw_header)); | ||
504 | |||
505 | fwhdr = (struct fw_header *)(fw->data+tmp); | ||
506 | } else { | ||
507 | /* No second boot loader */ | ||
508 | fwhdr = hdr; | ||
509 | } | ||
510 | |||
511 | dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n", | ||
512 | (fwhdr->version & 0x0000fc00) >> 10, | ||
513 | (fwhdr->version & 0x000003e0) >> 5, | ||
514 | (fwhdr->version & 0x0000001f), | ||
515 | (fwhdr->version & 0xffff0000) >> 16 | ||
516 | ); | ||
517 | |||
518 | if (version == fwhdr->version) { | ||
519 | /* No download, firmware already on board */ | ||
520 | ret = 0; | ||
521 | goto out; | ||
522 | } | ||
523 | |||
524 | if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { | ||
525 | if (updatebootloader) { | ||
526 | /* Get ready to upload the bootloader */ | ||
527 | bootloadersize = (boothdr->firmwaresize + | ||
528 | boothdr->bslsize) * 16 + | ||
529 | sizeof(struct fw_header); | ||
530 | |||
531 | bootloaderoffset = (u8 *)(fw->data + | ||
532 | sizeof(struct fw_header)); | ||
533 | |||
534 | dprintk(DBGLVL_FW, "bootloader d/l starts.\n"); | ||
535 | printk(KERN_INFO "%s() FirmwareSize = 0x%x\n", | ||
536 | __func__, boothdr->firmwaresize); | ||
537 | printk(KERN_INFO "%s() BSLSize = 0x%x\n", | ||
538 | __func__, boothdr->bslsize); | ||
539 | printk(KERN_INFO "%s() Reserved = 0x%x\n", | ||
540 | __func__, boothdr->reserved); | ||
541 | printk(KERN_INFO "%s() Version = 0x%x\n", | ||
542 | __func__, boothdr->version); | ||
543 | ret = saa7164_downloadimage( | ||
544 | dev, | ||
545 | bootloaderoffset, | ||
546 | bootloadersize, | ||
547 | SAA_DOWNLOAD_FLAGS, | ||
548 | dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, | ||
549 | SAA_DEVICE_BUFFERBLOCKSIZE); | ||
550 | if (ret < 0) { | ||
551 | printk(KERN_ERR | ||
552 | "bootloader d/l has failed\n"); | ||
553 | goto out; | ||
554 | } | ||
555 | dprintk(DBGLVL_FW, | ||
556 | "bootloader download complete.\n"); | ||
557 | |||
558 | } | ||
559 | |||
560 | printk(KERN_ERR "starting firmware download(2)\n"); | ||
561 | bootloadersize = (boothdr->firmwaresize + | ||
562 | boothdr->bslsize) * 16 + | ||
563 | sizeof(struct fw_header); | ||
564 | |||
565 | bootloaderoffset = | ||
566 | (u8 *)(fw->data + sizeof(struct fw_header)); | ||
567 | |||
568 | fwloaderoffset = bootloaderoffset + bootloadersize; | ||
569 | |||
570 | /* TODO: fix this bounds overrun here with old f/ws */ | ||
571 | fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) * | ||
572 | 16 + sizeof(struct fw_header); | ||
573 | |||
574 | ret = saa7164_downloadimage( | ||
575 | dev, | ||
576 | fwloaderoffset, | ||
577 | fwloadersize, | ||
578 | SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET, | ||
579 | dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET, | ||
580 | SAA_DEVICE_2ND_BUFFERBLOCKSIZE); | ||
581 | if (ret < 0) { | ||
582 | printk(KERN_ERR "firmware download failed\n"); | ||
583 | goto out; | ||
584 | } | ||
585 | printk(KERN_ERR "firmware download complete.\n"); | ||
586 | |||
587 | } else { | ||
588 | |||
589 | /* No bootloader update reqd, download firmware only */ | ||
590 | printk(KERN_ERR "starting firmware download(3)\n"); | ||
591 | |||
592 | ret = saa7164_downloadimage( | ||
593 | dev, | ||
594 | (u8 *)fw->data, | ||
595 | fw->size, | ||
596 | SAA_DOWNLOAD_FLAGS, | ||
597 | dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, | ||
598 | SAA_DEVICE_BUFFERBLOCKSIZE); | ||
599 | if (ret < 0) { | ||
600 | printk(KERN_ERR "firmware download failed\n"); | ||
601 | goto out; | ||
602 | } | ||
603 | printk(KERN_ERR "firmware download complete.\n"); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | dev->firmwareloaded = 1; | ||
608 | ret = 0; | ||
609 | |||
610 | out: | ||
611 | release_firmware(fw); | ||
612 | return ret; | ||
613 | } | ||
diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/video/saa7164/saa7164-i2c.c new file mode 100644 index 00000000000..26148f76cba --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-i2c.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 <linux/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 | void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd, | ||
73 | void *arg) | ||
74 | { | ||
75 | if (bus->i2c_rc != 0) | ||
76 | return; | ||
77 | |||
78 | i2c_clients_command(&bus->i2c_adap, cmd, arg); | ||
79 | } | ||
80 | |||
81 | static u32 saa7164_functionality(struct i2c_adapter *adap) | ||
82 | { | ||
83 | return I2C_FUNC_I2C; | ||
84 | } | ||
85 | |||
86 | static struct i2c_algorithm saa7164_i2c_algo_template = { | ||
87 | .master_xfer = i2c_xfer, | ||
88 | .functionality = saa7164_functionality, | ||
89 | }; | ||
90 | |||
91 | /* ----------------------------------------------------------------------- */ | ||
92 | |||
93 | static struct i2c_adapter saa7164_i2c_adap_template = { | ||
94 | .name = "saa7164", | ||
95 | .owner = THIS_MODULE, | ||
96 | .algo = &saa7164_i2c_algo_template, | ||
97 | }; | ||
98 | |||
99 | static struct i2c_client saa7164_i2c_client_template = { | ||
100 | .name = "saa7164 internal", | ||
101 | }; | ||
102 | |||
103 | int saa7164_i2c_register(struct saa7164_i2c *bus) | ||
104 | { | ||
105 | struct saa7164_dev *dev = bus->dev; | ||
106 | |||
107 | dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr); | ||
108 | |||
109 | memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template, | ||
110 | sizeof(bus->i2c_adap)); | ||
111 | |||
112 | memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template, | ||
113 | sizeof(bus->i2c_algo)); | ||
114 | |||
115 | memcpy(&bus->i2c_client, &saa7164_i2c_client_template, | ||
116 | sizeof(bus->i2c_client)); | ||
117 | |||
118 | bus->i2c_adap.dev.parent = &dev->pci->dev; | ||
119 | |||
120 | strlcpy(bus->i2c_adap.name, bus->dev->name, | ||
121 | sizeof(bus->i2c_adap.name)); | ||
122 | |||
123 | bus->i2c_algo.data = bus; | ||
124 | bus->i2c_adap.algo_data = bus; | ||
125 | i2c_set_adapdata(&bus->i2c_adap, bus); | ||
126 | i2c_add_adapter(&bus->i2c_adap); | ||
127 | |||
128 | bus->i2c_client.adapter = &bus->i2c_adap; | ||
129 | |||
130 | if (0 != bus->i2c_rc) | ||
131 | printk(KERN_ERR "%s: i2c bus %d register FAILED\n", | ||
132 | dev->name, bus->nr); | ||
133 | |||
134 | return bus->i2c_rc; | ||
135 | } | ||
136 | |||
137 | int saa7164_i2c_unregister(struct saa7164_i2c *bus) | ||
138 | { | ||
139 | i2c_del_adapter(&bus->i2c_adap); | ||
140 | return 0; | ||
141 | } | ||
diff --git a/drivers/media/video/saa7164/saa7164-reg.h b/drivers/media/video/saa7164/saa7164-reg.h new file mode 100644 index 00000000000..2bbf81583d3 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-reg.h | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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_STATUS_CONTROL 0x08 | ||
64 | #define GET_FW_VERSION_CONTROL 0x09 | ||
65 | #define SET_DEBUG_LEVEL_CONTROL 0x0B | ||
66 | #define GET_DEBUG_DATA_CONTROL 0x0C | ||
67 | #define GET_PRODUCTION_INFO_CONTROL 0x0D | ||
68 | |||
69 | /* cmd defines */ | ||
70 | #define SAA_CMDFLAG_CONTINUE 0x10 | ||
71 | #define SAA_CMD_MAX_MSG_UNITS 256 | ||
72 | |||
73 | /* Some defines */ | ||
74 | #define SAA_BUS_TIMEOUT 50 | ||
75 | #define SAA_DEVICE_TIMEOUT 5000 | ||
76 | #define SAA_DEVICE_MAXREQUESTSIZE 256 | ||
77 | |||
78 | /* Register addresses */ | ||
79 | #define SAA_DEVICE_VERSION 0x30 | ||
80 | #define SAA_DOWNLOAD_FLAGS 0x34 | ||
81 | #define SAA_DOWNLOAD_FLAG 0x34 | ||
82 | #define SAA_DOWNLOAD_FLAG_ACK 0x38 | ||
83 | #define SAA_DATAREADY_FLAG 0x3C | ||
84 | #define SAA_DATAREADY_FLAG_ACK 0x40 | ||
85 | |||
86 | /* Boot loader register and bit definitions */ | ||
87 | #define SAA_BOOTLOADERERROR_FLAGS 0x44 | ||
88 | #define SAA_DEVICE_IMAGE_SEARCHING 0x01 | ||
89 | #define SAA_DEVICE_IMAGE_LOADING 0x02 | ||
90 | #define SAA_DEVICE_IMAGE_BOOTING 0x03 | ||
91 | #define SAA_DEVICE_IMAGE_CORRUPT 0x04 | ||
92 | #define SAA_DEVICE_MEMORY_CORRUPT 0x08 | ||
93 | #define SAA_DEVICE_NO_IMAGE 0x10 | ||
94 | |||
95 | /* Register addresses */ | ||
96 | #define SAA_DEVICE_2ND_VERSION 0x50 | ||
97 | #define SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET 0x54 | ||
98 | |||
99 | /* Register addresses */ | ||
100 | #define SAA_SECONDSTAGEERROR_FLAGS 0x64 | ||
101 | |||
102 | /* Bootloader regs and flags */ | ||
103 | #define SAA_DEVICE_DEADLOCK_DETECTED_OFFSET 0x6C | ||
104 | #define SAA_DEVICE_DEADLOCK_DETECTED 0xDEADDEAD | ||
105 | |||
106 | /* Basic firmware status registers */ | ||
107 | #define SAA_DEVICE_SYSINIT_STATUS_OFFSET 0x70 | ||
108 | #define SAA_DEVICE_SYSINIT_STATUS 0x70 | ||
109 | #define SAA_DEVICE_SYSINIT_MODE 0x74 | ||
110 | #define SAA_DEVICE_SYSINIT_SPEC 0x78 | ||
111 | #define SAA_DEVICE_SYSINIT_INST 0x7C | ||
112 | #define SAA_DEVICE_SYSINIT_CPULOAD 0x80 | ||
113 | #define SAA_DEVICE_SYSINIT_REMAINHEAP 0x84 | ||
114 | |||
115 | #define SAA_DEVICE_DOWNLOAD_OFFSET 0x1000 | ||
116 | #define SAA_DEVICE_BUFFERBLOCKSIZE 0x1000 | ||
117 | |||
118 | #define SAA_DEVICE_2ND_BUFFERBLOCKSIZE 0x100000 | ||
119 | #define SAA_DEVICE_2ND_DOWNLOAD_OFFSET 0x200000 | ||
120 | |||
121 | /* Descriptors */ | ||
122 | #define CS_INTERFACE 0x24 | ||
123 | |||
124 | /* Descriptor subtypes */ | ||
125 | #define VC_INPUT_TERMINAL 0x02 | ||
126 | #define VC_OUTPUT_TERMINAL 0x03 | ||
127 | #define VC_SELECTOR_UNIT 0x04 | ||
128 | #define VC_PROCESSING_UNIT 0x05 | ||
129 | #define FEATURE_UNIT 0x06 | ||
130 | #define TUNER_UNIT 0x09 | ||
131 | #define ENCODER_UNIT 0x0A | ||
132 | #define EXTENSION_UNIT 0x0B | ||
133 | #define VC_TUNER_PATH 0xF0 | ||
134 | #define PVC_HARDWARE_DESCRIPTOR 0xF1 | ||
135 | #define PVC_INTERFACE_DESCRIPTOR 0xF2 | ||
136 | #define PVC_INFRARED_UNIT 0xF3 | ||
137 | #define DRM_UNIT 0xF4 | ||
138 | #define GENERAL_REQUEST 0xF5 | ||
139 | |||
140 | /* Format Types */ | ||
141 | #define VS_FORMAT_TYPE 0x02 | ||
142 | #define VS_FORMAT_TYPE_I 0x01 | ||
143 | #define VS_FORMAT_UNCOMPRESSED 0x04 | ||
144 | #define VS_FRAME_UNCOMPRESSED 0x05 | ||
145 | #define VS_FORMAT_MPEG2PS 0x09 | ||
146 | #define VS_FORMAT_MPEG2TS 0x0A | ||
147 | #define VS_FORMAT_MPEG4SL 0x0B | ||
148 | #define VS_FORMAT_WM9 0x0C | ||
149 | #define VS_FORMAT_DIVX 0x0D | ||
150 | #define VS_FORMAT_VBI 0x0E | ||
151 | #define VS_FORMAT_RDS 0x0F | ||
152 | |||
153 | /* Device extension commands */ | ||
154 | #define EXU_REGISTER_ACCESS_CONTROL 0x00 | ||
155 | #define EXU_GPIO_CONTROL 0x01 | ||
156 | #define EXU_GPIO_GROUP_CONTROL 0x02 | ||
157 | #define EXU_INTERRUPT_CONTROL 0x03 | ||
158 | |||
159 | /* State Transition and args */ | ||
160 | #define SAA_PROBE_CONTROL 0x01 | ||
161 | #define SAA_COMMIT_CONTROL 0x02 | ||
162 | #define SAA_STATE_CONTROL 0x03 | ||
163 | #define SAA_DMASTATE_STOP 0x00 | ||
164 | #define SAA_DMASTATE_ACQUIRE 0x01 | ||
165 | #define SAA_DMASTATE_PAUSE 0x02 | ||
166 | #define SAA_DMASTATE_RUN 0x03 | ||
167 | |||
168 | /* A/V Mux Input Selector */ | ||
169 | #define SU_INPUT_SELECT_CONTROL 0x01 | ||
170 | |||
171 | /* Encoder Profiles */ | ||
172 | #define EU_PROFILE_PS_DVD 0x06 | ||
173 | #define EU_PROFILE_TS_HQ 0x09 | ||
174 | #define EU_VIDEO_FORMAT_MPEG_2 0x02 | ||
175 | |||
176 | /* Tuner */ | ||
177 | #define TU_AUDIO_MODE_CONTROL 0x17 | ||
178 | |||
179 | /* Video Formats */ | ||
180 | #define TU_STANDARD_CONTROL 0x00 | ||
181 | #define TU_STANDARD_AUTO_CONTROL 0x01 | ||
182 | #define TU_STANDARD_NONE 0x00 | ||
183 | #define TU_STANDARD_NTSC_M 0x01 | ||
184 | #define TU_STANDARD_PAL_I 0x08 | ||
185 | #define TU_STANDARD_MANUAL 0x00 | ||
186 | #define TU_STANDARD_AUTO 0x01 | ||
187 | |||
188 | /* Video Controls */ | ||
189 | #define PU_BRIGHTNESS_CONTROL 0x02 | ||
190 | #define PU_CONTRAST_CONTROL 0x03 | ||
191 | #define PU_HUE_CONTROL 0x06 | ||
192 | #define PU_SATURATION_CONTROL 0x07 | ||
193 | #define PU_SHARPNESS_CONTROL 0x08 | ||
194 | |||
195 | /* Audio Controls */ | ||
196 | #define MUTE_CONTROL 0x01 | ||
197 | #define VOLUME_CONTROL 0x02 | ||
198 | #define AUDIO_DEFAULT_CONTROL 0x0D | ||
199 | |||
200 | /* Default Volume Levels */ | ||
201 | #define TMHW_LEV_ADJ_DECLEV_DEFAULT 0x00 | ||
202 | #define TMHW_LEV_ADJ_MONOLEV_DEFAULT 0x00 | ||
203 | #define TMHW_LEV_ADJ_NICLEV_DEFAULT 0x00 | ||
204 | #define TMHW_LEV_ADJ_SAPLEV_DEFAULT 0x00 | ||
205 | #define TMHW_LEV_ADJ_ADCLEV_DEFAULT 0x00 | ||
206 | |||
207 | /* Encoder Related Commands */ | ||
208 | #define EU_PROFILE_CONTROL 0x00 | ||
209 | #define EU_VIDEO_FORMAT_CONTROL 0x01 | ||
210 | #define EU_VIDEO_BIT_RATE_CONTROL 0x02 | ||
211 | #define EU_VIDEO_RESOLUTION_CONTROL 0x03 | ||
212 | #define EU_VIDEO_GOP_STRUCTURE_CONTROL 0x04 | ||
213 | #define EU_VIDEO_INPUT_ASPECT_CONTROL 0x0A | ||
214 | #define EU_AUDIO_FORMAT_CONTROL 0x0C | ||
215 | #define EU_AUDIO_BIT_RATE_CONTROL 0x0D | ||
216 | |||
217 | /* Firmware Debugging */ | ||
218 | #define SET_DEBUG_LEVEL_CONTROL 0x0B | ||
219 | #define GET_DEBUG_DATA_CONTROL 0x0C | ||
diff --git a/drivers/media/video/saa7164/saa7164-types.h b/drivers/media/video/saa7164/saa7164-types.h new file mode 100644 index 00000000000..1d2140a3eb3 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-types.h | |||
@@ -0,0 +1,442 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | struct tmComResHWDescr { | ||
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)); | ||
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 | struct tmComResInterfaceDescr { | ||
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 | }; | ||
60 | |||
61 | struct tmComResBusDescr { | ||
62 | u64 CommandRing; | ||
63 | u64 ResponseRing; | ||
64 | u32 CommandWrite; | ||
65 | u32 CommandRead; | ||
66 | u32 ResponseWrite; | ||
67 | u32 ResponseRead; | ||
68 | }; | ||
69 | |||
70 | enum tmBusType { | ||
71 | NONE = 0, | ||
72 | TYPE_BUS_PCI = 1, | ||
73 | TYPE_BUS_PCIe = 2, | ||
74 | TYPE_BUS_USB = 3, | ||
75 | TYPE_BUS_I2C = 4 | ||
76 | }; | ||
77 | |||
78 | struct tmComResBusInfo { | ||
79 | enum tmBusType Type; | ||
80 | u16 m_wMaxReqSize; | ||
81 | u8 *m_pdwSetRing; | ||
82 | u32 m_dwSizeSetRing; | ||
83 | u8 *m_pdwGetRing; | ||
84 | u32 m_dwSizeGetRing; | ||
85 | u32 m_dwSetWritePos; | ||
86 | u32 m_dwSetReadPos; | ||
87 | u32 m_dwGetWritePos; | ||
88 | u32 m_dwGetReadPos; | ||
89 | |||
90 | /* All access is protected */ | ||
91 | struct mutex lock; | ||
92 | |||
93 | }; | ||
94 | |||
95 | struct tmComResInfo { | ||
96 | u8 id; | ||
97 | u8 flags; | ||
98 | u16 size; | ||
99 | u32 command; | ||
100 | u16 controlselector; | ||
101 | u8 seqno; | ||
102 | } __attribute__((packed)); | ||
103 | |||
104 | enum tmComResCmd { | ||
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 | }; | ||
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 | struct tmDescriptor { | ||
125 | u32 pathid; | ||
126 | u32 size; | ||
127 | void *descriptor; | ||
128 | }; | ||
129 | |||
130 | struct tmComResDescrHeader { | ||
131 | u8 len; | ||
132 | u8 type; | ||
133 | u8 subtype; | ||
134 | u8 unitid; | ||
135 | } __attribute__((packed)); | ||
136 | |||
137 | struct tmComResExtDevDescrHeader { | ||
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)); | ||
148 | |||
149 | struct tmComResGPIO { | ||
150 | u32 pin; | ||
151 | u8 state; | ||
152 | } __attribute__((packed)); | ||
153 | |||
154 | struct tmComResPathDescrHeader { | ||
155 | u8 len; | ||
156 | u8 type; | ||
157 | u8 subtype; | ||
158 | u8 pathid; | ||
159 | } __attribute__((packed)); | ||
160 | |||
161 | /* terminaltype */ | ||
162 | enum tmComResTermType { | ||
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 | }; | ||
171 | |||
172 | struct tmComResAntTermDescrHeader { | ||
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)); | ||
182 | |||
183 | struct tmComResTunerDescrHeader { | ||
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)); | ||
194 | |||
195 | enum tmBufferFlag { | ||
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 | }; | ||
205 | |||
206 | struct tmBuffer { | ||
207 | u64 *pagetablevirt; | ||
208 | u64 pagetablephys; | ||
209 | u16 offset; | ||
210 | u8 *context; | ||
211 | u64 timestamp; | ||
212 | enum tmBufferFlag BufferFlag; | ||
213 | u32 lostbuffers; | ||
214 | u32 validbuffers; | ||
215 | u64 *dummypagevirt; | ||
216 | u64 dummypagephys; | ||
217 | u64 *addressvirt; | ||
218 | }; | ||
219 | |||
220 | struct tmHWStreamParameters { | ||
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 | }; | ||
231 | |||
232 | struct tmStreamParameters { | ||
233 | struct tmHWStreamParameters HWStreamParameters; | ||
234 | u64 qwDummyPageTablePhys; | ||
235 | u64 *pDummyPageTableVirt; | ||
236 | }; | ||
237 | |||
238 | struct tmComResDMATermDescrHeader { | ||
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)); | ||
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 | struct tmComResTSFormatDescrHeader { | ||
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)); | ||
287 | |||
288 | /* Encoder related structures */ | ||
289 | |||
290 | /* A/V Mux Selector */ | ||
291 | struct tmComResSelDescrHeader { | ||
292 | u8 len; | ||
293 | u8 type; | ||
294 | u8 subtype; | ||
295 | u8 unitid; | ||
296 | u8 nrinpins; | ||
297 | u8 sourceid; | ||
298 | } __attribute__((packed)); | ||
299 | |||
300 | /* A/V Audio processor definitions */ | ||
301 | struct tmComResProcDescrHeader { | ||
302 | u8 len; | ||
303 | u8 type; | ||
304 | u8 subtype; | ||
305 | u8 unitid; | ||
306 | u8 sourceid; | ||
307 | u16 wreserved; | ||
308 | u8 controlsize; | ||
309 | } __attribute__((packed)); | ||
310 | |||
311 | /* Video bitrate control message */ | ||
312 | #define EU_VIDEO_BIT_RATE_MODE_CONSTANT (0) | ||
313 | #define EU_VIDEO_BIT_RATE_MODE_VARIABLE_AVERAGE (1) | ||
314 | #define EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK (2) | ||
315 | struct tmComResEncVideoBitRate { | ||
316 | u8 ucVideoBitRateMode; | ||
317 | u32 dwVideoBitRate; | ||
318 | u32 dwVideoBitRatePeak; | ||
319 | } __attribute__((packed)); | ||
320 | |||
321 | /* Video Encoder Aspect Ratio message */ | ||
322 | struct tmComResEncVideoInputAspectRatio { | ||
323 | u8 width; | ||
324 | u8 height; | ||
325 | } __attribute__((packed)); | ||
326 | |||
327 | /* Video Encoder GOP IBP message */ | ||
328 | /* 1. IPPPPPPPPPPPPPP */ | ||
329 | /* 2. IBPBPBPBPBPBPBP */ | ||
330 | /* 3. IBBPBBPBBPBBP */ | ||
331 | #define SAA7164_ENCODER_DEFAULT_GOP_DIST (1) | ||
332 | #define SAA7164_ENCODER_DEFAULT_GOP_SIZE (15) | ||
333 | struct tmComResEncVideoGopStructure { | ||
334 | u8 ucGOPSize; /* GOP Size 12, 15 */ | ||
335 | u8 ucRefFrameDist; /* Reference Frame Distance */ | ||
336 | } __attribute__((packed)); | ||
337 | |||
338 | /* Encoder processor definition */ | ||
339 | struct tmComResEncoderDescrHeader { | ||
340 | u8 len; | ||
341 | u8 type; | ||
342 | u8 subtype; | ||
343 | u8 unitid; | ||
344 | u8 vsourceid; | ||
345 | u8 asourceid; | ||
346 | u8 iunit; | ||
347 | u32 dwmControlCap; | ||
348 | u32 dwmProfileCap; | ||
349 | u32 dwmVidFormatCap; | ||
350 | u8 bmVidBitrateCap; | ||
351 | u16 wmVidResolutionsCap; | ||
352 | u16 wmVidFrmRateCap; | ||
353 | u32 dwmAudFormatCap; | ||
354 | u8 bmAudBitrateCap; | ||
355 | } __attribute__((packed)); | ||
356 | |||
357 | /* Audio processor definition */ | ||
358 | struct tmComResAFeatureDescrHeader { | ||
359 | u8 len; | ||
360 | u8 type; | ||
361 | u8 subtype; | ||
362 | u8 unitid; | ||
363 | u8 sourceid; | ||
364 | u8 controlsize; | ||
365 | } __attribute__((packed)); | ||
366 | |||
367 | /* Audio control messages */ | ||
368 | struct tmComResAudioDefaults { | ||
369 | u8 ucDecoderLevel; | ||
370 | u8 ucDecoderFM_Level; | ||
371 | u8 ucMonoLevel; | ||
372 | u8 ucNICAM_Level; | ||
373 | u8 ucSAP_Level; | ||
374 | u8 ucADC_Level; | ||
375 | } __attribute__((packed)); | ||
376 | |||
377 | /* Audio bitrate control message */ | ||
378 | struct tmComResEncAudioBitRate { | ||
379 | u8 ucAudioBitRateMode; | ||
380 | u32 dwAudioBitRate; | ||
381 | u32 dwAudioBitRatePeak; | ||
382 | } __attribute__((packed)); | ||
383 | |||
384 | /* Tuner / AV Decoder messages */ | ||
385 | struct tmComResTunerStandard { | ||
386 | u8 std; | ||
387 | u32 country; | ||
388 | } __attribute__((packed)); | ||
389 | |||
390 | struct tmComResTunerStandardAuto { | ||
391 | u8 mode; | ||
392 | } __attribute__((packed)); | ||
393 | |||
394 | /* EEPROM definition for PS stream types */ | ||
395 | struct tmComResPSFormatDescrHeader { | ||
396 | u8 len; | ||
397 | u8 type; | ||
398 | u8 subtype; | ||
399 | u8 bFormatIndex; | ||
400 | u16 wPacketLength; | ||
401 | u16 wPackLength; | ||
402 | u8 bPackDataType; | ||
403 | } __attribute__((packed)); | ||
404 | |||
405 | /* VBI control structure */ | ||
406 | struct tmComResVBIFormatDescrHeader { | ||
407 | u8 len; | ||
408 | u8 type; | ||
409 | u8 subtype; /* VS_FORMAT_VBI */ | ||
410 | u8 bFormatIndex; | ||
411 | u32 VideoStandard; /* See KS_AnalogVideoStandard, NTSC = 1 */ | ||
412 | u8 StartLine; /* NTSC Start = 10 */ | ||
413 | u8 EndLine; /* NTSC = 21 */ | ||
414 | u8 FieldRate; /* 60 for NTSC */ | ||
415 | u8 bNumLines; /* Unused - scheduled for removal */ | ||
416 | } __attribute__((packed)); | ||
417 | |||
418 | struct tmComResProbeCommit { | ||
419 | u16 bmHint; | ||
420 | u8 bFormatIndex; | ||
421 | u8 bFrameIndex; | ||
422 | } __attribute__((packed)); | ||
423 | |||
424 | struct tmComResDebugSetLevel { | ||
425 | u32 dwDebugLevel; | ||
426 | } __attribute__((packed)); | ||
427 | |||
428 | struct tmComResDebugGetData { | ||
429 | u32 dwResult; | ||
430 | u8 ucDebugData[256]; | ||
431 | } __attribute__((packed)); | ||
432 | |||
433 | struct tmFwInfoStruct { | ||
434 | u32 status; | ||
435 | u32 mode; | ||
436 | u32 devicespec; | ||
437 | u32 deviceinst; | ||
438 | u32 CPULoad; | ||
439 | u32 RemainHeap; | ||
440 | u32 CPUClock; | ||
441 | u32 RAMSpeed; | ||
442 | } __attribute__((packed)); | ||
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c new file mode 100644 index 00000000000..e2e03415871 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-vbi.c | |||
@@ -0,0 +1,1380 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | static struct saa7164_tvnorm saa7164_tvnorms[] = { | ||
25 | { | ||
26 | .name = "NTSC-M", | ||
27 | .id = V4L2_STD_NTSC_M, | ||
28 | }, { | ||
29 | .name = "NTSC-JP", | ||
30 | .id = V4L2_STD_NTSC_M_JP, | ||
31 | } | ||
32 | }; | ||
33 | |||
34 | static const u32 saa7164_v4l2_ctrls[] = { | ||
35 | 0 | ||
36 | }; | ||
37 | |||
38 | /* Take the encoder configuration from the port struct and | ||
39 | * flush it to the hardware. | ||
40 | */ | ||
41 | static void saa7164_vbi_configure(struct saa7164_port *port) | ||
42 | { | ||
43 | struct saa7164_dev *dev = port->dev; | ||
44 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | ||
45 | |||
46 | port->vbi_params.width = port->width; | ||
47 | port->vbi_params.height = port->height; | ||
48 | port->vbi_params.is_50hz = | ||
49 | (port->encodernorm.id & V4L2_STD_625_50) != 0; | ||
50 | |||
51 | /* Set up the DIF (enable it) for analog mode by default */ | ||
52 | saa7164_api_initialize_dif(port); | ||
53 | |||
54 | /* Configure the correct video standard */ | ||
55 | #if 0 | ||
56 | saa7164_api_configure_dif(port, port->encodernorm.id); | ||
57 | #endif | ||
58 | |||
59 | #if 0 | ||
60 | /* Ensure the audio decoder is correct configured */ | ||
61 | saa7164_api_set_audio_std(port); | ||
62 | #endif | ||
63 | dprintk(DBGLVL_VBI, "%s() ends\n", __func__); | ||
64 | } | ||
65 | |||
66 | static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port) | ||
67 | { | ||
68 | struct list_head *c, *n, *p, *q, *l, *v; | ||
69 | struct saa7164_dev *dev = port->dev; | ||
70 | struct saa7164_buffer *buf; | ||
71 | struct saa7164_user_buffer *ubuf; | ||
72 | |||
73 | /* Remove any allocated buffers */ | ||
74 | mutex_lock(&port->dmaqueue_lock); | ||
75 | |||
76 | dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr); | ||
77 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
78 | buf = list_entry(c, struct saa7164_buffer, list); | ||
79 | list_del(c); | ||
80 | saa7164_buffer_dealloc(buf); | ||
81 | } | ||
82 | |||
83 | dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr); | ||
84 | list_for_each_safe(p, q, &port->list_buf_used.list) { | ||
85 | ubuf = list_entry(p, struct saa7164_user_buffer, list); | ||
86 | list_del(p); | ||
87 | saa7164_buffer_dealloc_user(ubuf); | ||
88 | } | ||
89 | |||
90 | dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr); | ||
91 | list_for_each_safe(l, v, &port->list_buf_free.list) { | ||
92 | ubuf = list_entry(l, struct saa7164_user_buffer, list); | ||
93 | list_del(l); | ||
94 | saa7164_buffer_dealloc_user(ubuf); | ||
95 | } | ||
96 | |||
97 | mutex_unlock(&port->dmaqueue_lock); | ||
98 | dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr); | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | /* Dynamic buffer switch at vbi start time */ | ||
104 | static int saa7164_vbi_buffers_alloc(struct saa7164_port *port) | ||
105 | { | ||
106 | struct saa7164_dev *dev = port->dev; | ||
107 | struct saa7164_buffer *buf; | ||
108 | struct saa7164_user_buffer *ubuf; | ||
109 | struct tmHWStreamParameters *params = &port->hw_streamingparams; | ||
110 | int result = -ENODEV, i; | ||
111 | int len = 0; | ||
112 | |||
113 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | ||
114 | |||
115 | /* TODO: NTSC SPECIFIC */ | ||
116 | /* Init and establish defaults */ | ||
117 | params->samplesperline = 1440; | ||
118 | params->numberoflines = 12; | ||
119 | params->numberoflines = 18; | ||
120 | params->pitch = 1600; | ||
121 | params->pitch = 1440; | ||
122 | params->numpagetables = 2 + | ||
123 | ((params->numberoflines * params->pitch) / PAGE_SIZE); | ||
124 | params->bitspersample = 8; | ||
125 | params->linethreshold = 0; | ||
126 | params->pagetablelistvirt = NULL; | ||
127 | params->pagetablelistphys = NULL; | ||
128 | params->numpagetableentries = port->hwcfg.buffercount; | ||
129 | |||
130 | /* Allocate the PCI resources, buffers (hard) */ | ||
131 | for (i = 0; i < port->hwcfg.buffercount; i++) { | ||
132 | buf = saa7164_buffer_alloc(port, | ||
133 | params->numberoflines * | ||
134 | params->pitch); | ||
135 | |||
136 | if (!buf) { | ||
137 | printk(KERN_ERR "%s() failed " | ||
138 | "(errno = %d), unable to allocate buffer\n", | ||
139 | __func__, result); | ||
140 | result = -ENOMEM; | ||
141 | goto failed; | ||
142 | } else { | ||
143 | |||
144 | mutex_lock(&port->dmaqueue_lock); | ||
145 | list_add_tail(&buf->list, &port->dmaqueue.list); | ||
146 | mutex_unlock(&port->dmaqueue_lock); | ||
147 | |||
148 | } | ||
149 | } | ||
150 | |||
151 | /* Allocate some kernel buffers for copying | ||
152 | * to userpsace. | ||
153 | */ | ||
154 | len = params->numberoflines * params->pitch; | ||
155 | |||
156 | if (vbi_buffers < 16) | ||
157 | vbi_buffers = 16; | ||
158 | if (vbi_buffers > 512) | ||
159 | vbi_buffers = 512; | ||
160 | |||
161 | for (i = 0; i < vbi_buffers; i++) { | ||
162 | |||
163 | ubuf = saa7164_buffer_alloc_user(dev, len); | ||
164 | if (ubuf) { | ||
165 | mutex_lock(&port->dmaqueue_lock); | ||
166 | list_add_tail(&ubuf->list, &port->list_buf_free.list); | ||
167 | mutex_unlock(&port->dmaqueue_lock); | ||
168 | } | ||
169 | |||
170 | } | ||
171 | |||
172 | result = 0; | ||
173 | |||
174 | failed: | ||
175 | return result; | ||
176 | } | ||
177 | |||
178 | |||
179 | static int saa7164_vbi_initialize(struct saa7164_port *port) | ||
180 | { | ||
181 | saa7164_vbi_configure(port); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | /* -- V4L2 --------------------------------------------------------- */ | ||
186 | static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) | ||
187 | { | ||
188 | struct saa7164_vbi_fh *fh = file->private_data; | ||
189 | struct saa7164_port *port = fh->port; | ||
190 | struct saa7164_dev *dev = port->dev; | ||
191 | unsigned int i; | ||
192 | |||
193 | dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)*id); | ||
194 | |||
195 | for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { | ||
196 | if (*id & saa7164_tvnorms[i].id) | ||
197 | break; | ||
198 | } | ||
199 | if (i == ARRAY_SIZE(saa7164_tvnorms)) | ||
200 | return -EINVAL; | ||
201 | |||
202 | port->encodernorm = saa7164_tvnorms[i]; | ||
203 | |||
204 | /* Update the audio decoder while is not running in | ||
205 | * auto detect mode. | ||
206 | */ | ||
207 | saa7164_api_set_audio_std(port); | ||
208 | |||
209 | dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)*id); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int vidioc_enum_input(struct file *file, void *priv, | ||
215 | struct v4l2_input *i) | ||
216 | { | ||
217 | int n; | ||
218 | |||
219 | char *inputs[] = { "tuner", "composite", "svideo", "aux", | ||
220 | "composite 2", "svideo 2", "aux 2" }; | ||
221 | |||
222 | if (i->index >= 7) | ||
223 | return -EINVAL; | ||
224 | |||
225 | strcpy(i->name, inputs[i->index]); | ||
226 | |||
227 | if (i->index == 0) | ||
228 | i->type = V4L2_INPUT_TYPE_TUNER; | ||
229 | else | ||
230 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
231 | |||
232 | for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++) | ||
233 | i->std |= saa7164_tvnorms[n].id; | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) | ||
239 | { | ||
240 | struct saa7164_vbi_fh *fh = file->private_data; | ||
241 | struct saa7164_port *port = fh->port; | ||
242 | struct saa7164_dev *dev = port->dev; | ||
243 | |||
244 | if (saa7164_api_get_videomux(port) != SAA_OK) | ||
245 | return -EIO; | ||
246 | |||
247 | *i = (port->mux_input - 1); | ||
248 | |||
249 | dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, *i); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int vidioc_s_input(struct file *file, void *priv, unsigned int i) | ||
255 | { | ||
256 | struct saa7164_vbi_fh *fh = file->private_data; | ||
257 | struct saa7164_port *port = fh->port; | ||
258 | struct saa7164_dev *dev = port->dev; | ||
259 | |||
260 | dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, i); | ||
261 | |||
262 | if (i >= 7) | ||
263 | return -EINVAL; | ||
264 | |||
265 | port->mux_input = i + 1; | ||
266 | |||
267 | if (saa7164_api_set_videomux(port) != SAA_OK) | ||
268 | return -EIO; | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
274 | struct v4l2_tuner *t) | ||
275 | { | ||
276 | struct saa7164_vbi_fh *fh = file->private_data; | ||
277 | struct saa7164_port *port = fh->port; | ||
278 | struct saa7164_dev *dev = port->dev; | ||
279 | |||
280 | if (0 != t->index) | ||
281 | return -EINVAL; | ||
282 | |||
283 | strcpy(t->name, "tuner"); | ||
284 | t->type = V4L2_TUNER_ANALOG_TV; | ||
285 | t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; | ||
286 | |||
287 | dprintk(DBGLVL_VBI, "VIDIOC_G_TUNER: tuner type %d\n", t->type); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
293 | struct v4l2_tuner *t) | ||
294 | { | ||
295 | /* Update the A/V core */ | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
300 | struct v4l2_frequency *f) | ||
301 | { | ||
302 | struct saa7164_vbi_fh *fh = file->private_data; | ||
303 | struct saa7164_port *port = fh->port; | ||
304 | |||
305 | f->type = V4L2_TUNER_ANALOG_TV; | ||
306 | f->frequency = port->freq; | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
312 | struct v4l2_frequency *f) | ||
313 | { | ||
314 | struct saa7164_vbi_fh *fh = file->private_data; | ||
315 | struct saa7164_port *port = fh->port; | ||
316 | struct saa7164_dev *dev = port->dev; | ||
317 | struct saa7164_port *tsport; | ||
318 | struct dvb_frontend *fe; | ||
319 | |||
320 | /* TODO: Pull this for the std */ | ||
321 | struct analog_parameters params = { | ||
322 | .mode = V4L2_TUNER_ANALOG_TV, | ||
323 | .audmode = V4L2_TUNER_MODE_STEREO, | ||
324 | .std = port->encodernorm.id, | ||
325 | .frequency = f->frequency | ||
326 | }; | ||
327 | |||
328 | /* Stop the encoder */ | ||
329 | dprintk(DBGLVL_VBI, "%s() frequency=%d tuner=%d\n", __func__, | ||
330 | f->frequency, f->tuner); | ||
331 | |||
332 | if (f->tuner != 0) | ||
333 | return -EINVAL; | ||
334 | |||
335 | if (f->type != V4L2_TUNER_ANALOG_TV) | ||
336 | return -EINVAL; | ||
337 | |||
338 | port->freq = f->frequency; | ||
339 | |||
340 | /* Update the hardware */ | ||
341 | if (port->nr == SAA7164_PORT_VBI1) | ||
342 | tsport = &dev->ports[SAA7164_PORT_TS1]; | ||
343 | else | ||
344 | if (port->nr == SAA7164_PORT_VBI2) | ||
345 | tsport = &dev->ports[SAA7164_PORT_TS2]; | ||
346 | else | ||
347 | BUG(); | ||
348 | |||
349 | fe = tsport->dvb.frontend; | ||
350 | |||
351 | if (fe && fe->ops.tuner_ops.set_analog_params) | ||
352 | fe->ops.tuner_ops.set_analog_params(fe, ¶ms); | ||
353 | else | ||
354 | printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__); | ||
355 | |||
356 | saa7164_vbi_initialize(port); | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static int vidioc_g_ctrl(struct file *file, void *priv, | ||
362 | struct v4l2_control *ctl) | ||
363 | { | ||
364 | struct saa7164_vbi_fh *fh = file->private_data; | ||
365 | struct saa7164_port *port = fh->port; | ||
366 | struct saa7164_dev *dev = port->dev; | ||
367 | |||
368 | dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__, | ||
369 | ctl->id, ctl->value); | ||
370 | |||
371 | switch (ctl->id) { | ||
372 | case V4L2_CID_BRIGHTNESS: | ||
373 | ctl->value = port->ctl_brightness; | ||
374 | break; | ||
375 | case V4L2_CID_CONTRAST: | ||
376 | ctl->value = port->ctl_contrast; | ||
377 | break; | ||
378 | case V4L2_CID_SATURATION: | ||
379 | ctl->value = port->ctl_saturation; | ||
380 | break; | ||
381 | case V4L2_CID_HUE: | ||
382 | ctl->value = port->ctl_hue; | ||
383 | break; | ||
384 | case V4L2_CID_SHARPNESS: | ||
385 | ctl->value = port->ctl_sharpness; | ||
386 | break; | ||
387 | case V4L2_CID_AUDIO_VOLUME: | ||
388 | ctl->value = port->ctl_volume; | ||
389 | break; | ||
390 | default: | ||
391 | return -EINVAL; | ||
392 | } | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
398 | struct v4l2_control *ctl) | ||
399 | { | ||
400 | struct saa7164_vbi_fh *fh = file->private_data; | ||
401 | struct saa7164_port *port = fh->port; | ||
402 | struct saa7164_dev *dev = port->dev; | ||
403 | int ret = 0; | ||
404 | |||
405 | dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__, | ||
406 | ctl->id, ctl->value); | ||
407 | |||
408 | switch (ctl->id) { | ||
409 | case V4L2_CID_BRIGHTNESS: | ||
410 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
411 | port->ctl_brightness = ctl->value; | ||
412 | saa7164_api_set_usercontrol(port, | ||
413 | PU_BRIGHTNESS_CONTROL); | ||
414 | } else | ||
415 | ret = -EINVAL; | ||
416 | break; | ||
417 | case V4L2_CID_CONTRAST: | ||
418 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
419 | port->ctl_contrast = ctl->value; | ||
420 | saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); | ||
421 | } else | ||
422 | ret = -EINVAL; | ||
423 | break; | ||
424 | case V4L2_CID_SATURATION: | ||
425 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
426 | port->ctl_saturation = ctl->value; | ||
427 | saa7164_api_set_usercontrol(port, | ||
428 | PU_SATURATION_CONTROL); | ||
429 | } else | ||
430 | ret = -EINVAL; | ||
431 | break; | ||
432 | case V4L2_CID_HUE: | ||
433 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
434 | port->ctl_hue = ctl->value; | ||
435 | saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); | ||
436 | } else | ||
437 | ret = -EINVAL; | ||
438 | break; | ||
439 | case V4L2_CID_SHARPNESS: | ||
440 | if ((ctl->value >= 0) && (ctl->value <= 255)) { | ||
441 | port->ctl_sharpness = ctl->value; | ||
442 | saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); | ||
443 | } else | ||
444 | ret = -EINVAL; | ||
445 | break; | ||
446 | case V4L2_CID_AUDIO_VOLUME: | ||
447 | if ((ctl->value >= -83) && (ctl->value <= 24)) { | ||
448 | port->ctl_volume = ctl->value; | ||
449 | saa7164_api_set_audio_volume(port, port->ctl_volume); | ||
450 | } else | ||
451 | ret = -EINVAL; | ||
452 | break; | ||
453 | default: | ||
454 | ret = -EINVAL; | ||
455 | } | ||
456 | |||
457 | return ret; | ||
458 | } | ||
459 | |||
460 | static int saa7164_get_ctrl(struct saa7164_port *port, | ||
461 | struct v4l2_ext_control *ctrl) | ||
462 | { | ||
463 | struct saa7164_vbi_params *params = &port->vbi_params; | ||
464 | |||
465 | switch (ctrl->id) { | ||
466 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
467 | ctrl->value = params->stream_type; | ||
468 | break; | ||
469 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
470 | ctrl->value = params->ctl_mute; | ||
471 | break; | ||
472 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
473 | ctrl->value = params->ctl_aspect; | ||
474 | break; | ||
475 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
476 | ctrl->value = params->refdist; | ||
477 | break; | ||
478 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
479 | ctrl->value = params->gop_size; | ||
480 | break; | ||
481 | default: | ||
482 | return -EINVAL; | ||
483 | } | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static int vidioc_g_ext_ctrls(struct file *file, void *priv, | ||
488 | struct v4l2_ext_controls *ctrls) | ||
489 | { | ||
490 | struct saa7164_vbi_fh *fh = file->private_data; | ||
491 | struct saa7164_port *port = fh->port; | ||
492 | int i, err = 0; | ||
493 | |||
494 | if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { | ||
495 | for (i = 0; i < ctrls->count; i++) { | ||
496 | struct v4l2_ext_control *ctrl = ctrls->controls + i; | ||
497 | |||
498 | err = saa7164_get_ctrl(port, ctrl); | ||
499 | if (err) { | ||
500 | ctrls->error_idx = i; | ||
501 | break; | ||
502 | } | ||
503 | } | ||
504 | return err; | ||
505 | |||
506 | } | ||
507 | |||
508 | return -EINVAL; | ||
509 | } | ||
510 | |||
511 | static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) | ||
512 | { | ||
513 | int ret = -EINVAL; | ||
514 | |||
515 | switch (ctrl->id) { | ||
516 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
517 | if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) || | ||
518 | (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)) | ||
519 | ret = 0; | ||
520 | break; | ||
521 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
522 | if ((ctrl->value >= 0) && | ||
523 | (ctrl->value <= 1)) | ||
524 | ret = 0; | ||
525 | break; | ||
526 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
527 | if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) && | ||
528 | (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100)) | ||
529 | ret = 0; | ||
530 | break; | ||
531 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
532 | if ((ctrl->value >= 0) && | ||
533 | (ctrl->value <= 255)) | ||
534 | ret = 0; | ||
535 | break; | ||
536 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
537 | if ((ctrl->value >= 1) && | ||
538 | (ctrl->value <= 3)) | ||
539 | ret = 0; | ||
540 | break; | ||
541 | default: | ||
542 | ret = -EINVAL; | ||
543 | } | ||
544 | |||
545 | return ret; | ||
546 | } | ||
547 | |||
548 | static int vidioc_try_ext_ctrls(struct file *file, void *priv, | ||
549 | struct v4l2_ext_controls *ctrls) | ||
550 | { | ||
551 | int i, err = 0; | ||
552 | |||
553 | if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { | ||
554 | for (i = 0; i < ctrls->count; i++) { | ||
555 | struct v4l2_ext_control *ctrl = ctrls->controls + i; | ||
556 | |||
557 | err = saa7164_try_ctrl(ctrl, 0); | ||
558 | if (err) { | ||
559 | ctrls->error_idx = i; | ||
560 | break; | ||
561 | } | ||
562 | } | ||
563 | return err; | ||
564 | } | ||
565 | |||
566 | return -EINVAL; | ||
567 | } | ||
568 | |||
569 | static int saa7164_set_ctrl(struct saa7164_port *port, | ||
570 | struct v4l2_ext_control *ctrl) | ||
571 | { | ||
572 | struct saa7164_vbi_params *params = &port->vbi_params; | ||
573 | int ret = 0; | ||
574 | |||
575 | switch (ctrl->id) { | ||
576 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
577 | params->stream_type = ctrl->value; | ||
578 | break; | ||
579 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
580 | params->ctl_mute = ctrl->value; | ||
581 | ret = saa7164_api_audio_mute(port, params->ctl_mute); | ||
582 | if (ret != SAA_OK) { | ||
583 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, | ||
584 | ret); | ||
585 | ret = -EIO; | ||
586 | } | ||
587 | break; | ||
588 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
589 | params->ctl_aspect = ctrl->value; | ||
590 | ret = saa7164_api_set_aspect_ratio(port); | ||
591 | if (ret != SAA_OK) { | ||
592 | printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, | ||
593 | ret); | ||
594 | ret = -EIO; | ||
595 | } | ||
596 | break; | ||
597 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
598 | params->refdist = ctrl->value; | ||
599 | break; | ||
600 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
601 | params->gop_size = ctrl->value; | ||
602 | break; | ||
603 | default: | ||
604 | return -EINVAL; | ||
605 | } | ||
606 | |||
607 | /* TODO: Update the hardware */ | ||
608 | |||
609 | return ret; | ||
610 | } | ||
611 | |||
612 | static int vidioc_s_ext_ctrls(struct file *file, void *priv, | ||
613 | struct v4l2_ext_controls *ctrls) | ||
614 | { | ||
615 | struct saa7164_vbi_fh *fh = file->private_data; | ||
616 | struct saa7164_port *port = fh->port; | ||
617 | int i, err = 0; | ||
618 | |||
619 | if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { | ||
620 | for (i = 0; i < ctrls->count; i++) { | ||
621 | struct v4l2_ext_control *ctrl = ctrls->controls + i; | ||
622 | |||
623 | err = saa7164_try_ctrl(ctrl, 0); | ||
624 | if (err) { | ||
625 | ctrls->error_idx = i; | ||
626 | break; | ||
627 | } | ||
628 | err = saa7164_set_ctrl(port, ctrl); | ||
629 | if (err) { | ||
630 | ctrls->error_idx = i; | ||
631 | break; | ||
632 | } | ||
633 | } | ||
634 | return err; | ||
635 | |||
636 | } | ||
637 | |||
638 | return -EINVAL; | ||
639 | } | ||
640 | |||
641 | static int vidioc_querycap(struct file *file, void *priv, | ||
642 | struct v4l2_capability *cap) | ||
643 | { | ||
644 | struct saa7164_vbi_fh *fh = file->private_data; | ||
645 | struct saa7164_port *port = fh->port; | ||
646 | struct saa7164_dev *dev = port->dev; | ||
647 | |||
648 | strcpy(cap->driver, dev->name); | ||
649 | strlcpy(cap->card, saa7164_boards[dev->board].name, | ||
650 | sizeof(cap->card)); | ||
651 | sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); | ||
652 | |||
653 | cap->capabilities = | ||
654 | V4L2_CAP_VBI_CAPTURE | | ||
655 | V4L2_CAP_READWRITE | | ||
656 | 0; | ||
657 | |||
658 | cap->capabilities |= V4L2_CAP_TUNER; | ||
659 | cap->version = 0; | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, | ||
665 | struct v4l2_fmtdesc *f) | ||
666 | { | ||
667 | if (f->index != 0) | ||
668 | return -EINVAL; | ||
669 | |||
670 | strlcpy(f->description, "VBI", sizeof(f->description)); | ||
671 | f->pixelformat = V4L2_PIX_FMT_MPEG; | ||
672 | |||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, | ||
677 | struct v4l2_format *f) | ||
678 | { | ||
679 | struct saa7164_vbi_fh *fh = file->private_data; | ||
680 | struct saa7164_port *port = fh->port; | ||
681 | struct saa7164_dev *dev = port->dev; | ||
682 | |||
683 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
684 | f->fmt.pix.bytesperline = 0; | ||
685 | f->fmt.pix.sizeimage = | ||
686 | port->ts_packet_size * port->ts_packet_count; | ||
687 | f->fmt.pix.colorspace = 0; | ||
688 | f->fmt.pix.width = port->width; | ||
689 | f->fmt.pix.height = port->height; | ||
690 | |||
691 | dprintk(DBGLVL_VBI, "VIDIOC_G_FMT: w: %d, h: %d\n", | ||
692 | port->width, port->height); | ||
693 | |||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, | ||
698 | struct v4l2_format *f) | ||
699 | { | ||
700 | struct saa7164_vbi_fh *fh = file->private_data; | ||
701 | struct saa7164_port *port = fh->port; | ||
702 | struct saa7164_dev *dev = port->dev; | ||
703 | |||
704 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
705 | f->fmt.pix.bytesperline = 0; | ||
706 | f->fmt.pix.sizeimage = | ||
707 | port->ts_packet_size * port->ts_packet_count; | ||
708 | f->fmt.pix.colorspace = 0; | ||
709 | dprintk(DBGLVL_VBI, "VIDIOC_TRY_FMT: w: %d, h: %d\n", | ||
710 | port->width, port->height); | ||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, | ||
715 | struct v4l2_format *f) | ||
716 | { | ||
717 | struct saa7164_vbi_fh *fh = file->private_data; | ||
718 | struct saa7164_port *port = fh->port; | ||
719 | struct saa7164_dev *dev = port->dev; | ||
720 | |||
721 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
722 | f->fmt.pix.bytesperline = 0; | ||
723 | f->fmt.pix.sizeimage = | ||
724 | port->ts_packet_size * port->ts_packet_count; | ||
725 | f->fmt.pix.colorspace = 0; | ||
726 | |||
727 | dprintk(DBGLVL_VBI, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", | ||
728 | f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static int vidioc_log_status(struct file *file, void *priv) | ||
734 | { | ||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | static int fill_queryctrl(struct saa7164_vbi_params *params, | ||
739 | struct v4l2_queryctrl *c) | ||
740 | { | ||
741 | switch (c->id) { | ||
742 | case V4L2_CID_BRIGHTNESS: | ||
743 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127); | ||
744 | case V4L2_CID_CONTRAST: | ||
745 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66); | ||
746 | case V4L2_CID_SATURATION: | ||
747 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62); | ||
748 | case V4L2_CID_HUE: | ||
749 | return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128); | ||
750 | case V4L2_CID_SHARPNESS: | ||
751 | return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8); | ||
752 | case V4L2_CID_MPEG_AUDIO_MUTE: | ||
753 | return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0); | ||
754 | case V4L2_CID_AUDIO_VOLUME: | ||
755 | return v4l2_ctrl_query_fill(c, -83, 24, 1, 20); | ||
756 | case V4L2_CID_MPEG_STREAM_TYPE: | ||
757 | return v4l2_ctrl_query_fill(c, | ||
758 | V4L2_MPEG_STREAM_TYPE_MPEG2_PS, | ||
759 | V4L2_MPEG_STREAM_TYPE_MPEG2_TS, | ||
760 | 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS); | ||
761 | case V4L2_CID_MPEG_VIDEO_ASPECT: | ||
762 | return v4l2_ctrl_query_fill(c, | ||
763 | V4L2_MPEG_VIDEO_ASPECT_1x1, | ||
764 | V4L2_MPEG_VIDEO_ASPECT_221x100, | ||
765 | 1, V4L2_MPEG_VIDEO_ASPECT_4x3); | ||
766 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | ||
767 | return v4l2_ctrl_query_fill(c, 1, 255, 1, 15); | ||
768 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: | ||
769 | return v4l2_ctrl_query_fill(c, | ||
770 | 1, 3, 1, 1); | ||
771 | default: | ||
772 | return -EINVAL; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
777 | struct v4l2_queryctrl *c) | ||
778 | { | ||
779 | struct saa7164_vbi_fh *fh = priv; | ||
780 | struct saa7164_port *port = fh->port; | ||
781 | int i, next; | ||
782 | u32 id = c->id; | ||
783 | |||
784 | memset(c, 0, sizeof(*c)); | ||
785 | |||
786 | next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); | ||
787 | c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; | ||
788 | |||
789 | for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) { | ||
790 | if (next) { | ||
791 | if (c->id < saa7164_v4l2_ctrls[i]) | ||
792 | c->id = saa7164_v4l2_ctrls[i]; | ||
793 | else | ||
794 | continue; | ||
795 | } | ||
796 | |||
797 | if (c->id == saa7164_v4l2_ctrls[i]) | ||
798 | return fill_queryctrl(&port->vbi_params, c); | ||
799 | |||
800 | if (c->id < saa7164_v4l2_ctrls[i]) | ||
801 | break; | ||
802 | } | ||
803 | |||
804 | return -EINVAL; | ||
805 | } | ||
806 | |||
807 | static int saa7164_vbi_stop_port(struct saa7164_port *port) | ||
808 | { | ||
809 | struct saa7164_dev *dev = port->dev; | ||
810 | int ret; | ||
811 | |||
812 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | ||
813 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
814 | printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", | ||
815 | __func__, ret); | ||
816 | ret = -EIO; | ||
817 | } else { | ||
818 | dprintk(DBGLVL_VBI, "%s() Stopped\n", __func__); | ||
819 | ret = 0; | ||
820 | } | ||
821 | |||
822 | return ret; | ||
823 | } | ||
824 | |||
825 | static int saa7164_vbi_acquire_port(struct saa7164_port *port) | ||
826 | { | ||
827 | struct saa7164_dev *dev = port->dev; | ||
828 | int ret; | ||
829 | |||
830 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
831 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
832 | printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", | ||
833 | __func__, ret); | ||
834 | ret = -EIO; | ||
835 | } else { | ||
836 | dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__); | ||
837 | ret = 0; | ||
838 | } | ||
839 | |||
840 | return ret; | ||
841 | } | ||
842 | |||
843 | static int saa7164_vbi_pause_port(struct saa7164_port *port) | ||
844 | { | ||
845 | struct saa7164_dev *dev = port->dev; | ||
846 | int ret; | ||
847 | |||
848 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
849 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | ||
850 | printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", | ||
851 | __func__, ret); | ||
852 | ret = -EIO; | ||
853 | } else { | ||
854 | dprintk(DBGLVL_VBI, "%s() Paused\n", __func__); | ||
855 | ret = 0; | ||
856 | } | ||
857 | |||
858 | return ret; | ||
859 | } | ||
860 | |||
861 | /* Firmware is very windows centric, meaning you have to transition | ||
862 | * the part through AVStream / KS Windows stages, forwards or backwards. | ||
863 | * States are: stopped, acquired (h/w), paused, started. | ||
864 | * We have to leave here will all of the soft buffers on the free list, | ||
865 | * else the cfg_post() func won't have soft buffers to correctly configure. | ||
866 | */ | ||
867 | static int saa7164_vbi_stop_streaming(struct saa7164_port *port) | ||
868 | { | ||
869 | struct saa7164_dev *dev = port->dev; | ||
870 | struct saa7164_buffer *buf; | ||
871 | struct saa7164_user_buffer *ubuf; | ||
872 | struct list_head *c, *n; | ||
873 | int ret; | ||
874 | |||
875 | dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); | ||
876 | |||
877 | ret = saa7164_vbi_pause_port(port); | ||
878 | ret = saa7164_vbi_acquire_port(port); | ||
879 | ret = saa7164_vbi_stop_port(port); | ||
880 | |||
881 | dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__, | ||
882 | port->nr); | ||
883 | |||
884 | /* Reset the state of any allocated buffer resources */ | ||
885 | mutex_lock(&port->dmaqueue_lock); | ||
886 | |||
887 | /* Reset the hard and soft buffer state */ | ||
888 | list_for_each_safe(c, n, &port->dmaqueue.list) { | ||
889 | buf = list_entry(c, struct saa7164_buffer, list); | ||
890 | buf->flags = SAA7164_BUFFER_FREE; | ||
891 | buf->pos = 0; | ||
892 | } | ||
893 | |||
894 | list_for_each_safe(c, n, &port->list_buf_used.list) { | ||
895 | ubuf = list_entry(c, struct saa7164_user_buffer, list); | ||
896 | ubuf->pos = 0; | ||
897 | list_move_tail(&ubuf->list, &port->list_buf_free.list); | ||
898 | } | ||
899 | |||
900 | mutex_unlock(&port->dmaqueue_lock); | ||
901 | |||
902 | /* Free any allocated resources */ | ||
903 | saa7164_vbi_buffers_dealloc(port); | ||
904 | |||
905 | dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr); | ||
906 | |||
907 | return ret; | ||
908 | } | ||
909 | |||
910 | static int saa7164_vbi_start_streaming(struct saa7164_port *port) | ||
911 | { | ||
912 | struct saa7164_dev *dev = port->dev; | ||
913 | int result, ret = 0; | ||
914 | |||
915 | dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); | ||
916 | |||
917 | port->done_first_interrupt = 0; | ||
918 | |||
919 | /* allocate all of the PCIe DMA buffer resources on the fly, | ||
920 | * allowing switching between TS and PS payloads without | ||
921 | * requiring a complete driver reload. | ||
922 | */ | ||
923 | saa7164_vbi_buffers_alloc(port); | ||
924 | |||
925 | /* Configure the encoder with any cache values */ | ||
926 | #if 0 | ||
927 | saa7164_api_set_encoder(port); | ||
928 | saa7164_api_get_encoder(port); | ||
929 | #endif | ||
930 | |||
931 | /* Place the empty buffers on the hardware */ | ||
932 | saa7164_buffer_cfg_port(port); | ||
933 | |||
934 | /* Negotiate format */ | ||
935 | if (saa7164_api_set_vbi_format(port) != SAA_OK) { | ||
936 | printk(KERN_ERR "%s() No supported VBI format\n", __func__); | ||
937 | ret = -EIO; | ||
938 | goto out; | ||
939 | } | ||
940 | |||
941 | /* Acquire the hardware */ | ||
942 | result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | ||
943 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
944 | printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", | ||
945 | __func__, result); | ||
946 | |||
947 | ret = -EIO; | ||
948 | goto out; | ||
949 | } else | ||
950 | dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__); | ||
951 | |||
952 | /* Pause the hardware */ | ||
953 | result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | ||
954 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
955 | printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", | ||
956 | __func__, result); | ||
957 | |||
958 | /* Stop the hardware, regardless */ | ||
959 | result = saa7164_vbi_stop_port(port); | ||
960 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
961 | printk(KERN_ERR "%s() pause/forced stop transition " | ||
962 | "failed, res = 0x%x\n", __func__, result); | ||
963 | } | ||
964 | |||
965 | ret = -EIO; | ||
966 | goto out; | ||
967 | } else | ||
968 | dprintk(DBGLVL_VBI, "%s() Paused\n", __func__); | ||
969 | |||
970 | /* Start the hardware */ | ||
971 | result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); | ||
972 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
973 | printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", | ||
974 | __func__, result); | ||
975 | |||
976 | /* Stop the hardware, regardless */ | ||
977 | result = saa7164_vbi_acquire_port(port); | ||
978 | result = saa7164_vbi_stop_port(port); | ||
979 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | ||
980 | printk(KERN_ERR "%s() run/forced stop transition " | ||
981 | "failed, res = 0x%x\n", __func__, result); | ||
982 | } | ||
983 | |||
984 | ret = -EIO; | ||
985 | } else | ||
986 | dprintk(DBGLVL_VBI, "%s() Running\n", __func__); | ||
987 | |||
988 | out: | ||
989 | return ret; | ||
990 | } | ||
991 | |||
992 | int saa7164_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f) | ||
993 | { | ||
994 | /* ntsc */ | ||
995 | f->fmt.vbi.samples_per_line = 1600; | ||
996 | f->fmt.vbi.samples_per_line = 1440; | ||
997 | f->fmt.vbi.sampling_rate = 27000000; | ||
998 | f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; | ||
999 | f->fmt.vbi.offset = 0; | ||
1000 | f->fmt.vbi.flags = 0; | ||
1001 | f->fmt.vbi.start[0] = 10; | ||
1002 | f->fmt.vbi.count[0] = 18; | ||
1003 | f->fmt.vbi.start[1] = 263 + 10 + 1; | ||
1004 | f->fmt.vbi.count[1] = 18; | ||
1005 | return 0; | ||
1006 | } | ||
1007 | |||
1008 | static int fops_open(struct file *file) | ||
1009 | { | ||
1010 | struct saa7164_dev *dev; | ||
1011 | struct saa7164_port *port; | ||
1012 | struct saa7164_vbi_fh *fh; | ||
1013 | |||
1014 | port = (struct saa7164_port *)video_get_drvdata(video_devdata(file)); | ||
1015 | if (!port) | ||
1016 | return -ENODEV; | ||
1017 | |||
1018 | dev = port->dev; | ||
1019 | |||
1020 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | ||
1021 | |||
1022 | /* allocate + initialize per filehandle data */ | ||
1023 | fh = kzalloc(sizeof(*fh), GFP_KERNEL); | ||
1024 | if (NULL == fh) | ||
1025 | return -ENOMEM; | ||
1026 | |||
1027 | file->private_data = fh; | ||
1028 | fh->port = port; | ||
1029 | |||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | static int fops_release(struct file *file) | ||
1034 | { | ||
1035 | struct saa7164_vbi_fh *fh = file->private_data; | ||
1036 | struct saa7164_port *port = fh->port; | ||
1037 | struct saa7164_dev *dev = port->dev; | ||
1038 | |||
1039 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | ||
1040 | |||
1041 | /* Shut device down on last close */ | ||
1042 | if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { | ||
1043 | if (atomic_dec_return(&port->v4l_reader_count) == 0) { | ||
1044 | /* stop vbi capture then cancel buffers */ | ||
1045 | saa7164_vbi_stop_streaming(port); | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | file->private_data = NULL; | ||
1050 | kfree(fh); | ||
1051 | |||
1052 | return 0; | ||
1053 | } | ||
1054 | |||
1055 | struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port) | ||
1056 | { | ||
1057 | struct saa7164_user_buffer *ubuf = NULL; | ||
1058 | struct saa7164_dev *dev = port->dev; | ||
1059 | u32 crc; | ||
1060 | |||
1061 | mutex_lock(&port->dmaqueue_lock); | ||
1062 | if (!list_empty(&port->list_buf_used.list)) { | ||
1063 | ubuf = list_first_entry(&port->list_buf_used.list, | ||
1064 | struct saa7164_user_buffer, list); | ||
1065 | |||
1066 | if (crc_checking) { | ||
1067 | crc = crc32(0, ubuf->data, ubuf->actual_size); | ||
1068 | if (crc != ubuf->crc) { | ||
1069 | printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", | ||
1070 | __func__, | ||
1071 | ubuf, ubuf->crc, crc); | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1075 | } | ||
1076 | mutex_unlock(&port->dmaqueue_lock); | ||
1077 | |||
1078 | dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf); | ||
1079 | |||
1080 | return ubuf; | ||
1081 | } | ||
1082 | |||
1083 | static ssize_t fops_read(struct file *file, char __user *buffer, | ||
1084 | size_t count, loff_t *pos) | ||
1085 | { | ||
1086 | struct saa7164_vbi_fh *fh = file->private_data; | ||
1087 | struct saa7164_port *port = fh->port; | ||
1088 | struct saa7164_user_buffer *ubuf = NULL; | ||
1089 | struct saa7164_dev *dev = port->dev; | ||
1090 | int ret = 0; | ||
1091 | int rem, cnt; | ||
1092 | u8 *p; | ||
1093 | |||
1094 | port->last_read_msecs_diff = port->last_read_msecs; | ||
1095 | port->last_read_msecs = jiffies_to_msecs(jiffies); | ||
1096 | port->last_read_msecs_diff = port->last_read_msecs - | ||
1097 | port->last_read_msecs_diff; | ||
1098 | |||
1099 | saa7164_histogram_update(&port->read_interval, | ||
1100 | port->last_read_msecs_diff); | ||
1101 | |||
1102 | if (*pos) { | ||
1103 | printk(KERN_ERR "%s() ESPIPE\n", __func__); | ||
1104 | return -ESPIPE; | ||
1105 | } | ||
1106 | |||
1107 | if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { | ||
1108 | if (atomic_inc_return(&port->v4l_reader_count) == 1) { | ||
1109 | |||
1110 | if (saa7164_vbi_initialize(port) < 0) { | ||
1111 | printk(KERN_ERR "%s() EINVAL\n", __func__); | ||
1112 | return -EINVAL; | ||
1113 | } | ||
1114 | |||
1115 | saa7164_vbi_start_streaming(port); | ||
1116 | msleep(200); | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | /* blocking wait for buffer */ | ||
1121 | if ((file->f_flags & O_NONBLOCK) == 0) { | ||
1122 | if (wait_event_interruptible(port->wait_read, | ||
1123 | saa7164_vbi_next_buf(port))) { | ||
1124 | printk(KERN_ERR "%s() ERESTARTSYS\n", __func__); | ||
1125 | return -ERESTARTSYS; | ||
1126 | } | ||
1127 | } | ||
1128 | |||
1129 | /* Pull the first buffer from the used list */ | ||
1130 | ubuf = saa7164_vbi_next_buf(port); | ||
1131 | |||
1132 | while ((count > 0) && ubuf) { | ||
1133 | |||
1134 | /* set remaining bytes to copy */ | ||
1135 | rem = ubuf->actual_size - ubuf->pos; | ||
1136 | cnt = rem > count ? count : rem; | ||
1137 | |||
1138 | p = ubuf->data + ubuf->pos; | ||
1139 | |||
1140 | dprintk(DBGLVL_VBI, | ||
1141 | "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n", | ||
1142 | __func__, (int)count, cnt, rem, ubuf, ubuf->pos); | ||
1143 | |||
1144 | if (copy_to_user(buffer, p, cnt)) { | ||
1145 | printk(KERN_ERR "%s() copy_to_user failed\n", __func__); | ||
1146 | if (!ret) { | ||
1147 | printk(KERN_ERR "%s() EFAULT\n", __func__); | ||
1148 | ret = -EFAULT; | ||
1149 | } | ||
1150 | goto err; | ||
1151 | } | ||
1152 | |||
1153 | ubuf->pos += cnt; | ||
1154 | count -= cnt; | ||
1155 | buffer += cnt; | ||
1156 | ret += cnt; | ||
1157 | |||
1158 | if (ubuf->pos > ubuf->actual_size) | ||
1159 | printk(KERN_ERR "read() pos > actual, huh?\n"); | ||
1160 | |||
1161 | if (ubuf->pos == ubuf->actual_size) { | ||
1162 | |||
1163 | /* finished with current buffer, take next buffer */ | ||
1164 | |||
1165 | /* Requeue the buffer on the free list */ | ||
1166 | ubuf->pos = 0; | ||
1167 | |||
1168 | mutex_lock(&port->dmaqueue_lock); | ||
1169 | list_move_tail(&ubuf->list, &port->list_buf_free.list); | ||
1170 | mutex_unlock(&port->dmaqueue_lock); | ||
1171 | |||
1172 | /* Dequeue next */ | ||
1173 | if ((file->f_flags & O_NONBLOCK) == 0) { | ||
1174 | if (wait_event_interruptible(port->wait_read, | ||
1175 | saa7164_vbi_next_buf(port))) { | ||
1176 | break; | ||
1177 | } | ||
1178 | } | ||
1179 | ubuf = saa7164_vbi_next_buf(port); | ||
1180 | } | ||
1181 | } | ||
1182 | err: | ||
1183 | if (!ret && !ubuf) { | ||
1184 | printk(KERN_ERR "%s() EAGAIN\n", __func__); | ||
1185 | ret = -EAGAIN; | ||
1186 | } | ||
1187 | |||
1188 | return ret; | ||
1189 | } | ||
1190 | |||
1191 | static unsigned int fops_poll(struct file *file, poll_table *wait) | ||
1192 | { | ||
1193 | struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data; | ||
1194 | struct saa7164_port *port = fh->port; | ||
1195 | unsigned int mask = 0; | ||
1196 | |||
1197 | port->last_poll_msecs_diff = port->last_poll_msecs; | ||
1198 | port->last_poll_msecs = jiffies_to_msecs(jiffies); | ||
1199 | port->last_poll_msecs_diff = port->last_poll_msecs - | ||
1200 | port->last_poll_msecs_diff; | ||
1201 | |||
1202 | saa7164_histogram_update(&port->poll_interval, | ||
1203 | port->last_poll_msecs_diff); | ||
1204 | |||
1205 | if (!video_is_registered(port->v4l_device)) | ||
1206 | return -EIO; | ||
1207 | |||
1208 | if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { | ||
1209 | if (atomic_inc_return(&port->v4l_reader_count) == 1) { | ||
1210 | if (saa7164_vbi_initialize(port) < 0) | ||
1211 | return -EINVAL; | ||
1212 | saa7164_vbi_start_streaming(port); | ||
1213 | msleep(200); | ||
1214 | } | ||
1215 | } | ||
1216 | |||
1217 | /* blocking wait for buffer */ | ||
1218 | if ((file->f_flags & O_NONBLOCK) == 0) { | ||
1219 | if (wait_event_interruptible(port->wait_read, | ||
1220 | saa7164_vbi_next_buf(port))) { | ||
1221 | return -ERESTARTSYS; | ||
1222 | } | ||
1223 | } | ||
1224 | |||
1225 | /* Pull the first buffer from the used list */ | ||
1226 | if (!list_empty(&port->list_buf_used.list)) | ||
1227 | mask |= POLLIN | POLLRDNORM; | ||
1228 | |||
1229 | return mask; | ||
1230 | } | ||
1231 | static const struct v4l2_file_operations vbi_fops = { | ||
1232 | .owner = THIS_MODULE, | ||
1233 | .open = fops_open, | ||
1234 | .release = fops_release, | ||
1235 | .read = fops_read, | ||
1236 | .poll = fops_poll, | ||
1237 | .unlocked_ioctl = video_ioctl2, | ||
1238 | }; | ||
1239 | |||
1240 | static const struct v4l2_ioctl_ops vbi_ioctl_ops = { | ||
1241 | .vidioc_s_std = vidioc_s_std, | ||
1242 | .vidioc_enum_input = vidioc_enum_input, | ||
1243 | .vidioc_g_input = vidioc_g_input, | ||
1244 | .vidioc_s_input = vidioc_s_input, | ||
1245 | .vidioc_g_tuner = vidioc_g_tuner, | ||
1246 | .vidioc_s_tuner = vidioc_s_tuner, | ||
1247 | .vidioc_g_frequency = vidioc_g_frequency, | ||
1248 | .vidioc_s_frequency = vidioc_s_frequency, | ||
1249 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
1250 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
1251 | .vidioc_querycap = vidioc_querycap, | ||
1252 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | ||
1253 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, | ||
1254 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | ||
1255 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | ||
1256 | .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, | ||
1257 | .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, | ||
1258 | .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, | ||
1259 | .vidioc_log_status = vidioc_log_status, | ||
1260 | .vidioc_queryctrl = vidioc_queryctrl, | ||
1261 | #if 0 | ||
1262 | .vidioc_g_chip_ident = saa7164_g_chip_ident, | ||
1263 | #endif | ||
1264 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1265 | #if 0 | ||
1266 | .vidioc_g_register = saa7164_g_register, | ||
1267 | .vidioc_s_register = saa7164_s_register, | ||
1268 | #endif | ||
1269 | #endif | ||
1270 | .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt, | ||
1271 | .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt, | ||
1272 | .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt, | ||
1273 | }; | ||
1274 | |||
1275 | static struct video_device saa7164_vbi_template = { | ||
1276 | .name = "saa7164", | ||
1277 | .fops = &vbi_fops, | ||
1278 | .ioctl_ops = &vbi_ioctl_ops, | ||
1279 | .minor = -1, | ||
1280 | .tvnorms = SAA7164_NORMS, | ||
1281 | .current_norm = V4L2_STD_NTSC_M, | ||
1282 | }; | ||
1283 | |||
1284 | static struct video_device *saa7164_vbi_alloc( | ||
1285 | struct saa7164_port *port, | ||
1286 | struct pci_dev *pci, | ||
1287 | struct video_device *template, | ||
1288 | char *type) | ||
1289 | { | ||
1290 | struct video_device *vfd; | ||
1291 | struct saa7164_dev *dev = port->dev; | ||
1292 | |||
1293 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | ||
1294 | |||
1295 | vfd = video_device_alloc(); | ||
1296 | if (NULL == vfd) | ||
1297 | return NULL; | ||
1298 | |||
1299 | *vfd = *template; | ||
1300 | snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, | ||
1301 | type, saa7164_boards[dev->board].name); | ||
1302 | |||
1303 | vfd->parent = &pci->dev; | ||
1304 | vfd->release = video_device_release; | ||
1305 | return vfd; | ||
1306 | } | ||
1307 | |||
1308 | int saa7164_vbi_register(struct saa7164_port *port) | ||
1309 | { | ||
1310 | struct saa7164_dev *dev = port->dev; | ||
1311 | int result = -ENODEV; | ||
1312 | |||
1313 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | ||
1314 | |||
1315 | if (port->type != SAA7164_MPEG_VBI) | ||
1316 | BUG(); | ||
1317 | |||
1318 | /* Sanity check that the PCI configuration space is active */ | ||
1319 | if (port->hwcfg.BARLocation == 0) { | ||
1320 | printk(KERN_ERR "%s() failed " | ||
1321 | "(errno = %d), NO PCI configuration\n", | ||
1322 | __func__, result); | ||
1323 | result = -ENOMEM; | ||
1324 | goto failed; | ||
1325 | } | ||
1326 | |||
1327 | /* Establish VBI defaults here */ | ||
1328 | |||
1329 | /* Allocate and register the video device node */ | ||
1330 | port->v4l_device = saa7164_vbi_alloc(port, | ||
1331 | dev->pci, &saa7164_vbi_template, "vbi"); | ||
1332 | |||
1333 | if (!port->v4l_device) { | ||
1334 | printk(KERN_INFO "%s: can't allocate vbi device\n", | ||
1335 | dev->name); | ||
1336 | result = -ENOMEM; | ||
1337 | goto failed; | ||
1338 | } | ||
1339 | |||
1340 | video_set_drvdata(port->v4l_device, port); | ||
1341 | result = video_register_device(port->v4l_device, | ||
1342 | VFL_TYPE_VBI, -1); | ||
1343 | if (result < 0) { | ||
1344 | printk(KERN_INFO "%s: can't register vbi device\n", | ||
1345 | dev->name); | ||
1346 | /* TODO: We're going to leak here if we don't dealloc | ||
1347 | The buffers above. The unreg function can't deal wit it. | ||
1348 | */ | ||
1349 | goto failed; | ||
1350 | } | ||
1351 | |||
1352 | printk(KERN_INFO "%s: registered device vbi%d [vbi]\n", | ||
1353 | dev->name, port->v4l_device->num); | ||
1354 | |||
1355 | /* Configure the hardware defaults */ | ||
1356 | |||
1357 | result = 0; | ||
1358 | failed: | ||
1359 | return result; | ||
1360 | } | ||
1361 | |||
1362 | void saa7164_vbi_unregister(struct saa7164_port *port) | ||
1363 | { | ||
1364 | struct saa7164_dev *dev = port->dev; | ||
1365 | |||
1366 | dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); | ||
1367 | |||
1368 | if (port->type != SAA7164_MPEG_VBI) | ||
1369 | BUG(); | ||
1370 | |||
1371 | if (port->v4l_device) { | ||
1372 | if (port->v4l_device->minor != -1) | ||
1373 | video_unregister_device(port->v4l_device); | ||
1374 | else | ||
1375 | video_device_release(port->v4l_device); | ||
1376 | |||
1377 | port->v4l_device = NULL; | ||
1378 | } | ||
1379 | |||
1380 | } | ||
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h new file mode 100644 index 00000000000..35b64306ba9 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164.h | |||
@@ -0,0 +1,623 @@ | |||
1 | /* | ||
2 | * Driver for the NXP SAA7164 PCIe bridge | ||
3 | * | ||
4 | * Copyright (c) 2010 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 | #include <linux/mutex.h> | ||
52 | #include <linux/crc32.h> | ||
53 | #include <linux/kthread.h> | ||
54 | #include <linux/freezer.h> | ||
55 | |||
56 | #include <media/tuner.h> | ||
57 | #include <media/tveeprom.h> | ||
58 | #include <media/videobuf-dma-sg.h> | ||
59 | #include <media/videobuf-dvb.h> | ||
60 | #include <dvb_demux.h> | ||
61 | #include <dvb_frontend.h> | ||
62 | #include <dvb_net.h> | ||
63 | #include <dvbdev.h> | ||
64 | #include <dmxdev.h> | ||
65 | #include <media/v4l2-common.h> | ||
66 | #include <media/v4l2-ioctl.h> | ||
67 | #include <media/v4l2-chip-ident.h> | ||
68 | |||
69 | #include "saa7164-reg.h" | ||
70 | #include "saa7164-types.h" | ||
71 | |||
72 | #define SAA7164_MAXBOARDS 8 | ||
73 | |||
74 | #define UNSET (-1U) | ||
75 | #define SAA7164_BOARD_NOAUTO UNSET | ||
76 | #define SAA7164_BOARD_UNKNOWN 0 | ||
77 | #define SAA7164_BOARD_UNKNOWN_REV2 1 | ||
78 | #define SAA7164_BOARD_UNKNOWN_REV3 2 | ||
79 | #define SAA7164_BOARD_HAUPPAUGE_HVR2250 3 | ||
80 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200 4 | ||
81 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5 | ||
82 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6 | ||
83 | #define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7 | ||
84 | #define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8 | ||
85 | #define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9 | ||
86 | |||
87 | #define SAA7164_MAX_UNITS 8 | ||
88 | #define SAA7164_TS_NUMBER_OF_LINES 312 | ||
89 | #define SAA7164_PS_NUMBER_OF_LINES 256 | ||
90 | #define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */ | ||
91 | #define SAA7164_MAX_ENCODER_BUFFERS 64 /* max 5secs of latency at 6Mbps */ | ||
92 | #define SAA7164_MAX_VBI_BUFFERS 64 | ||
93 | |||
94 | /* Port related defines */ | ||
95 | #define SAA7164_PORT_TS1 (0) | ||
96 | #define SAA7164_PORT_TS2 (SAA7164_PORT_TS1 + 1) | ||
97 | #define SAA7164_PORT_ENC1 (SAA7164_PORT_TS2 + 1) | ||
98 | #define SAA7164_PORT_ENC2 (SAA7164_PORT_ENC1 + 1) | ||
99 | #define SAA7164_PORT_VBI1 (SAA7164_PORT_ENC2 + 1) | ||
100 | #define SAA7164_PORT_VBI2 (SAA7164_PORT_VBI1 + 1) | ||
101 | #define SAA7164_MAX_PORTS (SAA7164_PORT_VBI2 + 1) | ||
102 | |||
103 | #define DBGLVL_FW 4 | ||
104 | #define DBGLVL_DVB 8 | ||
105 | #define DBGLVL_I2C 16 | ||
106 | #define DBGLVL_API 32 | ||
107 | #define DBGLVL_CMD 64 | ||
108 | #define DBGLVL_BUS 128 | ||
109 | #define DBGLVL_IRQ 256 | ||
110 | #define DBGLVL_BUF 512 | ||
111 | #define DBGLVL_ENC 1024 | ||
112 | #define DBGLVL_VBI 2048 | ||
113 | #define DBGLVL_THR 4096 | ||
114 | #define DBGLVL_CPU 8192 | ||
115 | |||
116 | #define SAA7164_NORMS \ | ||
117 | (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443) | ||
118 | |||
119 | enum port_t { | ||
120 | SAA7164_MPEG_UNDEFINED = 0, | ||
121 | SAA7164_MPEG_DVB, | ||
122 | SAA7164_MPEG_ENCODER, | ||
123 | SAA7164_MPEG_VBI, | ||
124 | }; | ||
125 | |||
126 | enum saa7164_i2c_bus_nr { | ||
127 | SAA7164_I2C_BUS_0 = 0, | ||
128 | SAA7164_I2C_BUS_1, | ||
129 | SAA7164_I2C_BUS_2, | ||
130 | }; | ||
131 | |||
132 | enum saa7164_buffer_flags { | ||
133 | SAA7164_BUFFER_UNDEFINED = 0, | ||
134 | SAA7164_BUFFER_FREE, | ||
135 | SAA7164_BUFFER_BUSY, | ||
136 | SAA7164_BUFFER_FULL | ||
137 | }; | ||
138 | |||
139 | enum saa7164_unit_type { | ||
140 | SAA7164_UNIT_UNDEFINED = 0, | ||
141 | SAA7164_UNIT_DIGITAL_DEMODULATOR, | ||
142 | SAA7164_UNIT_ANALOG_DEMODULATOR, | ||
143 | SAA7164_UNIT_TUNER, | ||
144 | SAA7164_UNIT_EEPROM, | ||
145 | SAA7164_UNIT_ZILOG_IRBLASTER, | ||
146 | SAA7164_UNIT_ENCODER, | ||
147 | }; | ||
148 | |||
149 | /* The PCIe bridge doesn't grant direct access to i2c. | ||
150 | * Instead, you address i2c devices using a uniqely | ||
151 | * allocated 'unitid' value via a messaging API. This | ||
152 | * is a problem. The kernel and existing demod/tuner | ||
153 | * drivers expect to talk 'i2c', so we have to maintain | ||
154 | * a translation layer, and a series of functions to | ||
155 | * convert i2c bus + device address into a unit id. | ||
156 | */ | ||
157 | struct saa7164_unit { | ||
158 | enum saa7164_unit_type type; | ||
159 | u8 id; | ||
160 | char *name; | ||
161 | enum saa7164_i2c_bus_nr i2c_bus_nr; | ||
162 | u8 i2c_bus_addr; | ||
163 | u8 i2c_reg_len; | ||
164 | }; | ||
165 | |||
166 | struct saa7164_board { | ||
167 | char *name; | ||
168 | enum port_t porta, portb, portc, | ||
169 | portd, porte, portf; | ||
170 | enum { | ||
171 | SAA7164_CHIP_UNDEFINED = 0, | ||
172 | SAA7164_CHIP_REV2, | ||
173 | SAA7164_CHIP_REV3, | ||
174 | } chiprev; | ||
175 | struct saa7164_unit unit[SAA7164_MAX_UNITS]; | ||
176 | }; | ||
177 | |||
178 | struct saa7164_subid { | ||
179 | u16 subvendor; | ||
180 | u16 subdevice; | ||
181 | u32 card; | ||
182 | }; | ||
183 | |||
184 | struct saa7164_encoder_fh { | ||
185 | struct saa7164_port *port; | ||
186 | atomic_t v4l_reading; | ||
187 | }; | ||
188 | |||
189 | struct saa7164_vbi_fh { | ||
190 | struct saa7164_port *port; | ||
191 | atomic_t v4l_reading; | ||
192 | }; | ||
193 | |||
194 | struct saa7164_histogram_bucket { | ||
195 | u32 val; | ||
196 | u32 count; | ||
197 | u64 update_time; | ||
198 | }; | ||
199 | |||
200 | struct saa7164_histogram { | ||
201 | char name[32]; | ||
202 | struct saa7164_histogram_bucket counter1[64]; | ||
203 | }; | ||
204 | |||
205 | struct saa7164_user_buffer { | ||
206 | struct list_head list; | ||
207 | |||
208 | /* Attributes */ | ||
209 | u8 *data; | ||
210 | u32 pos; | ||
211 | u32 actual_size; | ||
212 | |||
213 | u32 crc; | ||
214 | }; | ||
215 | |||
216 | struct saa7164_fw_status { | ||
217 | |||
218 | /* RISC Core details */ | ||
219 | u32 status; | ||
220 | u32 mode; | ||
221 | u32 spec; | ||
222 | u32 inst; | ||
223 | u32 cpuload; | ||
224 | u32 remainheap; | ||
225 | |||
226 | /* Firmware version */ | ||
227 | u32 version; | ||
228 | u32 major; | ||
229 | u32 sub; | ||
230 | u32 rel; | ||
231 | u32 buildnr; | ||
232 | }; | ||
233 | |||
234 | struct saa7164_dvb { | ||
235 | struct mutex lock; | ||
236 | struct dvb_adapter adapter; | ||
237 | struct dvb_frontend *frontend; | ||
238 | struct dvb_demux demux; | ||
239 | struct dmxdev dmxdev; | ||
240 | struct dmx_frontend fe_hw; | ||
241 | struct dmx_frontend fe_mem; | ||
242 | struct dvb_net net; | ||
243 | int feeding; | ||
244 | }; | ||
245 | |||
246 | struct saa7164_i2c { | ||
247 | struct saa7164_dev *dev; | ||
248 | |||
249 | enum saa7164_i2c_bus_nr nr; | ||
250 | |||
251 | /* I2C I/O */ | ||
252 | struct i2c_adapter i2c_adap; | ||
253 | struct i2c_algo_bit_data i2c_algo; | ||
254 | struct i2c_client i2c_client; | ||
255 | u32 i2c_rc; | ||
256 | }; | ||
257 | |||
258 | struct saa7164_ctrl { | ||
259 | struct v4l2_queryctrl v; | ||
260 | }; | ||
261 | |||
262 | struct saa7164_tvnorm { | ||
263 | char *name; | ||
264 | v4l2_std_id id; | ||
265 | }; | ||
266 | |||
267 | struct saa7164_encoder_params { | ||
268 | struct saa7164_tvnorm encodernorm; | ||
269 | u32 height; | ||
270 | u32 width; | ||
271 | u32 is_50hz; | ||
272 | u32 bitrate; /* bps */ | ||
273 | u32 bitrate_peak; /* bps */ | ||
274 | u32 bitrate_mode; | ||
275 | u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */ | ||
276 | |||
277 | u32 audio_sampling_freq; | ||
278 | u32 ctl_mute; | ||
279 | u32 ctl_aspect; | ||
280 | u32 refdist; | ||
281 | u32 gop_size; | ||
282 | }; | ||
283 | |||
284 | struct saa7164_vbi_params { | ||
285 | struct saa7164_tvnorm encodernorm; | ||
286 | u32 height; | ||
287 | u32 width; | ||
288 | u32 is_50hz; | ||
289 | u32 bitrate; /* bps */ | ||
290 | u32 bitrate_peak; /* bps */ | ||
291 | u32 bitrate_mode; | ||
292 | u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */ | ||
293 | |||
294 | u32 audio_sampling_freq; | ||
295 | u32 ctl_mute; | ||
296 | u32 ctl_aspect; | ||
297 | u32 refdist; | ||
298 | u32 gop_size; | ||
299 | }; | ||
300 | |||
301 | struct saa7164_port; | ||
302 | |||
303 | struct saa7164_buffer { | ||
304 | struct list_head list; | ||
305 | |||
306 | /* Note of which h/w buffer list index position we occupy */ | ||
307 | int idx; | ||
308 | |||
309 | struct saa7164_port *port; | ||
310 | |||
311 | /* Hardware Specific */ | ||
312 | /* PCI Memory allocations */ | ||
313 | enum saa7164_buffer_flags flags; /* Free, Busy, Full */ | ||
314 | |||
315 | /* A block of page align PCI memory */ | ||
316 | u32 pci_size; /* PCI allocation size in bytes */ | ||
317 | u64 __iomem *cpu; /* Virtual address */ | ||
318 | dma_addr_t dma; /* Physical address */ | ||
319 | u32 crc; /* Checksum for the entire buffer data */ | ||
320 | |||
321 | /* A page table that splits the block into a number of entries */ | ||
322 | u32 pt_size; /* PCI allocation size in bytes */ | ||
323 | u64 __iomem *pt_cpu; /* Virtual address */ | ||
324 | dma_addr_t pt_dma; /* Physical address */ | ||
325 | |||
326 | /* Encoder fops */ | ||
327 | u32 pos; | ||
328 | u32 actual_size; | ||
329 | }; | ||
330 | |||
331 | struct saa7164_port { | ||
332 | |||
333 | struct saa7164_dev *dev; | ||
334 | enum port_t type; | ||
335 | int nr; | ||
336 | |||
337 | /* --- Generic port attributes --- */ | ||
338 | |||
339 | /* HW stream parameters */ | ||
340 | struct tmHWStreamParameters hw_streamingparams; | ||
341 | |||
342 | /* DMA configuration values, is seeded during initialization */ | ||
343 | struct tmComResDMATermDescrHeader hwcfg; | ||
344 | |||
345 | /* hardware specific registers */ | ||
346 | u32 bufcounter; | ||
347 | u32 pitch; | ||
348 | u32 bufsize; | ||
349 | u32 bufoffset; | ||
350 | u32 bufptr32l; | ||
351 | u32 bufptr32h; | ||
352 | u64 bufptr64; | ||
353 | |||
354 | u32 numpte; /* Number of entries in array, only valid in head */ | ||
355 | |||
356 | struct mutex dmaqueue_lock; | ||
357 | struct saa7164_buffer dmaqueue; | ||
358 | |||
359 | u64 last_irq_msecs, last_svc_msecs; | ||
360 | u64 last_irq_msecs_diff, last_svc_msecs_diff; | ||
361 | u32 last_svc_wp; | ||
362 | u32 last_svc_rp; | ||
363 | u64 last_irq_svc_msecs_diff; | ||
364 | u64 last_read_msecs, last_read_msecs_diff; | ||
365 | u64 last_poll_msecs, last_poll_msecs_diff; | ||
366 | |||
367 | struct saa7164_histogram irq_interval; | ||
368 | struct saa7164_histogram svc_interval; | ||
369 | struct saa7164_histogram irq_svc_interval; | ||
370 | struct saa7164_histogram read_interval; | ||
371 | struct saa7164_histogram poll_interval; | ||
372 | |||
373 | /* --- DVB Transport Specific --- */ | ||
374 | struct saa7164_dvb dvb; | ||
375 | |||
376 | /* --- Encoder/V4L related attributes --- */ | ||
377 | /* Encoder */ | ||
378 | /* Defaults established in saa7164-encoder.c */ | ||
379 | struct saa7164_tvnorm encodernorm; | ||
380 | u32 height; | ||
381 | u32 width; | ||
382 | u32 freq; | ||
383 | u32 ts_packet_size; | ||
384 | u32 ts_packet_count; | ||
385 | u8 mux_input; | ||
386 | u8 encoder_profile; | ||
387 | u8 video_format; | ||
388 | u8 audio_format; | ||
389 | u8 video_resolution; | ||
390 | u16 ctl_brightness; | ||
391 | u16 ctl_contrast; | ||
392 | u16 ctl_hue; | ||
393 | u16 ctl_saturation; | ||
394 | u16 ctl_sharpness; | ||
395 | s8 ctl_volume; | ||
396 | |||
397 | struct tmComResAFeatureDescrHeader audfeat; | ||
398 | struct tmComResEncoderDescrHeader encunit; | ||
399 | struct tmComResProcDescrHeader vidproc; | ||
400 | struct tmComResExtDevDescrHeader ifunit; | ||
401 | struct tmComResTunerDescrHeader tunerunit; | ||
402 | |||
403 | struct work_struct workenc; | ||
404 | |||
405 | /* V4L Encoder Video */ | ||
406 | struct saa7164_encoder_params encoder_params; | ||
407 | struct video_device *v4l_device; | ||
408 | atomic_t v4l_reader_count; | ||
409 | |||
410 | struct saa7164_buffer list_buf_used; | ||
411 | struct saa7164_buffer list_buf_free; | ||
412 | wait_queue_head_t wait_read; | ||
413 | |||
414 | /* V4L VBI */ | ||
415 | struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc; | ||
416 | struct saa7164_vbi_params vbi_params; | ||
417 | |||
418 | /* Debug */ | ||
419 | u32 sync_errors; | ||
420 | u32 v_cc_errors; | ||
421 | u32 a_cc_errors; | ||
422 | u8 last_v_cc; | ||
423 | u8 last_a_cc; | ||
424 | u32 done_first_interrupt; | ||
425 | }; | ||
426 | |||
427 | struct saa7164_dev { | ||
428 | struct list_head devlist; | ||
429 | atomic_t refcount; | ||
430 | |||
431 | /* pci stuff */ | ||
432 | struct pci_dev *pci; | ||
433 | unsigned char pci_rev, pci_lat; | ||
434 | int pci_bus, pci_slot; | ||
435 | u32 __iomem *lmmio; | ||
436 | u8 __iomem *bmmio; | ||
437 | u32 __iomem *lmmio2; | ||
438 | u8 __iomem *bmmio2; | ||
439 | int pci_irqmask; | ||
440 | |||
441 | /* board details */ | ||
442 | int nr; | ||
443 | int hwrevision; | ||
444 | u32 board; | ||
445 | char name[16]; | ||
446 | |||
447 | /* firmware status */ | ||
448 | struct saa7164_fw_status fw_status; | ||
449 | u32 firmwareloaded; | ||
450 | |||
451 | struct tmComResHWDescr hwdesc; | ||
452 | struct tmComResInterfaceDescr intfdesc; | ||
453 | struct tmComResBusDescr busdesc; | ||
454 | |||
455 | struct tmComResBusInfo bus; | ||
456 | |||
457 | /* Interrupt status and ack registers */ | ||
458 | u32 int_status; | ||
459 | u32 int_ack; | ||
460 | |||
461 | struct cmd cmds[SAA_CMD_MAX_MSG_UNITS]; | ||
462 | struct mutex lock; | ||
463 | |||
464 | /* I2c related */ | ||
465 | struct saa7164_i2c i2c_bus[3]; | ||
466 | |||
467 | /* Transport related */ | ||
468 | struct saa7164_port ports[SAA7164_MAX_PORTS]; | ||
469 | |||
470 | /* Deferred command/api interrupts handling */ | ||
471 | struct work_struct workcmd; | ||
472 | |||
473 | /* A kernel thread to monitor the firmware log, used | ||
474 | * only in debug mode. | ||
475 | */ | ||
476 | struct task_struct *kthread; | ||
477 | |||
478 | }; | ||
479 | |||
480 | extern struct list_head saa7164_devlist; | ||
481 | extern unsigned int waitsecs; | ||
482 | extern unsigned int encoder_buffers; | ||
483 | extern unsigned int vbi_buffers; | ||
484 | |||
485 | /* ----------------------------------------------------------- */ | ||
486 | /* saa7164-core.c */ | ||
487 | void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr); | ||
488 | void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len); | ||
489 | void saa7164_getfirmwarestatus(struct saa7164_dev *dev); | ||
490 | u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev); | ||
491 | void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val); | ||
492 | |||
493 | /* ----------------------------------------------------------- */ | ||
494 | /* saa7164-fw.c */ | ||
495 | int saa7164_downloadfirmware(struct saa7164_dev *dev); | ||
496 | |||
497 | /* ----------------------------------------------------------- */ | ||
498 | /* saa7164-i2c.c */ | ||
499 | extern int saa7164_i2c_register(struct saa7164_i2c *bus); | ||
500 | extern int saa7164_i2c_unregister(struct saa7164_i2c *bus); | ||
501 | extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus, | ||
502 | unsigned int cmd, void *arg); | ||
503 | |||
504 | /* ----------------------------------------------------------- */ | ||
505 | /* saa7164-bus.c */ | ||
506 | int saa7164_bus_setup(struct saa7164_dev *dev); | ||
507 | void saa7164_bus_dump(struct saa7164_dev *dev); | ||
508 | int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, | ||
509 | void *buf); | ||
510 | int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, | ||
511 | void *buf, int peekonly); | ||
512 | |||
513 | /* ----------------------------------------------------------- */ | ||
514 | /* saa7164-cmd.c */ | ||
515 | int saa7164_cmd_send(struct saa7164_dev *dev, | ||
516 | u8 id, enum tmComResCmd command, u16 controlselector, | ||
517 | u16 size, void *buf); | ||
518 | void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno); | ||
519 | int saa7164_irq_dequeue(struct saa7164_dev *dev); | ||
520 | |||
521 | /* ----------------------------------------------------------- */ | ||
522 | /* saa7164-api.c */ | ||
523 | int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version); | ||
524 | int saa7164_api_enum_subdevs(struct saa7164_dev *dev); | ||
525 | int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, | ||
526 | u32 datalen, u8 *data); | ||
527 | int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, | ||
528 | u32 datalen, u8 *data); | ||
529 | int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr, | ||
530 | u32 datalen, u8 *data); | ||
531 | int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen); | ||
532 | int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); | ||
533 | int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); | ||
534 | int saa7164_api_transition_port(struct saa7164_port *port, u8 mode); | ||
535 | int saa7164_api_initialize_dif(struct saa7164_port *port); | ||
536 | int saa7164_api_configure_dif(struct saa7164_port *port, u32 std); | ||
537 | int saa7164_api_set_encoder(struct saa7164_port *port); | ||
538 | int saa7164_api_get_encoder(struct saa7164_port *port); | ||
539 | int saa7164_api_set_aspect_ratio(struct saa7164_port *port); | ||
540 | int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl); | ||
541 | int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl); | ||
542 | int saa7164_api_set_videomux(struct saa7164_port *port); | ||
543 | int saa7164_api_audio_mute(struct saa7164_port *port, int mute); | ||
544 | int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level); | ||
545 | int saa7164_api_set_audio_std(struct saa7164_port *port); | ||
546 | int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect); | ||
547 | int saa7164_api_get_videomux(struct saa7164_port *port); | ||
548 | int saa7164_api_set_vbi_format(struct saa7164_port *port); | ||
549 | int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level); | ||
550 | int saa7164_api_collect_debug(struct saa7164_dev *dev); | ||
551 | int saa7164_api_get_load_info(struct saa7164_dev *dev, | ||
552 | struct tmFwInfoStruct *i); | ||
553 | |||
554 | /* ----------------------------------------------------------- */ | ||
555 | /* saa7164-cards.c */ | ||
556 | extern struct saa7164_board saa7164_boards[]; | ||
557 | extern const unsigned int saa7164_bcount; | ||
558 | |||
559 | extern struct saa7164_subid saa7164_subids[]; | ||
560 | extern const unsigned int saa7164_idcount; | ||
561 | |||
562 | extern void saa7164_card_list(struct saa7164_dev *dev); | ||
563 | extern void saa7164_gpio_setup(struct saa7164_dev *dev); | ||
564 | extern void saa7164_card_setup(struct saa7164_dev *dev); | ||
565 | |||
566 | extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr); | ||
567 | extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr); | ||
568 | extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid); | ||
569 | |||
570 | /* ----------------------------------------------------------- */ | ||
571 | /* saa7164-dvb.c */ | ||
572 | extern int saa7164_dvb_register(struct saa7164_port *port); | ||
573 | extern int saa7164_dvb_unregister(struct saa7164_port *port); | ||
574 | |||
575 | /* ----------------------------------------------------------- */ | ||
576 | /* saa7164-buffer.c */ | ||
577 | extern struct saa7164_buffer *saa7164_buffer_alloc( | ||
578 | struct saa7164_port *port, u32 len); | ||
579 | extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf); | ||
580 | extern void saa7164_buffer_display(struct saa7164_buffer *buf); | ||
581 | extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i); | ||
582 | extern int saa7164_buffer_cfg_port(struct saa7164_port *port); | ||
583 | extern struct saa7164_user_buffer *saa7164_buffer_alloc_user( | ||
584 | struct saa7164_dev *dev, u32 len); | ||
585 | extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf); | ||
586 | extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i); | ||
587 | |||
588 | /* ----------------------------------------------------------- */ | ||
589 | /* saa7164-encoder.c */ | ||
590 | int saa7164_encoder_register(struct saa7164_port *port); | ||
591 | void saa7164_encoder_unregister(struct saa7164_port *port); | ||
592 | |||
593 | /* ----------------------------------------------------------- */ | ||
594 | /* saa7164-vbi.c */ | ||
595 | int saa7164_vbi_register(struct saa7164_port *port); | ||
596 | void saa7164_vbi_unregister(struct saa7164_port *port); | ||
597 | |||
598 | /* ----------------------------------------------------------- */ | ||
599 | |||
600 | extern unsigned int crc_checking; | ||
601 | |||
602 | extern unsigned int saa_debug; | ||
603 | #define dprintk(level, fmt, arg...)\ | ||
604 | do { if (saa_debug & level)\ | ||
605 | printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ | ||
606 | } while (0) | ||
607 | |||
608 | #define log_warn(fmt, arg...)\ | ||
609 | do { \ | ||
610 | printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\ | ||
611 | } while (0) | ||
612 | |||
613 | #define log_err(fmt, arg...)\ | ||
614 | do { \ | ||
615 | printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\ | ||
616 | } while (0) | ||
617 | |||
618 | #define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2)) | ||
619 | #define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) | ||
620 | |||
621 | #define saa7164_readb(reg) readl(dev->bmmio + (reg)) | ||
622 | #define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg)) | ||
623 | |||